Fix single header build with updated DN lib
This commit is contained in:
@@ -318,7 +318,7 @@ DN_INIToken DN_INI_NextToken(DN_INITokeniser const *tokeniser)
|
||||
if (DN_INI_Str8Eq(esc_str8, DN_INIStr8Lit("\n"))) {
|
||||
result.next_p += 2;
|
||||
} else if (DN_INI_Str8Eq(esc_str8, DN_INIStr8Lit("\\"))) {
|
||||
// NOTE: Backespace is escaping a backspace
|
||||
// NOTE: Backspace is escaping a backspace
|
||||
} else {
|
||||
result.next_p += 1;
|
||||
}
|
||||
@@ -340,7 +340,19 @@ DN_INIToken DN_INI_NextToken(DN_INITokeniser const *tokeniser)
|
||||
if (tokeniser->prev_token.type == DN_INITokenType_FieldSeparator || multiline_value || value) {
|
||||
if (tokeniser->data[pos] == ' ') // Value can have spaces in it without quotes
|
||||
continue;
|
||||
result.type = tokeniser->prev_token.type == DN_INITokenType_FieldSeparator ? DN_INITokenType_Value : DN_INITokenType_MultilineValue;
|
||||
|
||||
if (tokeniser->prev_token.type == DN_INITokenType_FieldSeparator) {
|
||||
if (result.line_start_new_line) {
|
||||
// NOTE: The user has terminated the value component of the key with a newline
|
||||
// (e.g. no value presented). For example: `their_key=\n` which means there's no
|
||||
// value and instead they started a new key-value pair.
|
||||
result.type = DN_INITokenType_Key;
|
||||
} else {
|
||||
result.type = DN_INITokenType_Value;
|
||||
}
|
||||
} else {
|
||||
result.type = DN_INITokenType_MultilineValue;
|
||||
}
|
||||
} else if (tokeniser->prev_token.type == DN_INITokenType_Key) {
|
||||
result.type = DN_INITokenType_Error;
|
||||
result.error = DN_INIStr8Lit("Invalid unquoted string, multiple consecutive keys encountered");
|
||||
@@ -849,7 +861,7 @@ DN_INIField *DN_INI_AppendKeyF(DN_INICore *ini, DN_INIArena *arena, DN_INISectio
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(DN_INI_WITH_UNIT_TESTS) || 1
|
||||
#if defined(DN_INI_WITH_UNIT_TESTS)
|
||||
void DN_INI_UnitTests()
|
||||
{
|
||||
// NOTE: Section and comments
|
||||
@@ -1106,5 +1118,27 @@ void DN_INI_UnitTests()
|
||||
DN_INI_Assert(parse.error_token.type == DN_INITokenType_Nil);
|
||||
DN_INI_Assert(DN_INI_Str8Eq(parse.first_section.child_first->first_field->key, DN_INIStr8Lit("foo")));
|
||||
}
|
||||
|
||||
// NOTE: 2 empty key-values consecutively
|
||||
{
|
||||
char const EXAMPLE[] =
|
||||
"[metadata]\n"
|
||||
"foo=\n"
|
||||
"bar=\n"
|
||||
;
|
||||
|
||||
DN_INICore parse = DN_INI_ParseFromPtr(EXAMPLE, sizeof(EXAMPLE) - 1, 0, 0);
|
||||
DN_INI_Assert(parse.error_token.type == DN_INITokenType_Nil);
|
||||
DN_INI_Assert(parse.total_sections_count == 1);
|
||||
DN_INI_Assert(parse.total_fields_count == 2);
|
||||
|
||||
char parse_memory[400];
|
||||
DN_INI_Assert(parse.memory_required <= sizeof(parse_memory));
|
||||
|
||||
parse = DN_INI_ParseFromPtr(EXAMPLE, sizeof(EXAMPLE) - 1, parse_memory, sizeof(parse_memory));
|
||||
DN_INI_Assert(parse.error_token.type == DN_INITokenType_Nil);
|
||||
DN_INI_Assert(DN_INI_Str8Eq(parse.first_section.child_first->first_field->key, DN_INIStr8Lit("foo")));
|
||||
DN_INI_Assert(DN_INI_Str8Eq(parse.first_section.child_first->first_field->next->key, DN_INIStr8Lit("bar")));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
+113
-20
@@ -1,34 +1,127 @@
|
||||
#if !defined(DN_INI_H)
|
||||
#define DN_INI_H
|
||||
|
||||
#include <stdint.h> // size_t
|
||||
// NOTE: DN INI Configuration
|
||||
// Getting Started
|
||||
// This is a single header and implementation file library that implements .ini file handling.
|
||||
// It supports the following .ini features:
|
||||
//
|
||||
// - Plain sections: [sections]
|
||||
// - Arbitrarily nested sections delimited by '.': [sections.a] [sections.a.b] [sections.a.b....]
|
||||
// - Repeated section names (the last section takes precedence)
|
||||
// - Comments marked by #: [section] # Comment
|
||||
// - Multi-line values for keys:
|
||||
// [my_section]
|
||||
// the_key = the_value \
|
||||
// another line for the key \
|
||||
// and another one
|
||||
// the_next_key = that's cool
|
||||
//
|
||||
// Include both .h and .c in your translation unit or compile the .c separately and link against
|
||||
// it to get started. The goals of the library are as follows:
|
||||
//
|
||||
// - Zero allocations to parse, accepts a NULL buffer to determine the amount of bytes required
|
||||
// to parse the .ini buffer
|
||||
// - Compile in C99 and compatible with C++
|
||||
//
|
||||
// Example
|
||||
/*
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "dn_ini.h"
|
||||
#include "dn_ini.c"
|
||||
|
||||
#if !defined(DN_INI_Assert)
|
||||
#include <assert.h>
|
||||
#define DN_INI_Assert(expr) assert(expr)
|
||||
#endif
|
||||
void main() {
|
||||
DN_INIStr8 ini_buffer = DN_INIStr8Lit(
|
||||
"[my_stuff]\n"
|
||||
"app=the_stuff # Comment on the stuff value\n"
|
||||
"\n"
|
||||
"[section]\n"
|
||||
"item=abc\n"
|
||||
"\n"
|
||||
"[section.foo.xyz]\n"
|
||||
"another=one\n"
|
||||
"multi_line_key=coolios \\\n"
|
||||
"from another line?!\n"
|
||||
);
|
||||
|
||||
#include <stdarg.h>
|
||||
// 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);
|
||||
size_t parse_buffer_size = ini.memory_required;
|
||||
char* parse_buffer = calloc(1, parse_buffer_size);
|
||||
|
||||
#if !defined(DN_INI_VSNPrintF)
|
||||
#include <stdio.h>
|
||||
#define DN_INI_VSNPrintF(buffer, size, fmt, args) vsnprintf(buffer, size, fmt, args)
|
||||
#endif
|
||||
// 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);
|
||||
|
||||
#if !defined(DN_INI_Memset) || !defined(DN_INI_Memcmp) || !defined(DN_INI_Memcpy)
|
||||
#include <string.h>
|
||||
#if !defined(DN_INI_Memset)
|
||||
#define DN_INI_Memset(ptr, val, size) memset(ptr, val, size)
|
||||
// NOTE: Process the ini file
|
||||
// The .INI file parsed into a tree, resembling
|
||||
//
|
||||
// [Root (Sentinel)] <-- This is ini.first_section, its children contains the .ini contents
|
||||
// |
|
||||
// +-- [my_stuff]
|
||||
// +-- [my_section]
|
||||
// |
|
||||
// +-- [foo]
|
||||
// |
|
||||
// +-- [xyz]
|
||||
//
|
||||
DN_INISection *my_stuff = DN_INI_ChildSectionFromStr8(&ini.first_section, DN_INIStr8Lit("my_stuff"));
|
||||
DN_INISection *my_section = DN_INI_ChildSectionFromStr8(&ini.first_section, DN_INIStr8Lit("my_section"));
|
||||
DN_INISection *my_section_foo = DN_INI_ChildSectionFromStr8(my_section, DN_INIStr8Lit("foo"));
|
||||
DN_INISection *my_section_foo_xyz = DN_INI_ChildSectionFromStr8(my_section_foo, DN_INIStr8Lit("xyz"));
|
||||
|
||||
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);
|
||||
|
||||
// Alternatively you can access the section directly by specifying the fully qualified path from
|
||||
// the section, e.g.:
|
||||
DN_INISection *my_section_foo_xyz_direct = DN_INI_ChildSectionFromStr8(&ini.first_section, DN_INIStr8Lit("my_section.foo.xyz"));
|
||||
if (my_section_foo_xyz_direct) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// NOTE: Release memory, `ini` and its contents are invalidated and should not be used
|
||||
free(parse_buffer);
|
||||
}
|
||||
*/
|
||||
|
||||
#include <stdint.h> // size_t
|
||||
|
||||
#if !defined(DN_INI_Assert)
|
||||
#include <assert.h>
|
||||
#define DN_INI_Assert(expr) assert(expr)
|
||||
#endif
|
||||
|
||||
#if !defined(DN_INI_Memcmp)
|
||||
#define DN_INI_Memcmp(dest, src, size) memcmp(dest, src, size)
|
||||
#include <stdarg.h>
|
||||
|
||||
#if !defined(DN_INI_VSNPrintF)
|
||||
#include <stdio.h>
|
||||
#define DN_INI_VSNPrintF(buffer, size, fmt, args) vsnprintf(buffer, size, fmt, args)
|
||||
#endif
|
||||
|
||||
#if !defined(DN_INI_Memcpy)
|
||||
#define DN_INI_Memcpy(dest, src, size) memcpy(dest, src, size)
|
||||
#if !defined(DN_INI_Memset) || !defined(DN_INI_Memcmp) || !defined(DN_INI_Memcpy)
|
||||
#include <string.h>
|
||||
#if !defined(DN_INI_Memset)
|
||||
#define DN_INI_Memset(ptr, val, size) memset(ptr, val, size)
|
||||
#endif
|
||||
|
||||
#if !defined(DN_INI_Memcmp)
|
||||
#define DN_INI_Memcmp(dest, src, size) memcmp(dest, src, size)
|
||||
#endif
|
||||
|
||||
#if !defined(DN_INI_Memcpy)
|
||||
#define DN_INI_Memcpy(dest, src, size) memcpy(dest, src, size)
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef enum DN_INITokenType {
|
||||
DN_INITokenType_Nil,
|
||||
@@ -175,7 +268,7 @@ DN_INIFieldStr8 DN_INI_FieldStr8FromSectionStr8 (DN_INISection *section, DN
|
||||
DN_INIFieldBool DN_INI_FieldBoolFromSectionStr8 (DN_INISection *section, DN_INIStr8 str8);
|
||||
DN_INICore DN_INI_ParseFromPtr (char const *buf, size_t count, char *base, size_t base_count);
|
||||
|
||||
// NOTE: Building
|
||||
// NOTE: Building
|
||||
DN_INISection * DN_INI_AppendSectionF (DN_INICore *ini, DN_INIArena *arena, DN_INISection *section, char const *fmt, ...);
|
||||
DN_INIField * DN_INI_AppendKeyBool (DN_INICore *ini, DN_INIArena *arena, DN_INISection *section, DN_INIStr8 key, bool value);
|
||||
DN_INIField * DN_INI_AppendKeyPtrBool (DN_INICore *ini, DN_INIArena *arena, DN_INISection *section, char const *key, size_t key_size, bool value);
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
#include "dn_json_builder.h"
|
||||
|
||||
static void DN_JSB_Indent_(DN_JSBBuilder *builder, char *buf, size_t buf_size, size_t depth)
|
||||
{
|
||||
for (size_t i = 0; i < depth; i++) {
|
||||
DN_JSB_AppendF_(builder, buf, buf_size, " ");
|
||||
}
|
||||
}
|
||||
|
||||
DN_JSBDepth *DN_JSB_PeekDepth_(DN_JSBBuilder *builder, size_t offset)
|
||||
{
|
||||
DN_JSBDepth *result = 0;
|
||||
if (builder->depth_count > offset)
|
||||
result = &builder->depth[builder->depth_count - (1 + offset)];
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DN_JSB_PushDepth_(DN_JSBBuilder *builder, DN_JSBDepthType type)
|
||||
{
|
||||
bool result = false;
|
||||
if (builder->depth_count < builder->depth_max) {
|
||||
DN_JSBDepth *depth = &builder->depth[builder->depth_count++];
|
||||
depth->last_action = DN_JSBAction_Nil;
|
||||
depth->type = type;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_JSBResult DN_JSB_PushObject(DN_JSBBuilder *builder, char *buf, size_t buf_size)
|
||||
{
|
||||
DN_JSBDepth *top_depth = DN_JSB_PeekDepth_(builder, 0);
|
||||
if (top_depth && top_depth->type != DN_JSBDepthType_Array) {
|
||||
if ((top_depth->last_action == DN_JSBAction_Value || top_depth->last_action == DN_JSBAction_PopArray || top_depth->last_action == DN_JSBAction_PushObject))
|
||||
return DN_JSBResult_BadAPIUsage;
|
||||
}
|
||||
|
||||
if (!DN_JSB_PushDepth_(builder, DN_JSBDepthType_Object))
|
||||
return DN_JSBResult_DepthArrayOutOfMem;
|
||||
|
||||
bool leading_comma = top_depth && (top_depth->last_action != DN_JSBAction_Nil && top_depth->last_action != DN_JSBAction_Key);
|
||||
if (builder->pretty && top_depth && top_depth->type == DN_JSBDepthType_Array) {
|
||||
if (leading_comma) {
|
||||
DN_JSB_AppendF_(builder, buf, buf_size, ",\n");
|
||||
} else {
|
||||
DN_JSB_AppendF_(builder, buf, buf_size, "\n");
|
||||
}
|
||||
DN_JSB_Indent_(builder, buf, buf_size, builder->depth_count - 1);
|
||||
leading_comma = false;
|
||||
}
|
||||
DN_JSBResult result = DN_JSB_AppendF_(builder, buf, buf_size, "%s{", leading_comma ? "," : "");
|
||||
if (top_depth)
|
||||
top_depth->last_action = DN_JSBAction_PushObject; // Record the push on the now, previous depth
|
||||
|
||||
if (builder->error == DN_JSBResult_Success)
|
||||
builder->error = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_JSBResult DN_JSB_PopObject(DN_JSBBuilder *builder, char *buf, size_t buf_size)
|
||||
{
|
||||
DN_JSBDepth *top_depth = DN_JSB_PeekDepth_(builder, 0);
|
||||
if (!top_depth || top_depth->type != DN_JSBDepthType_Object)
|
||||
return DN_JSBResult_BadAPIUsage;
|
||||
|
||||
bool had_children = top_depth->last_action != DN_JSBAction_Nil;
|
||||
builder->depth_count--; // Remove the depth entry from the stack
|
||||
|
||||
DN_JSBDepth *new_top = DN_JSB_PeekDepth_(builder, 0);
|
||||
if (new_top)
|
||||
new_top->last_action = DN_JSBAction_PopObject;
|
||||
|
||||
if (builder->pretty && had_children) {
|
||||
DN_JSB_AppendF_(builder, buf, buf_size, "\n");
|
||||
DN_JSB_Indent_(builder, buf, buf_size, builder->depth_count);
|
||||
}
|
||||
DN_JSBResult result = DN_JSB_AppendF_(builder, buf, buf_size, "}");
|
||||
if (builder->error == DN_JSBResult_Success)
|
||||
builder->error = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_JSBResult DN_JSB_PushArray(DN_JSBBuilder *builder, char *buf, size_t buf_size)
|
||||
{
|
||||
DN_JSBDepth *top_depth = DN_JSB_PeekDepth_(builder, 0);
|
||||
if (top_depth && top_depth->type != DN_JSBDepthType_Array) {
|
||||
if (top_depth->last_action == DN_JSBAction_Value || top_depth->last_action == DN_JSBAction_PopArray || top_depth->last_action == DN_JSBAction_PushObject || top_depth->last_action == DN_JSBAction_PopObject)
|
||||
return DN_JSBResult_BadAPIUsage;
|
||||
}
|
||||
|
||||
if (!DN_JSB_PushDepth_(builder, DN_JSBDepthType_Array))
|
||||
return DN_JSBResult_DepthArrayOutOfMem;
|
||||
|
||||
bool leading_comma = top_depth && (top_depth->last_action != DN_JSBAction_Nil && top_depth->last_action != DN_JSBAction_Key);
|
||||
if (builder->pretty && top_depth && top_depth->type == DN_JSBDepthType_Array) {
|
||||
if (leading_comma) {
|
||||
DN_JSB_AppendF_(builder, buf, buf_size, ",\n");
|
||||
} else {
|
||||
DN_JSB_AppendF_(builder, buf, buf_size, "\n");
|
||||
}
|
||||
DN_JSB_Indent_(builder, buf, buf_size, builder->depth_count - 1);
|
||||
leading_comma = false;
|
||||
}
|
||||
DN_JSBResult result = DN_JSB_AppendF_(builder, buf, buf_size, "%s[", leading_comma ? "," : "");
|
||||
if (top_depth)
|
||||
top_depth->last_action = DN_JSBAction_PushArray; // Record the push on the now, previous depth
|
||||
if (builder->error == DN_JSBResult_Success)
|
||||
builder->error = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_JSBResult DN_JSB_PopArray(DN_JSBBuilder *builder, char *buf, size_t buf_size)
|
||||
{
|
||||
DN_JSBDepth *top_depth = DN_JSB_PeekDepth_(builder, 0);
|
||||
if (!top_depth || top_depth->type != DN_JSBDepthType_Array)
|
||||
return DN_JSBResult_BadAPIUsage;
|
||||
|
||||
bool had_children = top_depth->last_action != DN_JSBAction_Nil;
|
||||
builder->depth_count--; // Remove the depth entry from the stack
|
||||
DN_JSBDepth *new_top = DN_JSB_PeekDepth_(builder, 0);
|
||||
if (new_top)
|
||||
new_top->last_action = DN_JSBAction_PopArray;
|
||||
|
||||
if (builder->pretty && had_children) {
|
||||
DN_JSB_AppendF_(builder, buf, buf_size, "\n");
|
||||
DN_JSB_Indent_(builder, buf, buf_size, builder->depth_count);
|
||||
}
|
||||
DN_JSBResult result = DN_JSB_AppendF_(builder, buf, buf_size, "]");
|
||||
if (builder->error == DN_JSBResult_Success)
|
||||
builder->error = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_JSBResult DN_JSB_KeyF(DN_JSBBuilder *builder, char *buf, size_t buf_size, char const *fmt, ...)
|
||||
{
|
||||
DN_JSBDepth *top_depth = DN_JSB_PeekDepth_(builder, 0);
|
||||
if (!top_depth || top_depth->last_action == DN_JSBAction_Key || top_depth->last_action == DN_JSBAction_PushArray)
|
||||
return DN_JSBResult_BadAPIUsage;
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
if (builder->pretty) {
|
||||
if (top_depth->last_action != DN_JSBAction_Nil) {
|
||||
DN_JSB_AppendF_(builder, buf, buf_size, ",\n");
|
||||
} else {
|
||||
DN_JSB_AppendF_(builder, buf, buf_size, "\n");
|
||||
}
|
||||
DN_JSB_Indent_(builder, buf, buf_size, builder->depth_count);
|
||||
DN_JSB_AppendF_(builder, buf, buf_size, "\"");
|
||||
DN_JSB_AppendFV_(builder, buf, buf_size, fmt, args);
|
||||
DN_JSBResult result = DN_JSB_AppendF_(builder, buf, buf_size, "\": ");
|
||||
va_end(args);
|
||||
if (result == DN_JSBResult_Success)
|
||||
top_depth->last_action = DN_JSBAction_Key;
|
||||
if (builder->error == DN_JSBResult_Success)
|
||||
builder->error = result;
|
||||
return result;
|
||||
}
|
||||
DN_JSB_AppendF_(builder, buf, buf_size, "%s\"", top_depth->last_action != DN_JSBAction_Nil ? "," : "");
|
||||
DN_JSB_AppendFV_(builder, buf, buf_size, fmt, args);
|
||||
DN_JSBResult result = DN_JSB_AppendF_(builder, buf, buf_size, "\":");
|
||||
va_end(args);
|
||||
|
||||
if (result == DN_JSBResult_Success)
|
||||
top_depth->last_action = DN_JSBAction_Key;
|
||||
if (builder->error == DN_JSBResult_Success)
|
||||
builder->error = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_JSBResult DN_JSB_ValueStringF(DN_JSBBuilder *builder, char *buf, size_t buf_size, char const *fmt, ...)
|
||||
{
|
||||
DN_JSBResult result = DN_JSB_BeginWriteValue_(builder, buf, buf_size);
|
||||
if (result == DN_JSBResult_Success) {
|
||||
DN_JSB_AppendF_(builder, buf, buf_size, "\"");
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_JSB_AppendFV_(builder, buf, buf_size, fmt, args);
|
||||
va_end(args);
|
||||
result = DN_JSB_AppendF_(builder, buf, buf_size, "\"");
|
||||
DN_JSB_FinishWriteValue_(builder, result);
|
||||
}
|
||||
if (builder->error == DN_JSBResult_Success)
|
||||
builder->error = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_JSBResult DN_JSB_ValueNull(DN_JSBBuilder *builder, char *buf, size_t buf_size)
|
||||
{
|
||||
DN_JSBResult result = DN_JSB_BeginWriteValue_(builder, buf, buf_size);
|
||||
if (result == DN_JSBResult_Success) {
|
||||
result = DN_JSB_AppendF_(builder, buf, buf_size, "null");
|
||||
DN_JSB_FinishWriteValue_(builder, result);
|
||||
}
|
||||
if (builder->error == DN_JSBResult_Success)
|
||||
builder->error = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_JSBResult DN_JSB_ValueBool(DN_JSBBuilder *builder, char *buf, size_t buf_size, bool flag)
|
||||
{
|
||||
DN_JSBResult result = DN_JSB_BeginWriteValue_(builder, buf, buf_size);
|
||||
if (result == DN_JSBResult_Success) {
|
||||
result = DN_JSB_AppendF_(builder, buf, buf_size, flag ? "true" : "false");
|
||||
DN_JSB_FinishWriteValue_(builder, result);
|
||||
}
|
||||
if (builder->error == DN_JSBResult_Success)
|
||||
builder->error = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_JSBResult DN_JSB_ValueNumber(DN_JSBBuilder *builder, char *buf, size_t buf_size, double number)
|
||||
{
|
||||
DN_JSBResult result = DN_JSB_BeginWriteValue_(builder, buf, buf_size);
|
||||
if (result == DN_JSBResult_Success) {
|
||||
result = DN_JSB_AppendF_(builder, buf, buf_size, "%.17G", number);
|
||||
DN_JSB_FinishWriteValue_(builder, result);
|
||||
}
|
||||
if (builder->error == DN_JSBResult_Success)
|
||||
builder->error = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_JSBResult DN_JSB_BeginWriteValue_(DN_JSBBuilder *builder, char *buf, size_t buf_size)
|
||||
{
|
||||
DN_JSBDepth *top = DN_JSB_PeekDepth_(builder, 0);
|
||||
if (!top)
|
||||
return DN_JSBResult_BadAPIUsage;
|
||||
|
||||
bool is_key = top->last_action == DN_JSBAction_Key;
|
||||
bool is_array = top->type == DN_JSBDepthType_Array;
|
||||
if (!is_key && !is_array)
|
||||
return DN_JSBResult_BadAPIUsage;
|
||||
|
||||
DN_JSBResult result = DN_JSBResult_Success;
|
||||
if (top->type == DN_JSBDepthType_Array) {
|
||||
if (builder->pretty) {
|
||||
if (top->last_action != DN_JSBAction_Nil) {
|
||||
DN_JSB_AppendF_(builder, buf, buf_size, ",\n");
|
||||
} else {
|
||||
DN_JSB_AppendF_(builder, buf, buf_size, "\n");
|
||||
}
|
||||
DN_JSB_Indent_(builder, buf, buf_size, builder->depth_count);
|
||||
} else if (top->last_action != DN_JSBAction_Nil) {
|
||||
result = DN_JSB_AppendF_(builder, buf, buf_size, ",");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void DN_JSB_FinishWriteValue_(DN_JSBBuilder *builder, DN_JSBResult last_value_raw_str_result)
|
||||
{
|
||||
if (last_value_raw_str_result == DN_JSBResult_Success) {
|
||||
DN_JSBDepth *top_depth = DN_JSB_PeekDepth_(builder, 0);
|
||||
top_depth->last_action = DN_JSBAction_Value;
|
||||
}
|
||||
}
|
||||
|
||||
DN_JSBResult DN_JSB_AppendFV_(DN_JSBBuilder *builder, char *buf, size_t buf_size, char const *fmt, va_list args)
|
||||
{
|
||||
// NOTE: Calc size required
|
||||
size_t size = 0;
|
||||
{
|
||||
va_list args_copy;
|
||||
va_copy(args_copy, args);
|
||||
size = DN_JSB_VSNPrintF(0, 0, fmt, args_copy);
|
||||
va_end(args_copy);
|
||||
}
|
||||
|
||||
// NOTE: Do the write
|
||||
DN_JSBResult result = DN_JSBResult_Success;
|
||||
if (buf) {
|
||||
if (builder->p > buf_size)
|
||||
return DN_JSBResult_BadAPIUsage;
|
||||
|
||||
char *dest = buf + builder->p;
|
||||
size_t dest_size = (buf + buf_size) - dest;
|
||||
if ((size + 1) <= dest_size) { // NOTE: +1 because vsnprintf always null-terminates the stream
|
||||
DN_JSB_VSNPrintF(dest, dest_size, fmt, args);
|
||||
} else {
|
||||
result = DN_JSBResult_BufOutOfMem;
|
||||
}
|
||||
}
|
||||
|
||||
if (result == DN_JSBResult_Success)
|
||||
builder->p += size;
|
||||
if (builder->error == DN_JSBResult_Success)
|
||||
builder->error = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_JSBResult DN_JSB_AppendF_(DN_JSBBuilder *builder, char *buf, size_t buf_size, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_JSBResult result = DN_JSB_AppendFV_(builder, buf, buf_size, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#if !defined(DN_JSON_BUILDER_H)
|
||||
#define DN_JSON_BUILDER_H
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#if !defined(__cplusplus)
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#if !defined(DN_JSB_VSNPrintF)
|
||||
#include <stdio.h>
|
||||
#define DN_JSB_VSNPrintF(buf, size, fmt, args) vsnprintf(buf, size, fmt, args)
|
||||
#endif
|
||||
|
||||
typedef enum DN_JSBResult {
|
||||
DN_JSBResult_Success,
|
||||
DN_JSBResult_BadAPIUsage,
|
||||
DN_JSBResult_DepthArrayOutOfMem,
|
||||
DN_JSBResult_BufOutOfMem,
|
||||
} DN_JSBResult;
|
||||
|
||||
typedef enum DN_JSBPrettify {
|
||||
DN_JSBPrettify_No,
|
||||
DN_JSBPrettify_Yes,
|
||||
} DN_JSBPrettify;
|
||||
|
||||
typedef enum DN_JSBDepthType {
|
||||
DN_JSBDepthType_Nil,
|
||||
DN_JSBDepthType_Object,
|
||||
DN_JSBDepthType_Array,
|
||||
} DN_JSBDepthType;
|
||||
|
||||
typedef enum DN_JSBAction {
|
||||
DN_JSBAction_Nil,
|
||||
DN_JSBAction_PushObject,
|
||||
DN_JSBAction_PopObject,
|
||||
DN_JSBAction_PushArray,
|
||||
DN_JSBAction_PopArray,
|
||||
DN_JSBAction_Key,
|
||||
DN_JSBAction_Value,
|
||||
} DN_JSBAction;
|
||||
|
||||
typedef struct DN_JSBDepth {
|
||||
DN_JSBDepthType type;
|
||||
DN_JSBAction last_action;
|
||||
} DN_JSBDepth;
|
||||
|
||||
typedef struct DN_JSBBuilder {
|
||||
DN_JSBDepth* depth;
|
||||
size_t depth_count;
|
||||
size_t depth_max;
|
||||
size_t p; // Size required not incl- the null-terminator (e.g. To build, pass in `p + 1` bytes)
|
||||
DN_JSBResult error;
|
||||
DN_JSBPrettify pretty;
|
||||
} DN_JSBBuilder;
|
||||
|
||||
DN_JSBResult DN_JSB_PushObject (DN_JSBBuilder *builder, char *buf, size_t buf_size);
|
||||
DN_JSBResult DN_JSB_PopObject (DN_JSBBuilder *builder, char *buf, size_t buf_size);
|
||||
DN_JSBResult DN_JSB_PushArray (DN_JSBBuilder *builder, char *buf, size_t buf_size);
|
||||
DN_JSBResult DN_JSB_PopArray (DN_JSBBuilder *builder, char *buf, size_t buf_size);
|
||||
DN_JSBResult DN_JSB_KeyF (DN_JSBBuilder *builder, char *buf, size_t buf_size, char const *fmt, ...);
|
||||
DN_JSBResult DN_JSB_ValueStringF (DN_JSBBuilder *builder, char *buf, size_t buf_size, char const *fmt, ...);
|
||||
DN_JSBResult DN_JSB_ValueBool (DN_JSBBuilder *builder, char *buf, size_t buf_size, bool flag);
|
||||
DN_JSBResult DN_JSB_ValueNull (DN_JSBBuilder *builder, char *buf, size_t buf_size);
|
||||
DN_JSBResult DN_JSB_ValueNumber (DN_JSBBuilder *builder, char *buf, size_t buf_size, double number);
|
||||
|
||||
DN_JSBDepth * DN_JSB_PeekDepth_ (DN_JSBBuilder *builder, size_t offset);
|
||||
bool DN_JSB_PushDepth_ (DN_JSBBuilder *builder, DN_JSBDepthType type);
|
||||
DN_JSBResult DN_JSB_AppendFV_ (DN_JSBBuilder *builder, char *buf, size_t buf_size, char const *fmt, va_list args);
|
||||
DN_JSBResult DN_JSB_AppendF_ (DN_JSBBuilder *builder, char *buf, size_t buf_size, char const *fmt, ...);
|
||||
DN_JSBResult DN_JSB_BeginWriteValue_ (DN_JSBBuilder *builder, char *buf, size_t buf_size);
|
||||
void DN_JSB_FinishWriteValue_(DN_JSBBuilder *builder, DN_JSBResult last_value_raw_str_result);
|
||||
#endif // !defined(DN_JSON_BUILDER_H)
|
||||
@@ -160,6 +160,9 @@ typedef struct DN_UTCore
|
||||
DN_UTStr8Link *output;
|
||||
} DN_UTCore;
|
||||
|
||||
DN_UTCore DN_UT_Init();
|
||||
void DN_UT_Deinit(DN_UTCore *ut);
|
||||
|
||||
void DN_UT_BeginFV(DN_UTCore *test, char const *fmt, va_list args);
|
||||
void DN_UT_BeginF(DN_UTCore *test, char const *fmt, ...);
|
||||
void DN_UT_End(DN_UTCore *test);
|
||||
|
||||
Reference in New Issue
Block a user