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 }