我正在尝试找出驱动程序问题的解决方案,其中我必须从任意位置开始从任意位流中提取多个位。我在这里搜索了帖子,我发现大多数Bit操作都是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位
这里Byte中的位布局是Big Endian,但字节是Little Endian。
如果我必须从Little endian流中提取Bit 4到11以获得0010 1001,那么我需要提取
来自Byte 0
1001,字节0的高半字节
来自字节1
0010,字节1的低半字节
将字节1左侧的位移位到0010,使用字节0的1001移位OR
问题是流可以达到64位的长度,并且位数(高达64)是任意的,并且起始位是任意的。但对于内部存储,我当然可以存储适当的数据类型,当然这些数据类型也适合。
除此之外,我还必须以相同的方式将它们打包,从有效数据到Little Big Endian流。 :'(
我还要担心运行时以及long是4个字节的事实。
因此,要存储48位的值,我需要维护以主机格式排列的6个字节的数组。 没有编译器。long long
支持
编辑:有64位long long
支持;我刚检查了编译器手册。
请提出任何建议,我现在被困了三个星期。
答案 0 :(得分:2)
读取每个字节的字节数,以主机字节顺序构建64位数(因为这是最大比特流长度)。
然后使用标准的bit-fiddling进行提取。
这个两步配方具有与宿主无关的好处。
在索引n
处获取i
位的窗口:
uint64_t extract(uint64_t in, int n, int i) {
return in<<(64-i-n)>>(64-n);
}
答案 1 :(得分:1)
我提供的解决方案是:
#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
的{{1}}和来自<stddef.h>
的{{1}},标题是必需的。标题应该是自包含且幂等的,这一点很重要。包含这两个标题对于使uint64_t
自包含是必要的;标题保护使它成为幂等的,虽然它们可以被删除但它仍然可以,因为代码中没有类型定义(并且标准标题已经被C标准限制为幂等)。
<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
测试需要扩展到跨越nybble边界,或完全保持在nybble中。
uint64_t
答案:见Why is GCC 4.8.2 complaining about addition under strict overflow?
基本上,上面提到的代码在严格的警告下完全编译:
#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;
}
如果集成到单个文件中,GCC 4.8.2的“功能”会启动并生成警告(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 -g -O3 -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
> -Wold-style-definition -Wold-style-declaration -Werror -c \
> pickbits.c picktest.c
$
,则不会发生溢出,更不用说-Werror
)。