How do I unpack and extract data properly using msgpack-c? - c

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!

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;
}

Related

How to refer to a specific struct in an array, while the array is in a struct in a function in C?

the program is run: ./program objekty
objekty - name of file without .txt
Here is the problem which should be as minimal as possible:
(I'm trying to get both printfs on stdout)
#include <stdio.h>
#include <stdlib.h>
struct obj_t {
int id;
float x;
float y;
};
struct cluster_t {
int size;
int capacity;
struct obj_t *obj;
};
void obj_ctor(struct obj_t *p, struct obj_t obj){
p->id = obj.id;
p->x = obj.x;
p->y = obj.y;
}
void pass(struct cluster_t *p, struct obj_t add){
obj_ctor(&p->obj[0],add);
p->size += 1;
}
void pass1(struct cluster_t **arr){
struct obj_t o3;
o3.id = 1; o3.x = 2; o3.y = 3;
int count = 20;
int pos = 0;
while(pos < 3){
arr[pos]->capacity = 3;
arr[pos]->size = 0;
arr[pos]->obj = malloc(count*sizeof(struct obj_t));
pass(arr[pos], o3);
pos++;
}
}
int main(int argc, char *argv[])
{
printf("Testing");
struct cluster_t *test;
pass1(&test);
printf("GOT HERE");
}
The final minimal example is nicely manageable — thank you. Here is a fairly straight-forward extension of that code. It is lazy in that it uses assert() to enforce necessary properties.
It includes a function to dump the data in a struct cluster_t structure. I regard such functions as a necessity — at the very least, they're extremely helpful. Quite often, I write them to take a FILE *fp argument so that messages can be written to standard output, standard error or to a log file, or, indeed, anywhere you can point a file stream. Often, I'd have a separate dump_obj() function that would be invoked from dump_cluster(), but it doesn't seem necessary here.
The key point is that it ensures that test.obj in main() points to an array of 3 struct obj_t. If you want dynamic memory allocation, changes are needed.
/* SO 7467-2430 */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
struct obj_t
{
int id;
float x;
float y;
};
struct cluster_t
{
int size;
int capacity;
struct obj_t *obj;
};
static void obj_ctor(struct obj_t *p, struct obj_t obj)
{
p->id = obj.id;
p->x = obj.x;
p->y = obj.y;
}
static void pass(struct cluster_t *p, struct obj_t add)
{
assert(p != NULL && p->obj != NULL);
assert(p->size < p->capacity);
obj_ctor(&p->obj[p->size], add);
p->size += 1;
}
static void dump_cluster(const char *tag, struct cluster_t *p)
{
printf("%s (%p):\n", tag, p);
if (p != NULL)
{
printf("size = %d, capacity = %d, objects = %p\n", p->size, p->capacity, p->obj);
for (int i = 0; i < p->size; i++)
printf(" [%d] id = %d, pos = (%g, %g)\n", i, p->obj[i].id, p->obj[i].x, p->obj[i].y);
}
}
int main(void)
{
printf("Testing\n");
struct cluster_t test;
struct obj_t o1, o2, o3;
o1.id = 1;
o2.id = 2;
o3.id = 3;
o1.x = 1;
o2.x = 2;
o3.x = 3;
o1.y = 1;
o2.y = 2;
o3.y = 3;
test.capacity = 3;
test.size = 0;
struct obj_t arr[3];
test.obj = arr;
pass(&test, o3);
printf("GOT HERE\n");
dump_cluster("After adding 1", &test);
pass(&test, o2);
pass(&test, o1);
dump_cluster("After adding 3", &test);
return 0;
}
Example output (the addresses will probably differ for you):
Testing
GOT HERE
After adding 1 (0x7ffeed15b3b0):
size = 1, capacity = 3, objects = 0x7ffeed15b3c0
[0] id = 3, pos = (3, 3)
After adding 3 (0x7ffeed15b3b0):
size = 3, capacity = 3, objects = 0x7ffeed15b3c0
[0] id = 3, pos = (3, 3)
[1] id = 2, pos = (2, 2)
[2] id = 1, pos = (1, 1)

gcc.exe warning cast from pointer to integer of different size [-Wpointer-to-int-cast]

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef void SeqList;
typedef void SeqListNode;
typedef unsigned int TSeqListNode;
typedef unsigned int TSeqListNode;
typedef struct _tag_SeqList
{
int capacity;
int length;
TSeqListNode *node;
}TSeqList;
SeqList * SeqList_Create(int capacity)
{
TSeqList *ret = NULL;
if(capacity>=0)
{
ret = (TSeqList *)malloc(sizeof(TSeqList) + sizeof(TSeqListNode)*capacity);
}
if(ret == NULL)
{
printf("malloc fail.\n");
exit(-1);
}
ret->capacity = capacity;
ret->length = 0;
ret->node = (TSeqListNode*)(ret+1);
return ret;
}
int SeqList_Insert(SeqList *list, SeqListNode *node, int pos)
{
TSeqList *sList = (TSeqList*)list;
int ret = (sList != NULL);
int i = 0;
ret = ret && (sList->length+1 <= sList->capacity);
ret = ret && (0 <= pos);
if(ret)
{
if(pos >= sList->length)
{
pos = sList->length;
}
for(i=sList->length; i > pos; i--)
{
sList->node[i] = sList->node[i-1];
}
sList->node[i] = (TSeqListNode)node;
sList->length++;
}
return ret;
}
int main()
{
system("pause");
return 0;
}
D:\mingw64\bin\gcc.exe -g D:\Cpp\DSA\test001.c -o D:\Cpp\test001.exe
D:\Cpp\test001.c: In function 'SeqList_Insert':
D:\Cpp\test001.c:56:26: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
sList->node[i] = (TSeqListNode)node;
^
What you are trying to do is already part of the ISO/IEC 9899:1999 C standard. It's called flexible array member. So no need for inventing your own code - just use what is already available.
Also notice that capacity and length are variables that can't be negative. So you should use an unsigned type. The common used type would be size_t.
So your code should be:
typedef struct _tag_SeqList
{
size_t capacity;
size_t length;
TSeqListNode node[]; // Flexible array member
} TSeqList;
Now you can simply do:
ret = malloc(sizeof(TSeqList) + sizeof(TSeqListNode)*capacity);
\--------------/ \----------------------------/
Memory for the Memory for the 'node' array
struct, i.e for with 'capacity' elements
capacity and
length
to allocate (no need for checking the value of capacity as it can't be negative).
And just use node as a normal array.
All the need for casts are gone.
Earlier versions of the C standard (C90) probably do necessitate casting. I think a placeholder data (int *)(array + 1) is entirely appropriate in some situations, assuming alignment is respected. Note that you might also make this design decision to comply with MISRA C 2012 Rule 18.7. I have eliminated the obfuscating typedefs and renamed parts of it to reflect what it actually does more clearly. A cast to void * is basically turning off type-checking, (through a typedef or not,) and should be avoided where possible.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct IntArray {
size_t capacity;
size_t length;
int *node;
};
/** #throws[malloc] */
static struct IntArray *IntArray_Create(const size_t capacity)
{
struct IntArray *ret = NULL;
ret = malloc(sizeof *ret + sizeof *ret->node * capacity);
if(ret == NULL) return 0; /* Let the caller decide what to do. */
ret->capacity = capacity;
ret->length = 0;
ret->node = (int *)(ret+1);
return ret;
}
/** Returns false if the `array` is full or null. Clips `pos` to `array`. */
static int IntArray_Insert(struct IntArray *array, const int node, size_t pos)
{
if(!array || array->length >= array->capacity) return 0;
if(pos >= array->length) pos = array->length;
/* Replaced looping over the array for performance. */
memmove(array->node + pos + 1, array->node + pos, array->length - pos);
array->length++;
array->node[pos] = node;
return 1;
}
#include <assert.h>
int main(void)
{
struct IntArray *a;
if(!(a = IntArray_Create(3))) { perror("fail"); return EXIT_FAILURE; };
assert(IntArray_Insert(a, 1, 42));
assert(IntArray_Insert(a, 3, 1));
assert(IntArray_Insert(a, 2, 1));
assert(!IntArray_Insert(a, 2, 1));
free(a);
return 0;
}
If you are targeting C99 or later, the flexible array member may be useful for this exact reason. If this is part of a macro, the only typedef you really need is typedef int ArrayType;.

C iterate array by known sizeof

I'm trying to make a small library for particle management that allows to "expand" struct with user's data (texture, animation frames, etc). The library would know only the size of the expanded struct.
How do I iterate through the array of unknown struct types but known size of a struct?
typedef struct{
int type;
}Base;
typedef struct{
Base base;
int value;
}inherited;
int main(){
size_t size = sizeof(inherited);
int count = 10;
void *b = malloc(size * count);
for (int i = 0; i < count; i++){
// iterate based on known size & send to callback
callback( &( (size)b )[i] );
}
free(b);
return 0;
}
I assume the code that does the malloc and calls callback doesn't know anything about the type of the object, only its size.
#include <stdlib.h>
void *alloc_and_init(size_t nmemb, size_t size, void (*callback)(void *))
{
void *b = calloc(nmemb, size);
if (b)
{
char *p = b;
for (size_t i = 0; i < nmemb; i++)
{
callback(p);
p += size;
}
}
return b;
}
typedef struct{
int type;
}Base;
typedef struct{
Base base;
int value;
}inherited;
void init_inherited(void *p)
{
inherited *obj = p;
/* do initialization of obj->* here */
}
int main(void)
{
int objcount = 10;
inherited *objarr;
objarr = alloc_and_init(objcount, sizeof(*objarr),
init_inherited);
/* ... */
free(objarr);
}
for( inherited *p = b, *e = p + count; p < e; p++ ){
callback(p);
}
char *b = malloc(size * count);
for (int i = 0; i < count; i++){
// iterate based on known size & send to callback
callback( b + i * size );
}
Polymorphism in C is always rather clunky. Basically you have to construct a "vtable" manually. The naive, simplified version below lets each object have its own function pointer. You'll end up with something rather contrived like this:
#include <stdio.h>
#include <stdlib.h>
typedef struct base_t base_t;
typedef void callback_t (const base_t* arg);
struct base_t
{
int type;
callback_t* callback;
};
typedef struct
{
base_t base;
int value;
} inherited_t;
void callback_base (const base_t* arg)
{
puts(__func__);
}
void callback_inherited (const base_t* arg)
{
const inherited_t* iarg = (const inherited_t*)arg;
printf("%s value: %d\n", __func__, iarg->value);
}
int main (void)
{
// allocate memory
base_t* array [3] =
{
[0] = malloc(sizeof(inherited_t)),
[1] = malloc(sizeof(base_t)),
[2] = malloc(sizeof(inherited_t)),
};
// initialize objects
*(inherited_t*)array[0] = (inherited_t){ .base.callback=callback_inherited, .value = 123 };
*(array[1]) = (base_t){ .callback=callback_base };
*(inherited_t*)array[2] = (inherited_t){ .base.callback=callback_inherited, .value = 456 };
for (int i = 0; i < 3; i++)
{
array[i]->callback(array[i]); // now we get polymorphism here
}
}
A more professional version involves writing a translation unit (.h + .c) per "class" and then combine allocation with initialization in the "constructor". It would be implemented with opaque type, see How to do private encapsulation in C? Inside the constructor, set the vtable corresponding to the type of object allocated.
I'd also boldly claim that any OO solution using void* arguments has some design flaw. The interface should be using the base class pointer. Void pointers are dangerous.

Printing Wrong Data

So I'm trying to simulate a cache. Right now, I created structs for the blocks and the sets and created their constructors. When the constructor for cache set are activated it initilize all of the tags and valid bits to 0. However, I keep getting garbage data printed out for the tags.I'm probably set up my pointer incorrectly, but I having problems figuring out what.
#include <inttypes.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
uint64_t tag;
unsigned int valid_bit;
}block;
typedef struct
{
unsigned int set_bit;
unsigned int number_of_blocks;
block * blocks;
}cache_set;
block *make_A_BLOCK(uint64_t tg, unsigned int v_b)
{
block *b = malloc(sizeof(block));
b->tag = tg;
b->valid_bit = v_b;
return b;
}
void change_tag(block *b,uint64_t t_g){b->tag = t_g;}
void change_bit(block *b,unsigned int v_b){b->valid_bit = v_b;}
uint64_t return_tag(block *b){ return b->tag;}
unsigned int return_bit(block *b){ return b->valid_bit;}
cache_set *make_A_CACHE_SET(unsigned int s_b, unsigned int n_b)
{
int i;
//uint64_t blank = 0;
cache_set *c_s = malloc(sizeof(cache_set));
c_s->set_bit = s_b;
c_s->number_of_blocks = n_b;
block *blocks = malloc(n_b * sizeof(block));
for (i=0; i < n_b; i++)
{
blocks[i].tag = 0;
blocks[i].valid_bit = 0;
}
free(blocks);
return c_s;
}
void print_cache_set(cache_set *c_s)
{
int i;
printf("Number of Cache Sets: %d \r\n",c_s->number_of_blocks);
for (i= 0; i < c_s->number_of_blocks ; i++)
{
printf("Block %d ",i);
printf(" Block Tag " "%" PRIu64, return_tag(&(c_s->blocks[i])));
//printf(" Block Bit %d \r\n", blocks[i].valid_bit);
}
}
int main(void)
{
cache_set *test = make_A_CACHE_SET(0,10);
print_cache_set(test);
printf("done");
return 0;
}
Example
When making a cash_set, you allocate blocks and assign the pointer to them to a local variable, and then you initialize the blocks one after the other.
But then, instead of letting the c_s->blocks point to this initialized list of blocks, you deallocate them with free(blocks).
So I'd suggest to replace free(blocks) by c_s->blocks = blocks

Struct arrays in C

Hi I'm having trouble trying to initializing each element of the struct array. When I try and assign the value ZERO to both 'bSize' and 'msgs', it doesn't work as it errors out when i get to malloc. In the printf statement it prints a -1852803823 number. Excuse the messy code as i'm playing around trying to figure it out.
struct message{
int *data;
int bSize;
int msgs;
};
int main(int argc, char *argv[]) {
.....
}
void getSchedFile (FILE *file, int **schd) {
struct message sMsg[nodeCount];
const int pakSize = 6;
// Iniitialise message buffer
for (int i=0; i<nodeCount; i++){
sMsg[i].bSize = 0;
sMsg[i].msgs = 0;
printf("bSize %d\n",sMsg[i].bSize);
}
/* Get the number of bytes */
fseek(file, 0L, SEEK_SET);
int time;
while((fscanf(file, "%d", &time)) != EOF){
int src;
fscanf(file, "%d", &src); // get source node id
// These are here for easier reading code
int aPos = sMsg[src].bSize;
int nMsg = sMsg[src].msgs;
printf("size %d\n", sMsg[src].bSize);
if (sMsg[src].bSize==0){
sMsg[src].data = malloc( pakSize * sizeof(int));
}else{
sMsg[src].data = realloc(sMsg[src].data, (aPos+pakSize)*sizeof(int));
}
Where is the nodeCount value coming from? Is it a global variable? You should be very careful with global variables, and avoid using them if possible.
Pass the nodeCount in the method parameter and as Charlie mentioned, check it for > 0

Resources