Convert struct in byte array and store in db. Read db and get byte array to recreate the struct in C

493 views Asked by At

Hello everyone sorry for asking but I could find any appropriate solution. I am working on a file system where I save each file node as key-value pair in a GDBM database. I have a struct which has some attributes and I convert this to a byte array

struct mystruct:

typedef struct nold{
char* name;
char* surname;
 int age;
}mystruct;

I convert it as following to a byte array

dead.name="john";
dead.surname="doe";
dead.age=22;

//copy bytes of the our struct 
char buffer[sizeof(dead)]; 
memcpy(buffer, &dead, sizeof(dead));

to store in a database we use a datum structure which is as following:

 typedef struct {
         char *dptr;
         int  dsize;
      } datum

I fill the datum structure as following:

//create a key datum
char* k="file.txt";
key.dptr=k;
key.dsize=strlen(k)+1;



//create a value datum  here I assign bytes
value.dptr=buffer;
value.dsize = sizeof(dead);

Now I store in GDBM as key-value pair

Then in another file I read the data I store and try to recast it back to a struct

datum result;

//read
result=gdbm_fetch(file,key);

char* bytes=result.dptr;

mystruct* reborn;

reborn=(mystruct*)bytes;


//print from our new struct
printf("%s\n",reborn->name);
printf("%s\n",reborn->surname);
printf("%d\n",reborn->age);

It prints the following:

E���D$�$ˈ�k����E��
$�$ˈ�k����E��
22

It manages to bring back the struct but data of char* is lost. Only the integer data is covered. Any idea why that happening? How to solve it? It it cant be solved by storing a byte array what about converting the byte array to hex or base64 and store it as such.

I really am struggling with this one.Thank you in advance.

3

There are 3 answers

2
e.jahandar On BEST ANSWER

A pointer inside struct is just a pointer to character array not the character array itself:

typedef struct nold{
   char* name;
   char* surname;
   int age;
}mystruct;

mystruct s;
s.name = "Salam";

This will reserver a memory space for string "Salam", put string Salam into and return back the pointer to s.name.

now you're copying entire struct into something other, youre copying from struct address with sizeof(mystruct) which isn't hold string "Salam" actually, it just holds a pointer to "Salam".

If you want to do this way, you have to preallocate some space for name :

#define MAX_NAME_LEN    50
typedef struct nold{
   char name[MAX_NAME_LEN];
   char surname[MAX_NAME_LEN];
   int age;
}mystruct;

mystruct s;
strcpy(s.name, "Salam");

now memcpy will work

mystruct d;
memcpy(&d, &s, sizeof(mystruct);
1
Leon Carlo Valencia On

The problem is that name and surname are pointers to string literals. So your struct does not contain the string itself but rather pointers to the data section where the literals reside. As soon as you copy that to your buffer, you are in fact simply copying the pointer values. Upon reconstruction, those pointer values would now point to some arbitrary area that don't contain your strings.

EDIT: Here's a function that transfers the data to a buffer:

size_t copyMyStructToBuffer(mystruct *aPerson, char **buffer) {
    size_t nameLen = strlen(aPerson->name);
    size_t surnameLen = strlen(aPerson->surname);
    size_t structLen = nameLen + 1 + surnameLen + 1 + sizeof(int);
    *buffer = malloc(structLen);

    memcpy(*buffer, aPerson->name, nameLen + 1); // w/ terminator
    memcpy((*buffer)[nameLen+1], aPerson->surname, surnameLen + 1); // w/ terminator
    memcpy((*buffer)[nameLen+1+surnameLen+1], &aPerson->age, sizeof(int));

    return structLen;
}

And how you can use it:

mystruct dead;
dead.name = "John";
dead.surname = "Doe";
dead.age = 22;
char *buff;
size_t buffLen;
buffLen = copyMyStructToBuffer(&dead, &buff);
// use buff here
free(buff);

Admittedly though, this code is difficult to maintain.

0
John Bollinger On

Pointer values are meaningful only in the context of a specific run of a specific program. It is possible to write them and read them back, but it is essential to remember that a pointer designates an address, not (directly) the data residing at that address, if indeed the address is valid at all for the program accessing it. You do not convey the pointed-to data between processes by conveying the pointer.

You could perhaps solve the problem by putting the data you want to convey directly into the struct:

typedef struct {
     char data[MAX_DATA_SIZE];
     int  dsize;
} datum;

The drawback of this approach is that you have to put a fixed upper bound on the size of the data, but in many cases that's not a problem. If you need unbounded data, then you need an altogether different approach.