1 /*
2 This file is part of the Okteta Kasten module, made within the KDE community.
3
4 SPDX-FileCopyrightText: 2010 Friedrich W. H. Kossebau <kossebau@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7 */
8
9 #include "bytearraybase85streamencoder.hpp"
10
11 // Okteta core
12 #include <Okteta/AbstractByteArrayModel>
13 // KF
14 #include <KLocalizedString>
15 // Qt
16 #include <QTextStream>
17
18 namespace Kasten {
19
streamEncoded(QTextStream & textStream,int & outputBytesPerLine,quint32 tuple,int inputByteCount)20 static inline void streamEncoded(QTextStream& textStream, int& outputBytesPerLine,
21 quint32 tuple, int inputByteCount)
22 {
23 // radix85 values, most significant first
24 char data[5];
25
26 for (int i = 4; i >= 0; --i) {
27 // TODO: find an efficient bit manipulating algorithm
28 data[i] = tuple % 85;
29 tuple /= 85;
30 }
31
32 // output inputByteCount+1 from radix85 values
33 for (int i = 0; i <= inputByteCount; ++i) {
34 textStream << (char)(data[i] + 33);
35 ++outputBytesPerLine;
36 if (outputBytesPerLine >= ByteArrayBase85StreamEncoder::maxOutputBytesPerLine) {
37 textStream << '\n';
38 outputBytesPerLine = 0;
39 }
40 }
41 }
42
43 // TODO: for now this is just the Adobe/Ascii85 implementation, so present as that
44 // later also add btoa with different version, e.g. 4.2 added a "y" for 4 spaces
ByteArrayBase85StreamEncoder()45 ByteArrayBase85StreamEncoder::ByteArrayBase85StreamEncoder()
46 : AbstractByteArrayStreamEncoder(i18nc("name of the encoding target", "Ascii85"), QStringLiteral("text/x-ascii85"))
47 {}
48
49 ByteArrayBase85StreamEncoder::~ByteArrayBase85StreamEncoder() = default;
50
encodeDataToStream(QIODevice * device,const ByteArrayView * byteArrayView,const Okteta::AbstractByteArrayModel * byteArrayModel,const Okteta::AddressRange & range)51 bool ByteArrayBase85StreamEncoder::encodeDataToStream(QIODevice* device,
52 const ByteArrayView* byteArrayView,
53 const Okteta::AbstractByteArrayModel* byteArrayModel,
54 const Okteta::AddressRange& range)
55 {
56 Q_UNUSED(byteArrayView);
57
58 bool success = true;
59
60 // encode
61 QTextStream textStream(device);
62
63 // prepare
64 InputByteIndex inputByteIndex = InputByteIndex::First;
65 quint32 tuple = 0;
66
67 // header
68 int outputBytesPerLine = 2;
69 textStream << "<~";
70
71 for (Okteta::Address i = range.start(); i <= range.end(); ++i) {
72 const Okteta::Byte byte = byteArrayModel->byte(i);
73
74 switch (inputByteIndex)
75 {
76 case InputByteIndex::First:
77 tuple |= (byte << 24);
78 inputByteIndex = InputByteIndex::Second;
79 break;
80 case InputByteIndex::Second:
81 tuple |= (byte << 16);
82 inputByteIndex = InputByteIndex::Third;
83 break;
84 case InputByteIndex::Third:
85 tuple |= (byte << 8);
86 inputByteIndex = InputByteIndex::Fourth;
87 break;
88 case InputByteIndex::Fourth:
89 tuple |= byte;
90 if (tuple == 0) {
91 textStream << 'z';
92 ++outputBytesPerLine;
93 if (outputBytesPerLine >= maxOutputBytesPerLine) {
94 textStream << '\n';
95 outputBytesPerLine = 0;
96 }
97 } else {
98 streamEncoded(textStream, outputBytesPerLine, tuple, static_cast<int>(inputByteIndex) + 1);
99 }
100 tuple = 0;
101 inputByteIndex = InputByteIndex::First;
102 break;
103 }
104 }
105
106 const bool hasBitsLeft = (inputByteIndex != InputByteIndex::First);
107 if (hasBitsLeft) {
108 streamEncoded(textStream, outputBytesPerLine, tuple, static_cast<int>(inputByteIndex));
109 }
110
111 // footer
112 if (outputBytesPerLine + 2 > maxOutputBytesPerLine) {
113 textStream << '\n';
114 }
115 textStream << "~>\n";
116
117 return success;
118 }
119
120 }
121