liteserver / binn

Binary Serialization
Apache License 2.0
440 stars 58 forks source link

How to serialize arrays? #12

Closed andrade closed 7 years ago

andrade commented 7 years ago

Hello. I've created a small example to understand how binn works, but I'm not sure how to properly serialize arrays using the library. How should I serialize an array of uint8_t or uint16_t?

I'm currently using binn_object_set_object but this doesn't look correct, in fact I get nothing on the other side but null. I'm also serializing a size_t as a uint64, I don't know if there is a better way to do with in binn.

The example I created is below. I basically serialize some data, save it to a buffer, and then use that buffer to do the inverse operation. I'm hoping to use binn to transfer data using sockets, but I figured the buffer would be enough for testing.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <string.h>

#include "binn.h"

// to test
struct my_struct {
    uint32_t num;
    size_t size;
    uint8_t buf[];
};

static uint8_t *host_to_buffer()
{
    binn *obj;
    uint8_t *buf;
    uint16_t a[] = {0x0001, 0x5502, 0x4409, 0x33A1, 0x22FC};
    size_t size = sizeof(a);

    obj = binn_object();

    binn_object_set_uint32(obj, "uint", 12345);
    binn_object_set_str(obj, "str", "hello world");
    binn_object_set_uint64(obj, "size", size);
    binn_object_set_object(obj, "array", a);  // what is the correct call?

    buf = malloc(binn_size(obj));
    memcpy(buf, binn_ptr(obj), binn_size(obj));

    binn_free(obj);

    return buf;
}

static void buffer_to_host(uint8_t *buf)
{
    binn *obj = binn_open(buf);

    uint32_t num = binn_object_uint32(obj, "uint");
    char *str = binn_object_str(obj, "str");
    size_t size = binn_object_uint64(obj, "size");
    uint16_t *a = binn_object_object(obj, "array");

    // print deseralization results
    printf("num=%"PRIu32", str=%s\n", num, str);
    if (a == NULL) {
        fprintf(stderr, "array is null\n");
    } else {
        for (int i = 0; i < size/sizeof(uint16_t); i++)
            printf("%04"PRIx16" ", a[i]);
        printf("\n");
    }

    binn_free(obj);
    free(buf);
}

int main()
{
    // save some data to a buffer using binn; then deserialize the data from buf
    uint8_t *buf = host_to_buffer();
    buffer_to_host(buf);
    return 0;
}
kroggen commented 7 years ago

By now Binn does not support arrays, but it supports lists.

You can create a list and add values to it. Then you can add the list to the object.

binn *list;

list = binn_list();
for(i=0; i < array_size; i++) {
  binn_list_add_uint16(list, a[i]);
}
binn_object_add_list(obj, "array", list);
binn_free(list);

When reading:

/* first get the list from the object */
void *list = binn_object_list(obj, "array");

/* then read the list */
int array_size = binn_count(list);
for(i=1; i <= array_size; i++) {
  uint16_t value = binn_list_uint16(list, i);
  printf("the value is %d\n", value);
}

In this last example the list is just a pointer and does not need to be released.

andrade commented 7 years ago

Thank you for the help, this solves my issue.

Two final questions, out of curiosity.

kroggen commented 7 years ago

Yes, we can add elements of different types to the same list. Even another lists.

Adding a list to another list would create a structure like this:

[1, "first", [123, 456], "last"]

andrade commented 7 years ago

Thank you!