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 "bytearraybase32streamencoder.hpp"
10 
11 // Okteta core
12 #include <Okteta/AbstractByteArrayModel>
13 // KF
14 #include <KLocalizedString>
15 // Qt
16 #include <QTextStream>
17 
18 namespace Kasten {
19 
20 static constexpr char base32ClassicEncodeMap[32] =
21 {
22     'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
23     'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
24     'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
25     'Y', 'Z', '2', '3', '4', '5', '6', '7'
26 };
27 static constexpr char base32HexEncodeMap[32] =
28 {
29     '0', '1', '2', '3', '4', '5', '6', '7',
30     '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
31     'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
32     'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V'
33 };
34 static constexpr char base32ZHexEncodeMap[32] =
35 {
36     'y', 'b', 'n', 'd', 'r', 'f', 'g', '8',
37     'e', 'j', 'k', 'm', 'c', 'p', 'q', 'x',
38     'o', 't', '1', 'u', 'w', 'i', 's', 'z',
39     'a', '3', '4', '5', 'h', '7', '6', '9'
40 };
41 
42 static constexpr const char* base32PaddingData[4] =
43 {
44     "======",
45     "====",
46     "===",
47     "="
48 };
49 
base32Padding(ByteArrayBase32StreamEncoder::InputByteIndex index)50 static inline constexpr const char* base32Padding(ByteArrayBase32StreamEncoder::InputByteIndex index)
51 {
52     return base32PaddingData[static_cast<int>(index) - 1];
53 }
noPadding(ByteArrayBase32StreamEncoder::InputByteIndex)54 static inline constexpr const char* noPadding(ByteArrayBase32StreamEncoder::InputByteIndex /*index*/)
55 {
56     return "";
57 }
58 
59 struct Base32EncodingData
60 {
61     const char* const encodeMap;
62     const char* (* padding)(ByteArrayBase32StreamEncoder::InputByteIndex);
63 };
64 
65 static constexpr Base32EncodingData
66     base32EncodingData[3] =
67 {
68     {base32ClassicEncodeMap, &base32Padding},
69     {base32HexEncodeMap, &base32Padding},
70     {base32ZHexEncodeMap, &noPadding}
71 };
72 
73 Base32StreamEncoderSettings::Base32StreamEncoderSettings() = default;
74 
ByteArrayBase32StreamEncoder()75 ByteArrayBase32StreamEncoder::ByteArrayBase32StreamEncoder()
76     : AbstractByteArrayStreamEncoder(i18nc("name of the encoding target", "Base32"), QStringLiteral("text/x-base32"))
77 {}
78 
79 ByteArrayBase32StreamEncoder::~ByteArrayBase32StreamEncoder() = default;
80 
encodeDataToStream(QIODevice * device,const ByteArrayView * byteArrayView,const Okteta::AbstractByteArrayModel * byteArrayModel,const Okteta::AddressRange & range)81 bool ByteArrayBase32StreamEncoder::encodeDataToStream(QIODevice* device,
82                                                       const ByteArrayView* byteArrayView,
83                                                       const Okteta::AbstractByteArrayModel* byteArrayModel,
84                                                       const Okteta::AddressRange& range)
85 {
86     Q_UNUSED(byteArrayView);
87 
88     bool success = true;
89 
90     // encode
91     QTextStream textStream(device);
92 
93     // prepare
94     const auto& algorithmEncodingData = base32EncodingData[static_cast<int>(mSettings.algorithmId)];
95     const char* const base32EncodeMap = algorithmEncodingData.encodeMap;
96     const char* (* base32Padding)(InputByteIndex) = algorithmEncodingData.padding;
97 
98     InputByteIndex inputByteIndex = InputByteIndex::First;
99     int outputGroupsPerLine = 0;
100     unsigned char bitsFromLastByte;
101 
102     for (Okteta::Address i = range.start(); i <= range.end(); ++i) {
103         const Okteta::Byte byte = byteArrayModel->byte(i);
104 
105         switch (inputByteIndex)
106         {
107         case InputByteIndex::First:
108             // bits 7..3
109             textStream << base32EncodeMap[(byte >> 3)];
110             // bits 2..0 -> 4..2 for next
111             bitsFromLastByte = (byte & 0x7) << 2;
112             inputByteIndex = InputByteIndex::Second;
113             break;
114         case InputByteIndex::Second:
115             // from last and bits 7..6 as 1..0 from this
116             textStream << base32EncodeMap[(bitsFromLastByte | byte >> 6)];
117             // bits 5..1 as 4..0
118             textStream << base32EncodeMap[(byte & 0x3E) >> 1];
119             // bits 0 -> 4 for next
120             bitsFromLastByte = (byte & 0x1) << 4;
121             inputByteIndex = InputByteIndex::Third;
122             break;
123         case InputByteIndex::Third:
124             // from last and bits 7..4 as 3..0 from this
125             textStream << base32EncodeMap[(bitsFromLastByte | byte >> 4)];
126             // bits 3..0 -> 4..1 for next
127             bitsFromLastByte = (byte & 0xF) << 1;
128             inputByteIndex = InputByteIndex::Fourth;
129             break;
130         case InputByteIndex::Fourth:
131             // from last and bit 7 as 0 from this
132             textStream << base32EncodeMap[(bitsFromLastByte | byte >> 7)];
133             // bits 6..2 as 4..0
134             textStream << base32EncodeMap[(byte & 0x7C) >> 2];
135             // bits 1..0 -> 4..3 for next
136             bitsFromLastByte = (byte & 0x3) << 3;
137             inputByteIndex = InputByteIndex::Fifth;
138             break;
139         case InputByteIndex::Fifth:
140             // from last and bits 7..5 as 2..0 from this
141             textStream << base32EncodeMap[(bitsFromLastByte | byte >> 5)];
142             // bits 4..0
143             textStream << base32EncodeMap[(byte & 0x1F)];
144             inputByteIndex = InputByteIndex::First;
145             ++outputGroupsPerLine;
146             if (outputGroupsPerLine >= maxOutputGroupsPerLine && i < range.end()) {
147                 textStream << "\r\n";
148                 outputGroupsPerLine = 0;
149             }
150             break;
151         }
152     }
153 
154     const bool hasBitsLeft = (inputByteIndex != InputByteIndex::First);
155     if (hasBitsLeft) {
156         textStream << base32EncodeMap[bitsFromLastByte]
157                    << base32Padding(inputByteIndex);
158     }
159 
160     return success;
161 }
162 
163 }
164