File indexing completed on 2025-01-05 05:23:51
0001 /* 0002 This file is part of the Okteta Kasten module, made within the KDE community. 0003 0004 SPDX-FileCopyrightText: 2010 Friedrich W. H. Kossebau <kossebau@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0007 */ 0008 0009 #include "bytearraybase32streamencoder.hpp" 0010 0011 // Okteta core 0012 #include <Okteta/AbstractByteArrayModel> 0013 // KF 0014 #include <KConfigGroup> 0015 #include <KSharedConfig> 0016 #include <KLocalizedString> 0017 // Qt 0018 #include <QTextStream> 0019 // Std 0020 #include <array> 0021 #include <algorithm> 0022 #include <iterator> 0023 0024 static constexpr int encodingTypeCount = 0025 static_cast<int>(Kasten::Base32StreamEncoderSettings::EncodingType::_Count); 0026 static const std::array<QString, encodingTypeCount> encodingTypeConfigValueList = { 0027 QStringLiteral("Classic"), 0028 QStringLiteral("base32hex"), 0029 QStringLiteral("z-base-32"), 0030 }; 0031 0032 template <> 0033 inline Kasten::Base32StreamEncoderSettings::EncodingType 0034 KConfigGroup::readEntry(const char *key, 0035 const Kasten::Base32StreamEncoderSettings::EncodingType &defaultValue) const 0036 { 0037 const QString entry = readEntry(key, QString()); 0038 0039 auto it = std::find(encodingTypeConfigValueList.cbegin(), encodingTypeConfigValueList.cend(), entry); 0040 if (it == encodingTypeConfigValueList.cend()) { 0041 return defaultValue; 0042 } 0043 0044 const int listIndex = std::distance(encodingTypeConfigValueList.cbegin(), it); 0045 return static_cast<Kasten::Base32StreamEncoderSettings::EncodingType>(listIndex); 0046 } 0047 0048 template <> 0049 inline void KConfigGroup::writeEntry(const char *key, 0050 const Kasten::Base32StreamEncoderSettings::EncodingType &value, 0051 KConfigBase::WriteConfigFlags flags) 0052 { 0053 const int listIndex = static_cast<int>(value); 0054 writeEntry(key, encodingTypeConfigValueList[listIndex], flags); 0055 } 0056 0057 namespace Kasten { 0058 0059 static constexpr char base32ClassicEncodeMap[32] = 0060 { 0061 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 0062 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 0063 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 0064 'Y', 'Z', '2', '3', '4', '5', '6', '7' 0065 }; 0066 static constexpr char base32HexEncodeMap[32] = 0067 { 0068 '0', '1', '2', '3', '4', '5', '6', '7', 0069 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 0070 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 0071 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V' 0072 }; 0073 static constexpr char base32ZHexEncodeMap[32] = 0074 { 0075 'y', 'b', 'n', 'd', 'r', 'f', 'g', '8', 0076 'e', 'j', 'k', 'm', 'c', 'p', 'q', 'x', 0077 'o', 't', '1', 'u', 'w', 'i', 's', 'z', 0078 'a', '3', '4', '5', 'h', '7', '6', '9' 0079 }; 0080 0081 static constexpr const char* base32PaddingData[4] = 0082 { 0083 "======", 0084 "====", 0085 "===", 0086 "=" 0087 }; 0088 0089 static inline constexpr const char* base32Padding(ByteArrayBase32StreamEncoder::InputByteIndex index) 0090 { 0091 return base32PaddingData[static_cast<int>(index) - 1]; 0092 } 0093 static inline constexpr const char* noPadding(ByteArrayBase32StreamEncoder::InputByteIndex /*index*/) 0094 { 0095 return ""; 0096 } 0097 0098 struct Base32EncodingData 0099 { 0100 const char* const encodeMap; 0101 const char* (* padding)(ByteArrayBase32StreamEncoder::InputByteIndex); 0102 }; 0103 0104 static constexpr Base32EncodingData 0105 base32EncodingData[3] = 0106 { 0107 {base32ClassicEncodeMap, &base32Padding}, 0108 {base32HexEncodeMap, &base32Padding}, 0109 {base32ZHexEncodeMap, &noPadding} 0110 }; 0111 0112 Base32StreamEncoderSettings::Base32StreamEncoderSettings() = default; 0113 0114 bool Base32StreamEncoderSettings::operator==(const Base32StreamEncoderSettings& other) const 0115 { 0116 return (encodingType == other.encodingType); 0117 } 0118 0119 void Base32StreamEncoderSettings::loadConfig(const KConfigGroup& configGroup) 0120 { 0121 encodingType = configGroup.readEntry(EncodingTypeConfigKey, DefaultEncodingType); 0122 } 0123 0124 void Base32StreamEncoderSettings::saveConfig(KConfigGroup& configGroup) const 0125 { 0126 configGroup.writeEntry(EncodingTypeConfigKey, encodingType); 0127 } 0128 0129 0130 ByteArrayBase32StreamEncoder::ByteArrayBase32StreamEncoder() 0131 : AbstractByteArrayStreamEncoder(i18nc("name of the encoding target", "Base32"), QStringLiteral("text/x-base32")) 0132 { 0133 const KConfigGroup configGroup(KSharedConfig::openConfig(), ConfigGroupId); 0134 mSettings.loadConfig(configGroup); 0135 } 0136 0137 ByteArrayBase32StreamEncoder::~ByteArrayBase32StreamEncoder() = default; 0138 0139 0140 void ByteArrayBase32StreamEncoder::setSettings(const Base32StreamEncoderSettings& settings) 0141 { 0142 if (mSettings == settings) { 0143 return; 0144 } 0145 0146 mSettings = settings; 0147 KConfigGroup configGroup(KSharedConfig::openConfig(), ConfigGroupId); 0148 mSettings.saveConfig(configGroup); 0149 Q_EMIT settingsChanged(); 0150 } 0151 0152 bool ByteArrayBase32StreamEncoder::encodeDataToStream(QIODevice* device, 0153 const ByteArrayView* byteArrayView, 0154 const Okteta::AbstractByteArrayModel* byteArrayModel, 0155 const Okteta::AddressRange& range) 0156 { 0157 Q_UNUSED(byteArrayView); 0158 0159 bool success = true; 0160 0161 // encode 0162 QTextStream textStream(device); 0163 0164 // prepare 0165 const auto& encodingTypeData = base32EncodingData[static_cast<int>(mSettings.encodingType)]; 0166 const char* const base32EncodeMap = encodingTypeData.encodeMap; 0167 const char* (* base32Padding)(InputByteIndex) = encodingTypeData.padding; 0168 0169 InputByteIndex inputByteIndex = InputByteIndex::First; 0170 int outputGroupsPerLine = 0; 0171 unsigned char bitsFromLastByte; 0172 0173 for (Okteta::Address i = range.start(); i <= range.end(); ++i) { 0174 const Okteta::Byte byte = byteArrayModel->byte(i); 0175 0176 switch (inputByteIndex) 0177 { 0178 case InputByteIndex::First: 0179 // bits 7..3 0180 textStream << base32EncodeMap[(byte >> 3)]; 0181 // bits 2..0 -> 4..2 for next 0182 bitsFromLastByte = (byte & 0x7) << 2; 0183 inputByteIndex = InputByteIndex::Second; 0184 break; 0185 case InputByteIndex::Second: 0186 // from last and bits 7..6 as 1..0 from this 0187 textStream << base32EncodeMap[(bitsFromLastByte | byte >> 6)]; 0188 // bits 5..1 as 4..0 0189 textStream << base32EncodeMap[(byte & 0x3E) >> 1]; 0190 // bits 0 -> 4 for next 0191 bitsFromLastByte = (byte & 0x1) << 4; 0192 inputByteIndex = InputByteIndex::Third; 0193 break; 0194 case InputByteIndex::Third: 0195 // from last and bits 7..4 as 3..0 from this 0196 textStream << base32EncodeMap[(bitsFromLastByte | byte >> 4)]; 0197 // bits 3..0 -> 4..1 for next 0198 bitsFromLastByte = (byte & 0xF) << 1; 0199 inputByteIndex = InputByteIndex::Fourth; 0200 break; 0201 case InputByteIndex::Fourth: 0202 // from last and bit 7 as 0 from this 0203 textStream << base32EncodeMap[(bitsFromLastByte | byte >> 7)]; 0204 // bits 6..2 as 4..0 0205 textStream << base32EncodeMap[(byte & 0x7C) >> 2]; 0206 // bits 1..0 -> 4..3 for next 0207 bitsFromLastByte = (byte & 0x3) << 3; 0208 inputByteIndex = InputByteIndex::Fifth; 0209 break; 0210 case InputByteIndex::Fifth: 0211 // from last and bits 7..5 as 2..0 from this 0212 textStream << base32EncodeMap[(bitsFromLastByte | byte >> 5)]; 0213 // bits 4..0 0214 textStream << base32EncodeMap[(byte & 0x1F)]; 0215 inputByteIndex = InputByteIndex::First; 0216 ++outputGroupsPerLine; 0217 if (outputGroupsPerLine >= maxOutputGroupsPerLine && i < range.end()) { 0218 textStream << "\r\n"; 0219 outputGroupsPerLine = 0; 0220 } 0221 break; 0222 } 0223 } 0224 0225 const bool hasBitsLeft = (inputByteIndex != InputByteIndex::First); 0226 if (hasBitsLeft) { 0227 textStream << base32EncodeMap[bitsFromLastByte] 0228 << base32Padding(inputByteIndex); 0229 } 0230 0231 return success; 0232 } 0233 0234 } 0235 0236 #include "moc_bytearraybase32streamencoder.cpp"