From 355c90713522f967bd9ee7d29c97553cb718ec87 Mon Sep 17 00:00:00 2001 From: sfja Date: Wed, 5 Mar 2025 03:33:55 +0100 Subject: [PATCH] runtime: parse req, send res --- slige/runtime/src/collection.h | 56 ++-- slige/runtime/src/http_server.c | 53 +--- slige/runtime/src/http_server_internal.h | 21 -- slige/runtime/src/json.c | 376 +++++++++++++++++++++++ slige/runtime/src/json.h | 44 +++ slige/runtime/src/main.c | 47 ++- slige/runtime/src/str_util.c | 46 +++ slige/runtime/src/str_util.h | 27 ++ 8 files changed, 575 insertions(+), 95 deletions(-) create mode 100644 slige/runtime/src/json.c create mode 100644 slige/runtime/src/json.h create mode 100644 slige/runtime/src/str_util.c create mode 100644 slige/runtime/src/str_util.h diff --git a/slige/runtime/src/collection.h b/slige/runtime/src/collection.h index ce33d66..4bcc58c 100644 --- a/slige/runtime/src/collection.h +++ b/slige/runtime/src/collection.h @@ -3,29 +3,33 @@ #include #include -#define DEFINE_VEC(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY) \ +#define MAYBE_UNUSED __attribute__((unused)) + +#define DEFINE_VEC_TYPE(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY) \ + typedef TYPE VEC_TYPE##T; \ typedef struct { \ - TYPE* data; \ + VEC_TYPE##T* data; \ size_t capacity; \ size_t size; \ - } VEC_TYPE; \ - \ - static inline int FN_PREFIX##_construct(VEC_TYPE* vec) \ + } VEC_TYPE; + +#define DEFINE_VEC_IMPL(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY) \ + MAYBE_UNUSED static inline int FN_PREFIX##_construct(VEC_TYPE* vec) \ { \ const size_t capacity = INITIAL_CAPACITY; \ - TYPE* data = malloc(sizeof(TYPE) * capacity); \ + VEC_TYPE##T* data = malloc(sizeof(VEC_TYPE##T) * capacity); \ if (!data) \ return -1; \ *vec = (VEC_TYPE) { data, capacity, .size = 0 }; \ return 0; \ } \ \ - static inline void FN_PREFIX##_destroy(VEC_TYPE* vec) \ + MAYBE_UNUSED static inline void FN_PREFIX##_destroy(VEC_TYPE* vec) \ { \ free(vec->data); \ } \ \ - static inline VEC_TYPE* FN_PREFIX##_new(void) \ + MAYBE_UNUSED static inline VEC_TYPE* FN_PREFIX##_new(void) \ { \ VEC_TYPE* vec = malloc(sizeof(VEC_TYPE)); \ if (!vec) \ @@ -36,13 +40,14 @@ return vec; \ } \ \ - static inline void FN_PREFIX##_free(VEC_TYPE* vec) \ + MAYBE_UNUSED static inline void FN_PREFIX##_free(VEC_TYPE* vec) \ { \ FN_PREFIX##_destroy(vec); \ free(vec); \ } \ \ - static inline int FN_PREFIX##_push(VEC_TYPE* vec, TYPE value) \ + MAYBE_UNUSED static inline int FN_PREFIX##_push( \ + VEC_TYPE* vec, VEC_TYPE##T value) \ { \ if (vec->size + 1 > vec->capacity) { \ size_t new_capacity = vec->capacity * 2; \ @@ -57,22 +62,30 @@ return 0; \ } \ \ - static inline TYPE* FN_PREFIX##_at(VEC_TYPE* vec, size_t idx) \ + MAYBE_UNUSED static inline VEC_TYPE##T* FN_PREFIX##_at( \ + VEC_TYPE* vec, size_t idx) \ { \ return &vec->data[idx]; \ } \ \ - static inline const TYPE* FN_PREFIX##_at_const( \ + MAYBE_UNUSED static inline const VEC_TYPE##T* FN_PREFIX##_at_const( \ const VEC_TYPE* vec, size_t idx) \ { \ return &vec->data[idx]; \ } \ \ - static inline TYPE FN_PREFIX##_get(const VEC_TYPE* vec, size_t idx) \ + MAYBE_UNUSED static inline VEC_TYPE##T FN_PREFIX##_get( \ + const VEC_TYPE* vec, size_t idx) \ { \ return vec->data[idx]; \ } +#define DEFINE_VEC(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY) \ + DEFINE_VEC_TYPE(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY) \ + DEFINE_VEC_IMPL(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY) + +#define DEFINE_MAYBE_UNUSED + #define DEFINE_STATIC_QUEUE(TYPE, QUEUE_TYPE, FN_PREFIX) \ typedef struct { \ TYPE* data; \ @@ -81,7 +94,7 @@ size_t front; \ } QUEUE_TYPE; \ \ - static inline int FN_PREFIX##_construct( \ + MAYBE_UNUSED static inline int FN_PREFIX##_construct( \ QUEUE_TYPE* queue, size_t capacity) \ { \ *queue = (QUEUE_TYPE) { \ @@ -93,12 +106,12 @@ return 0; \ } \ \ - static inline void FN_PREFIX##_destroy(QUEUE_TYPE* queue) \ + MAYBE_UNUSED static inline void FN_PREFIX##_destroy(QUEUE_TYPE* queue) \ { \ free(queue->data); \ } \ \ - static inline QUEUE_TYPE* FN_PREFIX##_new(size_t capacity) \ + MAYBE_UNUSED static inline QUEUE_TYPE* FN_PREFIX##_new(size_t capacity) \ { \ QUEUE_TYPE* queue = malloc(sizeof(QUEUE_TYPE)); \ if (!queue) \ @@ -109,13 +122,14 @@ return queue; \ } \ \ - static inline void FN_PREFIX##_free(QUEUE_TYPE* queue) \ + MAYBE_UNUSED static inline void FN_PREFIX##_free(QUEUE_TYPE* queue) \ { \ FN_PREFIX##_destroy(queue); \ free(queue); \ } \ \ - static inline int FN_PREFIX##_push(QUEUE_TYPE* queue, TYPE req) \ + MAYBE_UNUSED static inline int FN_PREFIX##_push( \ + QUEUE_TYPE* queue, TYPE req) \ { \ size_t front = queue->front + 1; \ if (front >= queue->capacity) { \ @@ -129,14 +143,16 @@ return 0; \ } \ \ - static inline size_t FN_PREFIX##_size(const QUEUE_TYPE* queue) \ + MAYBE_UNUSED static inline size_t FN_PREFIX##_size( \ + const QUEUE_TYPE* queue) \ { \ return queue->front >= queue->back \ ? queue->front - queue->back \ : (queue->capacity - queue->back) + queue->front; \ } \ \ - static inline int FN_PREFIX##_pop(QUEUE_TYPE* queue, TYPE* req) \ + MAYBE_UNUSED static inline int FN_PREFIX##_pop( \ + QUEUE_TYPE* queue, TYPE* req) \ { \ if (queue->back == queue->front) { \ return -1; \ diff --git a/slige/runtime/src/http_server.c b/slige/runtime/src/http_server.c index f24ba2b..e357d0d 100644 --- a/slige/runtime/src/http_server.c +++ b/slige/runtime/src/http_server.c @@ -1,5 +1,6 @@ #include "http_server.h" #include "http_server_internal.h" +#include "str_util.h" #include #include #include @@ -294,6 +295,8 @@ static inline void worker_handle_request(Worker* worker, Client* client) const char* length_val = req_get_header(&req, "Content-Length"); size_t length = strtoul(length_val, NULL, 10); body = calloc(length + 1, sizeof(char)); + strncpy(body, (char*)&buffer[body_idx], length); + body[length] = '\0'; } HttpCtx handler_ctx = { @@ -327,13 +330,13 @@ l0_return: static inline int parse_header( Req* req, size_t* body_idx, const char* const buf, size_t buf_size) { - Strlitter splitter = string_split(buf, buf_size, "\r\n"); + StrSplitter splitter = str_split(buf, buf_size, "\r\n"); - StrSlice first = split_next(&splitter); - Strlitter first_splitter = string_split(first.ptr, first.len, " "); - StrSlice method_str = split_next(&first_splitter); - StrSlice path_str = split_next(&first_splitter); - StrSlice version_str = split_next(&first_splitter); + StrSlice first = strsplit_next(&splitter); + StrSplitter first_splitter = str_split(first.ptr, first.len, " "); + StrSlice method_str = strsplit_next(&first_splitter); + StrSlice path_str = strsplit_next(&first_splitter); + StrSlice version_str = strsplit_next(&first_splitter); if (strncmp(version_str.ptr, "HTTP/1.1", 8) != 0) { fprintf(stderr, "error: unrecognized http version '%.*s'\n", @@ -363,7 +366,7 @@ static inline int parse_header( header_vec_construct(&headers); while (headers.size < MAX_HEADERS_LEN) { - StrSlice line = split_next(&splitter); + StrSlice line = strsplit_next(&splitter); if (line.len == 0) { *body_idx = (size_t)&line.ptr[2] - (size_t)buf; break; @@ -427,39 +430,3 @@ static inline const char* req_get_header(const Req* req, const char* key) } return NULL; } - -static inline Strlitter string_split( - const char* text, size_t text_len, const char* split) -{ - return (Strlitter) { - .text = text, - .text_len = text_len, - .i = 0, - .split = split, - .split_len = strlen(split), - }; -} - -static inline StrSlice split_next(Strlitter* splitter) -{ - for (size_t i = splitter->i; i < splitter->text_len; ++i) { - if (strncmp(&splitter->text[i], splitter->split, splitter->split_len) - == 0) { - const char* ptr = &splitter->text[splitter->i]; - size_t len = i - splitter->i; - splitter->i = i + splitter->split_len; - return (StrSlice) { ptr, len }; - } - } - return (StrSlice) { - .ptr = &splitter->text[splitter->i], - .len = splitter->text_len - splitter->i, - }; -} - -static inline void string_push_str(String* string, const char* str) -{ - for (size_t i = 0; i < strlen(str); ++i) { - string_push(string, str[i]); - } -} diff --git a/slige/runtime/src/http_server_internal.h b/slige/runtime/src/http_server_internal.h index d489651..156851b 100644 --- a/slige/runtime/src/http_server_internal.h +++ b/slige/runtime/src/http_server_internal.h @@ -103,25 +103,4 @@ struct HttpCtx { void* user_ctx; }; -typedef struct { - const char* ptr; - size_t len; -} StrSlice; - -typedef struct { - const char* text; - size_t text_len; - size_t i; - const char* split; - size_t split_len; -} Strlitter; - -static inline Strlitter string_split( - const char* text, size_t text_len, const char* split); -static inline StrSlice split_next(Strlitter* splitter); - -DEFINE_VEC(char, String, string, 8) - -static inline void string_push_str(String* string, const char* str); - const char* http_response_code_string(int code); diff --git a/slige/runtime/src/json.c b/slige/runtime/src/json.c new file mode 100644 index 0000000..08f1ec7 --- /dev/null +++ b/slige/runtime/src/json.c @@ -0,0 +1,376 @@ +#include "json.h" +#include "collection.h" +#include "str_util.h" +#include +#include +#include +#include + +typedef struct { + char* key; + JsonValue* val; +} KV; + +DEFINE_VEC(JsonValue*, Arr, arr, 4) +DEFINE_VEC(KV, Obj, obj, 4) + +struct JsonValue { + JsonType type; + union { + bool bool_val; + char* str_val; + Arr arr_val; + Obj obj_val; + }; +}; + +bool json_is(const JsonValue* value, JsonType type) +{ + return value != NULL && value->type == type; +} + +bool json_bool(const JsonValue* value) +{ + return value->bool_val; +} + +int64_t json_int(const JsonValue* value) +{ + return strtoll(value->str_val, NULL, 10); +} + +double json_float(const JsonValue* value) +{ + return strtof(value->str_val, NULL); +} + +const char* json_string(const JsonValue* value) +{ + return value->str_val; +} + +size_t json_array_size(const JsonValue* value) +{ + return value->arr_val.size; +} + +const JsonValue* json_array_get(const JsonValue* value, size_t idx) +{ + return value->arr_val.data[idx]; +} + +bool json_object_has(const JsonValue* value, const char* key) +{ + for (size_t i = 0; i < value->obj_val.size; ++i) { + KV* kv = &value->obj_val.data[i]; + if (strcmp(key, kv->key) == 0) { + return true; + } + } + return false; +} + +const JsonValue* json_object_get(const JsonValue* value, const char* key) +{ + for (size_t i = 0; i < value->obj_val.size; ++i) { + KV* kv = &value->obj_val.data[i]; + if (strcmp(key, kv->key) == 0) { + return kv->val; + } + } + return NULL; +} + +void json_value_free(JsonValue* value) +{ + switch (value->type) { + case JsonType_Error: + case JsonType_Null: + case JsonType_Bool: + break; + case JsonType_Number: + case JsonType_String: + free(value->str_val); + break; + case JsonType_Array: + for (size_t i = 0; i < value->arr_val.size; ++i) { + json_value_free(value->arr_val.data[i]); + } + arr_destroy(&value->arr_val); + break; + case JsonType_Object: + for (size_t i = 0; i < value->obj_val.size; ++i) { + free(value->obj_val.data[i].key); + json_value_free(value->obj_val.data[i].val); + } + obj_destroy(&value->obj_val); + break; + } +} + +#define TOK_EOF '\0' +#define TOK_ERROR 'e' +#define TOK_NULL 'n' +#define TOK_FALSE 'f' +#define TOK_TRUE 't' +#define TOK_NUMBER '0' +#define TOK_STRING '"' + +static inline JsonValue* alloc(JsonValue init); +static inline void lex(JsonParser* p); +static inline void lstep(JsonParser* p); + +void json_parser_construct(JsonParser* p, const char* text, size_t text_len) +{ + *p = (JsonParser) { + text, + text_len, + .i = 0, + .ch = text[0], + .curr_tok = TOK_EOF, + .curr_val = NULL, + }; + lex(p); +} + +void json_parser_destroy(JsonParser* p) +{ + (void)p; +} + +JsonValue* json_parser_parse(JsonParser* p) +{ + switch (p->curr_tok) { + case TOK_EOF: + fprintf(stderr, "error: json: unexpected eof\n"); + return NULL; + case TOK_ERROR: + lex(p); + return NULL; + case TOK_NULL: + lex(p); + return alloc((JsonValue) { .type = JsonType_Null }); + case TOK_FALSE: + lex(p); + return alloc( + (JsonValue) { .type = JsonType_Bool, .bool_val = false }); + case TOK_TRUE: + lex(p); + return alloc( + (JsonValue) { .type = JsonType_Null, .bool_val = true }); + case TOK_NUMBER: { + char* val = p->curr_val; + lex(p); + return alloc( + (JsonValue) { .type = JsonType_Number, .str_val = val }); + } + case TOK_STRING: { + char* val = p->curr_val; + lex(p); + return alloc( + (JsonValue) { .type = JsonType_String, .str_val = val }); + } + } + + if (p->curr_tok == '[') { + lex(p); + Arr arr; + arr_construct(&arr); + { + JsonValue* value = json_parser_parse(p); + if (!value) + return NULL; + arr_push(&arr, value); + } + while (p->curr_tok != TOK_EOF && p->curr_tok != ']') { + if (p->curr_tok != ',') { + fprintf(stderr, "error: json: expected ',' in array\n"); + return NULL; + } + lex(p); + JsonValue* value = json_parser_parse(p); + if (!value) + return NULL; + arr_push(&arr, value); + } + if (p->curr_tok != ']') { + fprintf(stderr, "error: json: expected ']' after array\n"); + return NULL; + } + lex(p); + return alloc((JsonValue) { .type = JsonType_Array, .arr_val = arr }); + } + + if (p->curr_tok == '{') { + lex(p); + Obj obj; + obj_construct(&obj); + { + if (p->curr_tok != '"') { + fprintf(stderr, "error: json: expected '\"' in kv\n"); + return NULL; + } + char* key = p->curr_val; + lex(p); + if (p->curr_tok != ':') { + fprintf(stderr, "error: json: expected ':' in kv\n"); + return NULL; + } + lex(p); + JsonValue* value = json_parser_parse(p); + if (!value) + return NULL; + obj_push(&obj, (KV) { key, value }); + } + while (p->curr_tok != TOK_EOF && p->curr_tok != '}') { + if (p->curr_tok != ',') { + fprintf(stderr, "error: json: expected ',' in object\n"); + return NULL; + } + lex(p); + + if (p->curr_tok != '"') { + fprintf(stderr, "error: json: expected '\"' in kv\n"); + return NULL; + } + char* key = p->curr_val; + lex(p); + if (p->curr_tok != ':') { + fprintf(stderr, "error: json: expected ':' in kv\n"); + return NULL; + } + lex(p); + JsonValue* value = json_parser_parse(p); + if (!value) + return NULL; + obj_push(&obj, (KV) { key, value }); + } + if (p->curr_tok != '}') { + fprintf(stderr, "error: json: expected '}' after object\n"); + return NULL; + } + lex(p); + return alloc((JsonValue) { .type = JsonType_Object, .obj_val = obj }); + } + + fprintf(stderr, "error: json: unexpeted tok\n"); + return NULL; +} + +static inline JsonValue* alloc(JsonValue init) +{ + JsonValue* value = malloc(sizeof(JsonValue)); + *value = init; + return value; +} + +static inline void lex(JsonParser* p) +{ + if (p->i >= p->text_len) { + p->curr_tok = TOK_EOF; + return; + } + switch (p->ch) { + case ' ': + case '\t': + case '\r': + case '\n': + lstep(p); + lex(p); + return; + case '[': + case ']': + case '{': + case '}': + case ',': + case ':': + p->curr_tok = p->ch; + lstep(p); + return; + case '0': + lstep(p); + p->curr_tok = TOK_NUMBER; + return; + } + if ((p->ch >= '1' && p->ch <= '9') || p->ch == '.') { + String value; + string_construct(&value); + int dec_seps = 0; + while (p->i < p->text_len + && ((p->ch >= '0' && p->ch <= '9') + || (p->ch == '.' && dec_seps <= 1))) { + if (p->ch == '.') { + dec_seps += 1; + } + string_push(&value, p->ch); + lstep(p); + } + // should be interned + char* copy = string_copy(&value); + + string_destroy(&value); + p->curr_tok = TOK_NUMBER; + p->curr_val = copy; + return; + } + if (p->ch == '"') { + lstep(p); + + String value; + string_construct(&value); + + while (p->i < p->text_len && p->text[p->i] != '"') { + if (p->text[p->i] == '\\') { + lstep(p); + if (p->i >= p->text_len) + break; + switch (p->ch) { + case '0': + string_push(&value, '\0'); + break; + case 'n': + string_push(&value, '\n'); + break; + case 'r': + string_push(&value, '\r'); + break; + case 't': + string_push(&value, '\t'); + break; + default: + string_push(&value, p->ch); + break; + } + } else { + string_push(&value, p->ch); + } + lstep(p); + } + if (p->i >= p->text_len || p->text[p->i] != '"') { + string_destroy(&value); + fprintf(stderr, "error: json: bad string\n"); + p->curr_tok = TOK_ERROR; + return; + } + lstep(p); + + // should be interned + char* copy = string_copy(&value); + + string_destroy(&value); + p->curr_tok = '"'; + p->curr_val = copy; + return; + } + + fprintf(stderr, "error: json: illegal char '%c'\n", p->ch); + lstep(p); + p->curr_tok = TOK_ERROR; + return; +} + +static inline void lstep(JsonParser* p) +{ + p->i += 1; + p->ch = p->text[p->i]; +} diff --git a/slige/runtime/src/json.h b/slige/runtime/src/json.h new file mode 100644 index 0000000..e6589dc --- /dev/null +++ b/slige/runtime/src/json.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include + +typedef enum { + JsonType_Error, + JsonType_Null, + JsonType_Bool, + JsonType_Number, + JsonType_String, + JsonType_Array, + JsonType_Object, +} JsonType; + +typedef struct JsonValue JsonValue; + +bool json_is(const JsonValue* value, JsonType type); +bool json_bool(const JsonValue* value); +int64_t json_int(const JsonValue* value); +double json_float(const JsonValue* value); +const char* json_string(const JsonValue* value); +size_t json_array_size(const JsonValue* value); +const JsonValue* json_array_get(const JsonValue* value, size_t idx); +bool json_object_has(const JsonValue* value, const char* key); +const JsonValue* json_object_get(const JsonValue* value, const char* key); + +void json_value_free(JsonValue* value); + +typedef struct { + const char* text; + size_t text_len; + size_t i; + char ch; + + char curr_tok; + char* curr_val; +} JsonParser; + +void json_parser_construct( + JsonParser* parser, const char* text, size_t text_len); +void json_parser_destroy(JsonParser* parser); +JsonValue* json_parser_parse(JsonParser* parser); diff --git a/slige/runtime/src/main.c b/slige/runtime/src/main.c index c6aedb5..30a26f7 100644 --- a/slige/runtime/src/main.c +++ b/slige/runtime/src/main.c @@ -1,4 +1,5 @@ #include "http_server.h" +#include "json.h" #include #include @@ -6,28 +7,52 @@ typedef struct { int number; } Cx; +#define RESPOND(HTTP_CTX, STATUS, MIME_TYPE, ...) \ + { \ + HttpCtx* _ctx = (HTTP_CTX); \ + char _body[512]; \ + snprintf(_body, 512 - 1, __VA_ARGS__); \ + \ + char content_length[24] = { 0 }; \ + snprintf(content_length, 24 - 1, "%ld", strlen(_body)); \ + \ + http_ctx_res_headers_set(_ctx, "Content-Type", MIME_TYPE); \ + http_ctx_res_headers_set(_ctx, "Content-Length", content_length); \ + \ + http_ctx_respond(_ctx, (STATUS), _body); \ + } + +#define RESPOND_HTML(HTTP_CTX, STATUS, ...) \ + RESPOND(HTTP_CTX, STATUS, "text/html", __VA_ARGS__) +#define RESPOND_JSON(HTTP_CTX, STATUS, ...) \ + RESPOND(HTTP_CTX, STATUS, "application/json", __VA_ARGS__) + void route_get_index(HttpCtx* ctx) { Cx* cx = http_ctx_user_ctx(ctx); - char body[512]; - snprintf(body, 512 - 1, + RESPOND_HTML(ctx, 200, "

