Jump to content
43oh

Shiv

Members
  • Content Count

    11
  • Joined

  • Last visited

Reputation Activity

  1. Like
    Shiv reacted to spirilis in Parsing "strings" in C - Attempting to parse MQTT payload   
    Anyway, that was fun... I'm glad I spent some time to write all that.  I'll end up using it later
    edit: Never did test the &key& scenario - that would produce a key that is equal to the whole rest of the string and NULL pointer for value.  Probably need if (key != NULL && value != NULL) before doing the run_callback stuff.
  2. Like
    Shiv reacted to spirilis in Parsing "strings" in C - Attempting to parse MQTT payload   
    Also for kicks, I ran the first code (test.c) with some extra quirks to test its limits:
     
    char text[] = "cmd=load&&&&&&&&&&&&&color=red&bg=blueα=0.56&&blah=&&&&=wtf&&&&";
     
    Here's the output:
    ebrundic@spock:~/inst$ ./test key=[cmd], value=[load] key=[color], value=[red] key=[bg], value=[blue] key=[alpha], value=[0.56] key=[blah], value=[] key=[wtf], value=[wtf] The only output that seems odd is the "=wtf" case, since the code took the value and pointed both key and value pointers to the value.  A state variable (called no_key here) could protect against this:
    #include <stdio.h> #include <stdint.h> #include <sys/types.h> #include <string.h> void run_callback(const char *, const char *); int main(int argc, char *argv[]) { size_t i; char text[] = "cmd=load&&&&&&&&&&&&&color=red&bg=blueα=0.56&&blah=&&&&=wtf&&&&"; size_t tlen = strlen(text); char *key=NULL, *value=NULL; int no_key = 0; for (i=0; i < tlen; i++) { if (text[i] == '&') { // end of key/value pair text[i] = '\0'; if (key != NULL) { run_callback(key, value); } key = NULL; value = NULL; no_key = 0; continue; } if (text[i] == '=') { // end of key, start of value no_key = 1; text[i] = '\0'; value = &text[i+1]; continue; } if (!no_key && key == NULL) { key = &text[i]; } // otherwise do nothing, keep incrementing i } // end of string, we might have a loaded key/value pair to process at the very end if (key != NULL) { run_callback(key, value); } return(0); } void run_callback(const char *key, const char *value) { if (key == NULL || value == NULL) { printf("key=ptr[%p], value=ptr[%p]\n", key, value); } else { printf("key=[%s], value=[%s]\n", key, value); } } which produces:
    key=[cmd], value=[load] key=[color], value=[red] key=[bg], value=[blue] key=[alpha], value=[0.56] key=[blah], value=[]
  3. Like
    Shiv reacted to spirilis in Parsing "strings" in C - Attempting to parse MQTT payload   
    Here's a simple custom tokenizer that modifies data in-place (I wrote this in C for execution at the command line in Linux, but, the code can be ripped into an MCU just the same):
    #include <stdio.h> #include <stdint.h> #include <sys/types.h> #include <string.h> void run_callback(const char *, const char *); int main(int argc, char *argv[]) { size_t i; char text[] = "cmd=load&color=red&bg=blueα=0.56"; size_t tlen = strlen(text); char *key=NULL, *value=NULL; for (i=0; i < tlen; i++) { if (text[i] == '&') { // end of key/value pair text[i] = '\0'; if (key != NULL) { run_callback(key, value); } key = NULL; value = NULL; continue; } if (text[i] == '=') { // end of key, start of value text[i] = '\0'; value = &text[i+1]; continue; } if (key == NULL) { key = &text[i]; } // otherwise do nothing, keep incrementing i } // end of string, we might have a loaded key/value pair to process at the very end if (key != NULL) { run_callback(key, value); } return(0); } void run_callback(const char *key, const char *value) { // do something with key & value - replace these with something more relevant to your application if (key == NULL || value == NULL) { printf("key=ptr[%p], value=ptr[%p]\n", key, value); } else { printf("key=[%s], value=[%s]\n", key, value); } } If it's absolutely mandatory that the original text NOT be modified (you should qualify this restriction though, since if it can be modified, this solution works fine), you can modify this tokenizer to copy the key & value into buffers:
    #include <stdio.h> #include <stdint.h> #include <sys/types.h> #include <string.h> void run_callback(const char *, const char *); #define KEYVALUE_BUFFER_MAXLEN 32 int main(int argc, char *argv[]) { size_t i; char text[] = "cmd=load&color=red&bg=blueα=0.56"; size_t tlen = strlen(text); char *key=NULL, *value=NULL; size_t keystart=0, valuestart=0, tmpsz; char keybuf[KEYVALUE_BUFFER_MAXLEN], valuebuf[KEYVALUE_BUFFER_MAXLEN]; for (i=0; i < tlen; i++) { if (text[i] == '&') { // end of key/value pair // copy value into buffer tmpsz = i-valuestart; if (tmpsz > KEYVALUE_BUFFER_MAXLEN-1) { tmpsz = KEYVALUE_BUFFER_MAXLEN - 1; // minus 1 to provide space for the '\0' terminator } memcpy(valuebuf, value, tmpsz); valuebuf[tmpsz] = '\0'; if (key != NULL) { run_callback(keybuf, valuebuf); } key = NULL; value = NULL; continue; } if (text[i] == '=') { // end of key, start of value // copy key into buffer tmpsz = i-keystart; if (tmpsz > KEYVALUE_BUFFER_MAXLEN-1) { tmpsz = KEYVALUE_BUFFER_MAXLEN - 1; } memcpy(keybuf, key, tmpsz); keybuf[tmpsz] = '\0'; value = &text[i+1]; valuestart = i+1; continue; } if (key == NULL) { key = &text[i]; keystart = i; } // otherwise do nothing, keep incrementing i } // end of string, we might have a loaded key/value pair to process at the very end if (key != NULL) { // copy value into buffer tmpsz = i-valuestart; if (tmpsz > KEYVALUE_BUFFER_MAXLEN-1) { tmpsz = KEYVALUE_BUFFER_MAXLEN - 1; } memcpy(valuebuf, value, tmpsz); valuebuf[tmpsz] = '\0'; run_callback(keybuf, valuebuf); } return(0); } void run_callback(const char *key, const char *value) { // do something with key & value - replace these with something more relevant to your application if (key == NULL || value == NULL) { printf("key=ptr[%p], value=ptr[%p]\n", key, value); } else { printf("key=[%s], value=[%s]\n", key, value); } } Note the cost here .... extra code.  Being able to modify the data in-place tidies things up a bit.  What I wrote up above could be reworked to use strtok, but I don't see much point since replicating strtok's code is reasonably compact anyhow (and we're doing a purpose-built version that acts as a state machine unique to your particular data).
  4. Like
    Shiv reacted to Clavier in Parsing "strings" in C - Attempting to parse MQTT payload   
    If a byte and a char have the same size, casting is no problem.
     
    To avoid modifying the data, don't use zero-terminated strings; just use pointer+length everywhere:
    static void parseParameter(const char *param, size_t length); static void handleNameValue(const char *name, size_t nameLength, const char *value, size_t valueLength); static void handleCmd(const char *value, size_t length); static void handleColor(const char *value, size_t length); // etc. void parsePayload(const char *payload, size_t length) { size_t i, paramStartIndex = 0; for (i = 0; i < length; i++) { if (payload[i] == '&') { parseParameter(paramStartIndex, i - paramStartIndex); paramStartIndex = i + 1; } } parseParameter(paramStartIndex, length - paramStartIndex); } static void parseParameter(const char *param, size_t length) { size_t i; for (i = 0; i < length; i++) if (param[i] == '=') { handleNameValue(param, i, &param[i + 1], length - (i + 1)); break; } } #define memeq(a,b,sz) (!memcmp(a,b,sz)) #define isString(value, length, literal) (length == sizeof(literal) - 1 && memeq(value, literal, length)) static void handleNameValue(const char *name, size_t nameLength, const char *value, size_t valueLength) { if (isString(name, nameLength, "cmd")) { handleCmd(value, valueLength); } else if (isString(name, nameLength, "color")) { handleColor(value, valueLength); } else if (isString(name, nameLength, "state")) { handleState(value, valueLength); } } static void handleCmd(const char *value, size_t length) { if (isString(value, length, "led")) { remoteCommand.command = Command::Led; } } static void handleColor(const char *value, size_t length) { if (isString(value, length, "red")) { remoteCommand.ledColor = LedColor::red; } else if (isString(value, length, "green")) { remoteCommand.ledColor = LedColor::green; } else if (isString(value, length, "blue")) { remoteCommand.ledColor = LedColor::blue; } } // etc.
×
×
  • Create New...