Я пытаюсь найти решение проблемы драйвера, в которой я должен извлечь количество битов из произвольного потока битов, начиная с произвольной позиции. Я искал этот пост здесь и обнаружил, что большинство задействованных битовых операций — это 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 и ИЛИ с 1001 от байта 0
Проблема в том, что поток может иметь длину 64 бита, а количество битов (до 64) произвольно, а начальный бит — произвольно. Но для внутреннего хранения я, конечно, могу хранить данные в соответствующем типе данных, который, конечно, может соответствовать размерам.
И чтобы добавить к этому, я должен упаковать их таким же образом, из достоверных данных в этот поток Little Big Endian. : ‘(
Я должен также беспокоиться о времени выполнения и о том, что long составляет 4 байта.
Поэтому для хранения значения в 48 бит мне нужно поддерживать массив из 6 байтов, расположенных в формате хоста. без компилятора .long long
служба поддержки
РЕДАКТИРОВАТЬ: 64-разрядный long long
поддержка есть; Я только что проверил руководство компилятора.
Любые предложения, пожалуйста, я застрял на три недели.
Читайте ваш поток за байтом, создавая 64-битное число в порядке байтов хоста (так как это максимальная длина потока битов).
Затем извлеките, используя стандартные биты.
Этот двухэтапный рецепт имеет преимущество, заключающееся в том, что он не зависит от хоста.
Получение окна 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
самодостаточный; защита заголовков делает его идемпотентным, хотя его можно удалить, и все будет в порядке, поскольку в коде нет определений типов (а стандартные заголовки уже ограничены идемпотентностью в стандарте Си).
#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;
}
Тест должен быть расширен, чтобы пересечь границы nybble, или остаться полностью в пределах nybble.
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
).