Mercurial
view dowa/d_string.c @ 279:b3b547563ec7
Add Google connector service and agent wiki
Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code.
Co-authored-by: Copilot <[email protected]>
Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Mon, 17 Aug 2026 22:22:36 -0700 |
| parents | 04fee26ecce0 |
| children |
line wrap: on
line source
#include "dowa.h" #ifndef MAX_STR_BUFFER #define MAX_STR_BUFFER 1024 #endif char *Dowa_String_Slice(char *from, size_t start, size_t end, Dowa_Arena *p_arena) { char *buffer = p_arena == NULL ? malloc(sizeof(char) * end-start+1) : Dowa_Arena_Allocate(p_arena, end-start+1); if (!buffer) return NULL; for (int32 i = 0; i < (end - start); i++) buffer[i] = from[start + i]; buffer[end - start] = '\0'; return buffer; } char **Dowa_String_Split(char *from, char *token, int32 from_length, int32 token_length, Dowa_Arena *p_arena) { if (!from) return NULL; int32 *token_pos_arr = NULL; for (int32 i = 0; i < from_length; i++) { if (from[i] == token[0]) { int32 curr_token_pointer = 0; while (curr_token_pointer < token_length && (i + curr_token_pointer) < from_length) { if (from[i + curr_token_pointer] != token[curr_token_pointer]) break; curr_token_pointer++; } if (curr_token_pointer == token_length) Dowa_Array_Push(token_pos_arr, i); } } char **splitted_strings = NULL; int32 start = 0; int32 num_tokens = Dowa_Array_Length(token_pos_arr); for (int32 i = 0; i <= num_tokens; i++) { int32 end = (i < num_tokens) ? token_pos_arr[i] : from_length; char *val = Dowa_String_Slice(from, start, end, p_arena); if (p_arena) Dowa_Array_Push_Arena(splitted_strings, val, p_arena); else Dowa_Array_Push(splitted_strings, val); if (i < num_tokens) start = token_pos_arr[i] + token_length; } Dowa_Array_Free(token_pos_arr); return splitted_strings; } int32 Dowa_String_Pos_Find(const char *from, const char *value, const size_t from_length, const size_t value_length) { if (value == NULL || from == NULL) return -1; for (int32 i = 0; i < from_length - value_length; i++) { if (from[i] == value[0]) { int32 j = 0; while (j < value_length && value[j] == from[i+j]) j++; if (j == value_length) return i; } } return -1; } char *Dowa_String_Find(const char *from, const char *value, const size_t from_length, const size_t value_length) { if (value == NULL || from == NULL) return NULL; for (int32 i = 0; i < from_length - value_length; i++) { if (from[i] == value[0]) { int32 j = 0; while (j < value_length && value[j] == from[i+j]) j++; if (j == value_length) return &from[i]; } } return NULL; } int32 Dowa_String_Pos_Find_Char(const char *from, int c, int32 from_length) { if (!from || from_length == 0) return -1; for (int32 i = 0; i < from_length; i++) { if ((int)from[i] == c) return i; } return -1; } char *Dowa_String_Find_Char(const char *from, int c, int32 from_length) { if (!from || from_length == 0) return NULL; for (int32 i = 0; i < from_length; i++) { if ((int)from[i] == c) return &from[i]; } return NULL ; } char *Dowa_String_Copy_Arena(char *from, Dowa_Arena *p_arena) { char *buffer = Dowa_Arena_Allocate(p_arena, sizeof(char*) * strlen(from) + 1); if (buffer) memcpy(buffer, from, strlen(from)); buffer[strlen(from)] = '\0'; return buffer; } char *Dowa_String_UUID(uint32 seed, void *buffer) { char *res = buffer ? buffer : malloc(sizeof(char)*37); if (!res) return NULL; const char *POOL = "abcdef0123456789"; int32 i = 0; while (i < 37) { if (i == 8 || i == 13 || i == 18 || i == 23) { res[i++] = '-'; continue; } seed = Dowa_Math_Random_Uint32(seed); res[i++] = POOL[seed % 16]; } res[36] = '\0'; return res; } // --- JSON Parser --- // static void json_skip_ws(const char *json, int32 *pos, int32 len) { while (*pos < len && (json[*pos] == ' ' || json[*pos] == '\t' || json[*pos] == '\n' || json[*pos] == '\r')) (*pos)++; } static char *json_parse_str(const char *json, int32 *pos, int32 len, Dowa_Arena *arena) { if (*pos >= len || json[*pos] != '"') return NULL; (*pos)++; int32 start = *pos; while (*pos < len) { if (json[*pos] == '\\' && *pos + 1 < len) { *pos += 2; continue; } if (json[*pos] == '"') break; (*pos)++; } int32 slen = *pos - start; char *str = arena ? Dowa_Arena_Allocate(arena, slen + 1) : malloc(slen + 1); if (!str) return NULL; int32 j = 0; for (int32 i = start; i < *pos; i++) { if (json[i] == '\\' && i + 1 < *pos) { i++; switch (json[i]) { case 'n': str[j++] = '\n'; break; case 't': str[j++] = '\t'; break; case 'r': str[j++] = '\r'; break; case '\\': str[j++] = '\\'; break; case '"': str[j++] = '"'; break; default: str[j++] = json[i]; break; } } else str[j++] = json[i]; } str[j] = '\0'; if (*pos < len && json[*pos] == '"') (*pos)++; return str; } // Forward declaration for recursion static Dowa_JSON_Value json_parse_val(const char *json, int32 *pos, int32 len, Dowa_Arena *arena); static Dowa_JSON_Entry *json_parse_obj(const char *json, int32 *pos, int32 len, Dowa_Arena *arena) { if (*pos >= len || json[*pos] != '{') return NULL; (*pos)++; Dowa_JSON_Entry *map = NULL; while (*pos < len) { json_skip_ws(json, pos, len); if (*pos >= len) break; if (json[*pos] == '}') { (*pos)++; break; } if (json[*pos] == ',') { (*pos)++; continue; } char *key = json_parse_str(json, pos, len, arena); if (!key) break; json_skip_ws(json, pos, len); if (*pos >= len || json[*pos] != ':') break; (*pos)++; Dowa_JSON_Value val = json_parse_val(json, pos, len, arena); if (arena) Dowa_HashMap_Push_Arena(map, key, val, arena); else Dowa_HashMap_Push(map, key, val); } return map; } static Dowa_JSON_Value *json_parse_arr(const char *json, int32 *pos, int32 len, Dowa_Arena *arena) { if (*pos >= len || json[*pos] != '[') return NULL; (*pos)++; Dowa_JSON_Value *arr = NULL; while (*pos < len) { json_skip_ws(json, pos, len); if (*pos >= len) break; if (json[*pos] == ']') { (*pos)++; break; } if (json[*pos] == ',') { (*pos)++; continue; } Dowa_JSON_Value val = json_parse_val(json, pos, len, arena); if (arena) Dowa_Array_Push_Arena(arr, val, arena); else Dowa_Array_Push(arr, val); } return arr; } static Dowa_JSON_Value json_parse_val(const char *json, int32 *pos, int32 len, Dowa_Arena *arena) { Dowa_JSON_Value val = {0}; json_skip_ws(json, pos, len); if (*pos >= len) { val.type = DOWA_JSON_NULL; return val; } char c = json[*pos]; // String if (c == '"') { val.type = DOWA_JSON_STRING; val.str_val = json_parse_str(json, pos, len, arena); return val; } // Object if (c == '{') { val.type = DOWA_JSON_OBJECT; val.object_val = json_parse_obj(json, pos, len, arena); return val; } // Array if (c == '[') { val.type = DOWA_JSON_ARRAY; val.array_val = json_parse_arr(json, pos, len, arena); return val; } // Number if (c == '-' || (c >= '0' && c <= '9')) { val.type = DOWA_JSON_NUMBER; int32 start = *pos; while (*pos < len && (json[*pos] == '-' || json[*pos] == '+' || json[*pos] == '.' || json[*pos] == 'e' || json[*pos] == 'E' || (json[*pos] >= '0' && json[*pos] <= '9'))) (*pos)++; char tmp[64]; int32 nlen = *pos - start; if (nlen >= 64) nlen = 63; memcpy(tmp, &json[start], nlen); tmp[nlen] = '\0'; val.num_val = atof(tmp); return val; } // true if (c == 't' && *pos + 4 <= len && memcmp(&json[*pos], "true", 4) == 0) { val.type = DOWA_JSON_BOOL; val.bool_val = TRUE; *pos += 4; return val; } // false if (c == 'f' && *pos + 5 <= len && memcmp(&json[*pos], "false", 5) == 0) { val.type = DOWA_JSON_BOOL; val.bool_val = FALSE; *pos += 5; return val; } // null if (c == 'n' && *pos + 4 <= len && memcmp(&json[*pos], "null", 4) == 0) { val.type = DOWA_JSON_NULL; *pos += 4; return val; } /* Unrecognised token — advance one byte to guarantee progress. */ (*pos)++; val.type = DOWA_JSON_NULL; return val; } Dowa_JSON_Value Dowa_JSON_Parse(const char *json, int32 length, Dowa_Arena *p_arena) { Dowa_JSON_Value val = {0}; if (!json || length <= 0) { val.type = DOWA_JSON_NULL; return val; } int32 pos = 0; return json_parse_val(json, &pos, length, p_arena); } Dowa_JSON_Value *Dowa_JSON_Get(Dowa_JSON_Entry *map, const char *key) { if (!map || !key) return NULL; void *kv = Dowa_HashMap_Get_Ptr(map, key); if (!kv) return NULL; return &((Dowa_JSON_Entry *)kv)->value; } char *Dowa_JSON_Get_String(Dowa_JSON_Entry *map, const char *key) { Dowa_JSON_Value *val = Dowa_JSON_Get(map, key); if (!val || val->type != DOWA_JSON_STRING) return NULL; return val->str_val; } double Dowa_JSON_Get_Number(Dowa_JSON_Entry *map, const char *key) { Dowa_JSON_Value *val = Dowa_JSON_Get(map, key); if (!val || val->type != DOWA_JSON_NUMBER) return 0.0; return val->num_val; } boolean Dowa_JSON_Get_Bool(Dowa_JSON_Entry *map, const char *key) { Dowa_JSON_Value *val = Dowa_JSON_Get(map, key); if (!val || val->type != DOWA_JSON_BOOL) return FALSE; return val->bool_val; } char *Dowa_JSON_Escape_String( const char *value, size_t length, Dowa_Arena *p_arena) { if (!value || !p_arena) return NULL; if (length == 0) length = strlen(value); if (length > (((size_t)-1) - 1) / 6) return NULL; char *escaped = Dowa_Arena_Allocate(p_arena, length * 6 + 1); if (!escaped) return NULL; static const char hex[] = "0123456789abcdef"; size_t output = 0; for (size_t i = 0; i < length; i++) { uint8 c = (uint8)value[i]; switch (c) { case '"': escaped[output++] = '\\'; escaped[output++] = '"'; break; case '\\': escaped[output++] = '\\'; escaped[output++] = '\\'; break; case '\b': escaped[output++] = '\\'; escaped[output++] = 'b'; break; case '\f': escaped[output++] = '\\'; escaped[output++] = 'f'; break; case '\n': escaped[output++] = '\\'; escaped[output++] = 'n'; break; case '\r': escaped[output++] = '\\'; escaped[output++] = 'r'; break; case '\t': escaped[output++] = '\\'; escaped[output++] = 't'; break; default: if (c < 0x20) { escaped[output++] = '\\'; escaped[output++] = 'u'; escaped[output++] = '0'; escaped[output++] = '0'; escaped[output++] = hex[c >> 4]; escaped[output++] = hex[c & 0x0f]; } else { escaped[output++] = (char)c; } break; } } escaped[output] = '\0'; return escaped; }