Я пытаюсь решить решение проблемы с драйвером, в котором я должен извлечь несколько бит из произвольного битового потока, начиная с произвольной позиции. Я искал сообщение здесь, и я обнаружил, что большинство операций Бит - это 64-битные машины или не имеют особых условий размещения в хранилище.
Ну, бит-поток хранится как массив байтов в 32-битном Micro. Но поток немногочисленный, например, поток байтов ниже
LSBit (бит 0) → 0100 1001 0100 0100 <- MSBit (бит 15)
сохраняется в памяти как
Байт 0 1001 0010 Бит, бит 7-> 1001 0010 → Бит 0
Байт 1 0010 0010 Бит, бит 15-> 0010 0010 → Бит 8
здесь битовая компоновка в байте - Big Endian, но байты Little Endian.
Если мне нужно извлечь бит 4 в 11 из потока Little endian, чтобы получить 0010 1001, тогда мне нужно извлечь
от байт 0
1001, Более высокий кусок байта 0
от байта 1
0010, нижний кусок байта 1
Сдвиньте биты слева от байта 1, чтобы получить 0010 и OR с 1001 из байт 0
Проблема состоит в том, что поток может идти до 64 бит, а число бит (до 64) является произвольным, а начальный бит произвольным. Но для хранения внутри я могу, конечно, хранить в соответствующем типе данных, который может, конечно, соответствовать бит.
И чтобы добавить к этому, я должен упаковать их таким же образом, от действительных данных в этот поток Little Big Endian. : '(
Мне также нужно беспокоиться о времени выполнения, а также о том, что он длится 4 байта. Поэтому для хранения значения 48 бит мне нужно поддерживать массив из 6 байтов, расположенных в формате хоста. без .long long
поддержки компилятора
EDIT: поддерживается 64-битная long long
поддержка; Я просто проверил руководство к компилятору.
Любые предложения, пожалуйста, я застрял уже три недели.
Прочитайте байт по байту за потоком, построив число 64Bit в порядке хоста-байта (так как это максимальная длина потока бит).
Затем извлеките, используя стандартную бит-скрипку.
Этот двухступенчатый рецепт имеет преимущество в отношении хозяина-эндианта-агностика.
Получение окна из n
бит в индексе i
:
uint64_t extract(uint64_t in, int n, int i) {
return in<<(64-i-n)>>(64-n);
}
Мое предложенное решение:
#ifndef PICKBITS_H_INCLUDED
#define PICKBITS_H_INCLUDED
#include <stddef.h>
#include <stdint.h>
extern uint64_t pick_bits(unsigned char *bytes, size_t nbytes, int lo, int hi);
#endif /* PICKBITS_H_INCLUDED */
Заголовки необходимы для size_t
из <stddef.h>
и uint64_t
из <inttypes.h>
. Важно, чтобы заголовки были самодостаточными и идемпотентными. Включение этих двух заголовков необходимо для того, чтобы сделать pickbits.h
самодостаточным; защитники заголовков делают его идемпотентным, хотя они могут быть удалены, и все равно все будет в порядке, поскольку в коде нет определений типов (а стандартные заголовки уже ограничены идемпотентностью по стандарту C).
#include "pickbits.h"
#include <assert.h>
uint64_t pick_bits(unsigned char *bytes, size_t nbytes, int lo, int hi)
{
assert(bytes != 0 && nbytes > 0 && nbytes <= 8);
assert(lo >= 0 && lo < 64);
assert(hi >= 0 && hi < 64 && hi >= lo);
uint64_t result = 0;
for (int i = nbytes - 1; i >= 0; i--)
result = (result << 8) | bytes[i];
result >>= lo;
result &= (UINT64_C(1) << (hi - lo + 1)) - 1;
return result;
}
Обратите внимание, что в том числе "pickbits.h"
сначала проверяет, что заголовок является самодостаточным. Макрос UINT64_C
гарантирует, что константа 1
рассматривается как значение uint64_t
.
#include "pickbits.h"
#include <inttypes.h>
#include <stdio.h>
int main(void)
{
unsigned char d1[8] = "\xA5\xB4\xC3\xD2\xE1\xF0\x96\x87";
/* Selecting nybbles */
for (int u = 0; u < 64; u += 4)
{
uint64_t v = pick_bits(d1, sizeof(d1), u, u+3);
printf("Picking bits %2d..%2d gives 0x%" PRIX64 "\n", u, u+3, v);
}
/* Select across nybble boundaries - T.B.D */
return 0;
}
Тест должен быть расширен, чтобы пересечь границы низы, или остаться полностью в пределах шарлатанства.
Picking bits 0.. 3 gives 0x5
Picking bits 4.. 7 gives 0xA
Picking bits 8..11 gives 0x4
Picking bits 12..15 gives 0xB
Picking bits 16..19 gives 0x3
Picking bits 20..23 gives 0xC
Picking bits 24..27 gives 0x2
Picking bits 28..31 gives 0xD
Picking bits 32..35 gives 0x1
Picking bits 36..39 gives 0xE
Picking bits 40..43 gives 0x0
Picking bits 44..47 gives 0xF
Picking bits 48..51 gives 0x6
Picking bits 52..55 gives 0x9
Picking bits 56..59 gives 0x7
Picking bits 60..63 gives 0x8
Ответ: см. Почему GCC 4.8.2 жалуется на добавление при строгом переполнении?
В принципе, приведенный выше код компилируется строго под строгими предупреждениями:
$ gcc -g -O3 -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
> -Wold-style-definition -Wold-style-declaration -Werror -c \
> pickbits.c picktest.c
$
Если он интегрирован в один файл, "функция" GCC 4.8.2 запускается и генерирует предупреждение (которое -Werror
преобразуется в ошибку), хотя ситуация, о которой она предупреждает, не может произойти (переполнение не может произойти если типы были int8_t
, не говоря уже о int
).