2016-06-14 6 views
2

Я немного растерялся. Я хочу извлечь до 64 бит с определенным битовым и битрейтом (без знака длинного длинного) из строки (исходящей из сети).Извлечение непрерывных битов из std :: string bytewise с смещением битов

Строка может иметь неопределенную длину, поэтому мне нужно обязательно получить к ней доступ только Bytewise. (Также означает, что я не могу использовать _bextr_u32 intrinsic). Я не могу использовать класс std bitet, потому что он не позволяет извлекать более одного бита со смещением, а также позволяет извлекать предварительно определенное количество бит.

Итак, я уже вычисляю байтовое (внутри строки) и битовое (в начальном байте).

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

Теперь я могу получить начальный адрес

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

И битовую маску

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

Но теперь я просто не могу понять, как извлечь до 64 бит от этого, как есть нет 128-битных целых чисел.

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

Это работает только на срок до 64-bitoffset бит, как я могу продлить это реально работать на 64 бита независимо обозначают от bitoffset. А также, поскольку это не побочный доступ, это может вызвать нарушение доступа к памяти.

Так что им действительно нужно найти пошаговое решение этой проблемы, которая работает до 64 бит. (Предпочтительно C или встроенные функции)

Update: После поиска и тестирования много я, вероятно, использовать эту функцию из RakNet: https://github.com/OculusVR/RakNet/blob/master/Source/BitStream.cpp#L551

ответ

2

Чтобы сделать это побайтно, просто прочитать строку (которая кстати это лучше интерпретировать как последовательность uint8_t, а не char) один байт в то время, обновляя свой результат, смещение его осталось 8 и or Ввод его с текущим байтом. Единственными осложнениями являются первый бит и последний бит, которые требуют, чтобы вы прочитали часть байта. Для первой части просто используйте битовую маску, чтобы получить бит, который вам нужен, и для последней части вниз сдвиньте его на необходимую сумму. Вот код:

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; 
+0

Мне нравится это разделение проблемы, это будет работать плохо, сделайте некоторые тесты позже сегодня – Hendrik

+0

«static_cast» должен работать, поскольку 'const char *' должен быть совместим с 'const uint8_t *'. Это даст вам некоторую безопасность против опечаток. –

+0

Это может скомпилировать какой-то довольно плохой asm с gcc. например он может фактически сдвигать и загружать один байт за раз. :/In asm, загрузка до 64 бит, которые не могут быть выровнены по байтам, должна выполняться с байтовой загрузкой, неуравновешенной нагрузкой на 64 бит и сменой пары. (И «OR», если вы не используете сдвиг с двумя регистрами, например [x86's 'shrd'] (http://www.felixcloutier.com/x86/SHRD.html), чтобы взять окно конкатенации 2 регс). Филиалы не являются обязательными, чтобы пропустить байтовую нагрузку, если одна 64-разрядная загрузка может включать в себя всю желаемую bitstring. Если вам повезет, вы получите asm от этого src. –

1

Это, как я хотел бы сделать это в современном стиле C++. Длина бита определяется размером буфера extractedBits: вместо использования unsigned long long вы также можете использовать любой другой тип данных (или даже тип массива) с требуемым размером.

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; 
       }); 
+0

hm Я тоже пробовал этот код, но даже базовые тестовые примеры не работают, и не используется битовая длина. – Hendrik

+0

@ Hendrik Обновлено! Теперь он работает, и я объяснил, как изменить длину бит. –

+0

Я думал, что длина бит OP не должна быть силой 2 или даже кратным 8, так, например, результат может быть 45 бит, сохраненный в низком 45 из 64-битного целого числа (с нулевым расширением, чтобы заполнить верхние 19 бит 0). –

Смежные вопросы