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