Search code examples
cxdr

How to use generated XDR structures from .x file (memory allocation)


I would please like some help with this case.

I need to send one XDR message, that it's composed by 2 files (switch case == OK).

Assuming I have in my code a message object called response:

message response;

If I had to only send one file in a message, I would do:

response.message_u.fdata.last_mod_time         = last_modification;
response.message_u.fdata.contents.contents_len = file_size;
response.message_u.fdata.contents.contents_val = buffer;

With buffer being:

buffer = malloc(file_size * sizeof(char));

Now, I understand that struct file fdata<2>; it's a variable size array (2 is the max length) and that I would have them index as something[0] and something[1].

Also I know I need to allocate memory, but I do not know how to do this in this case with multiple files.

Do I need to do one single malloc for everything I need to send? Something like this:

response.message_u.fdata.fdata_val = malloc ( (file_size1 * sizeof(char)) + (file_size2 * sizeof(char)) + (2 * sizeof(uint32_t)));   

(2 * sizeof(uint32_t)): one last_mod_time for each file to send

And the other question how to I refer to each file structure:

response.message_u.fdata[0]                        //?
response.message_u.fdata.last_mod_time[0]          //?
response.message_u.fdata[0].last_mod_time          //?
response.message_u.fdata.contents.contents_len[0]  //?
response.message_u.fdata.contents.contents_val[0]  //?

The .x file:

enum tagtype {
    GET  = 0,
    OK   = 1,
    QUIT = 2,
    ERR  = 3
};

struct file {
    opaque contents<>;
    unsigned int last_mod_time;
};

typedef string filename<256>;

union message switch (tagtype tag) {
    case GET:
        filename filenamedata<2>;
    case OK:
        struct file fdata<2>;
    case QUIT:
        void;
    case ERR:
        void;
};

The types.c (generated with rpcgen):

#include "xdr_types.h"

bool_t
xdr_tagtype (XDR *xdrs, tagtype *objp)
{
    register int32_t *buf;

    if (!xdr_enum (xdrs, (enum_t *) objp))
         return FALSE;
    return TRUE;
}

bool_t
xdr_file (XDR *xdrs, file *objp)
{
    register int32_t *buf;

    if (!xdr_bytes (xdrs, (char **)&objp->contents.contents_val, (u_int *) &objp->contents.contents_len, ~0))
        return FALSE;
    if (!xdr_u_int (xdrs, &objp->last_mod_time))
        return FALSE;
    return TRUE;
}


bool_t
xdr_filename (XDR *xdrs, filename *objp)
{
    register int32_t *buf;

    if (!xdr_string (xdrs, objp, 256))
         return FALSE;
    return TRUE;
}

bool_t
xdr_message (XDR *xdrs, message *objp)
{
     register int32_t *buf;

     if (!xdr_tagtype (xdrs, &objp->tag))
         return FALSE;

    switch (objp->tag) {
        case GET:
            if (!xdr_array (xdrs, (char **)&objp->message_u.filenamedata.filenamedata_val, (u_int *) &objp->message_u.filenamedata.filenamedata_len, 10,
            sizeof (filename), (xdrproc_t) xdr_filename))
            return FALSE;
            break;
        case OK:
            if (!xdr_array (xdrs, (char **)&objp->message_u.fdata.fdata_val, (u_int *) &objp->message_u.fdata.fdata_len, 10, sizeof (file), (xdrproc_t) xdr_file))
             return FALSE;
            break;
        case QUIT:
            break;
        case ERR:
            break;
        default:
            return FALSE;
    }
    return TRUE;
}

Thank you for reading this, and trying to understand this. I really appreciate it.

Thank you!


Solution

  • To answer this question it is easier to take a look at the types: tagtype, file, filename and message that rpcgen generates from your .x file. They are located in your xdr_types.h which you didn't include in your question so I used rpcgen with your .x file. The important ones are file and message:

    ...
    
    struct file {
       struct {
          u_int contents_len;
          char *contents_val;
       } contents;
       u_int last_mod_time;
    };
    typedef struct file file;
    
    typedef char *filename;
    
    struct message {
       tagtype tag;
       union {
          struct {
             u_int filenamedata_len;
             filename *filenamedata_val;
          } filenamedata;
          struct {
             u_int fdata_len;
             struct file *fdata_val;
          } fdata;
       } message_u;
    };
    typedef struct message message;
    
    ...
    

    To answer your 2 questions:

    If I had to only send one file in a message, I would do:

    response.message_u.fdata.last_mod_time         = last_modification;
    response.message_u.fdata.contents.contents_len = file_size;
    response.message_u.fdata.contents.contents_val = buffer;
    

    With buffer being:

    buffer = malloc(file_size * sizeof(char));
    

    and

    And the other question how to I refer to each file structure:

    response.message_u.fdata[0]                        //?
    response.message_u.fdata.last_mod_time[0]          //?
    response.message_u.fdata[0].last_mod_time          //?
    response.message_u.fdata.contents.contents_len[0]  //?
    response.message_u.fdata.contents.contents_val[0]  //?
    

    No, the part after response.message_u.fdata. is wrong. It can either be:

    response.message_u.fdata.fdata_len
    

    or

    response.message_u.fdata.fdata_val
    

    and you mean the 2nd variant which is struct file *fdata_val;. And for that part you have to allocate memory.

    The following example is for 2 files which will also answer your question about how to refer to the data:

    // Set variables and allocate space for 2 file structures
    message response;
    u_int no_files      = 2;
    u_int file_size     = 1024;
    u_int last_mod_time = 42;
    response.message_u.fdata.fdata_len = no_files;
    response.message_u.fdata.fdata_val = malloc(no_files * sizeof(struct file));
    
    // Access 1st file and allocate space for file content
    response.message_u.fdata.fdata_val[0].last_mod_time         = last_modification;
    response.message_u.fdata.fdata_val[0].contents.contents_len = file_size;
    response.message_u.fdata.fdata_val[0].contents.contents_val = malloc(file_size * sizeof(char));
    
    // Access 2nd file and allocate space for file content
    response.message_u.fdata.fdata_val[1].last_mod_time         = last_modification;
    response.message_u.fdata.fdata_val[1].contents.contents_len = file_size;
    response.message_u.fdata.fdata_val[1].contents.contents_val = malloc(file_size * sizeof(char));
    

    To learn more about the relationship between the XDR specification language and the generated C output this answer on SO: Understanding XDR specification to create a *.x file is helpful.

    Further there is a nice book Power Programming with RPC by John Bloomer, it's quite old (1991) but the Remote Procedure Call protocol is it too. This book is about the RPC protocol that uses the XDR for sending/receiving data. The XDR language (using rpcgen, filter functions, etc.) is very well described in that book.

    Below are some XDR specifications:

    xdr specification language taken from link provided above