Files
iKv/src/loaders/ikv2.c

952 lines
25 KiB
C

#include "ikv2.h"
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#define IKV2_BINARY_FLAGS_INDEXED_ROOT 1u
typedef struct
{
char *key;
uint32_t key_length;
uint32_t key_hash;
uint8_t type;
uint32_t payload_offset;
uint32_t payload_size;
uint8_t *payload_data;
uint32_t next_in_bucket;
} ikv2_index_entry_t;
typedef enum
{
IKV2_SOURCE_FILE = 1,
IKV2_SOURCE_MEMORY = 2
} ikv2_source_kind_t;
typedef struct
{
ikv_lazy_state_t base;
ikv2_source_kind_t source_kind;
FILE *file_handle;
uint8_t *memory_data;
size_t memory_size;
uint32_t entry_count;
uint32_t bucket_count;
uint32_t *bucket_heads;
ikv2_index_entry_t *entries;
} ikv2_lazy_root_t;
typedef struct
{
uint8_t *data;
size_t size;
size_t capacity;
bool ok;
} ikv2_buffer_t;
typedef struct
{
const uint8_t *data;
size_t size;
size_t offset;
} ikv2_cursor_t;
#define IKV2_INDEX_NONE 0xFFFFFFFFu
static uint32_t ikv2_hash_key(const char *value)
{
uint32_t hash = 2166136261u;
while (value && *value)
{
hash ^= (uint8_t)*value++;
hash *= 16777619u;
}
return hash;
}
static uint32_t ikv2_varu32_size(uint32_t value)
{
uint32_t size = 1u;
while (value >= 0x80u)
{
value >>= 7u;
++size;
}
return size;
}
static uint32_t ikv2_bucket_count_for_entries(uint32_t entry_count)
{
uint32_t bucket_count = 64u;
while (bucket_count < (entry_count * 4u) / 3u + 1u)
{
if (bucket_count > 0x7FFFFFFFu)
break;
bucket_count *= 2u;
}
return bucket_count;
}
static int ikv2_compare_entries(const void *lhs, const void *rhs)
{
const ikv2_index_entry_t *left = (const ikv2_index_entry_t *)lhs;
const ikv2_index_entry_t *right = (const ikv2_index_entry_t *)rhs;
return strcmp(left->key ? left->key : "", right->key ? right->key : "");
}
static bool ikv2_buffer_reserve(ikv2_buffer_t *buffer, size_t additional)
{
uint8_t *next = NULL;
size_t required = 0;
size_t capacity = 0;
if (!buffer || !buffer->ok)
return false;
if (additional > ((size_t)-1) - buffer->size)
{
buffer->ok = false;
return false;
}
required = buffer->size + additional;
if (required <= buffer->capacity)
return true;
capacity = buffer->capacity ? buffer->capacity : 256u;
while (capacity < required)
{
if (capacity > ((size_t)-1) / 2u)
{
buffer->ok = false;
return false;
}
capacity *= 2u;
}
next = (uint8_t *)IKV_REALLOC(buffer->data, capacity);
if (!next)
{
buffer->ok = false;
return false;
}
buffer->data = next;
buffer->capacity = capacity;
return true;
}
static void ikv2_buffer_write_bytes(ikv2_buffer_t *buffer, const void *data, size_t size)
{
if (!ikv2_buffer_reserve(buffer, size))
return;
if (size > 0u)
memcpy(buffer->data + buffer->size, data, size);
buffer->size += size;
}
static void ikv2_buffer_write_u8(ikv2_buffer_t *buffer, uint8_t value)
{
ikv2_buffer_write_bytes(buffer, &value, 1u);
}
static void ikv2_buffer_write_u32le(ikv2_buffer_t *buffer, uint32_t value)
{
uint8_t bytes[4];
bytes[0] = (uint8_t)(value & 0xFFu);
bytes[1] = (uint8_t)((value >> 8) & 0xFFu);
bytes[2] = (uint8_t)((value >> 16) & 0xFFu);
bytes[3] = (uint8_t)((value >> 24) & 0xFFu);
ikv2_buffer_write_bytes(buffer, bytes, sizeof(bytes));
}
static void ikv2_buffer_write_varu32(ikv2_buffer_t *buffer, uint32_t value)
{
while (value >= 0x80u)
{
ikv2_buffer_write_u8(buffer, (uint8_t)((value & 0x7Fu) | 0x80u));
value >>= 7;
}
ikv2_buffer_write_u8(buffer, (uint8_t)value);
}
static void ikv2_buffer_write_string(ikv2_buffer_t *buffer, const char *value)
{
size_t length = value ? strlen(value) : 0u;
ikv2_buffer_write_varu32(buffer, (uint32_t)length);
if (length > 0u)
ikv2_buffer_write_bytes(buffer, value, length);
}
static bool ikv2_cursor_need(const ikv2_cursor_t *cursor, size_t size)
{
return cursor && cursor->offset + size <= cursor->size;
}
static bool ikv2_cursor_read_u8(ikv2_cursor_t *cursor, uint8_t *out_value)
{
if (!ikv2_cursor_need(cursor, 1u))
return false;
*out_value = cursor->data[cursor->offset++];
return true;
}
static bool ikv2_cursor_read_u32le(ikv2_cursor_t *cursor, uint32_t *out_value)
{
const uint8_t *p = NULL;
if (!ikv2_cursor_need(cursor, 4u))
return false;
p = cursor->data + cursor->offset;
*out_value = (uint32_t)p[0] |
((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16) |
((uint32_t)p[3] << 24);
cursor->offset += 4u;
return true;
}
static bool ikv2_cursor_read_varu32(ikv2_cursor_t *cursor, uint32_t *out_value)
{
uint32_t shift = 0u;
uint32_t value = 0u;
while (shift < 32u)
{
uint8_t byte = 0u;
if (!ikv2_cursor_read_u8(cursor, &byte))
return false;
value |= (uint32_t)(byte & 0x7Fu) << shift;
if ((byte & 0x80u) == 0u)
{
*out_value = value;
return true;
}
shift += 7u;
}
return false;
}
static char *ikv2_cursor_read_string(ikv2_cursor_t *cursor)
{
uint32_t length = 0u;
char *value = NULL;
if (!ikv2_cursor_read_varu32(cursor, &length))
return NULL;
if (!ikv2_cursor_need(cursor, (size_t)length))
return NULL;
value = (char *)IKV_MALLOC((size_t)length + 1u);
if (!value)
return NULL;
if (length > 0u)
memcpy(value, cursor->data + cursor->offset, length);
value[length] = 0;
cursor->offset += (size_t)length;
return value;
}
static bool ikv2_file_read_u8(FILE *file, uint8_t *out_value)
{
return file && out_value && IKV_FREAD(out_value, 1u, 1u, file) == 1u;
}
static bool ikv2_file_read_u32le(FILE *file, uint32_t *out_value)
{
uint8_t bytes[4];
if (!file || !out_value || IKV_FREAD(bytes, 1u, sizeof(bytes), file) != sizeof(bytes))
return false;
*out_value = (uint32_t)bytes[0] |
((uint32_t)bytes[1] << 8) |
((uint32_t)bytes[2] << 16) |
((uint32_t)bytes[3] << 24);
return true;
}
static bool ikv2_file_read_varu32(FILE *file, uint32_t *out_value)
{
uint32_t shift = 0u;
uint32_t value = 0u;
while (shift < 32u)
{
uint8_t byte = 0u;
if (!ikv2_file_read_u8(file, &byte))
return false;
value |= (uint32_t)(byte & 0x7Fu) << shift;
if ((byte & 0x80u) == 0u)
{
*out_value = value;
return true;
}
shift += 7u;
}
return false;
}
static char *ikv2_file_read_string(FILE *file)
{
uint32_t length = 0u;
char *value = NULL;
if (!ikv2_file_read_varu32(file, &length))
return NULL;
value = (char *)IKV_MALLOC((size_t)length + 1u);
if (!value)
return NULL;
if (length > 0u && IKV_FREAD(value, 1u, length, file) != length)
{
IKV_FREE(value);
return NULL;
}
value[length] = 0;
return value;
}
static bool ikv2_collect_root_entries(const ikv_node_t *root, ikv2_index_entry_t **out_entries, uint32_t *out_count)
{
ikv2_index_entry_t *entries = NULL;
uint32_t count = 0u;
uint32_t index = 0u;
if (!root || root->type != IKV_OBJECT || !out_entries || !out_count)
return false;
count = root->value.object.size;
entries = count ? (ikv2_index_entry_t *)IKV_CALLOC(count, sizeof(*entries)) : NULL;
if (count > 0u && !entries)
return false;
for (uint32_t bucket = 0; bucket < root->value.object.bucket_count; ++bucket)
{
for (ikv_node_t *node = root->value.object.buckets[bucket]; node; node = node->next)
{
uint8_t *payload = NULL;
uint32_t payload_size = 0u;
if (index >= count || !ikv__write_binary_node_memory(node, &payload, &payload_size))
{
if (payload)
IKV_FREE(payload);
for (uint32_t i = 0; i < index; ++i)
{
IKV_FREE(entries[i].payload_data);
}
IKV_FREE(entries);
return false;
}
entries[index].key = (char *)(node->key ? node->key : "");
entries[index].key_length = (uint32_t)strlen(entries[index].key);
entries[index].key_hash = ikv2_hash_key(entries[index].key);
entries[index].type = (uint8_t)node->type;
entries[index].payload_data = payload;
entries[index].payload_size = payload_size;
entries[index].next_in_bucket = IKV2_INDEX_NONE;
++index;
}
}
qsort(entries, count, sizeof(*entries), ikv2_compare_entries);
*out_entries = entries;
*out_count = count;
return true;
}
static bool ikv2_build_indexed_binary(const ikv_node_t *root, uint8_t **out_data, uint32_t *out_size)
{
ikv2_index_entry_t *entries = NULL;
uint32_t entry_count = 0u;
uint32_t header_size = 0u;
uint32_t payload_base = 0u;
uint32_t total_size = 0u;
const char *root_key = NULL;
uint32_t root_key_length = 0u;
ikv2_buffer_t buffer = {0};
if (!root || !out_data || !out_size || root->type != IKV_OBJECT)
return false;
buffer.ok = true;
*out_data = NULL;
*out_size = 0u;
if (!ikv2_collect_root_entries(root, &entries, &entry_count))
return false;
root_key = (root->key && root->key[0]) ? root->key : "root";
root_key_length = (uint32_t)strlen(root_key);
header_size = 4u + 1u + 4u + 4u;
header_size += ikv2_varu32_size(root_key_length) + root_key_length;
header_size += ikv2_varu32_size(entry_count);
for (uint32_t i = 0; i < entry_count; ++i)
header_size += ikv2_varu32_size(entries[i].key_length) + entries[i].key_length;
header_size += entry_count * (1u + 4u + 4u);
payload_base = header_size;
total_size = header_size;
for (uint32_t i = 0; i < entry_count; ++i)
total_size += entries[i].payload_size;
buffer.data = total_size ? (uint8_t *)IKV_MALLOC(total_size) : NULL;
buffer.capacity = total_size;
if (total_size > 0u && !buffer.data)
{
for (uint32_t i = 0; i < entry_count; ++i)
IKV_FREE(entries[i].payload_data);
IKV_FREE(entries);
return false;
}
ikv2_buffer_write_bytes(&buffer, "iKv2", 4u);
ikv2_buffer_write_u8(&buffer, (uint8_t)'b');
ikv2_buffer_write_u32le(&buffer, IKV_V2);
ikv2_buffer_write_u32le(&buffer, IKV2_BINARY_FLAGS_INDEXED_ROOT);
ikv2_buffer_write_string(&buffer, root_key);
ikv2_buffer_write_varu32(&buffer, entry_count);
for (uint32_t i = 0; i < entry_count; ++i)
ikv2_buffer_write_string(&buffer, entries[i].key ? entries[i].key : "");
for (uint32_t i = 0; i < entry_count; ++i)
{
ikv2_buffer_write_u8(&buffer, entries[i].type);
ikv2_buffer_write_u32le(&buffer, payload_base);
ikv2_buffer_write_u32le(&buffer, entries[i].payload_size);
payload_base += entries[i].payload_size;
}
for (uint32_t i = 0; i < entry_count; ++i)
{
const uint8_t *payload = entries[i].payload_data;
ikv2_buffer_write_bytes(&buffer, payload, entries[i].payload_size);
}
for (uint32_t i = 0; i < entry_count; ++i)
{
IKV_FREE(entries[i].payload_data);
}
IKV_FREE(entries);
if (!buffer.ok || buffer.size == 0u || buffer.size > 0xFFFFFFFFu)
{
IKV_FREE(buffer.data);
return false;
}
*out_data = buffer.data;
*out_size = (uint32_t)buffer.size;
return true;
}
static void ikv2_lazy_root_destroy(ikv_lazy_state_t *state)
{
ikv2_lazy_root_t *lazy_root = (ikv2_lazy_root_t *)state;
if (!lazy_root)
return;
for (uint32_t i = 0; i < lazy_root->entry_count; ++i)
IKV_FREE(lazy_root->entries[i].key);
if (lazy_root->file_handle)
IKV_FCLOSE(lazy_root->file_handle);
IKV_FREE(lazy_root->bucket_heads);
IKV_FREE(lazy_root->entries);
IKV_FREE(lazy_root->memory_data);
IKV_FREE(lazy_root);
}
static bool ikv2_build_entry_buckets(ikv2_lazy_root_t *lazy_root)
{
if (!lazy_root)
return false;
lazy_root->bucket_count = ikv2_bucket_count_for_entries(lazy_root->entry_count);
lazy_root->bucket_heads = (uint32_t *)IKV_MALLOC(sizeof(*lazy_root->bucket_heads) * lazy_root->bucket_count);
if (!lazy_root->bucket_heads)
return false;
for (uint32_t i = 0; i < lazy_root->bucket_count; ++i)
lazy_root->bucket_heads[i] = IKV2_INDEX_NONE;
for (uint32_t i = 0; i < lazy_root->entry_count; ++i)
{
uint32_t bucket = lazy_root->entries[i].key_hash % lazy_root->bucket_count;
lazy_root->entries[i].next_in_bucket = lazy_root->bucket_heads[bucket];
lazy_root->bucket_heads[bucket] = i;
}
return true;
}
static ikv2_index_entry_t *ikv2_find_entry(ikv2_lazy_root_t *lazy_root, const char *key)
{
uint32_t hash = 0u;
uint32_t bucket = 0u;
uint32_t entry_index = IKV2_INDEX_NONE;
size_t key_length = 0u;
if (!lazy_root || !key || lazy_root->entry_count == 0u || lazy_root->bucket_count == 0u || !lazy_root->bucket_heads)
return NULL;
hash = ikv2_hash_key(key);
key_length = strlen(key);
bucket = hash % lazy_root->bucket_count;
entry_index = lazy_root->bucket_heads[bucket];
while (entry_index != IKV2_INDEX_NONE)
{
ikv2_index_entry_t *entry = &lazy_root->entries[entry_index];
if (entry->key_hash == hash &&
entry->key_length == (uint32_t)key_length &&
strcmp(key, entry->key ? entry->key : "") == 0)
return entry;
entry_index = entry->next_in_bucket;
}
return NULL;
}
static bool ikv2_read_payload_from_file(ikv2_lazy_root_t *lazy_root, uint32_t offset, uint32_t size, uint8_t **out_data)
{
uint8_t *data = NULL;
if (!lazy_root || !lazy_root->file_handle || !out_data)
return false;
*out_data = NULL;
if (IKV_FSEEK(lazy_root->file_handle, (long)offset, SEEK_SET) != 0)
return false;
data = (uint8_t *)IKV_MALLOC(size);
if (!data)
return false;
if (size > 0u && IKV_FREAD(data, 1u, size, lazy_root->file_handle) != size)
{
IKV_FREE(data);
return false;
}
*out_data = data;
return true;
}
static bool ikv2_payload_range_valid(size_t total_size, uint32_t payload_offset, uint32_t payload_size)
{
size_t end_offset = 0u;
if ((size_t)payload_offset > total_size)
return false;
if ((size_t)payload_size > total_size - (size_t)payload_offset)
return false;
end_offset = (size_t)payload_offset + (size_t)payload_size;
return end_offset <= total_size;
}
static ikv_node_t *ikv2_lazy_root_load_object_key(ikv_lazy_state_t *state, ikv_node_t *object_node, const char *key)
{
ikv2_lazy_root_t *lazy_root = (ikv2_lazy_root_t *)state;
ikv2_index_entry_t *entry = NULL;
const uint8_t *payload_data = NULL;
uint8_t *owned_payload = NULL;
ikv_node_t *node = NULL;
size_t consumed = 0u;
(void)object_node;
if (!lazy_root || !key)
return NULL;
entry = ikv2_find_entry(lazy_root, key);
if (!entry)
return NULL;
if (lazy_root->source_kind == IKV2_SOURCE_MEMORY)
{
if ((size_t)entry->payload_offset + (size_t)entry->payload_size > lazy_root->memory_size)
return NULL;
payload_data = lazy_root->memory_data + entry->payload_offset;
}
else
{
if (!ikv2_read_payload_from_file(lazy_root, entry->payload_offset, entry->payload_size, &owned_payload))
return NULL;
payload_data = owned_payload;
}
node = ikv__parse_binary_node_memory(payload_data, entry->payload_size, &consumed);
IKV_FREE(owned_payload);
if (!node || consumed != entry->payload_size)
{
if (node)
ikv_free(node);
return NULL;
}
return node;
}
static ikv_node_t *ikv2_parse_indexed_binary_buffer(uint8_t *buffer, size_t size)
{
ikv2_cursor_t cursor;
uint32_t version = 0u;
uint32_t flags = 0u;
uint32_t entry_count = 0u;
uint8_t kind = 0u;
char *root_name = NULL;
ikv_node_t *root = NULL;
ikv2_lazy_root_t *lazy_root = NULL;
cursor.data = buffer;
cursor.size = size;
cursor.offset = 0u;
if (!ikv2_cursor_need(&cursor, 9u))
return NULL;
if (memcmp(cursor.data, "iKv2", 4u) != 0)
return NULL;
cursor.offset += 4u;
if (!ikv2_cursor_read_u8(&cursor, &kind))
return NULL;
if (kind != (uint8_t)'b')
return NULL;
if (!ikv2_cursor_read_u32le(&cursor, &version) || version != IKV_V2)
return NULL;
if (!ikv2_cursor_read_u32le(&cursor, &flags) || (flags & IKV2_BINARY_FLAGS_INDEXED_ROOT) == 0u)
return NULL;
root_name = ikv2_cursor_read_string(&cursor);
if (!root_name)
return NULL;
if (!ikv2_cursor_read_varu32(&cursor, &entry_count))
{
IKV_FREE(root_name);
return NULL;
}
root = ikv_create_object(root_name);
IKV_FREE(root_name);
if (!root)
return NULL;
if (!ikv__object_reserve_for_count(root, entry_count))
{
ikv_free(root);
return NULL;
}
root->value.object.size = entry_count;
lazy_root = (ikv2_lazy_root_t *)IKV_CALLOC(1u, sizeof(*lazy_root));
if (!lazy_root)
{
ikv_free(root);
return NULL;
}
lazy_root->base.destroy = ikv2_lazy_root_destroy;
lazy_root->base.load_object_key = ikv2_lazy_root_load_object_key;
lazy_root->entry_count = entry_count;
lazy_root->entries = entry_count ? (ikv2_index_entry_t *)IKV_CALLOC(entry_count, sizeof(*lazy_root->entries)) : NULL;
if (entry_count > 0u && !lazy_root->entries)
{
ikv2_lazy_root_destroy(&lazy_root->base);
ikv_free(root);
return NULL;
}
for (uint32_t i = 0; i < entry_count; ++i)
{
lazy_root->entries[i].key = ikv2_cursor_read_string(&cursor);
if (!lazy_root->entries[i].key)
{
ikv2_lazy_root_destroy(&lazy_root->base);
ikv_free(root);
return NULL;
}
lazy_root->entries[i].key_length = (uint32_t)strlen(lazy_root->entries[i].key);
lazy_root->entries[i].key_hash = ikv2_hash_key(lazy_root->entries[i].key);
lazy_root->entries[i].next_in_bucket = IKV2_INDEX_NONE;
}
for (uint32_t i = 0; i < entry_count; ++i)
{
if (!ikv2_cursor_read_u8(&cursor, &lazy_root->entries[i].type) ||
!ikv2_cursor_read_u32le(&cursor, &lazy_root->entries[i].payload_offset) ||
!ikv2_cursor_read_u32le(&cursor, &lazy_root->entries[i].payload_size))
{
ikv2_lazy_root_destroy(&lazy_root->base);
ikv_free(root);
return NULL;
}
if (!ikv2_payload_range_valid(size,
lazy_root->entries[i].payload_offset,
lazy_root->entries[i].payload_size))
{
ikv2_lazy_root_destroy(&lazy_root->base);
ikv_free(root);
return NULL;
}
}
if (!ikv2_build_entry_buckets(lazy_root))
{
ikv2_lazy_root_destroy(&lazy_root->base);
ikv_free(root);
return NULL;
}
lazy_root->source_kind = IKV2_SOURCE_MEMORY;
lazy_root->memory_data = buffer;
lazy_root->memory_size = size;
root->lazy_state = &lazy_root->base;
return root;
}
static bool ikv2_write_text_file(const char *path, const ikv_node_t *root)
{
return ikv__write_text_file_version(path, root, IKV_V2);
}
static ikv_node_t *ikv2_parse_text_file(const char *path)
{
return ikv__parse_text_file_version(path, IKV_V2);
}
static ikv_node_t *ikv2_parse_text_string(const char *src)
{
return ikv__parse_text_string_version(src, IKV_V2);
}
static bool ikv2_write_binary_memory(const ikv_node_t *root, uint8_t **out_data, uint32_t *out_size)
{
return ikv2_build_indexed_binary(root, out_data, out_size);
}
static bool ikv2_write_binary_file(const char *path, const ikv_node_t *root)
{
uint8_t *data = NULL;
uint32_t size = 0u;
FILE *file = NULL;
bool ok = false;
if (!path || !ikv2_write_binary_memory(root, &data, &size))
return false;
file = IKV_FOPEN(path, "wb");
if (!file)
{
IKV_FREE(data);
return false;
}
ok = (fwrite(data, 1u, size, file) == size);
if (IKV_FCLOSE(file) != 0)
ok = false;
IKV_FREE(data);
return ok;
}
static ikv_node_t *ikv2_parse_binary_file(const char *path)
{
FILE *file = NULL;
uint8_t magic[4];
uint8_t kind = 0u;
uint32_t version = 0u;
uint32_t flags = 0u;
uint32_t entry_count = 0u;
char *root_name = NULL;
ikv_node_t *root = NULL;
ikv2_lazy_root_t *lazy_root = NULL;
long file_size_long = 0;
size_t file_size = 0u;
if (!path)
return NULL;
file = IKV_FOPEN(path, "rb");
if (!file)
return NULL;
if (IKV_FREAD(magic, 1u, sizeof(magic), file) != sizeof(magic) ||
memcmp(magic, "iKv2", sizeof(magic)) != 0 ||
!ikv2_file_read_u8(file, &kind) ||
kind != (uint8_t)'b' ||
!ikv2_file_read_u32le(file, &version) ||
version != IKV_V2 ||
!ikv2_file_read_u32le(file, &flags) ||
(flags & IKV2_BINARY_FLAGS_INDEXED_ROOT) == 0u)
{
IKV_FCLOSE(file);
return NULL;
}
root_name = ikv2_file_read_string(file);
if (!root_name || !ikv2_file_read_varu32(file, &entry_count))
{
IKV_FREE(root_name);
IKV_FCLOSE(file);
return NULL;
}
root = ikv_create_object(root_name);
IKV_FREE(root_name);
if (!root || !ikv__object_reserve_for_count(root, entry_count))
{
if (root)
ikv_free(root);
IKV_FCLOSE(file);
return NULL;
}
root->value.object.size = entry_count;
lazy_root = (ikv2_lazy_root_t *)IKV_CALLOC(1u, sizeof(*lazy_root));
if (!lazy_root)
{
ikv_free(root);
IKV_FCLOSE(file);
return NULL;
}
lazy_root->base.destroy = ikv2_lazy_root_destroy;
lazy_root->base.load_object_key = ikv2_lazy_root_load_object_key;
lazy_root->source_kind = IKV2_SOURCE_FILE;
lazy_root->entry_count = entry_count;
lazy_root->entries = entry_count ? (ikv2_index_entry_t *)IKV_CALLOC(entry_count, sizeof(*lazy_root->entries)) : NULL;
if (entry_count > 0u && !lazy_root->entries)
{
ikv2_lazy_root_destroy(&lazy_root->base);
ikv_free(root);
IKV_FCLOSE(file);
return NULL;
}
for (uint32_t i = 0; i < entry_count; ++i)
{
lazy_root->entries[i].key = ikv2_file_read_string(file);
if (!lazy_root->entries[i].key)
{
ikv2_lazy_root_destroy(&lazy_root->base);
ikv_free(root);
IKV_FCLOSE(file);
return NULL;
}
lazy_root->entries[i].key_length = (uint32_t)strlen(lazy_root->entries[i].key);
lazy_root->entries[i].key_hash = ikv2_hash_key(lazy_root->entries[i].key);
lazy_root->entries[i].next_in_bucket = IKV2_INDEX_NONE;
}
for (uint32_t i = 0; i < entry_count; ++i)
{
if (!ikv2_file_read_u8(file, &lazy_root->entries[i].type) ||
!ikv2_file_read_u32le(file, &lazy_root->entries[i].payload_offset) ||
!ikv2_file_read_u32le(file, &lazy_root->entries[i].payload_size))
{
ikv2_lazy_root_destroy(&lazy_root->base);
ikv_free(root);
IKV_FCLOSE(file);
return NULL;
}
}
if (IKV_FSEEK(file, 0L, SEEK_END) != 0)
{
ikv2_lazy_root_destroy(&lazy_root->base);
ikv_free(root);
IKV_FCLOSE(file);
return NULL;
}
file_size_long = IKV_FTELL(file);
if (file_size_long < 0L)
{
ikv2_lazy_root_destroy(&lazy_root->base);
ikv_free(root);
IKV_FCLOSE(file);
return NULL;
}
file_size = (size_t)file_size_long;
for (uint32_t i = 0; i < entry_count; ++i)
{
if (!ikv2_payload_range_valid(file_size,
lazy_root->entries[i].payload_offset,
lazy_root->entries[i].payload_size))
{
ikv2_lazy_root_destroy(&lazy_root->base);
ikv_free(root);
IKV_FCLOSE(file);
return NULL;
}
}
if (!ikv2_build_entry_buckets(lazy_root))
{
ikv2_lazy_root_destroy(&lazy_root->base);
ikv_free(root);
IKV_FCLOSE(file);
return NULL;
}
lazy_root->file_handle = file;
root->lazy_state = &lazy_root->base;
return root;
}
static ikv_node_t *ikv2_parse_binary_memory(const void *data, size_t size)
{
uint8_t *buffer = NULL;
if (!data || size == 0u)
return NULL;
buffer = (uint8_t *)IKV_MALLOC(size);
if (!buffer)
return NULL;
memcpy(buffer, data, size);
{
ikv_node_t *root = ikv2_parse_indexed_binary_buffer(buffer, size);
if (!root)
IKV_FREE(buffer);
return root;
}
}
const ikv_loader_t ikv_loader_v2 = {
IKV_VERSION_2,
ikv2_write_text_file,
ikv2_parse_text_file,
ikv2_parse_text_string,
ikv2_write_binary_file,
ikv2_write_binary_memory,
ikv2_parse_binary_file,
ikv2_parse_binary_memory,
};