如何在 c 中处理 capn_data?

How to handle capn_data in c?

我使用 c-capnproto 序列化我的传感器数据。现在我需要给它添加一个 HMAC。它需要是一个 capn_data blob,但这在 the example code.

中没有处理

当我尝试添加数据时,它不起作用。 这是我的 capnp 文件:

@0xe2d0214c41cb77af;

using C = import "c.capnp";
$C.fieldgetset;

struct Heartbeat @0x97002102216a8d67 {
    temperature @0 :UInt32;
}
 
struct AuthenticatedHeartbeat @0xe2fa13efd2e3a9dc {
    heartbeat @0 :Data;
    hmac @1 :Data;
}

编译后,我使用它:

static uint8_t beat[1024];
static uint8_t hmac[32] = {0xc2, 0xf1, 0x53, 0x12, 0x4b, 0x0c, 0xa4, 0xd1,
                           0x22, 0xf1, 0x64, 0x22, 0x1b, 0xbc, 0xa5, 0xd2,
                           0xe2, 0xf2, 0x75, 0x32, 0x2b, 0x3c, 0xa6, 0xd3,
                           0x72, 0xf1, 0x86, 0x42, 0x6b, 0x7c, 0xa7, 0xd4};
static uint8_t auth[1024];
int main(void) {
    int packed_size;
    {
        struct capn root;
        capn_init_malloc(&root);
        capn_ptr cr = capn_root(&root);
        struct capn_segment *cs = cr.seg;
        Heartbeat_ptr hb = new_Heartbeat(cs);
        Heartbeat_set_temperature(hb, 0x25);
        capn_setp(capn_root(&root), 0, hb.p);
        packed_size = capn_write_mem(&root, beat, sizeof(beat), 1/*packed*/);
        printf("BEAT:    max_unpacked_size=%d, packed_size=%d\n", capn_size(&root), packed_size);
        capn_free(&root);
    }
    print_buffer(beat, packed_size);
    //HMAC_calc(beat, packed_size, hmac);
    {
        struct capn root;
        capn_init_malloc(&root);
        capn_ptr cr = capn_root(&root);
        struct capn_segment *cs = cr.seg;
        AuthenticatedHeartbeat_ptr ahb = new_AuthenticatedHeartbeat(cs);
        AuthenticatedHeartbeat_set_heartbeat(ahb, buf_to_data(beat, packed_size));
        AuthenticatedHeartbeat_set_hmac(ahb, buf_to_data(hmac, 32));
        capn_setp(capn_root(&root), 0, ahb.p);
        packed_size = capn_write_mem(&root, auth, sizeof(auth), 1/*packed*/);
        printf("AUTH:    max_unpacked_size=%d, packed_size=%d\n", capn_size(&root), packed_size);
        capn_free(&root);
    }
    print_buffer(auth, packed_size);
}

我写了一个辅助函数,就像示例中提供的 chars_to_text 函数一样

static capn_data buf_to_data(char *buf, const int len) {
  return (capn_data) {{
    .type = CAPN_NULL,
    .has_ptr_tag = 0,
    .is_list_member = 0,
    .is_composite_list = 0,
    .datasz = 0,
    .ptrs = 0,
    .len = len,
    .data = buf,
    .seg = NULL,
  }};
}

但我想我遗漏了一些部分。 结果数据

BEAT:    max_unpacked_size=24, packed_size=6
[  0] - 10021001 - ....
[  1] - 01250000 - .%
AUTH:    max_unpacked_size=32, packed_size=6
[  0] - 10034002 - ..@.
[  1] - 00010000 - ..

至少在认证数据中不再包含温度 (0x25)。此外,HMAC 不存在,长度太短了。

能否请您提示我如何处理 capn_data blob、如何填充它以及如何将其打包到根目录中。

终于在 this closed issue 中找到了一个提示,这在我一开始的研究中是不可见或被忽视的...

我使用此代码序列化 capn_data(这替换了原始问题中的较低范围):

    {
        struct capn root;
        capn_init_malloc(&root);
        capn_ptr cr = capn_root(&root);
        struct capn_segment *cs = cr.seg;
        
        capn_list8 list8 = capn_new_list8(cs, packed_size);
        capn_setv8(list8, 0, beat, packed_size);
        capn_list8 list9 = capn_new_list8(cs, 32);
        capn_setv8(list9, 0, hmac, 32);
        
        struct AuthenticatedHeartbeat s_ahb;
        s_ahb.heartbeat.p = list8.p;
        s_ahb.hmac.p = list9.p;
        
        AuthenticatedHeartbeat_ptr pahb = new_AuthenticatedHeartbeat(cs);
        write_AuthenticatedHeartbeat(&s_ahb, pahb);
        capn_setp(capn_root(&root), 0, pahb.p);
        packed_size = capn_write_mem(&root, auth, sizeof(auth), 1/*packed*/);
        printf("AUTH:    max_unpacked_size=%d, packed_size=%d\n", capn_size(&root), packed_size);
        capn_free(&root);
    }

因此,首先创建一个“列表”,稍后用作 capn_data,因为这两个与 @eqvinox 所述基本相同。

不再需要 buf_to_data() 功能。

作为参考,这是反序列化数据的示例代码:

    {
        struct capn root;
        capn_init_mem(&root, auth, packed_size, 1/*packed*/);
        AuthenticatedHeartbeat_ptr ahbp;
        ahbp.p = capn_getp(capn_root(&root), 0, 1);
        capn_data dhb = AuthenticatedHeartbeat_get_heartbeat(ahbp);
        capn_data dhmac = AuthenticatedHeartbeat_get_hmac(ahbp);
        printf("dhb   ptr=%p, len=%d\n", dhb.p.data, dhb.p.len);
        printf("dhmac ptr=%p, len=%d\n", dhmac.p.data, dhmac.p.len);
        unpacked_size = dhb.p.len;
        hmac2_size = dhmac.p.len;
        memcpy(beat2, dhb.p.data, unpacked_size);
        memcpy(hmac2, dhmac.p.data, hmac2_size);
        capn_free(&root);
    }
    {
        struct capn root;
        capn_init_mem(&root, beat2, unpacked_size, 1/*packed*/);
        Heartbeat_ptr hbp;
        hbp.p = capn_getp(capn_root(&root), 0, 1);
        uint32_t temperature = Heartbeat_get_temperature(hbp);
        printf("DESER:   temperature=%x\n", temperature);
        capn_free(&root);
    }