位对齐访问任何位长度字节对齐的缓冲区

Posted

技术标签:

【中文标题】位对齐访问任何位长度字节对齐的缓冲区【英文标题】:Bit aligned Acess of any bit length byte aligned buffer 【发布时间】:2012-08-28 13:09:34 【问题描述】:

对于嵌入式系统(小 Endian ARM,语言 C - 即使我在这里用于测试目的 C++),我编写了附加的代码。我必须发送和接收不同的配置数据,位对齐和给定的位长度。使用 field_conf_t 对我必须发送/接收的每个变量执行配置,例如:

typedef struct field_conf_t  ... ; // see below

typedef struct 
    field_conf_t    a;
    field_conf_t    b;
    field_conf_t    c;
 tx_fields_conf_t;

const tx_fields_conf_t tx_fields_conf = 
     0, 8  ,      // a - offset/length
     28, 12 ,     // b - offset/length
     56, 8        // c - offset/length
;

我遇到的问题是,我对单个 Bit-Ops 想太多了,测试失败了。

代码如下:

#include <stdint.h>

#define BOOST_TEST_MODULE BitFieldTest
#include <boost/test/unit_test.hpp>

typedef uint8_t     uint8;
typedef uint16_t    uint16;
typedef uint32_t    uint32;
typedef unsigned long long uint64;

typedef struct 
    uint8   offset;     // [0-63]
    uint8   length;     // [1-32]
 field_conf_t;

typedef struct 
    field_conf_t    a;
    field_conf_t    b;
    field_conf_t    c;
 tx_fields_conf_t;

typedef union 
    uint8   buf[8];
    uint64  contents;
 msg_t;

void set_value(msg_t* const msg, const field_conf_t* const field, uint32 value)

    const uint32 mask = (1 << field->length) - 1;
    msg->contents    &= ~(mask          << field->offset);  // clear old contens
    msg->contents    |=  (value & mask) << field->offset;   // set new contens


uint32 get_value(const msg_t* const msg, const field_conf_t* const field)

    const uint32 mask = (1 << field->length) - 1;

    uint64 value = (msg->contents >> field->offset);
    value       &= mask;

    return (uint32)value;


// ########################################################################
struct TestFixture 
    TestFixture() : a(0xAA), b(0xBBB), c(0xCC) 
        conf.a.offset =  0; conf.a.length = 8;
        conf.b.offset = 25; conf.b.length = 12;
        conf.c.offset = 56; conf.a.length = 8;
    
    uint32 a, b, c;
    msg_t msg;
    tx_fields_conf_t conf;
;

BOOST_FIXTURE_TEST_SUITE(MsgBitfieldTest, TestFixture);

BOOST_AUTO_TEST_CASE(Test_01)

    set_value(&msg, &conf.a, a);
    BOOST_CHECK(get_value(&msg, &conf.a) == a);

    set_value(&msg, &conf.b, b);
    BOOST_CHECK(get_value(&msg, &conf.a) == a);
    BOOST_CHECK(get_value(&msg, &conf.b) == b);

    set_value(&msg, &conf.c, c);
    BOOST_CHECK(get_value(&msg, &conf.a) == a);
    BOOST_CHECK(get_value(&msg, &conf.b) == b);
    BOOST_CHECK(get_value(&msg, &conf.c) == c);


BOOST_AUTO_TEST_SUITE_END();

和测试运行输出

Running 1 test case...
d:/work/bugee/test/can_msg/msg_bitsfields/msg_bitsfields/main.cpp(65): error in
"Test_01": check get_value(&msg, &conf.b) == b failed
d:/work/bugee/test/can_msg/msg_bitsfields/msg_bitsfields/main.cpp(69): error in
"Test_01": check get_value(&msg, &conf.b) == b failed
d:/work/bugee/test/can_msg/msg_bitsfields/msg_bitsfields/main.cpp(70): error in
"Test_01": check get_value(&msg, &conf.c) == c failed

*** 3 failures detected in test suite "BitFieldTest"

缓冲区大小始终为 8 字节;我很高兴我可以在这里使用 64 位 long long;在我在字节对齐操作上也失败之前(每个字节上的掩码和位操作)。注意,该值不能大于 int32 /uint32。我也知道 C 位域。它们太慢了,我需要一个用于嵌入式系统的快速解决方案(还有其他耗时的任务)。

我也对具有 lsb/msb 32 位甚至 8 字节切片快速解决方案的解决方案感兴趣,但我认为由于编译器的原因,使用 64 位数据类型的性能更高。

【问题讨论】:

【参考方案1】:

在你的第一个代码块中你有什么问题:

const tx_fields_conf_t tx_fields_conf = 
     0, 8  ,      // a - offset/length
     28, 12 ,     // b - offset/length -- note the 28 here as the offset
     56, 8        // c - offset/length
;

但稍后在您的应用程序中,您有:

struct TestFixture 
    TestFixture() : a(0xAA), b(0xBBB), c(0xCC) 
        conf.a.offset =  0; conf.a.length = 8;
        conf.b.offset = 25; conf.b.length = 12;  // offset is *25* here, not 28 as above. Poss. problem?
        conf.c.offset = 56; conf.a.length = 8;
    
    uint32 a, b, c;
    msg_t msg;
    tx_fields_conf_t conf;
;

这可能是一个重要的差异吗?也许什么都没有,但我确实注意到了......我想我会指出来。

【讨论】:

感谢您确实指出了这一点,但这并不重要。我喜欢在最终版本中使用的结构“API”。【参考方案2】:

问题在于set_value

const uint32 mask = (1 << field->length) - 1;
msg->contents    &= ~(mask          << field->offset);  // clear old contens
msg->contents    |=  (value & mask) << field->offset;   // set new contens

mask &lt;&lt; field-&gt;offset 是一个 32 位值,因此当 field-&gt;offset + field-&gt;length &gt;= 32 时会溢出边缘。将mask 更改为 64 位应该可以将位移作为 64 位操作执行:

const uint64 mask = (1 << field->length) - 1;

【讨论】:

以上是关于位对齐访问任何位长度字节对齐的缓冲区的主要内容,如果未能解决你的问题,请参考以下文章

在未对齐的字节边界上有效地打包 10 位数据

结构体字节对齐问题探究

结构体字节对齐问题探究

C语言字节对齐

内存对齐

go 内存对齐