File indexing completed on 2024-05-26 05:56:49

0001 /*
0002     This file is part of the Okteta Kasten Framework, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 2009, 2010, 2011 Alex Richardson <alex.richardson@gmx.de>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #ifndef KASTEN_ALLPRIMITIVETYPES_HPP
0010 #define KASTEN_ALLPRIMITIVETYPES_HPP
0011 
0012 #include <QSysInfo>
0013 #include <QtEndian>
0014 
0015 #include <size.hpp>
0016 #include <Okteta/Address>
0017 #include <Okteta/AbstractByteArrayModel>
0018 
0019 #include "datatypes/datainformationbase.hpp"
0020 
0021 namespace Okteta {
0022 class AbstractByteArrayModel;
0023 }
0024 
0025 #ifdef Q_CC_GNU
0026 #define PACKED_STRUCT __attribute__((packed, aligned(8)))
0027 #else
0028 #define PACKED_STRUCT
0029 #endif
0030 
0031 /** Ensures that when used in a union the uint8 value will be equal to the lowest bits of the uint32 value
0032  * This means we need to add padding equal to 8-sizeof(T) before the value in the big endian case.
0033  * On little endian padding gets added at the end (not strictly necessary)
0034  */
0035 template <typename T, int padCount>
0036 struct EndianIndependentBase
0037 {
0038 #if Q_BYTE_ORDER == Q_BIG_ENDIAN
0039     char padding[padCount];
0040 #endif
0041     T value;
0042 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
0043     char padding[padCount];
0044 #endif
0045 } PACKED_STRUCT;
0046 template <typename T>
0047 struct EndianIndependentBase<T, 0>
0048 {
0049     T value;
0050 };
0051 
0052 template <typename T>
0053 struct EndianIndependent : public EndianIndependentBase<T
0054         , 8 - sizeof(T)>
0055 {
0056 };
0057 
0058 /** This union holds the value of one primitive datatype. Maximum size of a datatype is currently 64 bits.
0059  *  It has methods for reading and writing from @c Okteta::AbstractByteArrayModel */
0060 union AllPrimitiveTypes
0061 {
0062 private:
0063     EndianIndependent<qint8> _byte;
0064     EndianIndependent<quint8> _ubyte;
0065     EndianIndependent<qint16> _short;
0066     EndianIndependent<quint16> _ushort;
0067     EndianIndependent<qint32> _int;
0068     EndianIndependent<quint32> _uint;
0069     EndianIndependent<qint64> _long;
0070     EndianIndependent<quint64> _ulong;
0071     EndianIndependent<float> _float;
0072     EndianIndependent<double> _double;
0073 
0074 public:
0075     qint8 allBytes[8];
0076     inline AllPrimitiveTypes() { _ulong.value = 0; }
0077     inline AllPrimitiveTypes(const AllPrimitiveTypes &a) { _ulong.value = a._ulong.value; }
0078     inline AllPrimitiveTypes(quint64 val) { _ulong.value = val; }
0079     inline AllPrimitiveTypes(qint64 val) { _long.value = val; }
0080     // set all to zero first with smaller data types
0081     inline AllPrimitiveTypes(qint32 val) { _long.value = (val < 0 ? -1 : 0); _int.value = val; }
0082     inline AllPrimitiveTypes(quint32 val) { _ulong.value = 0; _uint.value = val; }
0083     inline AllPrimitiveTypes(qint16 val) { _long.value = (val < 0 ? -1 : 0);  _short.value = val; }
0084     inline AllPrimitiveTypes(quint16 val) { _ulong.value = 0; _ushort.value = val; }
0085     inline AllPrimitiveTypes(qint8 val) { _long.value = (val < 0 ? -1 : 0); _byte.value = val; }
0086     inline AllPrimitiveTypes(quint8 val) { _ulong.value = 0; _ubyte.value = val; }
0087     inline AllPrimitiveTypes(float val) { _ulong.value = 0; _float.value = val; }
0088     inline AllPrimitiveTypes(double val) { _double.value = val; }
0089     inline ~AllPrimitiveTypes() = default;
0090 
0091     inline AllPrimitiveTypes& operator=(const AllPrimitiveTypes &a)
0092     { _ulong.value = a._ulong.value; return *this; }
0093     inline bool operator!=(AllPrimitiveTypes other) const
0094     {
0095         return _ulong.value != other._ulong.value;
0096     }
0097     inline bool operator==(AllPrimitiveTypes other) const
0098     {
0099         return _ulong.value == other._ulong.value;
0100     }
0101     /** Not useful, but needed so we can store this in a QMap */
0102     inline bool operator<(AllPrimitiveTypes other) const
0103     {
0104         return _ulong.value < other._ulong.value;
0105     }
0106     /** Writes given number of bits to @p out.
0107      *  If the value of this union is not equal to @p newValue it is set to @p newValue.
0108      *  @p bitOffset is changed in this method so it does not have to be handled later.
0109      *  There is no need to write an optimised version of this method for reading complete bytes
0110      *  since this is already handled internally.
0111      *
0112      *  On failure value of this union is set to @c 0.
0113      *
0114      *  @param bitCount the number of bits to read
0115      *  @param newValue the new value of this union
0116      *  @param out the byte array the value is read from
0117      *  @param byteOrder the byteOrder used for reading values
0118      *  @param address the address in @p out
0119      *  @param bitsRemaining remaining number of bits remaining in @p input
0120      *  @param bitOffset the bit to start at in the first byte
0121      *  @return @c true on success, @c false otherwise
0122      */
0123     bool writeBits(quint8 bitCount, AllPrimitiveTypes newValue,
0124                    Okteta::AbstractByteArrayModel* out, QSysInfo::Endian byteOrder,
0125                    Okteta::Address address, BitCount64 bitsRemaining, quint8 * const bitOffset);
0126     /** Reads given number of bits from @p input and sets value of this union to
0127      *  the new value.
0128      *  @p bitOffset is changed in this method so it does not have to be handled later.
0129      *  There is no need to write an optimised version of this method for reading complete bytes
0130      *  since this is already handled internally.
0131      *
0132      *  On failure value of this union is set to @c 0.
0133      *
0134      *  @param bitCount the number of bits to read
0135      *  @param input the byte array the value is read from
0136      *  @param byteOrder the byteOrder used for reading values
0137      *  @param address the address in @p input
0138      *  @param bitsRemaining number of bytes remaining in @p input
0139      *  @param bitOffset the bit to start at in the first byte
0140      *  @return @c true on success, @c false otherwise
0141      */
0142     bool readBits(quint8 bitCount, const Okteta::AbstractByteArrayModel* input,
0143                   QSysInfo::Endian byteOrder, Okteta::Address address, BitCount64 bitsRemaining,
0144                   quint8 * const bitOffset);
0145 
0146     template <typename T> T value() const;
0147     /**
0148      * Read data of type @p T from the model. Range checking must have been performed before
0149      * @param input the input to read from
0150      * @param address the starting address
0151      * @param endianess the endianess to use when reading
0152      * @param bitOffset the number of bits into the first byte (different depending on endianess)
0153      * @return the read value
0154      */
0155     // TODO bool* ok parameter for when reading from model can cause errors (or exceptions sometime?)
0156     template <typename T> static T readValue(const Okteta::AbstractByteArrayModel* input, Okteta::Address address,
0157         QSysInfo::Endian endianess, quint8 bitOffset);
0158     // TODO add writeValue
0159 
0160 private:
0161     template <int size> static typename QIntegerForSize<size>::Unsigned readValuePrivate(
0162         const Okteta::AbstractByteArrayModel* input, Okteta::Address address,
0163         QSysInfo::Endian endianess, quint8 bitOffset);
0164     template <int size> static typename QIntegerForSize<size>::Unsigned readRawBytes(
0165         const Okteta::AbstractByteArrayModel* input, Okteta::Address address);
0166 
0167     void readDataLittleEndian(quint8 bitCount, const Okteta::AbstractByteArrayModel* input,
0168                               Okteta::Address address, quint8 bo);
0169     void writeDataLittleEndian(quint8 bitCount, AllPrimitiveTypes newValue,
0170                                Okteta::AbstractByteArrayModel* out, Okteta::Address address, quint8 bo) const;
0171 
0172     void readDataBigEndian(quint8 bitCount, const Okteta::AbstractByteArrayModel* input,
0173                            Okteta::Address address, quint8 bo);
0174     void writeDataBigEndian(quint8 bitCount, AllPrimitiveTypes newValue,
0175                             Okteta::AbstractByteArrayModel* out, Okteta::Address address, quint8 bo) const;
0176 
0177     // optimised methods for reading/writing full bytes
0178     void readFullBytes(quint8 byteCount, const Okteta::AbstractByteArrayModel* input,
0179                        QSysInfo::Endian byteOrder, Okteta::Address address);
0180     void writeFullBytes(quint8 byteCount, AllPrimitiveTypes newValue,
0181                         Okteta::AbstractByteArrayModel* out, QSysInfo::Endian byteOrder,
0182                         Okteta::Address address);
0183 };
0184 
0185 template <> inline quint8 AllPrimitiveTypes::value<quint8>() const { return _ubyte.value; }
0186 template <> inline quint16 AllPrimitiveTypes::value<quint16>() const { return _ushort.value; }
0187 template <> inline quint32 AllPrimitiveTypes::value<quint32>() const { return _uint.value; }
0188 template <> inline quint64 AllPrimitiveTypes::value<quint64>() const { return _ulong.value; }
0189 template <> inline qint8 AllPrimitiveTypes::value<qint8>() const { return _byte.value; }
0190 template <> inline qint16 AllPrimitiveTypes::value<qint16>() const { return _short.value; }
0191 template <> inline qint32 AllPrimitiveTypes::value<qint32>() const { return _int.value; }
0192 template <> inline qint64 AllPrimitiveTypes::value<qint64>() const { return _long.value; }
0193 template <> inline float AllPrimitiveTypes::value<float>() const { return _float.value; }
0194 template <> inline double AllPrimitiveTypes::value<double>() const { return _double.value; }
0195 
0196 template <typename T>
0197 inline T AllPrimitiveTypes::readValue(const Okteta::AbstractByteArrayModel* input,
0198                                       Okteta::Address address, QSysInfo::Endian endianess, quint8 bitOffset)
0199 {
0200     // check for out of bounds
0201     Q_ASSERT(BitCount64(input->size() - address) * 8 - bitOffset >= sizeof(T) * 8);
0202     Q_ASSERT(bitOffset < 8);
0203     // this union exists to force unsigned shifts
0204     union {
0205         T value;
0206         typename QIntegerForSizeof<T>::Unsigned unsignedValue;
0207     } u;
0208     u.unsignedValue = readValuePrivate<sizeof(T)>(input, address, endianess, bitOffset);
0209     return u.value;
0210 }
0211 
0212 template <int size>
0213 inline typename QIntegerForSize<size>::Unsigned AllPrimitiveTypes::readValuePrivate(
0214     const Okteta::AbstractByteArrayModel* input, Okteta::Address address,
0215     QSysInfo::Endian endianess, quint8 bitOffset)
0216 {
0217     typename QIntegerForSize<size>::Unsigned unsignedValue = readRawBytes<size>(input, address);
0218     if (endianess != QSysInfo::ByteOrder) {
0219         // swap the byte order if machine endianess does not match requested endianess
0220         unsignedValue = qbswap(unsignedValue);
0221     }
0222     if (Q_UNLIKELY(bitOffset != 0)) {
0223         quint8 lastByte = input->byte(address + size);
0224         // handle the remaining bits
0225         if (endianess == QSysInfo::BigEndian) {
0226             // the coming bits are the least significant, and range from bit (8-bitOffset)..7
0227             unsignedValue <<= bitOffset;
0228             lastByte >>= 8 - bitOffset; // unsigned shift
0229             Q_ASSERT((unsignedValue & lastByte) == 0); // must not overlap
0230             unsignedValue |= lastByte;
0231         } else {
0232             // the coming bits are the most significant bits and range from 0..bitOffset
0233             unsignedValue >>= bitOffset;
0234             // promote lastByte to unsigned T and mask off the interesting bits
0235             typename QIntegerForSize<size>::Unsigned tmp = lastByte & ((1U << bitOffset) - 1);
0236             tmp <<= (size * 8) - bitOffset;
0237             unsignedValue |= tmp;
0238         }
0239     }
0240     return unsignedValue;
0241 }
0242 
0243 template <int size>
0244 inline typename QIntegerForSize<size>::Unsigned AllPrimitiveTypes::readRawBytes(
0245     const Okteta::AbstractByteArrayModel* input, Okteta::Address address)
0246 {
0247     union {
0248         typename QIntegerForSize<size>::Unsigned value;
0249         Okteta::Byte bytes[size];
0250     } buf;
0251     Okteta::Size read = input->copyTo(buf.bytes, address, size);
0252     Q_ASSERT(read == size);
0253     Q_UNUSED(read)
0254     return buf.value;
0255 }
0256 
0257 // specialize it for the case where we only need to read one byte
0258 template <>
0259 inline quint8 AllPrimitiveTypes::readRawBytes<1>(
0260     const Okteta::AbstractByteArrayModel* input, Okteta::Address address)
0261 {
0262     return input->byte(address);
0263 }
0264 
0265 #endif /* KASTEN_ALLPRIMITIVETYPES_HPP */