Compare commits

..

6 Commits

46 changed files with 21343 additions and 33746 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-814
View File
@@ -1,814 +0,0 @@
#if defined(_CLANGD)
#include "../dn.h"
#endif
struct DN_ArrayFindEqMemcmpContext_
{
DN_USize elem_size;
void const *find;
};
DN_API void *DN_SliceAllocArena(void **data, DN_USize *slice_size_field, DN_USize size, DN_USize elem_size, DN_U8 align, DN_ZMem zmem, DN_Arena *arena)
{
void *result = *data;
*data = DN_ArenaAlloc(arena, size * elem_size, align, zmem);
if (*data)
*slice_size_field = size;
return result;
}
DN_API DN_ArrayFindResult DN_ArrayFind(void *data, DN_USize size, DN_USize elem_size, void const *find, DN_ArrayFindEqFunc *eq_func)
{
DN_ArrayFindResult result = {};
DN_Assert(data);
DN_Assert(elem_size);
if (find) {
for (DN_ForIndexU(index, size)) {
DN_U8 *it = DN_Cast(DN_U8 *) data + (index * elem_size);
if (eq_func(it, find)) {
result.index = index;
result.value = it;
result.success = true;
break;
}
}
}
return result;
}
static bool DN_ArrayFindEqMemEqUnsafe_(void const *lhs, void const *find)
{
DN_ArrayFindEqMemcmpContext_ *context = DN_Cast(DN_ArrayFindEqMemcmpContext_ *) find;
bool result = DN_MemEqUnsafe(lhs, context->find, context->elem_size);
return result;
}
DN_API DN_ArrayFindResult DN_ArrayFindMemEq(void *data, DN_USize size, DN_USize elem_size, void const *find)
{
DN_ArrayFindEqMemcmpContext_ context = {};
context.elem_size = elem_size;
context.find = find;
DN_ArrayFindResult result = DN_ArrayFind(data, size, elem_size, &context, DN_ArrayFindEqMemEqUnsafe_);
return result;
}
DN_API void *DN_ArrayInsertArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize index, void const *items, DN_USize count)
{
void *result = nullptr;
if (!data || !size || !items || count <= 0 || ((*size + count) > max))
return result;
DN_USize clamped_index = DN_Min(index, *size);
if (clamped_index != *size) {
char const *src = DN_Cast(char *)data + (clamped_index * elem_size);
char const *dest = DN_Cast(char *)data + ((clamped_index + count) * elem_size);
char const *end = DN_Cast(char *)data + (size[0] * elem_size);
DN_USize bytes_to_move = end - src;
DN_Memmove(DN_Cast(void *) dest, src, bytes_to_move);
}
result = DN_Cast(char *)data + (clamped_index * elem_size);
DN_Memcpy(result, items, elem_size * count);
*size += count;
return result;
}
DN_API void *DN_ArrayPopFront(void *data, DN_USize *size, DN_USize elem_size, DN_USize count)
{
if (!data || !size || *size == 0 || count == 0)
return nullptr;
DN_USize pop_count = DN_Min(count, *size);
void *result = data;
if (pop_count < *size) {
char *src = DN_Cast(char *)data + (pop_count * elem_size);
char *dest = DN_Cast(char *)data;
DN_USize bytes_to_move = (*size - pop_count) * elem_size;
DN_Memmove(dest, src, bytes_to_move);
}
*size -= pop_count;
return result;
}
DN_API void *DN_ArrayPopBack(void *data, DN_USize *size, DN_USize elem_size, DN_USize count)
{
if (!data || !size || *size == 0 || count == 0)
return nullptr;
DN_USize pop_count = DN_Min(count, *size);
*size -= pop_count;
return DN_Cast(char *)data + (*size * elem_size);
}
DN_API DN_ArrayEraseResult DN_ArrayEraseRange(void *data, DN_USize *size, DN_USize elem_size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase)
{
DN_ArrayEraseResult result = {};
result.it_index = begin_index;
if (!data || !size || *size == 0 || count == 0)
return result;
// Compute the range to erase
DN_USize start = 0, end = 0;
if (count < 0) {
// Erase backwards from begin_index, inclusive of begin_index
// Range: [begin_index + count + 1, begin_index + 1)
// Which is: [begin_index - abs(count) + 1, begin_index + 1)
DN_USize abs_count = DN_Abs(count);
start = (begin_index + 1 > abs_count) ? (begin_index + 1 - abs_count) : 0;
end = begin_index + 1;
} else {
start = begin_index;
end = begin_index + count;
}
// Clamp indices to valid bounds
start = DN_Min(start, *size);
end = DN_Min(end, *size);
// Erase the range [start, end)
DN_USize erase_count = end > start ? end - start : 0;
if (erase_count) {
char *dest = (char *)data + (elem_size * start);
char *array_end = (char *)data + (elem_size * *size);
char *src = dest + (elem_size * erase_count);
if (erase == DN_ArrayErase_Stable) {
DN_USize move_size = array_end - src;
DN_Memmove(dest, src, move_size);
} else {
char *unstable_src = array_end - (elem_size * erase_count);
DN_USize move_size = array_end - unstable_src;
DN_Memcpy(dest, unstable_src, move_size);
}
*size -= erase_count;
}
result.items_erased = erase_count;
result.it_index = start ? start - 1 : start;
return result;
}
DN_API void *DN_ArrayMakeArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize make_count, DN_ZMem z_mem)
{
void *result = nullptr;
DN_USize new_size = *size + make_count;
if (new_size <= max) {
result = DN_Cast(char *) data + (elem_size * size[0]);
*size = new_size;
if (z_mem == DN_ZMem_Yes)
DN_Memset(result, 0, elem_size * make_count);
}
return result;
}
DN_API void *DN_ArrayMakeArrayAssert(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize make_count, DN_ZMem z_mem, DN_CallSite call_site)
{
void *result = DN_ArrayMakeArray(data, size, max, elem_size, make_count, z_mem);
DN_AssertArgsF(result, call_site, "array=%p size=%zu max=%zu", data, *size, max);
return result;
}
DN_API void *DN_ArrayAddArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add)
{
void *result = DN_ArrayMakeArray(data, size, max, elem_size, elems_count, DN_ZMem_No);
if (result) {
if (add == DN_ArrayAdd_Append) {
DN_Memcpy(result, elems, elems_count * elem_size);
} else {
char *move_dest = DN_Cast(char *)data + (elems_count * elem_size); // Shift elements forward
char *move_src = DN_Cast(char *)data;
DN_Memmove(move_dest, move_src, elem_size * size[0]);
DN_Memcpy(data, elems, elem_size * elems_count);
}
}
return result;
}
DN_API void *DN_ArrayAddArrayAssert(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add, DN_CallSite call_site)
{
void *result = DN_ArrayAddArray(data, size, max, elem_size, elems, elems_count, add);
DN_AssertArgsF(result, call_site, "array=%p size=%zu max=%zu", data, *size, max);
return result;
}
DN_API bool DN_ArrayResizeFromArena(void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max)
{
bool result = true;
if (new_max != *max) {
DN_USize bytes_to_alloc = elem_size * new_max;
void *buffer = DN_PoolNewArray(pool, DN_U8, bytes_to_alloc);
if (buffer) {
DN_USize bytes_to_copy = elem_size * DN_Min(*size, new_max);
DN_Memcpy(buffer, *data, bytes_to_copy);
DN_PoolDealloc(pool, *data);
*data = buffer;
*max = new_max;
*size = DN_Min(*size, new_max);
} else {
result = false;
}
}
return result;
}
DN_API bool DN_ArrayResizeFromPool(void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max)
{
bool result = true;
if (new_max != *max) {
DN_USize bytes_to_alloc = elem_size * new_max;
void *buffer = DN_PoolNewArray(pool, DN_U8, bytes_to_alloc);
if (buffer) {
DN_USize bytes_to_copy = elem_size * DN_Min(*size, new_max);
DN_Memcpy(buffer, *data, bytes_to_copy);
DN_PoolDealloc(pool, *data);
*data = buffer;
*max = new_max;
*size = DN_Min(*size, new_max);
} else {
result = false;
}
}
return result;
}
DN_API bool DN_ArrayResizeFromArena(void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize new_max)
{
bool result = true;
if (new_max != *max) {
DN_USize bytes_to_alloc = elem_size * new_max;
void *buffer = DN_ArenaNewArray(arena, DN_U8, bytes_to_alloc, DN_ZMem_No);
if (buffer) {
DN_USize bytes_to_copy = elem_size * DN_Min(*size, new_max);
DN_Memcpy(buffer, *data, bytes_to_copy);
*data = buffer;
*max = new_max;
*size = DN_Min(*size, new_max);
} else {
result = false;
}
}
return result;
}
DN_API bool DN_ArrayGrowFromPool(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max)
{
bool result = true;
if (new_max > *max)
result = DN_ArrayResizeFromPool(data, &size, max, elem_size, pool, new_max);
return result;
}
DN_API bool DN_ArrayGrowFromArena(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize new_max)
{
bool result = true;
if (new_max > *max)
result = DN_ArrayResizeFromArena(data, &size, max, elem_size, arena, new_max);
return result;
}
DN_API bool DN_ArrayGrowIfNeededFromPool(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize add_count)
{
bool result = true;
DN_USize new_size = size + add_count;
if (new_size > *max) {
DN_USize new_max = DN_Max(DN_Max(*max * 2, new_size), 8);
result = DN_ArrayResizeFromPool(data, &size, max, elem_size, pool, new_max);
}
return result;
}
DN_API bool DN_ArrayGrowIfNeededFromArena(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize add_count)
{
bool result = true;
DN_USize new_size = size + add_count;
if (new_size > *max) {
DN_USize new_max = DN_Max(DN_Max(*max * 2, new_size), 8);
result = DN_ArrayResizeFromArena(data, &size, max, elem_size, arena, new_max);
}
return result;
}
DN_API void *DN_SinglyLLDetach(void **link, void **next)
{
void *result = *link;
if (*link) {
*link = *next;
*next = nullptr;
}
return result;
}
DN_API bool DN_RingHasSpace(DN_Ring const *ring, DN_U64 size)
{
DN_U64 avail = ring->write_pos - ring->read_pos;
DN_U64 space = ring->size - avail;
bool result = space >= size;
return result;
}
DN_API bool DN_RingHasData(DN_Ring const *ring, DN_U64 size)
{
DN_U64 data = ring->write_pos - ring->read_pos;
bool result = data >= size;
return result;
}
DN_API void DN_RingWrite(DN_Ring *ring, void const *src, DN_U64 src_size)
{
DN_Assert(src_size <= ring->size);
DN_U64 offset = ring->write_pos % ring->size;
DN_U64 bytes_before_split = ring->size - offset;
DN_U64 pre_split_bytes = DN_Min(bytes_before_split, src_size);
DN_U64 post_split_bytes = src_size - pre_split_bytes;
void const *pre_split_data = src;
void const *post_split_data = ((char *)src + pre_split_bytes);
DN_Memcpy(ring->base + offset, pre_split_data, pre_split_bytes);
DN_Memcpy(ring->base, post_split_data, post_split_bytes);
ring->write_pos += src_size;
}
DN_API void DN_RingRead(DN_Ring *ring, void *dest, DN_U64 dest_size)
{
DN_Assert(dest_size <= ring->size);
DN_U64 offset = ring->read_pos % ring->size;
DN_U64 bytes_before_split = ring->size - offset;
DN_U64 pre_split_bytes = DN_Min(bytes_before_split, dest_size);
DN_U64 post_split_bytes = dest_size - pre_split_bytes;
DN_Memcpy(dest, ring->base + offset, pre_split_bytes);
DN_Memcpy((char *)dest + pre_split_bytes, ring->base, post_split_bytes);
ring->read_pos += dest_size;
}
template <typename T>
DN_DSMap<T> DN_DSMapInit(DN_Arena *arena, DN_U32 size, DN_DSMapFlags flags)
{
DN_DSMap<T> result = {};
if (!DN_CheckF(DN_IsPowerOfTwo(size), "Power-of-two size required, given size was '%u'", size))
return result;
if (size <= 0)
return result;
if (!DN_Check(arena))
return result;
result.arena = arena;
result.pool = DN_PoolFromArena(arena, DN_POOL_DEFAULT_ALIGN);
result.hash_to_slot = DN_ArenaNewArray(result.arena, DN_U32, size, DN_ZMem_Yes);
result.slots = DN_ArenaNewArray(result.arena, DN_DSMapSlot<T>, size, DN_ZMem_Yes);
result.occupied = 1; // For sentinel
result.size = size;
result.initial_size = size;
result.flags = flags;
DN_AssertF(result.hash_to_slot && result.slots, "We pre-allocated a block of memory sufficient in size for the 2 arrays. Maybe the pointers needed extra space because of natural alignment?");
return result;
}
template <typename T>
void DN_DSMapDeinit(DN_DSMap<T> *map, DN_ZMem z_mem)
{
if (!map)
return;
// TODO(doyle): Use z_mem
(void)z_mem;
DN_MemListDeinit(map->arena->mem);
*map = {};
}
template <typename T>
bool DN_DSMapIsValid(DN_DSMap<T> const *map)
{
bool result = map &&
map->arena &&
map->hash_to_slot && // Hash to slot mapping array must be allocated
map->slots && // Slots array must be allocated
(map->size & (map->size - 1)) == 0 && // Must be power of two size
map->occupied >= 1; // DN_DS_MAP_SENTINEL_SLOT takes up one slot
return result;
}
template <typename T>
DN_U32 DN_DSMapHash(DN_DSMap<T> const *map, DN_DSMapKey key)
{
DN_U32 result = 0;
if (!map)
return result;
if (key.type == DN_DSMapKeyType_U64NoHash) {
result = DN_Cast(DN_U32) key.u64;
return result;
}
if (key.type == DN_DSMapKeyType_BufferAsU64NoHash) {
result = key.hash;
return result;
}
DN_U32 seed = map->hash_seed ? map->hash_seed : DN_DS_MAP_DEFAULT_HASH_SEED;
if (map->hash_function) {
map->hash_function(key, seed);
} else {
// NOTE: Courtesy of Demetri Spanos (which this hash table was inspired
// from), the following is a hashing function snippet provided for
// reliable, quick and simple quality hashing functions for hash table
// use.
// Source: https://github.com/demetri/scribbles/blob/c475464756c104c91bab83ed4e14badefef12ab5/hashing/ub_aware_hash_functions.c
char const *key_ptr = nullptr;
DN_U32 len = 0;
DN_U32 h = seed;
switch (key.type) {
case DN_DSMapKeyType_BufferAsU64NoHash: /*FALLTHRU*/
case DN_DSMapKeyType_U64NoHash: DN_InvalidCodePath; /*FALLTHRU*/
case DN_DSMapKeyType_Invalid: break;
case DN_DSMapKeyType_Buffer:
key_ptr = DN_Cast(char const *) key.buffer_data;
len = key.buffer_size;
break;
case DN_DSMapKeyType_U64:
key_ptr = DN_Cast(char const *) & key.u64;
len = sizeof(key.u64);
break;
}
// Murmur3 32-bit without UB unaligned accesses
// DN_U32 mur3_32_no_UB(const void *key, int len, DN_U32 h)
// main body, work on 32-bit blocks at a time
for (DN_U32 i = 0; i < len / 4; i++) {
DN_U32 k;
memcpy(&k, &key_ptr[i * 4], sizeof(k));
k *= 0xcc9e2d51;
k = ((k << 15) | (k >> 17)) * 0x1b873593;
h = (((h ^ k) << 13) | ((h ^ k) >> 19)) * 5 + 0xe6546b64;
}
// load/mix up to 3 remaining tail bytes into a tail block
DN_U32 t = 0;
uint8_t *tail = ((uint8_t *)key_ptr) + 4 * (len / 4);
switch (len & 3) {
case 3: t ^= tail[2] << 16;
case 2: t ^= tail[1] << 8;
case 1: {
t ^= tail[0] << 0;
h ^= ((0xcc9e2d51 * t << 15) | (0xcc9e2d51 * t >> 17)) * 0x1b873593;
}
}
// finalization mix, including key length
h = ((h ^ len) ^ ((h ^ len) >> 16)) * 0x85ebca6b;
h = (h ^ (h >> 13)) * 0xc2b2ae35;
result = h ^ (h >> 16);
}
return result;
}
template <typename T>
DN_U32 DN_DSMapHashToSlotIndex(DN_DSMap<T> const *map, DN_DSMapKey key)
{
DN_Assert(key.type != DN_DSMapKeyType_Invalid);
DN_U32 result = DN_DS_MAP_SENTINEL_SLOT;
if (!DN_DSMapIsValid(map))
return result;
result = key.hash & (map->size - 1);
for (;;) {
if (result == DN_DS_MAP_SENTINEL_SLOT) // Sentinel is reserved
result++;
if (map->hash_to_slot[result] == DN_DS_MAP_SENTINEL_SLOT) // Slot is vacant, can use
return result;
DN_DSMapSlot<T> *slot = map->slots + map->hash_to_slot[result];
if (slot->key.type == DN_DSMapKeyType_Invalid || (slot->key.hash == key.hash && slot->key == key))
return result;
result = (result + 1) & (map->size - 1);
}
}
template <typename T>
DN_DSMapResult<T> DN_DSMapFind(DN_DSMap<T> const *map, DN_DSMapKey key)
{
DN_DSMapResult<T> result = {};
if (DN_DSMapIsValid(map)) {
DN_U32 index = DN_DSMapHashToSlotIndex(map, key);
if (index != DN_DS_MAP_SENTINEL_SLOT && map->hash_to_slot[index] == DN_DS_MAP_SENTINEL_SLOT) {
result.slot = map->slots; // NOTE: Set to sentinel value
} else {
result.slot = map->slots + map->hash_to_slot[index];
result.found = true;
}
result.value = &result.slot->value;
}
return result;
}
template <typename T>
DN_DSMapResult<T> DN_DSMapMake(DN_DSMap<T> *map, DN_DSMapKey key)
{
DN_DSMapResult<T> result = {};
if (!DN_DSMapIsValid(map))
return result;
DN_U32 index = DN_DSMapHashToSlotIndex(map, key);
if (map->hash_to_slot[index] == DN_DS_MAP_SENTINEL_SLOT) {
// NOTE: Create the slot
if (index != DN_DS_MAP_SENTINEL_SLOT)
map->hash_to_slot[index] = map->occupied++;
// NOTE: Check if resize is required
bool map_is_75pct_full = (map->occupied * 4) > (map->size * 3);
if (map_is_75pct_full) {
if (!DN_DSMapResize(map, map->size * 2))
return result;
result = DN_DSMapMake(map, key);
} else {
result.slot = map->slots + map->hash_to_slot[index];
result.slot->key = key; // NOTE: Assign key to new slot
if ((key.type == DN_DSMapKeyType_Buffer ||
key.type == DN_DSMapKeyType_BufferAsU64NoHash) &&
!key.no_copy_buffer)
result.slot->key.buffer_data = DN_PoolNewArrayCopy(&map->pool, char, key.buffer_data, key.buffer_size);
}
} else {
result.slot = map->slots + map->hash_to_slot[index];
result.found = true;
}
result.value = &result.slot->value;
DN_Assert(result.slot->key.type != DN_DSMapKeyType_Invalid);
return result;
}
template <typename T>
DN_DSMapResult<T> DN_DSMapSet(DN_DSMap<T> *map, DN_DSMapKey key, T const &value)
{
DN_DSMapResult<T> result = {};
if (!DN_DSMapIsValid(map))
return result;
result = DN_DSMapMake(map, key);
result.slot->value = value;
return result;
}
template <typename T>
DN_DSMapResult<T> DN_DSMapFindKeyU64(DN_DSMap<T> const *map, DN_U64 key)
{
DN_DSMapKey map_key = DN_DSMapKeyU64(map, key);
DN_DSMapResult<T> result = DN_DSMapFind(map, map_key);
return result;
}
template <typename T>
DN_DSMapResult<T> DN_DSMapMakeKeyU64(DN_DSMap<T> *map, DN_U64 key)
{
DN_DSMapKey map_key = DN_DSMapKeyU64(map, key);
DN_DSMapResult<T> result = DN_DSMapMake(map, map_key);
return result;
}
template <typename T>
DN_DSMapResult<T> DN_DSMapSetKeyU64(DN_DSMap<T> *map, DN_U64 key, T const &value)
{
DN_DSMapKey map_key = DN_DSMapKeyU64(map, key);
DN_DSMapResult<T> result = DN_DSMapSet(map, map_key, value);
return result;
}
template <typename T>
DN_DSMapResult<T> DN_DSMapFindKeyStr8(DN_DSMap<T> const *map, DN_Str8 key)
{
DN_DSMapKey map_key = DN_DSMapKeyStr8(map, key);
DN_DSMapResult<T> result = DN_DSMapFind(map, map_key);
return result;
}
template <typename T>
DN_DSMapResult<T> DN_DSMapMakeKeyStr8(DN_DSMap<T> *map, DN_Str8 key)
{
DN_DSMapKey map_key = DN_DSMapKeyStr8(map, key);
DN_DSMapResult<T> result = DN_DSMapMake(map, map_key);
return result;
}
template <typename T>
DN_DSMapResult<T> DN_DSMapSetKeyStr8(DN_DSMap<T> *map, DN_Str8 key, T const &value)
{
DN_DSMapKey map_key = DN_DSMapKeyStr8(map, key);
DN_DSMapResult<T> result = DN_DSMapSet(map, map_key);
return result;
}
template <typename T>
bool DN_DSMapResize(DN_DSMap<T> *map, DN_U32 size)
{
if (!DN_DSMapIsValid(map) || size < map->occupied || size < map->initial_size)
return false;
DN_Arena *prev_arena = map->arena;
DN_MemList *new_mem = prev_arena->mem;
DN_MemList prev_mem = *prev_arena->mem;
prev_arena->mem = &prev_mem;
*new_mem = {};
new_mem->funcs = prev_mem.funcs;
new_mem->flags = prev_mem.flags;
DN_Arena new_arena = {};
new_arena.mem = new_mem;
DN_DSMap<T> new_map = DN_DSMapInit<T>(&new_arena, size, map->flags);
if (!DN_DSMapIsValid(&new_map))
return false;
new_map.initial_size = map->initial_size;
for (DN_U32 old_index = 1 /*Sentinel*/; old_index < map->occupied; old_index++) {
DN_DSMapSlot<T> *old_slot = map->slots + old_index;
DN_DSMapKey old_key = old_slot->key;
if (old_key.type == DN_DSMapKeyType_Invalid)
continue;
DN_DSMapSet(&new_map, old_key, old_slot->value);
}
if ((map->flags & DN_DSMapFlags_DontFreeArenaOnResize) == 0)
DN_DSMapDeinit(map, DN_ZMem_No);
*map = new_map; // Update the map inplace
map->arena = prev_arena; // Restore the previous arena pointer, it's been de-init-ed
*map->arena = new_arena; // Re-init the old arena with the new data
map->pool.arena = map->arena;
return true;
}
template <typename T>
bool DN_DSMapErase(DN_DSMap<T> *map, DN_DSMapKey key)
{
if (!DN_DSMapIsValid(map))
return false;
DN_U32 index = DN_DSMapHashToSlotIndex(map, key);
if (index == 0)
return true;
DN_U32 slot_index = map->hash_to_slot[index];
if (slot_index == DN_DS_MAP_SENTINEL_SLOT)
return false;
// NOTE: Mark the slot as unoccupied
map->hash_to_slot[index] = DN_DS_MAP_SENTINEL_SLOT;
DN_DSMapSlot<T> *slot = map->slots + slot_index;
if (!slot->key.no_copy_buffer)
DN_PoolDealloc(&map->pool, DN_Cast(void *) slot->key.buffer_data);
*slot = {}; // TODO: Optional?
if (map->occupied > 1 /*Sentinel*/) {
// NOTE: Repair the hash chain, e.g. rehash all the items after the removed
// element and reposition them if necessary.
for (DN_U32 probe_index = index;;) {
probe_index = (probe_index + 1) & (map->size - 1);
if (map->hash_to_slot[probe_index] == DN_DS_MAP_SENTINEL_SLOT)
break;
DN_DSMapSlot<T> *probe = map->slots + map->hash_to_slot[probe_index];
DN_U32 new_index = probe->key.hash & (map->size - 1);
if (index <= probe_index) {
if (index < new_index && new_index <= probe_index)
continue;
} else {
if (index < new_index || new_index <= probe_index)
continue;
}
map->hash_to_slot[index] = map->hash_to_slot[probe_index];
map->hash_to_slot[probe_index] = DN_DS_MAP_SENTINEL_SLOT;
index = probe_index;
}
// NOTE: We have erased a slot from the hash table, this leaves a gap
// in our contiguous array. After repairing the chain, the hash mapping
// is correct.
// We will now fill in the vacant spot that we erased using the last
// element in the slot list.
if (map->occupied >= 3 /*Ignoring sentinel, at least 2 other elements to unstable erase*/) {
DN_U32 last_index = map->occupied - 1;
if (last_index != slot_index) {
// NOTE: Copy in last slot to the erase slot
DN_DSMapSlot<T> *last_slot = map->slots + last_index;
map->slots[slot_index] = *last_slot;
// NOTE: Update the hash-to-slot mapping for the value that was copied in
DN_U32 hash_to_slot_index = DN_DSMapHashToSlotIndex(map, last_slot->key);
map->hash_to_slot[hash_to_slot_index] = slot_index;
*last_slot = {}; // TODO: Optional?
}
}
}
map->occupied--;
bool map_is_below_25pct_full = (map->occupied * 4) < (map->size * 1);
if (map_is_below_25pct_full && (map->size / 2) >= map->initial_size)
DN_DSMapResize(map, map->size / 2);
return true;
}
template <typename T>
bool DN_DSMapEraseKeyU64(DN_DSMap<T> *map, DN_U64 key)
{
DN_DSMapKey map_key = DN_DSMapKeyU64(map, key);
bool result = DN_DSMapErase(map, map_key);
return result;
}
template <typename T>
bool DN_DSMapEraseKeyStr8(DN_DSMap<T> *map, DN_Str8 key)
{
DN_DSMapKey map_key = DN_DSMapKeyStr8(map, key);
bool result = DN_DSMapErase(map, map_key);
return result;
}
template <typename T>
DN_DSMapKey DN_DSMapKeyBuffer(DN_DSMap<T> const *map, void const *data, DN_USize size)
{
DN_Assert(size > 0 && size <= UINT32_MAX);
DN_DSMapKey result = {};
result.type = DN_DSMapKeyType_Buffer;
result.buffer_data = data;
result.buffer_size = DN_Cast(DN_U32) size;
result.hash = DN_DSMapHash(map, result);
return result;
}
template <typename T>
DN_DSMapKey DN_DSMapKeyBufferAsU64NoHash(DN_DSMap<T> const *map, void const *data, DN_USize size)
{
DN_DSMapKey result = {};
result.type = DN_DSMapKeyType_BufferAsU64NoHash;
result.buffer_data = data;
result.buffer_size = DN_Cast(DN_U32) size;
DN_Assert(size >= sizeof(result.hash));
DN_Memcpy(&result.hash, data, sizeof(result.hash));
return result;
}
template <typename T>
DN_DSMapKey DN_DSMapKeyU64(DN_DSMap<T> const *map, DN_U64 u64)
{
DN_DSMapKey result = {};
result.type = DN_DSMapKeyType_U64;
result.u64 = u64;
result.hash = DN_DSMapHash(map, result);
return result;
}
template <typename T>
DN_DSMapKey DN_DSMapKeyStr8(DN_DSMap<T> const *map, DN_Str8 string)
{
DN_DSMapKey result = DN_DSMapKeyBuffer(map, string.data, string.size);
return result;
}
// NOTE: DN_DSMap
DN_API DN_DSMapKey DN_DSMapKeyU64NoHash(DN_U64 u64)
{
DN_DSMapKey result = {};
result.type = DN_DSMapKeyType_U64NoHash;
result.u64 = u64;
result.hash = DN_Cast(DN_U32) u64;
return result;
}
DN_API bool DN_DSMapKeyEquals(DN_DSMapKey lhs, DN_DSMapKey rhs)
{
bool result = false;
if (lhs.type == rhs.type && lhs.hash == rhs.hash) {
switch (lhs.type) {
case DN_DSMapKeyType_Invalid: result = true; break;
case DN_DSMapKeyType_U64NoHash: result = true; break;
case DN_DSMapKeyType_U64: result = lhs.u64 == rhs.u64; break;
case DN_DSMapKeyType_BufferAsU64NoHash: /*FALLTHRU*/
case DN_DSMapKeyType_Buffer: {
if (lhs.buffer_size == rhs.buffer_size)
result = DN_Memcmp(lhs.buffer_data, rhs.buffer_data, lhs.buffer_size) == 0;
} break;
}
}
return result;
}
DN_API bool operator==(DN_DSMapKey lhs, DN_DSMapKey rhs)
{
bool result = DN_DSMapKeyEquals(lhs, rhs);
return result;
}
-135
View File
@@ -1,135 +0,0 @@
#define DN_BASE_LEAK_CPP
#if defined(_CLANGD)
#include "../dn.h"
#endif
DN_API void DN_LeakTrackAlloc_(DN_LeakTracker *leak, void *ptr, DN_USize size, bool leak_permitted)
{
if (!ptr)
return;
DN_TicketMutex_Begin(&leak->alloc_table_mutex);
DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/);
DN_DSMap<DN_LeakAlloc> *alloc_table = &leak->alloc_table;
DN_DSMapResult<DN_LeakAlloc> alloc_entry = DN_DSMapMakeKeyU64(alloc_table, DN_Cast(DN_U64) ptr);
DN_LeakAlloc *alloc = alloc_entry.value;
if (alloc_entry.found) {
if ((alloc->flags & DN_LeakAllocFlag_Freed) == 0) {
DN_Str8x32 alloc_size = DN_Str8x32FromByteCountU64Auto(alloc->size);
DN_Str8x32 new_alloc_size = DN_Str8x32FromByteCountU64Auto(size);
DN_HardAssertF(
alloc->flags & DN_LeakAllocFlag_Freed,
"This pointer is already in the leak tracker, however it has not been freed yet. This "
"same pointer is being ask to be tracked twice in the allocation table, e.g. one if its "
"previous free calls has not being marked freed with an equivalent call to "
"DN_LeakTrackDealloc()\n"
"\n"
"The pointer (0x%p) originally allocated %.*s at:\n"
"\n"
"%.*s\n"
"\n"
"The pointer is allocating %.*s again at:\n"
"\n"
"%.*s\n",
ptr,
DN_Str8PrintFmt(alloc_size),
DN_Str8PrintFmt(alloc->stack_trace),
DN_Str8PrintFmt(new_alloc_size),
DN_Str8PrintFmt(stack_trace));
}
// NOTE: Pointer was reused, clean up the prior entry
leak->alloc_table_bytes_allocated_for_stack_traces -= alloc->stack_trace.size;
leak->alloc_table_bytes_allocated_for_stack_traces -= alloc->freed_stack_trace.size;
DN_OS_MemDealloc(alloc->stack_trace.data);
DN_OS_MemDealloc(alloc->freed_stack_trace.data);
*alloc = {};
}
alloc->ptr = ptr;
alloc->size = size;
alloc->stack_trace = stack_trace;
alloc->flags |= leak_permitted ? DN_LeakAllocFlag_LeakPermitted : 0;
leak->alloc_table_bytes_allocated_for_stack_traces += alloc->stack_trace.size;
DN_TicketMutex_End(&leak->alloc_table_mutex);
}
DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr)
{
if (!ptr)
return;
DN_TicketMutex_Begin(&leak->alloc_table_mutex);
DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/);
DN_DSMap<DN_LeakAlloc> *alloc_table = &leak->alloc_table;
DN_DSMapResult<DN_LeakAlloc> alloc_entry = DN_DSMapFindKeyU64(alloc_table, DN_Cast(uintptr_t) ptr);
DN_HardAssertF(alloc_entry.found,
"Allocated pointer can not be removed as it does not exist in the "
"allocation table. When this memory was allocated, the pointer was "
"not added to the allocation table [ptr=%p]",
ptr);
DN_LeakAlloc *alloc = alloc_entry.value;
if (alloc->flags & DN_LeakAllocFlag_Freed) {
DN_Str8x32 freed_size = DN_Str8x32FromByteCountU64Auto(alloc->freed_size);
DN_HardAssertF((alloc->flags & DN_LeakAllocFlag_Freed) == 0,
"Double free detected, pointer to free was already marked "
"as freed. Either the pointer was reallocated but not "
"traced, or, the pointer was freed twice.\n"
"\n"
"The pointer (0x%p) originally allocated %.*s at:\n"
"\n"
"%.*s\n"
"\n"
"The pointer was freed at:\n"
"\n"
"%.*s\n"
"\n"
"The pointer is being freed again at:\n"
"\n"
"%.*s\n",
ptr,
DN_Str8PrintFmt(freed_size),
DN_Str8PrintFmt(alloc->stack_trace),
DN_Str8PrintFmt(alloc->freed_stack_trace),
DN_Str8PrintFmt(stack_trace));
}
DN_Assert(alloc->freed_stack_trace.size == 0);
alloc->flags |= DN_LeakAllocFlag_Freed;
alloc->freed_stack_trace = stack_trace;
leak->alloc_table_bytes_allocated_for_stack_traces += alloc->freed_stack_trace.size;
DN_TicketMutex_End(&leak->alloc_table_mutex);
}
DN_API void DN_LeakDump_(DN_LeakTracker *leak)
{
DN_U64 leak_count = 0;
DN_U64 leaked_bytes = 0;
for (DN_USize index = 1; index < leak->alloc_table.occupied; index++) {
DN_DSMapSlot<DN_LeakAlloc> *slot = leak->alloc_table.slots + index;
DN_LeakAlloc *alloc = &slot->value;
bool alloc_leaked = (alloc->flags & DN_LeakAllocFlag_Freed) == 0;
bool leak_permitted = (alloc->flags & DN_LeakAllocFlag_LeakPermitted);
if (alloc_leaked && !leak_permitted) {
leaked_bytes += alloc->size;
leak_count++;
DN_Str8x32 alloc_size = DN_Str8x32FromByteCountU64Auto(alloc->size);
DN_LogWarningF(
"Pointer (0x%p) leaked %.*s at:\n"
"%.*s",
alloc->ptr,
DN_Str8PrintFmt(alloc_size),
DN_Str8PrintFmt(alloc->stack_trace));
}
}
if (leak_count) {
DN_Str8x32 leak_size = DN_Str8x32FromByteCountU64Auto(leaked_bytes);
DN_LogWarningF("There were %I64u leaked allocations totalling %.*s", leak_count, DN_Str8PrintFmt(leak_size));
}
}
-50
View File
@@ -1,50 +0,0 @@
#if !defined(DN_BASE_LEAK_H)
#define DN_BASE_LEAK_H
#if defined(_CLANGD)
#include "../dn.h"
#endif
enum DN_LeakAllocFlag
{
DN_LeakAllocFlag_Freed = 1 << 0,
DN_LeakAllocFlag_LeakPermitted = 1 << 1,
};
struct DN_LeakAlloc
{
void *ptr; // 8 Pointer to the allocation being tracked
DN_USize size; // 16 Size of the allocation
DN_USize freed_size; // 24 Store the size of the allocation when it is freed
DN_Str8 stack_trace; // 40 Stack trace at the point of allocation
DN_Str8 freed_stack_trace; // 56 Stack trace of where the allocation was freed
DN_U16 flags; // 72 Bit flags from `DN_LeakAllocFlag`
};
// NOTE: We aim to keep the allocation record as light as possible as memory tracking can get
// expensive. Enforce that there is no unexpected padding.
DN_StaticAssert(sizeof(DN_LeakAlloc) == 64 || sizeof(DN_LeakAlloc) == 32); // NOTE: 64 bit vs 32 bit pointers respectively
struct DN_LeakTracker
{
DN_DSMap<DN_LeakAlloc> alloc_table;
DN_TicketMutex alloc_table_mutex;
DN_MemList alloc_table_mem;
DN_Arena alloc_table_arena;
DN_U64 alloc_table_bytes_allocated_for_stack_traces;
};
DN_API void DN_LeakTrackAlloc_ (DN_LeakTracker *leak, void *ptr, DN_USize size, bool alloc_can_leak);
DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr);
DN_API void DN_LeakDump_ (DN_LeakTracker *leak);
#if defined(DN_LEAK_TRACKING)
#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) DN_LeakTrackAlloc_(leak, ptr, size, alloc_can_leak)
#define DN_LeakTrackDealloc(leak, ptr) DN_LeakTrackDealloc_(leak, ptr)
#define DN_LeakDump(leak) DN_LeakDump_(leak)
#else
#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0)
#define DN_LeakTrackDealloc(leak, ptr) do { (void)ptr; } while (0)
#define DN_LeakDump(leak) do { } while (0)
#endif
#endif // DN_BASE_LEAK_H
-4450
View File
File diff suppressed because it is too large Load Diff
-1248
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -614,7 +614,7 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback,
{
DN_Str8 str8 = va_arg(va, DN_Str8);
s = (char *)str8.data;
l = (uint32_t)str8.size;
l = (uint32_t)str8.count;
lead[0] = 0;
tail[0] = 0;
pr = 0;
-124
View File
@@ -1,124 +0,0 @@
#define DN_ASYNC_CPP
#if defined(_CLANGD)
#define DN_H_WITH_OS 1
#include "../dn.h"
#include "dn_async.h"
#endif
static DN_I32 DN_ASYNC_ThreadEntryPoint_(DN_OSThread *thread)
{
DN_OS_ThreadSetNameFmt("%.*s", DN_Str8PrintFmt(thread->name));
DN_ASYNCCore *async = DN_Cast(DN_ASYNCCore *) thread->user_context;
DN_Ring *ring = &async->ring;
for (;;) {
DN_OS_SemaphoreWait(&async->worker_sem, UINT32_MAX);
if (async->join_threads)
break;
DN_ASYNCTask task = {};
for (DN_OS_MutexScope(&async->ring_mutex)) {
if (DN_RingHasData(ring, sizeof(task)))
DN_RingRead(ring, &task, sizeof(task));
}
if (task.work.func) {
DN_OS_ConditionVariableBroadcast(&async->ring_write_cv); // Resume any blocked ring write(s)
DN_ASYNCWorkArgs args = {};
args.input = task.work.input;
args.thread = thread;
DN_AtomicAddU32(&async->busy_threads, 1);
task.work.func(args);
DN_AtomicSubU32(&async->busy_threads, 1);
if (task.completion_sem.handle != 0)
DN_OS_SemaphoreIncrement(&task.completion_sem, 1);
}
}
return 0;
}
DN_API void DN_ASYNC_Init(DN_ASYNCCore *async, char *base, DN_USize base_size, DN_OSThread *threads, DN_U32 threads_size)
{
DN_Assert(async);
async->ring.size = base_size;
async->ring.base = base;
async->ring_mutex = DN_OS_MutexInit();
async->ring_write_cv = DN_OS_ConditionVariableInit();
async->worker_sem = DN_OS_SemaphoreInit(0);
async->thread_count = threads_size;
async->threads = threads;
for (DN_ForIndexU(index, async->thread_count)) {
DN_OSThread *thread = async->threads + index;
DN_OS_ThreadInit(thread, DN_ASYNC_ThreadEntryPoint_, /*lane=*/ nullptr, DN_TCInitArgsDefault(), async);
}
}
DN_API void DN_ASYNC_Deinit(DN_ASYNCCore *async)
{
DN_Assert(async);
DN_AtomicSetValue32(&async->join_threads, true);
DN_OS_SemaphoreIncrement(&async->worker_sem, async->thread_count);
for (DN_ForItSize(it, DN_OSThread, async->threads, async->thread_count))
DN_OS_ThreadJoin(it.data, DN_TCDeinitArenas_Yes);
}
static bool DN_ASYNC_QueueTask_(DN_ASYNCCore *async, DN_ASYNCTask const *task, DN_U64 wait_time_ms) {
DN_U64 end_time_ms = DN_OS_DateUnixTimeMs() + wait_time_ms;
bool result = false;
for (DN_OS_MutexScope(&async->ring_mutex)) {
for (;;) {
if (DN_RingHasSpace(&async->ring, sizeof(*task))) {
DN_RingWriteStruct(&async->ring, task);
result = true;
break;
}
DN_OS_ConditionVariableWaitUntil(&async->ring_write_cv, &async->ring_mutex, end_time_ms);
if (DN_OS_DateUnixTimeMs() >= end_time_ms)
break;
}
}
if (result)
DN_OS_SemaphoreIncrement(&async->worker_sem, 1); // Flag that a job is available
return result;
}
DN_API bool DN_ASYNC_QueueWork(DN_ASYNCCore *async, DN_ASYNCWorkFunc *func, void *input, DN_U64 wait_time_ms)
{
DN_ASYNCTask task = {};
task.work.func = func;
task.work.input = input;
bool result = DN_ASYNC_QueueTask_(async, &task, wait_time_ms);
return result;
}
DN_API DN_ASYNCTask DN_ASYNC_QueueTask(DN_ASYNCCore *async, DN_ASYNCWorkFunc *func, void *input, DN_U64 wait_time_ms)
{
DN_ASYNCTask result = {};
result.work.func = func;
result.work.input = input;
result.completion_sem = DN_OS_SemaphoreInit(0);
result.queued = DN_ASYNC_QueueTask_(async, &result, wait_time_ms);
if (!result.queued)
DN_OS_SemaphoreDeinit(&result.completion_sem);
return result;
}
DN_API bool DN_ASYNC_WaitTask(DN_ASYNCTask *task, DN_U32 timeout_ms)
{
bool result = true;
if (!task->queued)
return result;
DN_OSSemaphoreWaitResult wait = DN_OS_SemaphoreWait(&task->completion_sem, timeout_ms);
result = wait == DN_OSSemaphoreWaitResult_Success;
if (result)
DN_OS_SemaphoreDeinit(&task->completion_sem);
return result;
}
-56
View File
@@ -1,56 +0,0 @@
#if !defined(DN_ASYNC_H)
#define DN_ASYNC_H
#if defined(_CLANGD)
#define DN_H_WITH_OS 1
#include "../dn.h"
#endif
enum DN_ASYNCPriority
{
DN_ASYNCPriority_Low,
DN_ASYNCPriority_High,
DN_ASYNCPriority_Count,
};
struct DN_ASYNCCore
{
DN_OSMutex ring_mutex;
DN_OSConditionVariable ring_write_cv;
DN_OSSemaphore worker_sem;
DN_Ring ring;
DN_OSThread *threads;
DN_U32 thread_count;
DN_U32 busy_threads;
DN_U32 join_threads;
};
struct DN_ASYNCWorkArgs
{
DN_OSThread *thread;
void *input;
};
typedef void(DN_ASYNCWorkFunc)(DN_ASYNCWorkArgs work_args);
struct DN_ASYNCWork
{
DN_ASYNCWorkFunc *func;
void *input;
void *output;
};
struct DN_ASYNCTask
{
bool queued;
DN_ASYNCWork work;
DN_OSSemaphore completion_sem;
};
DN_API void DN_ASYNC_Init (DN_ASYNCCore *async, char *base, DN_USize base_size, DN_OSThread *threads, DN_U32 threads_size);
DN_API void DN_ASYNC_Deinit (DN_ASYNCCore *async);
DN_API bool DN_ASYNC_QueueWork(DN_ASYNCCore *async, DN_ASYNCWorkFunc *func, void *input, DN_U64 wait_time_ms);
DN_API DN_ASYNCTask DN_ASYNC_QueueTask(DN_ASYNCCore *async, DN_ASYNCWorkFunc *func, void *input, DN_U64 wait_time_ms);
DN_API void DN_ASYNC_WaitTask (DN_OSSemaphore *sem, DN_U32 timeout_ms);
#endif // DN_ASYNC_H
-206
View File
@@ -1,206 +0,0 @@
#define DN_BIN_PACK_CPP
#if defined(_CLANGD)
#include "dn_bin_pack.h"
#endif
DN_API void DN_BinPackU64(DN_BinPack *pack, DN_BinPackMode mode, DN_U64 *item)
{
DN_U64 const VALUE_MASK = 0b0111'1111;
DN_U8 const CONTINUE_BIT = 0b1000'0000;
if (mode == DN_BinPackMode_Serialise) {
DN_U64 it = *item;
do {
DN_U8 write_value = DN_Cast(DN_U8)(it & VALUE_MASK);
it >>= 7;
if (it)
write_value |= CONTINUE_BIT;
DN_Str8BuilderAppendBytesCopy(&pack->writer, &write_value, sizeof(write_value));
} while (it);
} else {
*item = 0;
DN_USize bits_read = 0;
for (DN_U8 src = CONTINUE_BIT; (src & CONTINUE_BIT) && bits_read < 64; bits_read += 7) {
src = pack->read.data[pack->read_index++];
DN_U8 masked_src = src & VALUE_MASK;
*item |= (DN_Cast(DN_U64) masked_src << bits_read);
}
}
}
DN_API void DN_BinPackVarInt_(DN_BinPack *pack, DN_BinPackMode mode, void *item, DN_USize size)
{
DN_U64 value = 0;
DN_AssertF(size <= sizeof(value),
"An item larger than 64 bits (%zu) is trying to be packed as a variable integer which is not supported",
size * 8);
if (mode == DN_BinPackMode_Serialise) // Read `item` into U64 `value`
DN_Memcpy(&value, item, size);
DN_BinPackU64(pack, mode, &value);
if (mode == DN_BinPackMode_Deserialise) // Write U64 `value` into `item`
DN_Memcpy(item, &value, size);
}
DN_API bool DN_BinPackIsEndOfReadStream(DN_BinPack const *pack)
{
bool result = pack->read_index == pack->read.size;
return result;
}
DN_API void DN_BinPackUSize(DN_BinPack *pack, DN_BinPackMode mode, DN_USize *item)
{
DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}
DN_API void DN_BinPackU32(DN_BinPack *pack, DN_BinPackMode mode, DN_U32 *item)
{
DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}
DN_API void DN_BinPackU16(DN_BinPack *pack, DN_BinPackMode mode, DN_U16 *item)
{
DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}
DN_API void DN_BinPackU8(DN_BinPack *pack, DN_BinPackMode mode, DN_U8 *item)
{
DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}
DN_API void DN_BinPackI64(DN_BinPack *pack, DN_BinPackMode mode, DN_I64 *item)
{
DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}
DN_API void DN_BinPackI32(DN_BinPack *pack, DN_BinPackMode mode, DN_I32 *item)
{
DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}
DN_API void DN_BinPackI16(DN_BinPack *pack, DN_BinPackMode mode, DN_I16 *item)
{
DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}
DN_API void DN_BinPackI8(DN_BinPack *pack, DN_BinPackMode mode, DN_I8 *item)
{
DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}
DN_API void DN_BinPackF64(DN_BinPack *pack, DN_BinPackMode mode, DN_F64 *item)
{
DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}
DN_API void DN_BinPackF32(DN_BinPack *pack, DN_BinPackMode mode, DN_F32 *item)
{
DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}
#if defined(DN_MATH_H)
DN_API void DN_BinPackV2(DN_BinPack *pack, DN_BinPackMode mode, DN_V2F32 *item)
{
DN_BinPackF32(pack, mode, &item->x);
DN_BinPackF32(pack, mode, &item->y);
}
DN_API void DN_BinPackV4(DN_BinPack *pack, DN_BinPackMode mode, DN_V4F32 *item)
{
DN_BinPackF32(pack, mode, &item->x);
DN_BinPackF32(pack, mode, &item->y);
DN_BinPackF32(pack, mode, &item->z);
DN_BinPackF32(pack, mode, &item->w);
}
#endif
DN_API void DN_BinPackBool(DN_BinPack *pack, DN_BinPackMode mode, bool *item)
{
DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}
DN_API void DN_BinPackStr8FromArena(DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, DN_Str8 *string)
{
DN_BinPackVarInt_(pack, mode, &string->size, sizeof(string->size));
if (mode == DN_BinPackMode_Serialise) {
DN_Str8BuilderAppendBytesCopy(&pack->writer, string->data, string->size);
} else {
DN_Str8 src = DN_Str8Subset(pack->read, pack->read_index, string->size);
*string = DN_Str8FromStr8Arena(src, arena);
pack->read_index += src.size;
}
}
DN_API void DN_BinPackStr8FromPool(DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, DN_Str8 *string)
{
DN_BinPackVarInt_(pack, mode, &string->size, sizeof(string->size));
if (mode == DN_BinPackMode_Serialise) {
DN_Str8BuilderAppendBytesCopy(&pack->writer, string->data, string->size);
} else {
DN_Str8 src = DN_Str8Subset(pack->read, pack->read_index, string->size);
*string = DN_Str8FromStr8Pool(src, pool);
pack->read_index += src.size;
}
}
DN_API DN_Str8 DN_BinPackStr8FromBuffer(DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max)
{
DN_BinPackCBuffer(pack, mode, ptr, size, max);
DN_Str8 result = DN_Str8FromPtr(ptr, *size);
return result;
}
DN_API void DN_BinPackBytesFromArena(DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, void **ptr, DN_USize *size)
{
DN_Str8 string = DN_Str8FromPtr(*ptr, *size);
DN_BinPackStr8FromArena(pack, arena, mode, &string);
*ptr = string.data;
*size = string.size;
}
DN_API void DN_BinPackBytesFromPool(DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, void **ptr, DN_USize *size)
{
DN_Str8 string = DN_Str8FromPtr(*ptr, *size);
DN_BinPackStr8FromPool(pack, pool, mode, &string);
*ptr = string.data;
*size = string.size;
}
DN_API void DN_BinPackCArray(DN_BinPack *pack, DN_BinPackMode mode, void *ptr, DN_USize size)
{
DN_BinPackVarInt_(pack, mode, &size, sizeof(size));
if (mode == DN_BinPackMode_Serialise) {
DN_Str8BuilderAppendBytesCopy(&pack->writer, ptr, size);
} else {
DN_Str8 src = DN_Str8Subset(pack->read, pack->read_index, size);
DN_Assert(src.size == size);
DN_Memcpy(ptr, src.data, DN_Min(src.size, size));
pack->read_index += src.size;
}
}
DN_API void DN_BinPackCBuffer(DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max)
{
if (mode == DN_BinPackMode_Serialise) {
DN_BinPackUSize(pack, mode, size);
DN_Str8BuilderAppendBytesCopy(&pack->writer, ptr, *size);
} else {
DN_U64 size_u64 = 0;
DN_BinPackU64(pack, mode, &size_u64);
DN_Assert(size_u64 < DN_USIZE_MAX);
DN_Assert(size_u64 <= max);
*size = DN_Min(size_u64, max);
DN_Memcpy(ptr, pack->read.data + pack->read_index, *size);
pack->read_index += size_u64;
}
}
DN_API DN_Str8 DN_BinPackBuild(DN_BinPack const *pack, DN_Arena *arena)
{
DN_Str8 result = DN_Str8BuilderBuild(&pack->writer, arena);
return result;
}
-47
View File
@@ -1,47 +0,0 @@
#if !defined(DN_BIN_PACK_H)
#define DN_BIN_PACK_H
#if defined(_CLANGD)
#include "../dn.h"
#endif
enum DN_BinPackMode
{
DN_BinPackMode_Serialise,
DN_BinPackMode_Deserialise,
};
struct DN_BinPack
{
DN_Str8Builder writer;
DN_Str8 read;
DN_USize read_index;
};
DN_API bool DN_BinPackIsEndOfReadStream(DN_BinPack const *pack);
DN_API void DN_BinPackUSize (DN_BinPack *pack, DN_BinPackMode mode, DN_USize *item);
DN_API void DN_BinPackU64 (DN_BinPack *pack, DN_BinPackMode mode, DN_U64 *item);
DN_API void DN_BinPackU32 (DN_BinPack *pack, DN_BinPackMode mode, DN_U32 *item);
DN_API void DN_BinPackU16 (DN_BinPack *pack, DN_BinPackMode mode, DN_U16 *item);
DN_API void DN_BinPackU8 (DN_BinPack *pack, DN_BinPackMode mode, DN_U8 *item);
DN_API void DN_BinPackI64 (DN_BinPack *pack, DN_BinPackMode mode, DN_I64 *item);
DN_API void DN_BinPackI32 (DN_BinPack *pack, DN_BinPackMode mode, DN_I32 *item);
DN_API void DN_BinPackI16 (DN_BinPack *pack, DN_BinPackMode mode, DN_I16 *item);
DN_API void DN_BinPackI8 (DN_BinPack *pack, DN_BinPackMode mode, DN_I8 *item);
DN_API void DN_BinPackF64 (DN_BinPack *pack, DN_BinPackMode mode, DN_F64 *item);
DN_API void DN_BinPackF32 (DN_BinPack *pack, DN_BinPackMode mode, DN_F32 *item);
#if defined (DN_MATH_H)
DN_API void DN_BinPackV2 (DN_BinPack *pack, DN_BinPackMode mode, DN_V2F32 *item);
DN_API void DN_BinPackV4 (DN_BinPack *pack, DN_BinPackMode mode, DN_V4F32 *item);
#endif
DN_API void DN_BinPackBool (DN_BinPack *pack, DN_BinPackMode mode, bool *item);
DN_API void DN_BinPackStr8FromArena (DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, DN_Str8 *string);
DN_API void DN_BinPackStr8FromPool (DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, DN_Str8 *string);
DN_API DN_Str8 DN_BinPackStr8FromBuffer (DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max);
DN_API void DN_BinPackBytesFromArena (DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, void **ptr, DN_USize *size);
DN_API void DN_BinPackBytesFromPool (DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, void **ptr, DN_USize *size);
DN_API void DN_BinPackCArray (DN_BinPack *pack, DN_BinPackMode mode, void *ptr, DN_USize size);
DN_API void DN_BinPackCBuffer (DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max);
DN_API DN_Str8 DN_BinPackBuild (DN_BinPack const *pack, DN_Arena *arena);
#endif // !defined(DN_BIN_PACK_H)
File diff suppressed because it is too large Load Diff
-185
View File
@@ -1,185 +0,0 @@
#if !defined(DN_CGEN_H)
#define DN_CGEN_H
#if defined(_CLANGD)
#define DN_H_WITH_OS 1
#include "../dn.h"
#include "../Standalone/dn_cpp_file.h"
#endif
#if !defined(DN_NO_METADESK)
#if !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#define DN_UNDO_CRT_SECURE_NO_WARNINGS
#endif
// NOTE: Metadesk does not have the header for 'size_t'
#if defined(DN_COMPILER_GCC)
#include <stdint.h>
#endif
#define MD_DEFAULT_SPRINTF 0
#define MD_IMPL_Vsnprintf DN_VSNPrintF
#include "../External/metadesk/md.h"
#if defined(DN_UNDO_CRT_SECURE_NO_WARNINGS)
#undef _CRT_SECURE_NO_WARNINGS
#endif
#endif
#if !defined(DN_CPP_FILE_H)
#error dn_cpp_file.h must be included before this
#endif
#if defined(DN_PLATFORM_WINDOWS) && !defined(DN_NO_WINDOWS_H_REPLACEMENT_HEADER)
#error DN <Windows.h> replacement header must be disabled with DN_NO_WINDOWS_H_REPLACEMENT_HEADER since Metadesk includes <Windows.h>
#endif
#if !defined(MD_H)
#error Metadesk 'md.h' must be included before 'dn_cgen.h'
#endif
enum DN_CGenTableKeyType
{
DN_CGenTableKeyType_Nil,
DN_CGenTableKeyType_Name,
DN_CGenTableKeyType_Type,
};
enum DN_CGenTableType
{
DN_CGenTableType_Nil,
DN_CGenTableType_Data,
DN_CGenTableType_CodeGenBuiltinTypes,
DN_CGenTableType_CodeGenStruct,
DN_CGenTableType_CodeGenEnum,
DN_CGenTableType_Count,
};
enum DN_CGenTableRowTagType
{
DN_CGenTableRowTagType_Nil,
DN_CGenTableRowTagType_CommentDivider,
DN_CGenTableRowTagType_EmptyLine,
};
enum DN_CGenTableRowTagCommentDivider
{
DN_CGenTableRowTagCommentDivider_Nil,
DN_CGenTableRowTagCommentDivider_Label,
};
enum DN_CGenTableHeaderType
{
DN_CGenTableHeaderType_Name,
DN_CGenTableHeaderType_Table,
DN_CGenTableHeaderType_CppType,
DN_CGenTableHeaderType_CppName,
DN_CGenTableHeaderType_CppValue,
DN_CGenTableHeaderType_CppIsPtr,
DN_CGenTableHeaderType_CppOpEquals,
DN_CGenTableHeaderType_CppArraySize,
DN_CGenTableHeaderType_CppArraySizeField,
DN_CGenTableHeaderType_CppLabel,
DN_CGenTableHeaderType_GenTypeInfo,
DN_CGenTableHeaderType_GenEnumCount,
DN_CGenTableHeaderType_Count,
};
struct DN_CGenTableHeader
{
MD_String8 name;
int longest_string;
};
struct DN_CGenTableRowTag
{
DN_CGenTableRowTagType type;
MD_String8 comment;
DN_CGenTableRowTag *next;
};
struct DN_CGenTableColumn
{
MD_Node *node;
DN_Str8 string;
};
struct DN_CGenTableRow
{
DN_CGenTableRowTag *first_tag;
DN_CGenTableRowTag *last_tag;
DN_CGenTableColumn *columns;
};
struct DN_CGenTable
{
DN_CGenTableType type;
DN_Str8 name;
MD_Map headers_map;
DN_CGenTableHeader *headers;
DN_CGenTableRow *rows;
size_t column_count;
size_t row_count;
MD_Node *node;
MD_Node *headers_node;
DN_USize column_indexes[DN_CGenTableHeaderType_Count];
DN_CGenTable *next;
};
struct DN_CGen
{
MD_Arena *arena;
MD_Node *file_list;
MD_Map table_map;
DN_CGenTable *first_table;
DN_CGenTable *last_table;
DN_USize table_counts[DN_CGenTableType_Count];
};
struct DN_CGenMapNodeToEnum
{
uint32_t enum_val;
DN_Str8 node_string;
};
struct DN_CGenLookupTableIterator
{
DN_CGenTable *cgen_table;
DN_CGenTableRow *cgen_table_row;
DN_CGenTableColumn cgen_table_column[DN_CGenTableHeaderType_Count];
DN_CGenTable *table;
DN_USize row_index;
};
struct DN_CGenLookupColumnAtHeader
{
DN_USize index;
DN_CGenTableHeader header;
DN_CGenTableColumn column;
};
enum DN_CGenEmit
{
DN_CGenEmit_Prototypes = 1 << 0,
DN_CGenEmit_Implementation = 1 << 1,
};
#define DN_CGen_MDToDNStr8(str8) DN_Str8FromPtr((str8).str, (str8).size)
#define DN_CGen_DNToMDStr8(str8) \
{ \
DN_Cast(MD_u8 *) \
(str8).data, \
(str8).size \
}
DN_API DN_CGen DN_CGen_InitFilesArgV(int argc, char const **argv, DN_ErrSink *err);
DN_API DN_Str8 DN_CGen_TableHeaderTypeToDeclStr8(DN_CGenTableHeaderType type);
DN_API DN_CGenMapNodeToEnum DN_CGen_MapNodeToEnumOrExit(MD_Node const *node, DN_CGenMapNodeToEnum const *valid_keys, DN_USize valid_keys_size, char const *fmt, ...);
DN_API DN_USize DN_CGen_NodeChildrenCount(MD_Node const *node);
DN_API void DN_CGen_LogF(MD_MessageKind kind, MD_Node *node, DN_ErrSink *err, char const *fmt, ...);
DN_API bool DN_CGen_TableHasHeaders(DN_CGenTable const *table, DN_Str8 const *headers, DN_USize header_count, DN_ErrSink *err);
DN_API DN_CGenLookupColumnAtHeader DN_CGen_LookupColumnAtHeader(DN_CGenTable *table, DN_Str8 header, DN_CGenTableRow const *row);
DN_API bool DN_CGen_LookupNextTableInCodeGenTable(DN_CGen *cgen, DN_CGenTable *cgen_table, DN_CGenLookupTableIterator *it);
DN_API void DN_CGen_EmitCodeForTables(DN_CGen *cgen, DN_CGenEmit emit, DN_CppFile *cpp, DN_Str8 emit_prefix);
#endif // DN_CGEN_H
-290
View File
@@ -1,290 +0,0 @@
#define DN_CSV_CPP
#include "dn_csv.h"
DN_CSVTokeniser DN_CSV_TokeniserInit(DN_Str8 string, char delimiter)
{
DN_CSVTokeniser result = {};
result.string = string;
result.delimiter = delimiter;
return result;
}
bool DN_CSV_TokeniserValid(DN_CSVTokeniser *tokeniser)
{
bool result = tokeniser && !tokeniser->bad;
return result;
}
static void DN_CSV_TokeniserEatNewLines_(DN_CSVTokeniser *tokeniser)
{
char const *end = tokeniser->string.data + tokeniser->string.size;
while (tokeniser->it[0] == '\n' || tokeniser->it[0] == '\r')
if (++tokeniser->it == end)
break;
}
bool DN_CSV_TokeniserNextRow(DN_CSVTokeniser *tokeniser)
{
bool result = false;
if (DN_CSV_TokeniserValid(tokeniser) && tokeniser->string.size) {
// NOTE: First time querying row iterator is nil, let tokeniser advance
if (tokeniser->it) {
// NOTE: Only advance the tokeniser if we're at the end of the line and
// there's more to tokenise.
char const *end = tokeniser->string.data + tokeniser->string.size;
if (tokeniser->it != end && tokeniser->end_of_line) {
tokeniser->end_of_line = false;
result = true;
}
}
}
return result;
}
DN_Str8 DN_CSV_TokeniserNextField(DN_CSVTokeniser *tokeniser)
{
DN_Str8 result = {};
if (!DN_CSV_TokeniserValid(tokeniser))
return result;
if (tokeniser->string.size == 0) {
tokeniser->bad = true;
return result;
}
// NOTE: First time tokeniser is invoked with a string, set up initial state.
char const *string_end = tokeniser->string.data + tokeniser->string.size;
if (!tokeniser->it) {
tokeniser->it = tokeniser->string.data;
DN_CSV_TokeniserEatNewLines_(tokeniser); // NOTE: Skip any leading new lines
}
// NOTE: Tokeniser pointing at end, no more valid data to parse.
if (tokeniser->it == string_end)
return result;
// NOTE: Scan forward until the next control character.
// 1. '"' Double quoted field, extract everything between the quotes.
// 2. tokeniser->delimiter End of the field, extract everything leading up to the delimiter.
// 3. '\n' Last field in record, extract everything leading up the the new line.
char const *begin = tokeniser->it;
while (tokeniser->it != string_end && (tokeniser->it[0] != '"' &&
tokeniser->it[0] != tokeniser->delimiter &&
tokeniser->it[0] != '\n'))
tokeniser->it++;
bool quoted_field = (tokeniser->it != string_end) && tokeniser->it[0] == '"';
if (quoted_field) {
begin = ++tokeniser->it; // Begin after the quote
// NOTE: Scan forward until the next '"' which marks the end
// of the field unless it is escaped by another '"'.
find_next_quote:
while (tokeniser->it != string_end && tokeniser->it[0] != '"')
tokeniser->it++;
// NOTE: If we encounter a '"' right after, the quotes were escaped
// and we need to skip to the next instance of a '"'.
if (tokeniser->it != string_end && tokeniser->it + 1 != string_end && tokeniser->it[1] == '"') {
tokeniser->it += 2;
goto find_next_quote;
}
}
// NOTE: Mark the end of the field
char const *end = tokeniser->it;
tokeniser->end_of_line = tokeniser->it == string_end || end[0] == '\n';
// NOTE: In files with \r\n style new lines ensure that we don't include
// the \r byte in the CSV field we produce.
if (end != string_end && end[0] == '\n') {
DN_Assert((uintptr_t)(end - 1) > (uintptr_t)tokeniser->string.data &&
"Internal error: The string iterator is pointing behind the start of the string we're reading");
if (end[-1] == '\r')
end = end - 1;
}
// NOTE: Quoted fields may have whitespace after the closing quote, we skip
// until we reach the field terminator.
if (quoted_field)
while (tokeniser->it != string_end && (tokeniser->it[0] != tokeniser->delimiter && tokeniser->it[0] != '\n'))
tokeniser->it++;
// NOTE: Advance the tokeniser past the field terminator.
if (tokeniser->it != string_end)
tokeniser->it++;
// NOTE: Generate the record
result.data = DN_Cast(char *) begin;
result.size = DN_Cast(int)(end - begin);
return result;
}
DN_Str8 DN_CSV_TokeniserNextColumn(DN_CSVTokeniser *tokeniser)
{
DN_Str8 result = {};
if (!DN_CSV_TokeniserValid(tokeniser))
return result;
// NOTE: End of line, the user must explicitly advance to the next row
if (tokeniser->end_of_line)
return result;
// NOTE: Advance tokeniser to the next field in the row
result = DN_CSV_TokeniserNextField(tokeniser);
return result;
}
void DN_CSV_TokeniserSkipLine(DN_CSVTokeniser *tokeniser)
{
while (DN_CSV_TokeniserValid(tokeniser) && !tokeniser->end_of_line)
DN_CSV_TokeniserNextColumn(tokeniser);
DN_CSV_TokeniserNextRow(tokeniser);
}
int DN_CSV_TokeniserNextN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size, bool column_iterator)
{
if (!DN_CSV_TokeniserValid(tokeniser) || !fields || fields_size <= 0)
return 0;
int result = 0;
for (; result < fields_size; result++) {
fields[result] = column_iterator ? DN_CSV_TokeniserNextColumn(tokeniser) : DN_CSV_TokeniserNextField(tokeniser);
if (!DN_CSV_TokeniserValid(tokeniser) || !fields[result].data)
break;
}
return result;
}
int DN_CSV_TokeniserNextColumnN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size)
{
int result = DN_CSV_TokeniserNextN(tokeniser, fields, fields_size, true /*column_iterator*/);
return result;
}
int DN_CSV_TokeniserNextFieldN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size)
{
int result = DN_CSV_TokeniserNextN(tokeniser, fields, fields_size, false /*column_iterator*/);
return result;
}
void DN_CSV_TokeniserSkipLineN(DN_CSVTokeniser *tokeniser, int count)
{
for (int i = 0; i < count && DN_CSV_TokeniserValid(tokeniser); i++)
DN_CSV_TokeniserSkipLine(tokeniser);
}
void DN_CSV_PackU64(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U64 *value)
{
if (serialise == DN_CSVSerialise_Read) {
DN_Str8 csv_value = DN_CSV_TokeniserNextColumn(&pack->read_tokeniser);
DN_U64FromResult to_u64 = DN_U64FromStr8(csv_value, 0);
DN_Assert(to_u64.success);
*value = to_u64.value;
} else {
DN_Str8BuilderAppendF(&pack->write_builder, "%s%" PRIu64, pack->write_column++ ? "," : "", *value);
}
}
void DN_CSV_PackI64(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I64 *value)
{
if (serialise == DN_CSVSerialise_Read) {
DN_Str8 csv_value = DN_CSV_TokeniserNextColumn(&pack->read_tokeniser);
DN_I64FromResult to_i64 = DN_I64FromStr8(csv_value, 0);
DN_Assert(to_i64.success);
*value = to_i64.value;
} else {
DN_Str8BuilderAppendF(&pack->write_builder, "%s%" PRIu64, pack->write_column++ ? "," : "", *value);
}
}
void DN_CSV_PackI32(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I32 *value)
{
DN_I64 u64 = *value;
DN_CSV_PackI64(pack, serialise, &u64);
if (serialise == DN_CSVSerialise_Read)
*value = DN_SaturateCastI64ToI32(u64);
}
void DN_CSV_PackI16(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I16 *value)
{
DN_I64 u64 = *value;
DN_CSV_PackI64(pack, serialise, &u64);
if (serialise == DN_CSVSerialise_Read)
*value = DN_SaturateCastI64ToI16(u64);
}
void DN_CSV_PackI8(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I8 *value)
{
DN_I64 u64 = *value;
DN_CSV_PackI64(pack, serialise, &u64);
if (serialise == DN_CSVSerialise_Read)
*value = DN_SaturateCastI64ToI8(u64);
}
void DN_CSV_PackU32(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U32 *value)
{
DN_U64 u64 = *value;
DN_CSV_PackU64(pack, serialise, &u64);
if (serialise == DN_CSVSerialise_Read)
*value = DN_SaturateCastU64ToU32(u64);
}
void DN_CSV_PackU16(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U16 *value)
{
DN_U64 u64 = *value;
DN_CSV_PackU64(pack, serialise, &u64);
if (serialise == DN_CSVSerialise_Read)
*value = DN_SaturateCastU64ToU16(u64);
}
void DN_CSV_PackBoolAsU64(DN_CSVPack *pack, DN_CSVSerialise serialise, bool *value)
{
DN_U64 u64 = *value;
DN_CSV_PackU64(pack, serialise, &u64);
if (serialise == DN_CSVSerialise_Read)
*value = u64 ? 1 : 0;
}
void DN_CSV_PackStr8(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_Str8 *str8, DN_Arena *arena)
{
if (serialise == DN_CSVSerialise_Read) {
DN_Str8 csv_value = DN_CSV_TokeniserNextColumn(&pack->read_tokeniser);
*str8 = DN_Str8FromStr8Arena(csv_value, arena);
} else {
DN_Str8BuilderAppendF(&pack->write_builder, "%s%.*s", pack->write_column++ ? "," : "", DN_Str8PrintFmt(*str8));
}
}
void DN_CSV_PackBuffer(DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size)
{
if (serialise == DN_CSVSerialise_Read) {
DN_Str8 csv_value = DN_CSV_TokeniserNextColumn(&pack->read_tokeniser);
*size = DN_Min(*size, csv_value.size);
DN_Memcpy(dest, csv_value.data, *size);
} else {
DN_Str8BuilderAppendF(&pack->write_builder, "%s%.*s", pack->write_column++ ? "," : "", DN_Cast(int)(*size), dest);
}
}
void DN_CSV_PackBufferWithMax(DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size, size_t max)
{
if (serialise == DN_CSVSerialise_Read)
*size = max;
DN_CSV_PackBuffer(pack, serialise, dest, size);
}
bool DN_CSV_PackNewLine(DN_CSVPack *pack, DN_CSVSerialise serialise)
{
bool result = true;
if (serialise == DN_CSVSerialise_Read) {
result = DN_CSV_TokeniserNextRow(&pack->read_tokeniser);
} else {
pack->write_column = 0;
result = DN_Str8BuilderAppendRef(&pack->write_builder, DN_Str8Lit("\n"));
}
return result;
}
-62
View File
@@ -1,62 +0,0 @@
#if !defined(DN_CSV_H)
#define DN_CSV_H
// NOTE: Data structures to create and parse CSV files, supports Python style escaped quotes (e.g.
// Using "" to escape quotes inside a quoted string).
//
// API
// DN_CSV_TokeniserNextN: Reads the next N consecutive fields from the parser. If `column_iterator`
// is `false` then the read of the N consecutive fields does not proceed past the end of the
// current CSV row. If `true` then it reads the next N fields even if reading would progress onto
// the next row.
#if defined(_CLANGD)
#include "../dn.h"
#endif
enum DN_CSVSerialise
{
DN_CSVSerialise_Read,
DN_CSVSerialise_Write,
};
struct DN_CSVTokeniser
{
bool bad;
DN_Str8 string;
char delimiter;
char const *it;
bool end_of_line;
};
struct DN_CSVPack
{
DN_Str8Builder write_builder;
DN_USize write_column;
DN_CSVTokeniser read_tokeniser;
};
DN_CSVTokeniser DN_CSV_TokeniserInit (DN_Str8 string, char delimiter);
bool DN_CSV_TokeniserValid (DN_CSVTokeniser *tokeniser);
bool DN_CSV_TokeniserNextRow (DN_CSVTokeniser *tokeniser);
DN_Str8 DN_CSV_TokeniserNextField (DN_CSVTokeniser *tokeniser);
DN_Str8 DN_CSV_TokeniserNextColumn (DN_CSVTokeniser *tokeniser);
void DN_CSV_TokeniserSkipLine (DN_CSVTokeniser *tokeniser);
int DN_CSV_TokeniserNextN (DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size, bool column_iterator);
int DN_CSV_TokeniserNextColumnN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size);
int DN_CSV_TokeniserNextFieldN (DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size);
void DN_CSV_TokeniserSkipLineN (DN_CSVTokeniser *tokeniser, int count);
void DN_CSV_PackU64 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U64 *value);
void DN_CSV_PackI64 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I64 *value);
void DN_CSV_PackI32 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I32 *value);
void DN_CSV_PackI16 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I16 *value);
void DN_CSV_PackI8 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I8 *value);
void DN_CSV_PackU32 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U32 *value);
void DN_CSV_PackU16 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U16 *value);
void DN_CSV_PackBoolAsU64 (DN_CSVPack *pack, DN_CSVSerialise serialise, bool *value);
void DN_CSV_PackStr8 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_Str8 *str8, DN_Arena *arena);
void DN_CSV_PackBuffer (DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size);
void DN_CSV_PackBufferWithMax (DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size, size_t max);
bool DN_CSV_PackNewLine (DN_CSVPack *pack, DN_CSVSerialise serialise);
#endif // !defined(DN_CSV_H)
File diff suppressed because it is too large Load Diff
-153
View File
@@ -1,153 +0,0 @@
#define DN_HELPERS_CPP
#if defined(_CLANGD)
#include "dn_helpers.h"
#endif
DN_API DN_JSONBuilder DN_JSONBuilder_Init(DN_Arena *arena, int spaces_per_indent)
{
DN_JSONBuilder result = {};
result.spaces_per_indent = spaces_per_indent;
result.string_builder.arena = arena;
return result;
}
DN_API DN_Str8 DN_JSONBuilder_Build(DN_JSONBuilder const *builder, DN_Arena *arena)
{
DN_Str8 result = DN_Str8FromStr8BuilderArena(&builder->string_builder, arena);
return result;
}
DN_API void DN_JSONBuilder_KeyValue(DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value)
{
if (key.size == 0 && value.size == 0)
return;
DN_JSONBuilderItem item = DN_JSONBuilderItem_KeyValue;
if (value.size >= 1) {
if (value.data[0] == '{' || value.data[0] == '[')
item = DN_JSONBuilderItem_OpenContainer;
else if (value.data[0] == '}' || value.data[0] == ']')
item = DN_JSONBuilderItem_CloseContainer;
}
bool adding_to_container_with_items =
item != DN_JSONBuilderItem_CloseContainer && (builder->last_item == DN_JSONBuilderItem_KeyValue ||
builder->last_item == DN_JSONBuilderItem_CloseContainer);
uint8_t prefix_size = 0;
char prefix[2] = {0};
if (adding_to_container_with_items)
prefix[prefix_size++] = ',';
if (builder->last_item != DN_JSONBuilderItem_Empty)
prefix[prefix_size++] = '\n';
if (item == DN_JSONBuilderItem_CloseContainer)
builder->indent_level--;
int spaces_per_indent = builder->spaces_per_indent ? builder->spaces_per_indent : 2;
int spaces = builder->indent_level * spaces_per_indent;
if (key.size)
DN_Str8BuilderAppendF(&builder->string_builder,
"%.*s%*c\"%.*s\": %.*s",
prefix_size,
prefix,
spaces,
' ',
DN_Str8PrintFmt(key),
DN_Str8PrintFmt(value));
else if (spaces == 0)
DN_Str8BuilderAppendF(&builder->string_builder, "%.*s%.*s", prefix_size, prefix, DN_Str8PrintFmt(value));
else
DN_Str8BuilderAppendF(&builder->string_builder, "%.*s%*c%.*s", prefix_size, prefix, spaces, ' ', DN_Str8PrintFmt(value));
if (item == DN_JSONBuilderItem_OpenContainer)
builder->indent_level++;
builder->last_item = item;
}
DN_API void DN_JSONBuilder_KeyValueFV(DN_JSONBuilder *builder, DN_Str8 key, char const *value_fmt, va_list args)
{
DN_TCScratch scratch = DN_TCScratchBeginArena(&builder->string_builder.arena, 1);
DN_Str8 value = DN_Str8FromFmtVArena(&scratch.arena, value_fmt, args);
DN_JSONBuilder_KeyValue(builder, key, value);
DN_TCScratchEnd(&scratch);
}
DN_API void DN_JSONBuilder_KeyValueF(DN_JSONBuilder *builder, DN_Str8 key, char const *value_fmt, ...)
{
va_list args;
va_start(args, value_fmt);
DN_JSONBuilder_KeyValueFV(builder, key, value_fmt, args);
va_end(args);
}
DN_API void DN_JSONBuilder_ObjectBeginNamed(DN_JSONBuilder *builder, DN_Str8 name)
{
DN_JSONBuilder_KeyValue(builder, name, DN_Str8Lit("{"));
}
DN_API void DN_JSONBuilder_ObjectEnd(DN_JSONBuilder *builder)
{
DN_JSONBuilder_KeyValue(builder, DN_Str8Lit(""), DN_Str8Lit("}"));
}
DN_API void DN_JSONBuilder_ArrayBeginNamed(DN_JSONBuilder *builder, DN_Str8 name)
{
DN_JSONBuilder_KeyValue(builder, name, DN_Str8Lit("["));
}
DN_API void DN_JSONBuilder_ArrayEnd(DN_JSONBuilder *builder)
{
DN_JSONBuilder_KeyValue(builder, DN_Str8Lit(""), DN_Str8Lit("]"));
}
DN_API void DN_JSONBuilder_Str8Named(DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value)
{
DN_JSONBuilder_KeyValueF(builder, key, "\"%.*s\"", value.size, value.data);
}
DN_API void DN_JSONBuilder_LiteralNamed(DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value)
{
DN_JSONBuilder_KeyValueF(builder, key, "%.*s", value.size, value.data);
}
DN_API void DN_JSONBuilder_U64Named(DN_JSONBuilder *builder, DN_Str8 key, uint64_t value)
{
DN_JSONBuilder_KeyValueF(builder, key, "%I64u", value);
}
DN_API void DN_JSONBuilder_I64Named(DN_JSONBuilder *builder, DN_Str8 key, int64_t value)
{
DN_JSONBuilder_KeyValueF(builder, key, "%I64d", value);
}
DN_API void DN_JSONBuilder_F64Named(DN_JSONBuilder *builder, DN_Str8 key, double value, int decimal_places)
{
if (!builder)
return;
if (decimal_places >= 16)
decimal_places = 16;
// NOTE: Generate the format string for the float, depending on how many
// decimals places it wants.
char float_fmt[16];
if (decimal_places > 0) {
// NOTE: Emit the format string "%.<decimal_places>f" i.e. %.1f
DN_SNPrintF(float_fmt, sizeof(float_fmt), "%%.%df", decimal_places);
} else {
// NOTE: Emit the format string "%f"
DN_SNPrintF(float_fmt, sizeof(float_fmt), "%%f");
}
DN_JSONBuilder_KeyValueF(builder, key, float_fmt, value);
}
DN_API void DN_JSONBuilder_BoolNamed(DN_JSONBuilder *builder, DN_Str8 key, bool value)
{
DN_Str8 value_string = value ? DN_Str8Lit("true") : DN_Str8Lit("false");
DN_JSONBuilder_KeyValueF(builder, key, "%.*s", value_string.size, value_string.data);
}
-185
View File
@@ -1,185 +0,0 @@
#if !defined(DN_HELPERS_H)
#define DN_HELPERS_H
#if defined(_CLANGD)
#include "../dn.h"
#endif
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// $$\ $$\ $$$$$$$$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$$\ $$$$$$\
// $$ | $$ |$$ _____|$$ | $$ __$$\ $$ _____|$$ __$$\ $$ __$$\
// $$ | $$ |$$ | $$ | $$ | $$ |$$ | $$ | $$ |$$ / \__|
// $$$$$$$$ |$$$$$\ $$ | $$$$$$$ |$$$$$\ $$$$$$$ |\$$$$$$\
// $$ __$$ |$$ __| $$ | $$ ____/ $$ __| $$ __$$< \____$$\
// $$ | $$ |$$ | $$ | $$ | $$ | $$ | $$ |$$\ $$ |
// $$ | $$ |$$$$$$$$\ $$$$$$$$\ $$ | $$$$$$$$\ $$ | $$ |\$$$$$$ |
// \__| \__|\________|\________|\__| \________|\__| \__| \______/
//
// dn_helpers.h -- Helper functions/data structures
//
////////////////////////////////////////////////////////////////////////////////////////////////////
*/
#if !defined(DN_NO_JSON_BUILDER)
enum DN_JSONBuilderItem
{
DN_JSONBuilderItem_Empty,
DN_JSONBuilderItem_OpenContainer,
DN_JSONBuilderItem_CloseContainer,
DN_JSONBuilderItem_KeyValue,
};
struct DN_JSONBuilder
{
bool use_stdout; // When set, ignore the string builder and dump immediately to stdout
DN_Str8Builder string_builder; // (Internal)
int indent_level; // (Internal)
int spaces_per_indent; // The number of spaces per indent level
DN_JSONBuilderItem last_item;
};
#endif // !defined(DN_NO_JSON_BUIDLER)
template <typename T>
using DN_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs);
template <typename T>
bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
enum DN_BinarySearchType
{
// Index of the match. If no match is found, found is set to false and the
// index is set to the index where the match should be inserted/exist, if
// it were in the array
DN_BinarySearchType_Match,
// Index of the first element in the array that is `element >= find`. If no such
// item is found or the array is empty, then, the index is set to the array
// size and found is set to `false`.
//
// For example:
// int array[] = {0, 1, 2, 3, 4, 5};
// DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_LowerBound);
// printf("%zu\n", result.index); // Prints index '4'
DN_BinarySearchType_LowerBound,
// Index of the first element in the array that is `element > find`. If no such
// item is found or the array is empty, then, the index is set to the array
// size and found is set to `false`.
//
// For example:
// int array[] = {0, 1, 2, 3, 4, 5};
// DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_UpperBound);
// printf("%zu\n", result.index); // Prints index '5'
DN_BinarySearchType_UpperBound,
};
struct DN_BinarySearchResult
{
bool found;
DN_USize index;
};
#if !defined(DN_NO_JSON_BUILDER)
// NOTE: DN_JSONBuilder
#define DN_JSONBuilder_Object(builder) \
DN_DeferLoop(DN_JSONBuilder_ObjectBegin(builder), \
DN_JSONBuilder_ObjectEnd(builder))
#define DN_JSONBuilder_ObjectNamed(builder, name) \
DN_DeferLoop(DN_JSONBuilder_ObjectBeginNamed(builder, name), \
DN_JSONBuilder_ObjectEnd(builder))
#define DN_JSONBuilder_Array(builder) \
DN_DeferLoop(DN_JSONBuilder_ArrayBegin(builder), \
DN_JSONBuilder_ArrayEnd(builder))
#define DN_JSONBuilder_ArrayNamed(builder, name) \
DN_DeferLoop(DN_JSONBuilder_ArrayBeginNamed(builder, name), \
DN_JSONBuilder_ArrayEnd(builder))
DN_API DN_JSONBuilder DN_JSONBuilder_Init (DN_Arena *arena, int spaces_per_indent);
DN_API DN_Str8 DN_JSONBuilder_Build (DN_JSONBuilder const *builder, DN_Arena *arena);
DN_API void DN_JSONBuilder_KeyValue (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value);
DN_API void DN_JSONBuilder_KeyValueF (DN_JSONBuilder *builder, DN_Str8 key, char const *value_fmt, ...);
DN_API void DN_JSONBuilder_ObjectBeginNamed (DN_JSONBuilder *builder, DN_Str8 name);
DN_API void DN_JSONBuilder_ObjectEnd (DN_JSONBuilder *builder);
DN_API void DN_JSONBuilder_ArrayBeginNamed (DN_JSONBuilder *builder, DN_Str8 name);
DN_API void DN_JSONBuilder_ArrayEnd (DN_JSONBuilder *builder);
DN_API void DN_JSONBuilder_Str8Named (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value);
DN_API void DN_JSONBuilder_LiteralNamed (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value);
DN_API void DN_JSONBuilder_U64Named (DN_JSONBuilder *builder, DN_Str8 key, uint64_t value);
DN_API void DN_JSONBuilder_I64Named (DN_JSONBuilder *builder, DN_Str8 key, int64_t value);
DN_API void DN_JSONBuilder_F64Named (DN_JSONBuilder *builder, DN_Str8 key, double value, int decimal_places);
DN_API void DN_JSONBuilder_BoolNamed (DN_JSONBuilder *builder, DN_Str8 key, bool value);
#define DN_JSONBuilder_ObjectBegin(builder) DN_JSONBuilder_ObjectBeginNamed(builder, DN_Str8Lit(""))
#define DN_JSONBuilder_ArrayBegin(builder) DN_JSONBuilder_ArrayBeginNamed(builder, DN_Str8Lit(""))
#define DN_JSONBuilder_Str8(builder, value) DN_JSONBuilder_Str8Named(builder, DN_Str8Lit(""), value)
#define DN_JSONBuilder_Literal(builder, value) DN_JSONBuilder_LiteralNamed(builder, DN_Str8Lit(""), value)
#define DN_JSONBuilder_U64(builder, value) DN_JSONBuilder_U64Named(builder, DN_Str8Lit(""), value)
#define DN_JSONBuilder_I64(builder, value) DN_JSONBuilder_I64Named(builder, DN_Str8Lit(""), value)
#define DN_JSONBuilder_F64(builder, value) DN_JSONBuilder_F64Named(builder, DN_Str8Lit(""), value)
#define DN_JSONBuilder_Bool(builder, value) DN_JSONBuilder_BoolNamed(builder, DN_Str8Lit(""), value)
#endif // !defined(DN_NO_JSON_BUILDER)
// NOTE: DN_BinarySearch
template <typename T> bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
template <typename T> DN_BinarySearchResult DN_BinarySearch (T const *array, DN_USize array_size, T const &find, DN_BinarySearchType type = DN_BinarySearchType_Match, DN_BinarySearchLessThanProc<T> less_than = DN_BinarySearch_DefaultLessThan);
// NOTE: DN_BinarySearch
template <typename T>
bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs)
{
bool result = lhs < rhs;
return result;
}
template <typename T>
DN_BinarySearchResult DN_BinarySearch(T const *array,
DN_USize array_size,
T const &find,
DN_BinarySearchType type,
DN_BinarySearchLessThanProc<T> less_than)
{
DN_BinarySearchResult result = {};
if (!array || array_size <= 0 || !less_than)
return result;
T const *end = array + array_size;
T const *first = array;
T const *last = end;
while (first != last) {
DN_USize count = last - first;
T const *it = first + (count / 2);
bool advance_first = false;
if (type == DN_BinarySearchType_UpperBound)
advance_first = !less_than(find, it[0]);
else
advance_first = less_than(it[0], find);
if (advance_first)
first = it + 1;
else
last = it;
}
switch (type) {
case DN_BinarySearchType_Match: {
result.found = first != end && !less_than(find, *first);
} break;
case DN_BinarySearchType_LowerBound: /*FALLTHRU*/
case DN_BinarySearchType_UpperBound: {
result.found = first != end;
} break;
}
result.index = first - array;
return result;
}
#endif // !defined(DN_HELPERS_H)
-428
View File
@@ -1,428 +0,0 @@
#define DN_JSON_CPP
// NOTE: DN_JSON //////////////////////////////////////////////////////////////////////////////////
void *DN_JSON_ArenaAllocFunc(void *user_data, size_t count)
{
void *result = NULL;
if (!user_data)
return result;
DN_Arena *arena = DN_Cast(DN_Arena*)user_data;
result = DN_ArenaAlloc(arena, count, alignof(json_value_s), DN_ZMem_No);
return result;
}
char const *DN_JSON_TypeEnumCString(json_type_e type, size_t *size)
{
switch (type) {
case json_type_string: { if (size) { *size = sizeof("string") - 1; } return "string"; }
case json_type_number: { if (size) { *size = sizeof("number") - 1; } return "number"; }
case json_type_object: { if (size) { *size = sizeof("object") - 1; } return "object"; }
case json_type_array: { if (size) { *size = sizeof("array") - 1; } return "array"; }
case json_type_true: { if (size) { *size = sizeof("true (boolean)") - 1; } return "true (boolean)"; }
case json_type_false: { if (size) { *size = sizeof("false (boolean)") - 1; } return "false (boolean)"; }
default: /*FALLTHRU*/
case json_type_null: { if (size) { *size = sizeof("(null)") - 1; } return "(null)"; }
}
}
bool DN_JSON_String8Cmp(json_string_s const *lhs, DN_Str8 key)
{
bool result = false;
if (lhs && key.size) {
DN_Str8 lhs_string = DN_Str8FromPtr(lhs->string, lhs->string_size);
result = DN_Str8Eq(lhs_string, key);
}
return result;
}
// NOTE: DN_JSON_It ///////////////////////////////////////////////////////////////////////////////
DN_JSONIt DN_JSON_LoadFileToIt(DN_Arena *arena, DN_Str8 json)
{
json_parse_result_s parse_result = {};
json_value_ex_s *ex_value =
DN_Cast(json_value_ex_s *) json_parse_ex(json.data,
json.size,
json_parse_flags_allow_location_information,
DN_JSON_ArenaAllocFunc,
arena,
&parse_result);
DN_JSONIt result = {};
DN_JSON_ItPushValue(&result, &ex_value->value);
return result;
}
// NOTE: DN_JSON_ItPush/Pop ///////////////////////////////////////////////////////////////////////
bool DN_JSON_ItPushObjElement(DN_JSONIt *it, json_object_element_s *element)
{
if (!it || !element)
return false;
DN_Assert(it->stack_count < DN_ArrayCountI(it->stack));
it->stack[it->stack_count++] = {DN_JSON_ItEntryTypeObjElement, element};
return true;
}
bool DN_JSON_ItPushObj(DN_JSONIt *it, json_object_s *obj)
{
if (!it || !obj)
return false;
DN_Assert(it->stack_count < DN_ArrayCountI(it->stack));
it->stack[it->stack_count++] = {DN_JSON_ItEntryTypeObj, obj};
return true;
}
bool DN_JSON_ItPushArrayElement(DN_JSONIt *it, json_array_element_s *element)
{
if (!it || !element)
return false;
DN_Assert(it->stack_count < DN_ArrayCountI(it->stack));
it->stack[it->stack_count++] = {DN_JSON_ItEntryTypeArrayElement, element};
return true;
}
bool DN_JSON_ItPushArray(DN_JSONIt *it, json_value_s *value)
{
if (!it || !value || json_value_as_array(value) == nullptr)
return false;
DN_Assert(it->stack_count < DN_ArrayCountI(it->stack));
it->stack[it->stack_count++] = {DN_JSON_ItEntryTypeArray, value};
return true;
}
bool DN_JSON_ItPushValue(DN_JSONIt *it, json_value_s *value)
{
bool result = false;
if (!it || !value)
return result;
if (value->type == json_type_object) {
result = DN_JSON_ItPushObj(it, json_value_as_object(value));
} else if (value->type == json_type_array) {
result = DN_JSON_ItPushArray(it, value);
}
return result;
}
void DN_JSON_ItPop(DN_JSONIt *it)
{
if (!it)
return;
DN_Assert(it->stack_count > 0);
if (it->stack_count > 0)
it->stack_count--;
}
// NOTE: DN_JSON_It JSON tree navigation //////////////////////////////////////////////////////////
json_value_s *DN_JSON_ItPushCurrValue(DN_JSONIt *it)
{
json_value_s *result = nullptr;
DN_JSONItEntry *curr = DN_JSON_ItCurr(it);
if (!curr)
return result;
if (curr->type == DN_JSON_ItEntryTypeObjElement) {
json_object_element_s *element = DN_Cast(json_object_element_s *) curr->value;
result = element->value;
} else if (curr->type == DN_JSON_ItEntryTypeArrayElement) {
json_array_element_s *element = DN_Cast(json_array_element_s *) curr->value;
result = element->value;
} else {
result = DN_Cast(json_value_s *) curr->value;
}
if (result->type == json_type_array) {
json_array_s *array = json_value_as_array(result);
DN_Assert(array);
DN_JSON_ItPushArray(it, result);
} else if (result->type == json_type_object) {
json_object_s *obj = json_value_as_object(result);
DN_Assert(obj);
DN_JSON_ItPushObj(it, obj);
}
return result;
}
bool DN_JSON_ItNext(DN_JSONIt *it)
{
DN_JSONItEntry *curr = DN_JSON_ItCurr(it);
if (!curr)
return false;
json_object_element_s *obj_element = nullptr;
json_array_element_s *array_element = nullptr;
if (curr->type == DN_JSON_ItEntryTypeObj) {
auto *obj = DN_Cast(json_object_s *) curr->value;
obj_element = obj->start;
} else if (curr->type == DN_JSON_ItEntryTypeObjElement) {
auto *element = DN_Cast(json_object_element_s *) curr->value;
obj_element = element->next;
DN_JSON_ItPop(it);
} else if (curr->type == DN_JSON_ItEntryTypeArray) {
auto *value = DN_Cast(json_value_s *) curr->value;
auto *array = json_value_as_array(value);
array_element = array->start;
} else if (curr->type == DN_JSON_ItEntryTypeArrayElement) {
auto *element = DN_Cast(json_array_element_s *) curr->value;
array_element = element->next;
DN_JSON_ItPop(it);
} else {
DN_JSON_ItPop(it);
}
if (obj_element)
DN_JSON_ItPushObjElement(it, obj_element);
else if (array_element)
DN_JSON_ItPushArrayElement(it, array_element);
bool result = obj_element || array_element;
return result;
}
// NOTE: DN_JSON_ItCurr ///////////////////////////////////////////////////////////////////////////
DN_JSONItEntry *DN_JSON_ItCurr(DN_JSONIt *it)
{
DN_JSONItEntry *result = nullptr;
if (!it || it->stack_count <= 0)
return result;
result = &it->stack[it->stack_count - 1];
return result;
}
json_value_s *DN_JSON_ItCurrValue(DN_JSONIt *it)
{
json_value_s *result = nullptr;
DN_JSONItEntry *curr = DN_JSON_ItCurr(it);
if (!curr)
return result;
if (curr->type == DN_JSON_ItEntryTypeObjElement) {
auto *element = DN_Cast(json_object_element_s *)curr->value;
result = element->value;
} else if (curr->type == DN_JSON_ItEntryTypeArrayElement) {
auto *element = DN_Cast(json_array_element_s *)curr->value;
result = element->value;
} else if (curr->type == DN_JSON_ItEntryTypeString ||
curr->type == DN_JSON_ItEntryTypeNumber ||
curr->type == DN_JSON_ItEntryTypeObj ||
curr->type == DN_JSON_ItEntryTypeArray)
{
result = DN_Cast(json_value_s *)curr->value;
}
return result;
}
json_object_element_s *DN_JSON_ItCurrObjElement(DN_JSONIt *it)
{
DN_JSONItEntry *curr = DN_JSON_ItCurr(it);
auto *result = (curr && curr->type == DN_JSON_ItEntryTypeObjElement)
? DN_Cast(json_object_element_s *) curr->value
: nullptr;
return result;
}
// NOTE: DN_JSON_ItValueIs ////////////////////////////////////////////////////////////////////////
json_value_s *DN_JSON_ItValueIs(DN_JSONIt *it, json_type_e type)
{
json_value_s *curr = DN_JSON_ItCurrValue(it);
json_value_s *result = (curr && type == curr->type) ? curr : nullptr;
return result;
}
json_object_s *DN_JSON_ItValueIsObj(DN_JSONIt *it)
{
json_value_s *curr = DN_JSON_ItCurrValue(it);
json_object_s *result = curr ? json_value_as_object(curr) : nullptr;
return result;
}
json_array_s *DN_JSON_ItValueIsArray(DN_JSONIt *it)
{
json_value_s *curr = DN_JSON_ItCurrValue(it);
json_array_s *result = curr ? json_value_as_array(curr) : nullptr;
return result;
}
json_string_s *DN_JSON_ItValueIsString(DN_JSONIt *it)
{
json_value_s *curr = DN_JSON_ItCurrValue(it);
json_string_s *result = curr ? json_value_as_string(curr) : nullptr;
return result;
}
json_number_s *DN_JSON_ItValueIsNumber(DN_JSONIt *it)
{
json_value_s *curr = DN_JSON_ItCurrValue(it);
json_number_s *result = curr ? json_value_as_number(curr) : nullptr;
return result;
}
json_value_s *DN_JSON_ItValueIsBool(DN_JSONIt *it)
{
json_value_s *curr = DN_JSON_ItCurrValue(it);
json_value_s *result = (curr && (curr->type == json_type_true || curr->type == json_type_false)) ? curr : nullptr;
return result;
}
json_value_s *DN_JSON_ItValueIsNull(DN_JSONIt *it)
{
json_value_s *curr = DN_JSON_ItCurrValue(it);
json_value_s *result = (curr && (curr->type == json_type_null)) ? curr : nullptr;
return result;
}
size_t DN_JSON_ItValueArraySize(DN_JSONIt *it)
{
size_t result = 0;
if (json_array_s *curr = DN_JSON_ItValueIsArray(it))
result = curr->length;
return result;
}
// NOTE: DN_JSON_ItKeyValueIs /////////////////////////////////////////////////////////////////////
DN_Str8 DN_JSON_ItKey(DN_JSONIt *it)
{
json_object_element_s *curr = DN_JSON_ItCurrObjElement(it);
DN_Str8 result = {};
if (curr) {
result.data = DN_Cast(char *)curr->name->string;
result.size = curr->name->string_size;
}
return result;
}
bool DN_JSON_ItKeyIs(DN_JSONIt *it, DN_Str8 key)
{
json_object_element_s *curr = DN_JSON_ItCurrObjElement(it);
bool result = DN_JSON_String8Cmp(curr->name, key);
return result;
}
json_object_s *DN_JSON_ItKeyValueIsObj(DN_JSONIt *it, DN_Str8 key)
{
json_object_s *result = nullptr;
json_object_element_s *curr = DN_JSON_ItCurrObjElement(it);
if (curr && DN_JSON_String8Cmp(curr->name, key))
result = json_value_as_object(curr->value);
return result;
}
json_array_s *DN_JSON_ItKeyValueIsArray(DN_JSONIt *it, DN_Str8 key)
{
json_array_s *result = nullptr;
json_object_element_s *curr = DN_JSON_ItCurrObjElement(it);
if (curr && DN_JSON_String8Cmp(curr->name, key))
result = json_value_as_array(curr->value);
return result;
}
json_string_s *DN_JSON_ItKeyValueIsString(DN_JSONIt *it, DN_Str8 key)
{
json_object_element_s *curr = DN_JSON_ItCurrObjElement(it);
json_string_s *result = nullptr;
if (curr && DN_JSON_String8Cmp(curr->name, key))
result = json_value_as_string(curr->value);
return result;
}
json_number_s *DN_JSON_ItKeyValueIsNumber(DN_JSONIt *it, DN_Str8 key)
{
json_object_element_s *curr = DN_JSON_ItCurrObjElement(it);
json_number_s *result = nullptr;
if (curr && DN_JSON_String8Cmp(curr->name, key))
result = json_value_as_number(curr->value);
return result;
}
json_value_s *DN_JSON_ItKeyValueIsBool(DN_JSONIt *it, DN_Str8 key)
{
json_object_element_s *curr = DN_JSON_ItCurrObjElement(it);
json_value_s *result = nullptr;
if (curr && DN_JSON_String8Cmp(curr->name, key))
result = curr->value->type == json_type_true || curr->value->type == json_type_false ? curr->value : nullptr;
return result;
}
json_value_s *DN_JSON_ItKeyValueIsNull(DN_JSONIt *it, DN_Str8 key)
{
json_object_element_s *curr = DN_JSON_ItCurrObjElement(it);
json_value_s *result = nullptr;
if (curr && DN_JSON_String8Cmp(curr->name, key))
result = curr->value->type == json_type_null ? curr->value : nullptr;
return result;
}
// NOTE: DN_JSON_ItValueTo ////////////////////////////////////////////////////////////////////////
DN_Str8 DN_JSON_ItValueToString(DN_JSONIt *it)
{
DN_Str8 result = {};
if (json_string_s *curr = DN_JSON_ItValueIsString(it))
result = DN_Str8FromPtr(curr->string, curr->string_size);
return result;
}
int64_t DN_JSON_ItValueToI64(DN_JSONIt *it)
{
int64_t result = {};
if (json_number_s *curr = DN_JSON_ItValueIsNumber(it))
result = DN_I64FromStr8(DN_Str8FromPtr(curr->number, curr->number_size), 0 /*separator*/).value;
return result;
}
uint64_t DN_JSON_ItValueToU64(DN_JSONIt *it)
{
uint64_t result = {};
if (json_number_s *curr = DN_JSON_ItValueIsNumber(it))
result = DN_U64FromStr8(DN_Str8FromPtr(curr->number, curr->number_size), 0 /*separator*/).value;
return result;
}
bool DN_JSON_ItValueToBool(DN_JSONIt *it)
{
bool result = {};
if (json_value_s *curr = DN_JSON_ItValueIsBool(it))
result = curr->type == json_type_true;
return result;
}
void DN_JSON_ItErrorUnknownKeyValue_(DN_JSONIt *it, DN_CallSite call_site)
{
if (!it)
return;
json_object_element_s const *curr = DN_JSON_ItCurrObjElement(it);
if (!curr)
return;
size_t value_type_size = 0;
char const *value_type = DN_JSON_TypeEnumCString(DN_Cast(json_type_e)curr->value->type, &value_type_size);
json_string_s const *key = curr->name;
if (it->flags & json_parse_flags_allow_location_information) {
json_string_ex_s const *info = DN_Cast(json_string_ex_s const *)key;
DN_LogEmitFromType(DN_LogMakeU32LogTypeParam(DN_LogType_Warning),
call_site,
"Unknown key-value pair in object [loc=%zu:%zu, key=%.*s, value=%.*s]",
info->line_no,
info->row_no,
DN_Cast(int) key->string_size,
key->string,
DN_Cast(int) value_type_size,
value_type);
} else {
DN_LogEmitFromType(DN_LogMakeU32LogTypeParam(DN_LogType_Warning),
call_site,
"Unknown key-value pair in object [key=%.*s, value=%.*s]",
DN_Cast(int) key->string_size,
key->string,
DN_Cast(int) value_type_size,
value_type);
}
}
-96
View File
@@ -1,96 +0,0 @@
#if !defined(DN_JSON_H)
#define DN_JSON_H
#if defined(_CLANGD)
#include "../dn.h"
#include "../External/json.h"
#endif
#if !defined(SHEREDOM_JSON_H_INCLUDED)
#error Sheredom json.h (github.com/sheredom/json.h) must be included before this file
#endif
// NOTE: DN_JSON
void *DN_JSON_ArenaAllocFunc (void *user_data, size_t count);
char const *DN_JSON_TypeEnumCString(json_type_e type, size_t *size);
bool DN_JSON_String8Cmp (json_string_s const *lhs, DN_Str8 rhs);
// NOTE: DN_JSON_It
enum DN_JSONItEntryType
{
DN_JSON_ItEntryTypeObjElement,
DN_JSON_ItEntryTypeObj,
DN_JSON_ItEntryTypeArrayElement,
DN_JSON_ItEntryTypeArray,
DN_JSON_ItEntryTypeString,
DN_JSON_ItEntryTypeNumber,
};
struct DN_JSONItEntry
{
DN_JSONItEntryType type;
void *value;
};
struct DN_JSONIt
{
DN_JSONItEntry stack[128];
int stack_count;
size_t flags;
};
DN_JSONIt DN_JSON_LoadFileToIt(DN_Arena *arena, DN_Str8 json);
// NOTE: DN_JSON_ItPush/Pop
bool DN_JSON_ItPushObjElement (DN_JSONIt *it, json_object_element_s *element);
bool DN_JSON_ItPushObj (DN_JSONIt *it, json_object_s *obj);
bool DN_JSON_ItPushArrayElement(DN_JSONIt *it, json_array_element_s *element);
bool DN_JSON_ItPushArray (DN_JSONIt *it, json_value_s *value);
bool DN_JSON_ItPushValue (DN_JSONIt *it, json_value_s *value);
void DN_JSON_ItPop (DN_JSONIt *it);
// NOTE: DN_JSON_It tree navigation
json_value_s *DN_JSON_ItPushCurrValue(DN_JSONIt *it);
bool DN_JSON_ItNext(DN_JSONIt *it);
#define DN_JSON_ItPushCurrValueIterateThenPop(it) \
for(void *DN_UniqueName(ptr) = DN_JSON_ItPushCurrValue(it); DN_UniqueName(ptr); DN_JSON_ItPop(it), DN_UniqueName(ptr) = nullptr) \
while (DN_JSON_ItNext(it))
// NOTE: DN_JSON_ItCurr
DN_JSONItEntry *DN_JSON_ItCurr(DN_JSONIt *it);
json_value_s *DN_JSON_ItCurrValue(DN_JSONIt *it);
json_object_element_s *DN_JSON_ItCurrObjElement(DN_JSONIt *it);
// NOTE: DN_JSON_ItValueIs
json_value_s *DN_JSON_ItValueIs(DN_JSONIt *it, json_type_e type);
json_object_s *DN_JSON_ItValueIsObj(DN_JSONIt *it);
json_array_s *DN_JSON_ItValueIsArray(DN_JSONIt *it);
json_string_s *DN_JSON_ItValueIsString(DN_JSONIt *it);
json_number_s *DN_JSON_ItValueIsNumber(DN_JSONIt *it);
json_value_s *DN_JSON_ItValueIsBool(DN_JSONIt *it);
json_value_s *DN_JSON_ItValueIsNull(DN_JSONIt *it);
size_t DN_JSON_ItValueArraySize(DN_JSONIt *it);
// NOTE: DN_JSON_ItKeyValueIs
DN_Str8 DN_JSON_ItKey(DN_JSONIt *it);
bool DN_JSON_ItKeyIs(DN_JSONIt *it, DN_Str8 key);
json_object_s *DN_JSON_ItKeyValueIsObj(DN_JSONIt *it, DN_Str8 key);
json_array_s *DN_JSON_ItKeyValueIsArray(DN_JSONIt *it, DN_Str8 key);
json_string_s *DN_JSON_ItKeyValueIsString(DN_JSONIt *it, DN_Str8 key);
json_number_s *DN_JSON_ItKeyValueIsNumber(DN_JSONIt *it, DN_Str8 key);
json_value_s *DN_JSON_ItKeyValueIsBool(DN_JSONIt *it, DN_Str8 key);
json_value_s *DN_JSON_ItKeyValueIsNull(DN_JSONIt *it, DN_Str8 key);
// NOTE: DN_JSON_ItValueTo
DN_Str8 DN_JSON_ItValueToString(DN_JSONIt *it);
int64_t DN_JSON_ItValueToI64(DN_JSONIt *it);
uint64_t DN_JSON_ItValueToU64(DN_JSONIt *it);
bool DN_JSON_ItValueToBool(DN_JSONIt *it);
#define DN_JSON_ItErrorUnknownKeyValue(it) DN_JSON_ItErrorUnknownKeyValue_(it, DN_CALL_SITE)
void DN_JSON_ItErrorUnknownKeyValue_(DN_JSONIt *it, DN_CallSite call_site);
#endif // !defined(DN_JSON_H)
-130
View File
@@ -1,130 +0,0 @@
#define DN_NET_CURL_CPP
#if defined(_CLANGD)
#define DN_H_WITH_OS 1
#include "../dn.h"
#include "dn_net.h"
#endif
DN_Str8 DN_NET_Str8FromResponseState(DN_NETResponseState state)
{
DN_Str8 result = {};
switch (state) {
case DN_NETResponseState_Nil: result = DN_Str8Lit("Nil"); break;
case DN_NETResponseState_Error: result = DN_Str8Lit("Error"); break;
case DN_NETResponseState_HTTP: result = DN_Str8Lit("HTTP"); break;
case DN_NETResponseState_WSOpen: result = DN_Str8Lit("WS Open"); break;
case DN_NETResponseState_WSText: result = DN_Str8Lit("WS Text"); break;
case DN_NETResponseState_WSBinary: result = DN_Str8Lit("WS Binary"); break;
case DN_NETResponseState_WSClose: result = DN_Str8Lit("WS Close"); break;
case DN_NETResponseState_WSPing: result = DN_Str8Lit("WS Ping"); break;
case DN_NETResponseState_WSPong: result = DN_Str8Lit("WS Pong"); break;
}
return result;
}
DN_NETRequest *DN_NET_RequestFromHandle(DN_NETRequestHandle handle)
{
DN_NETRequest *ptr = DN_Cast(DN_NETRequest *) handle.handle;
DN_NETRequest *result = nullptr;
if (ptr && ptr->gen == handle.gen)
result = ptr;
return result;
}
DN_NETRequestHandle DN_NET_HandleFromRequest(DN_NETRequest *request)
{
DN_NETRequestHandle result = {};
if (request) {
result.handle = DN_Cast(DN_UPtr) request;
result.gen = request->gen;
}
return result;
}
bool DN_NET_ResponseHasFailed(DN_NETResponse const* resp)
{
bool result = false;
if (resp->type == DN_NETRequestType_HTTP)
result = resp->state == DN_NETResponseState_Error || resp->http_status >= 400;
else
result = resp->state == DN_NETResponseState_Error;
return result;
}
DN_Str8 DN_NET_Str8DiagnosticFromResponse(DN_NETResponse const* resp, DN_Arena *arena)
{
DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1);
DN_Str8Builder builder = DN_Str8BuilderFromArena(&scratch.arena);
bool resp_failed = DN_NET_ResponseHasFailed(resp);
DN_Str8BuilderAppendF(&builder, "Request %s (%s", resp_failed ? "failed" : "succeeded", resp->type == DN_NETRequestType_HTTP ? "HTTP" : "WS");
if (resp->type == DN_NETRequestType_HTTP) {
if (resp->http_status)
DN_Str8BuilderAppendF(&builder, " %u", resp->http_status);
}
DN_Str8BuilderAppendF(&builder, ")");
if (resp->body.size || resp->error_str8.size) {
DN_Str8BuilderAppendRef(&builder, DN_Str8Lit(" with "));
if (resp->body.size)
DN_Str8BuilderAppendF(&builder, "%.*s", DN_Str8PrintFmt(resp->body));
if (resp->error_str8.size)
DN_Str8BuilderAppendF(&builder, "%s%.*s", resp->body.size ? ". " : "", DN_Str8PrintFmt(resp->error_str8));
}
DN_Str8 result = DN_Str8FromStr8BuilderArena(&builder, arena);
DN_TCScratchEnd(&scratch);
return result;
}
void DN_NET_BaseInit(DN_NETCore *net, char *base, DN_U64 base_size)
{
net->base = base;
net->base_size = base_size;
net->mem = DN_MemListFromBuffer(net->base, net->base_size, DN_MemFlags_Nil);
net->arena = DN_ArenaFromMemList(&net->mem);
net->completion_sem = DN_OS_SemaphoreInit(0);
}
DN_NETRequestHandle DN_NET_SetupRequest(DN_NETRequest *request, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args, DN_NETRequestType type)
{
// NOTE: Setup request
DN_Assert(request);
if (request) {
if (!request->mem.curr)
request->mem = DN_MemListFromVMem(DN_Megabytes(1), DN_Kilobytes(1), DN_MemFlags_Nil);
request->arena = DN_ArenaTempBeginFromMemList(&request->mem);
request->type = type;
request->gen = DN_Max(request->gen + 1, 1);
request->url = DN_Str8FromStr8Arena(url, &request->arena);
request->method = DN_Str8FromStr8Arena(DN_Str8TrimWhitespaceAround(method), &request->arena);
if (args) {
request->args.flags = args->flags;
request->args.username = DN_Str8FromStr8Arena(args->username, &request->arena);
request->args.password = DN_Str8FromStr8Arena(args->password, &request->arena);
if (type == DN_NETRequestType_HTTP)
request->args.payload = DN_Str8FromStr8Arena(args->payload, &request->arena);
request->args.headers = DN_ArenaNewArray(&request->arena, DN_Str8, args->headers_size, DN_ZMem_No);
DN_Assert(request->args.headers);
if (request->args.headers) {
for (DN_ForItSize(it, DN_Str8, args->headers, args->headers_size))
request->args.headers[it.index] = DN_Str8FromStr8Arena(*it.data, &request->arena);
request->args.headers_size = args->headers_size;
}
}
request->completion_sem = DN_OS_SemaphoreInit(0);
request->start_response_arena = DN_ArenaTempBeginFromArena(&request->arena);
}
DN_NETRequestHandle result = DN_NET_HandleFromRequest(request);
request->response.request = result;
request->response.type = request->type;
return result;
}
void DN_NET_EndFinishedRequest(DN_NETRequest *request)
{
// NOTE: Deallocate the memory used in the request and reset the string builder
DN_ArenaTempEnd(&request->start_response_arena, DN_ArenaReset_Yes);
}
-134
View File
@@ -1,134 +0,0 @@
#if !defined(DN_NET_H)
#define DN_NET_H
#if defined(_CLANGD)
#define DN_H_WITH_OS 1
#include "../dn.h"
#endif
enum DN_NETRequestType
{
DN_NETRequestType_Nil,
DN_NETRequestType_HTTP,
DN_NETRequestType_WS,
};
enum DN_NETResponseState
{
DN_NETResponseState_Nil,
DN_NETResponseState_Error,
DN_NETResponseState_HTTP,
DN_NETResponseState_WSOpen,
DN_NETResponseState_WSText,
DN_NETResponseState_WSBinary,
DN_NETResponseState_WSClose,
DN_NETResponseState_WSPing,
DN_NETResponseState_WSPong,
};
enum DN_NETWSSend
{
DN_NETWSSend_Text,
DN_NETWSSend_Binary,
DN_NETWSSend_Close,
DN_NETWSSend_Ping,
DN_NETWSSend_Pong,
};
enum DN_NETDoHTTPFlags
{
DN_NETDoHTTPFlags_Nil = 0,
DN_NETDoHTTPFlags_BasicAuth = 1 << 0,
};
struct DN_NETDoHTTPArgs
{
// NOTE: WS and HTTP args
DN_NETDoHTTPFlags flags;
DN_Str8 username;
DN_Str8 password;
DN_Str8 *headers;
DN_U16 headers_size;
// NOTE: HTTP args only
DN_Str8 payload;
};
struct DN_NETRequestHandle
{
DN_UPtr handle;
DN_U64 gen;
};
struct DN_NETResponse
{
// NOTE: Common to WS and HTTP responses
DN_NETRequestType type;
DN_NETResponseState state;
DN_NETRequestHandle request;
DN_Str8 error_str8;
DN_Str8 body;
// NOTE: HTTP responses only
DN_U32 http_status;
};
struct DN_NETRequest
{
DN_MemList mem;
DN_Arena arena;
DN_Arena start_response_arena;
DN_NETRequestType type;
DN_U64 gen;
DN_Str8 url;
DN_Str8 method;
DN_OSSemaphore completion_sem;
DN_NETDoHTTPArgs args;
DN_NETResponse response;
DN_NETRequest *next;
DN_NETRequest *prev;
DN_U64 context[2];
};
typedef void (DN_NETInitFunc) (struct DN_NETCore *net, char *base, DN_U64 base_size);
typedef void (DN_NETDeinitFunc) (struct DN_NETCore *net);
typedef DN_NETRequestHandle(DN_NETDoHTTPFunc) (struct DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args);
typedef DN_NETRequestHandle(DN_NETDoWSFunc) (struct DN_NETCore *net, DN_Str8 url);
typedef void (DN_NETDoWSSendFunc) (DN_NETRequestHandle handle, DN_Str8 data, DN_NETWSSend send);
typedef DN_NETResponse (DN_NETWaitForResponseFunc) (DN_NETRequestHandle handle, DN_Arena *arena, DN_U32 timeout_ms);
typedef DN_NETResponse (DN_NETWaitForAnyResponseFunc)(struct DN_NETCore *net, DN_Arena *arena, DN_U32 timeout_ms);
struct DN_NETInterface
{
DN_NETInitFunc* init;
DN_NETDeinitFunc* deinit;
DN_NETDoHTTPFunc* do_http;
DN_NETDoWSFunc* do_ws;
DN_NETDoWSSendFunc* do_ws_send;
DN_NETWaitForResponseFunc* wait_for_response;
DN_NETWaitForAnyResponseFunc* wait_for_any_response;
};
struct DN_NETCore
{
char *base;
DN_U64 base_size;
DN_MemList mem;
DN_Arena arena;
DN_OSSemaphore completion_sem;
void *context;
DN_NETInterface api;
};
DN_Str8 DN_NET_Str8FromResponseState (DN_NETResponseState state);
DN_NETRequest * DN_NET_RequestFromHandle (DN_NETRequestHandle handle);
DN_NETRequestHandle DN_NET_HandleFromRequest (DN_NETRequest *request);
bool DN_NET_ResponseHasFailed (DN_NETResponse const* resp);
DN_Str8 DN_NET_Str8DiagnosticFromResponse(DN_NETResponse const* resp, DN_Arena *arena);
// NOTE: Internal functions for different networking implementations to use
void DN_NET_BaseInit (DN_NETCore *net, char *base, DN_U64 base_size);
DN_NETRequestHandle DN_NET_SetupRequest (DN_NETRequest *request, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args, DN_NETRequestType type);
void DN_NET_EndFinishedRequest (DN_NETRequest *request);
#endif // DN_NET_H
-708
View File
@@ -1,708 +0,0 @@
#define DN_NET_CURL_CPP
#if defined(_CLANGD)
#define DN_H_WITH_OS 1
#include "../dn.h"
#include "dn_net.h"
#include "dn_net_curl.h"
#endif
struct DN_NETCurlRequest
{
void *handle;
struct curl_slist *slist;
char error[CURL_ERROR_SIZE];
bool ws_has_more;
DN_Str8Builder str8_builder;
};
enum DN_NETCurlRingEventType
{
DN_NETCurlRingEventType_Nil,
DN_NETCurlRingEventType_DoRequest,
DN_NETCurlRingEventType_SendWS,
DN_NETCurlRingEventType_ReceivedWSReceipt,
DN_NETCurlRingEventType_DeinitRequest,
};
struct DN_NETCurlRingEvent
{
DN_NETCurlRingEventType type;
DN_NETRequestHandle request;
DN_USize ws_send_size;
DN_NETWSSend ws_send;
};
static DN_NETCurlRequest *DN_NET_CurlRequestFromRequest_(DN_NETRequest *req)
{
DN_NETCurlRequest *result = req ? DN_Cast(DN_NETCurlRequest *) req->context[0] : 0;
return result;
}
static DN_NETCore *DN_NET_CurlNetFromRequest(DN_NETRequest *req)
{
DN_NETCore *result = req ? DN_Cast(DN_NETCore *) req->context[1] : 0;
return result;
}
static bool DN_NET_CurlRequestIsInList(DN_NETRequest const *first, DN_NETRequest const *find)
{
bool result = false;
for (DN_NETRequest const *it = first; !result && it; it = it->next)
result = find == it;
return result;
}
static void DN_NET_CurlMarkRequestDone_(DN_NETCore *net, DN_NETRequest *request)
{
DN_Assert(request);
DN_Assert(net);
// NOTE: The done list in CURL is also used as a place to put websocket requests after removing it
// from the 'ws_list'. By doing this we are stopping the CURL thread from receiving more data on
// the socket as that thread ticks the list of 'ws_list' sockets for data.
//
// Once the caller waited and has received the data from the websocket, the request is put back
// into the 'ws_list' which then lets the CURL thread start receiving more data for that socket.
//
// Since CURL uses a background thread, we do this behind a mutex
DN_NETCurlCore *curl = DN_Cast(DN_NETCurlCore *)net->context;
for (DN_OS_MutexScope(&curl->list_mutex)) {
DN_Assert(DN_NET_CurlRequestIsInList(curl->thread_request_list, request));
DN_DoublyLLDetach(curl->thread_request_list, request);
DN_DoublyLLAppend(curl->response_list, request);
}
DN_OS_SemaphoreIncrement(&net->completion_sem, 1);
DN_OS_SemaphoreIncrement(&request->completion_sem, 1);
}
static DN_USize DN_NET_CurlHTTPCallback_(char *payload, DN_USize size, DN_USize count, void *user_data)
{
DN_NETRequest *req = DN_Cast(DN_NETRequest *) user_data;
DN_NETCurlRequest *curl_req = DN_NET_CurlRequestFromRequest_(req);
DN_USize result = 0;
DN_USize payload_size = size * count;
if (DN_Str8BuilderAppendBytesCopy(&curl_req->str8_builder, payload, payload_size))
result = payload_size;
return result;
}
static int32_t DN_NET_CurlThreadEntryPoint_(DN_OSThread *thread)
{
DN_NETCore *net = DN_Cast(DN_NETCore *) thread->user_context;
DN_NETCurlCore *curl = DN_Cast(DN_NETCurlCore *) net->context;
DN_OS_ThreadSetNameFmt("%.*s", DN_Str8PrintFmt(curl->thread.name));
while (!curl->kill_thread) {
DN_TCScratch tmem = DN_TCScratchBeginArena(nullptr, 0);
// NOTE: Handle events sitting in the ring queue
for (bool dequeue_ring = true; dequeue_ring;) {
DN_NETCurlRingEvent event = {};
for (DN_OS_MutexScope(&curl->ring_mutex)) {
if (DN_RingHasData(&curl->ring, sizeof(event)))
DN_RingRead(&curl->ring, &event, sizeof(event));
}
DN_NETRequest *req = DN_NET_RequestFromHandle(event.request);
DN_NETCurlRequest *curl_req = DN_NET_CurlRequestFromRequest_(req);
switch (event.type) {
case DN_NETCurlRingEventType_Nil: dequeue_ring = false; break;
case DN_NETCurlRingEventType_DoRequest: {
DN_Assert(req->response.state == DN_NETResponseState_Nil);
DN_Assert(req->type != DN_NETRequestType_Nil);
// NOTE: Attach it to the CURL thread's request list
for (DN_OS_MutexScope(&curl->list_mutex)) {
DN_Assert(DN_NET_CurlRequestIsInList(curl->request_list, req));
DN_DoublyLLDetach(curl->request_list, req);
}
DN_DoublyLLAppend(curl->thread_request_list, req);
// NOTE: Add the connection to CURLM and start ticking it once we finish handling all the
// ring events
CURLMcode multi_add = curl_multi_add_handle(curl->thread_curlm, curl_req->handle);
DN_Assert(multi_add == CURLM_OK);
} break;
case DN_NETCurlRingEventType_SendWS: {
DN_Str8 payload = {};
for (DN_OS_MutexScope(&curl->ring_mutex)) {
DN_Assert(DN_RingHasData(&curl->ring, event.ws_send_size));
payload = DN_Str8AllocArena(event.ws_send_size, DN_ZMem_No, &tmem.arena);
DN_RingRead(&curl->ring, payload.data, payload.size);
}
DN_U32 curlws_flag = 0;
switch (event.ws_send) {
case DN_NETWSSend_Text: curlws_flag = CURLWS_TEXT; break;
case DN_NETWSSend_Binary: curlws_flag = CURLWS_BINARY; break;
case DN_NETWSSend_Close: curlws_flag = CURLWS_CLOSE; break;
case DN_NETWSSend_Ping: curlws_flag = CURLWS_PING; break;
case DN_NETWSSend_Pong: curlws_flag = CURLWS_PONG; break;
}
DN_Assert(req->type == DN_NETRequestType_WS);
DN_Assert(req->response.state >= DN_NETResponseState_WSOpen && req->response.state <= DN_NETResponseState_WSPong);
DN_USize sent = 0;
CURLcode send_result = curl_ws_send(curl_req->handle, payload.data, payload.size, &sent, 0, curlws_flag);
DN_AssertF(send_result == CURLE_OK, "Failed to send: %s", curl_easy_strerror(send_result));
DN_AssertF(sent == payload.size, "Failed to send all bytes (%zu vs %zu)", sent, payload.size);
} break;
case DN_NETCurlRingEventType_ReceivedWSReceipt: {
DN_Assert(req->type == DN_NETRequestType_WS);
DN_Assert(req->response.state >= DN_NETResponseState_WSOpen && req->response.state <= DN_NETResponseState_WSPong);
req->response.state = DN_NETResponseState_WSOpen;
// NOTE: End the temp memory storing the WS data we just read and the user returned to us
// (we got their receipt back). Then restart the temp memory scope for the next websocket
// payload
DN_NET_EndFinishedRequest(req);
req->start_response_arena = DN_ArenaTempBeginFromArena(&req->arena);
curl_req->str8_builder = DN_Str8BuilderFromArena(&req->start_response_arena);
for (DN_OS_MutexScope(&curl->list_mutex)) {
DN_Assert(DN_NET_CurlRequestIsInList(curl->request_list, req));
DN_DoublyLLDetach(curl->request_list, req);
}
DN_DoublyLLAppend(curl->thread_request_list, req);
} break;
case DN_NETCurlRingEventType_DeinitRequest: {
DN_Assert(event.request.handle != 0);
DN_NETRequest *request = DN_Cast(DN_NETRequest *) event.request.handle;
// NOTE: Detach the request from the deinit list. This brings the request into this
// thread's provenance, no other threads modifying the deinit list will race with us.
for (DN_OS_MutexScope(&curl->list_mutex)) {
DN_Assert(DN_NET_CurlRequestIsInList(curl->deinit_list, request));
DN_DoublyLLDetach(curl->deinit_list, request);
}
// NOTE: Now we can modify the request, release resources
DN_NET_EndFinishedRequest(request);
DN_OS_SemaphoreDeinit(&request->completion_sem);
curl_multi_remove_handle(curl->thread_curlm, curl_req->handle);
curl_slist_free_all(curl_req->slist);
curl_easy_reset(curl_req->handle);
CURL *copy = curl_req->handle;
*curl_req = {};
curl_req->handle = copy;
// NOTE: Zero the struct preserving just the data we need to retain
DN_NETRequest resetter = {};
resetter.arena = request->arena;
resetter.gen = request->gen;
DN_Memcpy(resetter.context, request->context, sizeof(resetter.context));
*request = resetter;
// NOTE: Add it to the free list
for (DN_OS_MutexScope(&curl->list_mutex))
DN_DoublyLLAppend(curl->free_list, request);
} break;
}
}
// NOTE: Pump handles
int running_handles = 0;
CURLMcode perform_result = curl_multi_perform(curl->thread_curlm, &running_handles);
if (perform_result != CURLM_OK)
DN_InvalidCodePath;
// NOTE: Check pump result
for (;;) {
int msgs_in_queue = 0;
CURLMsg *msg = curl_multi_info_read(curl->thread_curlm, &msgs_in_queue);
if (msg) {
// NOTE: Get request handle
DN_NETRequest *req = nullptr;
curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, DN_Cast(void **) & req);
DN_Assert(req);
DN_Assert(DN_NET_CurlRequestIsInList(curl->thread_request_list, req));
DN_NETCurlRequest *curl_req = DN_NET_CurlRequestFromRequest_(req);
DN_Assert(curl_req->handle == msg->easy_handle);
if (msg->data.result == CURLE_OK) {
// NOTE: Get HTTP response code
CURLcode get_result = curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &req->response.http_status);
if (get_result == CURLE_OK) {
if (req->type == DN_NETRequestType_HTTP) {
req->response.state = DN_NETResponseState_HTTP;
} else {
DN_Assert(req->type == DN_NETRequestType_WS);
req->response.state = DN_NETResponseState_WSOpen;
}
} else {
req->response.error_str8 = DN_Str8FromFmtArena(&req->start_response_arena, "Failed to get HTTP response status (CURL %d): %s", msg->data.result, curl_easy_strerror(get_result));
req->response.state = DN_NETResponseState_Error;
}
} else {
DN_USize curl_extended_error_size = DN_CStr8Size(curl_req->error);
req->response.state = DN_NETResponseState_Error;
req->response.error_str8 = DN_Str8FromFmtArena(&req->start_response_arena,
"HTTP request '%.*s' failed (CURL %d): %s%s%s%s",
DN_Str8PrintFmt(req->url),
msg->data.result,
curl_easy_strerror(msg->data.result),
curl_extended_error_size ? " (" : "",
curl_extended_error_size ? curl_req->error : "",
curl_extended_error_size ? ")" : "");
}
if (req->type == DN_NETRequestType_HTTP || req->response.state == DN_NETResponseState_Error) {
// NOTE: Remove the request from the multi handle if we're a HTTP request
// because it typically terminates the connection. In websockets the
// connection remains in the multi-handle to allow you to send and
// receive WS data from it.
//
// If there's an error (either websocket or HTTP) we will also remove the
// connection from the multi handle as it failed. One a connection has
// failed, curl will not poll that connection so there's no point keeping
// it attached to the multi handle.
curl_multi_remove_handle(curl->thread_curlm, msg->easy_handle);
}
DN_NET_CurlMarkRequestDone_(net, req);
}
if (msgs_in_queue == 0)
break;
}
// NOTE: Check websockets
DN_USize ws_count = 0;
for (DN_NETRequest *req = curl->thread_request_list; req; req = req->next) {
DN_Assert(req->type == DN_NETRequestType_WS || req->type == DN_NETRequestType_HTTP);
if (req->type != DN_NETRequestType_WS || !(req->response.state >= DN_NETResponseState_WSOpen && req->response.state <= DN_NETResponseState_WSPong))
continue;
ws_count++;
const curl_ws_frame *meta = nullptr;
DN_NETCurlRequest *curl_req = DN_NET_CurlRequestFromRequest_(req);
CURLcode receive_result = CURLE_OK;
while (receive_result == CURLE_OK) {
// NOTE: Determine WS payload size received. Note that since we pass in a null pointer CURL
// will set meta->len to 0 and say that there's meta->bytesleft in the next chunk.
DN_USize bytes_read = 0;
receive_result = curl_ws_recv(curl_req->handle, nullptr, 0, &bytes_read, &meta);
if (receive_result != CURLE_OK)
continue;
DN_Assert(meta->len == 0);
if (meta->flags & CURLWS_TEXT)
req->response.state = DN_NETResponseState_WSText;
if (meta->flags & CURLWS_BINARY)
req->response.state = DN_NETResponseState_WSBinary;
if (meta->flags & CURLWS_PING)
req->response.state = DN_NETResponseState_WSPing;
if (meta->flags & CURLWS_PONG)
req->response.state = DN_NETResponseState_WSPong;
if (meta->flags & CURLWS_CLOSE)
req->response.state = DN_NETResponseState_WSClose;
curl_req->ws_has_more = meta->flags & CURLWS_CONT;
if (curl_req->ws_has_more) {
bool is_text_or_binary = req->response.state == DN_NETResponseState_WSText ||
req->response.state == DN_NETResponseState_WSBinary;
DN_Assert(is_text_or_binary);
}
// NOTE: Allocate and read (we use meta->bytesleft as per comment from initial recv)
if (meta->bytesleft) {
DN_Str8 buffer = DN_Str8AllocArena(meta->bytesleft, DN_ZMem_No, &req->start_response_arena);
DN_Assert(buffer.size == DN_Cast(DN_USize)meta->bytesleft);
receive_result = curl_ws_recv(curl_req->handle, buffer.data, buffer.size, &buffer.size, &meta);
DN_Assert(buffer.size == DN_Cast(DN_USize)meta->len);
DN_Str8BuilderAppendRef(&curl_req->str8_builder, buffer);
}
// NOTE: There are more bytes coming if meta->bytesleft is set, (e.g. the next chunk. We
// just read the current chunk).
//
// > If this is not a complete fragment, the bytesleft field informs about how many
// additional bytes are expected to arrive before this fragment is complete.
curl_req->ws_has_more |= meta && meta->bytesleft > 0;
if (!curl_req->ws_has_more)
break;
}
// NOTE: curl_ws_recv returns CURLE_GOT_NOTHING if the associated connection is closed.
if (receive_result == CURLE_GOT_NOTHING)
curl_req->ws_has_more = false;
// NOTE: We read all the possible bytes that CURL has received for this message, but, there are
// more bytes left that we will receive on subsequent calls. We will continue to the next
// request and return back to this one when PumpRequests is called again where hopefully that
// data has arrived.
if (curl_req->ws_has_more)
continue;
// For CURLE_AGAIN
//
// > Instead of blocking, the function returns CURLE_AGAIN. The correct behavior is then to
// > wait for the socket to signal readability before calling this function again.
//
// In which case we continue ticking the other sockets and eventually exit once all ticked.
// Right after this we wait on the CURLM instance which will wake us up again when there's
// data to be read.
//
// if we received data, e.g. state was set to Text, Binary ... e.t.c we bypass this and
// report it to the user first. When the user waits for the response, they consume the data
// and then that will reinsert it into request list for CURL to read from the socket again.
bool received_data = (req->response.state >= DN_NETResponseState_WSText && req->response.state <= DN_NETResponseState_WSPong);
if (receive_result == CURLE_AGAIN && !received_data)
continue;
if (!received_data) {
if (receive_result == CURLE_GOT_NOTHING) {
req->response.state = DN_NETResponseState_WSClose;
} else if (receive_result != CURLE_OK) {
DN_USize curl_extended_error_size = DN_CStr8Size(curl_req->error);
req->response.state = DN_NETResponseState_Error;
req->response.error_str8 = DN_Str8FromFmtArena(&req->start_response_arena,
"Websocket receive '%.*s' failed (CURL %d): %s%s%s%s",
DN_Str8PrintFmt(req->url),
receive_result,
curl_easy_strerror(receive_result),
curl_extended_error_size ? " (" : "",
curl_extended_error_size ? curl_req->error : "",
curl_extended_error_size ? ")" : "");
}
}
DN_NETRequest *request_copy = req;
req = req->prev;
DN_NET_CurlMarkRequestDone_(net, request_copy);
if (!req)
break;
}
DN_I32 sleep_time_ms = ws_count > 0 ? 16 : INT32_MAX;
curl_multi_poll(curl->thread_curlm, nullptr, 0, sleep_time_ms, nullptr);
DN_TCScratchEnd(&tmem);
}
return 0;
}
DN_NETInterface DN_NET_CurlInterface()
{
DN_NETInterface result = {};
result.init = DN_NET_CurlInit;
result.deinit = DN_NET_CurlDeinit;
result.do_http = DN_NET_CurlDoHTTP;
result.do_ws = DN_NET_CurlDoWS;
result.do_ws_send = DN_NET_CurlDoWSSend;
result.wait_for_response = DN_NET_CurlWaitForResponse;
result.wait_for_any_response = DN_NET_CurlWaitForAnyResponse;
return result;
}
void DN_NET_CurlInit(DN_NETCore *net, char *base, DN_U64 base_size)
{
DN_NET_BaseInit(net, base, base_size);
DN_NETCurlCore *curl = DN_ArenaNew(&net->arena, DN_NETCurlCore, DN_ZMem_Yes);
net->context = curl;
net->api = DN_NET_CurlInterface();
DN_USize arena_bytes_avail = (net->arena.mem->curr->reserve - net->arena.mem->curr->used);
curl->ring.size = arena_bytes_avail / 2;
curl->ring.base = DN_Cast(char *) DN_ArenaAlloc(&net->arena, curl->ring.size, /*align*/ 1, DN_ZMem_Yes);
DN_Assert(curl->ring.base);
curl->ring_mutex = DN_OS_MutexInit();
curl->list_mutex = DN_OS_MutexInit();
curl->thread_curlm = DN_Cast(CURLM *) curl_multi_init();
DN_FmtAppend(curl->thread.name.data, &curl->thread.name.size, sizeof(curl->thread.name.data), "NET (CURL)");
DN_OS_ThreadInit(&curl->thread, DN_NET_CurlThreadEntryPoint_, nullptr, DN_TCInitArgsDefault(), net);
}
void DN_NET_CurlDeinit(DN_NETCore *net)
{
DN_NETCurlCore *curl = DN_Cast(DN_NETCurlCore *) net->context;
curl->kill_thread = true;
curl_multi_wakeup(curl->thread_curlm);
DN_OS_ThreadJoin(&curl->thread, DN_TCDeinitArenas_Yes);
}
static DN_NETRequestHandle DN_NET_CurlDoRequest_(DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args, DN_NETRequestType type)
{
// NOTE: Allocate the request
DN_NETCurlCore *curl_core = DN_Cast(DN_NETCurlCore *) net->context;
DN_NETRequest *req = nullptr;
DN_NETRequestHandle result = {};
{
// NOTE: The free list is modified by both the calling thread and the CURLM thread (which ticks
// all the requests in the background for us)
for (DN_OS_MutexScope(&curl_core->list_mutex)) {
req = curl_core->free_list;
DN_DoublyLLDetach(curl_core->free_list, req);
}
// NOTE None in the free list so allocate one
if (!req) {
DN_OS_MutexLock(&curl_core->list_mutex);
DN_U64 arena_pos = DN_MemListPos(net->arena.mem);
req = DN_ArenaNewZ(&net->arena, DN_NETRequest);
DN_NETCurlRequest *curl_req = DN_ArenaNewZ(&net->arena, DN_NETCurlRequest);
if (!req || !curl_req) {
DN_MemListPopTo(net->arena.mem, arena_pos);
DN_OS_MutexUnlock(&curl_core->list_mutex);
return result;
}
DN_OS_MutexUnlock(&curl_core->list_mutex);
curl_req->handle = DN_Cast(CURL *) curl_easy_init();
req->context[0] = DN_Cast(DN_UPtr) curl_req;
}
}
// NOTE: Setup the request
DN_NETCurlRequest *curl_req = DN_NET_CurlRequestFromRequest_(req);
{
result = DN_NET_SetupRequest(req, url, method, args, type);
req->context[1] = DN_Cast(DN_UPtr) net;
curl_req->str8_builder = DN_Str8BuilderFromArena(&req->start_response_arena);
}
// NOTE: Setup the request for curl API
{
CURL *curl = curl_req->handle;
curl_easy_setopt(curl, CURLOPT_PRIVATE, req);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_req->error);
// NOTE: Perform request and read all response headers before handing
// control back to app.
curl_easy_setopt(curl, CURLOPT_URL, req->url.data);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
// NOTE: Setup response handler
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, DN_NET_CurlHTTPCallback_);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, req);
// NOTE: Assign HTTP headers
for (DN_ForItSize(it, DN_Str8, req->args.headers, req->args.headers_size)) {
DN_Assert(it.data->data[it.data->size] == 0);
curl_req->slist = curl_slist_append(curl_req->slist, it.data->data);
}
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_req->slist);
// NOTE: Setup handle for protocol
switch (req->type) {
case DN_NETRequestType_Nil: DN_InvalidCodePath; break;
case DN_NETRequestType_WS: {
curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L);
} break;
case DN_NETRequestType_HTTP: {
DN_Str8 const GET = DN_Str8Lit("GET");
DN_Str8 const POST = DN_Str8Lit("POST");
if (DN_Str8EqInsensitive(req->method, GET)) {
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
} else if (DN_Str8EqInsensitive(req->method, POST)) {
curl_easy_setopt(curl, CURLOPT_POST, 1);
if (req->args.payload.size > DN_Gigabytes(2))
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, req->args.payload.size);
else
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, req->args.payload.size);
curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, req->args.payload.data);
} else {
DN_InvalidCodePathF("Unimplemented");
}
} break;
}
// NOTE: Handle basic auth
if (req->args.flags & DN_NETDoHTTPFlags_BasicAuth) {
if (req->args.username.size && req->args.password.size) {
DN_Assert(req->args.username.data[req->args.username.size] == 0);
DN_Assert(req->args.password.data[req->args.password.size] == 0);
curl_easy_setopt(curl, CURLOPT_USERNAME, req->args.username.data);
curl_easy_setopt(curl, CURLOPT_PASSWORD, req->args.password.data);
}
}
}
// NOTE: Dispatch the request to the CURL thread
{
// NOTE: Immediately add the request to the request list so it happens "atomically" in the
// calling thread. If the calling thread deinitialises this layer before the CURL thread can be
// pre-empted, we can lose track of this request.
for (DN_OS_MutexScope(&curl_core->list_mutex))
DN_DoublyLLAppend(curl_core->request_list, req);
// NOTE: Enqueue request to go into CURL's ring queue. The CURL thread will sleep and wait for
// bytes to come in for the request and then dump the response into the done list to be consumed
// via wait for response
DN_NETCurlRingEvent event = {};
event.type = DN_NETCurlRingEventType_DoRequest;
event.request = result;
for (DN_OS_MutexScope(&curl_core->ring_mutex))
DN_RingWriteStruct(&curl_core->ring, &event);
curl_multi_wakeup(curl_core->thread_curlm);
}
return result;
}
DN_NETRequestHandle DN_NET_CurlDoHTTP(DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args)
{
DN_NETRequestHandle result = DN_NET_CurlDoRequest_(net, url, method, args, DN_NETRequestType_HTTP);
return result;
}
DN_NETRequestHandle DN_NET_CurlDoWSArgs(DN_NETCore *net, DN_Str8 url, DN_NETDoHTTPArgs const *args)
{
DN_NETRequestHandle result = DN_NET_CurlDoRequest_(net, url, DN_Str8Lit(""), args, DN_NETRequestType_WS);
return result;
}
DN_NETRequestHandle DN_NET_CurlDoWS(DN_NETCore *net, DN_Str8 url)
{
DN_NETRequestHandle result = DN_NET_CurlDoWSArgs(net, url, nullptr);
return result;
}
void DN_NET_CurlDoWSSend(DN_NETRequestHandle handle, DN_Str8 payload, DN_NETWSSend send)
{
DN_NETRequest *req = DN_NET_RequestFromHandle(handle);
if (!req)
return;
DN_NETCore *net = DN_NET_CurlNetFromRequest(req);
DN_NETCurlCore *curl = DN_Cast(DN_NETCurlCore *) net->context;
DN_Assert(curl);
DN_NETCurlRingEvent event = {};
event.type = DN_NETCurlRingEventType_SendWS;
event.request = handle;
event.ws_send_size = payload.size;
event.ws_send = send;
for (DN_OS_MutexScope(&curl->ring_mutex)) {
DN_Assert(DN_RingHasSpace(&curl->ring, payload.size));
DN_RingWriteStruct(&curl->ring, &event);
DN_RingWrite(&curl->ring, payload.data, payload.size);
}
curl_multi_wakeup(curl->thread_curlm);
}
static DN_NETResponse DN_NET_CurlHandleFinishedRequest_(DN_NETCurlCore *curl, DN_NETRequest *req, DN_Arena *arena)
{
// NOTE: Generate the response, copy out the strings into the user given memory
DN_NETResponse result = req->response;
DN_NETCurlRequest *curl_req = DN_NET_CurlRequestFromRequest_(req);
{
result.body = DN_Str8FromStr8BuilderArena(&curl_req->str8_builder, arena);
if (result.error_str8.size)
result.error_str8 = DN_Str8FromStr8Arena(result.error_str8, arena);
}
bool continue_ws_request = false;
if (req->type == DN_NETRequestType_WS &&
req->response.state != DN_NETResponseState_Error &&
req->response.state != DN_NETResponseState_WSClose) {
continue_ws_request = true;
}
// NOTE: Put the request into the requisite list
for (DN_OS_MutexScope(&curl->list_mutex)) {
// NOTE: Dequeue the request, it _must_ have been in the response list at this point for it to
// have ben waitable in the first place.
DN_AssertF(DN_NET_CurlRequestIsInList(curl->response_list, req),
"A completed response should only signal the completion semaphore when it's in the response list");
DN_DoublyLLDetach(curl->response_list, req);
// NOTE: A websocket that is continuing to get data should go back into the request list because
// there's more data to be received. All other requests need to go into the deinit list (so that
// we keep track of it in the time inbetween it takes for the CURL thread to be scheduled and
// release the CURL handle from CURLM and release resources e.t.c.)
if (continue_ws_request)
DN_DoublyLLAppend(curl->request_list, req);
else
DN_DoublyLLAppend(curl->deinit_list, req);
}
// NOTE: Submit the post-request event to the CURL thread
DN_NETCurlRingEvent event = {};
event.request = DN_NET_HandleFromRequest(req);
if (continue_ws_request) {
event.type = DN_NETCurlRingEventType_ReceivedWSReceipt;
} else {
// NOTE: Deinit _has_ to be sent to the CURL thread because we need to remove the CURL handle
// from the CURLM instance and the CURL thread uses the CURLM instance (e.g. CURLM is not thread
// safe)
event.type = DN_NETCurlRingEventType_DeinitRequest;
}
for (DN_OS_MutexScope(&curl->ring_mutex))
DN_RingWriteStruct(&curl->ring, &event);
curl_multi_wakeup(curl->thread_curlm);
return result;
}
DN_NETResponse DN_NET_CurlWaitForResponse(DN_NETRequestHandle handle, DN_Arena *arena, DN_U32 timeout_ms)
{
DN_NETResponse result = {};
DN_NETRequest *req = DN_NET_RequestFromHandle(handle);
if (!req)
return result;
DN_NETCore *net = DN_Cast(DN_NETCore *) req->context[1];
DN_NETCurlCore *curl = DN_Cast(DN_NETCurlCore *) net->context;
DN_Assert(curl);
DN_OSSemaphoreWaitResult wait = DN_OS_SemaphoreWait(&req->completion_sem, timeout_ms);
if (wait != DN_OSSemaphoreWaitResult_Success)
return result;
// NOTE: Decrement the global 'request done' completion semaphore since the user consumed the
// request individually.
DN_OSSemaphoreWaitResult net_wait_result = DN_OS_SemaphoreWait(&net->completion_sem, 0 /*timeout_ms*/);
DN_AssertF(net_wait_result == DN_OSSemaphoreWaitResult_Success, "Wait result was: %zu", DN_Cast(DN_USize) net_wait_result);
// NOTE: Finish handling the response
result = DN_NET_CurlHandleFinishedRequest_(curl, req, arena);
return result;
}
DN_NETResponse DN_NET_CurlWaitForAnyResponse(DN_NETCore *net, DN_Arena *arena, DN_U32 timeout_ms)
{
DN_NETCurlCore *curl = DN_Cast(DN_NETCurlCore *) net->context;
DN_Assert(curl);
DN_NETResponse result = {};
DN_OSSemaphoreWaitResult req_wait = DN_OS_SemaphoreWait(&net->completion_sem, timeout_ms);
if (req_wait != DN_OSSemaphoreWaitResult_Success)
return result;
// NOTE: Just grab the handle, handle finished request will dequeue for us
DN_NETRequestHandle handle = {};
for (DN_OS_MutexScope(&curl->list_mutex)) {
DN_Assert(curl->response_list);
handle = DN_NET_HandleFromRequest(curl->response_list);
}
// NOTE: Decrement the request's completion semaphore since the user consumed the global semaphore
DN_NETRequest *req = DN_NET_RequestFromHandle(handle);
DN_OSSemaphoreWaitResult net_wait = DN_OS_SemaphoreWait(&req->completion_sem, 0 /*timeout_ms*/);
DN_AssertF(net_wait == DN_OSSemaphoreWaitResult_Success, "Wait result was: %zu", DN_Cast(DN_USize) net_wait);
// NOTE: Finish handling the response
result = DN_NET_CurlHandleFinishedRequest_(curl, req, arena);
return result;
}
-39
View File
@@ -1,39 +0,0 @@
#if !defined(DN_NET_CURL_H)
#define DN_NET_CURL_H
#if defined(_CLANGD)
#include "dn_net.h"
#endif
struct DN_NETCurlCore
{
// NOTE: Shared w/ user and networking thread
DN_Ring ring;
DN_OSMutex ring_mutex;
bool kill_thread;
DN_OSMutex list_mutex; // Lock for request, response, deinit, free list
DN_NETRequest *request_list; // Current requests submitted by the user thread awaiting to move into the thread request list
DN_NETRequest *response_list; // Finished requests that are to be deqeued by the user via wait for response
DN_NETRequest *deinit_list; // Requests that are finished and are awaiting to be de-initialised by the CURL thread
DN_NETRequest *free_list; // Request pool that new requests will use before allocating
// NOTE: Networking thread only
DN_NETRequest *thread_request_list; // Current requests being executed by the CURL thread.
// This list is exclusively owned by the CURL thread so no locking is needed
DN_OSThread thread;
void *thread_curlm;
};
#define DN_NET_CurlCoreFromNet(net) ((net) ? (DN_Cast(DN_NETCurlCore *)(net)->context) : nullptr);
DN_NETInterface DN_NET_CurlInterface ();
void DN_NET_CurlInit (DN_NETCore *net, char *base, DN_U64 base_size);
void DN_NET_CurlDeinit (DN_NETCore *net);
DN_NETRequestHandle DN_NET_CurlDoHTTP (DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args);
DN_NETRequestHandle DN_NET_CurlDoWSArgs (DN_NETCore *net, DN_Str8 url, DN_NETDoHTTPArgs const *args);
DN_NETRequestHandle DN_NET_CurlDoWS (DN_NETCore *net, DN_Str8 url);
void DN_NET_CurlDoWSSend (DN_NETRequestHandle handle, DN_Str8 payload, DN_NETWSSend send);
DN_NETResponse DN_NET_CurlWaitForResponse (DN_NETRequestHandle handle, DN_Arena *arena, DN_U32 timeout_ms);
DN_NETResponse DN_NET_CurlWaitForAnyResponse(DN_NETCore *net, DN_Arena *arena, DN_U32 timeout_ms);
#endif // !defined(DN_NET_CURL_H)
-468
View File
@@ -1,468 +0,0 @@
#if !defined(__EMSCRIPTEN__)
#error "This file can only be compiled with Emscripten"
#endif
#include <emscripten.h>
#include <emscripten/fetch.h>
#include <emscripten/websocket.h>
#if defined(_CLANGD)
#include "dn_net.h"
#include "dn_net_emscripten.h"
#endif
struct DN_NETEmcWSEvent
{
DN_NETResponseState state;
DN_Str8 payload;
DN_NETEmcWSEvent *next;
};
struct DN_NETEmcCore
{
DN_Pool pool;
DN_NETRequest *response_list; // Responses received that are to be deqeued via wait for response
DN_NETRequest *free_list; // Request pool that new requests will use before allocating
};
struct DN_NETEmcRequest
{
int socket;
DN_NETEmcWSEvent *first_event;
DN_NETEmcWSEvent *last_event;
};
DN_NETInterface DN_NET_EmcInterface()
{
DN_NETInterface result = {};
result.init = DN_NET_EmcInit;
result.deinit = DN_NET_EmcDeinit;
result.do_http = DN_NET_EmcDoHTTP;
result.do_ws = DN_NET_EmcDoWS;
result.do_ws_send = DN_NET_EmcDoWSSend;
result.wait_for_response = DN_NET_EmcWaitForResponse;
result.wait_for_any_response = DN_NET_EmcWaitForAnyResponse;
return result;
}
static DN_NETEmcWSEvent *DN_NET_EmcAllocWSEvent_(DN_NETRequest *request)
{
// NOTE: Allocate the event and attach to the request
DN_NETEmcRequest *emc_request = DN_Cast(DN_NETEmcRequest *) request->context[1];
DN_NETEmcWSEvent *result = DN_ArenaNew(&request->arena, DN_NETEmcWSEvent, DN_ZMem_Yes);
DN_Assert(result);
if (result) {
if (!emc_request->first_event)
emc_request->first_event = result;
if (emc_request->last_event)
emc_request->last_event->next = result;
emc_request->last_event = result;
}
return result;
}
static void DN_NET_EmcOnRequestDone_(DN_NETCore *net, DN_NETRequest *request)
{
// NOTE: This may be call multiple times on the same request if we get multiple responses when we
// yield to the javascript event loop, e.g. the application received multiple WS payloads before
// it waited and consequently consumed the response from the payload.
//
// So if the next pointer is already set, then it should be that the request is already enqueued.
DN_NETEmcCore *emc = DN_Cast(DN_NETEmcCore *) net->context;
if (!request->next && !request->prev && request != emc->response_list) {
request->prev = nullptr;
request->next = emc->response_list;
if (emc->response_list)
emc->response_list->prev = request;
emc->response_list = request;
}
DN_OS_SemaphoreIncrement(&net->completion_sem, 1);
DN_OS_SemaphoreIncrement(&request->completion_sem, 1);
}
static bool DN_NET_EmcWSOnOpen(int eventType, EmscriptenWebSocketOpenEvent const *event, void *user_data)
{
DN_NETRequest *req = DN_Cast(DN_NETRequest *) user_data;
DN_NETCore *net = DN_Cast(DN_NETCore *) req->context[0];
DN_NETEmcWSEvent *net_event = DN_NET_EmcAllocWSEvent_(req);
net_event->state = DN_NETResponseState_WSOpen;
DN_NET_EmcOnRequestDone_(net, req);
return true;
}
static bool DN_NET_EmcWSOnMessage(int eventType, const EmscriptenWebSocketMessageEvent *event, void *user_data)
{
DN_NETRequest *req = DN_Cast(DN_NETRequest *) user_data;
DN_NETCore *net = DN_Cast(DN_NETCore *) req->context[0];
DN_NETEmcWSEvent *net_event = DN_NET_EmcAllocWSEvent_(req);
net_event->state = event->isText ? DN_NETResponseState_WSText : DN_NETResponseState_WSBinary;
if (event->numBytes > 0)
net_event->payload = DN_Str8FromPtrArena(event->data, event->numBytes, &req->arena);
DN_NET_EmcOnRequestDone_(net, req);
return true;
}
static bool DN_NET_EmcWSOnError(int eventType, EmscriptenWebSocketErrorEvent const *event, void *user_data)
{
DN_NETRequest *req = DN_Cast(DN_NETRequest *) user_data;
DN_NETCore *net = DN_Cast(DN_NETCore *) req->context[0];
DN_NETEmcWSEvent *net_event = DN_NET_EmcAllocWSEvent_(req);
net_event->state = DN_NETResponseState_Error;
DN_NET_EmcOnRequestDone_(net, req);
return true;
}
static bool DN_NET_EmcWSOnClose(int eventType, EmscriptenWebSocketCloseEvent const *event, void *user_data)
{
DN_NETRequest *req = DN_Cast(DN_NETRequest *) user_data;
DN_NETCore *net = DN_Cast(DN_NETCore *) req->context[0];
DN_NETEmcWSEvent *net_event = DN_NET_EmcAllocWSEvent_(req);
net_event->state = DN_NETResponseState_WSClose;
net_event->payload = DN_Str8FromFmtArena(&req->arena, "Websocket closed '%.*s': (%u) %s (was %s close)", DN_Str8PrintFmt(req->url), event->code, event->reason, event->wasClean ? "clean" : "unclean");
DN_NET_EmcOnRequestDone_(net, req);
return true;
}
static void DN_NET_EmcHTTPSuccessCallback(emscripten_fetch_t *fetch)
{
DN_NETRequest *req = DN_Cast(DN_NETRequest *) fetch->userData;
DN_NETCore *net = DN_Cast(DN_NETCore *) req->context[0];
req->response.http_status = fetch->status;
req->response.state = DN_NETResponseState_HTTP;
req->response.body = DN_Str8FromStr8Arena(DN_Str8FromPtr(fetch->data, fetch->numBytes - 1), &req->arena);
DN_NET_EmcOnRequestDone_(net, req);
}
static void DN_NET_EmcHTTPFailCallback(emscripten_fetch_t *fetch)
{
DN_NETRequest *req = DN_Cast(DN_NETRequest *) fetch->userData;
DN_NETCore *net = DN_Cast(DN_NETCore *) req->context[0];
req->response.http_status = fetch->status;
req->response.state = DN_NETResponseState_Error;
DN_NET_EmcOnRequestDone_(net, req);
}
static void DN_NET_EmcHTTPProgressCallback(emscripten_fetch_t *fetch)
{
}
void DN_NET_EmcInit(DN_NETCore *net, char *base, DN_U64 base_size)
{
DN_NET_BaseInit(net, base, base_size);
DN_NETEmcCore *emc = DN_ArenaNew(&net->arena, DN_NETEmcCore, DN_ZMem_Yes);
emc->pool = DN_PoolFromArena(&net->arena, 0);
net->context = emc;
}
void DN_NET_EmcDeinit(DN_NETCore *net)
{
(void)net;
// TODO: Track all the request handles and clean it up
}
static DN_NETRequest *DN_NET_EmcAllocRequest_(DN_NETCore *net)
{
// NOTE: Allocate request
DN_NETEmcCore *emc = DN_Cast(DN_NETEmcCore *) net->context;
DN_NETRequest *result = emc->free_list;
if (result) {
emc->free_list = emc->free_list->next;
result->next = nullptr;
DN_Assert(result->prev == nullptr);
if (emc->free_list) {
DN_Assert(emc->free_list->prev == nullptr);
}
} else {
// NOTE: Setup the request's arena here. WASM doesn't have the concept of virtual memory
// so we use malloc to initialise it.
result = DN_ArenaNew(&net->arena, DN_NETRequest, DN_ZMem_Yes);
if (result) {
result->arena = DN_ArenaFromMemList(&result->mem);
}
}
// NOTE: Setup some emscripten specific data into our request context
if (result) {
result->context[0] = DN_Cast(DN_UPtr) net;
}
return result;
}
DN_NETRequestHandle DN_NET_EmcDoHTTP(DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args)
{
DN_NETRequest *req = DN_NET_EmcAllocRequest_(net);
DN_NETRequestHandle result = DN_NET_SetupRequest(req, url, method, args, DN_NETRequestType_HTTP);
// NOTE: Setup the HTTP request via Emscripten
emscripten_fetch_attr_t fetch_attribs = {};
{
DN_Assert(req->args.payload.data[req->args.payload.size] == 0);
DN_Assert(req->url.data[req->url.size] == 0);
// NOTE: Setup request for emscripten
emscripten_fetch_attr_init(&fetch_attribs);
fetch_attribs.requestData = req->args.payload.data;
fetch_attribs.requestDataSize = req->args.payload.size;
DN_Assert(req->method.size < DN_ArrayCountU(fetch_attribs.requestMethod));
DN_Memcpy(fetch_attribs.requestMethod, req->method.data, req->method.size);
fetch_attribs.requestMethod[req->method.size] = 0;
// NOTE: Assign HTTP headers
if (req->args.headers_size) {
char **headers = DN_ArenaNewArray(&req->start_response_arena, char *, req->args.headers_size + 1, DN_ZMem_Yes);
for (DN_ForItSize(it, DN_Str8, req->args.headers, req->args.headers_size)) {
DN_Assert(it.data->data[it.data->size] == 0);
headers[it.index] = it.data->data;
}
fetch_attribs.requestHeaders = headers;
}
// NOTE: Handle basic auth
if (req->args.flags & DN_NETDoHTTPFlags_BasicAuth) {
if (req->args.username.size && req->args.password.size) {
DN_Assert(req->args.username.data[req->args.username.size] == 0);
DN_Assert(req->args.password.data[req->args.password.size] == 0);
fetch_attribs.withCredentials = true;
fetch_attribs.userName = req->args.username.data;
fetch_attribs.password = req->args.password.data;
}
}
// NOTE: It would be nice to use EMSCRIPTEN_FETCH_STREAM_DATA however
// emscripten has this note on the current version I'm using that this is
// only supported in Firefox so this is a no-go.
//
// > If passed, the intermediate streamed bytes will be passed in to the
// > onprogress() handler. If not specified, the onprogress() handler will still
// > be called, but without data bytes. Note: Firefox only as it depends on
// > 'moz-chunked-arraybuffer'.
fetch_attribs.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
fetch_attribs.onsuccess = DN_NET_EmcHTTPSuccessCallback;
fetch_attribs.onerror = DN_NET_EmcHTTPFailCallback;
fetch_attribs.onprogress = DN_NET_EmcHTTPProgressCallback;
fetch_attribs.userData = req;
}
// NOTE: Dispatch the asynchronous fetch
emscripten_fetch(&fetch_attribs, req->url.data);
return result;
}
DN_NETRequestHandle DN_NET_EmcDoWS(DN_NETCore *net, DN_Str8 url)
{
DN_Assert(emscripten_websocket_is_supported());
DN_NETRequest *req = DN_NET_EmcAllocRequest_(net);
DN_NETRequestHandle result = DN_NET_SetupRequest(req, url, /*method=*/DN_Str8Lit(""), /*args=*/nullptr, DN_NETRequestType_WS);
if (!req)
return result;
// NOTE: Setup some emscripten specific data into our request context
req->context[1] = DN_Cast(DN_UPtr) DN_ArenaNew(&req->start_response_arena, DN_NETEmcRequest, DN_ZMem_Yes);
// NOTE: Create the websocket request and dispatch it via emscripten
EmscriptenWebSocketCreateAttributes attr;
emscripten_websocket_init_create_attributes(&attr);
attr.url = req->url.data;
DN_NETEmcRequest *emc_request = DN_Cast(DN_NETEmcRequest *) req->context[1];
emc_request->socket = emscripten_websocket_new(&attr);
DN_Assert(emc_request->socket > 0);
emscripten_websocket_set_onopen_callback(emc_request->socket, /*userData=*/req, DN_NET_EmcWSOnOpen);
emscripten_websocket_set_onmessage_callback(emc_request->socket, /*userData=*/req, DN_NET_EmcWSOnMessage);
emscripten_websocket_set_onerror_callback(emc_request->socket, /*userData=*/req, DN_NET_EmcWSOnError);
emscripten_websocket_set_onclose_callback(emc_request->socket, /*userData=*/req, DN_NET_EmcWSOnClose);
return result;
}
void DN_NET_EmcDoWSSend(DN_NETRequestHandle handle, DN_Str8 data, DN_NETWSSend send)
{
DN_AssertF(send == DN_NETWSSend_Binary || send == DN_NETWSSend_Text || send == DN_NETWSSend_Close,
"Unimplemented, Emscripten only supports some of the available operations");
int result = 0;
DN_NETRequest *request_ptr = DN_Cast(DN_NETRequest *) handle.handle;
if (request_ptr && request_ptr->gen == handle.gen) {
DN_Assert(request_ptr->type == DN_NETRequestType_WS);
DN_NETEmcRequest *emc_request = DN_Cast(DN_NETEmcRequest *) request_ptr->context[1];
switch (send) {
default: DN_InvalidCodePath; break;
case DN_NETWSSend_Text: {
DN_U64 pos = DN_MemListPos(request_ptr->start_response_arena.mem);
DN_Str8 data_null_terminated = DN_Str8FromStr8Arena(data, &request_ptr->start_response_arena);
result = emscripten_websocket_send_utf8_text(emc_request->socket, data_null_terminated.data);
DN_MemListPopTo(request_ptr->arena.mem, pos);
} break;
case DN_NETWSSend_Binary: {
result = emscripten_websocket_send_binary(emc_request->socket, data.data, data.size);
} break;
case DN_NETWSSend_Close: {
result = emscripten_websocket_close(emc_request->socket, 0, nullptr);
} break;
}
}
// TODO: Handle result, the header file doesn't really elucidate what this result value is
(void)result;
}
static DN_NETResponse DN_NET_EmcHandleFinishedRequest_(DN_NETCore *net, DN_NETEmcCore *emc, DN_NETRequestHandle handle, DN_NETRequest *request, DN_Arena *arena)
{
// NOTE: Generate the response, copy out the strings into the user given memory
DN_NETEmcRequest *emc_request = DN_Cast(DN_NETEmcRequest *) request->context[1];
DN_NETResponse result = request->response;
bool end_request = true;
bool dequeue_request = true;
if (request->type == DN_NETRequestType_HTTP) {
result.body = DN_Str8FromStr8Arena(result.body, arena);
} else {
// NOTE: Get emscripten contexts
DN_NETEmcWSEvent *emc_event = emc_request->first_event;
DN_Assert(emc_event);
DN_AssertF((emc_event->state >= DN_NETResponseState_WSOpen && emc_event->state <= DN_NETResponseState_WSPong) || emc_event->state == DN_NETResponseState_Error,
"emc_event=%p", emc_event);
// NOTE: Build the result
result.state = emc_event->state;
result.request = handle;
result.body = DN_Str8FromStr8Arena(emc_event->payload, arena);
// NOTE: Advance the event list
{
if (emc_request->first_event == emc_request->last_event) {
emc_request->last_event = emc_request->last_event->next;
DN_Assert(emc_request->first_event->next == emc_request->last_event);
}
emc_request->first_event = emc_event->next;
// NOTE: If there's still an event on the request then we do not dequeue the request from the
// response list. The user can still "wait" for a response to read more data from it.
if (emc_request->first_event)
dequeue_request = false;
}
if (result.state != DN_NETResponseState_WSClose)
end_request = false;
}
// NOTE: Remove request from the response list which is doubly-linked
if (dequeue_request) {
if (request->prev) {
DN_AssertF(request->prev->next == request, "next=%p vs request=%p", request->prev->next, request);
request->prev->next = request->next;
}
if (request->next) {
DN_AssertF(request->next->prev == request, "prev=%p vs request=%p", request->next->prev, request);
request->next->prev = request->prev;
}
if (request == emc->response_list)
emc->response_list = emc->response_list->next;
request->prev = nullptr;
request->next = nullptr;
DN_Assert(emc_request->first_event == nullptr);
DN_Assert(emc_request->last_event == nullptr);
// NOTE: Deallocate the memory used in the request and reset the string builder (as all
// payload(s) have been read from the request).
if (!end_request)
DN_ArenaTempEnd(&request->start_response_arena, DN_ArenaReset_Yes);
}
if (end_request) {
DN_NET_EndFinishedRequest(request);
emscripten_websocket_delete(emc_request->socket);
emc_request->socket = 0;
DN_NETEmcCore *emc = DN_Cast(DN_NETEmcCore *) net->context;
request->next = emc->free_list;
request->prev = nullptr;
emc->free_list = request;
}
return result;
}
static DN_OSSemaphoreWaitResult DN_NET_EmcSemaphoreWait_(DN_OSSemaphore *sem, DN_U32 timeout_ms)
{
// NOTE: In emscripten you can't just block on the semaphore with 'timeout_ms' because it needs
// to yield to the javascript's event loop otherwise the fetching step cannot progress. Instead
// we use a timeout of 0 to just immediately check if the semaphore has been signalled, if not,
// then we yield to the event loop by calling sleep.
//
// Once yielded, fetch will execute and eventually in the callback it will signal the semaphore
// where it'll return and we can break out of the simulated "timeout".
DN_OSSemaphoreWaitResult result = {};
DN_U32 timeout_remaining_ms = timeout_ms;
DN_F64 begin_ms = emscripten_get_now();
for (;;) {
result = DN_OS_SemaphoreWait(sem, 0);
if (result == DN_OSSemaphoreWaitResult_Success)
break;
if (timeout_remaining_ms <= 0)
break;
emscripten_sleep(100 /*ms*/);
DN_F64 end_ms = emscripten_get_now();
DN_USize duration_ms = DN_Cast(DN_USize)(end_ms - begin_ms);
timeout_remaining_ms = timeout_remaining_ms >= duration_ms ? timeout_remaining_ms - duration_ms : 0;
begin_ms = end_ms;
}
return result;
}
DN_NETResponse DN_NET_EmcWaitForResponse(DN_NETRequestHandle handle, DN_Arena *arena, DN_U32 timeout_ms)
{
DN_NETResponse result = {};
DN_NETRequest *request_ptr = DN_Cast(DN_NETRequest *) handle.handle;
if (request_ptr && request_ptr->gen == handle.gen) {
DN_NETCore *net = DN_Cast(DN_NETCore *) request_ptr->context[0];
DN_NETEmcCore *emc = DN_Cast(DN_NETEmcCore *) net->context;
DN_Assert(emc);
DN_OSSemaphoreWaitResult wait = DN_NET_EmcSemaphoreWait_(&request_ptr->completion_sem, timeout_ms);
if (wait != DN_OSSemaphoreWaitResult_Success)
return result;
result = DN_NET_EmcHandleFinishedRequest_(net, emc, handle, request_ptr, arena);
// NOTE: Decrement the global 'request done' completion semaphore since the user consumed the
// request individually.
DN_OSSemaphoreWaitResult net_wait_result = DN_OS_SemaphoreWait(&net->completion_sem, 0 /*timeout_ms*/);
DN_AssertF(net_wait_result == DN_OSSemaphoreWaitResult_Success, "Wait result was: %zu", DN_Cast(DN_USize) net_wait_result);
}
return result;
}
DN_NETResponse DN_NET_EmcWaitForAnyResponse(DN_NETCore *net, DN_Arena *arena, DN_U32 timeout_ms)
{
DN_NETEmcCore *emc = DN_Cast(DN_NETEmcCore *) net->context;
DN_Assert(emc);
DN_NETResponse result = {};
DN_OSSemaphoreWaitResult wait = DN_NET_EmcSemaphoreWait_(&net->completion_sem, timeout_ms);
if (wait != DN_OSSemaphoreWaitResult_Success)
return result;
DN_AssertF(emc->response_list,
"This should be set otherwise we bumped the completion sem without queueing into the "
"done list or we forgot to wait on the global semaphore after a request finished");
// NOTE: Decrement the request's completion semaphore since the user consumed the global semaphore
DN_NETRequest *request_ptr = emc->response_list;
DN_OSSemaphoreWaitResult net_wait_result = DN_OS_SemaphoreWait(&request_ptr->completion_sem, 0 /*timeout_ms*/);
DN_AssertF(net_wait_result == DN_OSSemaphoreWaitResult_Success, "Wait result was: %zu", DN_Cast(DN_USize) net_wait_result);
DN_NETRequestHandle request = {};
request.handle = DN_Cast(DN_UPtr) request_ptr;
request.gen = request_ptr->gen;
result = DN_NET_EmcHandleFinishedRequest_(net, emc, request, request_ptr, arena);
return result;
}
-17
View File
@@ -1,17 +0,0 @@
#if !defined(DN_NET_EMSCRIPTEN_H)
#define DN_NET_EMSCRIPTEN_H
#if defined(_CLANGD)
#include "dn_net.h"
#endif
DN_NETInterface DN_NET_EmcInterface();
void DN_NET_EmcInit (DN_NETCore *net, char *base, DN_U64 base_size);
void DN_NET_EmcDeinit (DN_NETCore *net);
DN_NETRequestHandle DN_NET_EmcDoHTTP (DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args);
DN_NETRequestHandle DN_NET_EmcDoWS (DN_NETCore *net, DN_Str8 url);
void DN_NET_EmcDoWSSend (DN_NETRequestHandle handle, DN_Str8 data, DN_NETWSSend send);
DN_NETResponse DN_NET_EmcWaitForResponse (DN_NETRequestHandle handle, DN_Arena *arena, DN_U32 timeout_ms);
DN_NETResponse DN_NET_EmcWaitForAnyResponse(DN_NETCore *net, DN_Arena *arena, DN_U32 timeout_ms);
#endif // DN_NET_EMSCRIPTEN_H
+71 -224
View File
@@ -1,8 +1,6 @@
#if defined(_CLANGD)
#define DN_H_WITH_OS 1
#include "../dn.h"
#include "../Extra/dn_net.h"
#include "../Extra/dn_net_curl.h"
#define DN_WITH_OS 1
#include "dn.h"
#include "../Standalone/dn_utest.h"
#define DN_UNIT_TESTS_WITH_KECCAK
@@ -321,13 +319,13 @@ static DN_UTCore DN_TST_Base()
for (DN_UT_Test(&result, "Age from U64: 1001 converts to 1s 1ms (seconds and ms)")) {
DN_Str8x128 str8 = DN_AgeStr8FromMsU64(1001, DN_AgeUnit_Sec | DN_AgeUnit_Ms);
DN_Str8 expect = DN_Str8Lit("1s 1ms");
DN_UT_AssertF(&result, DN_MemEq(str8.data, str8.size, expect.data, expect.size), "str8=%.*s, expect=%.*s", DN_Str8PrintFmt(str8), DN_Str8PrintFmt(expect));
DN_UT_AssertF(&result, DN_MemEq(str8.data, str8.count, expect.data, expect.count), "str8=%.*s, expect=%.*s", DN_Str8PrintFmt(str8), DN_Str8PrintFmt(expect));
}
for (DN_UT_Test(&result, "Age from U64: 1001 converts to 1.001s (fractional)")) {
DN_Str8x128 str8 = DN_AgeStr8FromMsU64(1001, DN_AgeUnit_FractionalSec);
DN_Str8 expect = DN_Str8Lit("1.001s");
DN_UT_AssertF(&result, DN_MemEq(str8.data, str8.size, expect.data, expect.size), "str8=%.*s, expect=%.*s", DN_Str8PrintFmt(str8), DN_Str8PrintFmt(expect));
DN_UT_AssertF(&result, DN_MemEq(str8.data, str8.count, expect.data, expect.count), "str8=%.*s, expect=%.*s", DN_Str8PrintFmt(str8), DN_Str8PrintFmt(expect));
}
for (DN_UT_Test(&result, "FmtAppendTruncate: String truncates with 3 dots")) {
@@ -667,12 +665,12 @@ static DN_UTCore DN_TST_BaseBytesHex()
DN_UT_AssertF(&test,
DN_Str8Eq(bytes, DN_Str8Lit("\xf6\xed\x00")),
"number_hex=%.*s",
DN_Str8PrintFmt(DN_HexFromPtrBytesArena(bytes.data, bytes.size, &scratch.arena, DN_TrimLeadingZero_No)));
DN_Str8PrintFmt(DN_HexFromPtrBytesArena(bytes.data, bytes.count, &scratch.arena, DN_TrimLeadingZero_No)));
}
for (DN_UT_Test(&test, "Convert empty bytes to string", number)) {
DN_Str8 bytes = DN_Str8Lit("");
DN_Str8 as_hex = DN_HexFromPtrBytesArena(bytes.data, bytes.size, &scratch.arena, DN_TrimLeadingZero_No);
DN_Str8 as_hex = DN_HexFromPtrBytesArena(bytes.data, bytes.count, &scratch.arena, DN_TrimLeadingZero_No);
DN_UT_AssertF(&test, DN_Str8Eq(as_hex, DN_Str8Lit("")), "as_hex=%.*s", DN_Str8PrintFmt(as_hex));
}
}
@@ -1222,7 +1220,7 @@ static DN_UTCore DN_TST_BaseArray()
DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 3, 2, DN_ArrayErase_Stable);
int expected[] = {0, 1, 2, 5, 6, 7, 8, 9};
DN_UT_Assert(&result, erase.items_erased == 2);
DN_UT_Assert(&result, erase.it_index == 3);
DN_UT_Assert(&result, erase.it_index == 2);
DN_UT_Assert(&result, size == 8);
DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0);
}
@@ -1231,9 +1229,9 @@ static DN_UTCore DN_TST_BaseArray()
int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
DN_USize size = 10;
DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 5, -3, DN_ArrayErase_Stable);
int expected[] = {0, 1, 2, 6, 7, 8, 9};
int expected[] = {0, 1, 5, 6, 7, 8, 9};
DN_UT_Assert(&result, erase.items_erased == 3);
DN_UT_Assert(&result, erase.it_index == 3);
DN_UT_Assert(&result, erase.it_index == 1);
DN_UT_Assert(&result, size == 7);
DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0);
}
@@ -1242,9 +1240,9 @@ static DN_UTCore DN_TST_BaseArray()
int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
DN_USize size = 10;
DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 5, -1, DN_ArrayErase_Stable);
int expected[] = {0, 1, 2, 3, 4, 6, 7, 8, 9};
int expected[] = {0, 1, 2, 3, 5, 6, 7, 8, 9};
DN_UT_Assert(&result, erase.items_erased == 1);
DN_UT_Assert(&result, erase.it_index == 5);
DN_UT_AssertF(&result, erase.it_index == 3, "lhs=%zu", erase.it_index);
DN_UT_Assert(&result, size == 9);
DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0);
}
@@ -1255,7 +1253,7 @@ static DN_UTCore DN_TST_BaseArray()
DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 3, 2, DN_ArrayErase_Unstable);
int expected[] = {0, 1, 2, 8, 9, 5, 6, 7};
DN_UT_Assert(&result, erase.items_erased == 2);
DN_UT_Assert(&result, erase.it_index == 3);
DN_UT_Assert(&result, erase.it_index == 2);
DN_UT_Assert(&result, size == 8);
DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0);
}
@@ -1264,9 +1262,9 @@ static DN_UTCore DN_TST_BaseArray()
int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
DN_USize size = 10;
DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 5, -3, DN_ArrayErase_Unstable);
int expected[] = {0, 1, 2, 7, 8, 9, 6};
int expected[] = {0, 1, 7, 8, 9, 5, 6};
DN_UT_Assert(&result, erase.items_erased == 3);
DN_UT_Assert(&result, erase.it_index == 3);
DN_UT_Assert(&result, erase.it_index == 1);
DN_UT_Assert(&result, size == 7);
DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0);
}
@@ -1275,10 +1273,10 @@ static DN_UTCore DN_TST_BaseArray()
int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
DN_USize size = 10;
DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 0, -2, DN_ArrayErase_Stable);
int expected[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
DN_UT_Assert(&result, erase.items_erased == 1);
int expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
DN_UT_Assert(&result, erase.items_erased == 0);
DN_UT_Assert(&result, erase.it_index == 0);
DN_UT_Assert(&result, size == 9);
DN_UT_Assert(&result, size == 10);
DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0);
}
@@ -1299,7 +1297,7 @@ static DN_UTCore DN_TST_BaseArray()
DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 5, 0, DN_ArrayErase_Stable);
int expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
DN_UT_Assert(&result, erase.items_erased == 0);
DN_UT_Assert(&result, erase.it_index == 0);
DN_UT_Assert(&result, erase.it_index == 5);
DN_UT_Assert(&result, size == 10);
DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0);
}
@@ -1308,7 +1306,7 @@ static DN_UTCore DN_TST_BaseArray()
DN_USize size = 10;
DN_ArrayEraseResult erase = DN_ArrayEraseRange(nullptr, &size, sizeof(int), 5, 2, DN_ArrayErase_Stable);
DN_UT_Assert(&result, erase.items_erased == 0);
DN_UT_Assert(&result, erase.it_index == 0);
DN_UT_Assert(&result, erase.it_index == 5);
DN_UT_Assert(&result, size == 10);
}
@@ -1316,7 +1314,7 @@ static DN_UTCore DN_TST_BaseArray()
int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, NULL, sizeof(arr[0]), 5, 2, DN_ArrayErase_Stable);
DN_UT_Assert(&result, erase.items_erased == 0);
DN_UT_Assert(&result, erase.it_index == 0);
DN_UT_Assert(&result, erase.it_index == 5);
}
for (DN_UT_Test(&result, "Invalid input - empty array")) {
@@ -1324,7 +1322,7 @@ static DN_UTCore DN_TST_BaseArray()
DN_USize size = 0;
DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 5, 2, DN_ArrayErase_Stable);
DN_UT_Assert(&result, erase.items_erased == 0);
DN_UT_Assert(&result, erase.it_index == 0);
DN_UT_Assert(&result, erase.it_index == 5);
DN_UT_Assert(&result, size == 0);
}
@@ -1334,7 +1332,7 @@ static DN_UTCore DN_TST_BaseArray()
DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 15, 2, DN_ArrayErase_Stable);
int expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
DN_UT_Assert(&result, erase.items_erased == 0);
DN_UT_Assert(&result, erase.it_index == 10);
DN_UT_Assert(&result, erase.it_index == 9);
DN_UT_Assert(&result, size == 10);
DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0);
}
@@ -1342,156 +1340,6 @@ static DN_UTCore DN_TST_BaseArray()
return result;
}
static DN_UTCore DN_TST_BaseVArray()
{
DN_UTCore result = DN_UT_Init();
DN_UT_LogF(&result, "DN_VArray\n");
{
{
DN_VArray<DN_U32> array = DN_OS_VArrayInitByteSize<DN_U32>(DN_Kilobytes(64));
DN_DEFER
{
DN_OS_VArrayDeinit(&array);
};
for (DN_UT_Test(&result, "Test adding an array of items to the array")) {
DN_U32 array_literal[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
DN_OS_VArrayAddArray<DN_U32>(&array, array_literal, DN_ArrayCountU(array_literal));
DN_UT_Assert(&result, array.size == DN_ArrayCountU(array_literal));
DN_UT_Assert(&result, DN_Memcmp(array.data, array_literal, DN_ArrayCountU(array_literal) * sizeof(array_literal[0])) == 0);
}
for (DN_UT_Test(&result, "Test stable erase, 1 item, the '2' value from the array")) {
DN_OS_VArrayEraseRange(&array, 2 /*begin_index*/, 1 /*count*/, DN_ArrayErase_Stable);
DN_U32 array_literal[] = {0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
DN_UT_Assert(&result, array.size == DN_ArrayCountU(array_literal));
DN_UT_Assert(&result, DN_Memcmp(array.data, array_literal, DN_ArrayCountU(array_literal) * sizeof(array_literal[0])) == 0);
}
for (DN_UT_Test(&result, "Test unstable erase, 1 item, the '1' value from the array")) {
DN_OS_VArrayEraseRange(&array, 1 /*begin_index*/, 1 /*count*/, DN_ArrayErase_Unstable);
DN_U32 array_literal[] = {0, 15, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
DN_UT_Assert(&result, array.size == DN_ArrayCountU(array_literal));
DN_UT_Assert(&result, DN_Memcmp(array.data, array_literal, DN_ArrayCountU(array_literal) * sizeof(array_literal[0])) == 0);
}
DN_ArrayErase erase_enums[] = {DN_ArrayErase_Stable, DN_ArrayErase_Unstable};
for (DN_UT_Test(&result, "Test un/stable erase, OOB")) {
for (DN_ArrayErase erase : erase_enums) {
DN_U32 array_literal[] = {0, 15, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
DN_OS_VArrayEraseRange(&array, DN_ArrayCountU(array_literal) /*begin_index*/, DN_ArrayCountU(array_literal) + 100 /*count*/, erase);
DN_UT_Assert(&result, array.size == DN_ArrayCountU(array_literal));
DN_UT_Assert(&result, DN_Memcmp(array.data, array_literal, DN_ArrayCountU(array_literal) * sizeof(array_literal[0])) == 0);
}
}
for (DN_UT_Test(&result, "Test flipped begin/end index stable erase, 2 items, the '15, 3' value from the array")) {
DN_OS_VArrayEraseRange(&array, 2 /*begin_index*/, -2 /*count*/, DN_ArrayErase_Stable);
DN_U32 array_literal[] = {0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
DN_UT_Assert(&result, array.size == DN_ArrayCountU(array_literal));
DN_UT_Assert(&result, DN_Memcmp(array.data, array_literal, DN_ArrayCountU(array_literal) * sizeof(array_literal[0])) == 0);
}
for (DN_UT_Test(&result, "Test flipped begin/end index unstable erase, 2 items, the '4, 5' value from the array")) {
DN_OS_VArrayEraseRange(&array, 2 /*begin_index*/, -2 /*count*/, DN_ArrayErase_Unstable);
DN_U32 array_literal[] = {0, 13, 14, 6, 7, 8, 9, 10, 11, 12};
DN_UT_Assert(&result, array.size == DN_ArrayCountU(array_literal));
DN_UT_Assert(&result, DN_Memcmp(array.data, array_literal, DN_ArrayCountU(array_literal) * sizeof(array_literal[0])) == 0);
}
for (DN_UT_Test(&result, "Test stable erase range, 2+1 (oob) item, the '13, 14, +1 OOB' value from the array")) {
DN_OS_VArrayEraseRange(&array, 8 /*begin_index*/, 3 /*count*/, DN_ArrayErase_Stable);
DN_U32 array_literal[] = {0, 13, 14, 6, 7, 8, 9, 10};
DN_UT_Assert(&result, array.size == DN_ArrayCountU(array_literal));
DN_UT_Assert(&result, DN_Memcmp(array.data, array_literal, DN_ArrayCountU(array_literal) * sizeof(array_literal[0])) == 0);
}
for (DN_UT_Test(&result, "Test unstable erase range, 3+1 (oob) item, the '11, 12, +1 OOB' value from the array")) {
DN_OS_VArrayEraseRange(&array, 6 /*begin_index*/, 3 /*count*/, DN_ArrayErase_Unstable);
DN_U32 array_literal[] = {0, 13, 14, 6, 7, 8};
DN_UT_Assert(&result, array.size == DN_ArrayCountU(array_literal));
DN_UT_Assert(&result, DN_Memcmp(array.data, array_literal, DN_ArrayCountU(array_literal) * sizeof(array_literal[0])) == 0);
}
for (DN_UT_Test(&result, "Test stable erase -overflow OOB, erasing the '0, 13' value from the array")) {
DN_OS_VArrayEraseRange(&array, 1 /*begin_index*/, -DN_ISIZE_MAX /*count*/, DN_ArrayErase_Stable);
DN_U32 array_literal[] = {14, 6, 7, 8};
DN_UT_Assert(&result, array.size == DN_ArrayCountU(array_literal));
DN_UT_Assert(&result, DN_Memcmp(array.data, array_literal, DN_ArrayCountU(array_literal) * sizeof(array_literal[0])) == 0);
}
for (DN_UT_Test(&result, "Test unstable erase +overflow OOB, erasing the '7, 8' value from the array")) {
DN_OS_VArrayEraseRange(&array, 2 /*begin_index*/, DN_ISIZE_MAX /*count*/, DN_ArrayErase_Unstable);
DN_U32 array_literal[] = {14, 6};
DN_UT_Assert(&result, array.size == DN_ArrayCountU(array_literal));
DN_UT_Assert(&result, DN_Memcmp(array.data, array_literal, DN_ArrayCountU(array_literal) * sizeof(array_literal[0])) == 0);
}
for (DN_UT_Test(&result, "Test adding an array of items after erase")) {
DN_U32 array_literal[] = {0, 1, 2, 3};
DN_OS_VArrayAddArray<DN_U32>(&array, array_literal, DN_ArrayCountU(array_literal));
DN_U32 expected_literal[] = {14, 6, 0, 1, 2, 3};
DN_UT_Assert(&result, array.size == DN_ArrayCountU(expected_literal));
DN_UT_Assert(&result, DN_Memcmp(array.data, expected_literal, DN_ArrayCountU(expected_literal) * sizeof(expected_literal[0])) == 0);
}
}
for (DN_UT_Test(&result, "Array of unaligned objects are contiguously laid out in memory")) {
// NOTE: Since we allocate from a virtual memory block, each time
// we request memory from the block we can demand some alignment
// on the returned pointer from the memory block. If there's
// additional alignment done in that function then we can no
// longer access the items in the array contiguously leading to
// confusing memory "corruption" errors.
//
// This result makes sure that the unaligned objects are allocated
// from the memory block (and hence the array) contiguously
// when the size of the object is not aligned with the required
// alignment of the object.
DN_MSVC_WARNING_PUSH
DN_MSVC_WARNING_DISABLE(4324) // warning C4324: 'TestVArray::UnalignedObject': structure was padded due to alignment specifier
struct alignas(8) UnalignedObject
{
char data[511];
};
DN_MSVC_WARNING_POP
DN_VArray<UnalignedObject> array = DN_OS_VArrayInitByteSize<UnalignedObject>(DN_Kilobytes(64));
DN_DEFER
{
DN_OS_VArrayDeinit(&array);
};
// NOTE: Verify that the items returned from the data array are
// contiguous in memory.
UnalignedObject *make_item_a = DN_OS_VArrayMakeArray(&array, 1, DN_ZMem_Yes);
UnalignedObject *make_item_b = DN_OS_VArrayMakeArray(&array, 1, DN_ZMem_Yes);
DN_Memset(make_item_a->data, 'a', sizeof(make_item_a->data));
DN_Memset(make_item_b->data, 'b', sizeof(make_item_b->data));
DN_UT_Assert(&result, (uintptr_t)make_item_b == (uintptr_t)(make_item_a + 1));
// NOTE: Verify that accessing the items from the data array yield
// the same object.
DN_UT_Assert(&result, array.size == 2);
UnalignedObject *data_item_a = array.data + 0;
UnalignedObject *data_item_b = array.data + 1;
DN_UT_Assert(&result, (uintptr_t)data_item_b == (uintptr_t)(data_item_a + 1));
DN_UT_Assert(&result, (uintptr_t)data_item_b == (uintptr_t)(make_item_a + 1));
DN_UT_Assert(&result, (uintptr_t)data_item_b == (uintptr_t)make_item_b);
for (DN_USize i = 0; i < sizeof(data_item_a->data); i++)
DN_UT_Assert(&result, data_item_a->data[i] == 'a');
for (DN_USize i = 0; i < sizeof(data_item_b->data); i++)
DN_UT_Assert(&result, data_item_b->data[i] == 'b');
}
}
return result;
}
#if defined(DN_UNIT_TESTS_WITH_KECCAK)
DN_GCC_WARNING_PUSH
DN_GCC_WARNING_DISABLE(-Wunused-parameter)
@@ -1693,13 +1541,13 @@ DN_Str8 const DN_UT_HASH_STRING_[] =
void DN_TST_KeccakDispatch_(DN_UTCore *test, int hash_type, DN_Str8 input)
{
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
DN_Str8 input_hex = DN_HexFromPtrBytesArena(input.data, input.size, &scratch.arena, DN_TrimLeadingZero_No);
DN_Str8 input_hex = DN_HexFromPtrBytesArena(input.data, input.count, &scratch.arena, DN_TrimLeadingZero_No);
switch (hash_type) {
case Hash_SHA3_224: {
DN_SHA3U8x28 hash = DN_SHA3Hash224b(input.data, input.size);
DN_SHA3U8x28 hash = DN_SHA3Hash224b(input.data, input.count);
DN_SHA3U8x28 expect;
DN_RefImpl_FIPS202_SHA3_224_(DN_Cast(uint8_t *) input.data, input.size, (uint8_t *)expect.data);
DN_RefImpl_FIPS202_SHA3_224_(DN_Cast(uint8_t *) input.data, input.count, (uint8_t *)expect.data);
DN_Str8 hash_hex = DN_HexFromPtrBytesArena(hash.data, DN_ArrayCountU(hash.data), &scratch.arena, DN_TrimLeadingZero_No);
DN_Str8 expect_hex = DN_HexFromPtrBytesArena(expect.data, DN_ArrayCountU(expect.data), &scratch.arena, DN_TrimLeadingZero_No);
@@ -1714,9 +1562,9 @@ void DN_TST_KeccakDispatch_(DN_UTCore *test, int hash_type, DN_Str8 input)
} break;
case Hash_SHA3_256: {
DN_SHA3U8x32 hash = DN_SHA3Hash256b(input.data, input.size);
DN_SHA3U8x32 hash = DN_SHA3Hash256b(input.data, input.count);
DN_SHA3U8x32 expect;
DN_RefImpl_FIPS202_SHA3_256_(DN_Cast(uint8_t *) input.data, input.size, (uint8_t *)expect.data);
DN_RefImpl_FIPS202_SHA3_256_(DN_Cast(uint8_t *) input.data, input.count, (uint8_t *)expect.data);
DN_Str8 hash_hex = DN_HexFromPtrBytesArena(hash.data, DN_ArrayCountU(hash.data), &scratch.arena, DN_TrimLeadingZero_No);
DN_Str8 expect_hex = DN_HexFromPtrBytesArena(expect.data, DN_ArrayCountU(expect.data), &scratch.arena, DN_TrimLeadingZero_No);
@@ -1731,9 +1579,9 @@ void DN_TST_KeccakDispatch_(DN_UTCore *test, int hash_type, DN_Str8 input)
} break;
case Hash_SHA3_384: {
DN_SHA3U8x48 hash = DN_SHA3Hash384b(input.data, input.size);
DN_SHA3U8x48 hash = DN_SHA3Hash384b(input.data, input.count);
DN_SHA3U8x48 expect;
DN_RefImpl_FIPS202_SHA3_384_(DN_Cast(uint8_t *) input.data, input.size, (uint8_t *)expect.data);
DN_RefImpl_FIPS202_SHA3_384_(DN_Cast(uint8_t *) input.data, input.count, (uint8_t *)expect.data);
DN_Str8 hash_hex = DN_HexFromPtrBytesArena(hash.data, DN_ArrayCountU(hash.data), &scratch.arena, DN_TrimLeadingZero_No);
DN_Str8 expect_hex = DN_HexFromPtrBytesArena(expect.data, DN_ArrayCountU(expect.data), &scratch.arena, DN_TrimLeadingZero_No);
@@ -1748,9 +1596,9 @@ void DN_TST_KeccakDispatch_(DN_UTCore *test, int hash_type, DN_Str8 input)
} break;
case Hash_SHA3_512: {
DN_SHA3U8x64 hash = DN_SHA3Hash512b(input.data, input.size);
DN_SHA3U8x64 hash = DN_SHA3Hash512b(input.data, input.count);
DN_SHA3U8x64 expect;
DN_RefImpl_FIPS202_SHA3_512_(DN_Cast(uint8_t *) input.data, input.size, (uint8_t *)expect.data);
DN_RefImpl_FIPS202_SHA3_512_(DN_Cast(uint8_t *) input.data, input.count, (uint8_t *)expect.data);
DN_Str8 hash_hex = DN_HexFromPtrBytesArena(hash.data, DN_ArrayCountU(hash.data), &scratch.arena, DN_TrimLeadingZero_No);
DN_Str8 expect_hex = DN_HexFromPtrBytesArena(expect.data, DN_ArrayCountU(expect.data), &scratch.arena, DN_TrimLeadingZero_No);
@@ -1765,9 +1613,9 @@ void DN_TST_KeccakDispatch_(DN_UTCore *test, int hash_type, DN_Str8 input)
} break;
case Hash_Keccak_224: {
DN_SHA3U8x28 hash = DN_KeccakHash224b(input.data, input.size);
DN_SHA3U8x28 hash = DN_KeccakHash224b(input.data, input.count);
DN_SHA3U8x28 expect;
DN_RefImpl_Keccak_(1152, 448, DN_Cast(uint8_t *) input.data, input.size, 0x01, (uint8_t *)expect.data, sizeof(expect));
DN_RefImpl_Keccak_(1152, 448, DN_Cast(uint8_t *) input.data, input.count, 0x01, (uint8_t *)expect.data, sizeof(expect));
DN_Str8 hash_hex = DN_HexFromPtrBytesArena(hash.data, DN_ArrayCountU(hash.data), &scratch.arena, DN_TrimLeadingZero_No);
DN_Str8 expect_hex = DN_HexFromPtrBytesArena(expect.data, DN_ArrayCountU(expect.data), &scratch.arena, DN_TrimLeadingZero_No);
@@ -1782,9 +1630,9 @@ void DN_TST_KeccakDispatch_(DN_UTCore *test, int hash_type, DN_Str8 input)
} break;
case Hash_Keccak_256: {
DN_SHA3U8x32 hash = DN_KeccakHash256b(input.data, input.size);
DN_SHA3U8x32 hash = DN_KeccakHash256b(input.data, input.count);
DN_SHA3U8x32 expect;
DN_RefImpl_Keccak_(1088, 512, DN_Cast(uint8_t *) input.data, input.size, 0x01, (uint8_t *)expect.data, sizeof(expect));
DN_RefImpl_Keccak_(1088, 512, DN_Cast(uint8_t *) input.data, input.count, 0x01, (uint8_t *)expect.data, sizeof(expect));
DN_Str8 hash_hex = DN_HexFromPtrBytesArena(hash.data, DN_ArrayCountU(hash.data), &scratch.arena, DN_TrimLeadingZero_No);
DN_Str8 expect_hex = DN_HexFromPtrBytesArena(expect.data, DN_ArrayCountU(expect.data), &scratch.arena, DN_TrimLeadingZero_No);
@@ -1799,9 +1647,9 @@ void DN_TST_KeccakDispatch_(DN_UTCore *test, int hash_type, DN_Str8 input)
} break;
case Hash_Keccak_384: {
DN_SHA3U8x48 hash = DN_KeccakHash384b(input.data, input.size);
DN_SHA3U8x48 hash = DN_KeccakHash384b(input.data, input.count);
DN_SHA3U8x48 expect;
DN_RefImpl_Keccak_(832, 768, DN_Cast(uint8_t *) input.data, input.size, 0x01, (uint8_t *)expect.data, sizeof(expect));
DN_RefImpl_Keccak_(832, 768, DN_Cast(uint8_t *) input.data, input.count, 0x01, (uint8_t *)expect.data, sizeof(expect));
DN_Str8 hash_hex = DN_HexFromPtrBytesArena(hash.data, DN_ArrayCountU(hash.data), &scratch.arena, DN_TrimLeadingZero_No);
DN_Str8 expect_hex = DN_HexFromPtrBytesArena(expect.data, DN_ArrayCountU(expect.data), &scratch.arena, DN_TrimLeadingZero_No);
@@ -1816,9 +1664,9 @@ void DN_TST_KeccakDispatch_(DN_UTCore *test, int hash_type, DN_Str8 input)
} break;
case Hash_Keccak_512: {
DN_SHA3U8x64 hash = DN_KeccakHash512b(input.data, input.size);
DN_SHA3U8x64 hash = DN_KeccakHash512b(input.data, input.count);
DN_SHA3U8x64 expect;
DN_RefImpl_Keccak_(576, 1024, DN_Cast(uint8_t *) input.data, input.size, 0x01, (uint8_t *)expect.data, sizeof(expect));
DN_RefImpl_Keccak_(576, 1024, DN_Cast(uint8_t *) input.data, input.count, 0x01, (uint8_t *)expect.data, sizeof(expect));
DN_Str8 hash_hex = DN_HexFromPtrBytesArena(hash.data, DN_ArrayCountU(hash.data), &scratch.arena, DN_TrimLeadingZero_No);
DN_Str8 expect_hex = DN_HexFromPtrBytesArena(expect.data, DN_ArrayCountU(expect.data), &scratch.arena, DN_TrimLeadingZero_No);
@@ -1853,7 +1701,7 @@ DN_UTCore DN_TST_Keccak()
for (int hash_type = 0; hash_type < Hash_Count; hash_type++) {
DN_PCG32 rng = DN_PCG32Init(0xd48e'be21'2af8'733d);
for (DN_Str8 input : INPUTS) {
DN_UT_BeginF(&result, "%.*s - Input: %.*s", DN_Str8PrintFmt(DN_UT_HASH_STRING_[hash_type]), DN_Cast(int) DN_Min(input.size, 54), input.data);
DN_UT_BeginF(&result, "%.*s - Input: %.*s", DN_Str8PrintFmt(DN_UT_HASH_STRING_[hash_type]), DN_Cast(int) DN_Min(input.count, 54), input.data);
DN_TST_KeccakDispatch_(&result, hash_type, input);
DN_UT_End(&result);
}
@@ -1922,8 +1770,8 @@ static DN_UTCore DN_TST_OS()
for (DN_UT_Test(&result, "Query executable directory")) {
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
DN_Str8 os_result = DN_OS_EXEDir(&scratch.arena);
DN_UT_Assert(&result, os_result.size);
DN_UT_AssertF(&result, DN_OS_PathIsDir(os_result), "result(%zu): %.*s", os_result.size, DN_Str8PrintFmt(os_result));
DN_UT_Assert(&result, os_result.count);
DN_UT_AssertF(&result, DN_OS_PathIsDir(os_result), "result(%zu): %.*s", os_result.count, DN_Str8PrintFmt(os_result));
DN_TCScratchEnd(&scratch);
}
@@ -1973,9 +1821,9 @@ static DN_UTCore DN_TST_OS()
// NOTE: Read step
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
DN_Str8 read_file = DN_OS_FileReadAllArena(&scratch.arena, SRC_FILE, nullptr);
DN_UT_AssertF(&result, read_file.size, "Failed to load file");
DN_UT_AssertF(&result, read_file.size == 4, "File read wrong amount of bytes (%zu)", read_file.size);
DN_UT_AssertF(&result, DN_Str8Eq(read_file, DN_Str8Lit("1234")), "Read %zu bytes instead of the expected 4: '%.*s'", read_file.size, DN_Str8PrintFmt(read_file));
DN_UT_AssertF(&result, read_file.count, "Failed to load file");
DN_UT_AssertF(&result, read_file.count == 4, "File read wrong amount of bytes (%zu)", read_file.count);
DN_UT_AssertF(&result, DN_Str8Eq(read_file, DN_Str8Lit("1234")), "Read %zu bytes instead of the expected 4: '%.*s'", read_file.count, DN_Str8PrintFmt(read_file));
// NOTE: Copy step
DN_Str8 const COPY_FILE = DN_Str8Lit("dn_result_file_copy");
@@ -2191,13 +2039,13 @@ static DN_UTCore DN_TST_BaseStrings()
{
for (DN_UT_Test(&result, "Str8 literal")) {
DN_Str8 string = DN_Str8Lit("AB");
DN_UT_AssertF(&result, string.size == 2, "size: %zu", string.size);
DN_UT_AssertF(&result, string.count == 2, "size: %zu", string.count);
DN_UT_AssertF(&result, string.data[0] == 'A', "string[0]: %c", string.data[0]);
DN_UT_AssertF(&result, string.data[1] == 'B', "string[1]: %c", string.data[1]);
}
for (DN_UT_Test(&result, "C-string length")) {
DN_USize size = DN_CStr8Size("hello");
DN_USize size = DN_CStr8Count("hello");
DN_UT_AssertF(&result, size == 5, "size=%zu", size);
}
@@ -2228,7 +2076,7 @@ static DN_UTCore DN_TST_BaseStrings()
for (DN_UT_Test(&result, "Initialise with format string")) {
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
DN_Str8 string = DN_Str8FromFmtArena(&scratch.arena, "%s", "AB");
DN_UT_AssertF(&result, string.size == 2, "size: %zu", string.size);
DN_UT_AssertF(&result, string.count == 2, "size: %zu", string.count);
DN_UT_AssertF(&result, string.data[0] == 'A', "string[0]: %c", string.data[0]);
DN_UT_AssertF(&result, string.data[1] == 'B', "string[1]: %c", string.data[1]);
DN_UT_AssertF(&result, string.data[2] == 0, "string[2]: %c", string.data[2]);
@@ -2239,7 +2087,7 @@ static DN_UTCore DN_TST_BaseStrings()
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
DN_Str8 string = DN_Str8Lit("AB");
DN_Str8 copy = DN_Str8FromStr8Arena(string, &scratch.arena);
DN_UT_AssertF(&result, copy.size == 2, "size: %zu", copy.size);
DN_UT_AssertF(&result, copy.count == 2, "size: %zu", copy.count);
DN_UT_AssertF(&result, copy.data[0] == 'A', "copy[0]: %c", copy.data[0]);
DN_UT_AssertF(&result, copy.data[1] == 'B', "copy[1]: %c", copy.data[1]);
DN_UT_AssertF(&result, copy.data[2] == 0, "copy[2]: %c", copy.data[2]);
@@ -2254,7 +2102,7 @@ static DN_UTCore DN_TST_BaseStrings()
for (DN_UT_Test(&result, "Allocate string from arena")) {
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
DN_Str8 string = DN_Str8AllocArena(2, DN_ZMem_No, &scratch.arena);
DN_UT_AssertF(&result, string.size == 2, "size: %zu", string.size);
DN_UT_AssertF(&result, string.count == 2, "size: %zu", string.count);
DN_TCScratchEnd(&scratch);
}
@@ -2454,7 +2302,7 @@ static DN_UTCore DN_TST_BaseStrings()
DN_UT_Assert(&result, !str_result.found);
DN_UT_Assert(&result, str_result.index == 0);
DN_UT_Assert(&result, str_result.match.data == nullptr);
DN_UT_Assert(&result, str_result.match.size == 0);
DN_UT_Assert(&result, str_result.match.count == 0);
}
for (DN_UT_Test(&result, "Find: String (char) is in buffer")) {
@@ -2495,7 +2343,7 @@ static DN_UTCore DN_TST_BaseStrings()
char dest[64] = {};
DN_Str8TruncResult res = DN_Str8TruncMiddlePtr(str, 5, trunc, dest, sizeof(dest));
DN_UT_Assert(&result, !res.truncated);
DN_UT_Assert(&result, res.size_req == 5);
DN_UT_Assert(&result, res.count_req == 5);
DN_UT_AssertF(&result, DN_Str8Eq(res.str8, DN_Str8Lit("Hello")), "%.*s", DN_Str8PrintFmt(res.str8));
}
@@ -2505,7 +2353,7 @@ static DN_UTCore DN_TST_BaseStrings()
char dest[64] = {};
DN_Str8TruncResult res = DN_Str8TruncMiddlePtr(str, 5, trunc, dest, sizeof(dest));
DN_UT_Assert(&result, !res.truncated);
DN_UT_Assert(&result, res.size_req == 10);
DN_UT_Assert(&result, res.count_req == 10);
DN_UT_AssertF(&result, DN_Str8Eq(res.str8, DN_Str8Lit("HelloWorld")), "%.*s", DN_Str8PrintFmt(res.str8));
}
@@ -2515,7 +2363,7 @@ static DN_UTCore DN_TST_BaseStrings()
char dest[64] = {};
DN_Str8TruncResult res = DN_Str8TruncMiddlePtr(str, 5, trunc, dest, sizeof(dest));
DN_UT_Assert(&result, res.truncated);
DN_UT_Assert(&result, res.size_req == 13); // 5 + 3 + 5
DN_UT_Assert(&result, res.count_req == 13); // 5 + 3 + 5
DN_UT_AssertF(&result, DN_Str8Eq(res.str8, DN_Str8Lit("Hello...World")), "%.*s", DN_Str8PrintFmt(res.str8));
}
@@ -2525,7 +2373,7 @@ static DN_UTCore DN_TST_BaseStrings()
char dest[64] = {};
DN_Str8TruncResult res = DN_Str8TruncMiddlePtr(str, 5, trunc, dest, sizeof(dest));
DN_UT_Assert(&result, res.truncated);
DN_UT_Assert(&result, res.size_req == 10); // 5 + 0 + 5
DN_UT_Assert(&result, res.count_req == 10); // 5 + 0 + 5
DN_UT_AssertF(&result, DN_Str8Eq(res.str8, DN_Str8Lit("HelloWorld")), "%.*s", DN_Str8PrintFmt(res.str8));
}
@@ -2535,7 +2383,7 @@ static DN_UTCore DN_TST_BaseStrings()
char dest[64] = {};
DN_Str8TruncResult res = DN_Str8TruncMiddlePtr(str, 0, trunc, dest, sizeof(dest));
DN_UT_Assert(&result, res.truncated);
DN_UT_Assert(&result, res.size_req == 3);
DN_UT_Assert(&result, res.count_req == 3);
DN_UT_AssertF(&result, DN_Str8Eq(res.str8, DN_Str8Lit("...")), "%.*s", DN_Str8PrintFmt(res.str8));
}
@@ -2544,17 +2392,17 @@ static DN_UTCore DN_TST_BaseStrings()
DN_Str8 trunc = DN_Str8Lit("...");
DN_Str8TruncResult res = DN_Str8TruncMiddlePtr(str, 5, trunc, nullptr, 0);
DN_UT_Assert(&result, res.truncated);
DN_UT_Assert(&result, res.size_req == 13);
DN_UT_Assert(&result, res.count_req == 13);
DN_UT_Assert(&result, res.str8.data == nullptr);
}
for (DN_UT_Test(&result, "TruncMiddlePtr: size_req is consistent between dry-run and actual")) {
for (DN_UT_Test(&result, "TruncMiddlePtr: count_req is consistent between dry-run and actual")) {
DN_Str8 str = DN_Str8Lit("HelloBeautifulWorld");
DN_Str8 trunc = DN_Str8Lit("...");
DN_Str8TruncResult dry = DN_Str8TruncMiddlePtr(str, 5, trunc, nullptr, 0);
char dest[64] = {};
DN_Str8TruncResult actual = DN_Str8TruncMiddlePtr(str, 5, trunc, dest, sizeof(dest));
DN_UT_Assert(&result, dry.size_req == actual.size_req);
DN_UT_Assert(&result, dry.count_req == actual.count_req);
DN_UT_Assert(&result, dry.truncated == actual.truncated);
}
@@ -2564,7 +2412,7 @@ static DN_UTCore DN_TST_BaseStrings()
char dest[14] = {}; // Exactly 2*5 + 3 + 1
DN_Str8TruncResult res = DN_Str8TruncMiddlePtr(str, 5, trunc, dest, sizeof(dest));
DN_UT_Assert(&result, res.truncated);
DN_UT_Assert(&result, res.size_req == 13);
DN_UT_Assert(&result, res.count_req == 13);
DN_UT_AssertF(&result, DN_Str8Eq(res.str8, DN_Str8Lit("Hello...World")), "%.*s", DN_Str8PrintFmt(res.str8));
}
@@ -2574,7 +2422,7 @@ static DN_UTCore DN_TST_BaseStrings()
char dest[64] = {};
DN_Str8TruncResult res = DN_Str8TruncMiddlePtr(str, 1, trunc, dest, sizeof(dest));
DN_UT_Assert(&result, res.truncated);
DN_UT_Assert(&result, res.size_req == 5); // 1 + 3 + 1
DN_UT_Assert(&result, res.count_req == 5); // 1 + 3 + 1
DN_UT_AssertF(&result, DN_Str8Eq(res.str8, DN_Str8Lit("H...d")), "%.*s", DN_Str8PrintFmt(res.str8));
}
@@ -2584,7 +2432,7 @@ static DN_UTCore DN_TST_BaseStrings()
char dest[64] = {};
DN_Str8TruncResult res = DN_Str8TruncMiddlePtr(str, 100, trunc, dest, sizeof(dest));
DN_UT_Assert(&result, !res.truncated);
DN_UT_Assert(&result, res.size_req == 5);
DN_UT_Assert(&result, res.count_req == 5);
DN_UT_AssertF(&result, DN_Str8Eq(res.str8, DN_Str8Lit("Hello")), "%.*s", DN_Str8PrintFmt(res.str8));
}
@@ -2595,9 +2443,9 @@ static DN_UTCore DN_TST_BaseStrings()
DN_Str8 trunc = DN_Str8Lit("...");
DN_Str8TruncResult res = DN_Str8TruncMiddle(str, 5, trunc, &scratch.arena);
DN_UT_Assert(&result, res.truncated);
DN_UT_Assert(&result, res.size_req == 13);
DN_UT_Assert(&result, res.count_req == 13);
DN_UT_AssertF(&result, DN_Str8Eq(res.str8, DN_Str8Lit("Hello...World")), "%.*s", DN_Str8PrintFmt(res.str8));
DN_UT_Assert(&result, res.str8.data[res.str8.size] == '\0');
DN_UT_Assert(&result, res.str8.data[res.str8.count] == '\0');
DN_TCScratchEnd(&scratch);
}
}
@@ -2645,8 +2493,8 @@ static DN_UTCore DN_TST_Win()
int size_returned = DN_OS_W32Str16ToStr8Buffer(input16, nullptr, 0);
char const EXPECTED[] = {'S', 't', 'r', 'i', 'n', 'g', 0};
DN_UT_AssertF(&result, DN_Cast(int) string8.size == size_returned, "string_size: %d, result: %d", DN_Cast(int) string8.size, size_returned);
DN_UT_AssertF(&result, DN_Cast(int) string8.size == DN_ArrayCountU(EXPECTED) - 1, "string_size: %d, expected: %zu", DN_Cast(int) string8.size, DN_ArrayCountU(EXPECTED) - 1);
DN_UT_AssertF(&result, DN_Cast(int) string8.count == size_returned, "string_size: %d, result: %d", DN_Cast(int) string8.count, size_returned);
DN_UT_AssertF(&result, DN_Cast(int) string8.count == DN_ArrayCountU(EXPECTED) - 1, "string_size: %d, expected: %zu", DN_Cast(int) string8.count, DN_ArrayCountU(EXPECTED) - 1);
DN_UT_Assert(&result, DN_Memcmp(EXPECTED, string8.data, sizeof(EXPECTED)) == 0);
}
DN_TCScratchEnd(&scratch);
@@ -2669,7 +2517,7 @@ static DN_UTCore DN_TST_Net()
label = DN_Str8Lit("CURL");
#endif
if (label.size) {
if (label.count) {
DN_UT_LogF(&result, "DN_NET\n");
DN_MemList mem = DN_MemListFromHeap(DN_Megabytes(4), DN_MemFlags_Nil);
@@ -2688,8 +2536,8 @@ static DN_UTCore DN_TST_Net()
DN_NETResponse response = net_interface.wait_for_response(request, &arena, UINT32_MAX);
DN_UT_AssertF(&result, response.http_status == 200, "http_status=%u", response.http_status);
DN_UT_AssertF(&result, response.state == DN_NETResponseState_HTTP, "state=%u", response.state);
DN_UT_AssertF(&result, response.error_str8.size == 0, "%.*s", DN_Str8PrintFmt(response.error_str8));
DN_UT_Assert(&result, response.body.size);
DN_UT_AssertF(&result, response.error_str8.count == 0, "%.*s", DN_Str8PrintFmt(response.error_str8));
DN_UT_Assert(&result, response.body.count);
}
for (DN_UT_Test(&result, "%.*s WaitForResponse HTTP POST request", DN_Str8PrintFmt(label))) {
@@ -2697,8 +2545,8 @@ static DN_UTCore DN_TST_Net()
DN_NETResponse response = net_interface.wait_for_any_response(&net, &arena, UINT32_MAX);
DN_UT_AssertF(&result, response.http_status == 200, "http_status=%u", response.http_status);
DN_UT_AssertF(&result, response.state == DN_NETResponseState_HTTP, "state=%u", response.state);
DN_UT_AssertF(&result, response.error_str8.size == 0, "error=%.*s", DN_Str8PrintFmt(response.error_str8));
DN_UT_Assert(&result, response.body.size);
DN_UT_AssertF(&result, response.error_str8.count == 0, "error=%.*s", DN_Str8PrintFmt(response.error_str8));
DN_UT_Assert(&result, response.body.count);
}
for (DN_UT_Test(&result, "%.*s WaitForResponse WS request", DN_Str8PrintFmt(label))) {
@@ -2761,7 +2609,6 @@ DN_TSTResult DN_TST_RunSuite(DN_TSTPrint print)
DN_TST_BaseDSMap(),
DN_TST_BaseIArray(),
DN_TST_BaseArray(),
DN_TST_BaseVArray(),
DN_TST_Keccak(),
DN_TST_M4(),
DN_TST_OS(),
+9 -19
View File
@@ -1,31 +1,21 @@
#if defined(DN_UNIT_TESTS_WITH_CURL)
#if DN_WITH_NET_CURL
#define DN_NO_WINDOWS_H_REPLACEMENT_HEADER
#endif
#define DN_ARENA_TEMP_MEM_UAF_GUARD 1
#define DN_ARENA_TEMP_MEM_UAF_TRACE_ON_BY_DEFAULT 0
#define DN_H_WITH_OS 1
#define DN_H_WITH_CORE 1
#define DN_H_WITH_HASH 1
#define DN_H_WITH_HELPERS 1
#define DN_H_WITH_ASYNC 1
#define DN_H_WITH_NET 1
#define DN_WITH_OS 1
#define DN_WITH_NET 1
#if defined(DN_PLATFORM_EMSCRIPTEN)
#define DN_WITH_NET_EMSCRIPTEN 1
#endif
#include "../dn.h"
#include "../dn.cpp"
#if defined(DN_UNIT_TESTS_WITH_CURL)
#if DN_WITH_NET_CURL
#define CURL_STATICLIB
#include <curl/curl.h>
#include "../Extra/dn_net_curl.h"
#include "../Extra/dn_net_curl.cpp"
#endif
#if defined(DN_PLATFORM_EMSCRIPTEN)
#include <emscripten/emscripten.h>
#include <emscripten/fetch.h>
#include "../Extra/dn_net_emscripten.h"
#include "../Extra/dn_net_emscripten.cpp"
#endif
#include "../dn.cpp"
#define DN_UT_IMPLEMENTATION
#include "../Standalone/dn_utest.h"
@@ -36,7 +26,7 @@ DN_MSVC_WARNING_DISABLE(6262) // Function uses '29804' bytes of stack. Consider
int main(int, char**)
{
DN_Core dn = {};
DN_Init(&dn, DN_InitFlags_LogAllFeatures | DN_InitFlags_OS | DN_InitFlags_ThreadContext, DN_TCInitArgsDefault());
DN_Init(&dn, DN_InitFlags_LogAllFeatures | DN_InitFlags_OS, DN_TCInitArgsDefault());
DN_TST_RunSuite(DN_TSTPrint_Yes);
return 0;
}
-37
View File
@@ -1,37 +0,0 @@
#define DN_TYPE_INFO_CPP
#if defined(_CLANGD)
#include "dn_type_info.h"
#endif
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// $$$$$$$$\ $$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$\
// \__$$ __|\$$\ $$ |$$ __$$\ $$ _____| \_$$ _|$$$\ $$ |$$ _____|$$ __$$\
// $$ | \$$\ $$ / $$ | $$ |$$ | $$ | $$$$\ $$ |$$ | $$ / $$ |
// $$ | \$$$$ / $$$$$$$ |$$$$$\ $$ | $$ $$\$$ |$$$$$\ $$ | $$ |
// $$ | \$$ / $$ ____/ $$ __| $$ | $$ \$$$$ |$$ __| $$ | $$ |
// $$ | $$ | $$ | $$ | $$ | $$ |\$$$ |$$ | $$ | $$ |
// $$ | $$ | $$ | $$$$$$$$\ $$$$$$\ $$ | \$$ |$$ | $$$$$$ |
// \__| \__| \__| \________| \______|\__| \__|\__| \______/
//
// dn_type_info.cpp
//
////////////////////////////////////////////////////////////////////////////////////////////////////
*/
DN_TypeGetField DN_Type_GetField(DN_TypeInfo const *type_info, DN_Str8 name)
{
DN_TypeGetField result = {};
for (DN_USize index = 0; index < type_info->fields_count; index++) {
DN_TypeField const *type_field = type_info->fields + index;
if (DN_Str8Eq(type_field->name, name)) {
result.success = true;
result.index = index;
result.field = DN_Cast(DN_TypeField *)type_field;
break;
}
}
return result;
}
-68
View File
@@ -1,68 +0,0 @@
#if !defined(DN_TYPE_INFO_H)
#define DN_TYPE_INFO_H
#if defined(_CLANGD)
#include "../dn.h"
#endif
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// $$$$$$$$\ $$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$\
// \__$$ __|\$$\ $$ |$$ __$$\ $$ _____| \_$$ _|$$$\ $$ |$$ _____|$$ __$$\
// $$ | \$$\ $$ / $$ | $$ |$$ | $$ | $$$$\ $$ |$$ | $$ / $$ |
// $$ | \$$$$ / $$$$$$$ |$$$$$\ $$ | $$ $$\$$ |$$$$$\ $$ | $$ |
// $$ | \$$ / $$ ____/ $$ __| $$ | $$ \$$$$ |$$ __| $$ | $$ |
// $$ | $$ | $$ | $$ | $$ | $$ |\$$$ |$$ | $$ | $$ |
// $$ | $$ | $$ | $$$$$$$$\ $$$$$$\ $$ | \$$ |$$ | $$$$$$ |
// \__| \__| \__| \________| \______|\__| \__|\__| \______/
//
// dn_type_info.h -- C++ type introspection
//
////////////////////////////////////////////////////////////////////////////////////////////////////
*/
enum DN_TypeKind
{
DN_TypeKind_Nil,
DN_TypeKind_Basic,
DN_TypeKind_Enum,
DN_TypeKind_Struct,
};
struct DN_TypeField
{
uint16_t index;
DN_Str8 name;
DN_Str8 label;
DN_ISize value;
DN_USize offset_of;
DN_USize size_of;
DN_USize align_of;
DN_Str8 type_decl;
uint32_t type_enum;
bool is_pointer;
uint16_t array_size;
DN_TypeField const *array_size_field;
};
struct DN_TypeInfo
{
DN_Str8 name;
DN_TypeKind kind;
DN_USize size_of;
DN_TypeField const *fields;
uint16_t fields_count;
};
struct DN_TypeGetField
{
bool success;
DN_USize index;
DN_TypeField *field;
};
DN_TypeGetField DN_Type_GetField(DN_TypeInfo const *type_info, DN_Str8 name);
#endif // !defined(DN_TYPE_INFO_H)
-1486
View File
File diff suppressed because it is too large Load Diff
-615
View File
@@ -1,615 +0,0 @@
#if !defined(DN_OS_H)
#define DN_OS_H
#if defined(_CLANGD)
#define DN_H_WITH_OS 1
#include "../dn.h"
#endif
#include <new> // operator new
#if !defined(DN_OS_WIN32) || defined(DN_OS_WIN32_USE_PTHREADS)
#include <pthread.h>
#include <semaphore.h>
#endif
#if defined(DN_PLATFORM_POSIX) || defined(DN_PLATFORM_EMSCRIPTEN)
#include <errno.h> // errno
#include <fcntl.h> // O_RDONLY ... etc
#include <sys/ioctl.h> // ioctl
#include <sys/mman.h> // mmap
#include <sys/random.h> // getrandom
#include <sys/stat.h> // stat
#include <sys/types.h> // pid_t
#include <sys/wait.h> // waitpid
#include <time.h> // clock_gettime, nanosleep
#include <unistd.h> // access, gettid, write
#if !defined(DN_PLATFORM_EMSCRIPTEN)
#include <linux/fs.h> // FICLONE
#include <sys/sendfile.h> // sendfile
#endif
#endif
#if defined(DN_PLATFORM_EMSCRIPTEN)
#include <emscripten/fetch.h> // emscripten_fetch (for DN_OSHttpResponse)
#endif
extern DN_CPUFeatureDecl g_dn_cpu_feature_decl[DN_CPUFeature_Count];
struct DN_OSTimer /// Record time between two time-points using the OS's performance counter.
{
DN_U64 start;
DN_U64 end;
};
// NOTE: DN_OSFile
enum DN_OSPathInfoType
{
DN_OSPathInfoType_Unknown,
DN_OSPathInfoType_Directory,
DN_OSPathInfoType_File,
};
struct DN_OSPathInfo
{
bool exists;
DN_OSPathInfoType type;
DN_U64 create_time_in_s;
DN_U64 last_write_time_in_s;
DN_U64 last_access_time_in_s;
DN_U64 size;
};
struct DN_OSDirIterator
{
void *handle;
DN_Str8 file_name;
char buffer[512];
};
// NOTE: R/W Stream API
struct DN_OSFileRead
{
bool success;
DN_USize bytes_read;
};
struct DN_OSFile
{
bool error;
void *handle;
};
enum DN_OSFileOpen
{
DN_OSFileOpen_CreateAlways, // Create file if it does not exist, otherwise, zero out the file and open
DN_OSFileOpen_OpenIfExist, // Open file at path only if it exists
DN_OSFileOpen_OpenAlways, // Open file at path, create file if it does not exist
};
typedef DN_U32 DN_OSFileAccess;
enum DN_OSFileAccess_
{
DN_OSFileAccess_Read = 1 << 0,
DN_OSFileAccess_Write = 1 << 1,
DN_OSFileAccess_Execute = 1 << 2,
DN_OSFileAccess_AppendOnly = 1 << 3, // This flag cannot be combined with any other access mode
DN_OSFileAccess_ReadWrite = DN_OSFileAccess_Read | DN_OSFileAccess_Write,
DN_OSFileAccess_All = DN_OSFileAccess_ReadWrite | DN_OSFileAccess_Execute | DN_OSFileAccess_AppendOnly,
};
// NOTE: DN_OSPath
#if !defined(DN_OSPathSeperator)
#if defined(DN_OS_WIN32)
#define DN_OSPathSeperator "\\"
#else
#define DN_OSPathSeperator "/"
#endif
#define DN_OSPathSeperatorString DN_Str8Lit(DN_OSPathSeperator)
#endif
struct DN_OSPathLink
{
DN_Str8 string;
DN_OSPathLink *next;
DN_OSPathLink *prev;
};
struct DN_OSPath
{
bool has_prefix_path_separator;
DN_OSPathLink *head;
DN_OSPathLink *tail;
DN_USize string_size;
DN_U16 links_size;
};
// NOTE: DN_OSExec
typedef DN_U32 DN_OSExecFlags;
enum DN_OSExecFlags_
{
DN_OSExecFlags_Nil = 0,
DN_OSExecFlags_SaveStdout = 1 << 0,
DN_OSExecFlags_SaveStderr = 1 << 1,
DN_OSExecFlags_SaveOutput = DN_OSExecFlags_SaveStdout | DN_OSExecFlags_SaveStderr,
DN_OSExecFlags_MergeStderrToStdout = 1 << 2 | DN_OSExecFlags_SaveOutput,
};
struct DN_OSExecAsyncHandle
{
DN_OSExecFlags exec_flags;
DN_U32 os_error_code;
DN_U32 exit_code;
void *process;
void *stdout_read;
void *stdout_write;
void *stderr_read;
void *stderr_write;
};
struct DN_OSExecResult
{
bool finished;
DN_Str8 stdout_text;
DN_Str8 stderr_text;
DN_U32 os_error_code;
DN_U32 exit_code;
};
struct DN_OSExecArgs
{
DN_OSExecFlags flags;
DN_Str8 working_dir;
DN_Str8Slice environment;
};
// NOTE: DN_OSSemaphore
DN_U32 const DN_OS_SEMAPHORE_INFINITE_TIMEOUT = UINT32_MAX;
struct DN_OSSemaphore
{
DN_U64 handle;
};
struct DN_OSBarrier
{
DN_U64 handle;
};
enum DN_OSSemaphoreWaitResult
{
DN_OSSemaphoreWaitResult_Failed,
DN_OSSemaphoreWaitResult_Success,
DN_OSSemaphoreWaitResult_Timeout,
};
struct DN_OSMutex
{
DN_U64 handle;
};
struct DN_OSConditionVariable
{
DN_U64 handle;
};
// NOTE: DN_OSThread
typedef DN_I32(DN_OSThreadFunc)(struct DN_OSThread *);
struct DN_OSThreadLane
{
DN_USize index;
DN_USize count;
DN_OSBarrier barrier;
void* shared_mem;
};
struct DN_OSThreadLaneway
{
DN_OSThread* threads;
DN_USize threads_count;
DN_UPtr* shared_mem;
DN_OSBarrier barrier;
};
struct DN_OSThread
{
DN_Str8x64 name;
DN_TCCore context;
DN_OSThreadLane lane;
bool is_lane_set;
void *handle;
DN_U64 thread_id;
void *user_context;
DN_OSThreadFunc *func;
DN_OSSemaphore init_semaphore;
DN_TCInitArgs tc_init_args;
};
// NOTE: DN_OSHttp
enum DN_OSHttpRequestSecure
{
DN_OSHttpRequestSecure_No,
DN_OSHttpRequestSecure_Yes,
};
struct DN_OSHttpResponse
{
// NOTE: Response data
DN_U32 error_code;
DN_Str8 error_msg;
DN_U16 http_status;
DN_Str8 body;
DN_B32 done;
// NOTE: Book-keeping
DN_Arena *arena; // Allocates memory for the response
// NOTE: Async book-keeping
// Synchronous HTTP response uses the TLS scratch arena whereas async
// calls use their own dedicated arena.
DN_Arena tmp_arena;
DN_Arena scratch_arena;
DN_Str8Builder builder;
DN_OSSemaphore on_complete_semaphore;
#if defined(DN_PLATFORM_EMSCRIPTEN)
emscripten_fetch_t *em_handle;
#elif defined(DN_PLATFORM_WIN32)
HINTERNET w32_request_session;
HINTERNET w32_request_connection;
HINTERNET w32_request_handle;
#endif
};
struct DN_OSCore
{
DN_CPUReport cpu_report;
// NOTE: Logging
bool log_to_file; // Output logs to file as well as standard out
DN_OSFile log_file; // TODO(dn): Hmmm, how should we do this... ?
DN_TicketMutex log_file_mutex; // Is locked when instantiating the log_file for the first time
bool log_no_colour; // Disable colours in the logging output
DN_TicketMutex log_mutex;
// NOTE: OS
DN_U32 logical_processor_count;
DN_U32 page_size;
DN_U32 alloc_granularity;
// NOTE: Memory
// Total OS mem allocs in lifetime of program (e.g. malloc, VirtualAlloc, HeapAlloc ...). This
// only includes allocations routed through the library such as the growing nature of arenas or
// using the memory allocation routines in the library like DN_OS_MemCommit and so forth.
DN_U64 vmem_allocs_total;
DN_U64 vmem_allocs_frame; // Total OS virtual memory allocs since the last 'DN_Core_FrameBegin' was invoked
DN_U64 mem_allocs_total;
DN_U64 mem_allocs_frame; // Total OS heap allocs since the last 'DN_Core_FrameBegin' was invoked
DN_MemList mem;
DN_Arena arena;
void *platform_context;
};
struct DN_OSDiskSpace
{
bool success;
DN_U64 avail;
DN_U64 size;
};
DN_API DN_MemFuncs DN_MemFuncsFromType (DN_MemFuncsType type);
DN_API DN_MemFuncs DN_MemFuncsDefault ();
DN_API DN_MemList DN_MemListFromHeap (DN_U64 size, DN_MemFlags flags);
DN_API DN_MemList DN_MemListFromVMem (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags);
DN_API DN_Arena DN_ArenaFromHeap (DN_U64 wize, DN_MemFlags flags);
DN_API DN_Arena DN_ArenaFromVMem (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags);
DN_API DN_Str8 DN_Str8FromHeapF (DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_Str8 DN_Str8FromHeap (DN_USize size, DN_ZMem z_mem);
DN_API DN_Str8 DN_Str8BuilderBuildFromHeap (DN_Str8Builder const *builder);
DN_API void DN_OS_LogPrint (DN_LogTypeParam type, void *user_data, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API void DN_OS_SetLogPrintFuncToOS ();
DN_API void * DN_OS_MemReserve (DN_USize size, DN_MemCommit commit, DN_MemPage page_flags);
DN_API bool DN_OS_MemCommit (void *ptr, DN_USize size, DN_U32 page_flags);
DN_API void DN_OS_MemDecommit (void *ptr, DN_USize size);
DN_API void DN_OS_MemRelease (void *ptr, DN_USize size);
DN_API int DN_OS_MemProtect (void *ptr, DN_USize size, DN_U32 page_flags);
DN_API void * DN_OS_MemAlloc (DN_USize size, DN_ZMem z_mem);
DN_API void DN_OS_MemDealloc (void *ptr);
DN_API DN_Date DN_OS_DateLocalTimeNow ();
DN_API DN_Str8x32 DN_OS_DateLocalTimeStr8Now (char date_separator = '-', char hms_separator = ':');
DN_API DN_Str8x32 DN_OS_DateLocalTimeStr8 (DN_Date time, char date_separator = '-', char hms_separator = ':');
DN_API DN_U64 DN_OS_DateUnixTimeNs ();
#define DN_OS_DateUnixTimeUs() (DN_OS_DateUnixTimeNs() / 1000)
#define DN_OS_DateUnixTimeMs() (DN_OS_DateUnixTimeNs() / (1000 * 1000))
#define DN_OS_DateUnixTimeS() (DN_OS_DateUnixTimeNs() / (1000 * 1000 * 1000))
DN_API DN_U64 DN_OS_DateUnixTimeSFromLocalDate (DN_Date date);
DN_API DN_U64 DN_OS_DateLocalUnixTimeSFromUnixTimeS (DN_U64 unix_ts_s);
DN_API void DN_OS_GenBytesSecure (void *buffer, DN_U32 size);
DN_API bool DN_OS_SetEnvVar (DN_Str8 name, DN_Str8 value);
DN_API DN_OSDiskSpace DN_OS_DiskSpace (DN_Str8 path);
DN_API DN_Str8 DN_OS_EXEPath (DN_Arena *arena);
DN_API DN_Str8 DN_OS_EXEDir (DN_Arena *arena);
DN_API void DN_OS_SleepMs (DN_UInt milliseconds);
DN_API DN_U64 DN_OS_PerfCounterNow ();
DN_API DN_U64 DN_OS_PerfCounterFrequency ();
DN_API DN_F64 DN_OS_PerfCounterS (DN_U64 begin, uint64_t end);
DN_API DN_F64 DN_OS_PerfCounterMs (DN_U64 begin, uint64_t end);
DN_API DN_F64 DN_OS_PerfCounterUs (DN_U64 begin, uint64_t end);
DN_API DN_F64 DN_OS_PerfCounterNs (DN_U64 begin, uint64_t end);
DN_API DN_OSTimer DN_OS_TimerBegin ();
DN_API void DN_OS_TimerEnd (DN_OSTimer *timer);
DN_API DN_F64 DN_OS_TimerS (DN_OSTimer timer);
DN_API DN_F64 DN_OS_TimerMs (DN_OSTimer timer);
DN_API DN_F64 DN_OS_TimerUs (DN_OSTimer timer);
DN_API DN_F64 DN_OS_TimerNs (DN_OSTimer timer);
DN_API DN_U64 DN_OS_EstimateTSCPerSecond (uint64_t duration_ms_to_gauge_tsc_frequency);
DN_API bool DN_OS_FileCopy (DN_Str8 src, DN_Str8 dest, bool overwrite, DN_ErrSink *err);
DN_API bool DN_OS_FileMove (DN_Str8 src, DN_Str8 dest, bool overwrite, DN_ErrSink *err);
DN_API DN_OSFile DN_OS_FileOpen (DN_Str8 path, DN_OSFileOpen open_mode, DN_OSFileAccess access, DN_ErrSink *err);
DN_API DN_OSFileRead DN_OS_FileRead (DN_OSFile *file, void *buffer, DN_USize size, DN_ErrSink *err);
DN_API bool DN_OS_FileWritePtr (DN_OSFile *file, void const *data, DN_USize size, DN_ErrSink *err);
DN_API bool DN_OS_FileWrite (DN_OSFile *file, DN_Str8 buffer, DN_ErrSink *err);
DN_API bool DN_OS_FileWriteFV (DN_OSFile *file, DN_ErrSink *err, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API bool DN_OS_FileWriteF (DN_OSFile *file, DN_ErrSink *err, DN_FMT_ATTRIB char const *fmt, ...);
DN_API bool DN_OS_FileFlush (DN_OSFile *file, DN_ErrSink *err);
DN_API void DN_OS_FileClose (DN_OSFile *file);
DN_API DN_Str8 DN_OS_FileReadAll (DN_Allocator alloc_type, void *allocator, DN_Str8 path, DN_ErrSink *err);
DN_API DN_Str8 DN_OS_FileReadAllArena (DN_Arena *arena, DN_Str8 path, DN_ErrSink *err);
DN_API DN_Str8 DN_OS_FileReadAllPool (DN_Pool *pool, DN_Str8 path, DN_ErrSink *err);
DN_API bool DN_OS_FileWriteAll (DN_Str8 path, DN_Str8 buffer, DN_ErrSink *err);
DN_API bool DN_OS_FileWriteAllFV (DN_Str8 path, DN_ErrSink *err, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API bool DN_OS_FileWriteAllF (DN_Str8 path, DN_ErrSink *err, DN_FMT_ATTRIB char const *fmt, ...);
DN_API bool DN_OS_FileWriteAllSafe (DN_Str8 path, DN_Str8 buffer, DN_ErrSink *err);
DN_API bool DN_OS_FileWriteAllSafeFV (DN_Str8 path, DN_ErrSink *err, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API bool DN_OS_FileWriteAllSafeF (DN_Str8 path, DN_ErrSink *err, DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_Str8 DN_OS_Str8FromPathInfoType (DN_OSPathInfoType type);
DN_API DN_OSPathInfo DN_OS_PathInfo (DN_Str8 path);
DN_API bool DN_OS_PathIsOlderThan (DN_Str8 file, DN_Str8 check_against);
DN_API bool DN_OS_PathDelete (DN_Str8 path);
DN_API bool DN_OS_PathIsFile (DN_Str8 path);
DN_API bool DN_OS_PathIsDir (DN_Str8 path);
DN_API bool DN_OS_PathMakeDir (DN_Str8 path);
DN_API bool DN_OS_PathIterateDir (DN_Str8 path, DN_OSDirIterator *it);
DN_API bool DN_OS_PathAddRef (DN_Arena *arena, DN_OSPath *fs_path, DN_Str8 path);
DN_API bool DN_OS_PathAdd (DN_Arena *arena, DN_OSPath *fs_path, DN_Str8 path);
DN_API bool DN_OS_PathAddF (DN_Arena *arena, DN_OSPath *fs_path, DN_FMT_ATTRIB char const *fmt, ...);
DN_API bool DN_OS_PathPop (DN_OSPath *fs_path);
DN_API DN_Str8 DN_OS_PathBuildWithSeparator (DN_Arena *arena, DN_OSPath const *fs_path, DN_Str8 path_separator);
DN_API DN_Str8 DN_OS_PathTo (DN_Arena *arena, DN_Str8 path, DN_Str8 path_separtor);
DN_API DN_Str8 DN_OS_PathToF (DN_Arena *arena, DN_Str8 path_separator, DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_Str8 DN_OS_Path (DN_Arena *arena, DN_Str8 path);
DN_API DN_Str8 DN_OS_PathF (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...);
#define DN_OS_PathBuildFwdSlash(allocator, fs_path) DN_OS_PathBuildWithSeparator(allocator, fs_path, DN_Str8Lit("/"))
#define DN_OS_PathBuildBackSlash(allocator, fs_path) DN_OS_PathBuildWithSeparator(allocator, fs_path, DN_Str8Lit("\\"))
#define DN_OS_PathBuild(allocator, fs_path) DN_OS_PathBuildWithSeparator(allocator, fs_path, DN_OSPathSeparatorString)
DN_API void DN_OS_Exit (int32_t exit_code);
DN_API DN_OSExecResult DN_OS_ExecPump (DN_OSExecAsyncHandle handle, char *stdout_buffer, size_t *stdout_size, char *stderr_buffer, size_t *stderr_size, DN_U32 timeout_ms, DN_ErrSink *err);
DN_API DN_OSExecResult DN_OS_ExecWait (DN_OSExecAsyncHandle handle, DN_Arena *arena, DN_ErrSink *err);
DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync (DN_Str8Slice cmd_line, DN_OSExecArgs *args, DN_ErrSink *err);
DN_API DN_OSExecResult DN_OS_Exec (DN_Str8Slice cmd_line, DN_OSExecArgs *args, DN_Arena *arena, DN_ErrSink *err);
DN_API DN_OSExecResult DN_OS_ExecOrAbort (DN_Str8Slice cmd_line, DN_OSExecArgs *args, DN_Arena *arena);
DN_API DN_OSSemaphore DN_OS_SemaphoreInit (DN_U32 initial_count);
DN_API void DN_OS_SemaphoreDeinit (DN_OSSemaphore *semaphore);
DN_API void DN_OS_SemaphoreIncrement (DN_OSSemaphore *semaphore, DN_U32 amount);
DN_API DN_OSSemaphoreWaitResult DN_OS_SemaphoreWait (DN_OSSemaphore *semaphore, DN_U32 timeout_ms);
DN_API DN_OSBarrier DN_OS_BarrierInit (DN_U32 thread_count);
DN_API void DN_OS_BarrierDeinit (DN_OSBarrier *barrier);
DN_API void DN_OS_BarrierWait (DN_OSBarrier *barrier);
DN_API DN_OSMutex DN_OS_MutexInit ();
DN_API void DN_OS_MutexDeinit (DN_OSMutex *mutex);
DN_API void DN_OS_MutexLock (DN_OSMutex *mutex);
DN_API void DN_OS_MutexUnlock (DN_OSMutex *mutex);
#define DN_OS_MutexScope(mutex) DN_DeferLoop(DN_OS_MutexLock(mutex), DN_OS_MutexUnlock(mutex))
DN_API DN_OSConditionVariable DN_OS_ConditionVariableInit ();
DN_API void DN_OS_ConditionVariableDeinit (DN_OSConditionVariable *cv);
DN_API bool DN_OS_ConditionVariableWait (DN_OSConditionVariable *cv, DN_OSMutex *mutex, DN_U64 sleep_ms);
DN_API bool DN_OS_ConditionVariableWaitUntil (DN_OSConditionVariable *cv, DN_OSMutex *mutex, DN_U64 end_ts_ms);
DN_API void DN_OS_ConditionVariableSignal (DN_OSConditionVariable *cv);
DN_API void DN_OS_ConditionVariableBroadcast (DN_OSConditionVariable *cv);
DN_API bool DN_OS_ThreadInit (DN_OSThread *thread, DN_OSThreadFunc *func, DN_OSThreadLane *lane, DN_TCInitArgs tc_init_args, void *user_context);
DN_API bool DN_OS_ThreadJoin (DN_OSThread *thread, DN_TCDeinitArenas deinit_arenas);
DN_API DN_U32 DN_OS_ThreadID ();
DN_API void DN_OS_ThreadSetNameFmt (char const *fmt, ...);
// NOTE: Thread lanes provide an abstraction to represent the concept of programming a CPU like a
// GPU, e.g. SIMT (Single Instruction Multiple Threads). The lane terminology is popularised by Ryan
// Fleury. SIMT is formally defined as
//
// Threads are grouped into warps/wavefronts (typically 32 or 64 threads) that execute the same
// instruction in lockstep, but each thread operates on different data and maintains its own state
//
// The individual threads in a wavefront on the CPU side are colloquially dubbed "lanes" and a
// thread lane here contains the necessary state to facilitate this such as the current index in the
// wavefront and synchronisation primitives to coordinate the different lanes together.
//
// The idea is to write code in a single-threaded manner (linear execution) but across multiple
// threads so that the default is all execution paths are inherently multi-threaded by default. Opt
// out of parallelism instead of opt in. This optimises for the trend of core counts increasing
// whilst clock counts remain static.
//
// A laneway is a helper function to initialise the number of requested OS threads/lanes upfront and
// setup the required synchronisation primitives. It can then be dispatched all the threads which
// start executing the `entry_point` in parallel.
//
// API
// DN_OS_ThreadLaneSync
// A blocking call to synchronise the program-counter of all other lanes in the laneway to this
// function call invocation (using an OS barrier). Optionally pass in the pointer to a pointer
// `ptr_to_share` to broadcast the pointer from one lanes to the others. The lane that wishes
// to broadcast the pointer must have a non-null pointer, all other lanes must pass in a
// non-null pointer. A typical use case might look like:
/*
DN_OSThreadLane *lane = DN_OS_TCThreadLane(); // Get lane from current (t)hread (c)context
// NOTE: Allocate buffer in lane 0
DN_U8 *buffer = nullptr;
if (lane->index == 0)
buffer = DN_ArenaNewArray(DN_TCMainArena(), DN_U8, DN_Gigabytes(1), DN_ZMem_No);
// NOTE: Lane 0 broadcasts the `buffer` pointer to lane 1..N
DN_OS_ThreadLaneSync(lane, &buffer);
// NOTE: We use LaneRange to divide the buffer into equal sized chunks that each lane can
// write into without clobbering over each other.
DN_V2USize range = DN_OS_ThreadLaneRange(lane, DN_Gigabytes(1));
for (DN_USize index = range.begin; index < range.end; index++) { buffer[index] = index; }
*/
// In this example, lane 0 will allocate a 1GiB buffer pass in a `buffer` to
// DN_OS_ThreadLaneSync` that is non-null. Lanes 1->N will skip the branch (because their lanes
// indexes are 1..N) and invoke `DN_OS_ThreadLaneSync` with a nullptr `buffer`. After the
// blocking call is complete, lanes 0->N will now have synchronised the `buffer` pointer and all
// lanes point to the 1GiB range allocated in lane 0's allocator.
//
// Additionally we demonstrate `DN_OS_ThreadLaneRange` which does math behind the scenes to
// divide the buffer up and assign each lane their own indices in the buffer that they can work
// on in parallel without clobbering each others work.
//
// DN_OS_ThreadLaneRange
// Calculates the range of values the current lane in the laneway should execute. For example if
// you have 128 items and 16 threads each lane will receive the following `DN_V2USize` range:
// Lane 0 => [0, 8)
// Lane 1 => [8, 16)
// ...
// Lane 16 => [120, 128)
DN_API DN_OSThreadLane DN_OS_ThreadLaneInit (DN_USize index, DN_USize thread_count, DN_OSBarrier barrier, DN_UPtr *share_mem);
DN_API void DN_OS_ThreadLaneSync (DN_OSThreadLane *lane, void **ptr_to_share);
DN_API DN_V2USize DN_OS_ThreadLaneRange (DN_OSThreadLane const *lane, DN_USize values_count);
DN_API DN_OSThreadLaneway DN_OS_ThreadLanewayFromArgs (DN_OSThread* threads, DN_USize threads_count, DN_UPtr* shared_mem);
DN_API DN_OSThreadLaneway DN_OS_ThreadLanewayFromArena (DN_USize threads_count, DN_Arena* arena);
DN_API void DN_OS_ThreadLanewayDispatch (DN_OSThreadLaneway *laneway, DN_OSThreadFunc *entry_point, DN_TCInitArgs tc_init_args, void *user_context);
DN_API void DN_OS_ThreadLanewayJoin (DN_OSThreadLaneway *laneway, DN_TCDeinitArenas deinit_arenas);
DN_API DN_OSThreadLane* DN_OS_TCThreadLane ();
DN_API void DN_OS_TCThreadLaneSync (void **ptr_to_share);
DN_API DN_OSThreadLane DN_OS_TCThreadLaneEquip (DN_OSThreadLane lane);
DN_API void DN_OS_HttpRequestAsync (DN_OSHttpResponse *response, DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers);
DN_API void DN_OS_HttpRequestWait (DN_OSHttpResponse *response);
DN_API void DN_OS_HttpRequestFree (DN_OSHttpResponse *response);
DN_API DN_OSHttpResponse DN_OS_HttpRequest (DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers);
// NOTE: DN_OSPrint
enum DN_OSPrintDest
{
DN_OSPrintDest_Out,
DN_OSPrintDest_Err,
};
// NOTE: Print Macros
#define DN_OS_PrintOut(string) DN_OS_Print(DN_OSPrintDest_Out, string)
#define DN_OS_PrintOutF(fmt, ...) DN_OS_PrintF(DN_OSPrintDest_Out, fmt, ##__VA_ARGS__)
#define DN_OS_PrintOutFV(fmt, args) DN_OS_PrintFV(DN_OSPrintDest_Out, fmt, args)
#define DN_OS_PrintOutStyle(style, string) DN_OS_PrintStyle(DN_OSPrintDest_Out, style, string)
#define DN_OS_PrintOutFStyle(style, fmt, ...) DN_OS_PrintFStyle(DN_OSPrintDest_Out, style, fmt, ##__VA_ARGS__)
#define DN_OS_PrintOutFVStyle(style, fmt, args, ...) DN_OS_PrintFVStyle(DN_OSPrintDest_Out, style, fmt, args)
#define DN_OS_PrintOutLn(string) DN_OS_PrintLn(DN_OSPrintDest_Out, string)
#define DN_OS_PrintOutLnF(fmt, ...) DN_OS_PrintLnF(DN_OSPrintDest_Out, fmt, ##__VA_ARGS__)
#define DN_OS_PrintOutLnFV(fmt, args) DN_OS_PrintLnFV(DN_OSPrintDest_Out, fmt, args)
#define DN_OS_PrintOutLnStyle(style, string) DN_OS_PrintLnStyle(DN_OSPrintDest_Out, style, string);
#define DN_OS_PrintOutLnFStyle(style, fmt, ...) DN_OS_PrintLnFStyle(DN_OSPrintDest_Out, style, fmt, ##__VA_ARGS__)
#define DN_OS_PrintOutLnFVStyle(style, fmt, args) DN_OS_PrintLnFVStyle(DN_OSPrintDest_Out, style, fmt, args);
#define DN_OS_PrintErr(string) DN_OS_Print(DN_OSPrintDest_Err, string)
#define DN_OS_PrintErrF(fmt, ...) DN_OS_PrintF(DN_OSPrintDest_Err, fmt, ##__VA_ARGS__)
#define DN_OS_PrintErrFV(fmt, args) DN_OS_PrintFV(DN_OSPrintDest_Err, fmt, args)
#define DN_OS_PrintErrStyle(style, string) DN_OS_PrintStyle(DN_OSPrintDest_Err, style, string)
#define DN_OS_PrintErrFStyle(style, fmt, ...) DN_OS_PrintFStyle(DN_OSPrintDest_Err, style, fmt, ##__VA_ARGS__)
#define DN_OS_PrintErrFVStyle(style, fmt, args, ...) DN_OS_PrintFVStyle(DN_OSPrintDest_Err, style, fmt, args)
#define DN_OS_PrintErrLn(string) DN_OS_PrintLn(DN_OSPrintDest_Err, string)
#define DN_OS_PrintErrLnF(fmt, ...) DN_OS_PrintLnF(DN_OSPrintDest_Err, fmt, ##__VA_ARGS__)
#define DN_OS_PrintErrLnFV(fmt, args) DN_OS_PrintLnFV(DN_OSPrintDest_Err, fmt, args)
#define DN_OS_PrintErrLnStyle(style, string) DN_OS_PrintLnStyle(DN_OSPrintDest_Err, style, string);
#define DN_OS_PrintErrLnFStyle(style, fmt, ...) DN_OS_PrintLnFStyle(DN_OSPrintDest_Err, style, fmt, ##__VA_ARGS__)
#define DN_OS_PrintErrLnFVStyle(style, fmt, args) DN_OS_PrintLnFVStyle(DN_OSPrintDest_Err, style, fmt, args);
// NOTE: Print
DN_API void DN_OS_Print (DN_OSPrintDest dest, DN_Str8 string);
DN_API void DN_OS_PrintF (DN_OSPrintDest dest, DN_FMT_ATTRIB char const *fmt, ...);
DN_API void DN_OS_PrintFV (DN_OSPrintDest dest, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API void DN_OS_PrintStyle (DN_OSPrintDest dest, DN_LogStyle style, DN_Str8 string);
DN_API void DN_OS_PrintFStyle (DN_OSPrintDest dest, DN_LogStyle style, DN_FMT_ATTRIB char const *fmt, ...);
DN_API void DN_OS_PrintFVStyle (DN_OSPrintDest dest, DN_LogStyle style, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API void DN_OS_PrintLn (DN_OSPrintDest dest, DN_Str8 string);
DN_API void DN_OS_PrintLnF (DN_OSPrintDest dest, DN_FMT_ATTRIB char const *fmt, ...);
DN_API void DN_OS_PrintLnFV (DN_OSPrintDest dest, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API void DN_OS_PrintLnStyle (DN_OSPrintDest dest, DN_LogStyle style, DN_Str8 string);
DN_API void DN_OS_PrintLnFStyle (DN_OSPrintDest dest, DN_LogStyle style, DN_FMT_ATTRIB char const *fmt, ...);
DN_API void DN_OS_PrintLnFVStyle (DN_OSPrintDest dest, DN_LogStyle style, DN_FMT_ATTRIB char const *fmt, va_list args);
// NOTE: DN_VArray
// TODO(doyle): Add an API for shrinking the array by decomitting pages back to the OS.
template <typename T> struct DN_VArray
{
T *data; // Pointer to the start of the array items in the block of memory
DN_USize size; // Number of items currently in the array
DN_USize max; // Maximum number of items this array can store
DN_USize commit; // Bytes committed
T *begin() { return data; }
T *end () { return data + size; }
T const *begin() const { return data; }
T const *end () const { return data + size; }
};
template <typename T> DN_VArray<T> DN_OS_VArrayInitByteSize (DN_USize byte_size);
template <typename T> DN_VArray<T> DN_OS_VArrayInit (DN_USize max);
template <typename T, DN_USize N> DN_VArray<T> DN_OS_VArrayInitCArray (T const (&items)[N], DN_USize max);
template <typename T> void DN_OS_VArrayDeinit (DN_VArray<T> *array);
template <typename T> bool DN_OS_VArrayIsValid (DN_VArray<T> const *array);
template <typename T> bool DN_OS_VArrayReserve (DN_VArray<T> *array, DN_USize count);
template <typename T> T * DN_OS_VArrayAddArray (DN_VArray<T> *array, T const *items, DN_USize count);
template <typename T, DN_USize N> T * DN_OS_VArrayAddCArray (DN_VArray<T> *array, T const (&items)[N]);
template <typename T> T * DN_OS_VArrayAdd (DN_VArray<T> *array, T const &item);
#define DN_OS_VArrayAddArrayAssert(...) DN_HardAssert(DN_OS_VArrayAddArray(__VA_ARGS__))
#define DN_OS_VArrayAddCArrayAssert(...) DN_HardAssert(DN_OS_VArrayAddCArray(__VA_ARGS__))
#define DN_OS_VArrayAddAssert(...) DN_HardAssert(DN_OS_VArrayAdd(__VA_ARGS__))
template <typename T> T * DN_OS_VArrayMakeArray (DN_VArray<T> *array, DN_USize count, DN_ZMem z_mem);
template <typename T> T * DN_OS_VArrayMake (DN_VArray<T> *array, DN_ZMem z_mem);
#define DN_OS_VArrayMakeArrayAssert(...) DN_HardAssert(DN_OS_VArrayMakeArray(__VA_ARGS__))
#define DN_OS_VArrayMakeAssert(...) DN_HardAssert(DN_OS_VArrayMake(__VA_ARGS__))
template <typename T> T * DN_OS_VArrayInsertArray (DN_VArray<T> *array, DN_USize index, T const *items, DN_USize count);
template <typename T, DN_USize N> T * DN_OS_VArrayInsertCArray (DN_VArray<T> *array, DN_USize index, T const (&items)[N]);
template <typename T> T * DN_OS_VArrayInsert (DN_VArray<T> *array, DN_USize index, T const &item);
#define DN_OS_VArrayInsertArrayAssert(...) DN_HardAssert(DN_OS_VArrayInsertArray(__VA_ARGS__))
#define DN_OS_VArrayInsertCArrayAssert(...) DN_HardAssert(DN_OS_VArrayInsertCArray(__VA_ARGS__))
#define DN_OS_VArrayInsertAssert(...) DN_HardAssert(DN_OS_VArrayInsert(__VA_ARGS__))
template <typename T> T DN_OS_VArrayPopFront (DN_VArray<T> *array, DN_USize count);
template <typename T> T DN_OS_VArrayPopBack (DN_VArray<T> *array, DN_USize count);
template <typename T> DN_ArrayEraseResult DN_OS_VArrayEraseRange (DN_VArray<T> *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase);
template <typename T> void DN_OS_VArrayClear (DN_VArray<T> *array, DN_ZMem z_mem);
#endif // !defined(DN_OS_H)
+34 -190
View File
@@ -31,7 +31,7 @@ static DN_U32 DN_OS_MemConvertPageToOSFlags_(DN_U32 protect)
DN_API void *DN_OS_MemReserve(DN_USize size, DN_MemCommit commit, DN_U32 page_flags)
{
#if defined(DN_PLATFORM_EMSCRIPTEN)
DN_InvalidCodePathF("Emscripten does not support virtual memory, you should use DN_OS_MemAlloc");
DN_AssertInvalidCodePathF("Emscripten does not support virtual memory, you should use DN_OS_MemAlloc");
#endif
unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags);
@@ -50,7 +50,7 @@ DN_API void *DN_OS_MemReserve(DN_USize size, DN_MemCommit commit, DN_U32 page_fl
DN_API bool DN_OS_MemCommit(void *ptr, DN_USize size, DN_U32 page_flags)
{
#if defined(DN_PLATFORM_EMSCRIPTEN)
DN_InvalidCodePathF("Emscripten does not support virtual memory");
DN_AssertInvalidCodePathF("Emscripten does not support virtual memory");
#endif
bool result = false;
if (!ptr || size == 0)
@@ -66,7 +66,7 @@ DN_API bool DN_OS_MemCommit(void *ptr, DN_USize size, DN_U32 page_flags)
DN_API void DN_OS_MemDecommit(void *ptr, DN_USize size)
{
#if defined(DN_PLATFORM_EMSCRIPTEN)
DN_InvalidCodePathF("Emscripten does not support virtual memory");
DN_AssertInvalidCodePathF("Emscripten does not support virtual memory");
#endif
mprotect(ptr, size, PROT_NONE);
madvise(ptr, size, MADV_FREE);
@@ -75,7 +75,7 @@ DN_API void DN_OS_MemDecommit(void *ptr, DN_USize size)
DN_API void DN_OS_MemRelease(void *ptr, DN_USize size)
{
#if defined(DN_PLATFORM_EMSCRIPTEN)
DN_InvalidCodePathF("Emscripten does not support virtual memory");
DN_AssertInvalidCodePathF("Emscripten does not support virtual memory");
#endif
munmap(ptr, size);
}
@@ -83,7 +83,7 @@ DN_API void DN_OS_MemRelease(void *ptr, DN_USize size)
DN_API int DN_OS_MemProtect(void *ptr, DN_USize size, DN_U32 page_flags)
{
#if defined(DN_PLATFORM_EMSCRIPTEN)
DN_InvalidCodePathF("Emscripten does not support virtual memory");
DN_AssertInvalidCodePathF("Emscripten does not support virtual memory");
#endif
if (!ptr || size == 0)
return 0;
@@ -199,7 +199,7 @@ DN_API DN_Date DN_OS_DateUnixTimeSToDate(DN_U64 time)
DN_API void DN_OS_GenBytesSecure(void *buffer, DN_U32 size)
{
#if defined(DN_PLATFORM_EMSCRIPTEN)
DN_InvalidCodePath;
DN_AssertInvalidCodePath;
(void)buffer;
(void)size;
#else
@@ -219,7 +219,7 @@ DN_API void DN_OS_GenBytesSecure(void *buffer, DN_U32 size)
DN_API bool DN_OS_SetEnvVar(DN_Str8 name, DN_Str8 value)
{
DN_AssertFOnce(false, "Unimplemented");
DN_VerifyWarningF(false, "Unimplemented function");
(void)name;
(void)value;
bool result = false;
@@ -441,22 +441,18 @@ DN_API DN_OSFile DN_OS_FileOpen(DN_Str8 path,
DN_ErrSink *error)
{
DN_OSFile result = {};
if (path.size == 0 || path.size <= 0)
if (path.count == 0 || path.count <= 0)
return result;
if ((access & ~(DN_OSFileAccess_All) || ((access & DN_OSFileAccess_All) == 0))) {
DN_InvalidCodePath;
DN_AssertInvalidCodePath;
return result;
}
if (access & DN_OSFileAccess_Execute) {
result.error = true;
DN_ErrSinkAppendF(
error,
1,
"Failed to open file '%.*s': File access flag 'execute' is not supported",
DN_Str8PrintFmt(path));
DN_InvalidCodePath; // TODO: Not supported via fopen
DN_ErrSinkAppendF(error, 1, "Failed to open file '%.*s': File access flag 'execute' is not supported", DN_Str8PrintFmt(path));
DN_AssertInvalidCodePath; // TODO: Not supported via fopen
return result;
}
@@ -470,7 +466,7 @@ DN_API DN_OSFile DN_OS_FileOpen(DN_Str8 path,
case DN_OSFileOpen_CreateAlways: handle = fopen(path.data, "w"); break;
case DN_OSFileOpen_OpenIfExist: handle = fopen(path.data, "r"); break;
case DN_OSFileOpen_OpenAlways: handle = fopen(path.data, "a"); break;
default: DN_InvalidCodePath; break;
default: DN_AssertInvalidCodePath; break;
}
if (!handle) { // TODO(doyle): FileOpen flag to string
@@ -572,7 +568,7 @@ DN_API void DN_OS_FileClose(DN_OSFile *file)
DN_API DN_OSPathInfo DN_OS_PathInfo(DN_Str8 path)
{
DN_OSPathInfo result = {};
if (path.size == 0)
if (path.count == 0)
return result;
struct stat file_stat;
@@ -596,7 +592,7 @@ DN_API DN_OSPathInfo DN_OS_PathInfo(DN_Str8 path)
DN_API bool DN_OS_PathDelete(DN_Str8 path)
{
bool result = false;
if (path.size)
if (path.count)
result = remove(path.data) == 0;
return result;
}
@@ -604,7 +600,7 @@ DN_API bool DN_OS_PathDelete(DN_Str8 path)
DN_API bool DN_OS_PathIsFile(DN_Str8 path)
{
bool result = false;
if (path.size == 0)
if (path.count == 0)
return result;
struct stat stat_result;
@@ -616,7 +612,7 @@ DN_API bool DN_OS_PathIsFile(DN_Str8 path)
DN_API bool DN_OS_PathIsDir(DN_Str8 path)
{
bool result = false;
if (path.size == 0)
if (path.count == 0)
return result;
struct stat stat_result;
@@ -636,8 +632,8 @@ DN_API bool DN_OS_PathMakeDir(DN_Str8 path)
uint16_t path_indexes[64] = {};
DN_Str8 copy = DN_Str8FromStr8Arena(path, &scratch.arena);
for (DN_USize index = copy.size - 1; index < copy.size; index--) {
bool first_char = index == (copy.size - 1);
for (DN_USize index = copy.count - 1; index < copy.count; index--) {
bool first_char = index == (copy.count - 1);
char ch = copy.data[index];
if (ch == '/' || first_char) {
char temp = copy.data[index];
@@ -700,12 +696,12 @@ DN_API bool DN_OS_PathIterateDir(DN_Str8 path, DN_OSDirIterator *it)
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
DN_USize name_size = DN_CStr8Size(entry->d_name);
DN_USize clamped_size = DN_Min(sizeof(it->buffer) - 1, name_size);
DN_AssertF(name_size == clamped_size, "name: %s, name_size: %zu, clamped_size: %zu", entry->d_name, name_size, clamped_size);
DN_Memcpy(it->buffer, entry->d_name, clamped_size);
it->buffer[clamped_size] = 0;
it->file_name = DN_Str8FromPtr(it->buffer, clamped_size);
DN_USize name_count = DN_CStr8Count(entry->d_name);
DN_USize clamped_count = DN_Min(sizeof(it->buffer) - 1, name_count);
DN_AssertF(name_count == clamped_count, "name: %s, name_size: %zu, clamped_size: %zu", entry->d_name, name_count, clamped_count);
DN_Memcpy(it->buffer, entry->d_name, clamped_count);
it->buffer[clamped_count] = 0;
it->file_name = DN_Str8FromPtr(it->buffer, clamped_count);
return true;
}
@@ -747,7 +743,7 @@ DN_API DN_OSExecResult DN_OS_ExecWait(DN_OSExecAsyncHandle handle,
}
#if defined(DN_PLATFORM_EMSCRIPTEN)
DN_InvalidCodePathF("Unsupported operation");
DN_AssertInvalidCodePathF("Unsupported operation");
#endif
static_assert(sizeof(pid_t) <= sizeof(handle.process),
@@ -834,10 +830,9 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line,
DN_ErrSink *error)
{
#if defined(DN_PLATFORM_EMSCRIPTEN)
DN_InvalidCodePathF("Unsupported operation");
DN_AssertInvalidCodePathF("Unsupported operation");
#endif
DN_AssertFOnce(args->environment.count == 0, "Unimplemented in POSIX");
DN_VerifyWarningF(args->environment.count == 0, "Environment variables are unimplemented in POSIX");
DN_OSExecAsyncHandle result = {};
if (cmd_line.count == 0)
return result;
@@ -967,7 +962,7 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line,
free(prev_working_dir);
};
if (args->working_dir.size) {
if (args->working_dir.count) {
prev_working_dir = get_current_dir_name();
DN_Str8 working_dir = DN_Str8FromStr8Arena(args->working_dir, &scratch.arena);
if (chdir(working_dir.data) == -1) {
@@ -1025,7 +1020,7 @@ DN_API DN_OSExecResult DN_OS_ExecPump(DN_OSExecAsyncHandle handle,
DN_U32 timeout_ms,
DN_ErrSink *err)
{
DN_InvalidCodePath;
DN_AssertInvalidCodePath;
DN_OSExecResult result = {};
return result;
}
@@ -1415,23 +1410,23 @@ DN_API DN_OSPosixProcSelfStatus DN_OS_PosixProcSelfStatus()
for (DN_ForItSize(line_it, DN_Str8, lines.data, lines.count)) {
DN_Str8 line = DN_Str8TrimWhitespaceAround(*line_it.data);
if (DN_Str8StartsWith(line, NAME, DN_Str8EqCase_Insensitive)) {
DN_Str8 str8 = DN_Str8TrimWhitespaceAround(DN_Str8Subset(line, NAME.size, line.size));
result.name_size = DN_Min(str8.size, sizeof(result.name));
DN_Str8 str8 = DN_Str8TrimWhitespaceAround(DN_Str8Subset(line, NAME.count, line.count));
result.name_size = DN_Min(str8.count, sizeof(result.name));
DN_Memcpy(result.name, str8.data, result.name_size);
} else if (DN_Str8StartsWith(line, PID, DN_Str8EqCase_Insensitive)) {
DN_Str8 str8 = DN_Str8TrimWhitespaceAround(DN_Str8Subset(line, PID.size, line.size));
DN_Str8 str8 = DN_Str8TrimWhitespaceAround(DN_Str8Subset(line, PID.count, line.count));
DN_U64FromResult to_u64 = DN_U64FromStr8(str8, 0);
result.pid = to_u64.value;
DN_Assert(to_u64.success);
} else if (DN_Str8StartsWith(line, VM_SIZE, DN_Str8EqCase_Insensitive)) {
DN_Str8 size_with_kb = DN_Str8TrimWhitespaceAround(DN_Str8Subset(line, VM_SIZE.size, line.size));
DN_Str8 size_with_kb = DN_Str8TrimWhitespaceAround(DN_Str8Subset(line, VM_SIZE.count, line.count));
DN_Assert(DN_Str8EndsWith(size_with_kb, DN_Str8Lit("kB")));
DN_Str8 vm_size = DN_Str8BSplit(size_with_kb, DN_Str8Lit(" ")).lhs;
DN_U64FromResult to_u64 = DN_U64FromStr8(vm_size, 0);
result.vm_size = DN_Kilobytes(to_u64.value);
DN_Assert(to_u64.success);
} else if (DN_Str8StartsWith(line, VM_PEAK, DN_Str8EqCase_Insensitive)) {
DN_Str8 size_with_kb = DN_Str8TrimWhitespaceAround(DN_Str8Subset(line, VM_PEAK.size, line.size));
DN_Str8 size_with_kb = DN_Str8TrimWhitespaceAround(DN_Str8Subset(line, VM_PEAK.count, line.count));
DN_Assert(DN_Str8EndsWith(size_with_kb, DN_Str8Lit("kB")));
DN_Str8 vm_size = DN_Str8BSplit(size_with_kb, DN_Str8Lit(" ")).lhs;
DN_U64FromResult to_u64 = DN_U64FromStr8(vm_size, 0);
@@ -1444,154 +1439,3 @@ DN_API DN_OSPosixProcSelfStatus DN_OS_PosixProcSelfStatus()
DN_OS_FileClose(&file);
return result;
}
// NOTE: DN_OSHttp /////////////////////////////////////////////////////////////////////////////////
#if 0 // TODO(doyle): Implement websockets for Windows and Emscripten
static EM_BOOL EMWebSocketOnOpenCallback(int type, const EmscriptenWebSocketOpenEvent *event, void *user_context)
{
(void)user_context;
(void)type;
(void)event;
// EMSCRIPTEN_RESULT result = emscripten_websocket_send_utf8_text(event->socket, R"({"jsonrpc":"2.0","id":1,"method": "eth_subscribe","params":["newHeads"]})");
// if (result)
// DN_LogInfoF("Failed to emscripten_websocket_send_utf8_text(): %d\n", result);
return EM_TRUE;
}
static EM_BOOL EMWebSocketOnMsgCallback(int type, const EmscriptenWebSocketMessageEvent *event __attribute__((nonnull)), void *user_context)
{
(void)type;
(void)user_context;
(void)event;
if (event->isText) {
DN_LogInfoF("Received: %.*s", event->numBytes, event->data);
} else {
DN_LogInfoF("Received: %d bytes", event->numBytes);
}
return EM_TRUE;
}
static EM_BOOL EMWebSocketOnErrorCallback(int type, const EmscriptenWebSocketErrorEvent *event, void *user_context)
{
(void)user_context;
(void)type;
(void)event;
return EM_TRUE;
}
static EM_BOOL EMWebSocketOnCloseCallback(int type, const EmscriptenWebSocketCloseEvent *event, void *user_context)
{
(void)user_context;
(void)type;
(void)event;
return EM_TRUE;
}
#endif
#if defined(DN_PLATFORM_EMSCRIPTEN)
static void DN_OS_HttpRequestEMFetchOnSuccessCallback(emscripten_fetch_t *fetch)
{
DN_OSHttpResponse *response = DN_Cast(DN_OSHttpResponse *) fetch->userData;
if (!DN_Check(response))
return;
response->http_status = DN_Cast(DN_U32) fetch->status;
response->body = DN_Str8AllocArena(fetch->numBytes, DN_ZMem_No, response->arena);
if (response->body.data)
DN_Memcpy(response->body.data, fetch->data, fetch->numBytes);
DN_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1);
DN_AtomicAddU32(&response->done, 1);
}
static void DN_OS_HttpRequestEMFetchOnErrorCallback(emscripten_fetch_t *fetch)
{
DN_OSHttpResponse *response = DN_Cast(DN_OSHttpResponse *) fetch->userData;
if (!DN_Check(response))
return;
response->http_status = DN_Cast(DN_U32) fetch->status;
response->body = DN_Str8AllocArena(fetch->numBytes, DN_ZMem_No, response->arena);
if (response->body.size)
DN_Memcpy(response->body.data, fetch->data, fetch->numBytes);
DN_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1);
DN_AtomicAddU32(&response->done, 1);
}
#endif
DN_API void DN_OS_HttpRequestAsync(DN_OSHttpResponse *response,
DN_Arena *arena,
DN_Str8 host,
DN_Str8 path,
DN_OSHttpRequestSecure secure,
DN_Str8 method,
DN_Str8 body,
DN_Str8 headers)
{
if (!response || !arena)
return;
response->arena = arena;
response->builder.arena = response->scratch_arena.mem ? &response->scratch_arena : &response->tmp_arena;
DN_Arena *scratch = &response->scratch_arena;
DN_TCScratch scratch_ = DN_TCScratchBeginArena(&arena, 1);
DN_DEFER { DN_TCScratchEnd(&scratch_); };
if (!scratch)
scratch = &scratch_.arena;
#if defined(DN_PLATFORM_EMSCRIPTEN)
emscripten_fetch_attr_t fetch_attribs = {};
emscripten_fetch_attr_init(&fetch_attribs);
if (method.size >= sizeof(fetch_attribs.requestMethod)) {
response->error_msg =
DN_Str8FromFmtArena(arena,
"Request method in EM has a size limit of 31 characters, method was "
"'%.*s' which is %zu characters long",
DN_Str8PrintFmt(method),
method.size);
DN_CheckF(method.size < sizeof(fetch_attribs.requestMethod),
"%.*s",
DN_Str8PrintFmt(response->error_msg));
response->error_code = DN_Cast(DN_U32) - 1;
DN_AtomicAddU32(&response->done, 1);
return;
}
DN_Memcpy(fetch_attribs.requestMethod, method.data, method.size);
fetch_attribs.requestData = body.data;
fetch_attribs.requestDataSize = body.size;
fetch_attribs.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
fetch_attribs.onsuccess = DN_OS_HttpRequestEMFetchOnSuccessCallback;
fetch_attribs.onerror = DN_OS_HttpRequestEMFetchOnErrorCallback;
fetch_attribs.userData = response;
DN_Str8 url = DN_Str8FromFmtArena(scratch, "%.*s%.*s", DN_Str8PrintFmt(host), DN_Str8PrintFmt(path));
DN_LogInfoF("Initiating HTTP '%s' request to '%.*s' with payload '%.*s'",
fetch_attribs.requestMethod,
DN_Str8PrintFmt(url),
DN_Str8PrintFmt(body));
response->on_complete_semaphore = DN_OS_SemaphoreInit(0);
response->em_handle = emscripten_fetch(&fetch_attribs, url.data);
#else // #elif defined(DN_OS_WIN32)
DN_InvalidCodePathF("Unimplemented function");
#endif
}
DN_API void DN_OS_HttpRequestFree(DN_OSHttpResponse *response)
{
// NOTE: Cleanup
#if defined(DN_PLATFORM_EMSCRIPTEN)
if (response->em_handle) {
emscripten_fetch_close(response->em_handle);
response->em_handle = nullptr;
}
#endif // #elif defined(DN_OS_WIN32)
DN_MemListDeinit(response->tmp_arena.mem);
DN_OS_SemaphoreDeinit(&response->on_complete_semaphore);
*response = {};
}
+69 -269
View File
@@ -100,7 +100,7 @@ DN_API int DN_OS_MemProtect(void *ptr, DN_USize size, DN_U32 page_flags)
DN_API void *DN_OS_MemAlloc(DN_USize size, DN_ZMem z_mem)
{
DN_Core *dn = DN_Get();
DN_RawAssert(dn->init_flags & DN_InitFlags_OS && "DN must be initialised with the OS flag");
DN_AssertRaw(dn->init_flags & DN_InitFlags_OS && "DN must be initialised with the OS flag");
DN_U32 flags = z_mem == DN_ZMem_Yes ? HEAP_ZERO_MEMORY : 0;
DN_Assert(size <= DN_Cast(DWORD)(-1));
void *result = HeapAlloc(GetProcessHeap(), flags, DN_Cast(DWORD) size);
@@ -336,11 +336,11 @@ DN_API bool DN_OS_FileMove(DN_Str8 src, DN_Str8 dest, bool overwrite, DN_ErrSink
DN_API DN_OSFile DN_OS_FileOpen(DN_Str8 path, DN_OSFileOpen open_mode, DN_OSFileAccess access, DN_ErrSink *err)
{
DN_OSFile result = {};
if (path.size == 0 || path.size <= 0)
if (path.count == 0 || path.count <= 0)
return result;
if ((access & ~DN_OSFileAccess_All) || ((access & DN_OSFileAccess_All) == 0)) {
DN_InvalidCodePath;
DN_AssertInvalidCodePath;
return result;
}
@@ -349,7 +349,7 @@ DN_API DN_OSFile DN_OS_FileOpen(DN_Str8 path, DN_OSFileOpen open_mode, DN_OSFile
case DN_OSFileOpen_CreateAlways: create_flag = CREATE_ALWAYS; break;
case DN_OSFileOpen_OpenIfExist: create_flag = OPEN_EXISTING; break;
case DN_OSFileOpen_OpenAlways: create_flag = OPEN_ALWAYS; break;
default: DN_InvalidCodePath; return result;
default: DN_AssertInvalidCodePath; return result;
}
unsigned long access_mode = 0;
@@ -395,15 +395,9 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size
if (!file || !file->handle || file->error || !buffer || size <= 0)
return result;
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
if (!DN_Check(size <= (unsigned long)-1)) {
DN_Str8x32 buffer_size_str8 = DN_Str8x32FromByteCountU64Auto(size);
DN_ErrSinkAppendF(
err,
1 /*error_code*/,
"Current implementation doesn't support reading >4GiB file (requested %.*s), implement Win32 overlapped IO",
DN_Str8PrintFmt(buffer_size_str8));
DN_TCScratchEnd(&scratch);
if (size > ULONG_MAX) {
DN_Str8x32 desc = DN_Str8x32FromByteCountU64Auto(size);
DN_ErrSinkAppendF(err, 1 /*error_code*/, "Current implementation doesn't support reading >4GiB file (requested %.*s), implement Win32 overlapped IO", DN_Str8PrintFmt(desc));
return result;
}
@@ -414,6 +408,7 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size
/*LPDWORD lpNumberOfByesRead*/ &bytes_read,
/*LPOVERLAPPED lpOverlapped*/ nullptr);
if (read_result == 0) {
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
DN_ErrSinkAppendF(err, win_error.code, "Failed to read data from file: (%u) %.*s", win_error.code, DN_Str8PrintFmt(win_error.msg));
DN_TCScratchEnd(&scratch);
@@ -421,6 +416,7 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size
}
if (bytes_read != size) {
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
DN_ErrSinkAppendF(
err,
@@ -436,7 +432,6 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size
result.bytes_read = bytes_read;
result.success = true;
DN_TCScratchEnd(&scratch);
return result;
}
@@ -491,7 +486,7 @@ DN_API void DN_OS_FileClose(DN_OSFile *file)
DN_API DN_OSPathInfo DN_OS_PathInfo(DN_Str8 path)
{
DN_OSPathInfo result = {};
if (path.size == 0)
if (path.count == 0)
return result;
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
@@ -527,12 +522,12 @@ result.create_time_in_s = DN_OS_W32FileTimeToSeconds_(&attrib_data.ftCreati
DN_API bool DN_OS_PathDelete(DN_Str8 path)
{
bool result = false;
if (path.size == 0)
if (path.count == 0)
return result;
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
DN_Str16 path16 = DN_OS_W32Str8ToStr16(&scratch.arena, path);
if (path16.size) {
if (path16.count) {
result = DeleteFileW(path16.data);
if (!result)
result = RemoveDirectoryW(path16.data);
@@ -544,12 +539,12 @@ DN_API bool DN_OS_PathDelete(DN_Str8 path)
DN_API bool DN_OS_PathIsFile(DN_Str8 path)
{
bool result = false;
if (path.size == 0)
if (path.count == 0)
return result;
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
DN_Str16 path16 = DN_OS_W32Str8ToStr16(&scratch.arena, path);
if (path16.size) {
if (path16.count) {
WIN32_FILE_ATTRIBUTE_DATA attrib_data = {};
if (GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data))
result = (attrib_data.dwFileAttributes != INVALID_FILE_ATTRIBUTES) &&
@@ -562,12 +557,12 @@ DN_API bool DN_OS_PathIsFile(DN_Str8 path)
DN_API bool DN_OS_PathIsDir(DN_Str8 path)
{
bool result = false;
if (path.size == 0)
if (path.count == 0)
return result;
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
DN_Str16 path16 = DN_OS_W32Str8ToStr16(&scratch.arena, path);
if (path16.size) {
if (path16.count) {
WIN32_FILE_ATTRIBUTE_DATA attrib_data = {};
if (GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data))
result = (attrib_data.dwFileAttributes != INVALID_FILE_ATTRIBUTES) &&
@@ -596,8 +591,8 @@ DN_API bool DN_OS_PathMakeDir(DN_Str8 path)
// If we find a file at some point in the path we fail out because the
// series of directories can not be made if a file exists with the same
// name.
for (DN_USize index = 0; index < path16.size; index++) {
bool first_char = index == (path16.size - 1);
for (DN_USize index = 0; index < path16.count; index++) {
bool first_char = index == (path16.count - 1);
wchar_t ch = path16.data[index];
if (ch == '/' || ch == '\\' || first_char) {
wchar_t temp = path16.data[index];
@@ -633,7 +628,7 @@ DN_API bool DN_OS_PathMakeDir(DN_Str8 path)
DN_API bool DN_OS_PathIterateDir(DN_Str8 path, DN_OSDirIterator *it)
{
if (path.size == 0 || !it || path.size <= 0)
if (path.count == 0 || !it || path.count <= 0)
return false;
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
@@ -659,7 +654,7 @@ DN_API bool DN_OS_PathIterateDir(DN_Str8 path, DN_OSDirIterator *it)
}
path16 = DN_OS_W32Str8ToStr16(&scratch.arena, adjusted_path);
if (path16.size <= 0) { // Conversion error
if (path16.count <= 0) { // Conversion error
DN_TCScratchEnd(&scratch);
return false;
}
@@ -732,7 +727,7 @@ DN_API DN_OSExecResult DN_OS_ExecPump(DN_OSExecAsyncHandle handle,
DN_ErrSinkAppendF(err, result.os_error_code, "Executed command failed to terminate: %.*s", DN_Str8PrintFmt(win_error.msg));
DN_TCScratchEnd(&scratch);
return result;
} else if (DN_Check(exec_result == WAIT_TIMEOUT || exec_result == WAIT_OBJECT_0)) {
} else if (exec_result == WAIT_TIMEOUT || exec_result == WAIT_OBJECT_0) {
// NOTE: Read stdout from process
// If the pipes are full, the process will block. We periodically
// flush the pipes to make sure this doesn't happen
@@ -841,7 +836,7 @@ DN_API DN_OSExecResult DN_OS_ExecWait(DN_OSExecAsyncHandle handle, DN_Arena *are
result = DN_OS_ExecPump(handle, stdout_buffer, &stdout_size, stderr_buffer, &stderr_size, wait_ms, err);
DN_Str8BuilderAppendCopy(&stdout_builder, result.stdout_text);
DN_Str8BuilderAppendCopy(&stderr_builder, result.stderr_text);
wait_ms = (result.stdout_text.size || result.stderr_text.size) ? FAST_WAIT_TIME_MS : SLOW_WAIT_TIME_MS;
wait_ms = (result.stdout_text.count || result.stderr_text.count) ? FAST_WAIT_TIME_MS : SLOW_WAIT_TIME_MS;
}
// NOTE: Get stdout/stderr. If no arena is passed this is a no-op
@@ -870,7 +865,7 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_OSExecArgs
DN_Str8 env_block8 = DN_Str8FromStr8BuilderDelimitArena(&env_builder, DN_Str8Lit("\0"), &scratch.arena);
DN_Str16 env_block16 = {};
if (env_block8.size)
if (env_block8.count)
env_block16 = DN_OS_W32Str8ToStr16(&scratch.arena, env_block8);
// NOTE: Stdout/err security attributes
@@ -1341,204 +1336,6 @@ DN_API void DN_OS_W32ThreadSetName(DN_Str8 name)
DN_TCScratchEnd(&scratch);
}
void DN_OS_HttpRequestWin32Callback(HINTERNET session, DWORD *dwContext, DWORD dwInternetStatus, VOID *lpvStatusInformation, DWORD dwStatusInformationLength)
{
(void)session;
(void)dwStatusInformationLength;
DN_OSHttpResponse *response = DN_Cast(DN_OSHttpResponse *) dwContext;
HINTERNET request = DN_Cast(HINTERNET) response->w32_request_handle;
DN_OSW32Error error = {};
DWORD const READ_BUFFER_SIZE = DN_Megabytes(1);
if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_RESOLVING_NAME) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_NAME_RESOLVED) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SENDING_REQUEST) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_SENT) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HANDLE_CREATED) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_DETECTING_PROXY) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_REDIRECT) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SECURE_FAILURE) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE) {
DWORD status = 0;
DWORD status_size = sizeof(status_size);
if (WinHttpQueryHeaders(request,
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX,
&status,
&status_size,
WINHTTP_NO_HEADER_INDEX)) {
response->http_status = DN_Cast(uint16_t) status;
// NOTE: You can normally call into WinHttpQueryDataAvailable which means the kernel
// will buffer the response into a single buffer and return us the full size of the
// request.
//
// or
//
// You may call WinHttpReadData directly to write the memory into our buffer directly.
// This is advantageous to avoid a copy from the kernel buffer into our buffer. If the
// end user application knows the typical payload size then they can optimise for this
// to prevent unnecessary allocation on the user side.
void *buffer = DN_ArenaAlloc(response->builder.arena, READ_BUFFER_SIZE, 1 /*align*/, DN_ZMem_No);
if (!WinHttpReadData(request, buffer, READ_BUFFER_SIZE, nullptr))
error = DN_OS_W32LastError(&response->tmp_arena);
} else {
error = DN_OS_W32LastError(&response->tmp_arena);
}
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_READ_COMPLETE) {
DWORD bytes_read = dwStatusInformationLength;
if (bytes_read) {
DN_Str8 prev_buffer = DN_Str8FromPtr(DN_Cast(char *) lpvStatusInformation, bytes_read);
DN_Str8BuilderAppendRef(&response->builder, prev_buffer);
void *buffer = DN_ArenaAlloc(response->builder.arena, READ_BUFFER_SIZE, 1 /*align*/, DN_ZMem_No);
if (!WinHttpReadData(request, buffer, READ_BUFFER_SIZE, nullptr))
error = DN_OS_W32LastError(&response->tmp_arena);
}
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR) {
WINHTTP_ASYNC_RESULT *async_result = DN_Cast(WINHTTP_ASYNC_RESULT *) lpvStatusInformation;
error = DN_OS_W32ErrorCodeToMsg(&response->tmp_arena, DN_Cast(DN_U32) async_result->dwError);
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE) {
if (!WinHttpReceiveResponse(request, 0))
error = DN_OS_W32LastError(&response->tmp_arena);
}
// NOTE: If the request handle is missing, then, the response has been freed.
// MSDN says that this callback can still be called after closing the handle
// and trigger the WINHTTP_CALLBACK_STATUS_REQUEST_ERROR.
if (request) {
bool read_complete = dwInternetStatus == WINHTTP_CALLBACK_STATUS_READ_COMPLETE && dwStatusInformationLength == 0;
if (read_complete)
response->body = DN_Str8FromStr8BuilderArena(&response->builder, response->arena);
if (read_complete || dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR || error.code) {
DN_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1);
DN_AtomicAddU32(&response->done, 1);
}
if (error.code) {
response->error_code = error.code;
response->error_msg = error.msg;
}
}
}
DN_API void DN_OS_HttpRequestAsync(DN_OSHttpResponse *response,
DN_Arena *arena,
DN_Str8 host,
DN_Str8 path,
DN_OSHttpRequestSecure secure,
DN_Str8 method,
DN_Str8 body,
DN_Str8 headers)
{
if (!response || !arena)
return;
response->arena = arena;
response->builder = DN_Str8BuilderFromArena(response->scratch_arena.mem ? &response->scratch_arena : &response->tmp_arena);
DN_TCScratch scratch_ = DN_TCScratchBeginArena(&arena, 1);
if (!response->scratch_arena.mem)
response->scratch_arena = scratch_.arena;
DN_OSW32Error error = {};
DN_DEFER
{
response->error_msg = error.msg;
response->error_code = error.code;
if (error.code) {
// NOTE: 'Wait' handles failures gracefully, skipping the wait and
// cleans up the request
DN_OS_HttpRequestWait(response);
DN_AtomicAddU32(&response->done, 1);
}
DN_TCScratchEnd(&scratch_);
};
response->w32_request_session = WinHttpOpen(nullptr /*user agent*/, WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC);
if (!response->w32_request_session) {
error = DN_OS_W32LastError(&response->tmp_arena);
return;
}
DWORD callback_flags = WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE |
WINHTTP_CALLBACK_STATUS_READ_COMPLETE |
WINHTTP_CALLBACK_STATUS_REQUEST_ERROR |
WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE;
if (WinHttpSetStatusCallback(response->w32_request_session,
DN_Cast(WINHTTP_STATUS_CALLBACK) DN_OS_HttpRequestWin32Callback,
callback_flags,
DN_Cast(DWORD_PTR) nullptr /*dwReserved*/) == WINHTTP_INVALID_STATUS_CALLBACK) {
error = DN_OS_W32LastError(&response->tmp_arena);
return;
}
DN_Str16 host16 = DN_OS_W32Str8ToStr16(&response->scratch_arena, host);
response->w32_request_connection = WinHttpConnect(response->w32_request_session, host16.data, secure ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT, 0 /*reserved*/);
if (!response->w32_request_connection) {
error = DN_OS_W32LastError(&response->tmp_arena);
return;
}
DN_Str16 method16 = DN_OS_W32Str8ToStr16(&response->scratch_arena, method);
DN_Str16 path16 = DN_OS_W32Str8ToStr16(&response->scratch_arena, path);
response->w32_request_handle = WinHttpOpenRequest(response->w32_request_connection,
method16.data,
path16.data,
nullptr /*version*/,
nullptr /*referrer*/,
nullptr /*accept types*/,
secure ? WINHTTP_FLAG_SECURE : 0);
if (!response->w32_request_handle) {
error = DN_OS_W32LastError(&response->tmp_arena);
return;
}
DN_Str16 headers16 = DN_OS_W32Str8ToStr16(&response->scratch_arena, headers);
response->on_complete_semaphore = DN_OS_SemaphoreInit(0);
if (!WinHttpSendRequest(response->w32_request_handle,
headers16.data,
DN_Cast(DWORD) headers16.size,
body.data /*optional data*/,
DN_Cast(DWORD) body.size /*optional length*/,
DN_Cast(DWORD) body.size /*total content length*/,
DN_Cast(DWORD_PTR) response)) {
error = DN_OS_W32LastError(&response->tmp_arena);
return;
}
}
DN_API void DN_OS_HttpRequestFree(DN_OSHttpResponse *response)
{
// NOTE: Cleanup
// NOTE: These calls are synchronous even when the HTTP request is async.
WinHttpCloseHandle(response->w32_request_handle);
WinHttpCloseHandle(response->w32_request_connection);
WinHttpCloseHandle(response->w32_request_session);
response->w32_request_session = nullptr;
response->w32_request_connection = nullptr;
response->w32_request_handle = nullptr;
DN_MemListDeinit(response->tmp_arena.mem);
DN_OS_SemaphoreDeinit(&response->on_complete_semaphore);
*response = {};
}
// NOTE: DN_OS_W32
DN_API DN_Str16 DN_OS_W32ErrorCodeToMsg16Alloc(DN_U32 error_code)
{
DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
@@ -1549,7 +1346,7 @@ DN_API DN_Str16 DN_OS_W32ErrorCodeToMsg16Alloc(DN_U32 error_code)
}
wchar_t *result16 = nullptr;
DWORD size = FormatMessageW(/*DWORD dwFlags */ flags | FORMAT_MESSAGE_ALLOCATE_BUFFER,
DWORD count = FormatMessageW(/*DWORD dwFlags */ flags | FORMAT_MESSAGE_ALLOCATE_BUFFER,
/*LPCVOID lpSource */ module_to_get_errors_from,
/*DWORD dwMessageId */ error_code,
/*DWORD dwLanguageId*/ 0,
@@ -1559,7 +1356,7 @@ DN_API DN_Str16 DN_OS_W32ErrorCodeToMsg16Alloc(DN_U32 error_code)
DN_Str16 result = {};
result.data = result16;
result.size = size;
result.count = count;
return result;
}
@@ -1568,7 +1365,7 @@ DN_API DN_OSW32Error DN_OS_W32ErrorCodeToMsgAlloc(DN_U32 error_code)
DN_OSW32Error result = {};
result.code = error_code;
DN_Str16 error16 = DN_OS_W32ErrorCodeToMsg16Alloc(error_code);
if (error16.size)
if (error16.count)
result.msg = DN_OS_W32Str16ToStr8FromHeap(error16);
if (error16.data)
LocalFree(error16.data);
@@ -1581,7 +1378,7 @@ DN_API DN_OSW32Error DN_OS_W32ErrorCodeToMsg(DN_Arena *arena, DN_U32 error_code)
result.code = error_code;
if (arena) {
DN_Str16 error16 = DN_OS_W32ErrorCodeToMsg16Alloc(error_code);
if (error16.size)
if (error16.count)
result.msg = DN_OS_W32Str16ToStr8(arena, error16);
if (error16.data)
LocalFree(error16.data);
@@ -1626,10 +1423,10 @@ DN_API void DN_OS_W32MakeProcessDPIAware()
DN_API DN_Str16 DN_OS_W32Str8ToStr16(DN_Arena *arena, DN_Str8 src)
{
DN_Str16 result = {};
if (!arena || src.size == 0)
if (!arena || src.count == 0)
return result;
int required_size = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.size, nullptr /*dest*/, 0 /*dest size*/);
int required_size = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.count, nullptr /*dest*/, 0 /*dest size*/);
if (required_size <= 0)
return result;
@@ -1637,11 +1434,12 @@ DN_API DN_Str16 DN_OS_W32Str8ToStr16(DN_Arena *arena, DN_Str8 src)
if (!buffer)
return result;
int chars_written = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.size, buffer, required_size);
if (DN_Check(chars_written == required_size)) {
int chars_written = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.count, buffer, required_size);
DN_Assert(chars_written == required_size);
if (chars_written == required_size) {
result.data = buffer;
result.size = chars_written;
result.data[result.size] = 0;
result.count = chars_written;
result.data[result.count] = 0;
}
return result;
}
@@ -1649,14 +1447,14 @@ DN_API DN_Str16 DN_OS_W32Str8ToStr16(DN_Arena *arena, DN_Str8 src)
DN_API int DN_OS_W32Str8ToStr16Buffer(DN_Str8 src, wchar_t *dest, int dest_size)
{
int result = 0;
if (src.size == 0)
if (src.count == 0)
return result;
result = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.size, nullptr /*dest*/, 0 /*dest size*/);
result = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.count, nullptr /*dest*/, 0 /*dest size*/);
if (result <= 0 || result > dest_size || !dest)
return result;
result = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.size, dest, DN_Cast(int) dest_size);
result = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.count, dest, DN_Cast(int) dest_size);
dest[DN_Min(result, dest_size - 1)] = 0;
return result;
}
@@ -1664,10 +1462,10 @@ DN_API int DN_OS_W32Str8ToStr16Buffer(DN_Str8 src, wchar_t *dest, int dest_size)
DN_API int DN_OS_W32Str16ToStr8Buffer(DN_Str16 src, char *dest, int dest_size)
{
int result = 0;
if (src.size == 0)
if (src.count == 0)
return result;
int src_size = DN_SaturateCastISizeToInt(src.size);
int src_size = DN_SaturateCastISizeToInt(src.count);
if (src_size <= 0)
return result;
@@ -1683,10 +1481,10 @@ DN_API int DN_OS_W32Str16ToStr8Buffer(DN_Str16 src, char *dest, int dest_size)
DN_API DN_Str8 DN_OS_W32Str16ToStr8(DN_Arena *arena, DN_Str16 src)
{
DN_Str8 result = {};
if (!arena || src.size == 0)
if (!arena || src.count == 0)
return result;
int src_size = DN_SaturateCastISizeToInt(src.size);
int src_size = DN_SaturateCastISizeToInt(src.count);
if (src_size <= 0)
return result;
@@ -1698,24 +1496,25 @@ DN_API DN_Str8 DN_OS_W32Str16ToStr8(DN_Arena *arena, DN_Str16 src)
// null-termination already so no-need to +1 the required size
DN_Arena temp = DN_ArenaTempBeginFromArena(arena);
DN_Str8 buffer = DN_Str8AllocArena(required_size, DN_ZMem_No, &temp);
if (buffer.size) {
int chars_written = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, buffer.data, DN_Cast(int) buffer.size, nullptr, nullptr);
if (DN_Check(chars_written == required_size)) {
if (buffer.count) {
int chars_written = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, buffer.data, DN_Cast(int) buffer.count, nullptr, nullptr);
DN_Assert(chars_written == required_size);
if (chars_written == required_size) {
result = buffer;
result.data[result.size] = 0;
result.data[result.count] = 0;
}
}
DN_ArenaTempEnd(&temp, result.size == DN_Cast(DN_USize)required_size ? DN_ArenaReset_No : DN_ArenaReset_Yes);
DN_ArenaTempEnd(&temp, result.count == DN_Cast(DN_USize)required_size ? DN_ArenaReset_No : DN_ArenaReset_Yes);
return result;
}
DN_API DN_Str8 DN_OS_W32Str16ToStr8FromHeap(DN_Str16 src)
{
DN_Str8 result = {};
if (src.size == 0)
if (src.count == 0)
return result;
int src_size = DN_SaturateCastISizeToInt(src.size);
int src_size = DN_SaturateCastISizeToInt(src.count);
if (src_size <= 0)
return result;
@@ -1726,13 +1525,14 @@ DN_API DN_Str8 DN_OS_W32Str16ToStr8FromHeap(DN_Str16 src)
// NOTE: Str8 allocate ensures there's one extra byte for
// null-termination already so no-need to +1 the required size
DN_Str8 buffer = DN_Str8FromHeap(required_size, DN_ZMem_No);
if (buffer.size == 0)
if (buffer.count == 0)
return result;
int chars_written = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, buffer.data, DN_Cast(int) buffer.size, nullptr, nullptr);
if (DN_Check(chars_written == required_size)) {
int chars_written = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, buffer.data, DN_Cast(int) buffer.count, nullptr, nullptr);
DN_Assert(chars_written == required_size);
if (chars_written == required_size) {
result = buffer;
result.data[result.size] = 0;
result.data[result.count] = 0;
} else {
DN_OS_MemDealloc(buffer.data);
buffer = {};
@@ -1763,9 +1563,9 @@ DN_API DN_Str16 DN_OS_W32EXEPathW(DN_Arena *arena)
index_of_last_slash = module_path[index] == '\\' ? index : 0;
result.data = DN_ArenaNewArray(arena, wchar_t, module_size + 1, DN_ZMem_No);
result.size = module_size;
DN_Memcpy(result.data, module_path, sizeof(wchar_t) * result.size);
result.data[result.size] = 0;
result.count = module_size;
DN_Memcpy(result.data, module_path, sizeof(wchar_t) * result.count);
result.data[result.count] = 0;
DN_TCScratchEnd(&scratch);
return result;
}
@@ -1792,9 +1592,9 @@ DN_API DN_Str16 DN_OS_W32EXEDirW(DN_Arena *arena)
index_of_last_slash = module_path[index] == '\\' ? index : 0;
result.data = DN_ArenaNewArray(arena, wchar_t, index_of_last_slash + 1, DN_ZMem_No);
result.size = index_of_last_slash;
DN_Memcpy(result.data, module_path, sizeof(wchar_t) * result.size);
result.data[result.size] = 0;
result.count = index_of_last_slash;
DN_Memcpy(result.data, module_path, sizeof(wchar_t) * result.count);
result.data[result.count] = 0;
DN_TCScratchEnd(&scratch);
return result;
}
@@ -1811,13 +1611,13 @@ DN_API DN_Str8 DN_OS_W32WorkingDir(DN_Arena *arena, DN_Str8 suffix)
DN_API DN_Str16 DN_OS_W32WorkingDirW(DN_Arena *arena, DN_Str16 suffix)
{
DN_Assert(suffix.size >= 0);
DN_Assert(suffix.count >= 0);
DN_Str16 result = {};
// NOTE: required_size is the size required *including* the null-terminator
DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1);
unsigned long required_size = GetCurrentDirectoryW(0, nullptr);
unsigned long desired_size = required_size + DN_Cast(unsigned long) suffix.size;
unsigned long desired_size = required_size + DN_Cast(unsigned long) suffix.count;
wchar_t *scratch_w_path = DN_ArenaNewArray(&scratch.arena, wchar_t, desired_size, DN_ZMem_No);
if (!scratch_w_path) {
@@ -1838,9 +1638,9 @@ DN_API DN_Str16 DN_OS_W32WorkingDirW(DN_Arena *arena, DN_Str16 suffix)
return result;
}
if (suffix.size) {
if (suffix.count) {
DN_Memcpy(w_path, scratch_w_path, sizeof(*scratch_w_path) * bytes_written_wo_null_terminator);
DN_Memcpy(w_path + bytes_written_wo_null_terminator, suffix.data, sizeof(suffix.data[0]) * suffix.size);
DN_Memcpy(w_path + bytes_written_wo_null_terminator, suffix.data, sizeof(suffix.data[0]) * suffix.count);
w_path[desired_size] = 0;
}
@@ -1876,14 +1676,14 @@ DN_API bool DN_OS_W32DirWIterate(DN_Str16 path, DN_OSW32FolderIteratorW *it)
if (find_data.cFileName[0] == '.' || (find_data.cFileName[0] == '.' && find_data.cFileName[1] == '.'))
continue;
it->file_name.size = DN_CStr16Size(find_data.cFileName);
DN_Assert(it->file_name.size < (DN_ArrayCountU(it->file_name_buf) - 1));
DN_Memcpy(it->file_name.data, find_data.cFileName, it->file_name.size * sizeof(wchar_t));
it->file_name_buf[it->file_name.size] = 0;
it->file_name.count = DN_CStr16Count(find_data.cFileName);
DN_Assert(it->file_name.count < (DN_ArrayCountU(it->file_name_buf) - 1));
DN_Memcpy(it->file_name.data, find_data.cFileName, it->file_name.count * sizeof(wchar_t));
it->file_name_buf[it->file_name.count] = 0;
break;
} while (FindNextFileW(it->handle, &find_data) != 0);
bool result = it->file_name.size > 0;
bool result = it->file_name.count > 0;
if (!result)
FindClose(it->handle);
return result;
-271
View File
@@ -1,271 +0,0 @@
#define DN_SIMD_AVX512F_CPP
#include <immintrin.h>
DN_API DN_Str8FindResult DN_SIMD_Str8FindAVX512F(DN_Str8 string, DN_Str8 find)
{
// NOTE: Algorithm as described in http://0x80.pl/articles/simd-strfind.html
DN_Str8FindResult result = {};
if (string.size == 0 || find.size == 0 || find.size > string.size)
return result;
__m512i const find_first_ch = _mm512_set1_epi8(find.data[0]);
__m512i const find_last_ch = _mm512_set1_epi8(find.data[find.size - 1]);
DN_USize const search_size = string.size - find.size;
DN_USize simd_iterations = search_size / sizeof(__m512i);
char const *ptr = string.data;
while (simd_iterations--) {
__m512i find_first_ch_block = _mm512_loadu_si512(ptr);
__m512i find_last_ch_block = _mm512_loadu_si512(ptr + find.size - 1);
// NOTE: AVX512F does not have a cmpeq so we use XOR to place a 0 bit
// where matches are found.
__m512i first_ch_matches = _mm512_xor_si512(find_first_ch_block, find_first_ch);
// NOTE: We can combine the 2nd XOR and merge the 2 XOR results into one
// operation using the ternarylogic intrinsic.
//
// A = first_ch_matches (find_first_ch_block ^ find_first_ch)
// B = find_last_ch_block
// C = find_last_ch
//
// ternarylogic op => A | (B ^ C) => 0b1111'0110 => 0xf6
//
// / A / B / C / B ^ C / A | (B ^ C) /
// | 0 | 0 | 0 | 0 | 0 |
// | 0 | 0 | 1 | 1 | 1 |
// | 0 | 1 | 0 | 1 | 1 |
// | 0 | 1 | 1 | 0 | 0 |
// | 1 | 0 | 0 | 0 | 1 |
// | 1 | 0 | 1 | 1 | 1 |
// | 1 | 1 | 0 | 1 | 1 |
// | 1 | 1 | 1 | 0 | 1 |
__m512i ch_matches = _mm512_ternarylogic_epi32(first_ch_matches, find_last_ch_block, find_last_ch, 0xf6);
// NOTE: Matches were XOR-ed and are hence indicated as zero so we mask
// out which 32 bit elements in the vector had zero bytes. This uses a
// bit twiddling trick
// https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord
__mmask16 zero_byte_mask = {};
{
const __m512i v01 = _mm512_set1_epi32(0x01010101u);
const __m512i v80 = _mm512_set1_epi32(0x80808080u);
const __m512i v1 = _mm512_sub_epi32(ch_matches, v01);
const __m512i tmp1 = _mm512_ternarylogic_epi32(v1, ch_matches, v80, 0x20);
zero_byte_mask = _mm512_test_epi32_mask(tmp1, tmp1);
}
while (zero_byte_mask) {
uint64_t const lsb_zero_pos = _tzcnt_u64(zero_byte_mask);
char const *base_ptr = ptr + (4 * lsb_zero_pos);
if (DN_MEMCMP(base_ptr + 0, find.data, find.size) == 0) {
result.found = true;
result.index = base_ptr - string.data;
} else if (DN_MEMCMP(base_ptr + 1, find.data, find.size) == 0) {
result.found = true;
result.index = base_ptr - string.data + 1;
} else if (DN_MEMCMP(base_ptr + 2, find.data, find.size) == 0) {
result.found = true;
result.index = base_ptr - string.data + 2;
} else if (DN_MEMCMP(base_ptr + 3, find.data, find.size) == 0) {
result.found = true;
result.index = base_ptr - string.data + 3;
}
if (result.found) {
result.start_to_before_match = DN_Str8FromPtr(string.data, result.index);
result.match = DN_Str8FromPtr(string.data + result.index, find.size);
result.match_to_end_of_buffer = DN_Str8FromPtr(result.match.data, string.size - result.index);
result.after_match_to_end_of_buffer = DN_Str8Advance(result.match_to_end_of_buffer, find.size);
return result;
}
zero_byte_mask = DN_Bit_ClearNextLSB(zero_byte_mask);
}
ptr += sizeof(__m512i);
}
for (DN_USize index = ptr - string.data; index < string.size; index++) {
DN_Str8 string_slice = DN_Str8Slice(string, index, find.size);
if (DN_Str8Eq(string_slice, find)) {
result.found = true;
result.index = index;
result.start_to_before_match = DN_Str8FromPtr(string.data, index);
result.match = DN_Str8FromPtr(string.data + index, find.size);
result.match_to_end_of_buffer = DN_Str8FromPtr(result.match.data, string.size - index);
result.after_match_to_end_of_buffer = DN_Str8Advance(result.match_to_end_of_buffer, find.size);
return result;
}
}
return result;
}
DN_API DN_Str8FindResult DN_SIMD_Str8FindLastAVX512F(DN_Str8 string, DN_Str8 find)
{
// NOTE: Algorithm as described in http://0x80.pl/articles/simd-strfind.html
DN_Str8FindResult result = {};
if (string.size == 0 || find.size == 0 || find.size > string.size)
return result;
__m512i const find_first_ch = _mm512_set1_epi8(find.data[0]);
__m512i const find_last_ch = _mm512_set1_epi8(find.data[find.size - 1]);
DN_USize const search_size = string.size - find.size;
DN_USize simd_iterations = search_size / sizeof(__m512i);
char const *ptr = string.data + search_size + 1;
while (simd_iterations--) {
ptr -= sizeof(__m512i);
__m512i find_first_ch_block = _mm512_loadu_si512(ptr);
__m512i find_last_ch_block = _mm512_loadu_si512(ptr + find.size - 1);
// NOTE: AVX512F does not have a cmpeq so we use XOR to place a 0 bit
// where matches are found.
__m512i first_ch_matches = _mm512_xor_si512(find_first_ch_block, find_first_ch);
// NOTE: We can combine the 2nd XOR and merge the 2 XOR results into one
// operation using the ternarylogic intrinsic.
//
// A = first_ch_matches (find_first_ch_block ^ find_first_ch)
// B = find_last_ch_block
// C = find_last_ch
//
// ternarylogic op => A | (B ^ C) => 0b1111'0110 => 0xf6
//
// / A / B / C / B ^ C / A | (B ^ C) /
// | 0 | 0 | 0 | 0 | 0 |
// | 0 | 0 | 1 | 1 | 1 |
// | 0 | 1 | 0 | 1 | 1 |
// | 0 | 1 | 1 | 0 | 0 |
// | 1 | 0 | 0 | 0 | 1 |
// | 1 | 0 | 1 | 1 | 1 |
// | 1 | 1 | 0 | 1 | 1 |
// | 1 | 1 | 1 | 0 | 1 |
__m512i ch_matches = _mm512_ternarylogic_epi32(first_ch_matches, find_last_ch_block, find_last_ch, 0xf6);
// NOTE: Matches were XOR-ed and are hence indicated as zero so we mask
// out which 32 bit elements in the vector had zero bytes. This uses a
// bit twiddling trick
// https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord
__mmask16 zero_byte_mask = {};
{
const __m512i v01 = _mm512_set1_epi32(0x01010101u);
const __m512i v80 = _mm512_set1_epi32(0x80808080u);
const __m512i v1 = _mm512_sub_epi32(ch_matches, v01);
const __m512i tmp1 = _mm512_ternarylogic_epi32(v1, ch_matches, v80, 0x20);
zero_byte_mask = _mm512_test_epi32_mask(tmp1, tmp1);
}
while (zero_byte_mask) {
uint64_t const lsb_zero_pos = _tzcnt_u64(zero_byte_mask);
char const *base_ptr = ptr + (4 * lsb_zero_pos);
if (DN_MEMCMP(base_ptr + 0, find.data, find.size) == 0) {
result.found = true;
result.index = base_ptr - string.data;
} else if (DN_MEMCMP(base_ptr + 1, find.data, find.size) == 0) {
result.found = true;
result.index = base_ptr - string.data + 1;
} else if (DN_MEMCMP(base_ptr + 2, find.data, find.size) == 0) {
result.found = true;
result.index = base_ptr - string.data + 2;
} else if (DN_MEMCMP(base_ptr + 3, find.data, find.size) == 0) {
result.found = true;
result.index = base_ptr - string.data + 3;
}
if (result.found) {
result.start_to_before_match = DN_Str8FromPtr(string.data, result.index);
result.match = DN_Str8FromPtr(string.data + result.index, find.size);
result.match_to_end_of_buffer = DN_Str8FromPtr(result.match.data, string.size - result.index);
return result;
}
zero_byte_mask = DN_Bit_ClearNextLSB(zero_byte_mask);
}
}
for (DN_USize index = ptr - string.data - 1; index < string.size; index--) {
DN_Str8 string_slice = DN_Str8Slice(string, index, find.size);
if (DN_Str8Eq(string_slice, find)) {
result.found = true;
result.index = index;
result.start_to_before_match = DN_Str8FromPtr(string.data, index);
result.match = DN_Str8FromPtr(string.data + index, find.size);
result.match_to_end_of_buffer = DN_Str8FromPtr(result.match.data, string.size - index);
return result;
}
}
return result;
}
DN_API DN_Str8BSplitResult DN_SIMD_Str8BSplitAVX512F(DN_Str8 string, DN_Str8 find)
{
DN_Str8BSplitResult result = {};
DN_Str8FindResult find_result = DN_SIMD_Str8FindAVX512F(string, find);
if (find_result.found) {
result.lhs.data = string.data;
result.lhs.size = find_result.index;
result.rhs = DN_Str8Advance(find_result.match_to_end_of_buffer, find.size);
} else {
result.lhs = string;
}
return result;
}
DN_API DN_Str8BSplitResult DN_SIMD_Str8BSplitLastAVX512F(DN_Str8 string, DN_Str8 find)
{
DN_Str8BSplitResult result = {};
DN_Str8FindResult find_result = DN_SIMD_Str8FindLastAVX512F(string, find);
if (find_result.found) {
result.lhs.data = string.data;
result.lhs.size = find_result.index;
result.rhs = DN_Str8Advance(find_result.match_to_end_of_buffer, find.size);
} else {
result.lhs = string;
}
return result;
}
DN_API DN_USize DN_SIMD_Str8SplitAVX512F(DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitIncludeEmptyStrings mode)
{
DN_USize result = 0; // The number of splits in the actual string.
if (string.size == 0 || delimiter.size == 0 || delimiter.size <= 0)
return result;
DN_Str8BSplitResult split = {};
DN_Str8 first = string;
do {
split = DN_SIMD_Str8BSplitAVX512F(first, delimiter);
if (split.lhs.size || mode == DN_Str8SplitIncludeEmptyStrings_Yes) {
if (splits && result < splits_count)
splits[result] = split.lhs;
result++;
}
first = split.rhs;
} while (first.size);
return result;
}
DN_API DN_Slice<DN_Str8> DN_SIMD_Str8SplitAllocAVX512F(DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode)
{
DN_Slice<DN_Str8> result = {};
DN_USize splits_required = DN_SIMD_Str8SplitAVX512F(string, delimiter, /*splits*/ nullptr, /*count*/ 0, mode);
result.data = DN_ArenaNewArray(arena, DN_Str8, splits_required, DN_ZMem_No);
if (result.data) {
result.size = DN_SIMD_Str8SplitAVX512F(string, delimiter, result.data, splits_required, mode);
DN_Assert(splits_required == result.size);
}
return result;
}
-28
View File
@@ -1,28 +0,0 @@
#if !defined(DN_SIMD_AVX512F_H)
#define DN_SIMD_AVX512F_H
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// $$$$$$\ $$\ $$\ $$\ $$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$$\
// $$ __$$\ $$ | $$ |$$ | $$ | $$ ____| $$$$ | $$ __$$\ $$ _____|
// $$ / $$ |$$ | $$ |\$$\ $$ | $$ | \_$$ | \__/ $$ |$$ |
// $$$$$$$$ |\$$\ $$ | \$$$$ /$$$$$$\ $$$$$$$\ $$ | $$$$$$ |$$$$$\
// $$ __$$ | \$$\$$ / $$ $$< \______|\_____$$\ $$ | $$ ____/ $$ __|
// $$ | $$ | \$$$ / $$ /\$$\ $$\ $$ | $$ | $$ | $$ |
// $$ | $$ | \$ / $$ / $$ | \$$$$$$ |$$$$$$\ $$$$$$$$\ $$ |
// \__| \__| \_/ \__| \__| \______/ \______|\________|\__|
//
// dn_avx512f.h -- Functions implemented w/ AVX512
//
////////////////////////////////////////////////////////////////////////////////////////////////////
*/
DN_API DN_Str8FindResult DN_Str8FindStr8AVX512F (DN_Str8 string, DN_Str8 find);
DN_API DN_Str8FindResult DN_Str8FindLastStr8AVX512F (DN_Str8 string, DN_Str8 find);
DN_API DN_Str8BSplitResult DN_Str8BSplitAVX512F (DN_Str8 string, DN_Str8 find);
DN_API DN_Str8BSplitResult DN_Str8BSplitLastAVX512F(DN_Str8 string, DN_Str8 find);
DN_API DN_USize DN_Str8SplitAVX512F (DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitIncludeEmptyStrings mode);
DN_API DN_Slice<DN_Str8> DN_Str8SplitAllocAVX512F (DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode);
#endif // DN_SIMD_AVX512F_H
+61 -61
View File
@@ -41,7 +41,7 @@ DN_INIStr8 DN_INI_Str8FromPtr(char const *data, size_t count)
{
DN_INIStr8 result = {};
result.data = (char *)data;
result.size = count;
result.count = count;
return result;
}
@@ -82,20 +82,20 @@ DN_INIStr8FromResult DN_INI_Str8FromINI(DN_INICore const *ini, char *buffer, siz
if (it != &ini->first_section) {
DN_INI_Str8BuilderAppend_(&builder, "[");
for (DN_INISection *parent = it->parent; parent; parent = parent->parent)
if (parent->name.size)
if (parent->name.count)
parent_stack[parent_stack_count++] = parent;
for (size_t index = parent_stack_count - 1; index < parent_stack_count; index--) {
DN_INISection *parent = parent_stack[index];
DN_INI_Str8BuilderAppend_(&builder, "%.*s.", (int)parent->name.size, parent->name.data);
DN_INI_Str8BuilderAppend_(&builder, "%.*s.", (int)parent->name.count, parent->name.data);
}
parent_stack_count = 0;
DN_INI_Str8BuilderAppend_(&builder, "%.*s]\n", (int)it->name.size, it->name.data);
DN_INI_Str8BuilderAppend_(&builder, "%.*s]\n", (int)it->name.count, it->name.data);
}
for (DN_INIField *field = it->first_field; field; field = field->next) {
DN_INI_Str8BuilderAppend_(&builder, "%.*s = ", (int)field->key.size, field->key.data);
DN_INI_Str8BuilderAppend_(&builder, "%.*s = ", (int)field->key.count, field->key.data);
switch (field->value_type) {
case DN_INIFieldType_String: DN_INI_Str8BuilderAppend_(&builder, "%.*s\n", (int)field->value.size, field->value.data); break;
case DN_INIFieldType_String: DN_INI_Str8BuilderAppend_(&builder, "%.*s\n", (int)field->value.count, field->value.data); break;
case DN_INIFieldType_Bool: DN_INI_Str8BuilderAppend_(&builder, "%d\n", field->value_bool); break;
case DN_INIFieldType_USize: DN_INI_Str8BuilderAppend_(&builder, "%zu\n", field->value_usize); break;
}
@@ -113,7 +113,7 @@ DN_INIStr8FromResult DN_INI_Str8FromINI(DN_INICore const *ini, char *buffer, siz
result.size_req = builder.size_req;
if (buffer) {
result.str8.data = builder.data;
result.str8.size = builder.used;
result.str8.count = builder.used;
result.success = true;
} else {
result.success = true;
@@ -124,7 +124,7 @@ DN_INIStr8FromResult DN_INI_Str8FromINI(DN_INICore const *ini, char *buffer, siz
static bool DN_INI_Str8Eq(DN_INIStr8 lhs, DN_INIStr8 rhs)
{
bool result = lhs.size == rhs.size && DN_INI_Memcmp(lhs.data, rhs.data, lhs.size) == 0;
bool result = lhs.count == rhs.count && DN_INI_Memcmp(lhs.data, rhs.data, lhs.count) == 0;
return result;
}
@@ -132,11 +132,11 @@ static DN_INIStr8 DN_INI_Str8Slice(DN_INIStr8 slice, size_t offset, size_t size)
{
DN_INIStr8 result = {};
if (slice.data) {
size_t max_offset = slice.size;
size_t max_offset = slice.count;
size_t final_offset = offset <= max_offset ? offset : max_offset;
size_t max_size = slice.size - final_offset;
size_t max_size = slice.count - final_offset;
result.data = slice.data + final_offset;
result.size = size <= max_size ? size : max_size;
result.count = size <= max_size ? size : max_size;
}
return result;
}
@@ -144,15 +144,15 @@ static DN_INIStr8 DN_INI_Str8Slice(DN_INIStr8 slice, size_t offset, size_t size)
static DN_INIStr8BSplit DN_INI_Str8BSplit(DN_INIStr8 str8, DN_INIStr8 find)
{
DN_INIStr8BSplit result = {};
if (find.size > str8.size)
if (find.count > str8.count)
return result;
for (size_t index = 0; index < (str8.size - find.size) + 1; index++) {
DN_INIStr8 slice = DN_INI_Str8FromPtr(str8.data + index, find.size);
for (size_t index = 0; index < (str8.count - find.count) + 1; index++) {
DN_INIStr8 slice = DN_INI_Str8FromPtr(str8.data + index, find.count);
if (DN_INI_Str8Eq(slice, find)) {
result.lhs = DN_INI_Str8FromPtr(str8.data, (size_t)index);
size_t rhs_size = (size_t)(str8.size - (index + 1));
DN_INI_Assert(rhs_size < str8.size);
size_t rhs_size = (size_t)(str8.count - (index + 1));
DN_INI_Assert(rhs_size < str8.count);
result.rhs = DN_INI_Str8FromPtr(str8.data + index + 1, rhs_size);
break;
}
@@ -166,16 +166,16 @@ static DN_INIStr8BSplit DN_INI_Str8BSplit(DN_INIStr8 str8, DN_INIStr8 find)
static DN_INIStr8BSplit DN_INI_Str8BSplitReverse(DN_INIStr8 str8, DN_INIStr8 find)
{
DN_INIStr8BSplit result = {};
if (find.size > str8.size)
if (find.count > str8.count)
return result;
for (size_t index = str8.size - find.size; index > 0; index--) {
DN_INIStr8 slice = DN_INI_Str8FromPtr(str8.data + index, find.size);
for (size_t index = str8.count - find.count; index > 0; index--) {
DN_INIStr8 slice = DN_INI_Str8FromPtr(str8.data + index, find.count);
if (DN_INI_Str8Eq(slice, find)) {
result.lhs = DN_INI_Str8FromPtr(str8.data, (size_t)index);
size_t rhs_size = (size_t)(str8.size - index - find.size);
DN_INI_Assert(rhs_size < str8.size);
result.rhs = DN_INI_Str8FromPtr(str8.data + index + find.size, rhs_size);
size_t rhs_size = (size_t)(str8.count - index - find.count);
DN_INI_Assert(rhs_size < str8.count);
result.rhs = DN_INI_Str8FromPtr(str8.data + index + find.count, rhs_size);
break;
}
}
@@ -399,7 +399,7 @@ DN_INISection *DN_INI_ChildSectionFromStr8(DN_INISection *section, DN_INIStr8 st
DN_INISection *curr = section;
while (result) {
DN_INIStr8BSplit split = DN_INI_Str8BSplit(section_name, DN_INIStr8Lit("."));
if (split.lhs.size == 0)
if (split.lhs.count == 0)
break;
result = 0;
@@ -426,8 +426,8 @@ DN_INIField *DN_INI_FieldFromSectionStr8(DN_INISection *section, DN_INIStr8 str8
DN_INIStr8BSplit split = DN_INI_Str8BSplitReverse(str8, DN_INIStr8Lit("."));
DN_INIStr8 find_key = str8;
DN_INISection *find_section = section;
if (split.rhs.size) {
find_section = DN_INI_ChildSectionFromCStr(section, split.lhs.data, split.lhs.size);
if (split.rhs.count) {
find_section = DN_INI_ChildSectionFromCStr(section, split.lhs.data, split.lhs.count);
find_key = split.rhs;
}
@@ -458,20 +458,20 @@ DN_INIFieldUSize DN_INI_FieldUSizeFromSectionStr8(DN_INISection *section, DN_INI
// NOTE: Try parse string as USize
// NOTE: Sanitize input/output
DN_INIStr8 value = result.field->value;
while (value.size && DN_INI_CharIsWhitespace_(value.data[0])) {
while (value.count && DN_INI_CharIsWhitespace_(value.data[0])) {
value.data++;
value.size--;
value.count--;
}
// NOTE: Handle prefix '+'
if (value.size && value.data[0] == '+') {
if (value.count && value.data[0] == '+') {
value.data++;
value.size--;
value.count--;
}
// NOTE: Convert the string number to the binary number
size_t value_usize = 0;
for (size_t index = 0; index < value.size; index++) {
for (size_t index = 0; index < value.count; index++) {
char ch = value.data[index];
uint64_t digit = ch - '0';
if (!(ch >= '0' && ch <= '9'))
@@ -556,10 +556,10 @@ DN_INICore DN_INI_ParseFromPtr(char const *buf, size_t count, char *base, size_t
curr_section = &result.first_section;
for (;;) {
DN_INIStr8BSplit split = DN_INI_Str8BSplit(section_name, DN_INIStr8Lit("."));
if (split.lhs.size == 0)
if (split.lhs.count == 0)
break;
DN_INISection *next_section = DN_INI_ChildSectionFromCStr(parent, split.lhs.data, split.lhs.size);
DN_INISection *next_section = DN_INI_ChildSectionFromCStr(parent, split.lhs.data, split.lhs.count);
if (!next_section) {
result.total_sections_count++;
next_section = (DN_INISection *)DN_INI_ArenaAlloc(&arena, sizeof(*parent));
@@ -598,7 +598,7 @@ DN_INICore DN_INI_ParseFromPtr(char const *buf, size_t count, char *base, size_t
if (field) {
field->key.data = token.data;
field->key.size = token.count;
field->key.count = token.count;
}
if (curr_section) {
@@ -623,7 +623,7 @@ DN_INICore DN_INI_ParseFromPtr(char const *buf, size_t count, char *base, size_t
result.memory_required += bytes_req;
if (curr_section && field) {
DN_INI_Assert(curr_section->fields_count);
DN_INI_Assert(field->key.size);
DN_INI_Assert(field->key.count);
char *string = (char *)DN_INI_ArenaAlloc(&arena, bytes_req);
if (!string) {
@@ -634,16 +634,16 @@ DN_INICore DN_INI_ParseFromPtr(char const *buf, size_t count, char *base, size_t
if (tokeniser.prev_token.type == DN_INITokenType_Value) {
field->value.data = string;
field->value.size = 0;
field->value.count = 0;
for (size_t index = 0; index < tokeniser.prev_token.count; index++) {
char ch = tokeniser.prev_token.data[index];
char next = index + 1 < tokeniser.prev_token.count ? tokeniser.prev_token.data[index + 1] : 0;
if (ch == '\\' && next == 'n') {
field->value.data[field->value.size++] = '\n';
field->value.data[field->value.count++] = '\n';
index++;
} else {
field->value.data[field->value.size++] = ch;
field->value.data[field->value.count++] = ch;
}
}
} else {
@@ -657,10 +657,10 @@ DN_INICore DN_INI_ParseFromPtr(char const *buf, size_t count, char *base, size_t
char ch = token.data[index];
char next = index + 1 < token.count ? token.data[index + 1] : 0;
if (ch == '\\' && next == 'n') {
field->value.data[field->value.size++] = '\n';
field->value.data[field->value.count++] = '\n';
index++;
} else {
field->value.data[field->value.size++] = ch;
field->value.data[field->value.count++] = ch;
}
}
}
@@ -682,7 +682,7 @@ DN_INICore DN_INI_ParseFromPtr(char const *buf, size_t count, char *base, size_t
result.memory_required += bytes_req;
if (curr_section && field) {
DN_INI_Assert(curr_section->fields_count);
DN_INI_Assert(field->key.size);
DN_INI_Assert(field->key.count);
if (bytes_req) {
field->value.data = (char *)DN_INI_ArenaAlloc(&arena, bytes_req);
if (!field->value.data) {
@@ -694,16 +694,16 @@ DN_INICore DN_INI_ParseFromPtr(char const *buf, size_t count, char *base, size_t
char ch = token.data[index];
char next = index + 1 < token.count ? token.data[index + 1] : 0;
if (ch == '\\' && next == 'n') {
field->value.data[field->value.size++] = '\n';
field->value.data[field->value.count++] = '\n';
index++;
} else {
field->value.data[field->value.size++] = ch;
field->value.data[field->value.count++] = ch;
}
}
DN_INI_Assert(field->value.size <= bytes_req);
DN_INI_Assert(field->value.count <= bytes_req);
} else {
field->value.data = token.data;
field->value.size = token.count;
field->value.count = token.count;
}
}
} break;
@@ -738,8 +738,8 @@ DN_INISection *DN_INI_AppendSectionF(DN_INICore *ini, DN_INIArena *arena, DN_INI
if (arena && arena->used + mem_req <= arena->max) {
result = (DN_INISection *)DN_INI_ArenaAlloc(arena, sizeof(*result));
result->name.data = (char *)DN_INI_ArenaAlloc(arena, size_req + 1);
result->name.size = size_req;
vsnprintf(result->name.data, result->name.size + 1, fmt, args_copy);
result->name.count = size_req;
vsnprintf(result->name.data, result->name.count + 1, fmt, args_copy);
if (!section)
section = &ini->first_section;
@@ -766,13 +766,13 @@ DN_INISection *DN_INI_AppendSectionF(DN_INICore *ini, DN_INIArena *arena, DN_INI
static DN_INIField *DN_INI_AllocFieldInternal(DN_INIStr8 key, DN_INIArena *arena)
{
DN_INIField *result = 0;
size_t mem_req = sizeof(*result) + (key.size + 1);
size_t mem_req = sizeof(*result) + (key.count + 1);
if (arena->used + mem_req <= arena->max) {
result = (DN_INIField *)DN_INI_ArenaAlloc(arena, sizeof(*result));
result->key.data = (char *)DN_INI_ArenaAlloc(arena, key.size + 1);
result->key.size = key.size;
DN_INI_Memcpy(result->key.data, key.data, key.size);
result->key.data[result->key.size] = 0;
result->key.data = (char *)DN_INI_ArenaAlloc(arena, key.count + 1);
result->key.count = key.count;
DN_INI_Memcpy(result->key.data, key.data, key.count);
result->key.data[result->key.count] = 0;
}
return result;
}
@@ -780,13 +780,13 @@ static DN_INIField *DN_INI_AllocFieldInternal(DN_INIStr8 key, DN_INIArena *arena
DN_INIField *DN_INI_AppendKeyBool(DN_INICore *ini, DN_INIArena *arena, DN_INISection *section, DN_INIStr8 key, bool value)
{
DN_INIField *result = 0;
size_t mem_req = sizeof(*result) + key.size + sizeof(value);
size_t mem_req = sizeof(*result) + key.count + sizeof(value);
ini->memory_required += mem_req;
if (arena && arena->used + mem_req <= arena->max) {
result = DN_INI_AllocFieldInternal(key, arena);
result->value_bool = value;
result->value_type = DN_INIFieldType_Bool;
DN_INI_Memcpy(result->key.data, key.data, key.size);
DN_INI_Memcpy(result->key.data, key.data, key.count);
DN_INI_AppendValue(section, result);
}
return result;
@@ -802,13 +802,13 @@ DN_INIField *DN_INI_AppendKeyPtrBool(DN_INICore *ini, DN_INIArena *arena, DN_INI
DN_INIField *DN_INI_AppendKeyUSize(DN_INICore *ini, DN_INIArena *arena, DN_INISection *section, DN_INIStr8 key, size_t value)
{
DN_INIField *result = 0;
size_t mem_req = sizeof(*result) + key.size + sizeof(value);
size_t mem_req = sizeof(*result) + key.count + sizeof(value);
ini->memory_required += mem_req;
if (arena && arena->used + mem_req <= arena->max) {
result = DN_INI_AllocFieldInternal(key, arena);
result->value_usize = value;
result->value_type = DN_INIFieldType_USize;
DN_INI_Memcpy(result->key.data, key.data, key.size);
DN_INI_Memcpy(result->key.data, key.data, key.count);
DN_INI_AppendValue(section, result);
}
return result;
@@ -824,12 +824,12 @@ DN_INIField *DN_INI_AppendKeyPtrUSize(DN_INICore *ini, DN_INIArena *arena, DN_IN
DN_INIField *DN_INI_AppendKeyCStr8(DN_INICore *ini, DN_INIArena *arena, DN_INISection *section, DN_INIStr8 key, char const *value, size_t value_size)
{
DN_INIField *result = 0;
size_t mem_req = sizeof(*result) + (key.size + 1) + value_size;
size_t mem_req = sizeof(*result) + (key.count + 1) + value_size;
ini->memory_required += mem_req;
if (arena && arena->used + mem_req <= arena->max) {
result = DN_INI_AllocFieldInternal(key, arena);
result->value.data = (char *)DN_INI_ArenaAlloc(arena, value_size);
result->value.size = value_size;
result->value.count = value_size;
result->value_type = DN_INIFieldType_String;
DN_INI_Memcpy(result->value.data, value, value_size);
DN_INI_AppendValue(section, result);
@@ -846,14 +846,14 @@ DN_INIField *DN_INI_AppendKeyF(DN_INICore *ini, DN_INIArena *arena, DN_INISectio
va_end(args);
DN_INIField *result = 0;
size_t mem_req = sizeof(*result) + (key.size + 1) + (size_req + 1);
size_t mem_req = sizeof(*result) + (key.count + 1) + (size_req + 1);
ini->memory_required += mem_req;
if (arena && arena->used + mem_req <= arena->max) {
result = DN_INI_AllocFieldInternal(key, arena);
result->value.data = (char *)DN_INI_ArenaAlloc(arena, size_req + 1);
result->value.size = size_req;
vsnprintf(result->value.data, result->value.size + 1, fmt, args_copy);
result->value.data[result->value.size] = 0;
result->value.count = size_req;
vsnprintf(result->value.data, result->value.count + 1, fmt, args_copy);
result->value.data[result->value.count] = 0;
DN_INI_AppendValue(section, result);
}
va_end(args_copy);
+5 -5
View File
@@ -48,13 +48,13 @@
// NOTE: Calculate the number of bytes required to parse the buffer and make one single
// allocation
DN_INICore ini = DN_INI_ParseFromPtr(ini_buffer.data, ini_buffer.size, NULL, 0);
DN_INICore ini = DN_INI_ParseFromPtr(ini_buffer.data, ini_buffer.count, NULL, 0);
size_t parse_buffer_size = ini.memory_required;
char* parse_buffer = calloc(1, parse_buffer_size);
// NOTE: Parse the buffer into the `ini` object from the single allocation. No additional
// allocations are made
ini = DN_INI_ParseFromPtr(ini_buffer.data, ini_buffer.size, parse_buffer, parse_buffer_size);
ini = DN_INI_ParseFromPtr(ini_buffer.data, ini_buffer.count, parse_buffer, parse_buffer_size);
// NOTE: Process the ini file
// The .INI file parsed into a tree, resembling
@@ -75,7 +75,7 @@
printf("My Section Foo XYZ: %zu fields\n", my_section_foo_xyz->fields_count);
for (DN_INIField *field = my_section_foo->first_field; field; field = field->next)
printf(" %.*s: %.*s\n", (int)field->key.size, field->key.data, (int)field->value.size, field->value.data);
printf(" %.*s: %.*s\n", (int)field->key.count, field->key.data, (int)field->value.count, field->value.data);
// Alternatively you can access the section directly by specifying the fully qualified path from
// the section, e.g.:
@@ -87,7 +87,7 @@
// You may also lookup the field directly by specifying the fully qualified path
DN_INIField *my_section_item = DN_INI_FieldFromSectionStr8(&ini.first_section, DN_INIStr8Lit("my_section.item"));
if (my_section_item)
printf(" %.*s: %.*s\n", (int)my_section_item->key.size, my_section_item->key.data, (int)my_section_item->value.size, my_section_item->value.data);
printf(" %.*s: %.*s\n", (int)my_section_item->key.count, my_section_item->key.data, (int)my_section_item->value.count, my_section_item->value.data);
// NOTE: Release memory, `ini` and its contents are invalidated and should not be used
free(parse_buffer);
@@ -137,7 +137,7 @@ typedef enum DN_INITokenType {
typedef struct DN_INIStr8 {
char *data;
size_t size;
size_t count;
} DN_INIStr8;
#if defined(__cplusplus)
+11810 -41
View File
File diff suppressed because it is too large Load Diff
+4416 -59
View File
File diff suppressed because it is too large Load Diff
+7 -2
View File
@@ -34,7 +34,7 @@ pushd %build_dir%
:: GR- Disable C RTTI
:: Oi Use CPU Intrinsics
:: Z7 Combine multi-debug files to one debug file
set flags=%flags% -D DN_UNIT_TESTS_WITH_KECCAK -D DN_UNIT_TESTS_WITH_NET %script_dir%\Source\Extra\dn_tests_main.cpp
set flags=%flags% -D DN_UNIT_TESTS_WITH_KECCAK %script_dir%\Source\Extra\dn_tests_main.cpp
set msvc_driver_flags=-EHa -GR- -Od -Oi -Z7 -wd4201 -W4 -nologo %flags% -fsanitize=address
where /q emcc && (
@@ -46,14 +46,19 @@ pushd %build_dir%
echo [BUILD] MSVC cl detected, compiling ...
set msvc_cmd=cl -MTd %msvc_driver_flags% -analyze -Fe:dn_unit_tests_msvc -Fo:dn_unit_tests_msvc
if exist %build_dir%/Curl/Install/lib/libcurl-d.lib (
set msvc_cmd=!msvc_cmd! -D DN_UNIT_TESTS_WITH_CURL -I %build_dir%/Curl/Install/include %build_dir%/Curl/Install/lib/libcurl-d.lib crypt32.lib ws2_32.lib advapi32.lib wldap32.lib iphlpapi.lib secur32.lib
set msvc_cmd=!msvc_cmd! -D DN_WITH_NET_CURL=1 -I %build_dir%/Curl/Install/include %build_dir%/Curl/Install/lib/libcurl-d.lib crypt32.lib ws2_32.lib advapi32.lib wldap32.lib iphlpapi.lib secur32.lib
)
set msvc_cmd=!msvc_cmd! -link
REM Build the single header
powershell -Command "$time = Measure-Command { !msvc_cmd! | Out-Default }; Write-Host '[BUILD] msvc:'$time.TotalSeconds's'; exit $LASTEXITCODE" || echo MSVC build failed&& exit /b 1
echo [BUILD] Single header generator ...
call cl %script_dir%\single_header_generator.cpp -Z7 -nologo -link || echo Single header generator build failed&& exit /b 1
call %build_dir%\single_header_generator.exe %script_dir%\Source %script_dir%\Single-Header || echo Single header generation failed&& exit /b 1
REM Build the single header using the single header (to test that the generated single header)
call cl %script_dir%\single_header_generator.cpp -Z7 -nologo -D USE_SINGLE_HEADER -link || echo Single header generator build failed&& exit /b 1
call %build_dir%\single_header_generator.exe %script_dir%\Source %script_dir%\Single-Header || echo Single header generation failed&& exit /b 1
)
where /q clang-cl && (
+6 -11
View File
@@ -1,14 +1,11 @@
#define USE_SINGLE_HEADER 1
#define DN_H_WITH_OS 1
#define DN_H_WITH_CORE 1
#if USE_SINGLE_HEADER
#define DN_WITH_OS 1
#if defined(USE_SINGLE_HEADER)
#include "Single-Header/dn_single_header.h"
#else
#include "Source/dn.h"
#endif
#if USE_SINGLE_HEADER
#if defined(USE_SINGLE_HEADER)
#include "Single-Header/dn_single_header.cpp"
#else
#include "Source/dn.cpp"
@@ -35,7 +32,7 @@ static void AppendCppFileLineByLine(DN_Str8Builder *dest, DN_Str8 cpp_path)
DN_ErrSinkEndExitIfErrorF(err, -1, "Failed to load file from '%S' for appending", cpp_path);
bool inside_clangd_preprocessor_block = false;
for (DN_Str8 walker = buffer; walker.size;) {
for (DN_Str8 walker = buffer; walker.count;) {
// NOTE: Trim the whitespace, mainly for windows, the file we read will have \r\n whereas we just want to emit \n
DN_Str8BSplitResult split = DN_Str8BSplit(walker, DN_Str8Lit("\n"));
DN_Str8 line = DN_Str8TrimTailWhitespace(split.lhs);
@@ -84,7 +81,7 @@ static void AppendCppFileLineByLine(DN_Str8Builder *dest, DN_Str8 cpp_path)
DN_Str8BuilderAppendCopy(dest, line);
DN_Str8BuilderAppendRef(dest, DN_Str8Lit("\n"));
if (extra_include_path.size)
if (extra_include_path.count)
AppendCppFileLineByLine(dest, extra_include_path);
}
DN_TCScratchEnd(&scratch);
@@ -93,7 +90,7 @@ static void AppendCppFileLineByLine(DN_Str8Builder *dest, DN_Str8 cpp_path)
int main(int argc, char **argv)
{
DN_Core dn = {};
DN_Init(&dn, DN_InitFlags_OS | DN_InitFlags_ThreadContext, DN_TCInitArgsDefault());
DN_Init(&dn, DN_InitFlags_OS, DN_TCInitArgsDefault());
if (argc != 3) {
DN_OS_PrintErrF("USAGE: %s <path/to/dn/Source> <output_dir>", argv[0]);
@@ -109,8 +106,6 @@ int main(int argc, char **argv)
DN_Str8 const REL_FILE_PATHS[] = {
DN_Str8Lit("dn"),
DN_Str8Lit("Extra/dn_bin_pack"),
DN_Str8Lit("Extra/dn_csv"),
};
for (DN_ForIndexU(type, FileType_Count)) {