222 lines
5.5 KiB
C
222 lines
5.5 KiB
C
#ifndef _STRING_HEADER
|
|
#define _STRING_HEADER 1
|
|
|
|
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
|
|
#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
|