diff --git a/str.c b/str.c index bc95951f..a6c79dc7 100644 --- a/str.c +++ b/str.c @@ -43,32 +43,67 @@ char *tohex(char *dstHex, const unsigned char *srcBinary, size_t bytes) } /* Convert nbinary*2 ASCII hex characters [0-9A-Fa-f] to nbinary bytes of data. Can be used to - perform the conversion in-place, eg, fromhex(buf, (char*)buf, n); Returns -1 if a non-hex-digit - character is encountered, otherwise returns the number of binary bytes produced (= nbinary). - @author Andrew Bettison + * perform the conversion in-place, eg, fromhex(buf, (char*)buf, n); Returns -1 if a non-hex-digit + * character is encountered, otherwise returns the number of binary bytes produced (= nbinary). + * + * @author Andrew Bettison */ size_t fromhex(unsigned char *dstBinary, const char *srcHex, size_t nbinary) { - size_t count = 0; - while (count != nbinary) { - unsigned char high = hexvalue(*srcHex++); - if (high & 0xf0) return -1; - unsigned char low = hexvalue(*srcHex++); - if (low & 0xf0) return -1; - dstBinary[count++] = (high << 4) + low; - } - return count; + if (strn_fromhex(dstBinary, nbinary, srcHex, NULL) == nbinary) + return nbinary; + return -1; } -/* Convert nbinary*2 ASCII hex characters [0-9A-Fa-f] followed by a nul '\0' character to nbinary bytes of data. Can be used to - perform the conversion in-place, eg, fromhex(buf, (char*)buf, n); Returns -1 if a non-hex-digit - character is encountered or the character immediately following the last hex digit is not a nul, - otherwise returns zero. - @author Andrew Bettison +/* Convert nbinary*2 ASCII hex characters [0-9A-Fa-f] followed by a nul '\0' character to nbinary + * bytes of data. Can be used to perform the conversion in-place, eg, fromhex(buf, (char*)buf, n); + * Returns -1 if a non-hex-digit character is encountered or the character immediately following the + * last hex digit is not a nul, otherwise returns zero. + * + * @author Andrew Bettison */ int fromhexstr(unsigned char *dstBinary, const char *srcHex, size_t nbinary) { - return (fromhex(dstBinary, srcHex, nbinary) == nbinary && srcHex[nbinary * 2] == '\0') ? 0 : -1; + const char *p; + if (strn_fromhex(dstBinary, nbinary, srcHex, &p) == nbinary && *p == '\0') + return 0; + return -1; +} + +/* Decode pairs of ASCII hex characters [0-9A-Fa-f] into binary data with an optional upper limit on + * the number of binary bytes produced (destination buffer size). Returns the number of binary + * bytes decoded. If 'afterHex' is not NULL, then sets *afterHex to point to the source character + * immediately following the last hex digit consumed. + * + * Can be used to perform a conversion in-place, eg: + * + * strn_fromhex((unsigned char *)buf, n, (const char *)buf, NULL); + * + * Can also be used to count hex digits without converting, eg: + * + * strn_fromhex(NULL, -1, buf, NULL); + * + * @author Andrew Bettison + */ +size_t strn_fromhex(unsigned char *dstBinary, ssize_t dstlen, const char *srcHex, const char **afterHex) +{ + unsigned char *dstorig = dstBinary; + unsigned char *dstend = dstBinary + dstlen; + while (dstlen == -1 || dstBinary < dstend) { + int high = hexvalue(srcHex[0]); + if (high == -1) + break; + int low = hexvalue(srcHex[1]); + if (low == -1) + break; + if (dstorig != NULL) + *dstBinary = (high << 4) + low; + ++dstBinary; + srcHex += 2; + } + if (afterHex) + *afterHex = srcHex; + return dstBinary - dstorig; } /* Does this whole buffer contain the same value? */ diff --git a/str.h b/str.h index 3a5d0bc3..ae50a729 100644 --- a/str.h +++ b/str.h @@ -64,19 +64,36 @@ extern const char hexdigit[16]; char *tohex(char *dstHex, const unsigned char *srcBinary, size_t bytes); size_t fromhex(unsigned char *dstBinary, const char *srcHex, size_t nbinary); int fromhexstr(unsigned char *dstBinary, const char *srcHex, size_t nbinary); -int is_all_matching(const unsigned char *ptr, size_t len, unsigned char value); -char *str_toupper_inplace(char *s); +size_t strn_fromhex(unsigned char *dstBinary, ssize_t dstlen, const char *src, const char **afterp); #define alloca_tohex(buf,len) tohex((char *)alloca((len)*2+1), (buf), (len)) __STR_INLINE int hexvalue(char c) { - if (c >= '0' && c <= '9') return c - '0'; - if (c >= 'A' && c <= 'F') return c - 'A' + 10; - if (c >= 'a' && c <= 'f') return c - 'a' + 10; + switch (c) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'a': case 'A': return 10; + case 'b': case 'B': return 11; + case 'c': case 'C': return 12; + case 'd': case 'D': return 13; + case 'e': case 'E': return 14; + case 'f': case 'F': return 15; + } return -1; } +int is_all_matching(const unsigned char *ptr, size_t len, unsigned char value); +char *str_toupper_inplace(char *s); + char *toprint(char *dstStr, ssize_t dstBufSiz, const char *srcBuf, size_t srcBytes, const char quotes[2]); char *toprint_str(char *dstStr, ssize_t dstBufSiz, const char *srcStr, const char quotes[2]); size_t toprint_len(const char *srcBuf, size_t srcBytes, const char quotes[2]);