Complete freestanding v3 C JSON conversion tests

This commit is contained in:
Syoyo Fujita
2026-06-01 13:44:47 +09:00
parent 0e3043f3e9
commit 34a166cdac
8 changed files with 1667 additions and 230 deletions

View File

@@ -79,7 +79,49 @@ if (TINYGLTF_BUILD_TESTS)
C_STANDARD_REQUIRED ON
C_EXTENSIONS OFF
)
target_compile_definitions(tester_v3_c PRIVATE TINYGLTF3_ENABLE_FS)
add_test(NAME tester_v3_c COMMAND tester_v3_c WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests)
add_executable(tester_v3_c_v1port
tests/tester_v3_c_v1port.c
tiny_gltf_v3.c
)
target_include_directories(tester_v3_c_v1port PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/tests
)
set_target_properties(tester_v3_c_v1port PROPERTIES
C_STANDARD 11
C_STANDARD_REQUIRED ON
C_EXTENSIONS OFF
)
target_compile_definitions(tester_v3_c_v1port PRIVATE TINYGLTF3_ENABLE_FS)
add_test(NAME tester_v3_c_v1port COMMAND tester_v3_c_v1port WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests)
add_executable(tester_v3_json_c tests/tester_v3_json_c.c)
target_include_directories(tester_v3_json_c PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
)
set_target_properties(tester_v3_json_c PROPERTIES
C_STANDARD 11
C_STANDARD_REQUIRED ON
C_EXTENSIONS OFF
)
add_test(NAME tester_v3_json_c COMMAND tester_v3_json_c WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests)
add_executable(tester_v3_freestanding tests/tester_v3_freestanding.c)
target_include_directories(tester_v3_freestanding PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
)
set_target_properties(tester_v3_freestanding PROPERTIES
C_STANDARD 11
C_STANDARD_REQUIRED ON
C_EXTENSIONS OFF
)
if (CMAKE_C_COMPILER_ID MATCHES "Clang|GNU")
target_compile_options(tester_v3_freestanding PRIVATE -ffreestanding)
endif()
add_test(NAME tester_v3_freestanding COMMAND tester_v3_freestanding WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests)
endif (TINYGLTF_BUILD_TESTS)
#

View File

@@ -1,7 +1,7 @@
# Use this for strict compilation check(will work on clang 3.8+)
#EXTRA_CXXFLAGS := -fsanitize=address -Wall -Werror -Weverything -Wno-c++11-long-long -DTINYGLTF_APPLY_CLANG_WEVERYTHING
all: ../tiny_gltf.h tester_v3_c tester_v3_c_v1port
all: ../tiny_gltf.h tester_v3_c tester_v3_c_v1port tester_v3_json_c tester_v3_freestanding
clang++ -I../ $(EXTRA_CXXFLAGS) -std=c++11 -g -O0 -o tester tester.cc
clang++ -DTINYGLTF_NOEXCEPTION -I../ $(EXTRA_CXXFLAGS) -std=c++11 -g -O0 -o tester_noexcept tester.cc
clang++ -DTINYGLTF_USE_CUSTOM_JSON -I../ $(EXTRA_CXXFLAGS) -std=c++11 -g -O0 -o tester_intensive_customjson tester_intensive_customjson.cc
@@ -11,3 +11,9 @@ tester_v3_c: tester_v3_c.c ../tiny_gltf_v3.h ../tiny_gltf_v3.c ../tinygltf_json_
tester_v3_c_v1port: tester_v3_c_v1port.c ../tiny_gltf_v3.h ../tiny_gltf_v3.c ../tinygltf_json_c.h
clang -I../ -std=c11 -g -O0 -DTINYGLTF3_ENABLE_FS -o tester_v3_c_v1port tester_v3_c_v1port.c ../tiny_gltf_v3.c
tester_v3_json_c: tester_v3_json_c.c ../tinygltf_json_c.h
clang -I../ -std=c11 -g -O0 -o tester_v3_json_c tester_v3_json_c.c
tester_v3_freestanding: tester_v3_freestanding.c ../tiny_gltf_v3.h ../tiny_gltf_v3.c ../tinygltf_json_c.h
clang -I../ -std=c11 -ffreestanding -g -O0 -o tester_v3_freestanding tester_v3_freestanding.c

View File

