summaryrefslogtreecommitdiff
path: root/src/domain/message.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/domain/message.c')
-rw-r--r--src/domain/message.c255
1 files changed, 255 insertions, 0 deletions
diff --git a/src/domain/message.c b/src/domain/message.c
new file mode 100644
index 0000000..d973be4
--- /dev/null
+++ b/src/domain/message.c
@@ -0,0 +1,255 @@
+#include "domain/message.h"
+
+#include <stdlib.h>
+#include <string.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;
+}
+
+void
+tiny_message_init(tiny_message_t *message)
+{
+ memset(message, 0, sizeof(*message));
+ message->metadata.memory_tier = TINY_TIER_SHORT;
+}
+
+void
+tiny_message_free(tiny_message_t *message)
+{
+ size_t index;
+ 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;
+ 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)
+ {
+ goto failure;
+ }
+ 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)
+ {
+ goto failure;
+ }
+ }
+ if (source->metadata.tag_count > 0)
+ {
+ destination->metadata.tags = calloc(source->metadata.tag_count, sizeof(char *));
+ if (destination->metadata.tags == NULL)
+ {
+ goto failure;
+ }
+ 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)
+ {
+ goto failure;
+ }
+ destination->metadata.tag_count++;
+ }
+ }
+ return 0;
+
+failure:
+ tiny_message_free(destination);
+ return -1;
+}
+
+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 (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 (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;
+ 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 (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;
+}