#include "domain/message.h" #include #include #include "base/allocator.h" static char * duplicate_string(const char *value) { const size_t length = strlen(value); char *copy = malloc(length + 1); if (copy != NULL) { memcpy(copy, value, length + 1); } return copy; } static int copy_failed(tiny_message_t *message) { tiny_message_free(message); return -1; } void tiny_message_init(tiny_message_t *message) { if (message == NULL) { return; } memset(message, 0, sizeof(*message)); message->metadata.memory_tier = TINY_TIER_SHORT; } void tiny_message_free(tiny_message_t *message) { size_t index; if (message == NULL) { return; } free(message->session_id); free(message->content); free(message->metadata.tool_call_id); for (index = 0; index < message->metadata.tag_count; ++index) { free(message->metadata.tags[index]); } free(message->metadata.tags); tiny_message_init(message); } int tiny_message_copy(tiny_message_t *destination, const tiny_message_t *source) { size_t index; if (destination == NULL || source == NULL || destination == source || source->session_id == NULL || source->content == NULL || (source->metadata.tag_count != 0 && source->metadata.tags == NULL)) { return -1; } tiny_message_init(destination); memcpy(destination->message_id, source->message_id, sizeof(destination->message_id)); destination->timestamp_ms = source->timestamp_ms; destination->role = source->role; destination->metadata.memory_tier = source->metadata.memory_tier; destination->session_id = duplicate_string(source->session_id); destination->content = duplicate_string(source->content); if (destination->session_id == NULL || destination->content == NULL) { return copy_failed(destination); } if (source->metadata.tool_call_id != NULL) { destination->metadata.tool_call_id = duplicate_string(source->metadata.tool_call_id); if (destination->metadata.tool_call_id == NULL) { return copy_failed(destination); } } if (source->metadata.tag_count > 0) { destination->metadata.tags = calloc(source->metadata.tag_count, sizeof(char *)); if (destination->metadata.tags == NULL) { return copy_failed(destination); } for (index = 0; index < source->metadata.tag_count; ++index) { destination->metadata.tags[index] = duplicate_string(source->metadata.tags[index]); if (destination->metadata.tags[index] == NULL) { return copy_failed(destination); } destination->metadata.tag_count++; } } return 0; } const char * tiny_role_name(tiny_role_t role) { switch (role) { case TINY_ROLE_USER: return "user"; case TINY_ROLE_ASSISTANT: return "assistant"; case TINY_ROLE_SYSTEM: return "system"; default: return "unknown"; } } bool tiny_role_parse(const char *value, tiny_role_t *role) { if (value == NULL || role == NULL) { return false; } if (strcmp(value, "user") == 0) { *role = TINY_ROLE_USER; } else if (strcmp(value, "assistant") == 0) { *role = TINY_ROLE_ASSISTANT; } else if (strcmp(value, "system") == 0) { *role = TINY_ROLE_SYSTEM; } else { return false; } return true; } const char * tiny_tier_name(tiny_memory_tier_t tier) { switch (tier) { case TINY_TIER_SHORT: return "short"; case TINY_TIER_MID: return "mid"; case TINY_TIER_LONG: return "long"; case TINY_TIER_DISCARD: return "discard"; default: return "unknown"; } } bool tiny_tier_parse(const char *value, tiny_memory_tier_t *tier) { if (value == NULL || tier == NULL) { return false; } if (strcmp(value, "short") == 0) { *tier = TINY_TIER_SHORT; } else if (strcmp(value, "mid") == 0) { *tier = TINY_TIER_MID; } else if (strcmp(value, "long") == 0) { *tier = TINY_TIER_LONG; } else if (strcmp(value, "discard") == 0) { *tier = TINY_TIER_DISCARD; } else { return false; } return true; } bool tiny_utf8_valid(const char *value, size_t length) { size_t index = 0; if (value == NULL) { return length == 0; } while (index < length) { const unsigned char first = (unsigned char)value[index++]; unsigned needed; uint32_t codepoint; if (first < 0x80) { if (first == 0) { return false; } continue; } if ((first & 0xe0) == 0xc0) { needed = 1; codepoint = first & 0x1f; } else if ((first & 0xf0) == 0xe0) { needed = 2; codepoint = first & 0x0f; } else if ((first & 0xf8) == 0xf0) { needed = 3; codepoint = first & 0x07; } else { return false; } if (index + needed > length) { return false; } while (needed-- > 0) { const unsigned char next = (unsigned char)value[index++]; if ((next & 0xc0) != 0x80) { return false; } codepoint = (codepoint << 6) | (next & 0x3f); } if (codepoint > 0x10ffff || (codepoint >= 0xd800 && codepoint <= 0xdfff)) { return false; } if ((first & 0xe0) == 0xc0 && codepoint < 0x80) { return false; } if ((first & 0xf0) == 0xe0 && codepoint < 0x800) { return false; } if ((first & 0xf8) == 0xf0 && codepoint < 0x10000) { return false; } } return true; } bool tiny_message_id_valid(const char *value) { static const char alphabet[] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; size_t index; if (value == NULL || strlen(value) != TINY_MESSAGE_ID_LENGTH || value[0] > '7') { return false; } for (index = 0; index < TINY_MESSAGE_ID_LENGTH; ++index) { if (strchr(alphabet, value[index]) == NULL) { return false; } } return true; }