From cad560ae7962310a4073e9e8537dc82c7ad3ed79 Mon Sep 17 00:00:00 2001 From: Lev Walkin Date: Sat, 16 Mar 2013 07:00:58 -0700 Subject: [PATCH] make one-pass parsing for object identifier --- skeletons/INTEGER.c | 51 ++++++++++++++++++++++++++++++----- skeletons/INTEGER.h | 11 +++++--- skeletons/OBJECT_IDENTIFIER.c | 45 ++++++++++++++++--------------- skeletons/tests/check-OIDs.c | 13 ++++++--- 4 files changed, 86 insertions(+), 34 deletions(-) diff --git a/skeletons/INTEGER.c b/skeletons/INTEGER.c index 8203ffd1..56d02b49 100644 --- a/skeletons/INTEGER.c +++ b/skeletons/INTEGER.c @@ -499,14 +499,17 @@ INTEGER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chun break; case ST_DIGITS: dec_value_end = lstop; + /* FALL THROUGH */ case ST_DIGITS_TRAILSPACE: /* The last symbol encountered was a digit. */ - switch(asn_strtol(dec_value_start, dec_value_end, &dec_value)) { + switch(asn_strtol_lim(dec_value_start, &dec_value_end, &dec_value)) { case ASN_STRTOL_OK: break; case ASN_STRTOL_ERROR_RANGE: return XPBD_DECODER_LIMIT; case ASN_STRTOL_ERROR_INVAL: + case ASN_STRTOL_EXPECT_MORE: + case ASN_STRTOL_EXTRA_DATA: return XPBD_BROKEN_ENCODING; } break; @@ -950,15 +953,44 @@ asn_long2INTEGER(INTEGER_t *st, long value) { return 0; } +/* + * This function is going to be DEPRECATED soon. + */ enum asn_strtol_result_e asn_strtol(const char *str, const char *end, long *lp) { + const char *endp = end; + + switch(asn_strtol_lim(str, &endp, lp)) { + case ASN_STRTOL_ERROR_RANGE: + return ASN_STRTOL_ERROR_RANGE; + case ASN_STRTOL_ERROR_INVAL: + return ASN_STRTOL_ERROR_INVAL; + case ASN_STRTOL_EXPECT_MORE: + return ASN_STRTOL_ERROR_INVAL; /* Retain old behavior */ + case ASN_STRTOL_OK: + return ASN_STRTOL_OK; + case ASN_STRTOL_EXTRA_DATA: + return ASN_STRTOL_ERROR_INVAL; /* Retain old behavior */ + } + + return ASN_STRTOL_ERROR_INVAL; /* Retain old behavior */ +} + +/* + * Parse the number in the given string until the given *end position, + * returning the position after the last parsed character back using the + * same (*end) pointer. + * WARNING: This behavior is different from the standard strtol(3). + */ +enum asn_strtol_result_e +asn_strtol_lim(const char *str, const char **end, long *lp) { int sign = 1; long l; const long upper_boundary = LONG_MAX / 10; long last_digit_max = LONG_MAX % 10; - if(str >= end) return ASN_STRTOL_ERROR_INVAL; + if(str >= *end) return ASN_STRTOL_ERROR_INVAL; switch(*str) { case '-': @@ -966,11 +998,13 @@ asn_strtol(const char *str, const char *end, long *lp) { sign = -1; case '+': str++; + if(str >= *end) { + *end = str; + return ASN_STRTOL_EXPECT_MORE; + } } - if(str >= end) return ASN_STRTOL_ERROR_INVAL; - - for(l = 0; str < end; str++) { + for(l = 0; str < (*end); str++) { switch(*str) { case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: { @@ -986,18 +1020,23 @@ asn_strtol(const char *str, const char *end, long *lp) { l = -l * 10 - d; } } else { + *end = str; return ASN_STRTOL_ERROR_RANGE; } } else { + *end = str; return ASN_STRTOL_ERROR_RANGE; } } continue; default: - return ASN_STRTOL_ERROR_INVAL; + *end = str; + *lp = sign * l; + return ASN_STRTOL_EXTRA_DATA; } } + *end = str; *lp = sign * l; return ASN_STRTOL_OK; } diff --git a/skeletons/INTEGER.h b/skeletons/INTEGER.h index 8164e9e7..fe08b038 100644 --- a/skeletons/INTEGER.h +++ b/skeletons/INTEGER.h @@ -59,10 +59,15 @@ int asn_ulong2INTEGER(INTEGER_t *i, unsigned long l); /* A a reified version of strtol(3) with nicer error reporting. */ enum asn_strtol_result_e { - ASN_STRTOL_ERROR_INVAL = -1, /* Invalid input */ - ASN_STRTOL_OK = 0, /* Conversion succeded */ - ASN_STRTOL_ERROR_RANGE = 1, /* Input out of range */ + ASN_STRTOL_ERROR_RANGE = -3, /* Input outside of numeric range for long type */ + ASN_STRTOL_ERROR_INVAL = -2, /* Invalid data encountered (e.g., "+-") */ + ASN_STRTOL_EXPECT_MORE = -1, /* More data expected (e.g. "+") */ + ASN_STRTOL_OK = 0, /* Conversion succeded, number ends at (*end) */ + ASN_STRTOL_EXTRA_DATA = 1, /* Conversion succeded, but the string has extra stuff */ }; +enum asn_strtol_result_e asn_strtol_lim(const char *str, const char **end, long *l); + +/* The asn_strtol is going to be DEPRECATED soon */ enum asn_strtol_result_e asn_strtol(const char *str, const char *end, long *l); /* diff --git a/skeletons/OBJECT_IDENTIFIER.c b/skeletons/OBJECT_IDENTIFIER.c index 2939f25a..066d5625 100644 --- a/skeletons/OBJECT_IDENTIFIER.c +++ b/skeletons/OBJECT_IDENTIFIER.c @@ -648,12 +648,11 @@ OBJECT_IDENTIFIER_parse_arcs(const char *oid_text, ssize_t oid_txt_length, long *arcs, unsigned int arcs_slots, const char **opt_oid_text_end) { unsigned int arcs_count = 0; const char *oid_end; - const char *value_start; enum { ST_LEADSPACE, ST_TAILSPACE, + ST_AFTERVALUE, /* Next character ought to be '.' or a space */ ST_WAITDIGITS, /* Next character is expected to be a digit */ - ST_DIGITS /* INVARIANT: value_start != 0 in this state */ } state = ST_LEADSPACE; if(!oid_text || oid_txt_length < -1 || (arcs_slots && !arcs)) { @@ -665,13 +664,16 @@ OBJECT_IDENTIFIER_parse_arcs(const char *oid_text, ssize_t oid_txt_length, if(oid_txt_length == -1) oid_txt_length = strlen(oid_text); -#define _OID_CAPTURE_ARC(value_start, oid_text) do { \ +#define _OID_CAPTURE_ARC(oid_text, oid_end) do { \ + const char *endp = oid_end; \ long value; \ - switch(asn_strtol(value_start, oid_text, &value)) { \ + switch(asn_strtol_lim(oid_text, &endp, &value)) { \ + case ASN_STRTOL_EXTRA_DATA: \ case ASN_STRTOL_OK: \ if(arcs_count < arcs_slots) \ arcs[arcs_count] = value; \ arcs_count++; \ + oid_text = endp - 1; \ break; \ case ASN_STRTOL_ERROR_RANGE: \ if(opt_oid_text_end) \ @@ -679,6 +681,7 @@ OBJECT_IDENTIFIER_parse_arcs(const char *oid_text, ssize_t oid_txt_length, errno = ERANGE; \ return -1; \ case ASN_STRTOL_ERROR_INVAL: \ + case ASN_STRTOL_EXPECT_MORE: \ if(opt_oid_text_end) \ *opt_oid_text_end = oid_text; \ errno = EINVAL; \ @@ -693,23 +696,24 @@ OBJECT_IDENTIFIER_parse_arcs(const char *oid_text, ssize_t oid_txt_length, case ST_LEADSPACE: case ST_TAILSPACE: continue; - case ST_DIGITS: - _OID_CAPTURE_ARC(value_start, oid_text); + case ST_AFTERVALUE: state = ST_TAILSPACE; continue; case ST_WAITDIGITS: - break; + break; /* Digits expected after ".", got whitespace */ } + break; case 0x2e: /* '.' */ switch(state) { case ST_LEADSPACE: - case ST_WAITDIGITS: - break; case ST_TAILSPACE: - state = ST_WAITDIGITS; + case ST_WAITDIGITS: + if(opt_oid_text_end) + *opt_oid_text_end = oid_text; + errno = EINVAL; /* Broken OID */ + return -1; break; - case ST_DIGITS: - _OID_CAPTURE_ARC(value_start, oid_text); + case ST_AFTERVALUE: state = ST_WAITDIGITS; continue; } @@ -718,14 +722,15 @@ OBJECT_IDENTIFIER_parse_arcs(const char *oid_text, ssize_t oid_txt_length, case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: switch(state) { case ST_TAILSPACE: - state = ST_WAITDIGITS; - break; + case ST_AFTERVALUE: + if(opt_oid_text_end) + *opt_oid_text_end = oid_text; + errno = EINVAL; /* "1. 1" => broken OID */ + return -1; case ST_LEADSPACE: case ST_WAITDIGITS: - state = ST_DIGITS; - value_start = oid_text; - continue; - case ST_DIGITS: + _OID_CAPTURE_ARC(oid_text, oid_end); + state = ST_AFTERVALUE; continue; } break; @@ -744,12 +749,10 @@ OBJECT_IDENTIFIER_parse_arcs(const char *oid_text, ssize_t oid_txt_length, switch(state) { case ST_LEADSPACE: return 0; /* No OID found in input data */ - case ST_DIGITS: - _OID_CAPTURE_ARC(value_start, oid_text); - return arcs_count; case ST_WAITDIGITS: errno = EINVAL; /* Broken OID */ return -1; + case ST_AFTERVALUE: case ST_TAILSPACE: return arcs_count; } diff --git a/skeletons/tests/check-OIDs.c b/skeletons/tests/check-OIDs.c index ce62bdae..05b49932 100644 --- a/skeletons/tests/check-OIDs.c +++ b/skeletons/tests/check-OIDs.c @@ -228,7 +228,8 @@ check_speed() { static void check_parse(const char *oid_txt, int retval) { int ret; long l[2]; - const char *p; + const char *p = oid_txt - 13; + assert(p < oid_txt); ret = OBJECT_IDENTIFIER_parse_arcs(oid_txt, -1, l, 2, &p); printf("[%s] => %d == %d\n", oid_txt, ret, retval); @@ -266,6 +267,7 @@ static void check_xer(int expect_arcs, char *xer) { for(i = 0; i < ret; i++) { if(i) printf("."); printf("%ld", arcs[i]); + if(arcs[i] != i + 1) printf(" != %d\n", i + 1); assert(arcs[i] == i + 1); } printf(": %d == %d\n", ret, expect_arcs); @@ -383,9 +385,8 @@ main() { CHECK_REGEN_OID(19); CHECK_REGEN_OID(20); - check_parse("", -1); - check_parse(" ", -1); - check_parse(" ", -1); + check_parse("", 0); + check_parse(" ", 0); check_parse(".", -1); check_parse(" .", -1); check_parse(".1", -1); @@ -404,6 +405,10 @@ main() { check_parse(" 1.2 ", 2); check_parse("1. 2", -1); check_parse("1 .2", -1); + check_parse(" 1 .2", -1); + check_parse(" 1 .2 ", -1); + check_parse("1 .2 ", -1); + check_parse("1.+1", -1); check_parse("10.30.234.234", 4); check_parse("10.30.234.234 ", 4); check_parse("10.30.234. 234 ", -1);