11

I'm currently trying to use msgpack in a project written in C. I'm using msgpack for the purpose of serializing the contents of a struct, which is then to be sent over the network, and deserialized back into a corresponding struct on the other side.

Condensed version of what I'm trying to do:

#include <stdio.h>
#include <msgpack.h>
#include <stdbool.h>

typedef someStruct{
uint32_t a;
uint32_t b;
float    c;
} someStruct;

int main (void){
    someStruct data;
    /* ... Fill 'data' with some data for test purposes ...*/

    msgpack_sbuffer* buff = msgpack_sbuffer_new();
    msgpack_packer* pck = msgpack_packer_new(buff, msgpack_sbuffer_write);        

    someStruct* structs = malloc(sizeof(someStruct) * 10);

    /* ... Fill 'structs' with members containing test data ... */
    // Serialize
    msgpack_pack_array (pck, 10);

    int i;
    for(i = 0 ; i < 10 ; i++){
    msgpack_pack_array (pck, 3);
    msgpack_pack_uint32 (pck, structs[i].a);
    msgpack_pack_uint32 (pck, structs[i].b);
    msgpack_pack_float (pck, structs[i].c);
    }

    free(structs);
    msgpack_packer_free(pck);

    // Deserialize
    msgpack_unpacked msg;
    msgpack_unpacked_init(&msg);
    bool deserialize_success = msgpack_unpack_next
                               (&msg, buff->data, buff->size, NULL);
    if(!deserialize_success) /* Error */

    msgpack_object obj = msg.data;
    msgpack_object_print(stdout,obj); // This seems to work perfectly, indicating serialize / deserialize works as intended...

    someStruct deserialized_data;
    /* Insert code to extract and cast deserialized data to 'deserialized_data */
    // Clean
    msgpack_sbuffer_free(buff);
    msgpack_packer_free(pck);

return 0;
}

The code listed is more or less ripped straight from here, which seems to be one of very few resources on msgpack-c.

Can anyone point me in the right direction as to a way to 'recreate' the original struct on the other side of the wire? The only way I've found to actually utilize the deserialized data, is to use the msgpack_object_print() call to print from the messagepack_object. This does, however seem to work, so I'm certain the data is there.

Do I need to somehow loop through the serialized data and use msgpack_unpack_next() with an offset to retrieve each someStruct member? Using memcpy to a local byte buffer?

Any help is greatly appreciated!

halvdanhg
  • 198
  • 1
  • 1
  • 9

1 Answers1

9

Please find below a rewritten version that illustrates how to pack / unpack your data.

The whole idea is to pack each successive field of your struct, in a contiguous fashion, and apply (of course), the same logic at unpack time.

Right after pack, you are free to use the buffer the way you want (e.g send over the network, save on-disk, etc).

#include <stdio.h>
#include <assert.h>
#include <msgpack.h>

typedef struct some_struct {
  uint32_t a;
  uint32_t b;
  float c;
} some_struct;

static char *pack(const some_struct *s, int num, int *size);
static some_struct *unpack(const void *ptr, int size, int *num);

/* Fixtures */
some_struct ary[] = {
  { 1234, 5678, 3.14f },
  { 4321, 8765, 4.13f },
  { 2143, 6587, 1.34f }
};

int main(void) {
  /** PACK */
  int size;
  char *buf = pack(ary, sizeof(ary)/sizeof(ary[0]), &size);
  printf("pack %zd struct(s): %d byte(s)\n", sizeof(ary)/sizeof(ary[0]), size);

  /** UNPACK */
  int num;
  some_struct *s = unpack(buf, size, &num);
  printf("unpack: %d struct(s)\n", num);

  /** CHECK */
  assert(num == (int) sizeof(ary)/sizeof(ary[0]));
  for (int i = 0; i < num; i++) {
    assert(s[i].a == ary[i].a);
    assert(s[i].b == ary[i].b);
    assert(s[i].c == ary[i].c);
  }
  printf("check ok. Exiting...\n");

  free(buf);
  free(s);

  return 0;
}

static char *pack(const some_struct *s, int num, int *size) {
  assert(num > 0);
  char *buf = NULL;
  msgpack_sbuffer sbuf;
  msgpack_sbuffer_init(&sbuf);
  msgpack_packer pck;
  msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
  /* The array will store `num` contiguous blocks made of a, b, c attributes */
  msgpack_pack_array(&pck, 3 * num);
  for (int i = 0; i < num; ++i) {
    msgpack_pack_uint32(&pck, s[i].a);
    msgpack_pack_uint32(&pck, s[i].b);
    msgpack_pack_float(&pck, s[i].c);
  }
  *size = sbuf.size;
  buf = malloc(sbuf.size);
  memcpy(buf, sbuf.data, sbuf.size);
  msgpack_sbuffer_destroy(&sbuf);
  return buf;
}

static some_struct *unpack(const void *ptr, int size, int *num) {
  some_struct *s = NULL;
  msgpack_unpacked msg;
  msgpack_unpacked_init(&msg);
  if (msgpack_unpack_next(&msg, ptr, size, NULL)) {
    msgpack_object root = msg.data;
    if (root.type == MSGPACK_OBJECT_ARRAY) {
      assert(root.via.array.size % 3 == 0);
      *num = root.via.array.size / 3;
      s = malloc(root.via.array.size*sizeof(*s));
      for (int i = 0, j = 0; i < root.via.array.size; i += 3, j++) {
        s[j].a = root.via.array.ptr[i].via.u64;
        s[j].b = root.via.array.ptr[i + 1].via.u64;
        s[j].c = root.via.array.ptr[i + 2].via.dec;
      }
    }
  }
  msgpack_unpacked_destroy(&msg);
  return s;
}
deltheil
  • 15,496
  • 2
  • 44
  • 64
  • 1
    Wow, thank you so much. That really clears thing up. Am I right to assume that the data members are always treated as 64-bit on unpack? Can I safely cast to the corresponding types (i.e. uint32, float) when extracting the fields? – halvdanhg Mar 14 '13 at 12:33
  • Yes you're totally right :) See http://stackoverflow.com/questions/12431441/messagepack-c-api/12581029#12581029 for more details. – deltheil Mar 14 '13 at 13:24
  • Can you tell me what would be the cases when msgpack_unpack_next return false. I am trying to unpack some data, but in my case the msgpack_unpack_next return false. – atuljangra Jun 04 '14 at 03:55
  • That may mean an error occurred or you pass wrong arguments (e.g an invalid offset). You can check your data with [unpack.c](https://github.com/deltheil/unpack.c) to make sure it contains what you expect. – deltheil Jun 04 '14 at 09:13
  • This does not compile any more with msgpack 0.5.9. – sivann Feb 16 '15 at 17:25
  • What? [unpack.c](https://github.com/deltheil/unpack.c) or the above code? On my side both compile with `msgpack-0.5.9` (on OS X 10.9.5). – deltheil Feb 16 '15 at 18:40
  • sivann Compiled for me latest stable release, @deltheil, what would the type be for binary data? – Rubber Duck Feb 23 '16 at 05:22
  • 1
    See https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family and also https://github.com/msgpack/msgpack-c/blob/953077b/include/msgpack/pack.h#L95-L96 – deltheil Feb 23 '16 at 08:49
  • if anyone else looking for binary types: https://github.com/msgpack/msgpack-c/blob/953077b73fdce159027801f932ae653abb709311/include/msgpack/unpack_define.h#L36-L38 – Rubber Duck Feb 24 '16 at 00:42