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"