#ifndef _STRING_HEADER #define _STRING_HEADER 1 #include #include #include "logging.h" #define HTTP_STR_TO_VIEW(http_str) ((string_view){(char*)http_str.buf, (size_t)http_str.len}) typedef struct string_s { char* buf; size_t len; size_t size; } *string; typedef struct string_view_s { char* buf; size_t len; } string_view; const size_t po2s[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384 }; size_t find_next_size(size_t old_size) { if (old_size >= 16384) { return (old_size / 16384 + 1) * 16384; } else { for(size_t i = 0; i < sizeof(po2s)/sizeof(size_t); ++i) { if (old_size < po2s[i]) { return po2s[i]; } } assert(false /*can not reach here */); } } string string_init(size_t size) { size_t actual_size = find_next_size(size); string str = malloc(sizeof (struct string_s)); assert(str != NULL /* str allocation was successful */); str->len = 0; str->size = actual_size; str->buf = malloc(actual_size); assert(str->buf != NULL /* str->buf allocation was successful */); return str; } string string_from(const char * content) { size_t length = strlen(content); size_t size = find_next_size(length); string str = string_init(size); memcpy(str->buf, content, length); str->len = length; return str; } void string_free(string str) { free(str->buf); free(str); } void string_grow(string str, size_t amount) { size_t new_size = find_next_size(str->size + amount); str->buf = realloc(str->buf, new_size); assert(str->buf != NULL /* str->buf reallocation was successful */); str->size = new_size; } void string_append_with_len(string str, const char * content, size_t len) { if (str->len + len + 1 > str->size) { string_grow(str, len + 1); } assert(str->len + len <= str->size /* string is large enough to fit appended content */); memcpy(str->buf + str->len, content, len); str->len += len; } void string_append(string str, const char * content) { size_t len = strlen(content); string_append_with_len(str, content, len); } void string_append_char(string str, char ch) { string_append_with_len(str, &ch, 1); } void string_concat(string str, const string_view *other) { string_append_with_len(str, other->buf, other->len); } void string_copy(string str, const string_view *other) { str->len = 0; string_concat(str, other); } void string_copy_chars(string str, const char *other) { str->len = 0; string_append(str, other); } void string_null_terminate(string str) { if (str->len + 1 > str->size) { string_grow(str, 1); } assert(str->len + 1 <= str->size /* string is large enough to fit null terminator at the end */); str->buf[str->len] = '\0'; } string_view string_get_view(string_view *str, size_t offset, int length) { string_view view; if (length == -1) { length = str->len - offset; } if (offset < str->len && offset + length - 1 < str->len) { view.buf = str->buf + offset; view.len = length; } else { view.buf = str->buf + str->len - 1; view.len = 0; } return view; } size_t string_split(string_view *str, char delimiter, string_view **out) { const char* strPtr = str->buf; size_t remainingLength = str->len; const char* newlinePtr; string_view *lines = malloc(sizeof(string_view)); assert(lines != NULL /* lines allocation was successful */); int nextLineIndex = 0; size_t offset, length; while(NULL != (newlinePtr = memchr(strPtr, delimiter, remainingLength))) { offset = strPtr - str->buf; length = newlinePtr - strPtr; lines[nextLineIndex] = string_get_view(str, offset, length); nextLineIndex += 1; strPtr = newlinePtr + 1; remainingLength -= length + 1; lines = realloc(lines, sizeof(string_view) * (nextLineIndex + 1)); assert(lines != NULL /* lines allocation was successful */); } offset = strPtr - str->buf; lines[nextLineIndex] = string_get_view(str, offset, -1); *out = lines; return nextLineIndex + 1; } bool string_is_whitespace(string_view *str) { for (size_t i = 0; i < str->len; ++i) { char ch = str->buf[i]; if (!isspace(ch)) return false; } return true; } int hex_char_to_num(char ch) { if (ch >= '0' && ch <= '9') { return ch - '0'; } if (ch >= 'a' && ch <= 'f') { return ch - 'a' + 10; } if (ch >= 'A' && ch <= 'F') { return ch - 'A' + 10; } return -1; } int decode_url_hex(const char * input) { char ch1 = input[0]; char ch2 = input[1]; int n1 = hex_char_to_num(ch1); int n2 = hex_char_to_num(ch2); if (n1 == -1 || n2 == -1) { return -1; } else { return n1 * 16 + n2; } } string string_urldecode(string_view *str) { string result = string_init(str->len); int c; for(size_t i = 0; i < str->len; ++i) { c = str->buf[i]; if (c == '+') { c = ' '; } else if (c == '%') { if (i + 2 < str->len) { c = decode_url_hex(&(str->buf[i + 1])); i += 2; if (c == -1) { goto error; } } else { goto error; } } string_append_char(result, (char)c); } return result; error: string_free(result); return NULL; } #endif