689 lines
23 KiB
C++
689 lines
23 KiB
C++
// Generated by the DN single header generator 2026-05-18 11:16:15
|
|
|
|
#if (_CLANGD)
|
|
#define DN_H_WITH_OS 1
|
|
#include "dn.h"
|
|
#endif
|
|
#define DN_ASYNC_CPP
|
|
|
|
// DN: Single header generator commented out => #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_, nullptr, 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;
|
|
}
|
|
|
|
#define DN_BIN_PACK_CPP
|
|
|
|
// DN: Single header generator commented out => #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;
|
|
}
|
|
#define DN_CSV_CPP
|
|
|
|
// DN: Single header generator commented out => #include "dn_csv.h"
|
|
#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.
|
|
|
|
// DN: Single header generator commented out => #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)
|
|
|
|
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;
|
|
}
|
|
#define DN_HELPERS_CPP |