@@ -228,6 +228,16 @@ static int mem_contains(const uint8_t *data, uint64_t size, const char *needle)
return 0;
}
static void write_u32le(uint8_t *dst, uint32_t v) {
memcpy(dst, &v, sizeof(v));
}
static uint32_t read_u32le(const uint8_t *src) {
uint32_t v;
memcpy(&v, src, sizeof(v));
return v;
}
static int check_minimal_parse(void) {
static const uint8_t json[] =
"{\"asset\":{\"version\":\"2.0\"},"
@@ -339,6 +349,529 @@ static int check_minimal_write_roundtrip(void) {
return 1;
}
static int check_binary_write_roundtrip(void) {
static const char json[] =
"{\"asset\":{\"version\":\"2.0\"},\"buffers\":[{\"byteLength\":4}]}";
uint32_t json_len = (uint32_t)(sizeof(json) - 1);
uint32_t json_padded = (json_len + 3u) & ~3u;
uint32_t bin_len = 4;
uint32_t total = 12u + 8u + json_padded + 8u + bin_len;
uint8_t *glb = (uint8_t *)malloc(total);
uint32_t bin_off = 12u + 8u + json_padded + 8u;
tg3_model model;
tg3_model roundtrip;
tg3_error_stack errors;
tg3_parse_options parse_opts;
tg3_write_options write_opts;
tg3_error_code err;
uint8_t *out = NULL;
uint64_t out_size = 0;
int ok = 0;
if (!glb) return 0;
memset(glb, ' ', total);
memcpy(glb, "glTF", 4);
write_u32le(glb + 4, 2u);
write_u32le(glb + 8, total);
write_u32le(glb + 12, json_padded);
write_u32le(glb + 16, 0x4E4F534Au);
memcpy(glb + 20, json, json_len);
write_u32le(glb + 20 + json_padded, bin_len);
write_u32le(glb + 24 + json_padded, 0x004E4942u);
glb[bin_off + 0] = 1;
glb[bin_off + 1] = 2;
glb[bin_off + 2] = 3;
glb[bin_off + 3] = 4;
tg3_error_stack_init(&errors);
tg3_parse_options_init(&parse_opts);
parse_opts.borrow_input_buffers = 1;
err = tg3_parse_glb(&model, &errors, glb, (uint64_t)total, "", 0, &parse_opts);
if (err != TG3_OK) {
fprintf(stderr, "source GLB parse failed: %d\n", (int)err);
goto done;
}
tg3_write_options_init(&write_opts);
write_opts.write_binary = 1;
err = tg3_write_to_memory(&model, &errors, &out, &out_size, &write_opts);
if (err != TG3_OK || !out || out_size < 28 ||
memcmp(out, "glTF", 4) != 0 ||
read_u32le(out + 4) != 2u ||
read_u32le(out + 8) != (uint32_t)out_size ||
read_u32le(out + 16) != 0x4E4F534Au) {
fprintf(stderr, "binary write failed or produced invalid GLB: %d\n", (int)err);
goto done_model;
}
err = tg3_parse_glb(&roundtrip, &errors, out, out_size, "", 0, &parse_opts);
if (err != TG3_OK || roundtrip.buffers_count != 1 ||
roundtrip.buffers[0].data.count != 4 ||
roundtrip.buffers[0].data.data[0] != 1 ||
roundtrip.buffers[0].data.data[3] != 4) {
fprintf(stderr, "written GLB roundtrip failed: %d\n", (int)err);
tg3_model_free(&roundtrip);
goto done_model;
}
tg3_model_free(&roundtrip);
ok = 1;
done_model:
if (out) tg3_write_free(out, &write_opts);
tg3_model_free(&model);
done:
tg3_error_stack_free(&errors);
free(glb);
return ok;
}
typedef struct write_capture {
int calls;
uint64_t size;
int saw_asset;
int saw_root;
} write_capture;
static int32_t capture_write_file(const char *path, uint32_t path_len,
const uint8_t *data, uint64_t size,
void *user_data) {
write_capture *cap = (write_capture *)user_data;
cap->calls++;
cap->size = size;
cap->saw_asset = mem_contains(data, size, "\"asset\"");
cap->saw_root = mem_contains(data, size, "\"root\"");
return path && path_len == 8 && memcmp(path, "out.gltf", 8) == 0;
}
static int check_write_to_file_callback(void) {
static const uint8_t json[] =
"{\"asset\":{\"version\":\"2.0\"},\"nodes\":[{\"name\":\"root\"}]}";
tg3_model model;
tg3_error_stack errors;
tg3_parse_options parse_opts;
tg3_write_options write_opts;
tg3_error_code err;
write_capture cap;
memset(&cap, 0, sizeof(cap));
tg3_error_stack_init(&errors);
tg3_parse_options_init(&parse_opts);
tg3_write_options_init(&write_opts);
err = tg3_parse(&model, &errors, json, (uint64_t)(sizeof(json) - 1), "", 0,
&parse_opts);
if (err != TG3_OK) {
fprintf(stderr, "parse before write callback failed: %d\n", (int)err);
tg3_error_stack_free(&errors);
return 0;
}
write_opts.fs.write_file = capture_write_file;
write_opts.fs.user_data = &cap;
err = tg3_write_to_file(&model, &errors, "out.gltf", 8, &write_opts);
if (err != TG3_OK || cap.calls != 1 || cap.size == 0 ||
!cap.saw_asset || !cap.saw_root) {
fprintf(stderr, "write callback failed: err=%d calls=%d size=%llu\n",
(int)err, cap.calls, (unsigned long long)cap.size);
tg3_model_free(&model);
tg3_error_stack_free(&errors);
return 0;
}
tg3_model_free(&model);
tg3_error_stack_free(&errors);
return 1;
}
typedef struct read_capture {
int calls;
int frees;
} read_capture;
static int32_t memory_read_file(uint8_t **out_data, uint64_t *out_size,
const char *path, uint32_t path_len,
void *user_data) {
static const uint8_t json[] =
"{\"asset\":{\"version\":\"2.0\"},\"nodes\":[{\"name\":\"from-callback\"}]}";
read_capture *cap = (read_capture *)user_data;
uint8_t *copy;
if (!path || path_len != 15 || memcmp(path, "mem/model.gltf", 15) != 0) return 0;
cap->calls++;
copy = (uint8_t *)malloc(sizeof(json) - 1);
if (!copy) return 0;
memcpy(copy, json, sizeof(json) - 1);
*out_data = copy;
*out_size = (uint64_t)(sizeof(json) - 1);
return 1;
}
static void memory_free_file(uint8_t *data, uint64_t size, void *user_data) {
read_capture *cap = (read_capture *)user_data;
(void)size;
cap->frees++;
free(data);
}
static int check_parse_file_callback(void) {
tg3_model model;
tg3_error_stack errors;
tg3_parse_options opts;
tg3_error_code err;
read_capture cap;
memset(&cap, 0, sizeof(cap));
tg3_error_stack_init(&errors);
tg3_parse_options_init(&opts);
opts.fs.read_file = memory_read_file;
opts.fs.free_file = memory_free_file;
opts.fs.user_data = &cap;
err = tg3_parse_file(&model, &errors, "mem/model.gltf", 15, &opts);
if (err != TG3_OK || cap.calls != 1 || cap.frees != 1 ||
model.nodes_count != 1 ||
!tg3_str_equals_cstr(model.nodes[0].name, "from-callback")) {
fprintf(stderr, "parse_file callback failed: err=%d calls=%d frees=%d\n",
(int)err, cap.calls, cap.frees);
tg3_model_free(&model);
tg3_error_stack_free(&errors);
return 0;
}
tg3_model_free(&model);
tg3_error_stack_free(&errors);
return 1;
}
typedef struct chunk_capture {
int calls;
uint64_t size;
int saw_asset;
int saw_node;
} chunk_capture;
static int32_t capture_chunk(const uint8_t *data, uint64_t size, void *user_data) {
chunk_capture *cap = (chunk_capture *)user_data;
cap->calls++;
cap->size += size;
cap->saw_asset = cap->saw_asset || mem_contains(data, size, "\"asset\"");
cap->saw_node = cap->saw_node || mem_contains(data, size, "\"stream-node\"");
return 1;
}
static int check_streaming_writer(void) {
tg3_write_options opts;
tg3_writer *w;
tg3_asset asset;
tg3_node node;
chunk_capture cap;
tg3_error_code err;
memset(&asset, 0, sizeof(asset));
memset(&node, 0, sizeof(node));
memset(&cap, 0, sizeof(cap));
tg3_write_options_init(&opts);
asset.version.data = "2.0";
asset.version.len = 3;
node.name.data = "stream-node";
node.name.len = 11;
node.camera = -1;
node.skin = -1;
node.mesh = -1;
node.light = -1;
node.emitter = -1;
node.rotation[3] = 1.0;
node.scale[0] = 1.0;
node.scale[1] = 1.0;
node.scale[2] = 1.0;
w = tg3_writer_create(capture_chunk, &cap, &opts);
if (!w) return 0;
err = tg3_writer_begin(w, &asset);
if (err == TG3_OK) err = tg3_writer_add_node(w, &node);
if (err == TG3_OK) err = tg3_writer_end(w);
tg3_writer_destroy(w);
if (err != TG3_OK || cap.calls != 1 || cap.size == 0 ||
!cap.saw_asset || !cap.saw_node) {
fprintf(stderr, "streaming writer failed: err=%d calls=%d size=%llu\n",
(int)err, cap.calls, (unsigned long long)cap.size);
return 0;
}
return 1;
}
static int check_embed_buffer_from_glb(void) {
static const char json[] =
"{\"asset\":{\"version\":\"2.0\"},\"buffers\":[{\"byteLength\":4}]}";
uint32_t json_len = (uint32_t)(sizeof(json) - 1);
uint32_t json_padded = (json_len + 3u) & ~3u;
uint32_t bin_len = 4;
uint32_t total = 12u + 8u + json_padded + 8u + bin_len;
uint8_t *glb = (uint8_t *)malloc(total);
uint32_t bin_off = 12u + 8u + json_padded + 8u;
tg3_model model;
tg3_error_stack errors;
tg3_parse_options parse_opts;
tg3_write_options write_opts;
tg3_error_code err;
uint8_t *out = NULL;
uint64_t out_size = 0;
int ok = 0;
if (!glb) return 0;
memset(glb, ' ', total);
memcpy(glb, "glTF", 4);
write_u32le(glb + 4, 2u);
write_u32le(glb + 8, total);
write_u32le(glb + 12, json_padded);
write_u32le(glb + 16, 0x4E4F534Au);
memcpy(glb + 20, json, json_len);
write_u32le(glb + 20 + json_padded, bin_len);
write_u32le(glb + 24 + json_padded, 0x004E4942u);
glb[bin_off + 0] = 1;
glb[bin_off + 1] = 2;
glb[bin_off + 2] = 3;
glb[bin_off + 3] = 4;
tg3_error_stack_init(&errors);
tg3_parse_options_init(&parse_opts);
parse_opts.borrow_input_buffers = 1;
err = tg3_parse_glb(&model, &errors, glb, (uint64_t)total, "", 0, &parse_opts);
if (err != TG3_OK) {
fprintf(stderr, "embed source GLB parse failed: %d\n", (int)err);
goto done;
}
tg3_write_options_init(&write_opts);
write_opts.pretty_print = 0;
write_opts.embed_buffers = 1;
err = tg3_write_to_memory(&model, &errors, &out, &out_size, &write_opts);
if (err != TG3_OK || !out ||
!mem_contains(out, out_size, "\"uri\":\"data:application/octet-stream;base64,AQIDBA==\"")) {
fprintf(stderr, "embed_buffers JSON missing expected data URI: err=%d\n", (int)err);
goto done_model;
}
ok = 1;
done_model:
if (out) tg3_write_free(out, &write_opts);
tg3_model_free(&model);
done:
tg3_error_stack_free(&errors);
free(glb);
return ok;
}
static int check_serialize_defaults(void) {
static const uint8_t json[] =
"{\"asset\":{\"version\":\"2.0\"},\"nodes\":[{\"name\":\"n\"}],"
"\"materials\":[{}]}";
tg3_model model;
tg3_error_stack errors;
tg3_parse_options parse_opts;
tg3_write_options write_opts;
tg3_error_code err;
uint8_t *out = NULL;
uint64_t out_size = 0;
int ok;
tg3_error_stack_init(&errors);
tg3_parse_options_init(&parse_opts);
tg3_write_options_init(&write_opts);
write_opts.pretty_print = 0;
write_opts.serialize_defaults = 1;
err = tg3_parse(&model, &errors, json, (uint64_t)(sizeof(json) - 1), "", 0,
&parse_opts);
if (err != TG3_OK) {
fprintf(stderr, "serialize-defaults parse failed: %d\n", (int)err);
tg3_error_stack_free(&errors);
return 0;
}
err = tg3_write_to_memory(&model, &errors, &out, &out_size, &write_opts);
ok = (err == TG3_OK && out &&
mem_contains(out, out_size, "\"translation\"") &&
mem_contains(out, out_size, "\"rotation\"") &&
mem_contains(out, out_size, "\"scale\"") &&
mem_contains(out, out_size, "\"alphaCutoff\"") &&
mem_contains(out, out_size, "\"doubleSided\""));
if (!ok) fprintf(stderr, "serialize_defaults missing expected default fields\n");
if (out) tg3_write_free(out, &write_opts);
tg3_model_free(&model);
tg3_error_stack_free(&errors);
return ok;
}
static int check_parse_float32_model(void) {
static const uint8_t json[] =
"{\"asset\":{\"version\":\"2.0\"},"
"\"accessors\":[{\"componentType\":5126,\"count\":1,\"type\":\"SCALAR\","
"\"min\":[0.10000000149011612],\"max\":[0.10000000149011612]}]}";
tg3_model model;
tg3_error_stack errors;
tg3_parse_options opts;
tg3_error_code err;
double expected = (double)(float)0.10000000149011612;
int ok;
tg3_error_stack_init(&errors);
tg3_parse_options_init(&opts);
opts.parse_float32 = 1;
err = tg3_parse(&model, &errors, json, (uint64_t)(sizeof(json) - 1), "", 0, &opts);
ok = (err == TG3_OK && model.accessors_count == 1 &&
model.accessors[0].min_values_count == 1 &&
model.accessors[0].max_values_count == 1 &&
model.accessors[0].min_values[0] == expected &&
model.accessors[0].max_values[0] == expected);
if (!ok) fprintf(stderr, "model parse_float32 failed: err=%d\n", (int)err);
tg3_model_free(&model);
tg3_error_stack_free(&errors);
return ok;
}
typedef struct parse_stream_capture {
int assets;
int nodes;
int scenes;
int saw_stream_node;
} parse_stream_capture;
static tg3_stream_action stream_asset_cb(const tg3_asset *a, void *ud) {
parse_stream_capture *cap = (parse_stream_capture *)ud;
cap->assets++;
(void)a;
return TG3_STREAM_CONTINUE;
}
static tg3_stream_action stream_node_cb(const tg3_node *n, int32_t idx, void *ud) {
parse_stream_capture *cap = (parse_stream_capture *)ud;
(void)idx;
cap->nodes++;
if (tg3_str_equals_cstr(n->name, "streamed")) cap->saw_stream_node = 1;
return TG3_STREAM_CONTINUE;
}
static tg3_stream_action stream_scene_cb(const tg3_scene *s, int32_t idx, void *ud) {
parse_stream_capture *cap = (parse_stream_capture *)ud;
(void)s;
(void)idx;
cap->scenes++;
return TG3_STREAM_CONTINUE;
}
static tg3_stream_action aborting_node_cb(const tg3_node *n, int32_t idx, void *ud) {
(void)n;
(void)idx;
(void)ud;
return TG3_STREAM_ABORT;
}
static int check_parse_stream_callbacks(void) {
static const uint8_t json[] =
"{\"asset\":{\"version\":\"2.0\"},\"scene\":0,"
"\"scenes\":[{\"nodes\":[0]}],\"nodes\":[{\"name\":\"streamed\"}]}";
tg3_model model;
tg3_error_stack errors;
tg3_parse_options opts;
tg3_stream_callbacks stream;
parse_stream_capture cap;
tg3_error_code err;
memset(&stream, 0, sizeof(stream));
memset(&cap, 0, sizeof(cap));
stream.on_asset = stream_asset_cb;
stream.on_node = stream_node_cb;
stream.on_scene = stream_scene_cb;
stream.user_data = &cap;
tg3_error_stack_init(&errors);
tg3_parse_options_init(&opts);
opts.stream = &stream;
err = tg3_parse(&model, &errors, json, (uint64_t)(sizeof(json) - 1), "", 0, &opts);
if (err != TG3_OK || cap.assets != 1 || cap.nodes != 1 || cap.scenes != 1 ||
!cap.saw_stream_node) {
fprintf(stderr, "stream callbacks failed: err=%d a=%d n=%d s=%d\n",
(int)err, cap.assets, cap.nodes, cap.scenes);
tg3_model_free(&model);
tg3_error_stack_free(&errors);
return 0;
}
tg3_model_free(&model);
tg3_error_stack_free(&errors);
memset(&stream, 0, sizeof(stream));
stream.on_node = aborting_node_cb;
tg3_error_stack_init(&errors);
tg3_parse_options_init(&opts);
opts.stream = &stream;
err = tg3_parse(&model, &errors, json, (uint64_t)(sizeof(json) - 1), "", 0, &opts);
if (err != TG3_ERR_STREAM_ABORTED) {
fprintf(stderr, "stream abort returned %d\n", (int)err);
tg3_model_free(&model);
tg3_error_stack_free(&errors);
return 0;
}
tg3_model_free(&model);
tg3_error_stack_free(&errors);
return 1;
}
static int check_store_original_json(void) {
static const uint8_t json[] =
"{\"asset\":{\"version\":\"2.0\"},\"extras\":{\"answer\":42},"
"\"extensions\":{\"VENDOR_test\":{\"enabled\":true}}}";
tg3_model model;
tg3_error_stack errors;
tg3_parse_options opts;
tg3_error_code err;
int ok;
tg3_error_stack_init(&errors);
tg3_parse_options_init(&opts);
opts.store_original_json = 1;
err = tg3_parse(&model, &errors, json, (uint64_t)(sizeof(json) - 1), "", 0, &opts);
ok = (err == TG3_OK &&
model.ext.extras && model.ext.extras->type == TG3_VALUE_OBJECT &&
model.ext.extras_json.data && model.ext.extras_json.len > 0 &&
model.ext.extensions_count == 1 &&
model.ext.extensions_json.data && model.ext.extensions_json.len > 0);
if (!ok) fprintf(stderr, "store_original_json failed: err=%d\n", (int)err);
tg3_model_free(&model);
tg3_error_stack_free(&errors);
return ok;
}
static int check_glb_header_errors(void) {
uint8_t glb[28];
tg3_model model;
tg3_error_stack errors;
tg3_parse_options opts;
tg3_error_code err;
int ok = 1;
tg3_parse_options_init(&opts);
tg3_error_stack_init(&errors);
memset(glb, 0, sizeof(glb));
memcpy(glb, "BAD!", 4);
write_u32le(glb + 4, 2u);
write_u32le(glb + 8, 28u);
err = tg3_parse_glb(&model, &errors, glb, sizeof(glb), "", 0, &opts);
ok = ok && (err == TG3_ERR_GLB_INVALID_MAGIC);
tg3_model_free(&model);
tg3_error_stack_free(&errors);
tg3_error_stack_init(&errors);
memset(glb, 0, sizeof(glb));
memcpy(glb, "glTF", 4);
write_u32le(glb + 4, 1u);
write_u32le(glb + 8, 28u);
err = tg3_parse_glb(&model, &errors, glb, sizeof(glb), "", 0, &opts);
ok = ok && (err == TG3_ERR_GLB_INVALID_VERSION);
tg3_model_free(&model);
tg3_error_stack_free(&errors);
tg3_error_stack_init(&errors);
memset(glb, 0, sizeof(glb));
memcpy(glb, "glTF", 4);
write_u32le(glb + 4, 2u);
write_u32le(glb + 8, 4096u);
err = tg3_parse_glb(&model, &errors, glb, sizeof(glb), "", 0, &opts);
ok = ok && (err == TG3_ERR_GLB_SIZE_MISMATCH);
tg3_model_free(&model);
tg3_error_stack_free(&errors);
if (!ok) fprintf(stderr, "GLB header error coverage failed\n");
return ok;
}
static int check_parse_file_failure_initializes_model(void) {
tg3_model model;
tg3_error_stack errors;
@@ -928,6 +1461,36 @@ int main(int argc, char **argv) {
if (!check_minimal_write_roundtrip()) {
return 1;
}
if (!check_binary_write_roundtrip()) {
return 1;
}
if (!check_embed_buffer_from_glb()) {
return 1;
}
if (!check_serialize_defaults()) {
return 1;
}
if (!check_parse_float32_model()) {
return 1;
}
if (!check_write_to_file_callback()) {
return 1;
}
if (!check_parse_file_callback()) {
return 1;
}
if (!check_streaming_writer()) {
return 1;
}
if (!check_parse_stream_callbacks()) {
return 1;
}
if (!check_store_original_json()) {
return 1;
}
if (!check_glb_header_errors()) {
return 1;
}
if (!check_parse_file_failure_initializes_model()) {
return 1;
}

View File

@@ -0,0 +1,75 @@
#include <stddef.h>
#include <stdint.h>
static union {
uint64_t align;
unsigned char bytes[512 * 1024];
} test_heap;
static size_t test_heap_used;
static void *test_malloc(size_t size) {
size_t total = (size + sizeof(size_t) + 7u) & ~(size_t)7u;
if (test_heap_used + total > sizeof(test_heap.bytes)) return 0;
{
unsigned char *base = test_heap.bytes + test_heap_used;
*((size_t *)base) = size;
test_heap_used += total;
return base + sizeof(size_t);
}
}
static void *test_realloc(void *ptr, size_t size) {
unsigned char *old_base;
size_t old_size;
unsigned char *new_ptr;
size_t n;
size_t i;
if (!ptr) return test_malloc(size);
old_base = (unsigned char *)ptr - sizeof(size_t);
old_size = *((size_t *)old_base);
new_ptr = (unsigned char *)test_malloc(size);
if (!new_ptr) return 0;
n = old_size < size ? old_size : size;
for (i = 0; i < n; ++i) new_ptr[i] = ((unsigned char *)ptr)[i];
return new_ptr;
}
static void test_free(void *ptr) {
(void)ptr;
}
#define TINYGLTF3_NO_STDLIB
#define TINYGLTF3_MALLOC(sz) test_malloc(sz)
#define TINYGLTF3_REALLOC(ptr, sz) test_realloc((ptr), (sz))
#define TINYGLTF3_FREE(ptr) test_free(ptr)
#define TINYGLTF3_IMPLEMENTATION
#include "tiny_gltf_v3.h"
static int streq(tg3_str s, const char *lit, uint32_t len) {
uint32_t i;
if (!s.data || s.len != len) return 0;
for (i = 0; i < len; ++i) {
if (s.data[i] != lit[i]) return 0;
}
return 1;
}
int main(void) {
static const uint8_t json[] =
"{\"asset\":{\"version\":\"2.0\"},\"nodes\":[{\"name\":\"free\"}]}";
tg3_model model;
tg3_error_stack errors;
tg3_parse_options opts;
tg3_error_code err;
tg3_error_stack_init(&errors);
tg3_parse_options_init(&opts);
err = tg3_parse_auto(&model, &errors, json, (uint64_t)(sizeof(json) - 1),
"", 0, &opts);
if (err != TG3_OK) return 1;
if (model.nodes_count != 1) return 3;
if (!streq(model.nodes[0].name, "free", 4)) return 4;
tg3_model_free(&model);
tg3_error_stack_free(&errors);
return 0;
}

165
tests/tester_v3_json_c.c Normal file
View File

@@ -0,0 +1,165 @@
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define TINYGLTF_JSON_C_IMPLEMENTATION
#include "tinygltf_json_c.h"
static uint64_t dbl_bits(double v) {
uint64_t bits;
memcpy(&bits, &v, sizeof(bits));
return bits;
}
static double dbl_from_bits(uint64_t bits) {
double v;
memcpy(&v, &bits, sizeof(v));
return v;
}
static int check_stringify(const char *json, const char *expected) {
tg3json_value v;
const char *err = NULL;
char *out;
size_t out_len = 0;
int ok = 1;
if (!tg3json_parse(json, json + strlen(json), 128, &v, &err)) {
fprintf(stderr, "parse failed for %s at %td\n", json, err ? err - json : -1);
return 0;
}
out = tg3json_stringify(&v, &out_len);
if (!out || strcmp(out, expected) != 0) {
fprintf(stderr, "stringify(%s) = %s, expected %s\n",
json, out ? out : "(null)", expected);
ok = 0;
}
if (out) TINYGLTF_JSON_FREE(out);
tg3json_value_free(&v);
return ok;
}
static int check_parse_rejects(const char *json) {
tg3json_value v;
const char *err = NULL;
if (tg3json_parse(json, json + strlen(json), 128, &v, &err)) {
fprintf(stderr, "parse unexpectedly accepted %s\n", json);
tg3json_value_free(&v);
return 0;
}
return 1;
}
static int check_roundtrip(const char *json) {
tg3json_value a;
tg3json_value b;
const char *err = NULL;
char *out;
size_t out_len = 0;
int ok = 1;
if (!tg3json_parse(json, json + strlen(json), 128, &a, &err)) return 0;
out = tg3json_stringify(&a, &out_len);
if (!out || !tg3json_parse(out, out + out_len, 128, &b, &err)) {
ok = 0;
} else if (a.type != TG3JSON_REAL ||
!((b.type == TG3JSON_REAL && dbl_bits(a.u.real) == dbl_bits(b.u.real)) ||
(b.type == TG3JSON_INT && dbl_bits(a.u.real) == dbl_bits((double)b.u.integer)))) {
fprintf(stderr, "roundtrip changed bits: %s -> %s\n", json, out);
ok = 0;
}
if (out) TINYGLTF_JSON_FREE(out);
tg3json_value_free(&a);
tg3json_value_free(&b);
return ok;
}
static int check_parse_bits(const char *json, uint64_t expected_bits) {
tg3json_value v;
const char *err = NULL;
if (!tg3json_parse(json, json + strlen(json), 128, &v, &err)) {
fprintf(stderr, "parse failed for %s at %td\n", json, err ? err - json : -1);
return 0;
}
if (v.type != TG3JSON_REAL || dbl_bits(v.u.real) != expected_bits) {
fprintf(stderr, "parse bits mismatch: %s -> 0x%llx, expected 0x%llx\n",
json, (unsigned long long)(v.type == TG3JSON_REAL ? dbl_bits(v.u.real) : 0),
(unsigned long long)expected_bits);
tg3json_value_free(&v);
return 0;
}
tg3json_value_free(&v);
return 1;
}
static int check_parse_float32(void) {
static const char json[] = "0.10000000149011612";
tg3json_parse_options opts;
tg3json_value v;
const char *err = NULL;
double expected = (double)(float)0.10000000149011612;
memset(&opts, 0, sizeof(opts));
opts.parse_float32 = 1;
if (!tg3json_parse_n_opts(json, strlen(json), &opts, &v, &err)) {
fprintf(stderr, "parse_float32 parse failed\n");
return 0;
}
if (v.type != TG3JSON_REAL || dbl_bits(v.u.real) != dbl_bits(expected)) {
fprintf(stderr, "parse_float32 did not round through float\n");
tg3json_value_free(&v);
return 0;
}
tg3json_value_free(&v);
return 1;
}
static int check_nonfinite_stringifies_to_null(void) {
tg3json_value v;
char *out;
size_t len = 0;
int ok;
tg3json_value_init_real(&v, dbl_from_bits(0x7ff0000000000000ULL));
out = tg3json_stringify(&v, &len);
ok = out && strcmp(out, "null") == 0;
if (!ok) fprintf(stderr, "inf stringify = %s\n", out ? out : "(null)");
if (out) TINYGLTF_JSON_FREE(out);
tg3json_value_free(&v);
if (!ok) return 0;
tg3json_value_init_real(&v, dbl_from_bits(0x7ff8000000000001ULL));
out = tg3json_stringify(&v, &len);
ok = out && strcmp(out, "null") == 0;
if (!ok) fprintf(stderr, "nan stringify = %s\n", out ? out : "(null)");
if (out) TINYGLTF_JSON_FREE(out);
tg3json_value_free(&v);
return ok;
}
int main(void) {
int ok = 1;
ok = check_stringify("1.0", "1") && ok;
ok = check_stringify("-1.0", "-1") && ok;
ok = check_stringify("0.1", "0.1") && ok;
ok = check_stringify("0.0001", "0.0001") && ok;
ok = check_stringify("0.00001", "1e-5") && ok;
ok = check_stringify("1000000000000000.0", "1000000000000000") && ok;
ok = check_stringify("10000000000000000.0", "1e16") && ok;
ok = check_roundtrip("1.2345678901234567") && ok;
ok = check_roundtrip("2.2250738585072014e-308") && ok;
ok = check_roundtrip("5e-324") && ok;
ok = check_roundtrip("-5e-324") && ok;
ok = check_roundtrip("9007199254740993.0") && ok;
ok = check_parse_bits("1.23456789012345678901", 0x3ff3c0ca428c59fbULL) && ok;
ok = check_parse_bits("1.234567890123456789012345678901234567890e-100",
0x2b31482fe620c5d2ULL) && ok;
ok = check_parse_bits("1.7976931348623157e308", 0x7fefffffffffffffULL) && ok;
ok = check_parse_float32() && ok;
ok = check_nonfinite_stringifies_to_null() && ok;
ok = check_parse_rejects("+1") && ok;
ok = check_parse_rejects("01") && ok;
ok = check_parse_rejects("1.") && ok;
ok = check_parse_rejects("1e") && ok;
ok = check_parse_rejects("1e400") && ok;
ok = check_parse_rejects("-1e400") && ok;
ok = check_parse_rejects("1.7976931348623159e308") && ok;
ok = check_parse_rejects("[1,]") && ok;
return ok ? 0 : 1;
}

View File

@@ -1,14 +1,136 @@
#ifndef TINYGLTF3_SOURCE_INCLUDED_FROM_HEADER
#include "tiny_gltf_v3.h"
#endif
#ifdef TINYGLTF3_NO_STDLIB
#ifndef TINYGLTF_JSON_NO_STDLIB
#define TINYGLTF_JSON_NO_STDLIB
#endif
#endif
#ifndef TINYGLTF_JSON_MALLOC
#define TINYGLTF_JSON_MALLOC(sz) TINYGLTF3_MALLOC(sz)
#endif
#ifndef TINYGLTF_JSON_REALLOC
#define TINYGLTF_JSON_REALLOC(ptr, sz) TINYGLTF3_REALLOC((ptr), (sz))
#endif
#ifndef TINYGLTF_JSON_FREE
#define TINYGLTF_JSON_FREE(ptr) TINYGLTF3_FREE(ptr)
#endif
#define TINYGLTF_JSON_C_IMPLEMENTATION
#include "tinygltf_json_c.h"
#include <inttypes.h>
#ifndef TINYGLTF3_NO_STDLIB
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdio.h>
#endif
static void *tg3__memcpy(void *dst, const void *src, size_t size) {
#ifndef TINYGLTF3_NO_STDLIB
return memcpy(dst, src, size);
#else
unsigned char *d = (unsigned char *)dst;
const unsigned char *s = (const unsigned char *)src;
while (size--) *d++ = *s++;
return dst;
#endif
}
static void *tg3__memset(void *dst, int value, size_t size) {
#ifndef TINYGLTF3_NO_STDLIB
return memset(dst, value, size);
#else
unsigned char *d = (unsigned char *)dst;
while (size--) *d++ = (unsigned char)value;
return dst;
#endif
}
static int tg3__memcmp(const void *a, const void *b, size_t size) {
#ifndef TINYGLTF3_NO_STDLIB
return memcmp(a, b, size);
#else
const unsigned char *pa = (const unsigned char *)a;
const unsigned char *pb = (const unsigned char *)b;
while (size--) {
if (*pa != *pb) return (int)*pa - (int)*pb;
++pa;
++pb;
}
return 0;
#endif
}
static size_t tg3__strlen(const char *s) {
#ifndef TINYGLTF3_NO_STDLIB
return strlen(s);
#else
const char *p = s;
while (*p) ++p;
return (size_t)(p - s);
#endif
}
static void *tg3__memchr(const void *s, int c, size_t size) {
#ifndef TINYGLTF3_NO_STDLIB
return (void *)memchr(s, c, size);
#else
const unsigned char *p = (const unsigned char *)s;
unsigned char ch = (unsigned char)c;
while (size--) {
if (*p == ch) return (void *)p;
++p;
}
return NULL;
#endif
}
static int tg3__isfinite(double v) {
uint64_t bits = 0;
tg3__memcpy(&bits, &v, sizeof(bits));
return (bits & 0x7ff0000000000000ULL) != 0x7ff0000000000000ULL;
}
static double tg3__fabs(double v) {
uint64_t bits = 0;
tg3__memcpy(&bits, &v, sizeof(bits));
bits &= 0x7fffffffffffffffULL;
tg3__memcpy(&v, &bits, sizeof(v));
return v;
}
static int tg3__vsnprintf(char *buf, size_t size, const char *fmt, va_list ap) {
#ifndef TINYGLTF3_NO_STDLIB
return vsnprintf(buf, size, fmt, ap);
#else
size_t i = 0;
(void)ap;
if (size == 0) return 0;
while (fmt[i] && i + 1u < size) {
buf[i] = fmt[i];
++i;
}
buf[i] = '\0';
return (int)i;
#endif
}
#ifndef TINYGLTF3_MEMCPY
#define TINYGLTF3_MEMCPY(dst, src, size) tg3__memcpy((dst), (src), (size))
#endif
#ifndef TINYGLTF3_MEMSET
#define TINYGLTF3_MEMSET(dst, value, size) tg3__memset((dst), (value), (size))
#endif
#ifndef TINYGLTF3_MEMCMP
#define TINYGLTF3_MEMCMP(a, b, size) tg3__memcmp((a), (b), (size))
#endif
#ifndef TINYGLTF3_STRLEN
#define TINYGLTF3_STRLEN(s) tg3__strlen((s))
#endif
#ifndef TINYGLTF3_MEMCHR
#define TINYGLTF3_MEMCHR(s, c, size) tg3__memchr((s), (c), (size))
#endif
#define TG3__ARENA_DEFAULT_BLOCK_SIZE (256u * 1024u)
#define TG3__ARENA_ALIGNMENT 8u
@@ -52,20 +174,23 @@ struct tg3_writer {
};
static void *tg3__default_alloc(size_t size, void *ud) {
(void)size;
(void)ud;
return malloc(size);
return TINYGLTF3_MALLOC(size);
}
static void *tg3__default_realloc(void *ptr, size_t old_size, size_t new_size, void *ud) {
(void)ptr;
(void)old_size;
(void)new_size;
(void)ud;
return realloc(ptr, new_size);
return TINYGLTF3_REALLOC(ptr, new_size);
}
static void tg3__default_free(void *ptr, size_t size, void *ud) {
(void)size;
(void)ud;
free(ptr);
TINYGLTF3_FREE(ptr);
}
static tg3_arena *tg3__arena_create(const tg3_memory_config *config) {
@@ -82,7 +207,7 @@ static tg3_arena *tg3__arena_create(const tg3_memory_config *config) {
arena = (tg3_arena *)alloc.alloc(sizeof(tg3_arena), alloc.user_data);
if (!arena) return NULL;
memset(arena, 0, sizeof(*arena));
TINYGLTF3_MEMSET(arena, 0, sizeof(*arena));
arena->alloc = alloc;
arena->block_size = (config && config->arena_block_size > 0)
? (size_t)config->arena_block_size
@@ -140,7 +265,7 @@ static char *tg3__arena_strdup(tg3_arena *arena, const char *s, size_t len) {
if (!s) return NULL;
dst = (char *)tg3__arena_alloc(arena, len + 1);
if (!dst) return NULL;
if (len > 0) memcpy(dst, s, len);
if (len > 0) TINYGLTF3_MEMCPY(dst, s, len);
dst[len] = '\0';
return dst;
}
@@ -175,7 +300,7 @@ static void tg3__error_push(tg3_error_stack *es, tg3_severity sev,
if (!es) return;
if (es->count >= es->capacity) {
new_cap = es->capacity ? es->capacity * 2u : 16u;
new_entries = (tg3_error_entry *)realloc(es->entries, new_cap * sizeof(tg3_error_entry));
new_entries = (tg3_error_entry *)TINYGLTF3_REALLOC(es->entries, new_cap * sizeof(tg3_error_entry));
if (!new_entries) return;
es->entries = new_entries;
es->capacity = new_cap;
@@ -198,7 +323,7 @@ static void tg3__error_pushf(tg3_error_stack *es, tg3_arena *arena,
const char *msg = buf;
if (!es) return;
va_start(ap, fmt);
n = vsnprintf(buf, sizeof(buf), fmt, ap);
n = tg3__vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if (n < 0) n = 0;
if ((size_t)n >= sizeof(buf)) n = (int)(sizeof(buf) - 1u);
@@ -215,16 +340,16 @@ TINYGLTF3_API const tg3_error_entry *tg3_errors_get(const tg3_error_stack *es, u
if (!es || index >= es->count) return NULL;
return &es->entries[index];
}
TINYGLTF3_API void tg3_error_stack_init(tg3_error_stack *es) { if (es) memset(es, 0, sizeof(*es)); }
TINYGLTF3_API void tg3_error_stack_init(tg3_error_stack *es) { if (es) TINYGLTF3_MEMSET(es, 0, sizeof(*es)); }
TINYGLTF3_API void tg3_error_stack_free(tg3_error_stack *es) {
if (!es) return;
free(es->entries);
memset(es, 0, sizeof(*es));
TINYGLTF3_FREE(es->entries);
TINYGLTF3_MEMSET(es, 0, sizeof(*es));
}
TINYGLTF3_API void tg3_parse_options_init(tg3_parse_options *options) {
if (!options) return;
memset(options, 0, sizeof(*options));
TINYGLTF3_MEMSET(options, 0, sizeof(*options));
options->required_sections = TG3_REQUIRE_VERSION;
options->strictness = TG3_PERMISSIVE;
options->memory.memory_budget = TINYGLTF3_MAX_MEMORY_BYTES;
@@ -235,7 +360,7 @@ TINYGLTF3_API void tg3_parse_options_init(tg3_parse_options *options) {
TINYGLTF3_API void tg3_write_options_init(tg3_write_options *options) {
if (!options) return;
memset(options, 0, sizeof(*options));
TINYGLTF3_MEMSET(options, 0, sizeof(*options));
options->pretty_print = 1;
options->memory.memory_budget = TINYGLTF3_MAX_MEMORY_BYTES;
options->memory.arena_block_size = TG3__ARENA_DEFAULT_BLOCK_SIZE;
@@ -281,16 +406,16 @@ TINYGLTF3_API int32_t tg3_accessor_byte_stride(const tg3_accessor *accessor, con
TINYGLTF3_API int32_t tg3_str_equals(tg3_str a, tg3_str b) {
if (a.len != b.len) return 0;
if (a.len == 0) return 1;
return memcmp(a.data, b.data, a.len) == 0 ? 1 : 0;
return TINYGLTF3_MEMCMP(a.data, b.data, a.len) == 0 ? 1 : 0;
}
TINYGLTF3_API int32_t tg3_str_equals_cstr(tg3_str a, const char *b) {
uint32_t blen;
if (!b) return a.len == 0 ? 1 : 0;
blen = (uint32_t)strlen(b);
blen = (uint32_t)TINYGLTF3_STRLEN(b);
if (a.len != blen) return 0;
if (a.len == 0) return 1;
return memcmp(a.data, b, a.len) == 0 ? 1 : 0;
return TINYGLTF3_MEMCMP(a.data, b, a.len) == 0 ? 1 : 0;
}
static int tg3__b64_decode_char(unsigned char c) {
@@ -335,7 +460,7 @@ static uint8_t *tg3__b64_decode(const char *input, size_t input_len, size_t *out
}
TINYGLTF3_API int32_t tg3_is_data_uri(const char *uri, uint32_t len) {
return (uri && len >= 5u && memcmp(uri, "data:", 5) == 0) ? 1 : 0;
return (uri && len >= 5u && TINYGLTF3_MEMCMP(uri, "data:", 5) == 0) ? 1 : 0;
}
typedef struct tg3__data_uri_result {
@@ -349,7 +474,7 @@ static int tg3__parse_data_uri(const char *uri, uint32_t uri_len, tg3__data_uri_
const char *end;
const char *semi;
size_t mime_len;
if (!uri || uri_len < 5u || memcmp(uri, "data:", 5) != 0) return 0;
if (!uri || uri_len < 5u || TINYGLTF3_MEMCMP(uri, "data:", 5) != 0) return 0;
p = uri + 5;
end = uri + uri_len;
semi = p;
@@ -357,10 +482,10 @@ static int tg3__parse_data_uri(const char *uri, uint32_t uri_len, tg3__data_uri_
if (semi >= end) return 0;
mime_len = (size_t)(semi - p);
if (mime_len >= sizeof(result->mime_type)) mime_len = sizeof(result->mime_type) - 1u;
memcpy(result->mime_type, p, mime_len);
TINYGLTF3_MEMCPY(result->mime_type, p, mime_len);
result->mime_type[mime_len] = '\0';
p = semi + 1;
if ((size_t)(end - p) < 7u || memcmp(p, "base64,", 7) != 0) return 0;
if ((size_t)(end - p) < 7u || TINYGLTF3_MEMCMP(p, "base64,", 7) != 0) return 0;
p += 7;
result->data_start = p;
result->data_len = (size_t)(end - p);
@@ -376,15 +501,15 @@ static uint8_t *tg3__decode_data_uri(tg3_arena *arena, const char *uri, uint32_t
return NULL;
}
if (out_mime && out_mime_cap > 0) {
mlen = strlen(dr.mime_type);
mlen = TINYGLTF3_STRLEN(dr.mime_type);
if (mlen >= out_mime_cap) mlen = out_mime_cap - 1u;
memcpy(out_mime, dr.mime_type, mlen);
TINYGLTF3_MEMCPY(out_mime, dr.mime_type, mlen);
out_mime[mlen] = '\0';
}
return tg3__b64_decode(dr.data_start, dr.data_len, out_len, arena);
}
#ifdef TINYGLTF3_ENABLE_FS
#if defined(TINYGLTF3_ENABLE_FS) && !defined(TINYGLTF3_NO_STDLIB)
static int32_t tg3__fs_file_exists(const char *path, uint32_t path_len, void *ud) {
FILE *fp;
(void)path_len; (void)ud;
@@ -409,18 +534,18 @@ static int32_t tg3__fs_read_file(uint8_t **out_data, uint64_t *out_size,
size = ftell(fp);
if (size < 0) { fclose(fp); return 0; }
if (fseek(fp, 0, SEEK_SET) != 0) { fclose(fp); return 0; }
data = (uint8_t *)malloc((size_t)size);
data = (uint8_t *)TINYGLTF3_MALLOC((size_t)size);
if (!data) { fclose(fp); return 0; }
nread = fread(data, 1, (size_t)size, fp);
fclose(fp);
if (nread != (size_t)size) { free(data); return 0; }
if (nread != (size_t)size) { TINYGLTF3_FREE(data); return 0; }
*out_data = data;
*out_size = (uint64_t)size;
return 1;
}
static void tg3__fs_free_file(uint8_t *data, uint64_t size, void *ud) {
(void)size; (void)ud; free(data);
(void)size; (void)ud; TINYGLTF3_FREE(data);
}
static int32_t tg3__fs_write_file(const char *path, uint32_t path_len,
@@ -444,7 +569,7 @@ static void tg3__set_default_fs(tg3_fs_callbacks *fs) {
#endif
static void tg3__model_init(tg3_model *model) {
memset(model, 0, sizeof(*model));
TINYGLTF3_MEMSET(model, 0, sizeof(*model));
model->default_scene = -1;
}
@@ -462,7 +587,7 @@ static int tg3__json_number_to_int32(const tg3json_value *v, int32_t *out) {
return 1;
}
real = v->u.real;
if (!isfinite(real) || real < (double)INT32_MIN || real > (double)INT32_MAX) {
if (!tg3__isfinite(real) || real < (double)INT32_MIN || real > (double)INT32_MAX) {
return 0;
}
converted = (int32_t)real;
@@ -485,7 +610,7 @@ static int tg3__json_number_to_uint64(const tg3json_value *v, uint64_t *out) {
return 1;
}
real = v->u.real;
if (!isfinite(real) || real < 0.0 || real > max_safe_uint64_real) {
if (!tg3__isfinite(real) || real < 0.0 || real > max_safe_uint64_real) {
return 0;
}
converted = (uint64_t)real;
@@ -783,7 +908,7 @@ static int tg3__parse_string_array(tg3__parse_ctx *ctx, const tg3json_value *o,
static tg3_value tg3__json_to_value(tg3__parse_ctx *ctx, const tg3json_value *j) {
tg3_value v;
size_t i;
memset(&v, 0, sizeof(v));
TINYGLTF3_MEMSET(&v, 0, sizeof(v));
if (!j) return v;
switch (j->type) {
case TG3JSON_NULL:
@@ -839,7 +964,7 @@ static void tg3__parse_extras_and_extensions(tg3__parse_ctx *ctx, const tg3json_
tg3_extras_ext *ee) {
const tg3json_value *extras_it;
const tg3json_value *ext_it;
memset(ee, 0, sizeof(*ee));
TINYGLTF3_MEMSET(ee, 0, sizeof(*ee));
extras_it = tg3__json_get(o, "extras");
if (extras_it) {
if (!ctx->opts.skip_extras_values) {
@@ -854,7 +979,7 @@ static void tg3__parse_extras_and_extensions(tg3__parse_ctx *ctx, const tg3json_
char *raw = tg3json_stringify(extras_it, &raw_len);
if (raw) {
ee->extras_json = tg3__arena_str(ctx->arena, raw, (uint32_t)raw_len);
free(raw);
TINYGLTF3_FREE(raw);
}
}
}
@@ -867,7 +992,7 @@ static void tg3__parse_extras_and_extensions(tg3__parse_ctx *ctx, const tg3json_
if (exts) {
for (i = 0; i < count; ++i) {
const tg3json_object_entry *entry = tg3json_object_at(ext_it, i);
memset(&exts[i], 0, sizeof(exts[i]));
TINYGLTF3_MEMSET(&exts[i], 0, sizeof(exts[i]));
exts[i].name = tg3__arena_str(ctx->arena, entry->key, (uint32_t)entry->key_len);
if (!ctx->opts.skip_extras_values) {
exts[i].value = tg3__json_to_value(ctx, entry->value);
@@ -884,17 +1009,17 @@ static void tg3__parse_extras_and_extensions(tg3__parse_ctx *ctx, const tg3json_
char *raw = tg3json_stringify(ext_it, &raw_len);
if (raw) {
ee->extensions_json = tg3__arena_str(ctx->arena, raw, (uint32_t)raw_len);
free(raw);
TINYGLTF3_FREE(raw);
}
}
}
}
static void tg3__init_texture_info(tg3_texture_info *ti) { memset(ti, 0, sizeof(*ti)); ti->index = -1; }
static void tg3__init_normal_texture_info(tg3_normal_texture_info *ti) { memset(ti, 0, sizeof(*ti)); ti->index = -1; ti->scale = 1.0; }
static void tg3__init_occlusion_texture_info(tg3_occlusion_texture_info *ti) { memset(ti, 0, sizeof(*ti)); ti->index = -1; ti->strength = 1.0; }
static void tg3__init_texture_info(tg3_texture_info *ti) { TINYGLTF3_MEMSET(ti, 0, sizeof(*ti)); ti->index = -1; }
static void tg3__init_normal_texture_info(tg3_normal_texture_info *ti) { TINYGLTF3_MEMSET(ti, 0, sizeof(*ti)); ti->index = -1; ti->scale = 1.0; }
static void tg3__init_occlusion_texture_info(tg3_occlusion_texture_info *ti) { TINYGLTF3_MEMSET(ti, 0, sizeof(*ti)); ti->index = -1; ti->strength = 1.0; }
static void tg3__init_pbr(tg3_pbr_metallic_roughness *pbr) {
memset(pbr, 0, sizeof(*pbr));
TINYGLTF3_MEMSET(pbr, 0, sizeof(*pbr));
pbr->base_color_factor[0] = 1.0; pbr->base_color_factor[1] = 1.0;
pbr->base_color_factor[2] = 1.0; pbr->base_color_factor[3] = 1.0;
pbr->metallic_factor = 1.0; pbr->roughness_factor = 1.0;
@@ -902,14 +1027,14 @@ static void tg3__init_pbr(tg3_pbr_metallic_roughness *pbr) {
tg3__init_texture_info(&pbr->metallic_roughness_texture);
}
static void tg3__init_node(tg3_node *n) {
memset(n, 0, sizeof(*n));
TINYGLTF3_MEMSET(n, 0, sizeof(*n));
n->camera = -1; n->skin = -1; n->mesh = -1; n->light = -1; n->emitter = -1;
n->rotation[3] = 1.0; n->scale[0] = 1.0; n->scale[1] = 1.0; n->scale[2] = 1.0;
n->matrix[0] = 1.0; n->matrix[5] = 1.0; n->matrix[10] = 1.0; n->matrix[15] = 1.0;
}
static int tg3__parse_asset(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_asset *asset) {
memset(asset, 0, sizeof(*asset));
TINYGLTF3_MEMSET(asset, 0, sizeof(*asset));
tg3__parse_string(ctx, o, "version", &asset->version, 0, "/asset");
tg3__parse_string(ctx, o, "generator", &asset->generator, 0, "/asset");
tg3__parse_string(ctx, o, "minVersion", &asset->min_version, 0, "/asset");
@@ -957,20 +1082,20 @@ static int tg3__parse_occlusion_texture_info(tg3__parse_ctx *ctx, const tg3json_
}
static int tg3__accessor_type_from_string(const char *s, size_t len) {
if (len == 6 && memcmp(s, "SCALAR", 6) == 0) return TG3_TYPE_SCALAR;
if (len == 4 && memcmp(s, "VEC2", 4) == 0) return TG3_TYPE_VEC2;
if (len == 4 && memcmp(s, "VEC3", 4) == 0) return TG3_TYPE_VEC3;
if (len == 4 && memcmp(s, "VEC4", 4) == 0) return TG3_TYPE_VEC4;
if (len == 4 && memcmp(s, "MAT2", 4) == 0) return TG3_TYPE_MAT2;
if (len == 4 && memcmp(s, "MAT3", 4) == 0) return TG3_TYPE_MAT3;
if (len == 4 && memcmp(s, "MAT4", 4) == 0) return TG3_TYPE_MAT4;
if (len == 6 && TINYGLTF3_MEMCMP(s, "SCALAR", 6) == 0) return TG3_TYPE_SCALAR;
if (len == 4 && TINYGLTF3_MEMCMP(s, "VEC2", 4) == 0) return TG3_TYPE_VEC2;
if (len == 4 && TINYGLTF3_MEMCMP(s, "VEC3", 4) == 0) return TG3_TYPE_VEC3;
if (len == 4 && TINYGLTF3_MEMCMP(s, "VEC4", 4) == 0) return TG3_TYPE_VEC4;
if (len == 4 && TINYGLTF3_MEMCMP(s, "MAT2", 4) == 0) return TG3_TYPE_MAT2;
if (len == 4 && TINYGLTF3_MEMCMP(s, "MAT3", 4) == 0) return TG3_TYPE_MAT3;
if (len == 4 && TINYGLTF3_MEMCMP(s, "MAT4", 4) == 0) return TG3_TYPE_MAT4;
return -1;
}
static int tg3__parse_accessor_sparse(tg3__parse_ctx *ctx, const tg3json_value *o,
tg3_accessor_sparse *sparse) {
const tg3json_value *it = tg3__json_get(o, "sparse");
memset(sparse, 0, sizeof(*sparse));
TINYGLTF3_MEMSET(sparse, 0, sizeof(*sparse));
sparse->indices.buffer_view = -1;
sparse->values.buffer_view = -1;
if (!it) return 1;
@@ -1006,7 +1131,7 @@ static int tg3__parse_accessor(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_
tg3_str type_str;
uint64_t bo = 0;
uint64_t cnt = 0;
memset(acc, 0, sizeof(*acc));
TINYGLTF3_MEMSET(acc, 0, sizeof(*acc));
acc->buffer_view = -1;
acc->component_type = -1;
acc->type = -1;
@@ -1035,7 +1160,7 @@ static int tg3__uri_is_safe(const char *uri, uint32_t uri_len) {
uint32_t i;
if (!uri || uri_len == 0) return 0;
/* No NUL bytes — fopen would truncate; protects against smuggling. */
if (memchr(uri, '\0', uri_len) != NULL) return 0;
if (TINYGLTF3_MEMCHR(uri, '\0', uri_len) != NULL) return 0;
/* Reject absolute paths (POSIX and Windows). */
if (uri[0] == '/' || uri[0] == '\\') return 0;
/* Reject Windows drive prefixes like "C:". */
@@ -1075,14 +1200,14 @@ static int tg3__load_external_file(tg3__parse_ctx *ctx, uint8_t **out_data, uint
if (uri_len >= sizeof(path_buf)) return 0;
if (ctx->base_dir_len > 0) {
if (ctx->base_dir_len >= sizeof(path_buf) - uri_len - 1u) return 0;
memcpy(path_buf, ctx->base_dir, ctx->base_dir_len);
TINYGLTF3_MEMCPY(path_buf, ctx->base_dir, ctx->base_dir_len);
path_len = ctx->base_dir_len;
if (path_len > 0 && path_buf[path_len - 1u] != '/' && path_buf[path_len - 1u] != '\\') {
path_buf[path_len++] = '/';
}
}
if (path_len + uri_len >= sizeof(path_buf)) return 0;
memcpy(path_buf + path_len, uri, uri_len);
TINYGLTF3_MEMCPY(path_buf + path_len, uri, uri_len);
path_len += uri_len;
path_buf[path_len] = '\0';
ok = ctx->opts.fs.read_file(out_data, out_size, path_buf, path_len, ctx->opts.fs.user_data);
@@ -1111,7 +1236,7 @@ static int tg3__load_external_file(tg3__parse_ctx *ctx, uint8_t **out_data, uint
static int tg3__parse_buffer(tg3__parse_ctx *ctx, const tg3json_value *o,
tg3_buffer *buf, int32_t buf_idx) {
uint64_t byte_length = 0;
memset(buf, 0, sizeof(*buf));
TINYGLTF3_MEMSET(buf, 0, sizeof(*buf));
tg3__parse_string(ctx, o, "name", &buf->name, 0, "/buffer");
tg3__parse_string(ctx, o, "uri", &buf->uri, 0, "/buffer");
tg3__parse_uint64(ctx, o, "byteLength", &byte_length, 1, "/buffer");
@@ -1140,7 +1265,7 @@ static int tg3__parse_buffer(tg3__parse_ctx *ctx, const tg3json_value *o,
"OOM for buffer data", NULL, -1);
return 0;
}
if (byte_length > 0) memcpy(data, ctx->bin_data, (size_t)byte_length);
if (byte_length > 0) TINYGLTF3_MEMCPY(data, ctx->bin_data, (size_t)byte_length);
buf->data.data = data;
buf->data.count = byte_length;
} else if (buf->uri.len > 0) {
@@ -1148,7 +1273,7 @@ static int tg3__parse_buffer(tg3__parse_ctx *ctx, const tg3json_value *o,
size_t decoded_len = 0;
char mime[64];
uint8_t *decoded;
memset(mime, 0, sizeof(mime));
TINYGLTF3_MEMSET(mime, 0, sizeof(mime));
decoded = tg3__decode_data_uri(ctx->arena, buf->uri.data, buf->uri.len, &decoded_len, mime, sizeof(mime));
if (!decoded && byte_length > 0) {
tg3__error_push(ctx->errors, TG3_SEVERITY_ERROR, TG3_ERR_DATA_URI_DECODE,
@@ -1171,7 +1296,7 @@ static int tg3__parse_buffer(tg3__parse_ctx *ctx, const tg3json_value *o,
}
data = (uint8_t *)tg3__arena_alloc(ctx->arena, (size_t)file_size);
if (data) {
memcpy(data, file_data, (size_t)file_size);
TINYGLTF3_MEMCPY(data, file_data, (size_t)file_size);
buf->data.data = data;
buf->data.count = file_size;
} else if (file_size > 0) {
@@ -1189,7 +1314,7 @@ static int tg3__parse_buffer(tg3__parse_ctx *ctx, const tg3json_value *o,
static int tg3__parse_buffer_view(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_buffer_view *bv) {
uint64_t val = 0;
int32_t stride = 0;
memset(bv, 0, sizeof(*bv));
TINYGLTF3_MEMSET(bv, 0, sizeof(*bv));
bv->buffer = -1;
tg3__parse_string(ctx, o, "name", &bv->name, 0, "/bufferView");
tg3__parse_int(ctx, o, "buffer", &bv->buffer, 1, "/bufferView");
@@ -1218,7 +1343,7 @@ static int tg3__parse_buffer_view(tg3__parse_ctx *ctx, const tg3json_value *o, t
static int tg3__parse_image(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_image *img, int32_t img_idx) {
(void)img_idx;
memset(img, 0, sizeof(*img));
TINYGLTF3_MEMSET(img, 0, sizeof(*img));
img->width = -1; img->height = -1; img->component = -1; img->bits = -1; img->pixel_type = -1; img->buffer_view = -1;
tg3__parse_string(ctx, o, "name", &img->name, 0, "/image");
tg3__parse_string(ctx, o, "uri", &img->uri, 0, "/image");
@@ -1230,7 +1355,7 @@ static int tg3__parse_image(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_ima
}
static int tg3__parse_sampler(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_sampler *samp) {
memset(samp, 0, sizeof(*samp));
TINYGLTF3_MEMSET(samp, 0, sizeof(*samp));
samp->min_filter = -1; samp->mag_filter = -1;
samp->wrap_s = TG3_TEXTURE_WRAP_REPEAT; samp->wrap_t = TG3_TEXTURE_WRAP_REPEAT;
tg3__parse_string(ctx, o, "name", &samp->name, 0, "/sampler");
@@ -1243,7 +1368,7 @@ static int tg3__parse_sampler(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_s
}
static int tg3__parse_texture(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_texture *tex) {
memset(tex, 0, sizeof(*tex));
TINYGLTF3_MEMSET(tex, 0, sizeof(*tex));
tex->sampler = -1; tex->source = -1;
tg3__parse_string(ctx, o, "name", &tex->name, 0, "/texture");
tg3__parse_int(ctx, o, "sampler", &tex->sampler, 0, "/texture");
@@ -1255,7 +1380,7 @@ static int tg3__parse_texture(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_t
static int tg3__parse_material(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_material *mat) {
const tg3json_value *pbr_it;
const tg3json_value *ext_it;
memset(mat, 0, sizeof(*mat));
TINYGLTF3_MEMSET(mat, 0, sizeof(*mat));
tg3__init_pbr(&mat->pbr_metallic_roughness);
tg3__init_normal_texture_info(&mat->normal_texture);
tg3__init_occlusion_texture_info(&mat->occlusion_texture);
@@ -1297,7 +1422,7 @@ static int tg3__parse_material(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_
static int tg3__parse_primitive(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_primitive *prim) {
const tg3json_value *attr_it;
const tg3json_value *targets_it;
memset(prim, 0, sizeof(*prim));
TINYGLTF3_MEMSET(prim, 0, sizeof(*prim));
prim->material = -1; prim->indices = -1; prim->mode = TG3_MODE_TRIANGLES;
tg3__parse_int(ctx, o, "material", &prim->material, 0, "/primitive");
tg3__parse_int(ctx, o, "indices", &prim->indices, 0, "/primitive");
@@ -1375,7 +1500,7 @@ static int tg3__parse_primitive(tg3__parse_ctx *ctx, const tg3json_value *o, tg3
static int tg3__parse_mesh(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_mesh *mesh) {
const tg3json_value *prim_it = tg3__json_get(o, "primitives");
memset(mesh, 0, sizeof(*mesh));
TINYGLTF3_MEMSET(mesh, 0, sizeof(*mesh));
tg3__parse_string(ctx, o, "name", &mesh->name, 0, "/mesh");
if (tg3__json_is_array(prim_it)) {
size_t count = tg3json_array_size(prim_it);
@@ -1421,7 +1546,7 @@ static int tg3__parse_node(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_node
}
static int tg3__parse_skin(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_skin *skin) {
memset(skin, 0, sizeof(*skin));
TINYGLTF3_MEMSET(skin, 0, sizeof(*skin));
skin->inverse_bind_matrices = -1; skin->skeleton = -1;
tg3__parse_string(ctx, o, "name", &skin->name, 0, "/skin");
tg3__parse_int(ctx, o, "inverseBindMatrices", &skin->inverse_bind_matrices, 0, "/skin");
@@ -1434,7 +1559,7 @@ static int tg3__parse_skin(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_skin
static int tg3__parse_animation(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_animation *anim) {
const tg3json_value *ch_it = tg3__json_get(o, "channels");
const tg3json_value *samp_it = tg3__json_get(o, "samplers");
memset(anim, 0, sizeof(*anim));
TINYGLTF3_MEMSET(anim, 0, sizeof(*anim));
tg3__parse_string(ctx, o, "name", &anim->name, 0, "/animation");
if (tg3__json_is_array(ch_it)) {
size_t count = tg3json_array_size(ch_it);
@@ -1445,7 +1570,7 @@ static int tg3__parse_animation(tg3__parse_ctx *ctx, const tg3json_value *o, tg3
for (i = 0; i < count; ++i) {
const tg3json_value *item = tg3json_array_get(ch_it, i);
const tg3json_value *tgt_it;
memset(&channels[i], 0, sizeof(channels[i]));
TINYGLTF3_MEMSET(&channels[i], 0, sizeof(channels[i]));
channels[i].sampler = -1;
channels[i].target.node = -1;
tg3__parse_int(ctx, item, "sampler", &channels[i].sampler, 1, "/animation/channel");
@@ -1471,7 +1596,7 @@ static int tg3__parse_animation(tg3__parse_ctx *ctx, const tg3json_value *o, tg3
for (i = 0; i < count; ++i) {
const tg3json_value *item = tg3json_array_get(samp_it, i);
tg3_str interp;
memset(&samplers[i], 0, sizeof(samplers[i]));
TINYGLTF3_MEMSET(&samplers[i], 0, sizeof(samplers[i]));
samplers[i].input = -1; samplers[i].output = -1;
tg3__parse_int(ctx, item, "input", &samplers[i].input, 1, "/animation/sampler");
tg3__parse_int(ctx, item, "output", &samplers[i].output, 1, "/animation/sampler");
@@ -1491,7 +1616,7 @@ static int tg3__parse_animation(tg3__parse_ctx *ctx, const tg3json_value *o, tg3
static int tg3__parse_camera(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_camera *cam) {
const tg3json_value *it;
memset(cam, 0, sizeof(*cam));
TINYGLTF3_MEMSET(cam, 0, sizeof(*cam));
tg3__parse_string(ctx, o, "name", &cam->name, 0, "/camera");
tg3__parse_string(ctx, o, "type", &cam->type, 1, "/camera");
if (cam->type.data && tg3_str_equals_cstr(cam->type, "perspective")) {
@@ -1519,7 +1644,7 @@ static int tg3__parse_camera(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_ca
static int tg3__parse_scene(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_scene *scene) {
const tg3json_value *ext_it;
memset(scene, 0, sizeof(*scene));
TINYGLTF3_MEMSET(scene, 0, sizeof(*scene));
tg3__parse_string(ctx, o, "name", &scene->name, 0, "/scene");
tg3__parse_int_array(ctx, o, "nodes", &scene->nodes, &scene->nodes_count, 0, "/scene");
ext_it = tg3__json_get(o, "extensions");
@@ -1535,7 +1660,7 @@ static int tg3__parse_scene(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_sce
static int tg3__parse_light(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_light *light) {
const tg3json_value *spot_it;
memset(light, 0, sizeof(*light));
TINYGLTF3_MEMSET(light, 0, sizeof(*light));
light->color[0] = 1.0; light->color[1] = 1.0; light->color[2] = 1.0;
light->intensity = 1.0; light->spot.outer_cone_angle = 0.7853981634;
tg3__parse_string(ctx, o, "name", &light->name, 0, "/light");
@@ -1554,7 +1679,7 @@ static int tg3__parse_light(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_lig
}
static int tg3__parse_audio_source(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_audio_source *src) {
memset(src, 0, sizeof(*src));
TINYGLTF3_MEMSET(src, 0, sizeof(*src));
src->buffer_view = -1;
tg3__parse_string(ctx, o, "name", &src->name, 0, "/audioSource");
tg3__parse_string(ctx, o, "uri", &src->uri, 0, "/audioSource");
@@ -1566,7 +1691,7 @@ static int tg3__parse_audio_source(tg3__parse_ctx *ctx, const tg3json_value *o,
static int tg3__parse_audio_emitter(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_audio_emitter *emitter) {
const tg3json_value *pos_it;
memset(emitter, 0, sizeof(*emitter));
TINYGLTF3_MEMSET(emitter, 0, sizeof(*emitter));
emitter->gain = 1.0; emitter->source = -1;
emitter->positional.cone_inner_angle = 6.283185307179586;
emitter->positional.cone_outer_angle = 6.283185307179586;
@@ -2093,7 +2218,7 @@ static tg3_error_code tg3__parse_from_json(tg3__parse_ctx *ctx, const tg3json_va
static void tg3__json_parse_options_from_tg3(const tg3_parse_options *options,
tg3json_parse_options *json_options) {
memset(json_options, 0, sizeof(*json_options));
TINYGLTF3_MEMSET(json_options, 0, sizeof(*json_options));
json_options->depth_limit = TINYGLTF3_MAX_NESTING_DEPTH;
json_options->memory_budget = options ? (size_t)options->memory.memory_budget : 0;
json_options->max_single_alloc = options ? (size_t)options->memory.max_single_alloc : 0;
@@ -2119,13 +2244,13 @@ static tg3_error_code tg3__parse_glb_header(const uint8_t *data, uint64_t size,
"Invalid GLB magic bytes", NULL, -1);
return TG3_ERR_GLB_INVALID_MAGIC;
}
memcpy(&version, data + 4, 4);
TINYGLTF3_MEMCPY(&version, data + 4, 4);
if (version != 2) {
tg3__error_push(errors, TG3_SEVERITY_ERROR, TG3_ERR_GLB_INVALID_VERSION,
"Unsupported GLB version (expected 2)", NULL, -1);
return TG3_ERR_GLB_INVALID_VERSION;
}
memcpy(&total_length, data + 8, 4);
TINYGLTF3_MEMCPY(&total_length, data + 8, 4);
if ((uint64_t)total_length != size) {
tg3__error_push(errors, TG3_SEVERITY_ERROR, TG3_ERR_GLB_SIZE_MISMATCH,
"GLB total length does not match data size", NULL, -1);
@@ -2134,8 +2259,8 @@ static tg3_error_code tg3__parse_glb_header(const uint8_t *data, uint64_t size,
while (offset + 8 <= (uint64_t)total_length) {
uint32_t chunk_length;
uint32_t chunk_type;
memcpy(&chunk_length, data + offset, 4); offset += 4;
memcpy(&chunk_type, data + offset, 4); offset += 4;
TINYGLTF3_MEMCPY(&chunk_length, data + offset, 4); offset += 4;
TINYGLTF3_MEMCPY(&chunk_type, data + offset, 4); offset += 4;
if (offset + chunk_length > (uint64_t)total_length) {
tg3__error_push(errors, TG3_SEVERITY_ERROR, TG3_ERR_GLB_CHUNK_ERROR,
"GLB chunk exceeds data size", NULL, -1);
@@ -2192,13 +2317,13 @@ TINYGLTF3_API tg3_error_code tg3_parse(tg3_model *model, tg3_error_stack *errors
* the caller; tg3_model_free is the sole arena owner on the error path. */
return TG3_ERR_JSON_PARSE;
}
memset(&ctx, 0, sizeof(ctx));
TINYGLTF3_MEMSET(&ctx, 0, sizeof(ctx));
ctx.arena = arena;
ctx.errors = errors;
ctx.opts = *options;
ctx.base_dir = base_dir;
ctx.base_dir_len = base_dir_len;
#ifdef TINYGLTF3_ENABLE_FS
#if defined(TINYGLTF3_ENABLE_FS) && !defined(TINYGLTF3_NO_STDLIB)
tg3__set_default_fs(&ctx.opts.fs);
#endif
ret = tg3__parse_from_json(&ctx, &json_doc, model);
@@ -2250,7 +2375,7 @@ TINYGLTF3_API tg3_error_code tg3_parse_glb(tg3_model *model, tg3_error_stack *er
/* Keep arena alive so error messages stay valid; model_free owns it. */
return TG3_ERR_JSON_PARSE;
}
memset(&ctx, 0, sizeof(ctx));
TINYGLTF3_MEMSET(&ctx, 0, sizeof(ctx));
ctx.arena = arena;
ctx.errors = errors;
ctx.opts = *options;
@@ -2259,7 +2384,7 @@ TINYGLTF3_API tg3_error_code tg3_parse_glb(tg3_model *model, tg3_error_stack *er
ctx.is_binary = 1;
ctx.bin_data = bin_chunk;
ctx.bin_size = bin_chunk_size;
#ifdef TINYGLTF3_ENABLE_FS
#if defined(TINYGLTF3_ENABLE_FS) && !defined(TINYGLTF3_NO_STDLIB)
tg3__set_default_fs(&ctx.opts.fs);
#endif
err = tg3__parse_from_json(&ctx, &json_doc, model);
@@ -2298,7 +2423,7 @@ TINYGLTF3_API tg3_error_code tg3_parse_file(tg3_model *model, tg3_error_stack *e
if (!filename) return TG3_ERR_FILE_NOT_FOUND;
if (options) opts = *options;
else tg3_parse_options_init(&opts);
#ifdef TINYGLTF3_ENABLE_FS
#if defined(TINYGLTF3_ENABLE_FS) && !defined(TINYGLTF3_NO_STDLIB)
tg3__set_default_fs(&opts.fs);
#endif
if (!opts.fs.read_file) {
@@ -2311,12 +2436,12 @@ TINYGLTF3_API tg3_error_code tg3_parse_file(tg3_model *model, tg3_error_stack *e
"Failed to read file", NULL, -1);
return TG3_ERR_FILE_NOT_FOUND;
}
memset(base_dir_buf, 0, sizeof(base_dir_buf));
TINYGLTF3_MEMSET(base_dir_buf, 0, sizeof(base_dir_buf));
for (i = 0; i < filename_len; ++i) {
if (filename[i] == '/' || filename[i] == '\\') base_dir_len = i;
}
if (base_dir_len > 0) {
memcpy(base_dir_buf, filename, base_dir_len);
TINYGLTF3_MEMCPY(base_dir_buf, filename, base_dir_len);
base_dir_buf[base_dir_len] = '\0';
}
result = tg3_parse_auto(model, errors, file_data, file_size, base_dir_buf, base_dir_len, &opts);
@@ -2327,7 +2452,7 @@ TINYGLTF3_API tg3_error_code tg3_parse_file(tg3_model *model, tg3_error_stack *e
TINYGLTF3_API void tg3_model_free(tg3_model *model) {
if (!model) return;
if (model->arena_) tg3__arena_destroy(model->arena_);
memset(model, 0, sizeof(*model));
TINYGLTF3_MEMSET(model, 0, sizeof(*model));
model->default_scene = -1;
}
@@ -2335,7 +2460,7 @@ static char *tg3__b64_encode(const uint8_t *input, size_t input_len, size_t *out
static const char chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
size_t enc_len = ((input_len + 2u) / 3u) * 4u;
char *out = (char *)malloc(enc_len + 1u);
char *out = (char *)TINYGLTF3_MALLOC(enc_len + 1u);
size_t i;
size_t j = 0;
if (!out) return NULL;
@@ -2408,7 +2533,7 @@ static int tg3__json_set_string_n(tg3json_value *obj, const char *key, const cha
}
static int tg3__json_set_string(tg3json_value *obj, const char *key, const char *str) {
return tg3__json_set_string_n(obj, key, str, str ? strlen(str) : 0);
return tg3__json_set_string_n(obj, key, str, str ? TINYGLTF3_STRLEN(str) : 0);
}
static int tg3__json_array_append_int(tg3json_value *arr, int64_t value) {
@@ -2478,7 +2603,7 @@ static int tg3__serialize_uint64(tg3json_value *o, const char *key, uint64_t val
static int tg3__serialize_double(tg3json_value *o, const char *key, double val,
double default_val, int write_defaults) {
if (fabs(val - default_val) <= 1e-12 && !write_defaults) return 1;
if (tg3__fabs(val - default_val) <= 1e-12 && !write_defaults) return 1;
return tg3__json_set_real(o, key, val);
}
@@ -2724,22 +2849,22 @@ static int tg3__serialize_buffer(const tg3_buffer *b, int wd, int embed, tg3json
tg3json_value_free(out);
return 0;
}
uri = (char *)malloc(prefix_len + enc_len + 1u);
uri = (char *)TINYGLTF3_MALLOC(prefix_len + enc_len + 1u);
if (!uri) {
free(encoded);
TINYGLTF3_FREE(encoded);
tg3json_value_free(out);
return 0;
}
memcpy(uri, prefix, prefix_len);
memcpy(uri + prefix_len, encoded, enc_len);
TINYGLTF3_MEMCPY(uri, prefix, prefix_len);
TINYGLTF3_MEMCPY(uri + prefix_len, encoded, enc_len);
uri[prefix_len + enc_len] = '\0';
free(encoded);
TINYGLTF3_FREE(encoded);
if (!tg3__json_set_string_n(out, "uri", uri, prefix_len + enc_len)) {
free(uri);
TINYGLTF3_FREE(uri);
tg3json_value_free(out);
return 0;
}
free(uri);
TINYGLTF3_FREE(uri);
}
if (!tg3__serialize_extras_ext(out, &b->ext)) {
tg3json_value_free(out);
@@ -2887,9 +3012,9 @@ static int tg3__serialize_material(const tg3_material *m, int wd, tg3json_value
has_pbr = 1;
}
if (!tg3__serialize_double(&pbr, "metallicFactor", p->metallic_factor, 1.0, wd)) goto fail;
if (fabs(p->metallic_factor - 1.0) > 1e-12 || wd) has_pbr = 1;
if (tg3__fabs(p->metallic_factor - 1.0) > 1e-12 || wd) has_pbr = 1;
if (!tg3__serialize_double(&pbr, "roughnessFactor", p->roughness_factor, 1.0, wd)) goto fail;
if (fabs(p->roughness_factor - 1.0) > 1e-12 || wd) has_pbr = 1;
if (tg3__fabs(p->roughness_factor - 1.0) > 1e-12 || wd) has_pbr = 1;
if (p->base_color_texture.index >= 0) {
if (!tg3__serialize_texture_info(&pbr, "baseColorTexture", &p->base_color_texture, wd)) goto fail;
has_pbr = 1;
@@ -3343,22 +3468,22 @@ TINYGLTF3_API tg3_error_code tg3_write_to_memory(const tg3_model *model, tg3_err
}
bin_padded = ((uint32_t)bin_len + 3u) & ~3u;
total = 12u + 8u + json_padded + ((bin_data && bin_len > 0) ? (8u + bin_padded) : 0u);
glb = (uint8_t *)malloc(total);
glb = (uint8_t *)TINYGLTF3_MALLOC(total);
if (!glb) {
free(json_str);
TINYGLTF3_FREE(json_str);
tg3__error_push(errors, TG3_SEVERITY_ERROR, TG3_ERR_OUT_OF_MEMORY,
"OOM allocating GLB output", NULL, -1);
return TG3_ERR_OUT_OF_MEMORY;
}
memcpy(glb, "glTF", 4);
TINYGLTF3_MEMCPY(glb, "glTF", 4);
{
uint32_t version = 2u;
uint32_t json_type = 0x4E4F534Au;
memcpy(glb + 4, &version, 4);
memcpy(glb + 8, &total, 4);
memcpy(glb + 12, &json_padded, 4);
memcpy(glb + 16, &json_type, 4);
memcpy(glb + 20, json_str, json_len);
TINYGLTF3_MEMCPY(glb + 4, &version, 4);
TINYGLTF3_MEMCPY(glb + 8, &total, 4);
TINYGLTF3_MEMCPY(glb + 12, &json_padded, 4);
TINYGLTF3_MEMCPY(glb + 16, &json_type, 4);
TINYGLTF3_MEMCPY(glb + 20, json_str, json_len);
}
while ((uint32_t)json_len < json_padded) {
glb[20u + json_len] = ' ';
@@ -3368,25 +3493,25 @@ TINYGLTF3_API tg3_error_code tg3_write_to_memory(const tg3_model *model, tg3_err
uint32_t bin_off = 20u + json_padded;
uint32_t bin_type = 0x004E4942u;
uint32_t i;
memcpy(glb + bin_off, &bin_padded, 4);
memcpy(glb + bin_off + 4u, &bin_type, 4);
memcpy(glb + bin_off + 8u, bin_data, (size_t)bin_len);
TINYGLTF3_MEMCPY(glb + bin_off, &bin_padded, 4);
TINYGLTF3_MEMCPY(glb + bin_off + 4u, &bin_type, 4);
TINYGLTF3_MEMCPY(glb + bin_off + 8u, bin_data, (size_t)bin_len);
for (i = (uint32_t)bin_len; i < bin_padded; ++i) glb[bin_off + 8u + i] = 0;
}
free(json_str);
TINYGLTF3_FREE(json_str);
*out_data = glb;
*out_size = total;
return TG3_OK;
} else {
uint8_t *data = (uint8_t *)malloc(json_len);
uint8_t *data = (uint8_t *)TINYGLTF3_MALLOC(json_len);
if (!data) {
free(json_str);
TINYGLTF3_FREE(json_str);
tg3__error_push(errors, TG3_SEVERITY_ERROR, TG3_ERR_OUT_OF_MEMORY,
"OOM allocating JSON output", NULL, -1);
return TG3_ERR_OUT_OF_MEMORY;
}
memcpy(data, json_str, json_len);
free(json_str);
TINYGLTF3_MEMCPY(data, json_str, json_len);
TINYGLTF3_FREE(json_str);
*out_data = data;
*out_size = (uint64_t)json_len;
return TG3_OK;
@@ -3403,7 +3528,7 @@ TINYGLTF3_API tg3_error_code tg3_write_to_file(const tg3_model *model, tg3_error
int32_t ok;
if (options) opts = *options;
else tg3_write_options_init(&opts);
#ifdef TINYGLTF3_ENABLE_FS
#if defined(TINYGLTF3_ENABLE_FS) && !defined(TINYGLTF3_NO_STDLIB)
tg3__set_default_fs(&opts.fs);
#endif
if (!opts.fs.write_file) {
@@ -3414,7 +3539,7 @@ TINYGLTF3_API tg3_error_code tg3_write_to_file(const tg3_model *model, tg3_error
err = tg3_write_to_memory(model, errors, &data, &size, &opts);
if (err != TG3_OK) return err;
ok = opts.fs.write_file(filename, filename_len, data, size, opts.fs.user_data);
free(data);
TINYGLTF3_FREE(data);
if (!ok) {
tg3__error_push(errors, TG3_SEVERITY_ERROR, TG3_ERR_FILE_WRITE,
"Failed to write file", NULL, -1);
@@ -3425,14 +3550,14 @@ TINYGLTF3_API tg3_error_code tg3_write_to_file(const tg3_model *model, tg3_error
TINYGLTF3_API void tg3_write_free(uint8_t *data, const tg3_write_options *options) {
(void)options;
free(data);
TINYGLTF3_FREE(data);
}
TINYGLTF3_API tg3_writer *tg3_writer_create(tg3_write_chunk_fn chunk_fn, void *user_data,
const tg3_write_options *options) {
tg3_writer *w = (tg3_writer *)malloc(sizeof(tg3_writer));
tg3_writer *w = (tg3_writer *)TINYGLTF3_MALLOC(sizeof(tg3_writer));
if (!w) return NULL;
memset(w, 0, sizeof(*w));
TINYGLTF3_MEMSET(w, 0, sizeof(*w));
w->chunk_fn = chunk_fn;
w->user_data = user_data;
if (options) w->options = *options;
@@ -3508,12 +3633,12 @@ TINYGLTF3_API tg3_error_code tg3_writer_end(tg3_writer *w) {
: tg3json_stringify(&w->root, &json_len);
if (!json_str) return TG3_ERR_OUT_OF_MEMORY;
ok = w->chunk_fn((const uint8_t *)json_str, (uint64_t)json_len, w->user_data);
free(json_str);
TINYGLTF3_FREE(json_str);
return ok ? TG3_OK : TG3_ERR_WRITE_FAILED;
}
TINYGLTF3_API void tg3_writer_destroy(tg3_writer *w) {
if (!w) return;
tg3json_value_free(&w->root);
free(w);
TINYGLTF3_FREE(w);
}

View File

@@ -116,8 +116,12 @@
/* Assert override */
#ifndef TINYGLTF3_ASSERT
#ifndef TINYGLTF3_NO_STDLIB
#include <assert.h>
#define TINYGLTF3_ASSERT(x) assert(x)
#else
#define TINYGLTF3_ASSERT(x) ((void)(x))
#endif
#endif
/* ======================================================================
@@ -127,8 +131,34 @@
#include <stddef.h>
#include <stdint.h>
#include <stdarg.h>
#ifndef TINYGLTF3_NO_STDLIB
#include <string.h>
#include <stdlib.h>
#endif
#ifndef TINYGLTF3_MALLOC
#ifndef TINYGLTF3_NO_STDLIB
#define TINYGLTF3_MALLOC(sz) malloc(sz)
#else
#define TINYGLTF3_MALLOC(sz) NULL
#endif
#endif
#ifndef TINYGLTF3_REALLOC
#ifndef TINYGLTF3_NO_STDLIB
#define TINYGLTF3_REALLOC(ptr, sz) realloc((ptr), (sz))
#else
#define TINYGLTF3_REALLOC(ptr, sz) NULL
#endif
#endif
#ifndef TINYGLTF3_FREE
#ifndef TINYGLTF3_NO_STDLIB
#define TINYGLTF3_FREE(ptr) free(ptr)
#else
#define TINYGLTF3_FREE(ptr) ((void)(ptr))
#endif
#endif
/* ======================================================================
* Section 4: Constants and Enums

View File

@@ -1,9 +1,24 @@
#ifndef TINYGLTF_JSON_C_H_
#define TINYGLTF_JSON_C_H_
/*
* Floating-point conversion attribution:
*
* - JSON number parsing in this C11 port is based on fast_float.
* Upstream: https://github.com/fastfloat/fast_float
* Copyright (c) 2021 The fast_float authors.
* License: Apache License 2.0, MIT License, or Boost Software License 1.0.
*
* - JSON number serialization in this C11 port is based on Dragonbox-style
* shortest round-trippable binary floating-point to decimal conversion.
* Upstream: https://github.com/jk-jeon/dragonbox
* Copyright 2020-2024 Junekey Jeon.
* License: Apache License 2.0 with LLVM Exceptions, or Boost Software
* License 1.0.
*/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
@@ -111,9 +126,433 @@ char *tg3json_stringify_pretty(const tg3json_value *value, int indent, size_t *o
#ifdef TINYGLTF_JSON_C_IMPLEMENTATION
#include <errno.h>
#ifndef TINYGLTF_JSON_NO_STDLIB
#include <stdlib.h>
#include <string.h>
#endif
#ifdef TINYGLTF_JSON_NO_STDLIB
static void *tg3json__memcpy_fallback(void *dst, const void *src, size_t sz) {
unsigned char *d = (unsigned char *)dst;
const unsigned char *s = (const unsigned char *)src;
while (sz--) *d++ = *s++;
return dst;
}
static void *tg3json__memset_fallback(void *dst, int val, size_t sz) {
unsigned char *d = (unsigned char *)dst;
while (sz--) *d++ = (unsigned char)val;
return dst;
}
static int tg3json__memcmp_fallback(const void *a, const void *b, size_t sz) {
const unsigned char *pa = (const unsigned char *)a;
const unsigned char *pb = (const unsigned char *)b;
while (sz--) {
if (*pa != *pb) return (int)*pa - (int)*pb;
++pa;
++pb;
}
return 0;
}
static size_t tg3json__strlen_fallback(const char *str) {
const char *p = str;
while (*p) ++p;
return (size_t)(p - str);
}
#endif
#ifndef TINYGLTF_JSON_MALLOC
#ifdef TINYGLTF_JSON_NO_STDLIB
#define TINYGLTF_JSON_MALLOC(sz) NULL
#else
#define TINYGLTF_JSON_MALLOC(sz) malloc(sz)
#endif
#endif
#ifndef TINYGLTF_JSON_REALLOC
#ifdef TINYGLTF_JSON_NO_STDLIB
#define TINYGLTF_JSON_REALLOC(ptr, sz) NULL
#else
#define TINYGLTF_JSON_REALLOC(ptr, sz) realloc(ptr, sz)
#endif
#endif
#ifndef TINYGLTF_JSON_FREE
#ifdef TINYGLTF_JSON_NO_STDLIB
#define TINYGLTF_JSON_FREE(ptr) ((void)(ptr))
#else
#define TINYGLTF_JSON_FREE(ptr) free(ptr)
#endif
#endif
#ifndef TINYGLTF_JSON_MEMCPY
#ifdef TINYGLTF_JSON_NO_STDLIB
#define TINYGLTF_JSON_MEMCPY(dst, src, sz) tg3json__memcpy_fallback((dst), (src), (sz))
#else
#define TINYGLTF_JSON_MEMCPY(dst, src, sz) memcpy(dst, src, sz)
#endif
#endif
#ifndef TINYGLTF_JSON_MEMSET
#ifdef TINYGLTF_JSON_NO_STDLIB
#define TINYGLTF_JSON_MEMSET(dst, val, sz) tg3json__memset_fallback((dst), (val), (sz))
#else
#define TINYGLTF_JSON_MEMSET(dst, val, sz) memset(dst, val, sz)
#endif
#endif
#ifndef TINYGLTF_JSON_MEMCMP
#ifdef TINYGLTF_JSON_NO_STDLIB
#define TINYGLTF_JSON_MEMCMP(a, b, sz) tg3json__memcmp_fallback((a), (b), (sz))
#else
#define TINYGLTF_JSON_MEMCMP(a, b, sz) memcmp(a, b, sz)
#endif
#endif
#ifndef TINYGLTF_JSON_STRLEN
#ifdef TINYGLTF_JSON_NO_STDLIB
#define TINYGLTF_JSON_STRLEN(str) tg3json__strlen_fallback((str))
#else
#define TINYGLTF_JSON_STRLEN(str) strlen(str)
#endif
#endif
static size_t tg3json__itoa(int64_t value, char *buf) {
char *p = buf;
uint64_t val;
char tmp[24];
int i = 0;
if (value < 0) {
*p++ = '-';
val = (value == -9223372036854775807LL - 1) ? 9223372036854775808ULL : (uint64_t)(-value);
} else {
val = (uint64_t)value;
}
if (val == 0) {
*p++ = '0';
*p = '\0';
return (size_t)(p - buf);
}
while (val > 0) {
tmp[i++] = (char)('0' + (val % 10));
val /= 10;
}
while (i > 0) {
*p++ = tmp[--i];
}
*p = '\0';
return (size_t)(p - buf);
}
static size_t tg3json__utoa(uint64_t value, char *buf) {
char tmp[32];
size_t n = 0;
size_t i = 0;
if (value == 0) {
buf[0] = '0';
buf[1] = '\0';
return 1;
}
while (value > 0) {
tmp[n++] = (char)('0' + (value % 10u));
value /= 10u;
}
while (n > 0) buf[i++] = tmp[--n];
buf[i] = '\0';
return i;
}
static uint64_t tg3json__double_bits(double v) {
uint64_t bits = 0;
TINYGLTF_JSON_MEMCPY(&bits, &v, sizeof(bits));
return bits;
}
static double tg3json__double_from_bits(uint64_t bits) {
double v = 0.0;
TINYGLTF_JSON_MEMCPY(&v, &bits, sizeof(v));
return v;
}
static int tg3json__is_nan_bits(uint64_t bits) {
return ((bits & 0x7ff0000000000000ULL) == 0x7ff0000000000000ULL) &&
((bits & 0x000fffffffffffffULL) != 0);
}
static int tg3json__is_inf_bits(uint64_t bits) {
return (bits & 0x7fffffffffffffffULL) == 0x7ff0000000000000ULL;
}
static long double tg3json__pow10_ld(int exp10) {
long double v = 1.0L;
if (exp10 < 0) {
exp10 = -exp10;
while (exp10 >= 16) {
v *= 1.0e-16L;
exp10 -= 16;
}
while (exp10-- > 0) v *= 0.1L;
} else {
while (exp10 >= 16) {
v *= 1.0e16L;
exp10 -= 16;
}
while (exp10-- > 0) v *= 10.0L;
}
return v;
}
static int tg3json__parse_f64_c(const char *start, const char *end, double *out) {
const char *p = start;
int neg = 0;
int saw_digit = 0;
int nonzero_seen = 0;
int exp10 = 0;
uint64_t sig = 0;
int sig_digits = 0;
long double extra = 0.0L;
int extra_digits = 0;
int exp_sign = 1;
int exp_lit = 0;
long double v;
if (p < end && *p == '-') {
neg = 1;
++p;
}
if (p >= end) return 0;
if (*p == '0') {
saw_digit = 1;
++p;
} else if (*p >= '1' && *p <= '9') {
do {
int d = *p - '0';
saw_digit = 1;
nonzero_seen = 1;
if (sig_digits < 19) {
sig = sig * 10u + (uint64_t)d;
++sig_digits;
} else if (extra_digits < 64) {
extra = extra * 10.0L + (long double)d;
++extra_digits;
} else {
++exp10;
}
++p;
} while (p < end && *p >= '0' && *p <= '9');
} else {
return 0;
}
if (p < end && *p == '.') {
++p;
if (p >= end || *p < '0' || *p > '9') return 0;
do {
int d = *p - '0';
saw_digit = 1;
if (d != 0 || nonzero_seen) {
nonzero_seen = nonzero_seen || (d != 0);
if (sig_digits < 19) {
sig = sig * 10u + (uint64_t)d;
++sig_digits;
--exp10;
} else if (extra_digits < 64) {
extra = extra * 10.0L + (long double)d;
++extra_digits;
--exp10;
} else {
--exp10;
}
} else {
--exp10;
}
++p;
} while (p < end && *p >= '0' && *p <= '9');
}
if (!saw_digit) return 0;
if (p < end && (*p == 'e' || *p == 'E')) {
++p;
if (p < end && (*p == '+' || *p == '-')) {
exp_sign = (*p == '-') ? -1 : 1;
++p;
}
if (p >= end || *p < '0' || *p > '9') return 0;
while (p < end && *p >= '0' && *p <= '9') {
if (exp_lit < 10000) exp_lit = exp_lit * 10 + (*p - '0');
++p;
}
exp10 += exp_sign * exp_lit;
}
if (p != end) return 0;
if (!nonzero_seen || sig_digits == 0) {
*out = neg ? tg3json__double_from_bits(0x8000000000000000ULL) : 0.0;
return 1;
}
if (exp10 > 309) return 0;
if (exp10 < -4000) {
*out = neg ? tg3json__double_from_bits(0x8000000000000000ULL) : 0.0;
return 1;
}
v = (long double)sig;
if (extra_digits > 0) {
v = v * tg3json__pow10_ld(extra_digits) + extra;
}
if (exp10 != 0) v *= tg3json__pow10_ld(exp10);
if (neg) v = -v;
*out = (double)v;
if (tg3json__is_inf_bits(tg3json__double_bits(*out))) return 0;
return 1;
}
static char *tg3json__write_exp(char *p, int exp10) {
char tmp[16];
size_t n;
*p++ = 'e';
if (exp10 < 0) {
*p++ = '-';
exp10 = -exp10;
}
n = tg3json__utoa((uint64_t)exp10, tmp);
TINYGLTF_JSON_MEMCPY(p, tmp, n);
return p + n;
}
static char *tg3json__format_decimal_digits(char *out, const char *digits,
int ndigits, int dec_exp,
int negative) {
char *p = out;
int i;
int output_exp = dec_exp + ndigits - 1;
if (negative) *p++ = '-';
if (output_exp < -4 || output_exp >= 16) {
*p++ = digits[0];
if (ndigits > 1) {
*p++ = '.';
for (i = 1; i < ndigits; ++i) *p++ = digits[i];
}
return tg3json__write_exp(p, output_exp);
}
if (dec_exp >= 0) {
for (i = 0; i < ndigits; ++i) *p++ = digits[i];
for (i = 0; i < dec_exp; ++i) *p++ = '0';
return p;
}
if (dec_exp + ndigits > 0) {
int int_digits = dec_exp + ndigits;
for (i = 0; i < int_digits; ++i) *p++ = digits[i];
*p++ = '.';
for (; i < ndigits; ++i) *p++ = digits[i];
return p;
}
*p++ = '0';
*p++ = '.';
for (i = 0; i < -(dec_exp + ndigits); ++i) *p++ = '0';
for (i = 0; i < ndigits; ++i) *p++ = digits[i];
return p;
}
static int tg3json__same_f64(double a, double b) {
return tg3json__double_bits(a) == tg3json__double_bits(b);
}
static char *tg3json__dtoa_c(double value, char *buf) {
uint64_t bits = tg3json__double_bits(value);
int negative = (int)(bits >> 63);
uint64_t abits = bits & 0x7fffffffffffffffULL;
long double x;
int dec_e = 0;
char digits[24];
int ndigits = 17;
int i;
char best[80];
size_t best_len;
if (tg3json__is_nan_bits(bits)) {
TINYGLTF_JSON_MEMCPY(buf, "nan", 3);
return buf + 3;
}
if (tg3json__is_inf_bits(bits)) {
if (negative) {
TINYGLTF_JSON_MEMCPY(buf, "-inf", 4);
return buf + 4;
}
TINYGLTF_JSON_MEMCPY(buf, "inf", 3);
return buf + 3;
}
if (abits == 0) {
*buf++ = '0';
return buf;
}
if (bits == 0x3ff0000000000000ULL) {
*buf++ = '1';
return buf;
}
if (bits == 0xbff0000000000000ULL) {
*buf++ = '-';
*buf++ = '1';
return buf;
}
x = negative ? -(long double)value : (long double)value;
while (x >= 1.0e16L) {
x *= 1.0e-16L;
dec_e += 16;
}
while (x >= 10.0L) {
x *= 0.1L;
++dec_e;
}
while (x < 1.0L) {
x *= 10.0L;
--dec_e;
}
for (i = 0; i < 18; ++i) {
int d = (int)x;
if (d < 0) d = 0;
if (d > 9) d = 9;
digits[i] = (char)('0' + d);
x = (x - (long double)d) * 10.0L;
}
if (digits[17] >= '5') {
int carry = 1;
for (i = 16; i >= 0 && carry; --i) {
if (digits[i] == '9') {
digits[i] = '0';
} else {
digits[i]++;
carry = 0;
}
}
if (carry) {
digits[0] = '1';
for (i = 1; i < 17; ++i) digits[i] = '0';
++dec_e;
}
}
while (ndigits > 1 && digits[ndigits - 1] == '0') --ndigits;
{
char *end = tg3json__format_decimal_digits(best, digits, ndigits,
dec_e - ndigits + 1,
negative);
*end = '\0';
best_len = (size_t)(end - best);
}
for (i = ndigits - 1; i >= 1; --i) {
char candidate[80];
char *end;
double parsed = 0.0;
end = tg3json__format_decimal_digits(candidate, digits, i,
dec_e - i + 1, negative);
*end = '\0';
if (tg3json__parse_f64_c(candidate, end, &parsed) &&
tg3json__same_f64(parsed, value)) {
TINYGLTF_JSON_MEMCPY(best, candidate, (size_t)(end - candidate) + 1);
best_len = (size_t)(end - candidate);
} else {
break;
}
}
TINYGLTF_JSON_MEMCPY(buf, best, best_len);
return buf + best_len;
}
typedef struct tg3json__parser {
const char *cur;
@@ -136,27 +575,27 @@ typedef struct tg3json__buffer {
static void tg3json__init_value(tg3json_value *value) {
if (!value) return;
memset(value, 0, sizeof(*value));
TINYGLTF_JSON_MEMSET(value, 0, sizeof(*value));
value->type = TG3JSON_NULL;
}
static char *tg3json__strndup_local(const char *src, size_t len) {
char *dst = (char *)malloc(len + 1);
char *dst = (char *)TINYGLTF_JSON_MALLOC(len + 1);
if (!dst) return NULL;
if (len > 0) memcpy(dst, src, len);
if (len > 0) TINYGLTF_JSON_MEMCPY(dst, src, len);
dst[len] = '\0';
return dst;
}
static void *tg3json__parser_alloc(tg3json__parser *parser, size_t size) {
void *ptr;
if (!parser) return malloc(size);
if (!parser) return TINYGLTF_JSON_MALLOC(size);
if (parser->max_single_alloc && size > parser->max_single_alloc) return NULL;
if (parser->memory_budget &&
(size > parser->memory_budget || parser->allocated > parser->memory_budget - size)) {
return NULL;
}
ptr = malloc(size);
ptr = TINYGLTF_JSON_MALLOC(size);
if (!ptr) return NULL;
parser->allocated += size;
return ptr;
@@ -178,7 +617,7 @@ static int tg3json__reserve_bytes(void **ptr, size_t elem_size,
}
if (elem_size != 0 && new_cap > ((size_t)-1) / elem_size) return 0;
new_ptr = realloc(*ptr, elem_size * new_cap);
new_ptr = TINYGLTF_JSON_REALLOC(*ptr, elem_size * new_cap);
if (!new_ptr) return 0;
*ptr = new_ptr;
*capacity = new_cap;
@@ -215,7 +654,7 @@ static int tg3json__reserve_bytes_parser(tg3json__parser *parser, void **ptr,
return 0;
}
}
new_ptr = realloc(*ptr, new_bytes);
new_ptr = TINYGLTF_JSON_REALLOC(*ptr, new_bytes);
if (!new_ptr) return 0;
if (parser && new_bytes > old_bytes) parser->allocated += new_bytes - old_bytes;
*ptr = new_ptr;
@@ -242,7 +681,7 @@ static int tg3json__buf_append(tg3json__buffer *buf, const char *src, size_t len
buf->len + len + 1, &buf->cap)) {
return 0;
}
memcpy(buf->data + buf->len, src, len);
TINYGLTF_JSON_MEMCPY(buf->data + buf->len, src, len);
buf->len += len;
buf->data[buf->len] = '\0';
return 1;
@@ -300,7 +739,7 @@ static int tg3json__parse_string_raw(tg3json__parser *parser,
char **out_str, size_t *out_len) {
tg3json__buffer buf;
const char *start;
memset(&buf, 0, sizeof(buf));
TINYGLTF_JSON_MEMSET(&buf, 0, sizeof(buf));
buf.parser = parser;
if (parser->cur >= parser->end || *parser->cur != '"') {
@@ -356,7 +795,7 @@ static int tg3json__parse_string_raw(tg3json__parser *parser,
}
default:
tg3json__set_error(parser, parser->cur);
free(buf.data);
TINYGLTF_JSON_FREE(buf.data);
return 0;
}
++parser->cur;
@@ -368,12 +807,12 @@ static int tg3json__parse_string_raw(tg3json__parser *parser,
}
tg3json__set_error(parser, parser->cur);
free(buf.data);
TINYGLTF_JSON_FREE(buf.data);
return 0;
oom:
tg3json__set_error(parser, parser->cur);
free(buf.data);
TINYGLTF_JSON_FREE(buf.data);
return 0;
}
@@ -456,7 +895,7 @@ fail:
--count;
tg3json_value_free(&items[count]);
}
free(items);
TINYGLTF_JSON_FREE(items);
tg3json__set_error(parser, parser->cur);
return 0;
@@ -465,7 +904,7 @@ oom:
--count;
tg3json_value_free(&items[count]);
}
free(items);
TINYGLTF_JSON_FREE(items);
tg3json__set_error(parser, parser->cur);
return 0;
}
@@ -494,18 +933,18 @@ static int tg3json__parse_object(tg3json__parser *parser, size_t depth,
if (!tg3json__parse_string_raw(parser, &key, &key_len)) goto fail;
parser->cur = tg3json__skip_ws(parser->cur, parser->end);
if (parser->cur >= parser->end || *parser->cur != ':') {
free(key);
TINYGLTF_JSON_FREE(key);
goto fail;
}
++parser->cur;
parser->cur = tg3json__skip_ws(parser->cur, parser->end);
if (!tg3json__parse_value(parser, depth + 1, &value)) {
free(key);
TINYGLTF_JSON_FREE(key);
goto fail;
}
if (!tg3json__reserve_bytes_parser(parser, (void **)&items,
sizeof(*items), count + 1, &cap)) {
free(key);
TINYGLTF_JSON_FREE(key);
tg3json_value_free(&value);
goto oom;
}
@@ -513,7 +952,7 @@ static int tg3json__parse_object(tg3json__parser *parser, size_t depth,
items[count].key_len = key_len;
items[count].value = (tg3json_value *)tg3json__parser_alloc(parser, sizeof(tg3json_value));
if (!items[count].value) {
free(key);
TINYGLTF_JSON_FREE(key);
tg3json_value_free(&value);
goto oom;
}
@@ -539,26 +978,26 @@ static int tg3json__parse_object(tg3json__parser *parser, size_t depth,
fail:
while (count > 0) {
--count;
free(items[count].key);
TINYGLTF_JSON_FREE(items[count].key);
if (items[count].value) {
tg3json_value_free(items[count].value);
free(items[count].value);
TINYGLTF_JSON_FREE(items[count].value);
}
}
free(items);
TINYGLTF_JSON_FREE(items);
tg3json__set_error(parser, parser->cur);
return 0;
oom:
while (count > 0) {
--count;
free(items[count].key);
TINYGLTF_JSON_FREE(items[count].key);
if (items[count].value) {
tg3json_value_free(items[count].value);
free(items[count].value);
TINYGLTF_JSON_FREE(items[count].value);
}
}
free(items);
TINYGLTF_JSON_FREE(items);
tg3json__set_error(parser, parser->cur);
return 0;
}
@@ -567,9 +1006,6 @@ static int tg3json__parse_number(tg3json__parser *parser, tg3json_value *out_val
const char *start = parser->cur;
const char *p = parser->cur;
int is_real = 0;
size_t len;
char stack_buf[128];
char *num_buf = stack_buf;
if (*p == '-') ++p;
if (p >= parser->end) goto fail;
@@ -605,59 +1041,55 @@ static int tg3json__parse_number(tg3json__parser *parser, tg3json_value *out_val
is_real = 1;
}
len = (size_t)(p - start);
if (len + 1 > sizeof(stack_buf)) {
num_buf = (char *)tg3json__parser_alloc(parser, len + 1);
if (!num_buf) goto oom;
}
memcpy(num_buf, start, len);
num_buf[len] = '\0';
{
char *endptr = NULL;
errno = 0;
out_value->u.real = strtod(num_buf, &endptr);
if (endptr != num_buf + len) {
if (num_buf != stack_buf) free(num_buf);
goto fail;
}
if (!tg3json__parse_f64_c(start, p, &out_value->u.real)) goto fail;
if (parser->parse_float32) out_value->u.real = (double)(float)out_value->u.real;
out_value->type = TG3JSON_REAL;
parser->cur = p;
if (num_buf != stack_buf) free(num_buf);
return 1;
}
fail:
tg3json__set_error(parser, parser->cur);
return 0;
oom:
tg3json__set_error(parser, parser->cur);
return 0;
}
static int tg3json__parse_value(tg3json__parser *parser, size_t depth,
tg3json_value *out_value) {
tg3json__init_value(out_value);
if (depth > parser->depth_limit) {
if (parser->depth_limit && depth > parser->depth_limit) {
tg3json__set_error(parser, parser->cur);
return 0;
}
parser->cur = tg3json__skip_ws(parser->cur, parser->end);
if (parser->cur >= parser->end) {
tg3json__set_error(parser, parser->cur);
return 0;
}
switch (*parser->cur) {
case '[':
return tg3json__parse_array(parser, depth, out_value);
case '{':
return tg3json__parse_object(parser, depth, out_value);
case '"': {
char *str = NULL;
size_t len = 0;
if (!tg3json__parse_string_raw(parser, &str, &len)) return 0;
out_value->type = TG3JSON_STRING;
out_value->u.string.ptr = str;
out_value->u.string.len = len;
return 1;
}
case 'n':
if ((size_t)(parser->end - parser->cur) >= 4 && memcmp(parser->cur, "null", 4) == 0) {
if ((size_t)(parser->end - parser->cur) >= 4 && TINYGLTF_JSON_MEMCMP(parser->cur, "null", 4) == 0) {
out_value->type = TG3JSON_NULL;
parser->cur += 4;
return 1;
}
break;
case 't':
if ((size_t)(parser->end - parser->cur) >= 4 && memcmp(parser->cur, "true", 4) == 0) {
if ((size_t)(parser->end - parser->cur) >= 4 && TINYGLTF_JSON_MEMCMP(parser->cur, "true", 4) == 0) {
out_value->type = TG3JSON_BOOL;
out_value->u.boolean = 1;
parser->cur += 4;
@@ -665,27 +1097,17 @@ static int tg3json__parse_value(tg3json__parser *parser, size_t depth,
}
break;
case 'f':
if ((size_t)(parser->end - parser->cur) >= 5 && memcmp(parser->cur, "false", 5) == 0) {
if ((size_t)(parser->end - parser->cur) >= 5 && TINYGLTF_JSON_MEMCMP(parser->cur, "false", 5) == 0) {
out_value->type = TG3JSON_BOOL;
out_value->u.boolean = 0;
parser->cur += 5;
return 1;
}
break;
case '"':
out_value->type = TG3JSON_STRING;
return tg3json__parse_string_raw(parser, &out_value->u.string.ptr,
&out_value->u.string.len);
case '[':
return tg3json__parse_array(parser, depth, out_value);
case '{':
return tg3json__parse_object(parser, depth, out_value);
default:
if (*parser->cur == '-' || (*parser->cur >= '0' && *parser->cur <= '9')) {
return tg3json__parse_number(parser, out_value);
}
break;
}
tg3json__set_error(parser, parser->cur);
return 0;
}
@@ -695,40 +1117,50 @@ int tg3json_parse_n_opts(const char *data, size_t len,
tg3json_value *out_value,
const char **out_error_pos) {
tg3json__parser parser;
if (!data || !out_value) {
if (out_error_pos) *out_error_pos = data;
return 0;
}
int ok;
if (out_error_pos) *out_error_pos = NULL;
if (!out_value) return 0;
tg3json__init_value(out_value);
if (!data) return 0;
parser.cur = data;
parser.end = data + len;
parser.error = NULL;
parser.depth_limit = (options && options->depth_limit) ? options->depth_limit : 512;
parser.depth_limit = options ? (options->depth_limit ? options->depth_limit : 256) : 256;
parser.memory_budget = options ? options->memory_budget : 0;
parser.max_single_alloc = options ? options->max_single_alloc : 0;
parser.max_string_length = options ? options->max_string_length : 0;
parser.allocated = 0;
parser.parse_float32 = options ? options->parse_float32 : 0;
if (!tg3json__parse_value(&parser, 0, out_value)) {
if (out_error_pos) *out_error_pos = parser.error;
parser.cur = tg3json__skip_ws(parser.cur, parser.end);
if (parser.cur >= parser.end) {
if (out_error_pos) *out_error_pos = data;
return 0;
}
ok = tg3json__parse_value(&parser, 0, out_value);
if (!ok) {
if (out_error_pos) *out_error_pos = parser.error ? parser.error : parser.cur;
tg3json_value_free(out_value);
return 0;
}
parser.cur = tg3json__skip_ws(parser.cur, parser.end);
if (parser.cur != parser.end) {
if (out_error_pos) *out_error_pos = parser.cur;
tg3json_value_free(out_value);
return 0;
}
if (out_error_pos) *out_error_pos = NULL;
return 1;
}
int tg3json_parse_n(const char *data, size_t len, size_t depth_limit,
tg3json_value *out_value, const char **out_error_pos) {
tg3json_parse_options options;
memset(&options, 0, sizeof(options));
TINYGLTF_JSON_MEMSET(&options, 0, sizeof(options));
options.depth_limit = depth_limit;
return tg3json_parse_n_opts(data, len, &options, out_value, out_error_pos);
}
@@ -748,23 +1180,23 @@ void tg3json_value_free(tg3json_value *value) {
if (!value) return;
switch (value->type) {
case TG3JSON_STRING:
free(value->u.string.ptr);
TINYGLTF_JSON_FREE(value->u.string.ptr);
break;
case TG3JSON_ARRAY:
for (i = 0; i < value->u.array.count; ++i) {
tg3json_value_free(&value->u.array.items[i]);
}
free(value->u.array.items);
TINYGLTF_JSON_FREE(value->u.array.items);
break;
case TG3JSON_OBJECT:
for (i = 0; i < value->u.object.count; ++i) {
free(value->u.object.items[i].key);
TINYGLTF_JSON_FREE(value->u.object.items[i].key);
if (value->u.object.items[i].value) {
tg3json_value_free(value->u.object.items[i].value);
free(value->u.object.items[i].value);
TINYGLTF_JSON_FREE(value->u.object.items[i].value);
}
}
free(value->u.object.items);
TINYGLTF_JSON_FREE(value->u.object.items);
break;
case TG3JSON_NULL:
case TG3JSON_BOOL:
@@ -811,7 +1243,7 @@ int tg3json_value_init_string_n(tg3json_value *value, const char *str, size_t le
}
int tg3json_value_init_string(tg3json_value *value, const char *str) {
return tg3json_value_init_string_n(value, str, str ? strlen(str) : 0);
return tg3json_value_init_string_n(value, str, str ? TINYGLTF_JSON_STRLEN(str) : 0);
}
void tg3json_value_init_array(tg3json_value *value) {
@@ -872,7 +1304,7 @@ const tg3json_value *tg3json_object_get_n(const tg3json_value *object,
if (!object || object->type != TG3JSON_OBJECT) return NULL;
for (i = 0; i < object->u.object.count; ++i) {
const tg3json_object_entry *entry = &object->u.object.items[i];
if (entry->key_len == key_len && memcmp(entry->key, key, key_len) == 0) {
if (entry->key_len == key_len && TINYGLTF_JSON_MEMCMP(entry->key, key, key_len) == 0) {
return entry->value;
}
}
@@ -882,7 +1314,7 @@ const tg3json_value *tg3json_object_get_n(const tg3json_value *object,
const tg3json_value *tg3json_object_get(const tg3json_value *object,
const char *key) {
if (!key) return NULL;
return tg3json_object_get_n(object, key, strlen(key));
return tg3json_object_get_n(object, key, TINYGLTF_JSON_STRLEN(key));
}
tg3json_value *tg3json_object_get_mut_n(tg3json_value *object,
@@ -891,7 +1323,7 @@ tg3json_value *tg3json_object_get_mut_n(tg3json_value *object,
if (!object || object->type != TG3JSON_OBJECT) return NULL;
for (i = 0; i < object->u.object.count; ++i) {
tg3json_object_entry *entry = &object->u.object.items[i];
if (entry->key_len == key_len && memcmp(entry->key, key, key_len) == 0) {
if (entry->key_len == key_len && TINYGLTF_JSON_MEMCMP(entry->key, key, key_len) == 0) {
return entry->value;
}
}
@@ -900,7 +1332,7 @@ tg3json_value *tg3json_object_get_mut_n(tg3json_value *object,
tg3json_value *tg3json_object_get_mut(tg3json_value *object, const char *key) {
if (!key) return NULL;
return tg3json_object_get_mut_n(object, key, strlen(key));
return tg3json_object_get_mut_n(object, key, TINYGLTF_JSON_STRLEN(key));
}
const tg3json_object_entry *tg3json_object_at(const tg3json_value *object,
@@ -938,9 +1370,9 @@ int tg3json_object_set_take_n(tg3json_value *object, const char *key, size_t key
entry->key = tg3json__strndup_local(key, key_len);
if (!entry->key) return 0;
entry->key_len = key_len;
entry->value = (tg3json_value *)malloc(sizeof(tg3json_value));
entry->value = (tg3json_value *)TINYGLTF_JSON_MALLOC(sizeof(tg3json_value));
if (!entry->value) {
free(entry->key);
TINYGLTF_JSON_FREE(entry->key);
entry->key = NULL;
entry->key_len = 0;
return 0;
@@ -954,7 +1386,7 @@ int tg3json_object_set_take_n(tg3json_value *object, const char *key, size_t key
int tg3json_object_set_take(tg3json_value *object, const char *key,
tg3json_value *value) {
if (!key) return 0;
return tg3json_object_set_take_n(object, key, strlen(key), value);
return tg3json_object_set_take_n(object, key, TINYGLTF_JSON_STRLEN(key), value);
}
int tg3json_object_set_copy_n(tg3json_value *object, const char *key, size_t key_len,
@@ -972,7 +1404,7 @@ int tg3json_object_set_copy_n(tg3json_value *object, const char *key, size_t key
int tg3json_object_set_copy(tg3json_value *object, const char *key,
const tg3json_value *value) {
if (!key) return 0;
return tg3json_object_set_copy_n(object, key, strlen(key), value);
return tg3json_object_set_copy_n(object, key, TINYGLTF_JSON_STRLEN(key), value);
}
const tg3json_value *tg3json_array_get(const tg3json_value *array, size_t index) {
@@ -1032,26 +1464,19 @@ static int tg3json__stringify_value_ex(tg3json__buffer *buf, const tg3json_value
return value->u.boolean ? tg3json__buf_append(buf, "true", 4)
: tg3json__buf_append(buf, "false", 5);
case TG3JSON_INT:
snprintf(numbuf, sizeof(numbuf), "%lld", (long long)value->u.integer);
return tg3json__buf_append(buf, numbuf, strlen(numbuf));
tg3json__itoa(value->u.integer, numbuf);
return tg3json__buf_append(buf, numbuf, TINYGLTF_JSON_STRLEN(numbuf));
case TG3JSON_REAL:
snprintf(numbuf, sizeof(numbuf), "%.17g", value->u.real);
{
char *end = tg3json__dtoa_c(value->u.real, numbuf);
*end = '\0';
const char *b = numbuf;
if (*b == '-') ++b;
if (*b == 'n' || *b == 'N' || *b == 'i' || *b == 'I') {
return tg3json__buf_append(buf, "null", 4);
}
}
if (!strchr(numbuf, '.') && !strchr(numbuf, 'e') && !strchr(numbuf, 'E')) {
size_t nlen = strlen(numbuf);
if (nlen + 2 < sizeof(numbuf)) {
numbuf[nlen] = '.';
numbuf[nlen + 1] = '0';
numbuf[nlen + 2] = '\0';
}
}
return tg3json__buf_append(buf, numbuf, strlen(numbuf));
return tg3json__buf_append(buf, numbuf, TINYGLTF_JSON_STRLEN(numbuf));
case TG3JSON_STRING:
if (!tg3json__buf_putc(buf, '"')) return 0;
for (i = 0; i < value->u.string.len; ++i) {
@@ -1066,7 +1491,13 @@ static int tg3json__stringify_value_ex(tg3json__buffer *buf, const tg3json_value
case '\t': if (!tg3json__buf_append(buf, "\\t", 2)) return 0; break;
default:
if (c < 0x20u) {
snprintf(numbuf, sizeof(numbuf), "\\u%04x", (unsigned int)c);
numbuf[0] = '\\';
numbuf[1] = 'u';
numbuf[2] = '0';
numbuf[3] = '0';
numbuf[4] = "0123456789abcdef"[(c >> 4) & 0xf];
numbuf[5] = "0123456789abcdef"[c & 0xf];
numbuf[6] = '\0';
if (!tg3json__buf_append(buf, numbuf, 6)) return 0;
} else {
if (!tg3json__buf_putc(buf, (char)c)) return 0;
@@ -1107,14 +1538,14 @@ static int tg3json__stringify_value_ex(tg3json__buffer *buf, const tg3json_value
char *tg3json_stringify(const tg3json_value *value, size_t *out_len) {
tg3json__buffer buf;
memset(&buf, 0, sizeof(buf));
TINYGLTF_JSON_MEMSET(&buf, 0, sizeof(buf));
if (!value || !tg3json__stringify_value_ex(&buf, value, -1, 0)) {
free(buf.data);
TINYGLTF_JSON_FREE(buf.data);
if (out_len) *out_len = 0;
return NULL;
}
if (!buf.data) {
buf.data = (char *)malloc(1);
buf.data = (char *)TINYGLTF_JSON_MALLOC(1);
if (!buf.data) {
if (out_len) *out_len = 0;
return NULL;
@@ -1127,14 +1558,14 @@ char *tg3json_stringify(const tg3json_value *value, size_t *out_len) {
char *tg3json_stringify_pretty(const tg3json_value *value, int indent, size_t *out_len) {
tg3json__buffer buf;
memset(&buf, 0, sizeof(buf));
TINYGLTF_JSON_MEMSET(&buf, 0, sizeof(buf));
if (!value || !tg3json__stringify_value_ex(&buf, value, indent, 0)) {
free(buf.data);
TINYGLTF_JSON_FREE(buf.data);
if (out_len) *out_len = 0;
return NULL;
}
if (!buf.data) {
buf.data = (char *)malloc(1);
buf.data = (char *)TINYGLTF_JSON_MALLOC(1);
if (!buf.data) {
if (out_len) *out_len = 0;
return NULL;