Number = %d

", cx->number); - - char content_length[24] = { 0 }; - snprintf(content_length, 24 - 1, "%ld", strlen(body)); - - http_ctx_res_headers_set(ctx, "Content-Type", "text/html"); - http_ctx_res_headers_set(ctx, "Content-Length", content_length); - - http_ctx_respond(ctx, 200, body); } void route_post_set_number(HttpCtx* ctx) { - printf("set number\n"); + Cx* cx = http_ctx_user_ctx(ctx); + + const char* body_text = http_ctx_req_body(ctx); + JsonParser parser; + json_parser_construct(&parser, body_text, strlen(body_text)); + JsonValue* body = json_parser_parse(&parser); + json_parser_destroy(&parser); + + int64_t value = json_int(json_object_get(body, "value")); + cx->number = (int)value; + + json_value_free(body); + + RESPOND_JSON(ctx, 200, "{\"ok\": true}\r\n"); } HttpServer* server; diff --git a/slige/runtime/src/str_util.c b/slige/runtime/src/str_util.c new file mode 100644 index 0000000..b8c82bc --- /dev/null +++ b/slige/runtime/src/str_util.c @@ -0,0 +1,46 @@ +#include "str_util.h" +#include +#include + +StrSplitter str_split(const char* text, size_t text_len, const char* split) +{ + return (StrSplitter) { + .text = text, + .text_len = text_len, + .i = 0, + .split = split, + .split_len = strlen(split), + }; +} + +StrSlice strsplit_next(StrSplitter* splitter) +{ + for (size_t i = splitter->i; i < splitter->text_len; ++i) { + if (strncmp(&splitter->text[i], splitter->split, splitter->split_len) + == 0) { + const char* ptr = &splitter->text[splitter->i]; + size_t len = i - splitter->i; + splitter->i = i + splitter->split_len; + return (StrSlice) { ptr, len }; + } + } + return (StrSlice) { + .ptr = &splitter->text[splitter->i], + .len = splitter->text_len - splitter->i, + }; +} + +void string_push_str(String* string, const char* str) +{ + for (size_t i = 0; i < strlen(str); ++i) { + string_push(string, str[i]); + } +} + +char* string_copy(const String* string) +{ + char* copy = malloc(string->size + 1); + strncpy(copy, string->data, string->size); + copy[string->size] = '\0'; + return copy; +} diff --git a/slige/runtime/src/str_util.h b/slige/runtime/src/str_util.h new file mode 100644 index 0000000..5b523f1 --- /dev/null +++ b/slige/runtime/src/str_util.h @@ -0,0 +1,27 @@ +#pragma once + +#include "collection.h" +#include + +typedef struct { + const char* ptr; + size_t len; +} StrSlice; + +typedef struct { + const char* text; + size_t text_len; + size_t i; + const char* split; + size_t split_len; +} StrSplitter; + +StrSplitter str_split(const char* text, size_t text_len, const char* split); +StrSlice strsplit_next(StrSplitter* splitter); + +DEFINE_VEC(char, String, string, 8) + +void string_push_str(String* string, const char* str); +char* string_copy(const String* string); + +DEFINE_VEC(char*, RawStrVec, rawstr_vec, 8)