From db8bb01bd618f0c2649369340a9e9daf09305a98 Mon Sep 17 00:00:00 2001 From: Mathias Laurenz Baumann Date: Fri, 5 Feb 2010 18:39:42 +0100 Subject: [PATCH] added api without callbacks, added first impl. --- include/psyc_impl.h | 397 +++++++++++++++++++++++++++ include/psyc_parser.h | 135 +++++++++ include/{psyc.h => psyc_parser_cb.h} | 59 ++-- 3 files changed, 574 insertions(+), 17 deletions(-) create mode 100644 include/psyc_impl.h create mode 100644 include/psyc_parser.h rename include/{psyc.h => psyc_parser_cb.h} (72%) diff --git a/include/psyc_impl.h b/include/psyc_impl.h new file mode 100644 index 0000000..6ea2681 --- /dev/null +++ b/include/psyc_impl.h @@ -0,0 +1,397 @@ +#include + +/** @brief isGlyph + * + * @todo: document this function + */ +inline char isGlyph(uint8_t g) +{ + switch(g) + { + case ':': + case '-': + case '+': + case '=': + return 1; + default: + return 0; + } + +} + + +inline char isAlphaNumeric(uint8_t c) +{ + return + (( c >= 'a' && c <= 'z' )|| + ( c >= 'A' && c <= 'Z' )|| + ( c >= '0' && c <= '9' )) + ; + +} + +inline int PSYC_parseElement( + unsigned int* cursor, + const uint8_t * data, unsigned int dlength, + const uint8_t** name, unsigned int *nlength, + const uint8_t** value, unsigned int *vlength, + char inHeader); + +/** @brief parses a routerVariable + * + * This function parses one routing variable, + * advances the cursor after the variable, + * writes the variables name, value and their + * lengths in the corresponding out parameters + * and returns 0 or an errorcode. + * + * + * + * + * @param data pointer to the packet data + * @param dlength length of the data (amount of bytes) + * @param cursor pointer to the current parsing position. + * should be initialized to 0. + * @param name pointer-pointer, used to return the position + * of the name string + * @param nlength pointer to which the length of + * the name string will be written + * @param value pointer-pointer, used to retrun the position + * of the value string + * @param vlength pointer to which the length of + * the value string will be written + * + * @returns 0 on success + * 1 on insufficient data. + * This does not advance + * the cursor. + * 2 when no longer in the header, + * This advances the cursor to the + * body/entity section, but leaves + * the other out parameters invalid. + * 3 the packet is complete. + * >3 on a context error, + * <0 on a parsing error. + * This invalidates all but the cursor + * out paramater. */ +inline int PSYC_parseHeader( + unsigned int* cursor, + const uint8_t * data, unsigned int dlength, + const uint8_t** name, unsigned int *nlength, + const uint8_t** value, unsigned int *vlength) +{ + return PSYC_parseElement(cursor,data,dlength,name,nlength,value,vlength,1); +} + + + + +/** @brief parses one bodyelement + * + * This function is nearly identical to + * its brother parseClosedBody. * + * + * It assumes that we don’t know the + * real length of the packet and thus + * searches for the terminator. + */ +inline int PSYC_parseOpenBody( + unsigned int* cursor, + const uint8_t * data, unsigned int dlength, + const uint8_t** name, unsigned int *nlength, + const uint8_t** value, unsigned int *vlength) +{ + + return PSYC_parseElement(cursor,data,dlength,name,nlength,value,vlength,0); +} + + + + + + + + + +/** @brief generalized linebased parser */ +inline int PSYC_parseElement( + unsigned int* cursor, + const uint8_t * data, unsigned int dlength, + const uint8_t** name, unsigned int *nlength, + const uint8_t** value, unsigned int *vlength, + char inHeader) +{ + /* first we test if we can access the first char */ + if(dlength<*cursor) // cursor is not inside the length + return 1; // return insufficient data. + + // in case we return insufficent, we rewind to the start. + unsigned int startc=*cursor; + + /* each line of the header starts with a glyph. + * iE :_name, -_name +_name etc, so just test if + * the first char is a glyph. */ + if(1==inHeader && !isGlyph(data[*cursor])) // is the first char not a glyph? + { + // the only other possibility now is that the packet + // is complete(empty packet) or that the method started. + if(isAlphaNumeric(data[*cursor])) + return 2; // return header finished + + if(data[*cursor] == '|') + { + if(dlength<++(*cursor)) // incremented cursor inside lenght? + { + *cursor=startc; // set to start value + return 1; // return insufficient + } + + if(data[*cursor]=='\n') + { + ++(*cursor); + return 3; // return packet finished + } + } + return -6; // report error + } + + char method=0; + /* in the body, the same applies, only that the + * method does not start with a glyph.*/ + if(0==inHeader && !isGlyph(data[*cursor])) + if(!isAlphaNumeric(data[*cursor])) + { + // the body rule is optional, which means + // that now also just |\n can follow. + if(data[*cursor] == '|') + { + if(dlength<++(*cursor)) // incremented cursor inside lenght? + { + *cursor=startc; // set to start value + return 1; // return insufficient + } + + if(data[*cursor]=='\n') + { + ++(*cursor); + return 3; // return packet finished + } + } + return -5; // report error + } + else + method=1; + + /* validate the incremented cursor */ + if(dlength<++(*cursor)) + { + *cursor=startc; + return 1; + } + + /* what follows is the name. At least one char. + * allowed is alphanumeric and _ */ + + // first char must exist. + if(!isAlphaNumeric(data[*cursor]) // is it not alphanum + && data[*cursor] != '_') // AND not '_'. must be invalid then + return -1; // return parser error. + + + *name=data+*cursor; // the name starts here. + *nlength=1; // the initial length is 1. + + + /* now checking how long the name of the variable is. */ + unsigned int i=0; + while(1) + { + if(dlength<++(*cursor)) // incremented cursor inside lenght? + { + *cursor=startc; // set to start value + return 1; // return insufficient + } + + // same as bevore + if(!isAlphaNumeric(data[*cursor]) && + data[*cursor] != '_') + break; // not valid? then stop the loop right here + + ++(*nlength); // was a valid char, increase length + + } + + /* we now parsed the variable name successfully + * after the name either a \n or a \t follows. + * + * for the method, the data starts after an \n + * so checking for \n too here + + * We dont check if cursor inside length, because + * the last loop iteration did that already. */ + if(data[*cursor] == '\t' || (data[*cursor] == '\n' && method==1)) + { + /* after the \t the arg-data follows, which is + * anything but \n. arg-data can be of length 0 + * + * for the method: after the \n data follows, + * which is anything but \n|\n + * + * arg-data=*value. we set value here so it + * points to a valid range and so we point + * to the first potential arg-data byte. + * If there is no arg-data, we still have + * the length attribute on 0. */ + *value=data+*cursor+1; + *vlength=0; + + while(1) + { + if(dlength<++(*cursor)) // incremented cursor inside lenght? + { + *cursor=startc; // set to start value + return 1; // return insufficient + } + + if(data[*cursor] == '\n') + { + if(method==1) + { + if(dlength<++(*cursor)+1) // incremented cursor inside lenght? + { + *cursor=startc; // set to start value + return 1; // return insufficient + } + + /* packet finishes here */ + if(data[*cursor] == '|' && + data[*cursor+1] == '\n') + return 3; + + }else + break; + } + ++(*vlength); + } + + } + + + /* if there was a \t, then we parsed up until the + * \n char from the simple-arg rule ( \t arg-data \n ) + * + * Now, if there would be no \t, we still would be at + * the point where a \n must follow. + * + * So, just checking \n here will cover both cases of + * the alternative ( simple-arg / \n ) from rule + * routing-modifier + * + * again, the loop has already validated the cursors + * position*/ + if(data[*cursor] != '\n') + return -2; // return parser error. + + /* if a \n follows now, the an body is attached. + * if not, a |\n must follow */ + if(dlength<++(*cursor)) // incremented cursor inside lenght? + { + *cursor=startc; // set to start value + return 1; // return insufficient + } + + if(*cursor == '\n') + return 0; // line is complete + else if(*cursor != '|') + return -3; + + if(dlength<++(*cursor)) // incremented cursor inside lenght? + { + *cursor=startc; // set to start value + return 1; // return insufficient + } + if(*cursor != '\n') + return -4; + + + return 3; // packet is complete + + +} + +/** @brief parses one variable in two buffers + * + * This function is nearly identical to its + * brother parseHeader. The difference is, + * it uses two buffers and return parameters + * for everything. It is meant to be used + * in case parseHeader returned 2 for + * insufficient data and you don’t + * like to copy memory around to + * have all the data in one buffer. + * Using this function, you can pass two + * data buffers. The consequence is, + * that name and value can be distributed + * on two different buffers and thus need + * also two out paramaters. If only one + * will be used, length of the second + * will be 0. + * + * If your data is spread over more + * than two buffers, you need to + * copy that in one or two buffers. + * Given the unlikleyness of that + * event, we don't offer a three + * or more buffer function here. + * + */ +int PSYC_parseHeader2( + unsigned int* cursor, + const uint8_t * data1, unsigned int dlength1, + const uint8_t * data2, unsigned int dlength2, + const uint8_t** name1, unsigned int *nlength1, + const uint8_t** name2, unsigned int *nlength2, + const uint8_t** value1, unsigned int *vlength1, + const uint8_t** value2, unsigned int *vlength2); + +/** @brief parses one bodyelement + * + * This parses one body element, that is + * either an entity-variable or the method + * + * The function assumes that dlength is set + * to the exact length of the packet + * so that data[dlength-1] would be the + * ending "\n" of the packet. + * + * The parameters are nearly the same as for + * PSYC_routerVariable, only difference is + * that a returnvalue of 2 means, we encountered + * the method. + * This means that the out paramterer + * name contains the methodname and + * value the content. + * */ +int PSYC_parseClosedBody( + unsigned int* cursor, + const uint8_t * data, unsigned int dlength, + const uint8_t** name, unsigned int *nlength, + const uint8_t** value, unsigned int *vlength); + +/* @brief parses an bodyelement in two buffers + * + * This function is the brother of parseHeader2. + * It behaives the same as + * parseOpenBody and parseHeader2. */ +int PSYC_parseOpenBody2( + unsigned int* cursor, + const uint8_t * data1, unsigned int dlength1, + const uint8_t * data2, unsigned int dlength2, + const uint8_t** name1, unsigned int *nlength1, + const uint8_t** name2, unsigned int *nlength2, + const uint8_t** value1, unsigned int *vlength1, + const uint8_t** value2, unsigned int *vlength2); + + + + diff --git a/include/psyc_parser.h b/include/psyc_parser.h new file mode 100644 index 0000000..6d5f326 --- /dev/null +++ b/include/psyc_parser.h @@ -0,0 +1,135 @@ +/** @brief parses a routerVariable + * + * This function parses one routing variable, + * advances the cursor after the variable, + * writes the variables name, value and their + * lengths in the corresponding out parameters + * and returns 0 or an errorcode. + * + * + * + * + * @param data pointer to the packet data + * @param dlength length of the data (amount of bytes) + * @param cursor pointer to the current parsing position + * @param name pointer-pointer, used to return the position + * of the name string + * @param nlength pointer to which the length of + * the name string will be written + * @param value pointer-pointer, used to retrun the position + * of the value string + * @param vlength pointer to which the length of + * the value string will be written + * + * @returns 0 on success + * 1 on insufficient data. + * This does not advance + * the cursor. + * 2 when no longer in the header, + * This advances the cursor to the + * body/entity section, but leaves + * the other out parameters invalid. + * >2 on a context error, + * <0 on a parsing error. + * This invalidates all but the cursor + * out paramater. */ +int PSYC_parseHeader( + unsigned int* cursor, + const uint8_t * data, unsigned int dlength, + const uint8_t** name, unsigned int *nlength, + const uint8_t** value, unsigned int *vlength); + +/** @brief parses one variable in two buffers + * + * This function is nearly identical to its + * brother parseHeader. The difference is, + * it uses two buffers and return parameters + * for everything. It is meant to be used + * in case parseHeader returned 2 for + * insufficient data and you don’t + * like to copy memory around to + * have all the data in one buffer. + * Using this function, you can pass two + * data buffers. The consequence is, + * that name and value can be distributed + * on two different buffers and thus need + * also two out paramaters. If only one + * will be used, length of the second + * will be 0. + * + * If your data is spread over more + * than two buffers, you need to + * copy that in one or two buffers. + * Given the unlikleyness of that + * event, we don't offer a three + * or more buffer function here. + * + */ +int PSYC_parseHeader2( + unsigned int* cursor, + const uint8_t * data1, unsigned int dlength1, + const uint8_t * data2, unsigned int dlength2, + const uint8_t** name1, unsigned int *nlength1, + const uint8_t** name2, unsigned int *nlength2, + const uint8_t** value1, unsigned int *vlength1, + const uint8_t** value2, unsigned int *vlength2); + +/** @brief parses one bodyelement + * + * This parses one body element, that is + * either an entity-variable or the method + * + * The function assumes that dlength is set + * to the exact length of the packet + * so that data[dlength-1] would be the + * ending "|" of the packet. + * + * The parameters are nearly the same as for + * PSYC_routerVariable, only difference is + * that a returnvalue of 2 means, we encountered + * the method. + * This means that the out paramterer + * name contains the methodname and + * value the content. + * */ +int PSYC_parseClosedBody( + unsigned int* cursor, + const uint8_t * data, unsigned int dlength, + const uint8_t** name, unsigned int *nlength, + const uint8_t** value, unsigned int *vlength); + +/** @brief parses one bodyelement + * + * This function is nearly identical to + * its brother parseClosedBody. * + * + * It assumes that we don’t know the + * real length of the packet and thus + * searches for the terminator. + */ +int PSYC_parseOpenBody( + unsigned int* cursor, + const uint8_t * data, unsigned int dlength, + const uint8_t** name, unsigned int *nlength, + const uint8_t** value, unsigned int *vlength); + + + +/* @brief parses an bodyelement in two buffers + * + * This function is the borther of parseHeader2. + * It behaives the same as + * parseOpenBody and parseHeader2. */ +int PSYC_parseOpenBody2( + unsigned int* cursor, + const uint8_t * data1, unsigned int dlength1, + const uint8_t * data2, unsigned int dlength2, + const uint8_t** name1, unsigned int *nlength1, + const uint8_t** name2, unsigned int *nlength2, + const uint8_t** value1, unsigned int *vlength1, + const uint8_t** value2, unsigned int *vlength2); + + + + +#include diff --git a/include/psyc.h b/include/psyc_parser_cb.h similarity index 72% rename from include/psyc.h rename to include/psyc_parser_cb.h index 7baf689..25b212e 100644 --- a/include/psyc.h +++ b/include/psyc_parser_cb.h @@ -1,3 +1,15 @@ +#include + +struct PSYC_Parser; + +/** @brief initialize a pstate struct + * + * @param pstate pointer to an allocated + * PSYC_Parser struct. + */ +void PSYC_initState(struct PSYC_Parser* pstate); + + /** @brief parses a packet * * This function parses rawdata @@ -26,7 +38,9 @@ * raw data that is to be processed. * @param length the amount of bytes to parse * @param pstate pointer to a preallocated - * instance of the struct state + * and initialized (PSYC_initState) + * instance of the struct state + * */ void PSYC_parse(const uint8_t* data, unsigned int length, struct PSYC_Parser* pstate); @@ -40,8 +54,7 @@ enum PSYC_Modifier DIMINISH=0x08, SET=0x10, QUERY=0x20 -} - +}; struct PSYC_Parser { @@ -63,19 +76,19 @@ struct PSYC_Parser * @param modifers modifer of the variable (see Modifer) * @param inEntity wether this variable is an entity * variable(true) or a routing variable(false) */ - void (*stateCallback)(PSYC_Parser* pstate, + void (*stateCallback)(struct PSYC_Parser* pstate, const uint8_t *name, const unsigned int nlength, - const uint8_t *value, const unsigned int vlength; - PSYC_Modifer modifiers, bool inEntity); + const uint8_t *value, const unsigned int vlength, + enum PSYC_Modifier modifiers, char inEntity); /** @brief gets called after the routing-header was parsed * - * @return if true, parser will continue to parse + * @return if 0, parser will continue to parse * the content part and calls bodyCallback * when finished, - * if false, parser will stop parsing and + * if not 0, parser will stop parsing and * calls contentCallback */ - bool (*routingCallback)(PSYC_Parser* pstate); + char (*routingCallback)(struct PSYC_Parser* pstate); /** @brief Body callback, gets called when the body was parsed * @@ -89,9 +102,9 @@ struct PSYC_Parser * containing the data section * @param content not null terminated c-string * @param clength length of the content string */ - void (*bodyCallback)(PSYC_Parser* pstate, + void (*bodyCallback)(struct PSYC_Parser* pstate, const uint8_t* method, unsigned int mlength, - const uint8_t* data, unsigned int dlength + const uint8_t* data, unsigned int dlength, const uint8_t* content, unsigned int clength); /** @brief Error callback, gets called to indicate @@ -110,7 +123,7 @@ struct PSYC_Parser * * Any previous state or body callbacks become * invalid and have to be purged.*/ - void (*errorCallback)(PSYC_Parser* pstate, + void (*errorCallback)(struct PSYC_Parser* pstate, const uint8_t *method, unsigned int mlength); /** @brief error state callback @@ -119,11 +132,23 @@ struct PSYC_Parser * The callback will be called once for each * state variable in the error report packet */ - void (*errorStateCallback)(PSYC_Parser* pstate, + void (*errorStateCallback)(struct PSYC_Parser* pstate, const uint8_t *name, const unsigned int nlength, - const uint8_t *value, const unsigned int vlength; - PSYC_Modifer modifiers); - -} + const uint8_t *value, const unsigned int vlength, + enum PSYC_Modifier modifiers); + + + /******************************************* + * The following variables and datatypes * + * are being used to remember the * + * internal state. You should not * + * touch them. * + *******************************************/ + uint8_t glyph; + unsigned int contpos; // position inside the content + unsigned int mstart,mlength, // position and length of the method + dstart,dlength; // + +};