Files
util.nix/package/c/hectic/hectic.c
2025-04-06 19:18:14 +00:00

716 lines
25 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "hectic.h"
void set_output_color_mode(ColorMode mode) {
color_mode = mode;
}
// ------------
// -- Logger --
// ------------
const char* log_level_to_string(LogLevel level) {
switch (level) {
case LOG_LEVEL_TRACE: return "TRACE";
case LOG_LEVEL_ZALUPA: return "ZALUPA";
case LOG_LEVEL_DEBUG: return "DEBUG";
case LOG_LEVEL_LOG: return "LOG";
case LOG_LEVEL_INFO: return "INFO";
case LOG_LEVEL_NOTICE: return "NOTICE";
case LOG_LEVEL_WARN: return "WARN";
case LOG_LEVEL_EXCEPTION: return "EXCEPTION";
default: return "UNKNOWN";
}
}
const char* log_level_to_color(LogLevel level) {
switch (level) {
case LOG_LEVEL_TRACE: return OPTIONAL_COLOR(COLOR_GREEN);
case LOG_LEVEL_ZALUPA: return OPTIONAL_COLOR(COLOR_MAGENTA);
case LOG_LEVEL_DEBUG: return OPTIONAL_COLOR(COLOR_BLUE);
case LOG_LEVEL_LOG: return OPTIONAL_COLOR(COLOR_CYAN);
case LOG_LEVEL_INFO: return OPTIONAL_COLOR(COLOR_GREEN);
case LOG_LEVEL_NOTICE: return OPTIONAL_COLOR(COLOR_CYAN);
case LOG_LEVEL_WARN: return OPTIONAL_COLOR(COLOR_YELLOW);
case LOG_LEVEL_EXCEPTION: return OPTIONAL_COLOR(COLOR_RED);
default: return OPTIONAL_COLOR(COLOR_RESET);
}
}
LogLevel log_level_from_string(const char *level_str) {
if (!level_str) return LOG_LEVEL_INFO;
if (strcmp(level_str, "TRACE") == 0)
return LOG_LEVEL_TRACE;
else if (strcmp(level_str, "ZALUPA") == 0)
return LOG_LEVEL_ZALUPA;
else if (strcmp(level_str, "DEBUG") == 0)
return LOG_LEVEL_DEBUG;
else if (strcmp(level_str, "LOG") == 0)
return LOG_LEVEL_LOG;
else if (strcmp(level_str, "INFO") == 0)
return LOG_LEVEL_INFO;
else if (strcmp(level_str, "NOTICE") == 0)
return LOG_LEVEL_NOTICE;
else if (strcmp(level_str, "WARN") == 0)
return LOG_LEVEL_WARN;
else if (strcmp(level_str, "EXCEPTION") == 0)
return LOG_LEVEL_EXCEPTION;
else
return LOG_LEVEL_INFO;
}
LogLevel current_log_level = LOG_LEVEL_INFO;
void logger_level_reset() {
current_log_level = LOG_LEVEL_INFO;
}
void logger_level(LogLevel level) {
current_log_level = level;
}
void init_logger(void) {
current_log_level = log_level_from_string(getenv("LOG_LEVEL"));
}
char* raise_message(
LogLevel level,
const char *file,
const char *func,
int line,
const char *format,
...) {
(void)func;
if (level < current_log_level) {
return NULL;
}
time_t now = time(NULL);
struct tm tm_info;
localtime_r(&now, &tm_info);
static char timeStr[20];
strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", &tm_info);
fprintf(stderr, "%s %s%s%s %s:%d ", timeStr, log_level_to_color(level), log_level_to_string(level), OPTIONAL_COLOR(COLOR_RESET), file, line);
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
fprintf(stderr, "\n");
return timeStr;
}
// -----------
// -- arena --
// -----------
Arena arena_init__(const char *file, const char *func, int line, size_t size) {
Arena arena;
arena.begin = malloc(size);
memset(arena.begin, 0, size);
arena.current = arena.begin;
arena.capacity = size;
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"Initialized arena at %p with capacity %zu", arena.begin, size);
return arena;
}
void* arena_alloc_or_null__(const char *file, const char *func, int line, Arena *arena, size_t size) {
raise_message(LOG_LEVEL_TRACE, file, func, line, "arena_alloc_or_null(%p, %zu)", arena, size);
void *mem = NULL;
if (arena->begin == 0) {
*arena = arena_init__(file, func, line, 1024); // ARENA_DEFAULT_SIZE assumed as 1024
}
size_t current = (size_t)arena->current - (size_t)arena->begin;
if (arena->capacity <= current || arena->capacity - current < size) {
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"Arena %p (capacity %zu) used %zu cannot allocate %zu bytes",
arena->begin, arena->capacity, current, size);
return NULL;
} else {
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"Arena %p (capacity %zu) used %zu will allocate %zu bytes",
arena->begin, arena->capacity, current, size);
mem = arena->current;
arena->current = (char*)arena->current + size;
}
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Allocated at %p", mem);
return mem;
}
void* arena_alloc__(const char *file, const char *func, int line, Arena *arena, size_t size) {
void *mem = arena_alloc_or_null__(file, func, line, arena, size);
if (!mem) {
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"Arena out of memory when trying to allocate %zu bytes", size);
raise_message(LOG_LEVEL_EXCEPTION, file, func, line,
"Arena out of memory");
exit(1);
}
return mem;
}
void arena_reset__(const char *file, const char *func, int line, Arena *arena) {
arena->current = arena->begin;
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"Arena %p reset", arena->begin);
}
void arena_free__(const char *file, const char *func, int line, Arena *arena) {
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"Freeing arena at %p", arena->begin);
free(arena->begin);
}
char* arena_strdup__(const char *file, const char *func, int line, Arena *arena, const char *s) {
char *result;
if (s) {
size_t len = strlen(s) + 1;
result = (char*)arena_alloc__(file, func, line, arena, len);
memcpy(result, s, len);
} else {
result = NULL;
}
return result;
}
char* arena_repstr__(const char *file, const char *func, int line, Arena *arena,
const char *src, size_t start, size_t len, const char *rep) {
raise_message(LOG_LEVEL_TRACE, file, func, line, "arena_repstr__(%p, %p, %zu, \"%s\")", src, start, len, rep);
int src_len = strlen(src);
int rep_len = strlen(rep);
int new_len = src_len - (int)len + rep_len;
char *new_str = (char*)arena_alloc__(file, func, line, arena, new_len + 1);
memcpy(new_str, src, start);
memcpy(new_str + start, rep, rep_len);
strcpy(new_str + start + rep_len, src + start + len);
return new_str;
}
void* arena_realloc_copy__(const char *file, const char *func, int line, Arena *arena,
void *old_ptr, size_t old_size, size_t new_size) {
void *new_ptr = NULL;
if (old_ptr == NULL) {
new_ptr = arena_alloc__(file, func, line, arena, new_size);
} else if (new_size <= old_size) {
new_ptr = old_ptr;
} else {
new_ptr = arena_alloc_or_null__(file, func, line, arena, new_size);
if (new_ptr)
memcpy(new_ptr, old_ptr, old_size);
}
return new_ptr;
}
// ----------
// -- misc --
// ----------
void substr_clone__(const char *file, const char *func, int line, const char * const src, char *dest, size_t from, size_t len) {
// Log function entry with all parameters.
raise_message(LOG_LEVEL_TRACE, file, func, line,
"substr_cloning(src=\"%s\", src_ptr=%p, dest=%p, from=%zu, len=%zu)",
src, src, dest, from, len);
size_t srclen = strlen(src);
if (from >= srclen) {
// Log warning with context when 'from' is out of range.
raise_message(LOG_LEVEL_WARN, file, func, line,
"Invalid 'from' index (%zu): exceeds source length (%zu)",
from, srclen);
dest[0] = '\0';
return;
}
if (from + len > srclen)
len = srclen - from;
strncpy(dest, src + from, len);
dest[len] = '\0';
// Log success message with result.
raise_message(LOG_LEVEL_TRACE, file, func, line,
"Completed substr_cloning: result=\"%s\", copied_length=%zu",
dest, len);
}
// ----------
// -- Json --
// ----------
/* Utility: Skip whitespace */
static const char *skip_whitespace(const char *s) {
while (*s && isspace((unsigned char)*s))
s++;
return s;
}
/* Parse a JSON string (does not handle full escaping) */
static char *json_parse_string__(const char *file, const char *func, int line, const char **s_ptr, Arena *arena) {
const char *s = *s_ptr;
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Entering json_parse_string__ at position: %p", s);
if (*s != '"') {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Expected '\"' at start of string, got: %c", *s);
return NULL;
}
s++; // skip opening quote
const char *start = s;
while (*s && *s != '"') {
if (*s == '\\') {
s++; // skip escape char indicator
}
s++;
}
if (*s != '"') {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Unterminated string starting at: %p", start);
return NULL;
}
size_t len = s - start;
char *str = arena_alloc__(file, func, line, arena, len + 1);
if (!str) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Memory allocation failed in json_parse_string__");
return NULL;
}
memcpy(str, start, len);
str[len] = '\0';
*s_ptr = s + 1; // skip closing quote
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Parsed string: \"%s\" (length: %zu)", str, len);
return str;
}
/* Parse a number using strtod */
static double json_parse_number__(const char *file, const char *func, int line, const char **s_ptr) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Parsing number at position: %p", *s_ptr);
char *end;
double num = strtod(*s_ptr, &end);
if (*s_ptr == end)
raise_message(LOG_LEVEL_DEBUG, file, func, line, "No valid number found at: %p", *s_ptr);
*s_ptr = end;
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Parsed number: %g", num);
return num;
}
/* Forward declaration */
static Json *json_parse_value__(const char *file, const char *func, int line, const char **s, Arena *arena);
/* Parse a JSON array: [ value, value, ... ] */
static Json *json_parse_array__(const char *file, const char *func, int line, const char **s, Arena *arena) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Entering json_parse_array__ at position: %p", *s);
if (**s != '[') return NULL;
(*s)++; // skip '['
*s = skip_whitespace(*s);
Json *array = arena_alloc__(file, func, line, arena, sizeof(Json));
if (!array) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Memory allocation failed in json_parse_array__");
return NULL;
}
memset(array, 0, sizeof(Json));
array->type = JSON_ARRAY;
Json *last = NULL;
if (**s == ']') { // empty array
(*s)++;
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Parsed empty array");
return array;
}
while (**s) {
Json *element = json_parse_value__(file, func, line, s, arena);
if (!element) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Failed to parse array element");
return NULL;
}
if (!array->child)
array->child = element;
else
last->next = element;
last = element;
*s = skip_whitespace(*s);
if (**s == ',') {
(*s)++;
*s = skip_whitespace(*s);
} else if (**s == ']') {
(*s)++;
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Completed parsing array");
break;
} else {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Unexpected character '%c' in array", **s);
return NULL;
}
}
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Completed parsing array");
return array;
}
/* Parse a JSON object: { "key": value, ... } */
static Json *json_parse_object__(const char *file, const char *func, int line, const char **s, Arena *arena) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Entering json_parse_object__ at position: %p", *s);
if (**s != '{') return NULL;
(*s)++; // skip '{'
*s = skip_whitespace(*s);
Json *object = arena_alloc__(file, func, line, arena, sizeof(Json));
if (!object) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Memory allocation failed in json_parse_object__");
return NULL;
}
memset(object, 0, sizeof(Json));
object->type = JSON_OBJECT;
Json *last = NULL;
if (**s == '}') {
(*s)++;
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Parsed empty object");
return object;
}
while (**s) {
char *key = json_parse_string__(file, func, line, s, arena);
if (!key) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Failed to parse key in object");
return NULL;
}
*s = skip_whitespace(*s);
if (**s != ':') {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Expected ':' after key \"%s\", got: %c", key, **s);
return NULL;
}
(*s)++; // skip ':'
*s = skip_whitespace(*s);
Json *value = json_parse_value__(file, func, line, s, arena);
if (!value) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Failed to parse value for key \"%s\"", key);
return NULL;
}
value->key = key; // assign key to the value
if (!object->child)
object->child = value;
else
last->next = value;
last = value;
*s = skip_whitespace(*s);
if (**s == ',') {
(*s)++;
*s = skip_whitespace(*s);
} else if (**s == '}') {
(*s)++;
break;
} else {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Unexpected character '%c' in object", **s);
return NULL;
}
}
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Completed parsing object");
return object;
}
/* Full JSON value parser */
static Json *json_parse_value__(const char *file, const char *func, int line, const char **s, Arena *arena) {
*s = skip_whitespace(*s);
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Parsing JSON value at position: %p", *s);
if (**s == '"') {
Json *item = arena_alloc__(file, func, line, arena, sizeof(Json));
if (!item) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Memory allocation failed in json_parse_value for string");
return NULL;
}
memset(item, 0, sizeof(Json));
item->type = JSON_STRING;
item->JsonValue.string = json_parse_string__(file, func, line, s, arena);
return item;
} else if (strncmp(*s, "null", 4) == 0) {
Json *item = arena_alloc__(file, func, line, arena, sizeof(Json));
if (!item) return NULL;
memset(item, 0, sizeof(Json));
item->type = JSON_NULL;
*s += 4;
return item;
} else if (strncmp(*s, "true", 4) == 0) {
Json *item = arena_alloc__(file, func, line, arena, sizeof(Json));
if (!item) return NULL;
memset(item, 0, sizeof(Json));
item->type = JSON_BOOL;
item->JsonValue.boolean = 1;
*s += 4;
return item;
} else if (strncmp(*s, "false", 5) == 0) {
Json *item = arena_alloc__(file, func, line, arena, sizeof(Json));
if (!item) return NULL;
memset(item, 0, sizeof(Json));
item->type = JSON_BOOL;
item->JsonValue.boolean = 0;
*s += 5;
return item;
} else if ((**s == '-') || isdigit((unsigned char)**s)) {
Json *item = arena_alloc__(file, func, line, arena, sizeof(Json));
if (!item) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Memory allocation failed in json_parse_value for number");
return NULL;
}
memset(item, 0, sizeof(Json));
item->type = JSON_NUMBER;
item->JsonValue.number = json_parse_number__(file, func, line, s);
return item;
} else if (**s == '[') {
return json_parse_array__(file, func, line, s, arena);
} else if (**s == '{') {
return json_parse_object__(file, func, line, s, arena);
}
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Unrecognized JSON value at position: %p", *s);
return NULL;
}
Json *json_parse__(const char* file, const char* func, int line, Arena *arena, const char **s) {
Json *result = json_parse_value__(file, func, line, s, arena);
if (!result)
raise_message(LOG_LEVEL_DEBUG, file, func, line, "json_parse failed at position: %p", *s);
return result;
}
char *json_to_string__(const char* file, const char* func, int line, Arena *arena, const Json * const item) {
return json_to_string_with_opts__(file, func, line, arena, item, JSON_NORAW);
}
/* Minimal JSON printer with raw output option.
When raw is non-zero and the item is a JSON_STRING, it is printed without quotes.
*/
char *json_to_string_with_opts__(const char* file, const char* func, int line, Arena *arena, const Json * const item, JsonRawOpt raw) {
char *out = arena_alloc__(file, func, line, arena, 1024);
if (!out) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Memory allocation failed in json_to_string_with_opts");
return NULL;
}
char *ptr = out;
if (item->type == JSON_OBJECT) {
ptr += sprintf(ptr, "{");
Json *child = item->child;
while (child) {
ptr += sprintf(ptr, "\"%s\":", child->key ? child->key : "");
char *child_str = json_to_string_with_opts__(file, func, line, arena, child, raw);
ptr += sprintf(ptr, "%s", child_str);
if (child->next)
ptr += sprintf(ptr, ",");
child = child->next;
}
sprintf(ptr, "}");
} else if (item->type == JSON_ARRAY) {
ptr += sprintf(ptr, "[");
Json *child = item->child;
while (child) {
char *child_str = json_to_string_with_opts__(file, func, line, arena, child, raw);
ptr += sprintf(ptr, "%s", child_str);
if (child->next)
ptr += sprintf(ptr, ",");
child = child->next;
}
sprintf(ptr, "]");
} else if (item->type == JSON_STRING) {
if ((int)raw)
sprintf(ptr, "%s", item->JsonValue.string);
else
sprintf(ptr, "\"%s\"", item->JsonValue.string);
} else if (item->type == JSON_NUMBER) {
sprintf(ptr, "%g", item->JsonValue.number);
} else if (item->type == JSON_BOOL) {
sprintf(ptr, item->JsonValue.boolean ? "true" : "false");
} else if (item->type == JSON_NULL) {
sprintf(ptr, "null");
}
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Converted JSON to string: %s", out);
return out;
}
/* Retrieve an object item by key (case-sensitive) */
Json *json_get_object_item__(const char* file, const char* func, int line, const Json * const object, const char * const key) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "json_get_object_item: Searching for key \"%s\"", key);
if (!object || object->type != JSON_OBJECT) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Invalid object passed to json_get_object_item");
return NULL;
}
Json *child = object->child;
while (child) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Comparing child key \"%s\" with \"%s\"", child->key, key);
if (child->key && strcmp(child->key, key) == 0) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Key \"%s\" found", key);
return child;
}
child = child->next;
}
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Key \"%s\" not found in object", key);
return NULL;
}
// -----------
// -- slice --
// -----------
// Create a slice from an array with boundary check.
Slice slice_create__(const char *file, const char *func, int line, size_t isize, void *array, size_t array_len, size_t start, size_t len) {
raise_message(LOG_LEVEL_TRACE, file, func, line, "slice_create(<optimized>, <optimized>, <optimized>, <optimized>, <optimized>)");
if (start + len > array_len)
return (Slice){NULL, 0, isize};
return (Slice){ (char *)array + start * isize, len, isize };
}
// Return a subslice from an existing slice.
Slice slice_subslice__(const char *file, const char *func, int line, Slice s, size_t start, size_t len) {
raise_message(LOG_LEVEL_TRACE, file, func, line, "slice_subslice(<optimized>, <optimized>, <optimized>)");
if (start + len > s.len)
return (Slice){NULL, 0, s.isize};
return (Slice){(char*)s.data + start * s.isize, len, s.isize};
}
int* arena_slice_copy__(const char *file, const char *func, int line, Arena *arena, Slice s) {
raise_message(LOG_LEVEL_TRACE, file, func, line, "arena_slice_copy(<optimized>, <optimized>)");
int *copy = (void*) arena_alloc__(file, func, line, arena, s.len * sizeof(int));
if (copy)
memcpy(copy, s.data, s.len * s.isize);
return copy;
}
// ------------
// -- debug --
// ------------
char* slice_to_debug_str(Arena *arena, Slice slice) {
// Создадим полную информацию о структуре Slice
char buffer_meta[128];
snprintf(buffer_meta, sizeof(buffer_meta), "Slice{addr=%p, data=%p, len=%zu, isize=%zu, content=",
(void*)&slice, slice.data, slice.len, slice.isize);
size_t meta_len = strlen(buffer_meta);
// Для NULL-данных выведем простое сообщение
if (!slice.data) {
char* result = arena_alloc(arena, meta_len + 6);
strcpy(result, buffer_meta);
strcat(result, "NULL}");
return result;
}
// Allocate buffer with space for quotes, metadata and null terminator
size_t buffer_size = meta_len + slice.len * 4 + 20; // Extra space for escaping and closing brace
char* buffer = arena_alloc(arena, buffer_size);
// Копируем метаданные
strcpy(buffer, buffer_meta);
char* pos = buffer + meta_len;
*pos++ = '"';
// Copy slice data with escaping
for (size_t i = 0; i < slice.len; i++) {
char c = ((char*)slice.data)[i];
if (c == '\0') {
*pos++ = '\\';
*pos++ = '0';
} else if (c == '\n') {
*pos++ = '\\';
*pos++ = 'n';
} else if (c == '\r') {
*pos++ = '\\';
*pos++ = 'r';
} else if (c == '\t') {
*pos++ = '\\';
*pos++ = 't';
} else if (c == '"') {
*pos++ = '\\';
*pos++ = '"';
} else if (c == '\\') {
*pos++ = '\\';
*pos++ = '\\';
} else if (c < 32 || c > 126) {
// Non-printable characters as hex
pos += sprintf(pos, "\\x%02x", (unsigned char)c);
} else {
*pos++ = c;
}
}
*pos++ = '"';
*pos++ = '}'; // Закрывающая скобка для структуры
*pos = '\0';
raise_trace("slice_to_debug_str: %s", buffer);
return buffer;
}
char* json_to_debug_str(Arena *arena, Json json) {
// Добавляем информацию о самой структуре JSON
char meta_buffer[256];
const char* type_str = "";
switch (json.type) {
case JSON_NULL: type_str = "NULL"; break;
case JSON_BOOL: type_str = "BOOL"; break;
case JSON_NUMBER: type_str = "NUMBER"; break;
case JSON_STRING: type_str = "STRING"; break;
case JSON_ARRAY: type_str = "ARRAY"; break;
case JSON_OBJECT: type_str = "OBJECT"; break;
default: type_str = "UNKNOWN";
}
snprintf(meta_buffer, sizeof(meta_buffer), "Json{addr=%p, type=%s, key=%s, child=%p, next=%p, value=",
(void*)&json, type_str, json.key ? json.key : "NULL", (void*)json.child, (void*)json.next);
size_t meta_len = strlen(meta_buffer);
char value_buffer[256] = {0};
switch (json.type) {
case JSON_NULL:
strcpy(value_buffer, "null");
break;
case JSON_BOOL:
strcpy(value_buffer, json.JsonValue.boolean ? "true" : "false");
break;
case JSON_NUMBER:
snprintf(value_buffer, sizeof(value_buffer), "%g", json.JsonValue.number);
break;
case JSON_STRING: {
if (!json.JsonValue.string) {
strcpy(value_buffer, "null");
} else {
snprintf(value_buffer, sizeof(value_buffer), "\"%s\"", json.JsonValue.string);
}
break;
}
case JSON_ARRAY: {
// Для массивов просто отметим количество элементов
size_t count = 0;
Json *item = json.child;
while (item) {
count++;
item = item->next;
}
snprintf(value_buffer, sizeof(value_buffer), "[array with %zu elements]", count);
break;
}
case JSON_OBJECT: {
// Для объектов отметим количество пар ключ-значение
size_t count = 0;
Json *item = json.child;
while (item) {
count++;
item = item->next;
}
snprintf(value_buffer, sizeof(value_buffer), "{object with %zu key-value pairs}", count);
break;
}
default:
strcpy(value_buffer, "<UNKNOWN JSON TYPE>");
}
// Создаем итоговую строку
size_t result_len = meta_len + strlen(value_buffer) + 2; // +2 для закрывающей скобки и нулевого символа
char* result = arena_alloc(arena, result_len);
strcpy(result, meta_buffer);
strcat(result, value_buffer);
strcat(result, "}");
return result;
}