| /** @file hei_bit_string.cpp |
| * @brief BitString and BitStringBuffer class definitions |
| */ |
| |
| #include <hei_user_defines.hpp> |
| #include <util/hei_bit_string.hpp> |
| |
| #include <algorithm> |
| |
| namespace libhei |
| { |
| |
| //############################################################################## |
| // BitString class |
| //############################################################################## |
| |
| // number of bits in a uint64_t |
| constexpr uint64_t BitString::UINT64_BIT_LEN = sizeof(uint64_t) * 8; |
| // number of bits in a uint8_t |
| constexpr uint64_t BitString::UINT8_BIT_LEN = sizeof(uint8_t) * 8; |
| |
| //------------------------------------------------------------------------------ |
| |
| uint64_t BitString::getFieldRight(uint64_t i_pos, uint64_t i_len) const |
| { |
| HEI_ASSERT(nullptr != getBufAddr()); // must to have a valid address |
| HEI_ASSERT(0 < i_len); // must have at least one bit |
| HEI_ASSERT(i_len <= UINT64_BIT_LEN); // i_len length must be valid |
| HEI_ASSERT(i_pos + i_len <= getBitLen()); // field must be within range |
| |
| // Get the relative address of this byte and the relative starting position |
| // within the byte. |
| uint64_t relPos = 0; |
| uint8_t* relAddr = getRelativePosition(relPos, i_pos); |
| |
| // Get the length of the target bit field within this byte and the length of |
| // the bit field for any remaining bits. |
| uint64_t bf_len = i_len; |
| uint64_t remain_len = 0; |
| if (UINT8_BIT_LEN < relPos + i_len) |
| { |
| // The target bit field crosses a byte boundary. So truncate the bit |
| // length for this byte and update the remaining length. |
| bf_len = UINT8_BIT_LEN - relPos; |
| remain_len = i_len - bf_len; |
| } |
| |
| // Get the target bit field within this byte (right justified). |
| uint8_t bf = *relAddr; |
| bf <<= relPos; // Mask off uneeded bits on the left side. |
| bf >>= UINT8_BIT_LEN - bf_len; // Right justify the value. |
| |
| // Check for any remaining bits after this target byte. |
| if (0 != remain_len) |
| { |
| // Recursively call this function on the remaining bits and push them |
| // into the right side of the return value. |
| uint64_t val = static_cast<uint64_t>(bf) << remain_len; |
| return val | getFieldRight(i_pos + bf_len, remain_len); |
| } |
| |
| // Nothing more to do. Simply return this bit field. |
| return bf; |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| void BitString::setFieldLeft(uint64_t i_pos, uint64_t i_len, uint64_t i_val) |
| { |
| HEI_ASSERT(nullptr != getBufAddr()); // must to have a valid address |
| HEI_ASSERT(0 < i_len); // must have at least one bit |
| HEI_ASSERT(i_len <= UINT64_BIT_LEN); // i_len length must be valid |
| HEI_ASSERT(i_pos + i_len <= getBitLen()); // field must be within range |
| |
| // Get the relative address of this byte and the relative starting position |
| // within the byte. |
| uint64_t relPos = 0; |
| uint8_t* relAddr = getRelativePosition(relPos, i_pos); |
| |
| // Get the length of the target bit field within this byte and the length of |
| // the bit field for any remaining bits. |
| uint64_t bf_len = i_len; |
| uint64_t remain_len = 0; |
| if (UINT8_BIT_LEN < relPos + i_len) |
| { |
| // The target bit field crosses a byte boundary. So truncate the bit |
| // length for this byte and update the remaining length. |
| bf_len = UINT8_BIT_LEN - relPos; |
| remain_len = i_len - bf_len; |
| } |
| |
| // It is possible there are bits in this byte on either side of the target |
| // bit field that must be preserved. Get the length of each of those bit |
| // fields. |
| uint64_t bf_l_len = relPos; |
| uint64_t bf_r_len = UINT8_BIT_LEN - (bf_l_len + bf_len); |
| |
| // Get the target bit field from the left justified inputed value. |
| uint8_t bf = (i_val >> (UINT64_BIT_LEN - bf_len)) << bf_r_len; |
| |
| // Get the bit fields on either side of the target bit field. |
| uint64_t bf_l_shift = UINT8_BIT_LEN - bf_l_len; |
| uint64_t bf_r_shift = UINT8_BIT_LEN - bf_r_len; |
| |
| uint8_t bf_l = *relAddr; |
| bf_l >>= bf_l_shift; |
| bf_l <<= bf_l_shift; |
| |
| uint8_t bf_r = *relAddr; |
| bf_r <<= bf_r_shift; |
| bf_r >>= bf_r_shift; |
| |
| // Combine all three parts of the byte and write it out to memory. |
| *relAddr = bf_l | bf | bf_r; |
| |
| // Check for any remaining bits after this target byte. |
| if (0 != remain_len) |
| { |
| // Recursively call this function on the remaining bits. |
| setFieldLeft(i_pos + bf_len, remain_len, i_val << bf_len); |
| } |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| void BitString::setPattern(uint64_t i_sPos, uint64_t i_sLen, uint64_t i_pattern, |
| uint64_t i_pLen) |
| { |
| |
| HEI_ASSERT(nullptr != getBufAddr()); // must to have a valid address |
| HEI_ASSERT(0 < i_sLen); // must have at least one bit |
| HEI_ASSERT(i_sPos + i_sLen <= getBitLen()); // field must be within range |
| HEI_ASSERT(0 < i_pLen); // must have at least one bit |
| HEI_ASSERT(i_pLen <= UINT64_BIT_LEN); // i_pLen length must be valid |
| |
| // Get a bit string for the pattern subset (right justified). |
| // Note that we cannot use a BitStringBuffer here because this function |
| // could be used in the constructor of BitStringBuffer, which could causes |
| // an infinite loop. |
| uint8_t a[sizeof(i_pattern)] = {}; |
| BitString bs{sizeof(i_pattern) * 8, a}; |
| bs.setFieldRight(0, i_pLen, i_pattern); |
| |
| // Iterate the range in chunks the size of i_pLen. |
| uint64_t endPos = i_sPos + i_sLen; |
| for (uint64_t pos = i_sPos; pos < endPos; pos += i_pLen) |
| { |
| // The true chunk size is either i_pLen or the leftovers at the end. |
| uint64_t len = std::min(i_pLen, endPos - pos); |
| |
| // Get this chunk's pattern value, truncate (right justified) if needed. |
| uint64_t pattern = bs.getFieldRight(0, len); |
| |
| // Set the pattern in this string. |
| setFieldRight(pos, len, pattern); |
| } |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| void BitString::setString(const BitString& i_sStr, uint64_t i_sPos, |
| uint64_t i_sLen, uint64_t i_dPos) |
| { |
| // Ensure the source parameters are valid. |
| HEI_ASSERT(nullptr != i_sStr.getBufAddr()); |
| HEI_ASSERT(0 < i_sLen); // at least one bit to copy |
| HEI_ASSERT(i_sPos + i_sLen <= i_sStr.getBitLen()); |
| |
| // Ensure the destination has at least one bit available to copy. |
| HEI_ASSERT(nullptr != getBufAddr()); |
| HEI_ASSERT(i_dPos < getBitLen()); |
| |
| // If the source length is greater than the destination length than the |
| // extra source bits are ignored. |
| uint64_t actLen = std::min(i_sLen, getBitLen() - i_dPos); |
| |
| // The bit strings may be in overlapping memory spaces. So we need to copy |
| // the data in the correct direction to prevent overlapping. |
| uint64_t sRelOffset = 0, dRelOffset = 0; |
| uint8_t* sRelAddr = i_sStr.getRelativePosition(sRelOffset, i_sPos); |
| uint8_t* dRelAddr = getRelativePosition(dRelOffset, i_dPos); |
| |
| // Copy the data. |
| if ((dRelAddr == sRelAddr) && (dRelOffset == sRelOffset)) |
| { |
| // Do nothing. The source and destination are the same. |
| } |
| else if ((dRelAddr < sRelAddr) || |
| ((dRelAddr == sRelAddr) && (dRelOffset < sRelOffset))) |
| { |
| // Copy the data forward. |
| for (uint64_t pos = 0; pos < actLen; pos += UINT64_BIT_LEN) |
| { |
| uint64_t len = std::min(actLen - pos, UINT64_BIT_LEN); |
| |
| uint64_t value = i_sStr.getFieldRight(i_sPos + pos, len); |
| setFieldRight(i_dPos + pos, len, value); |
| } |
| } |
| else // Copy the data backwards. |
| { |
| // Get the first position of the last chunk (byte aligned). |
| uint64_t lastPos = ((actLen - 1) / UINT64_BIT_LEN) * UINT64_BIT_LEN; |
| |
| // Start with the last chunk and work backwards. |
| for (int32_t pos = lastPos; 0 <= pos; pos -= UINT64_BIT_LEN) |
| { |
| uint64_t len = std::min(actLen - pos, UINT64_BIT_LEN); |
| uint64_t value = i_sStr.getFieldRight(i_sPos + pos, len); |
| setFieldRight(i_dPos + pos, len, value); |
| } |
| } |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| void BitString::maskString(const BitString& i_mask) |
| { |
| // Get the length of the smallest string. |
| uint64_t actLen = std::min(getBitLen(), i_mask.getBitLen()); |
| |
| for (uint64_t pos = 0; pos < actLen; pos += UINT64_BIT_LEN) |
| { |
| uint64_t len = std::min(actLen - pos, UINT64_BIT_LEN); |
| |
| uint64_t dVal = getFieldRight(pos, len); |
| uint64_t sVal = i_mask.getFieldRight(pos, len); |
| |
| setFieldRight(pos, len, dVal & ~sVal); |
| } |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| bool BitString::isEqual(const BitString& i_str) const |
| { |
| if (getBitLen() != i_str.getBitLen()) |
| return false; // size not equal |
| |
| for (uint64_t pos = 0; pos < getBitLen(); pos += UINT64_BIT_LEN) |
| { |
| uint64_t len = std::min(getBitLen() - pos, UINT64_BIT_LEN); |
| |
| if (getFieldRight(pos, len) != i_str.getFieldRight(pos, len)) |
| return false; // bit strings do not match |
| } |
| |
| return true; // bit strings match |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| bool BitString::isZero() const |
| { |
| for (uint64_t pos = 0; pos < getBitLen(); pos += UINT64_BIT_LEN) |
| { |
| uint64_t len = std::min(getBitLen() - pos, UINT64_BIT_LEN); |
| |
| if (0 != getFieldRight(pos, len)) |
| return false; // something is non-zero |
| } |
| |
| return true; // everything was zero |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| uint64_t BitString::getSetCount(uint64_t i_pos, uint64_t i_len) const |
| { |
| uint64_t endPos = i_pos + i_len; |
| |
| HEI_ASSERT(endPos <= getBitLen()); |
| |
| uint64_t count = 0; |
| |
| for (uint64_t i = i_pos; i < endPos; i++) |
| { |
| if (isBitSet(i)) |
| count++; |
| } |
| |
| return count; |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| BitStringBuffer BitString::operator~() const |
| { |
| BitStringBuffer bsb(getBitLen()); |
| |
| for (uint64_t pos = 0; pos < getBitLen(); pos += UINT64_BIT_LEN) |
| { |
| uint64_t len = std::min(getBitLen() - pos, UINT64_BIT_LEN); |
| |
| uint64_t dVal = getFieldRight(pos, len); |
| |
| bsb.setFieldRight(pos, len, ~dVal); |
| } |
| |
| return bsb; |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| BitStringBuffer BitString::operator&(const BitString& i_bs) const |
| { |
| // Get the length of the smallest string. |
| uint64_t actLen = std::min(getBitLen(), i_bs.getBitLen()); |
| |
| BitStringBuffer bsb(actLen); |
| |
| for (uint64_t pos = 0; pos < actLen; pos += UINT64_BIT_LEN) |
| { |
| uint64_t len = std::min(actLen - pos, UINT64_BIT_LEN); |
| |
| uint64_t dVal = getFieldRight(pos, len); |
| uint64_t sVal = i_bs.getFieldRight(pos, len); |
| |
| bsb.setFieldRight(pos, len, dVal & sVal); |
| } |
| |
| return bsb; |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| BitStringBuffer BitString::operator|(const BitString& i_bs) const |
| { |
| // Get the length of the smallest string. |
| uint64_t actLen = std::min(getBitLen(), i_bs.getBitLen()); |
| |
| BitStringBuffer bsb(actLen); |
| |
| for (uint64_t pos = 0; pos < actLen; pos += UINT64_BIT_LEN) |
| { |
| uint64_t len = std::min(actLen - pos, UINT64_BIT_LEN); |
| |
| uint64_t dVal = getFieldRight(pos, len); |
| uint64_t sVal = i_bs.getFieldRight(pos, len); |
| |
| bsb.setFieldRight(pos, len, dVal | sVal); |
| } |
| |
| return bsb; |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| BitStringBuffer BitString::operator>>(uint64_t i_shift) const |
| { |
| BitStringBuffer bsb(getBitLen()); // default all zeros |
| |
| if (i_shift < getBitLen()) |
| { |
| // bso overlays bsb, containing the shifted offset. |
| BitString bso(bsb.getBitLen() - i_shift, bsb.getBufAddr(), i_shift); |
| |
| // Copy this into bso. |
| bso.setString(*this); |
| } |
| |
| return bsb; |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| BitStringBuffer BitString::operator<<(uint64_t i_shift) const |
| { |
| BitStringBuffer bsb(getBitLen()); // default all zeros |
| |
| if (i_shift < getBitLen()) |
| { |
| // bso overlays *this, containing the shifted offset. |
| BitString bso(this->getBitLen() - i_shift, this->getBufAddr(), i_shift); |
| |
| // Copy bso into bsb. |
| bsb.setString(bso); |
| } |
| |
| return bsb; |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| uint8_t* BitString::getRelativePosition(uint64_t& o_relPos, |
| uint64_t i_absPos) const |
| { |
| HEI_ASSERT(nullptr != getBufAddr()); // must to have a valid address |
| HEI_ASSERT(i_absPos < getBitLen()); // must be a valid position |
| |
| o_relPos = (i_absPos + iv_offset) % UINT8_BIT_LEN; |
| |
| return ((uint8_t*)iv_bufAddr + ((i_absPos + iv_offset) / UINT8_BIT_LEN)); |
| } |
| |
| //############################################################################## |
| // BitStringBuffer class |
| //############################################################################## |
| |
| BitStringBuffer::BitStringBuffer(uint64_t i_bitLen) : |
| BitString(i_bitLen, nullptr) |
| { |
| initBuffer(); |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| BitStringBuffer::~BitStringBuffer() |
| { |
| delete[](uint8_t*) getBufAddr(); |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| BitStringBuffer::BitStringBuffer(const BitString& i_bs) : |
| BitString(i_bs.getBitLen(), nullptr) |
| { |
| initBuffer(); |
| if (!i_bs.isZero()) |
| { |
| setString(i_bs); |
| } |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| BitStringBuffer::BitStringBuffer(const BitStringBuffer& i_bsb) : |
| BitString(i_bsb.getBitLen(), nullptr) |
| { |
| initBuffer(); |
| if (!i_bsb.isZero()) |
| { |
| setString(i_bsb); |
| } |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| BitStringBuffer& BitStringBuffer::operator=(const BitString& i_bs) |
| { |
| // The initBuffer() function will deallocate the buffer as well, however we |
| // also need to deallocate the buffer here before we set the length. |
| delete[](uint8_t*) getBufAddr(); |
| setBufAddr(nullptr); |
| |
| setBitLen(i_bs.getBitLen()); |
| initBuffer(); |
| if (!i_bs.isZero()) |
| { |
| setString(i_bs); |
| } |
| |
| return *this; |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| BitStringBuffer& BitStringBuffer::operator=(const BitStringBuffer& i_bsb) |
| { |
| if (this != &i_bsb) // Check for assignment to self |
| { |
| // The initBuffer() function will deallocate the buffer as well, however |
| // we also need to deallocate the buffer here before we set the length. |
| delete[](uint8_t*) getBufAddr(); |
| setBufAddr(nullptr); |
| |
| setBitLen(i_bsb.getBitLen()); |
| initBuffer(); |
| if (!i_bsb.isZero()) |
| { |
| setString(i_bsb); |
| } |
| } |
| |
| return *this; |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| void BitStringBuffer::initBuffer() |
| { |
| // Deallocate the current buffer. |
| delete[](uint8_t*) getBufAddr(); |
| |
| // create new buffer, initialized to 0's |
| setBufAddr(new uint8_t[getMinBytes(getBitLen())]()); |
| } |
| |
| } // end namespace libhei |