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 */