Thread: parsing char array to array of struct to process packets

  1. #1
    Registered User
    Join Date
    May 2013
    Posts
    5

    parsing char array to array of struct to process packets

    hi guys and gals.
    i wrote this simplified version of a program i am writing that parses data in UDP packets. In the process of doing so i pretty much answered all my questions and fix all the problems i was having.

    decodeSystemMap function will be in loop, and will proccess packets that have mostly the same data, only a few items will be added or changed or deleted.
    whats the best way to check if there are any new, deleted, or removed items in the packet and only modify those?
    Is there anything unsafe / dangrous about the way the code is now?

    Code:
    /*  * File:   main.c
     * Author: david
     *
     * Created on May 23, 2013, 11:57 AM
     */
    
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    #include <stdbool.h>
    #include <string.h>
    
    
    #define DATA_OFFSET 3
    
    
    //a sample packet with 2 device information fields
    unsigned char rxPacket1[13] = {0xff, 0xff, 0xff,
                                   0x01, 0x55, 0x02, 0x2b, 0x64, 
                                   0x02, 0x28, 0x1e, 0x36, 0x75};
    //same with only 1 device
    unsigned char rxPacket2[8] =  {0xff, 0xff, 0xff,
                                   0x01, 0x75, 0x64, 0xdf, 0xd0};
    //and 3 devices here
    unsigned char rxPacket3[18] = {0xff, 0xff, 0xff,
                                   0x01, 0x51, 0x49, 0x9e, 0x11, 
                                   0x02, 0xe6, 0xc1, 0x0d, 0x00, 
                                   0x03, 0x29, 0xe4, 0xd5, 0x66};
    //device structure
    typedef struct {
        uint8_t     id;
        uint16_t    var1;
        uint16_t    var2;
    }__attribute__((packed)) systemDevice_t;
    
    
    int decodeSystemMap(unsigned char *packet, uint16_t rxPacketSize, systemDevice_t *systemMap){
        int i; 
        int c=0;
        for (i=0;i<(rxPacketSize-DATA_OFFSET); i+=5) {
            systemDevice_t *t = (systemDevice_t*) (packet+i+DATA_OFFSET);         
            systemMap[c].id   = t->id;
            systemMap[c].var1 = ntohs(t->var1);
            systemMap[c].var2 = ntohs(t->var2);
            c++;
        }
        return c;
    }
    
    
    int main(int argc, char *argv[])    {
        int i;
        int numDevices;
        systemDevice_t* systemMap;
        
        //decode the  packet
        numDevices = decodeSystemMap(rxPacket1, sizeof(rxPacket1), systemMap);
        
        //print out the contents 
        for (i=0;i<numDevices;i++){
            printf("device %i id: %i var1:%04X var2:%04X\n", i, systemMap[i].id, systemMap[i].var1, systemMap[i].var2);
        }
    
    
    
    
    }
    Last edited by davidkay; 05-28-2013 at 08:55 AM.

  2. #2
    Registered User
    Join Date
    May 2012
    Posts
    505
    Use the equation val16 = (highbyte << 8) | lowbyte to portably extract a 16 bit value from a byte stream. Then there's no need for packed directives.
    It's not clear what you mean by data not changing much. Do you mean that packets commonly contain a large number of 5 byte values, and following packets have just the occasional insertion and deletion, and the occasional changed value?
    If that's the case, then you can certainly reduce the packet to a change representation. But it's going to be quite a lot of code. Normally it's easier just to say "this is the new packet".
    I'm the author of MiniBasic: How to write a script interpreter and Basic Algorithms
    Visit my website for lots of associated C programming resources.
    https://github.com/MalcolmMcLean


  3. #3
    Registered User
    Join Date
    May 2013
    Posts
    5
    malcom thanks for the reply
    i have a new question. The above program compiles and works, but the actual program im using would get segfaults.
    I can reproduce the segfault in the above program by declaring a struct directly after i declare systemMap

    Code:
    int i;
    int numDevices;
    systemDevice_t* s ystemMap;
    struct sockaddr_in serverSock;
    when compiled without serverSock, gdb shows after declaration ServerMap is

    Code:
    (gdb) print systemMap
    $1 = (systemDevice_t *) 0x7fffffffe140
    if i declare serverSock after systemMap
    Code:
    (gdb) print systemMap = (systemDevice_t *) 0x1
    (gdb)
    suggestions?
    i guess the issue is i am not initalize / allocating memory for system map?
    but im not how sure how to do that as the size changes

    *******EDIT**********
    errhh
    weill i think i fixed it with
    systemDevice_t systemMap[MAX_DEVICES];
    Last edited by davidkay; 05-28-2013 at 12:12 PM.

  4. #4
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Just for the lulz, here's an alternate approach.

    The device map is actually a single data structure. Since each device ID is just one char long, you can only have UCHAR_MAX+1 devices.

    Just to make it more interesting, I added the two functions -- adding a new device, and changing the properties of an existing device -- as callbacks.

    The sysmap.c below consumes binary data from standard input; device packets only, no padding.
    Code:
    #include <stdlib.h>
    #include <string.h>
    #include <limits.h>
    #include <stdio.h>
    #include <errno.h>
    
    /* Since the device ID is one char long, we can have up to 2**CHAR_BIT devices. */
    #define   MAX_DEVICES  (UCHAR_MAX + 1U)
    
    /* Device state map */
    typedef struct {
        unsigned char   state[MAX_DEVICES];
        unsigned short  var1[MAX_DEVICES];
        unsigned short  var2[MAX_DEVICES];
    } device_map_t;
    #define   STATE_UNUSED   0U
    #define   STATE_DEVICE   1U  /* Only used by example_add()! */
    
    void map_init(device_map_t *const map)
    {
        if (map) {
            size_t  i = MAX_DEVICES;
            while (i-->0) {
                map->state[i] = STATE_UNUSED;
                map->var1[i]  = 0U;
                map->var2[i]  = 0U;
            }
        }
    }
    
    /* Parse (limit - *dataptr) bytes at (*dataptr), i.e. from (*dataptr) up to but not including (limit).
     * Record structure is
     *    1 byte    Device ID
     *    2 bytes   Network endian var1
     *    2 bytes   Network endian var2
     * dataptr will be advanced to first un-decoded byte.
     * If the function returns zero, check errno for errors:
     *    0         No errors, at least one packet was decoded
     *    EAGAIN    Incomplete data/no data, need more
     *    EDOM      Invalid device ID
     *    ENOSPC    set() callback failed
     *    ENOENT    add() callback failed
     *
     * For each device in the data:
     *    - If the map state for that device is STATE_UNUSED, the
     *          add(map, id, var1, var2)
     *      callback will be called.
     *      If the callback returns nonzero, the function will abort.
     *    - If the map state for that device is not STATE_UNUSED,
     *      and either var1 or var2 or both differ from the mapped state, the
     *          set(map, id, var1, var2)
     *      callback will be called.
     *      If the callback returns nonzero, the function will abort.
    */
    int decode(const unsigned char **const dataptr, const unsigned char *const limit, device_map_t *const map,
               int (*add)(device_map_t *const, const unsigned char, const unsigned short, const unsigned short),
               int (*set)(device_map_t *const, const unsigned char, const unsigned short, const unsigned short))
    {
        if (!dataptr || !limit || !map) {
            errno = EINVAL; /* EINVAL: Invalid parameters. */
            return 0;
    
        } else
        if (*dataptr + 5 < limit) {
            errno = EAGAIN; /* EAGAIN: Not enough data yet. */
            return 0;
    
        } else {
            const unsigned char *data = *dataptr;
            int                  retval;
    
            while (data + 5 <= limit) {
                const unsigned char  id = data[0];
                const unsigned int   var1 = data[1] * 256U + data[2];
                const unsigned int   var2 = data[3] * 256U + data[4];
    
    #if MAX_DEVICES <= UCHAR_MAX
                /* Make sure device ID is not too large. */
                if (id >= MAX_DEVICES) {
                    errno = EDOM; /* EDOM: Invalid device ID. */
                    return 0;
                }
    #endif
    
                if (map->state[id] == STATE_UNUSED) {
    
                    if (add != NULL) {
                        retval = add(map, id, var1, var2);
                        if (retval) {
                            errno = ENOSPC; /* ENOSPC: add() failed */
                            return retval;
                        }
                    }
    
                } else
                if (map->var1[id] != var1 || map->var2[id] != var2) {
    
                    if (set != NULL) {
                        retval = set(map, id, var1, var2);
                        if (retval) {
                            errno = ENOENT; /* ENOENT: set() failed */
                            return retval;
                        }
                    }
                }
    
                /* Advance pointer to next possible data structure */
                data += 5;
                *dataptr = data;
            }
    
            errno = 0; /* OK */
            return 0;
        }
    }
                
    /* Example add callback. */
    int example_add(device_map_t *const  map,
                    const unsigned char  id, 
                    const unsigned short var1,
                    const unsigned short var2)
    {
        map->state[id] = STATE_DEVICE;
        map->var1[id] = var1;
        map->var2[id] = var2;
    
        fprintf(stdout, "Added device %d: var1 = %d, var2 = %d\n", id, var1, var2);
        fflush(stdout);
    
        return 0;
    }
    
    /* Example set callback. */
    int example_set(device_map_t *const  map,
                    const unsigned char  id,
                    const unsigned short var1,
                    const unsigned short var2)
    {
    
        fprintf(stdout, "Changed device %d: var1 = %d (was %d), var2 = %d (was %d)\n",
                id, var1, map->var1[id], var2, map->var2[id]);
        fflush(stdout);
    
        map->var1[id] = var1;
        map->var2[id] = var2;
    
        return 0;
    }
    
    int main(void)
    {
        device_map_t   map;
        unsigned char  data_buffer[16]; /* Anything larger than 2 packets is ok. */
        unsigned char *data_next = data_buffer;
        unsigned char *data_ends = data_buffer;
        int            c, result;
    
        /* Clear device map. */
        map_init(&map);
    
        while (1) {
    
            result = decode((const unsigned char **)&data_next, data_ends, &map, example_add, example_set);
            if (result)
                switch (errno) {
                case ENOSPC:
                    fprintf(stderr, "add() callback failed, return value %d.\n", result);
                    return EXIT_FAILURE;
                case ENOENT:
                    fprintf(stderr, "set() callback failed, return value %d.\n", result);
                    return EXIT_FAILURE;
                default:
                    fprintf(stderr, "Bug in decode(), return value %d.\n", result);
                    return EXIT_FAILURE;
                }
            /* Result is 0, so check errno too */
            if (errno != 0 && errno != EAGAIN) {
                fprintf(stderr, "decode() failed: %s.\n", strerror(errno));
                return EXIT_FAILURE;
            }
    
            /* Buffer empty? Need to repack the buffer? */
            if (data_next >= data_ends) {
                data_next = data_buffer;
                data_ends = data_buffer;
            } else
            if (data_next >= data_buffer + (sizeof data_buffer) / 2) {
                memmove(data_buffer, data_next, (size_t)(data_ends - data_next));
                data_ends = data_buffer + (data_ends - data_next);
                data_next = data_buffer;
            }
    
    #ifdef PARANOID
            if (data_ends >= data_buffer + sizeof data_buffer) {
                /* Buffer is full already! */
                fprintf(stderr, "decode() left a full buffer!\n");
                return EXIT_FAILURE;
            }
    #endif
    
            /* decode() has processed all data it could from the buffer,
             * so we definitely need more. */
            c = getc(stdin);
            if (c == EOF)
                break;
    
            /* Append to buffer. */
            *(data_ends++) = c;
        }
    
        if (data_ends > data_next)
            fprintf(stderr, "No more data; last %d bytes ignored.\n", (int)(data_ends - data_next));
        else
            fprintf(stderr, "No more data; all data processed.\n");
    
        return 0;
    }
    Compile using e.g.
    Code:
    gcc -W -Wall -O3 sysmap.c -o sysmap
    and run your test cases using e.g.
    Code:
    printf '\x01\x55\x02\x2b\x64\x02\x28\x1e\x36\x75' | ./sysmap
    printf '\x01\x75\x64\xdf\xd0' | ./sysmap
    printf '\x01\x51\x49\x9e\x11\x02\xe6\xc1\x0d\x00\x03\x29\xe4\xd5\x66' | ./sysmap

  5. #5
    Registered User
    Join Date
    May 2013
    Posts
    5
    uhhu i can say is wow! im goint to figure this out now and report back!
    thanks

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Struct array - char array
    By Veda88 in forum C Programming
    Replies: 7
    Last Post: 09-25-2009, 09:55 AM
  2. parsing char array
    By brb9412 in forum C Programming
    Replies: 1
    Last Post: 12-30-2008, 08:20 AM
  3. ANSI C parsing char array as pointer
    By cyberjunky in forum C Programming
    Replies: 3
    Last Post: 11-30-2008, 03:25 AM
  4. Parsing char array to CString and int array
    By wawocat in forum C Programming
    Replies: 2
    Last Post: 10-25-2007, 08:05 PM