bird-lg/string.h

222 lines
5.5 KiB
C
Raw Normal View History

2021-12-10 15:11:40 +01:00
#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