Created
June 11, 2019 11:29
-
-
Save lafka/e16e47ae63d6ef8ee610666661ef267d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Happy case-insensitive hex decoder which supports optional | |
* byte separators. | |
* | |
* `out` is a pointer to zero filled memory | |
* | |
* `size` is the size of `out` | |
* | |
* `in` MUST be null-terminated string. | |
* | |
* `ptr` an optional double pointer to `buf` which will be set to the start | |
* address of the decoded data. | |
* | |
* Returns -1 if non-hex (and non-separator characters) where found | |
* in input. Otherwise returns length. Valid separators are ':', '-' and ' '. | |
* | |
* Processing is done from left to right and `*ptr` is set to the start | |
* of the decoded data. Optionally the returned size can be used to | |
* deduce the starting point. | |
* | |
* Example (with *ptr): | |
* | |
* #define SIZE 64 | |
* // returns {0xaa, 0xbb, 0xcc, 0xdd, 0xee} | |
* const char hexstr[] = "aabbccddee"; | |
* uint8_t *ptr; | |
* buf = malloc(SIZE); | |
* memset(buf, 0, SIZE); | |
* length = hex_decode(buf, SIZE, hexstr, &ptr); | |
* | |
* for (int i = 0; i < length; i++) { | |
* printf("%02x ", ptr[i]); | |
* } | |
* | |
* printf("\n"); | |
* | |
* free(buf); | |
* | |
* | |
* Example (with size): | |
* #define SIZE 64 | |
* // returns {0xaa, 0x0b, 0xcc, 0xdd, 0xee} | |
* const char hexstr[] = "aa:b:cc dd ee"; | |
* uint8_t *buf = malloc(SIZE); | |
* memset(buf, 0, SIZE); | |
* int length = hex_decode(buf, SIZE, hexstr, NULL); | |
* | |
* for (int i = 0; i < length; i++) { | |
* printf("%02x ", buf[SIZE - length + i]); | |
* } | |
* | |
* printf("\n"); | |
* | |
* free(buf); | |
* | |
*/ | |
int hex_decode(uint8_t *out, size_t size, const char *in, uint8_t **ptr) { | |
int pos = size - 1; | |
int insize = strlen(in); | |
int first = 0 == insize % 2; | |
/** | |
* Loop in reverse to easily fix padding. | |
* | |
* The process is like this: | |
* - First lowercase input by OR'ing with ' ' (a single space) - bit 5 | |
* - check if it's a character or number by AND'ing with '@' - bit 6 | |
* - If `first` is initially 1 we're processing the right most nibble | |
* - If we encounter a separator character we flip `first` thus making | |
* the next nibble the first (remember we're using modulo to "guess" | |
* which nibble we're working on). | |
*/ | |
for (int i = strlen(in) - 1; i >= 0; i--) { | |
if (' ' == in[i] || '-' == in[i] || ':' == in[i]) { | |
/** | |
* if we've only processed the right most nibble we don't need to | |
* flip `first` since the next nibble will have same evenness | |
*/ | |
if (first != i % 2) { | |
pos--; | |
continue; | |
} | |
first = !first; | |
continue; | |
} | |
//if ( ! (('0' > in[pos] && in[pos] < '0') || ('a >= in[pos] && in[pos] <= 'f'))) { | |
// return -1; | |
//} | |
out[pos] |= ((in[i] | ' ') - ('@' & in[i] ? 0x57 : 0x30)) << (first == i % 2 ? 0 : 4); | |
if (first != i % 2) { | |
pos--; | |
} | |
} | |
// If the last processed character was first nibble processed `pos` has not | |
// been decremented yet | |
if (NULL != ptr) { | |
*ptr = &out[pos + (first ? 1 : 0)]; | |
} | |
return size - pos - (first ? 1 : 0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment