File indexing completed on 2025-01-05 05:23:52

0001 /*
0002     This file is part of the Okteta Kasten module, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 2007-2008 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 "bytearraysourcecodestreamencoder.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 primitiveDataTypeCount =
0025     static_cast<int>(Kasten::SourceCodeStreamEncoderSettings::PrimitiveDataType::_Count);
0026 static const std::array<QString, primitiveDataTypeCount> primitiveDataTypeConfigValueList = {
0027     QStringLiteral("Char"),
0028     QStringLiteral("UnsignedChar"),
0029     QStringLiteral("Short"),
0030     QStringLiteral("UnsignedShort"),
0031     QStringLiteral("Integer"),
0032     QStringLiteral("UnsignedInteger"),
0033     QStringLiteral("Float"),
0034     QStringLiteral("Double"),
0035 };
0036 
0037 
0038 template <>
0039 inline Kasten::SourceCodeStreamEncoderSettings::PrimitiveDataType KConfigGroup::readEntry(const char *key,
0040                                                                                    const Kasten::SourceCodeStreamEncoderSettings::PrimitiveDataType &defaultValue) const
0041 {
0042     const QString entry = readEntry(key, QString());
0043 
0044     auto it = std::find(primitiveDataTypeConfigValueList.cbegin(), primitiveDataTypeConfigValueList.cend(), entry);
0045     if (it == primitiveDataTypeConfigValueList.cend()) {
0046         return defaultValue;
0047     }
0048 
0049     const int listIndex = std::distance(primitiveDataTypeConfigValueList.cbegin(), it);
0050     return static_cast<Kasten::SourceCodeStreamEncoderSettings::PrimitiveDataType>(listIndex);
0051 }
0052 
0053 template <>
0054 inline void KConfigGroup::writeEntry(const char *key,
0055                                      const Kasten::SourceCodeStreamEncoderSettings::PrimitiveDataType &value,
0056                                      KConfigBase::WriteConfigFlags flags)
0057 {
0058     const int listIndex = static_cast<int>(value);
0059     writeEntry(key, primitiveDataTypeConfigValueList[listIndex], flags);
0060 }
0061 
0062 namespace Kasten {
0063 
0064 const QString SourceCodeStreamEncoderSettings::DefaultVariableName = QStringLiteral("array");
0065 
0066 static constexpr  const char* PrimitiveDataTypeName[] = {
0067     "char",
0068     "unsigned char",
0069     "short",
0070     "unsigned short",
0071     "int",
0072     "unsigned int",
0073     "float",
0074     "double"
0075 };
0076 
0077 static constexpr int SizeOfPrimitiveDataType[] =
0078 {
0079     sizeof(char),
0080     sizeof(unsigned char),
0081     sizeof(short),
0082     sizeof(unsigned short),
0083     sizeof(int),
0084     sizeof(unsigned int),
0085     sizeof(float),
0086     sizeof(double)
0087 };
0088 
0089 static constexpr int NoOfPrimitiveDataTypes = 8;
0090 
0091 inline QString decimalFormattedNumberPlaceHolder() { return QStringLiteral("%1"); }
0092 inline QString hexadecimalFormattedNumberPlaceHolder() { return QStringLiteral("0x%1"); }
0093 
0094 SourceCodeStreamEncoderSettings::SourceCodeStreamEncoderSettings() = default;
0095 
0096 bool SourceCodeStreamEncoderSettings::operator==(const SourceCodeStreamEncoderSettings& other) const
0097 {
0098     return
0099         (variableName == other.variableName) &&
0100         (dataType == other.dataType) &&
0101         (elementsPerLine == other.elementsPerLine) &&
0102         (unsignedAsHexadecimal == other.unsignedAsHexadecimal);
0103 }
0104 
0105 void SourceCodeStreamEncoderSettings::loadConfig(const KConfigGroup& configGroup)
0106 {
0107     variableName = configGroup.readEntry(VariableNameConfigKey, DefaultVariableName);
0108     dataType = configGroup.readEntry(DataTypeConfigKey, DefaultDataType);
0109     elementsPerLine = configGroup.readEntry(ElementsPerLineConfigKey, DefaultElementsPerLine);
0110     unsignedAsHexadecimal = configGroup.readEntry(UnsignedAsHexadecimalConfigKey, DefaultUnsignedAsHexadecimal);
0111 }
0112 
0113 void SourceCodeStreamEncoderSettings::saveConfig(KConfigGroup& configGroup) const
0114 {
0115     configGroup.writeEntry(VariableNameConfigKey, variableName);
0116     configGroup.writeEntry(DataTypeConfigKey, dataType);
0117     configGroup.writeEntry(ElementsPerLineConfigKey, elementsPerLine);
0118     configGroup.writeEntry(UnsignedAsHexadecimalConfigKey, unsignedAsHexadecimal);
0119 }
0120 
0121 ByteArraySourceCodeStreamEncoder::ByteArraySourceCodeStreamEncoder()
0122     : AbstractByteArrayStreamEncoder(i18nc("name of the encoding target", "C Array"), QStringLiteral("text/x-csrc"))
0123 {
0124     const KConfigGroup configGroup(KSharedConfig::openConfig(), ConfigGroupId);
0125     mSettings.loadConfig(configGroup);
0126 }
0127 
0128 ByteArraySourceCodeStreamEncoder::~ByteArraySourceCodeStreamEncoder() = default;
0129 
0130 const char* const* ByteArraySourceCodeStreamEncoder::dataTypeNames() const { return PrimitiveDataTypeName; }
0131 int ByteArraySourceCodeStreamEncoder::dataTypesCount() const { return NoOfPrimitiveDataTypes; }
0132 
0133 void ByteArraySourceCodeStreamEncoder::setSettings(const SourceCodeStreamEncoderSettings& settings)
0134 {
0135     if (mSettings == settings) {
0136         return;
0137     }
0138 
0139     mSettings = settings;
0140     KConfigGroup configGroup(KSharedConfig::openConfig(), ConfigGroupId);
0141     mSettings.saveConfig(configGroup);
0142     Q_EMIT settingsChanged();
0143 }
0144 
0145 bool ByteArraySourceCodeStreamEncoder::encodeDataToStream(QIODevice* device,
0146                                                           const ByteArrayView* byteArrayView,
0147                                                           const Okteta::AbstractByteArrayModel* byteArrayModel,
0148                                                           const Okteta::AddressRange& range)
0149 {
0150     Q_UNUSED(byteArrayView)
0151 
0152     bool success = true;
0153 
0154     // encode
0155     QTextStream textStream(device);
0156 
0157 //     textStream << "// from File , selection \n";
0158 
0159     const int size = range.width();
0160     const int dataTypeSize = SizeOfPrimitiveDataType[static_cast<int>(mSettings.dataType)];
0161     const int sizeOfArray = (size + dataTypeSize - 1) / dataTypeSize;
0162 
0163     textStream << "const " << QLatin1String(PrimitiveDataTypeName[static_cast<int>(mSettings.dataType)]) << ' '
0164                << mSettings.variableName << '[' << sizeOfArray << "] =" << Qt::endl
0165                << '{' << Qt::endl;
0166 
0167     int elementAddedOnLine = 0;
0168     for (Okteta::Address i = range.start(); i <= range.end(); i += dataTypeSize) {
0169         if (elementAddedOnLine == 0) {
0170             textStream << "   "; // just 3, one space before every datum
0171         }
0172         textStream << ' ' << printFormatted(byteArrayModel, i, range.end() - i + 1);
0173         if (i + dataTypeSize <= range.end()) {
0174             textStream << ',';
0175         }
0176 
0177         if (++elementAddedOnLine >= mSettings.elementsPerLine) {
0178             textStream << Qt::endl;
0179             elementAddedOnLine = 0;
0180         }
0181     }
0182 
0183     if (elementAddedOnLine > 0) {
0184         textStream << Qt::endl;
0185     }
0186 
0187     textStream << "};" << Qt::endl;
0188 
0189     return success;
0190 }
0191 
0192 QString ByteArraySourceCodeStreamEncoder::printFormatted(const Okteta::AbstractByteArrayModel* byteArrayModel, Okteta::Address offset,
0193                                                          unsigned int dataSize) const
0194 {
0195     QString result;
0196     switch (mSettings.dataType)
0197     {
0198     case SourceCodeStreamEncoderSettings::PrimitiveDataType::Char:
0199     {
0200         char e = 0;
0201         byteArrayModel->copyTo(reinterpret_cast<Okteta::Byte*>(&e), offset, qMin<size_t>(sizeof(e), dataSize));
0202         constexpr int fieldWidth = 4;
0203         result = decimalFormattedNumberPlaceHolder().arg((int)e, fieldWidth);
0204         break;
0205     }
0206     case SourceCodeStreamEncoderSettings::PrimitiveDataType::UnsignedChar:
0207     {
0208         unsigned char e = 0;
0209         byteArrayModel->copyTo(reinterpret_cast<Okteta::Byte*>(&e), offset, qMin(uint(sizeof(e)), dataSize));
0210         const int base = mSettings.unsignedAsHexadecimal ? 16 : 10;
0211         const int fieldWidth = mSettings.unsignedAsHexadecimal ? 2 : 3;
0212         const QString formattedNumberPlaceHolder = mSettings.unsignedAsHexadecimal ?
0213                                                    hexadecimalFormattedNumberPlaceHolder() : decimalFormattedNumberPlaceHolder();
0214         const QChar stuffChar = QLatin1Char(mSettings.unsignedAsHexadecimal ? '0' : ' ');
0215         result = formattedNumberPlaceHolder.arg(e, fieldWidth, base, stuffChar);
0216         break;
0217     }
0218     case SourceCodeStreamEncoderSettings::PrimitiveDataType::Short:
0219     {
0220         short e = 0;
0221         byteArrayModel->copyTo(reinterpret_cast<Okteta::Byte*>(&e), offset, qMin(uint(sizeof(e)), dataSize));
0222         constexpr int fieldWidth = 6;
0223         result = decimalFormattedNumberPlaceHolder().arg(e, fieldWidth);
0224         break;
0225     }
0226     case SourceCodeStreamEncoderSettings::PrimitiveDataType::UnsignedShort:
0227     {
0228         unsigned short e = 0;
0229         byteArrayModel->copyTo(reinterpret_cast<Okteta::Byte*>(&e), offset, qMin(uint(sizeof(e)), dataSize));
0230         const int base = mSettings.unsignedAsHexadecimal ? 16 : 10;
0231         const int fieldWidth = mSettings.unsignedAsHexadecimal ? 4 : 5;
0232         const QString formattedNumberPlaceHolder = mSettings.unsignedAsHexadecimal ?
0233                                                    hexadecimalFormattedNumberPlaceHolder() : decimalFormattedNumberPlaceHolder();
0234         const QChar stuffChar = QLatin1Char(mSettings.unsignedAsHexadecimal ? '0' : ' ');
0235         result = formattedNumberPlaceHolder.arg(e, fieldWidth, base, stuffChar);
0236         break;
0237     }
0238     case SourceCodeStreamEncoderSettings::PrimitiveDataType::Integer:
0239     {
0240         int e = 0;
0241         byteArrayModel->copyTo(reinterpret_cast<Okteta::Byte*>(&e), offset, qMin(uint(sizeof(e)), dataSize));
0242         constexpr int fieldWidth = 11;
0243         result = decimalFormattedNumberPlaceHolder().arg(e, fieldWidth);
0244         break;
0245     }
0246     case SourceCodeStreamEncoderSettings::PrimitiveDataType::UnsignedInteger:
0247     {
0248         unsigned int e = 0;
0249         byteArrayModel->copyTo(reinterpret_cast<Okteta::Byte*>(&e), offset, qMin(uint(sizeof(e)), dataSize));
0250         const int base = mSettings.unsignedAsHexadecimal ? 16 : 10;
0251         const int fieldWidth = mSettings.unsignedAsHexadecimal ? 8 : 10;
0252         const QString formattedNumberPlaceHolder = mSettings.unsignedAsHexadecimal ?
0253                                                    hexadecimalFormattedNumberPlaceHolder() : decimalFormattedNumberPlaceHolder();
0254         const QChar stuffChar = QLatin1Char(mSettings.unsignedAsHexadecimal ? '0' : ' ');
0255         result = formattedNumberPlaceHolder.arg(e, fieldWidth, base, stuffChar);
0256         break;
0257     }
0258     case SourceCodeStreamEncoderSettings::PrimitiveDataType::Float:
0259     {
0260         float e = 0;
0261         byteArrayModel->copyTo(reinterpret_cast<Okteta::Byte*>(&e), offset, qMin(uint(sizeof(e)), dataSize));
0262         constexpr int fieldWidth = 13;
0263         result = decimalFormattedNumberPlaceHolder().arg(e, fieldWidth);
0264         break;
0265     }
0266     case SourceCodeStreamEncoderSettings::PrimitiveDataType::Double:
0267     {
0268         double e = 0;
0269         byteArrayModel->copyTo(reinterpret_cast<Okteta::Byte*>(&e), offset, qMin(uint(sizeof(e)), dataSize));
0270         constexpr int fieldWidth = 13;
0271         result = decimalFormattedNumberPlaceHolder().arg(e, fieldWidth);
0272         break;
0273     }
0274     case SourceCodeStreamEncoderSettings::PrimitiveDataType::_Count:
0275         // dummy entry to avoid compiler warning -Wswitch, can this be avoided?
0276         break;
0277     }
0278 
0279     return result;
0280 }
0281 
0282 }
0283 
0284 #include "moc_bytearraysourcecodestreamencoder.cpp"