File indexing completed on 2025-01-05 05:23:47
0001 /* 0002 This file is part of the Okteta Kasten module, made within the KDE community. 0003 0004 SPDX-FileCopyrightText: 2010 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 #include "allprimitivetypes.hpp" 0010 #include "datatypes/primitive/primitivedatainformation.hpp" 0011 0012 #include <Okteta/AbstractByteArrayModel> 0013 0014 Q_STATIC_ASSERT(sizeof(double) == 8); 0015 Q_STATIC_ASSERT(sizeof(float) == 4); 0016 Q_STATIC_ASSERT(sizeof(AllPrimitiveTypes) == 8); 0017 0018 // FIXME this code really needs unit tests! 0019 // TODO optimised methods for *bitOffset == 0 && bitCount % 8 == 0 0020 0021 bool AllPrimitiveTypes::writeBits(quint8 bitCount, AllPrimitiveTypes newValue, 0022 Okteta::AbstractByteArrayModel* out, QSysInfo::Endian byteOrder, Okteta::Address address, 0023 BitCount64 bitsRemaining, quint8* const bitOffset) 0024 { 0025 Q_ASSERT(*bitOffset < 8); 0026 Q_ASSERT(bitCount <= 64); 0027 if (bitsRemaining < bitCount) { 0028 _ulong.value = 0; 0029 *bitOffset = 0; 0030 return false; 0031 } 0032 // set if not 0033 if (_ulong.value != newValue._ulong.value) { 0034 _ulong.value = newValue._ulong.value; 0035 } 0036 0037 if (bitCount % 8 == 0 && *bitOffset == 0) { 0038 // only writing full bytes 0039 writeFullBytes(bitCount / 8, newValue, out, byteOrder, address); 0040 } else { 0041 if (byteOrder == QSysInfo::LittleEndian) { 0042 writeDataLittleEndian(bitCount, newValue, out, address, *bitOffset); 0043 } else if (byteOrder == QSysInfo::BigEndian) { 0044 writeDataBigEndian(bitCount, newValue, out, address, *bitOffset); 0045 } else { 0046 Q_ASSERT(false); 0047 return false; 0048 } 0049 *bitOffset = (*bitOffset + bitCount) % 8; 0050 } 0051 return true; 0052 } 0053 0054 bool AllPrimitiveTypes::readBits(quint8 bitCount, const Okteta::AbstractByteArrayModel* input, 0055 QSysInfo::Endian byteOrder, Okteta::Address address, BitCount64 bitsRemaining, 0056 quint8* const bitOffset) 0057 { 0058 Q_ASSERT(bitCount <= 64); 0059 Q_ASSERT(*bitOffset < 8); 0060 if (bitsRemaining < bitCount) { 0061 _ulong.value = 0; 0062 *bitOffset = 0; 0063 return false; 0064 } 0065 // set to zero before reading 0066 _ulong.value = 0; 0067 if (bitCount % 8 == 0 && *bitOffset == 0) { 0068 // only reading full bytes 0069 readFullBytes(bitCount / 8, input, byteOrder, address); 0070 } else { 0071 if (byteOrder == QSysInfo::LittleEndian) { 0072 readDataLittleEndian(bitCount, input, address, *bitOffset); 0073 } else if (byteOrder == QSysInfo::BigEndian) { 0074 readDataBigEndian(bitCount, input, address, *bitOffset); 0075 } else { 0076 Q_ASSERT(false); 0077 return false; 0078 } 0079 *bitOffset = (*bitOffset + bitCount) % 8; 0080 } 0081 return true; 0082 } 0083 0084 void AllPrimitiveTypes::readDataLittleEndian(quint8 bitCount, 0085 const Okteta::AbstractByteArrayModel* input, Okteta::Address address, quint8 bo) 0086 { 0087 if (bitCount <= (unsigned) (8 - bo)) { 0088 // fits completely 0089 const quint8 lowerMask = 0xff << bo; // all lower bits are 0 0090 const quint8 higherMask = 0xff >> (8 - (bo + bitCount)); // all higher bits are 0 0091 const quint8 completeMask = lowerMask & higherMask; // region in the middle 0092 const quint8 readByte = input->byte(address); 0093 const quint8 maskedByte = readByte & completeMask; 0094 _ubyte.value = maskedByte >> bo; 0095 } else { 0096 const quint8 firstByteMask = 0xff << bo; 0097 const quint8 firstByte = input->byte(address); 0098 const quint8 firstByteMasked = firstByte & firstByteMask; 0099 _ubyte.value = firstByteMasked >> bo; 0100 // if spans more than this one byte continue 0101 for (uint i = 8; i < bitCount + bo; i += 8) { 0102 quint8 readVal = input->byte(address + (i / 8)); 0103 if (bitCount + bo < i + 8) { 0104 // this is last byte needed, possibly cut off top values 0105 const quint8 missingBits = (bitCount + bo) % 8; 0106 const quint8 mask = (1 << missingBits) - 1; 0107 readVal &= mask; // mask the top few bits 0108 } 0109 // otherwise we need full byte -> nothing to do 0110 // needs cast since otherwise compiler decides to use 32 bit int and top 32 bits get lost 0111 const quint64 shiftedVal = (quint64) readVal << i; 0112 _ulong.value |= shiftedVal >> bo; // move to correct byte 0113 } 0114 } 0115 } 0116 0117 void AllPrimitiveTypes::readDataBigEndian(quint8 bitCount, 0118 const Okteta::AbstractByteArrayModel* input, Okteta::Address address, quint8 bo) 0119 { 0120 if (bitCount <= (unsigned) (8 - bo)) { 0121 const quint8 lowerMask = 0xff << (8 - (bo + bitCount)); 0122 const quint8 higherMask = 0xff >> bo; 0123 const quint8 completeMask = lowerMask & higherMask; 0124 // completeMask maskes the value -> negate it to clear all the bytes 0125 const quint8 readByte = input->byte(address); 0126 const quint8 maskedByte = readByte & completeMask; 0127 _ubyte.value = maskedByte >> (8 - (bo + bitCount)); 0128 } else { 0129 const quint8 firstByteMask = 0xff >> bo; 0130 const quint8 firstByte = input->byte(address); 0131 // needs quint64 since otherwise compiler decides to use 32 bit int when shifting and top 32 bits get lost 0132 const quint64 firstByteMasked = firstByte & firstByteMask; 0133 const quint64 firstByteShifted = firstByteMasked << (bo + bitCount - 8); 0134 _ulong.value = firstByteShifted; 0135 // if spans more than this one byte continue 0136 for (uint i = 8; i < bitCount + bo; i += 8) { 0137 quint8 readVal = input->byte(address + (i / 8)); 0138 if (bitCount + bo < i + 8) { 0139 // this is last byte needed, possibly cut off lower values 0140 const quint8 missingBits = (bo + bitCount) % 8; 0141 const quint8 mask = 0xff << (8 - missingBits); 0142 const quint8 maskedVal = readVal & mask; // cut off lower bits 0143 const quint8 shiftedVal = maskedVal >> (8 - missingBits); 0144 _ulong.value |= shiftedVal; 0145 } else { 0146 // otherwise we need full byte -> nothing to do 0147 // needs cast since otherwise compiler decides to use 32 bit int and top 32 bits get lost 0148 const quint64 shiftedVal = (quint64) readVal << ((bo + bitCount) 0149 - (8 + i)); // move to correct byte 0150 _ulong.value |= shiftedVal; 0151 } 0152 } 0153 } 0154 } 0155 0156 void AllPrimitiveTypes::writeDataLittleEndian(quint8 bitCount, 0157 AllPrimitiveTypes newValue, Okteta::AbstractByteArrayModel* out, 0158 Okteta::Address address, quint8 bo) const 0159 { 0160 if (bitCount <= (unsigned) (8 - bo)) { 0161 // fits completely 0162 const quint8 lowerMask = (1 << bo) - 1; // all lower bits are 1 0163 const quint8 higherMask = 0xff << (bo + bitCount); // all higher bits are 1 0164 const quint8 completeMask = lowerMask | higherMask; // region in the middle is 0 0165 const quint8 readByte = out->byte(address); 0166 const quint8 maskedByte = readByte & completeMask; 0167 const quint8 addedVal = newValue._ubyte.value << bo; 0168 const quint8 newVal = maskedByte | addedVal; 0169 out->setByte(address, newVal); 0170 } else { 0171 const quint8 firstByteMask = (1 << bo) - 1; 0172 const quint8 firstByte = out->byte(address); 0173 const quint8 firstByteMasked = firstByte & firstByteMask; 0174 const quint8 firstAddedVal = (newValue._ubyte.value << bo); 0175 const quint8 firstByteWithValAdded = firstByteMasked | firstAddedVal; 0176 out->setByte(address, firstByteWithValAdded); 0177 // if spans more than this one byte continue 0178 for (uint i = 8; i < bitCount + bo; i += 8) { 0179 const quint8 currentByte = newValue._ulong.value >> (i - bo); 0180 if (bitCount + bo < i + 8) { 0181 const quint8 readVal = out->byte(address + (i / 8)); 0182 // this is last byte needed, possibly cut off bottom 0183 const quint8 missingBits = (bitCount + bo) % 8; 0184 const quint8 mask = 0xff << missingBits; 0185 const quint8 readValMasked = readVal & mask; // remove the bottom values 0186 const quint8 resultingVal = readValMasked | currentByte; 0187 out->setByte(address + (i / 8), resultingVal); 0188 } else { 0189 // otherwise we need full byte -> nothing to do 0190 out->setByte(address + (i / 8), currentByte); 0191 } 0192 } 0193 } 0194 } 0195 0196 void AllPrimitiveTypes::writeDataBigEndian(quint8 bitCount, 0197 AllPrimitiveTypes newValue, Okteta::AbstractByteArrayModel* out, 0198 Okteta::Address address, quint8 bo) const 0199 { 0200 if (bitCount <= (unsigned) (8 - bo)) { 0201 // fits completely 0202 const quint8 lowerMask = 0xff >> (bo + bitCount); // all lower bits are 1 0203 const quint8 higherMask = 0xff << (8 - bo); // all higher bits are 1 0204 const quint8 completeMask = lowerMask | higherMask; // region in the middle is 0 0205 const quint8 readByte = out->byte(address); 0206 const quint8 maskedByte = readByte & completeMask; 0207 const quint8 addedVal = newValue._ubyte.value << (8 - bo - 1); // move to missing area 0208 const quint8 maskedByteWithValueAdded = maskedByte | addedVal; 0209 out->setByte(address, maskedByteWithValueAdded); 0210 } else { 0211 quint8 missingBits = (bitCount + bo) % 8; 0212 missingBits = (missingBits == 0 ? 8 : missingBits); 0213 const quint8 lastAddress = address + ((bo + bitCount) / 8) - (missingBits 0214 > 0 ? 0 : 1); 0215 const quint8 lastByte = out->byte(lastAddress); 0216 const quint8 lastByteMask = (1 << missingBits) - 1; 0217 const quint8 lastByteMasked = lastByte & lastByteMask; // remove the top values 0218 const quint8 lastByteAddedVal = newValue._ubyte.value << (8 - missingBits); 0219 const quint8 lastByteWithValAdded = lastByteMasked | lastByteAddedVal; 0220 out->setByte(lastAddress, lastByteWithValAdded); 0221 for (int currAddress = lastAddress - 1; currAddress >= address; currAddress--) { 0222 const quint8 currentByte = out->byte(currAddress); 0223 if (currAddress == address) { 0224 // last byte to read 0225 const quint8 firstByteMask = 0xff << (8 - bo); 0226 const quint8 firstByteMasked = currentByte & firstByteMask; 0227 const quint8 highestByte = newValue._ulong.value 0228 >> (bo + bitCount - 8); 0229 const quint8 firstByteWithValAdded = firstByteMasked | highestByte; 0230 out->setByte(address, firstByteWithValAdded); 0231 } else { 0232 const int bytesNotToShift = 1 + (lastAddress - address) 0233 - (lastAddress - currAddress); 0234 const quint8 thisByteShifted = newValue._ulong.value >> (bo + bitCount 0235 - (8 * bytesNotToShift)); 0236 out->setByte(currAddress, thisByteShifted); 0237 } 0238 } 0239 } 0240 } 0241 0242 void AllPrimitiveTypes::readFullBytes(quint8 byteCount, const Okteta::AbstractByteArrayModel* input, 0243 QSysInfo::Endian byteOrder, Okteta::Address address) 0244 { 0245 Q_ASSERT(byteCount <= 8); 0246 // always use unsigned value 0247 for (int i = 0; i < byteCount; i++) { 0248 int index = (byteOrder == QSysInfo::LittleEndian) ? i : ((byteCount - 1) - i); 0249 Okteta::Byte readByte = input->byte(address + i); 0250 allBytes[index] = readByte; 0251 } 0252 } 0253 0254 void AllPrimitiveTypes::writeFullBytes(quint8 byteCount, AllPrimitiveTypes newValue, 0255 Okteta::AbstractByteArrayModel* out, QSysInfo::Endian byteOrder, Okteta::Address address) 0256 { 0257 Q_ASSERT(byteCount <= 8); 0258 for (int i = 0; i < byteCount; ++i) { 0259 int index = (byteOrder == QSysInfo::LittleEndian) ? i : ((byteCount - 1) - i); 0260 out->setByte(address + i, newValue.allBytes[index]); 0261 } 0262 }