2
votes

I'm kind of at a loss i want to extract up to 64bits with a defined bitoffset and bitlength (unsigned long long) from a string (coming from network).

The string can be at an undefined length, so i need to be sure to only access it Bytewise. (Also means i cant use _bextr_u32 intrinsic). I cant use the std bitset class because it doesnt allow extraction of more then one bit with an offset and also only allows extraction of a predefined number of bits.

So I already calculate the byteoffset (within the string) and bitoffset (within the starting byte).

m_nByteOffset = nBitOffset / 8;
m_nBitOffset = nBitOffset % 8;

Now i can get the starting address

const char* sSource = str.c_str()+m_nByteOffset;

And the bitmask

unsigned long long nMask = 0xFFFFFFFFFFFFFFFFULL >> (64-nBitLen);

But now I just cant figure out how to extract up to 64 bits from this as there are no 128 bit integers available.

unsigned long long nResult = ((*(unsigned long long*)sSource) >> m_nBitOffset) & nMask;

This only works for up to 64-bitoffset bits, how can i extend it to really work for 64 bit indepently of the bitoffset. And also as this is not a bytewise access it could cause a memory read access violation.

So im really looking for a bytewise solution to this problem that works for up to 64 bits. (preferably C or intrinsics)

Update: After searching and testing a lot I will probably use this function from RakNet: https://github.com/OculusVR/RakNet/blob/master/Source/BitStream.cpp#L551

2

2 Answers

2
votes

To do it byte-wise, just read the string (which BTW it is better to interpret as a sequence of uint8_t rather than char) one byte at a time, updating your result by shifting it left 8 and oring it with the current byte. The only complications are the first bit and the last bit, which both require you to read a part of a byte. For the first part simply use a bit mask to get the bit you need, and for the last part down shift it by the amount needed. Here is the code:

const uint8_t* sSource = reinterpret_cast<const uint8_t*>(str.c_str()+m_nByteOffset);

uint64_t result = 0;
uint8_t FULL_MASK = 0xFF;

if(m_nBitOffset) {
    result = (*sSource & (FULL_MASK >> m_nBitOffset));
    nBitLen -= (8 - m_nBitOffset);
    sSource++;
}

while(nBitLen > 8) {
    result <<= 8;
    result |= *sSource;
    nBitLen -= 8;
    ++sSource;
}

if(nBitLen) {
    result <<= nBitLen;
    result |= (*sSource >> (8 - nBitLen));
}

return result;
1
votes

This is how I would do it in modern C++ style. The bit length is determined by the size of the buffer extractedBits: instead of using an unsigned long long, you could also use any other data type (or even array type) with the desired size.

See it live

unsigned long long extractedBits;
char* extractedString = reinterpret_cast<char*>(&extractedBits);
std::transform(str.begin() + m_nByteOffset,
               str.begin() + m_nByteOffset + sizeof(extractedBits),
               str.begin() + m_nByteOffset + 1,
               extractedString,
               [=](char c, char d)
               {
                   char bitsFromC = (c << m_nBitOffset);
                   char bitsFromD = 
                       (static_cast<unsigned char>(d) >> (CHAR_BIT - m_nBitOffset));
                   return bitsFromC | bitsFromD;
               });