mirror of
https://github.com/syoyo/tinygltf.git
synced 2026-06-08 03:03:50 +00:00
Complete freestanding v3 C JSON conversion tests
This commit is contained in:
@@ -79,7 +79,49 @@ if (TINYGLTF_BUILD_TESTS)
|
|||||||
C_STANDARD_REQUIRED ON
|
C_STANDARD_REQUIRED ON
|
||||||
C_EXTENSIONS OFF
|
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_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)
|
endif (TINYGLTF_BUILD_TESTS)
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Use this for strict compilation check(will work on clang 3.8+)
|
# 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
|
#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++ -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_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
|
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
|
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
|
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
|
||||||
|
|||||||
@@ -228,6 +228,16 @@ static int mem_contains(const uint8_t *data, uint64_t size, const char *needle)
|
|||||||
return 0;
|
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 int check_minimal_parse(void) {
|
||||||
static const uint8_t json[] =
|
static const uint8_t json[] =
|
||||||
"{\"asset\":{\"version\":\"2.0\"},"
|
"{\"asset\":{\"version\":\"2.0\"},"
|
||||||
@@ -339,6 +349,529 @@ static int check_minimal_write_roundtrip(void) {
|
|||||||
return 1;
|
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 = ∩
|
||||||
|
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 = ∩
|
||||||
|
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 = ∩
|
||||||
|
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) {
|
static int check_parse_file_failure_initializes_model(void) {
|
||||||
tg3_model model;
|
tg3_model model;
|
||||||
tg3_error_stack errors;
|
tg3_error_stack errors;
|
||||||
@@ -928,6 +1461,36 @@ int main(int argc, char **argv) {
|
|||||||
if (!check_minimal_write_roundtrip()) {
|
if (!check_minimal_write_roundtrip()) {
|
||||||
return 1;
|
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()) {
|
if (!check_parse_file_failure_initializes_model()) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
75
tests/tester_v3_freestanding.c
Normal file
75
tests/tester_v3_freestanding.c
Normal 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
165
tests/tester_v3_json_c.c
Normal 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;
|
||||||
|
}
|
||||||
365
tiny_gltf_v3.c
365
tiny_gltf_v3.c
@@ -1,14 +1,136 @@
|
|||||||
#ifndef TINYGLTF3_SOURCE_INCLUDED_FROM_HEADER
|
#ifndef TINYGLTF3_SOURCE_INCLUDED_FROM_HEADER
|
||||||
#include "tiny_gltf_v3.h"
|
#include "tiny_gltf_v3.h"
|
||||||
#endif
|
#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
|
#define TINYGLTF_JSON_C_IMPLEMENTATION
|
||||||
#include "tinygltf_json_c.h"
|
#include "tinygltf_json_c.h"
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#ifndef TINYGLTF3_NO_STDLIB
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.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_DEFAULT_BLOCK_SIZE (256u * 1024u)
|
||||||
#define TG3__ARENA_ALIGNMENT 8u
|
#define TG3__ARENA_ALIGNMENT 8u
|
||||||
@@ -52,20 +174,23 @@ struct tg3_writer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void *tg3__default_alloc(size_t size, void *ud) {
|
static void *tg3__default_alloc(size_t size, void *ud) {
|
||||||
|
(void)size;
|
||||||
(void)ud;
|
(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) {
|
static void *tg3__default_realloc(void *ptr, size_t old_size, size_t new_size, void *ud) {
|
||||||
|
(void)ptr;
|
||||||
(void)old_size;
|
(void)old_size;
|
||||||
|
(void)new_size;
|
||||||
(void)ud;
|
(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) {
|
static void tg3__default_free(void *ptr, size_t size, void *ud) {
|
||||||
(void)size;
|
(void)size;
|
||||||
(void)ud;
|
(void)ud;
|
||||||
free(ptr);
|
TINYGLTF3_FREE(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static tg3_arena *tg3__arena_create(const tg3_memory_config *config) {
|
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);
|
arena = (tg3_arena *)alloc.alloc(sizeof(tg3_arena), alloc.user_data);
|
||||||
if (!arena) return NULL;
|
if (!arena) return NULL;
|
||||||
memset(arena, 0, sizeof(*arena));
|
TINYGLTF3_MEMSET(arena, 0, sizeof(*arena));
|
||||||
arena->alloc = alloc;
|
arena->alloc = alloc;
|
||||||
arena->block_size = (config && config->arena_block_size > 0)
|
arena->block_size = (config && config->arena_block_size > 0)
|
||||||
? (size_t)config->arena_block_size
|
? (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;
|
if (!s) return NULL;
|
||||||
dst = (char *)tg3__arena_alloc(arena, len + 1);
|
dst = (char *)tg3__arena_alloc(arena, len + 1);
|
||||||
if (!dst) return NULL;
|
if (!dst) return NULL;
|
||||||
if (len > 0) memcpy(dst, s, len);
|
if (len > 0) TINYGLTF3_MEMCPY(dst, s, len);
|
||||||
dst[len] = '\0';
|
dst[len] = '\0';
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
@@ -175,7 +300,7 @@ static void tg3__error_push(tg3_error_stack *es, tg3_severity sev,
|
|||||||
if (!es) return;
|
if (!es) return;
|
||||||
if (es->count >= es->capacity) {
|
if (es->count >= es->capacity) {
|
||||||
new_cap = es->capacity ? es->capacity * 2u : 16u;
|
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;
|
if (!new_entries) return;
|
||||||
es->entries = new_entries;
|
es->entries = new_entries;
|
||||||
es->capacity = new_cap;
|
es->capacity = new_cap;
|
||||||
@@ -198,7 +323,7 @@ static void tg3__error_pushf(tg3_error_stack *es, tg3_arena *arena,
|
|||||||
const char *msg = buf;
|
const char *msg = buf;
|
||||||
if (!es) return;
|
if (!es) return;
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
n = vsnprintf(buf, sizeof(buf), fmt, ap);
|
n = tg3__vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
if (n < 0) n = 0;
|
if (n < 0) n = 0;
|
||||||
if ((size_t)n >= sizeof(buf)) n = (int)(sizeof(buf) - 1u);
|
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;
|
if (!es || index >= es->count) return NULL;
|
||||||
return &es->entries[index];
|
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) {
|
TINYGLTF3_API void tg3_error_stack_free(tg3_error_stack *es) {
|
||||||
if (!es) return;
|
if (!es) return;
|
||||||
free(es->entries);
|
TINYGLTF3_FREE(es->entries);
|
||||||
memset(es, 0, sizeof(*es));
|
TINYGLTF3_MEMSET(es, 0, sizeof(*es));
|
||||||
}
|
}
|
||||||
|
|
||||||
TINYGLTF3_API void tg3_parse_options_init(tg3_parse_options *options) {
|
TINYGLTF3_API void tg3_parse_options_init(tg3_parse_options *options) {
|
||||||
if (!options) return;
|
if (!options) return;
|
||||||
memset(options, 0, sizeof(*options));
|
TINYGLTF3_MEMSET(options, 0, sizeof(*options));
|
||||||
options->required_sections = TG3_REQUIRE_VERSION;
|
options->required_sections = TG3_REQUIRE_VERSION;
|
||||||
options->strictness = TG3_PERMISSIVE;
|
options->strictness = TG3_PERMISSIVE;
|
||||||
options->memory.memory_budget = TINYGLTF3_MAX_MEMORY_BYTES;
|
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) {
|
TINYGLTF3_API void tg3_write_options_init(tg3_write_options *options) {
|
||||||
if (!options) return;
|
if (!options) return;
|
||||||
memset(options, 0, sizeof(*options));
|
TINYGLTF3_MEMSET(options, 0, sizeof(*options));
|
||||||
options->pretty_print = 1;
|
options->pretty_print = 1;
|
||||||
options->memory.memory_budget = TINYGLTF3_MAX_MEMORY_BYTES;
|
options->memory.memory_budget = TINYGLTF3_MAX_MEMORY_BYTES;
|
||||||
options->memory.arena_block_size = TG3__ARENA_DEFAULT_BLOCK_SIZE;
|
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) {
|
TINYGLTF3_API int32_t tg3_str_equals(tg3_str a, tg3_str b) {
|
||||||
if (a.len != b.len) return 0;
|
if (a.len != b.len) return 0;
|
||||||
if (a.len == 0) return 1;
|
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) {
|
TINYGLTF3_API int32_t tg3_str_equals_cstr(tg3_str a, const char *b) {
|
||||||
uint32_t blen;
|
uint32_t blen;
|
||||||
if (!b) return a.len == 0 ? 1 : 0;
|
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 != blen) return 0;
|
||||||
if (a.len == 0) return 1;
|
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) {
|
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) {
|
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 {
|
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 *end;
|
||||||
const char *semi;
|
const char *semi;
|
||||||
size_t mime_len;
|
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;
|
p = uri + 5;
|
||||||
end = uri + uri_len;
|
end = uri + uri_len;
|
||||||
semi = p;
|
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;
|
if (semi >= end) return 0;
|
||||||
mime_len = (size_t)(semi - p);
|
mime_len = (size_t)(semi - p);
|
||||||
if (mime_len >= sizeof(result->mime_type)) mime_len = sizeof(result->mime_type) - 1u;
|
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';
|
result->mime_type[mime_len] = '\0';
|
||||||
p = semi + 1;
|
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;
|
p += 7;
|
||||||
result->data_start = p;
|
result->data_start = p;
|
||||||
result->data_len = (size_t)(end - 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;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (out_mime && out_mime_cap > 0) {
|
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;
|
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';
|
out_mime[mlen] = '\0';
|
||||||
}
|
}
|
||||||
return tg3__b64_decode(dr.data_start, dr.data_len, out_len, arena);
|
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) {
|
static int32_t tg3__fs_file_exists(const char *path, uint32_t path_len, void *ud) {
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
(void)path_len; (void)ud;
|
(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);
|
size = ftell(fp);
|
||||||
if (size < 0) { fclose(fp); return 0; }
|
if (size < 0) { fclose(fp); return 0; }
|
||||||
if (fseek(fp, 0, SEEK_SET) != 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; }
|
if (!data) { fclose(fp); return 0; }
|
||||||
nread = fread(data, 1, (size_t)size, fp);
|
nread = fread(data, 1, (size_t)size, fp);
|
||||||
fclose(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_data = data;
|
||||||
*out_size = (uint64_t)size;
|
*out_size = (uint64_t)size;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tg3__fs_free_file(uint8_t *data, uint64_t size, void *ud) {
|
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,
|
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
|
#endif
|
||||||
|
|
||||||
static void tg3__model_init(tg3_model *model) {
|
static void tg3__model_init(tg3_model *model) {
|
||||||
memset(model, 0, sizeof(*model));
|
TINYGLTF3_MEMSET(model, 0, sizeof(*model));
|
||||||
model->default_scene = -1;
|
model->default_scene = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,7 +587,7 @@ static int tg3__json_number_to_int32(const tg3json_value *v, int32_t *out) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
real = v->u.real;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
converted = (int32_t)real;
|
converted = (int32_t)real;
|
||||||
@@ -485,7 +610,7 @@ static int tg3__json_number_to_uint64(const tg3json_value *v, uint64_t *out) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
real = v->u.real;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
converted = (uint64_t)real;
|
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) {
|
static tg3_value tg3__json_to_value(tg3__parse_ctx *ctx, const tg3json_value *j) {
|
||||||
tg3_value v;
|
tg3_value v;
|
||||||
size_t i;
|
size_t i;
|
||||||
memset(&v, 0, sizeof(v));
|
TINYGLTF3_MEMSET(&v, 0, sizeof(v));
|
||||||
if (!j) return v;
|
if (!j) return v;
|
||||||
switch (j->type) {
|
switch (j->type) {
|
||||||
case TG3JSON_NULL:
|
case TG3JSON_NULL:
|
||||||
@@ -839,7 +964,7 @@ static void tg3__parse_extras_and_extensions(tg3__parse_ctx *ctx, const tg3json_
|
|||||||
tg3_extras_ext *ee) {
|
tg3_extras_ext *ee) {
|
||||||
const tg3json_value *extras_it;
|
const tg3json_value *extras_it;
|
||||||
const tg3json_value *ext_it;
|
const tg3json_value *ext_it;
|
||||||
memset(ee, 0, sizeof(*ee));
|
TINYGLTF3_MEMSET(ee, 0, sizeof(*ee));
|
||||||
extras_it = tg3__json_get(o, "extras");
|
extras_it = tg3__json_get(o, "extras");
|
||||||
if (extras_it) {
|
if (extras_it) {
|
||||||
if (!ctx->opts.skip_extras_values) {
|
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);
|
char *raw = tg3json_stringify(extras_it, &raw_len);
|
||||||
if (raw) {
|
if (raw) {
|
||||||
ee->extras_json = tg3__arena_str(ctx->arena, raw, (uint32_t)raw_len);
|
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) {
|
if (exts) {
|
||||||
for (i = 0; i < count; ++i) {
|
for (i = 0; i < count; ++i) {
|
||||||
const tg3json_object_entry *entry = tg3json_object_at(ext_it, 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);
|
exts[i].name = tg3__arena_str(ctx->arena, entry->key, (uint32_t)entry->key_len);
|
||||||
if (!ctx->opts.skip_extras_values) {
|
if (!ctx->opts.skip_extras_values) {
|
||||||
exts[i].value = tg3__json_to_value(ctx, entry->value);
|
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);
|
char *raw = tg3json_stringify(ext_it, &raw_len);
|
||||||
if (raw) {
|
if (raw) {
|
||||||
ee->extensions_json = tg3__arena_str(ctx->arena, raw, (uint32_t)raw_len);
|
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_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) { memset(ti, 0, sizeof(*ti)); ti->index = -1; ti->scale = 1.0; }
|
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) { memset(ti, 0, sizeof(*ti)); ti->index = -1; ti->strength = 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) {
|
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[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->base_color_factor[2] = 1.0; pbr->base_color_factor[3] = 1.0;
|
||||||
pbr->metallic_factor = 1.0; pbr->roughness_factor = 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);
|
tg3__init_texture_info(&pbr->metallic_roughness_texture);
|
||||||
}
|
}
|
||||||
static void tg3__init_node(tg3_node *n) {
|
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->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->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;
|
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) {
|
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, "version", &asset->version, 0, "/asset");
|
||||||
tg3__parse_string(ctx, o, "generator", &asset->generator, 0, "/asset");
|
tg3__parse_string(ctx, o, "generator", &asset->generator, 0, "/asset");
|
||||||
tg3__parse_string(ctx, o, "minVersion", &asset->min_version, 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) {
|
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 == 6 && TINYGLTF3_MEMCMP(s, "SCALAR", 6) == 0) return TG3_TYPE_SCALAR;
|
||||||
if (len == 4 && memcmp(s, "VEC2", 4) == 0) return TG3_TYPE_VEC2;
|
if (len == 4 && TINYGLTF3_MEMCMP(s, "VEC2", 4) == 0) return TG3_TYPE_VEC2;
|
||||||
if (len == 4 && memcmp(s, "VEC3", 4) == 0) return TG3_TYPE_VEC3;
|
if (len == 4 && TINYGLTF3_MEMCMP(s, "VEC3", 4) == 0) return TG3_TYPE_VEC3;
|
||||||
if (len == 4 && memcmp(s, "VEC4", 4) == 0) return TG3_TYPE_VEC4;
|
if (len == 4 && TINYGLTF3_MEMCMP(s, "VEC4", 4) == 0) return TG3_TYPE_VEC4;
|
||||||
if (len == 4 && memcmp(s, "MAT2", 4) == 0) return TG3_TYPE_MAT2;
|
if (len == 4 && TINYGLTF3_MEMCMP(s, "MAT2", 4) == 0) return TG3_TYPE_MAT2;
|
||||||
if (len == 4 && memcmp(s, "MAT3", 4) == 0) return TG3_TYPE_MAT3;
|
if (len == 4 && TINYGLTF3_MEMCMP(s, "MAT3", 4) == 0) return TG3_TYPE_MAT3;
|
||||||
if (len == 4 && memcmp(s, "MAT4", 4) == 0) return TG3_TYPE_MAT4;
|
if (len == 4 && TINYGLTF3_MEMCMP(s, "MAT4", 4) == 0) return TG3_TYPE_MAT4;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tg3__parse_accessor_sparse(tg3__parse_ctx *ctx, const tg3json_value *o,
|
static int tg3__parse_accessor_sparse(tg3__parse_ctx *ctx, const tg3json_value *o,
|
||||||
tg3_accessor_sparse *sparse) {
|
tg3_accessor_sparse *sparse) {
|
||||||
const tg3json_value *it = tg3__json_get(o, "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->indices.buffer_view = -1;
|
||||||
sparse->values.buffer_view = -1;
|
sparse->values.buffer_view = -1;
|
||||||
if (!it) return 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;
|
tg3_str type_str;
|
||||||
uint64_t bo = 0;
|
uint64_t bo = 0;
|
||||||
uint64_t cnt = 0;
|
uint64_t cnt = 0;
|
||||||
memset(acc, 0, sizeof(*acc));
|
TINYGLTF3_MEMSET(acc, 0, sizeof(*acc));
|
||||||
acc->buffer_view = -1;
|
acc->buffer_view = -1;
|
||||||
acc->component_type = -1;
|
acc->component_type = -1;
|
||||||
acc->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;
|
uint32_t i;
|
||||||
if (!uri || uri_len == 0) return 0;
|
if (!uri || uri_len == 0) return 0;
|
||||||
/* No NUL bytes — fopen would truncate; protects against smuggling. */
|
/* 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). */
|
/* Reject absolute paths (POSIX and Windows). */
|
||||||
if (uri[0] == '/' || uri[0] == '\\') return 0;
|
if (uri[0] == '/' || uri[0] == '\\') return 0;
|
||||||
/* Reject Windows drive prefixes like "C:". */
|
/* 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 (uri_len >= sizeof(path_buf)) return 0;
|
||||||
if (ctx->base_dir_len > 0) {
|
if (ctx->base_dir_len > 0) {
|
||||||
if (ctx->base_dir_len >= sizeof(path_buf) - uri_len - 1u) return 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;
|
path_len = ctx->base_dir_len;
|
||||||
if (path_len > 0 && path_buf[path_len - 1u] != '/' && path_buf[path_len - 1u] != '\\') {
|
if (path_len > 0 && path_buf[path_len - 1u] != '/' && path_buf[path_len - 1u] != '\\') {
|
||||||
path_buf[path_len++] = '/';
|
path_buf[path_len++] = '/';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (path_len + uri_len >= sizeof(path_buf)) return 0;
|
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_len += uri_len;
|
||||||
path_buf[path_len] = '\0';
|
path_buf[path_len] = '\0';
|
||||||
ok = ctx->opts.fs.read_file(out_data, out_size, path_buf, path_len, ctx->opts.fs.user_data);
|
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,
|
static int tg3__parse_buffer(tg3__parse_ctx *ctx, const tg3json_value *o,
|
||||||
tg3_buffer *buf, int32_t buf_idx) {
|
tg3_buffer *buf, int32_t buf_idx) {
|
||||||
uint64_t byte_length = 0;
|
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, "name", &buf->name, 0, "/buffer");
|
||||||
tg3__parse_string(ctx, o, "uri", &buf->uri, 0, "/buffer");
|
tg3__parse_string(ctx, o, "uri", &buf->uri, 0, "/buffer");
|
||||||
tg3__parse_uint64(ctx, o, "byteLength", &byte_length, 1, "/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);
|
"OOM for buffer data", NULL, -1);
|
||||||
return 0;
|
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.data = data;
|
||||||
buf->data.count = byte_length;
|
buf->data.count = byte_length;
|
||||||
} else if (buf->uri.len > 0) {
|
} 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;
|
size_t decoded_len = 0;
|
||||||
char mime[64];
|
char mime[64];
|
||||||
uint8_t *decoded;
|
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));
|
decoded = tg3__decode_data_uri(ctx->arena, buf->uri.data, buf->uri.len, &decoded_len, mime, sizeof(mime));
|
||||||
if (!decoded && byte_length > 0) {
|
if (!decoded && byte_length > 0) {
|
||||||
tg3__error_push(ctx->errors, TG3_SEVERITY_ERROR, TG3_ERR_DATA_URI_DECODE,
|
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);
|
data = (uint8_t *)tg3__arena_alloc(ctx->arena, (size_t)file_size);
|
||||||
if (data) {
|
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.data = data;
|
||||||
buf->data.count = file_size;
|
buf->data.count = file_size;
|
||||||
} else if (file_size > 0) {
|
} 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) {
|
static int tg3__parse_buffer_view(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_buffer_view *bv) {
|
||||||
uint64_t val = 0;
|
uint64_t val = 0;
|
||||||
int32_t stride = 0;
|
int32_t stride = 0;
|
||||||
memset(bv, 0, sizeof(*bv));
|
TINYGLTF3_MEMSET(bv, 0, sizeof(*bv));
|
||||||
bv->buffer = -1;
|
bv->buffer = -1;
|
||||||
tg3__parse_string(ctx, o, "name", &bv->name, 0, "/bufferView");
|
tg3__parse_string(ctx, o, "name", &bv->name, 0, "/bufferView");
|
||||||
tg3__parse_int(ctx, o, "buffer", &bv->buffer, 1, "/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) {
|
static int tg3__parse_image(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_image *img, int32_t img_idx) {
|
||||||
(void)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;
|
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, "name", &img->name, 0, "/image");
|
||||||
tg3__parse_string(ctx, o, "uri", &img->uri, 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) {
|
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->min_filter = -1; samp->mag_filter = -1;
|
||||||
samp->wrap_s = TG3_TEXTURE_WRAP_REPEAT; samp->wrap_t = TG3_TEXTURE_WRAP_REPEAT;
|
samp->wrap_s = TG3_TEXTURE_WRAP_REPEAT; samp->wrap_t = TG3_TEXTURE_WRAP_REPEAT;
|
||||||
tg3__parse_string(ctx, o, "name", &samp->name, 0, "/sampler");
|
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) {
|
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;
|
tex->sampler = -1; tex->source = -1;
|
||||||
tg3__parse_string(ctx, o, "name", &tex->name, 0, "/texture");
|
tg3__parse_string(ctx, o, "name", &tex->name, 0, "/texture");
|
||||||
tg3__parse_int(ctx, o, "sampler", &tex->sampler, 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) {
|
static int tg3__parse_material(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_material *mat) {
|
||||||
const tg3json_value *pbr_it;
|
const tg3json_value *pbr_it;
|
||||||
const tg3json_value *ext_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_pbr(&mat->pbr_metallic_roughness);
|
||||||
tg3__init_normal_texture_info(&mat->normal_texture);
|
tg3__init_normal_texture_info(&mat->normal_texture);
|
||||||
tg3__init_occlusion_texture_info(&mat->occlusion_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) {
|
static int tg3__parse_primitive(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_primitive *prim) {
|
||||||
const tg3json_value *attr_it;
|
const tg3json_value *attr_it;
|
||||||
const tg3json_value *targets_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;
|
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, "material", &prim->material, 0, "/primitive");
|
||||||
tg3__parse_int(ctx, o, "indices", &prim->indices, 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) {
|
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");
|
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");
|
tg3__parse_string(ctx, o, "name", &mesh->name, 0, "/mesh");
|
||||||
if (tg3__json_is_array(prim_it)) {
|
if (tg3__json_is_array(prim_it)) {
|
||||||
size_t count = tg3json_array_size(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) {
|
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;
|
skin->inverse_bind_matrices = -1; skin->skeleton = -1;
|
||||||
tg3__parse_string(ctx, o, "name", &skin->name, 0, "/skin");
|
tg3__parse_string(ctx, o, "name", &skin->name, 0, "/skin");
|
||||||
tg3__parse_int(ctx, o, "inverseBindMatrices", &skin->inverse_bind_matrices, 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) {
|
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 *ch_it = tg3__json_get(o, "channels");
|
||||||
const tg3json_value *samp_it = tg3__json_get(o, "samplers");
|
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");
|
tg3__parse_string(ctx, o, "name", &anim->name, 0, "/animation");
|
||||||
if (tg3__json_is_array(ch_it)) {
|
if (tg3__json_is_array(ch_it)) {
|
||||||
size_t count = tg3json_array_size(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) {
|
for (i = 0; i < count; ++i) {
|
||||||
const tg3json_value *item = tg3json_array_get(ch_it, i);
|
const tg3json_value *item = tg3json_array_get(ch_it, i);
|
||||||
const tg3json_value *tgt_it;
|
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].sampler = -1;
|
||||||
channels[i].target.node = -1;
|
channels[i].target.node = -1;
|
||||||
tg3__parse_int(ctx, item, "sampler", &channels[i].sampler, 1, "/animation/channel");
|
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) {
|
for (i = 0; i < count; ++i) {
|
||||||
const tg3json_value *item = tg3json_array_get(samp_it, i);
|
const tg3json_value *item = tg3json_array_get(samp_it, i);
|
||||||
tg3_str interp;
|
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;
|
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, "input", &samplers[i].input, 1, "/animation/sampler");
|
||||||
tg3__parse_int(ctx, item, "output", &samplers[i].output, 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) {
|
static int tg3__parse_camera(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_camera *cam) {
|
||||||
const tg3json_value *it;
|
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, "name", &cam->name, 0, "/camera");
|
||||||
tg3__parse_string(ctx, o, "type", &cam->type, 1, "/camera");
|
tg3__parse_string(ctx, o, "type", &cam->type, 1, "/camera");
|
||||||
if (cam->type.data && tg3_str_equals_cstr(cam->type, "perspective")) {
|
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) {
|
static int tg3__parse_scene(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_scene *scene) {
|
||||||
const tg3json_value *ext_it;
|
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_string(ctx, o, "name", &scene->name, 0, "/scene");
|
||||||
tg3__parse_int_array(ctx, o, "nodes", &scene->nodes, &scene->nodes_count, 0, "/scene");
|
tg3__parse_int_array(ctx, o, "nodes", &scene->nodes, &scene->nodes_count, 0, "/scene");
|
||||||
ext_it = tg3__json_get(o, "extensions");
|
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) {
|
static int tg3__parse_light(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_light *light) {
|
||||||
const tg3json_value *spot_it;
|
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->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;
|
light->intensity = 1.0; light->spot.outer_cone_angle = 0.7853981634;
|
||||||
tg3__parse_string(ctx, o, "name", &light->name, 0, "/light");
|
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) {
|
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;
|
src->buffer_view = -1;
|
||||||
tg3__parse_string(ctx, o, "name", &src->name, 0, "/audioSource");
|
tg3__parse_string(ctx, o, "name", &src->name, 0, "/audioSource");
|
||||||
tg3__parse_string(ctx, o, "uri", &src->uri, 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) {
|
static int tg3__parse_audio_emitter(tg3__parse_ctx *ctx, const tg3json_value *o, tg3_audio_emitter *emitter) {
|
||||||
const tg3json_value *pos_it;
|
const tg3json_value *pos_it;
|
||||||
memset(emitter, 0, sizeof(*emitter));
|
TINYGLTF3_MEMSET(emitter, 0, sizeof(*emitter));
|
||||||
emitter->gain = 1.0; emitter->source = -1;
|
emitter->gain = 1.0; emitter->source = -1;
|
||||||
emitter->positional.cone_inner_angle = 6.283185307179586;
|
emitter->positional.cone_inner_angle = 6.283185307179586;
|
||||||
emitter->positional.cone_outer_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,
|
static void tg3__json_parse_options_from_tg3(const tg3_parse_options *options,
|
||||||
tg3json_parse_options *json_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->depth_limit = TINYGLTF3_MAX_NESTING_DEPTH;
|
||||||
json_options->memory_budget = options ? (size_t)options->memory.memory_budget : 0;
|
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;
|
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);
|
"Invalid GLB magic bytes", NULL, -1);
|
||||||
return TG3_ERR_GLB_INVALID_MAGIC;
|
return TG3_ERR_GLB_INVALID_MAGIC;
|
||||||
}
|
}
|
||||||
memcpy(&version, data + 4, 4);
|
TINYGLTF3_MEMCPY(&version, data + 4, 4);
|
||||||
if (version != 2) {
|
if (version != 2) {
|
||||||
tg3__error_push(errors, TG3_SEVERITY_ERROR, TG3_ERR_GLB_INVALID_VERSION,
|
tg3__error_push(errors, TG3_SEVERITY_ERROR, TG3_ERR_GLB_INVALID_VERSION,
|
||||||
"Unsupported GLB version (expected 2)", NULL, -1);
|
"Unsupported GLB version (expected 2)", NULL, -1);
|
||||||
return TG3_ERR_GLB_INVALID_VERSION;
|
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) {
|
if ((uint64_t)total_length != size) {
|
||||||
tg3__error_push(errors, TG3_SEVERITY_ERROR, TG3_ERR_GLB_SIZE_MISMATCH,
|
tg3__error_push(errors, TG3_SEVERITY_ERROR, TG3_ERR_GLB_SIZE_MISMATCH,
|
||||||
"GLB total length does not match data size", NULL, -1);
|
"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) {
|
while (offset + 8 <= (uint64_t)total_length) {
|
||||||
uint32_t chunk_length;
|
uint32_t chunk_length;
|
||||||
uint32_t chunk_type;
|
uint32_t chunk_type;
|
||||||
memcpy(&chunk_length, data + offset, 4); offset += 4;
|
TINYGLTF3_MEMCPY(&chunk_length, data + offset, 4); offset += 4;
|
||||||
memcpy(&chunk_type, data + offset, 4); offset += 4;
|
TINYGLTF3_MEMCPY(&chunk_type, data + offset, 4); offset += 4;
|
||||||
if (offset + chunk_length > (uint64_t)total_length) {
|
if (offset + chunk_length > (uint64_t)total_length) {
|
||||||
tg3__error_push(errors, TG3_SEVERITY_ERROR, TG3_ERR_GLB_CHUNK_ERROR,
|
tg3__error_push(errors, TG3_SEVERITY_ERROR, TG3_ERR_GLB_CHUNK_ERROR,
|
||||||
"GLB chunk exceeds data size", NULL, -1);
|
"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. */
|
* the caller; tg3_model_free is the sole arena owner on the error path. */
|
||||||
return TG3_ERR_JSON_PARSE;
|
return TG3_ERR_JSON_PARSE;
|
||||||
}
|
}
|
||||||
memset(&ctx, 0, sizeof(ctx));
|
TINYGLTF3_MEMSET(&ctx, 0, sizeof(ctx));
|
||||||
ctx.arena = arena;
|
ctx.arena = arena;
|
||||||
ctx.errors = errors;
|
ctx.errors = errors;
|
||||||
ctx.opts = *options;
|
ctx.opts = *options;
|
||||||
ctx.base_dir = base_dir;
|
ctx.base_dir = base_dir;
|
||||||
ctx.base_dir_len = base_dir_len;
|
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);
|
tg3__set_default_fs(&ctx.opts.fs);
|
||||||
#endif
|
#endif
|
||||||
ret = tg3__parse_from_json(&ctx, &json_doc, model);
|
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. */
|
/* Keep arena alive so error messages stay valid; model_free owns it. */
|
||||||
return TG3_ERR_JSON_PARSE;
|
return TG3_ERR_JSON_PARSE;
|
||||||
}
|
}
|
||||||
memset(&ctx, 0, sizeof(ctx));
|
TINYGLTF3_MEMSET(&ctx, 0, sizeof(ctx));
|
||||||
ctx.arena = arena;
|
ctx.arena = arena;
|
||||||
ctx.errors = errors;
|
ctx.errors = errors;
|
||||||
ctx.opts = *options;
|
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.is_binary = 1;
|
||||||
ctx.bin_data = bin_chunk;
|
ctx.bin_data = bin_chunk;
|
||||||
ctx.bin_size = bin_chunk_size;
|
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);
|
tg3__set_default_fs(&ctx.opts.fs);
|
||||||
#endif
|
#endif
|
||||||
err = tg3__parse_from_json(&ctx, &json_doc, model);
|
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 (!filename) return TG3_ERR_FILE_NOT_FOUND;
|
||||||
if (options) opts = *options;
|
if (options) opts = *options;
|
||||||
else tg3_parse_options_init(&opts);
|
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);
|
tg3__set_default_fs(&opts.fs);
|
||||||
#endif
|
#endif
|
||||||
if (!opts.fs.read_file) {
|
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);
|
"Failed to read file", NULL, -1);
|
||||||
return TG3_ERR_FILE_NOT_FOUND;
|
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) {
|
for (i = 0; i < filename_len; ++i) {
|
||||||
if (filename[i] == '/' || filename[i] == '\\') base_dir_len = i;
|
if (filename[i] == '/' || filename[i] == '\\') base_dir_len = i;
|
||||||
}
|
}
|
||||||
if (base_dir_len > 0) {
|
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';
|
base_dir_buf[base_dir_len] = '\0';
|
||||||
}
|
}
|
||||||
result = tg3_parse_auto(model, errors, file_data, file_size, base_dir_buf, base_dir_len, &opts);
|
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) {
|
TINYGLTF3_API void tg3_model_free(tg3_model *model) {
|
||||||
if (!model) return;
|
if (!model) return;
|
||||||
if (model->arena_) tg3__arena_destroy(model->arena_);
|
if (model->arena_) tg3__arena_destroy(model->arena_);
|
||||||
memset(model, 0, sizeof(*model));
|
TINYGLTF3_MEMSET(model, 0, sizeof(*model));
|
||||||
model->default_scene = -1;
|
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[] =
|
static const char chars[] =
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
size_t enc_len = ((input_len + 2u) / 3u) * 4u;
|
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 i;
|
||||||
size_t j = 0;
|
size_t j = 0;
|
||||||
if (!out) return NULL;
|
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) {
|
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) {
|
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,
|
static int tg3__serialize_double(tg3json_value *o, const char *key, double val,
|
||||||
double default_val, int write_defaults) {
|
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);
|
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);
|
tg3json_value_free(out);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
uri = (char *)malloc(prefix_len + enc_len + 1u);
|
uri = (char *)TINYGLTF3_MALLOC(prefix_len + enc_len + 1u);
|
||||||
if (!uri) {
|
if (!uri) {
|
||||||
free(encoded);
|
TINYGLTF3_FREE(encoded);
|
||||||
tg3json_value_free(out);
|
tg3json_value_free(out);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
memcpy(uri, prefix, prefix_len);
|
TINYGLTF3_MEMCPY(uri, prefix, prefix_len);
|
||||||
memcpy(uri + prefix_len, encoded, enc_len);
|
TINYGLTF3_MEMCPY(uri + prefix_len, encoded, enc_len);
|
||||||
uri[prefix_len + enc_len] = '\0';
|
uri[prefix_len + enc_len] = '\0';
|
||||||
free(encoded);
|
TINYGLTF3_FREE(encoded);
|
||||||
if (!tg3__json_set_string_n(out, "uri", uri, prefix_len + enc_len)) {
|
if (!tg3__json_set_string_n(out, "uri", uri, prefix_len + enc_len)) {
|
||||||
free(uri);
|
TINYGLTF3_FREE(uri);
|
||||||
tg3json_value_free(out);
|
tg3json_value_free(out);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
free(uri);
|
TINYGLTF3_FREE(uri);
|
||||||
}
|
}
|
||||||
if (!tg3__serialize_extras_ext(out, &b->ext)) {
|
if (!tg3__serialize_extras_ext(out, &b->ext)) {
|
||||||
tg3json_value_free(out);
|
tg3json_value_free(out);
|
||||||
@@ -2887,9 +3012,9 @@ static int tg3__serialize_material(const tg3_material *m, int wd, tg3json_value
|
|||||||
has_pbr = 1;
|
has_pbr = 1;
|
||||||
}
|
}
|
||||||
if (!tg3__serialize_double(&pbr, "metallicFactor", p->metallic_factor, 1.0, wd)) goto fail;
|
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 (!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 (p->base_color_texture.index >= 0) {
|
||||||
if (!tg3__serialize_texture_info(&pbr, "baseColorTexture", &p->base_color_texture, wd)) goto fail;
|
if (!tg3__serialize_texture_info(&pbr, "baseColorTexture", &p->base_color_texture, wd)) goto fail;
|
||||||
has_pbr = 1;
|
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;
|
bin_padded = ((uint32_t)bin_len + 3u) & ~3u;
|
||||||
total = 12u + 8u + json_padded + ((bin_data && bin_len > 0) ? (8u + bin_padded) : 0u);
|
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) {
|
if (!glb) {
|
||||||
free(json_str);
|
TINYGLTF3_FREE(json_str);
|
||||||
tg3__error_push(errors, TG3_SEVERITY_ERROR, TG3_ERR_OUT_OF_MEMORY,
|
tg3__error_push(errors, TG3_SEVERITY_ERROR, TG3_ERR_OUT_OF_MEMORY,
|
||||||
"OOM allocating GLB output", NULL, -1);
|
"OOM allocating GLB output", NULL, -1);
|
||||||
return TG3_ERR_OUT_OF_MEMORY;
|
return TG3_ERR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
memcpy(glb, "glTF", 4);
|
TINYGLTF3_MEMCPY(glb, "glTF", 4);
|
||||||
{
|
{
|
||||||
uint32_t version = 2u;
|
uint32_t version = 2u;
|
||||||
uint32_t json_type = 0x4E4F534Au;
|
uint32_t json_type = 0x4E4F534Au;
|
||||||
memcpy(glb + 4, &version, 4);
|
TINYGLTF3_MEMCPY(glb + 4, &version, 4);
|
||||||
memcpy(glb + 8, &total, 4);
|
TINYGLTF3_MEMCPY(glb + 8, &total, 4);
|
||||||
memcpy(glb + 12, &json_padded, 4);
|
TINYGLTF3_MEMCPY(glb + 12, &json_padded, 4);
|
||||||
memcpy(glb + 16, &json_type, 4);
|
TINYGLTF3_MEMCPY(glb + 16, &json_type, 4);
|
||||||
memcpy(glb + 20, json_str, json_len);
|
TINYGLTF3_MEMCPY(glb + 20, json_str, json_len);
|
||||||
}
|
}
|
||||||
while ((uint32_t)json_len < json_padded) {
|
while ((uint32_t)json_len < json_padded) {
|
||||||
glb[20u + json_len] = ' ';
|
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_off = 20u + json_padded;
|
||||||
uint32_t bin_type = 0x004E4942u;
|
uint32_t bin_type = 0x004E4942u;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
memcpy(glb + bin_off, &bin_padded, 4);
|
TINYGLTF3_MEMCPY(glb + bin_off, &bin_padded, 4);
|
||||||
memcpy(glb + bin_off + 4u, &bin_type, 4);
|
TINYGLTF3_MEMCPY(glb + bin_off + 4u, &bin_type, 4);
|
||||||
memcpy(glb + bin_off + 8u, bin_data, (size_t)bin_len);
|
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;
|
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_data = glb;
|
||||||
*out_size = total;
|
*out_size = total;
|
||||||
return TG3_OK;
|
return TG3_OK;
|
||||||
} else {
|
} else {
|
||||||
uint8_t *data = (uint8_t *)malloc(json_len);
|
uint8_t *data = (uint8_t *)TINYGLTF3_MALLOC(json_len);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
free(json_str);
|
TINYGLTF3_FREE(json_str);
|
||||||
tg3__error_push(errors, TG3_SEVERITY_ERROR, TG3_ERR_OUT_OF_MEMORY,
|
tg3__error_push(errors, TG3_SEVERITY_ERROR, TG3_ERR_OUT_OF_MEMORY,
|
||||||
"OOM allocating JSON output", NULL, -1);
|
"OOM allocating JSON output", NULL, -1);
|
||||||
return TG3_ERR_OUT_OF_MEMORY;
|
return TG3_ERR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
memcpy(data, json_str, json_len);
|
TINYGLTF3_MEMCPY(data, json_str, json_len);
|
||||||
free(json_str);
|
TINYGLTF3_FREE(json_str);
|
||||||
*out_data = data;
|
*out_data = data;
|
||||||
*out_size = (uint64_t)json_len;
|
*out_size = (uint64_t)json_len;
|
||||||
return TG3_OK;
|
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;
|
int32_t ok;
|
||||||
if (options) opts = *options;
|
if (options) opts = *options;
|
||||||
else tg3_write_options_init(&opts);
|
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);
|
tg3__set_default_fs(&opts.fs);
|
||||||
#endif
|
#endif
|
||||||
if (!opts.fs.write_file) {
|
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);
|
err = tg3_write_to_memory(model, errors, &data, &size, &opts);
|
||||||
if (err != TG3_OK) return err;
|
if (err != TG3_OK) return err;
|
||||||
ok = opts.fs.write_file(filename, filename_len, data, size, opts.fs.user_data);
|
ok = opts.fs.write_file(filename, filename_len, data, size, opts.fs.user_data);
|
||||||
free(data);
|
TINYGLTF3_FREE(data);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
tg3__error_push(errors, TG3_SEVERITY_ERROR, TG3_ERR_FILE_WRITE,
|
tg3__error_push(errors, TG3_SEVERITY_ERROR, TG3_ERR_FILE_WRITE,
|
||||||
"Failed to write file", NULL, -1);
|
"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) {
|
TINYGLTF3_API void tg3_write_free(uint8_t *data, const tg3_write_options *options) {
|
||||||
(void)options;
|
(void)options;
|
||||||
free(data);
|
TINYGLTF3_FREE(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
TINYGLTF3_API tg3_writer *tg3_writer_create(tg3_write_chunk_fn chunk_fn, void *user_data,
|
TINYGLTF3_API tg3_writer *tg3_writer_create(tg3_write_chunk_fn chunk_fn, void *user_data,
|
||||||
const tg3_write_options *options) {
|
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;
|
if (!w) return NULL;
|
||||||
memset(w, 0, sizeof(*w));
|
TINYGLTF3_MEMSET(w, 0, sizeof(*w));
|
||||||
w->chunk_fn = chunk_fn;
|
w->chunk_fn = chunk_fn;
|
||||||
w->user_data = user_data;
|
w->user_data = user_data;
|
||||||
if (options) w->options = *options;
|
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);
|
: tg3json_stringify(&w->root, &json_len);
|
||||||
if (!json_str) return TG3_ERR_OUT_OF_MEMORY;
|
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);
|
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;
|
return ok ? TG3_OK : TG3_ERR_WRITE_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
TINYGLTF3_API void tg3_writer_destroy(tg3_writer *w) {
|
TINYGLTF3_API void tg3_writer_destroy(tg3_writer *w) {
|
||||||
if (!w) return;
|
if (!w) return;
|
||||||
tg3json_value_free(&w->root);
|
tg3json_value_free(&w->root);
|
||||||
free(w);
|
TINYGLTF3_FREE(w);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,8 +116,12 @@
|
|||||||
|
|
||||||
/* Assert override */
|
/* Assert override */
|
||||||
#ifndef TINYGLTF3_ASSERT
|
#ifndef TINYGLTF3_ASSERT
|
||||||
|
#ifndef TINYGLTF3_NO_STDLIB
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#define TINYGLTF3_ASSERT(x) assert(x)
|
#define TINYGLTF3_ASSERT(x) assert(x)
|
||||||
|
#else
|
||||||
|
#define TINYGLTF3_ASSERT(x) ((void)(x))
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* ======================================================================
|
/* ======================================================================
|
||||||
@@ -127,8 +131,34 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#ifndef TINYGLTF3_NO_STDLIB
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.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
|
* Section 4: Constants and Enums
|
||||||
|
|||||||
@@ -1,9 +1,24 @@
|
|||||||
#ifndef TINYGLTF_JSON_C_H_
|
#ifndef TINYGLTF_JSON_C_H_
|
||||||
#define 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 <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -111,9 +126,433 @@ char *tg3json_stringify_pretty(const tg3json_value *value, int indent, size_t *o
|
|||||||
|
|
||||||
#ifdef TINYGLTF_JSON_C_IMPLEMENTATION
|
#ifdef TINYGLTF_JSON_C_IMPLEMENTATION
|
||||||
|
|
||||||
#include <errno.h>
|
#ifndef TINYGLTF_JSON_NO_STDLIB
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.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 {
|
typedef struct tg3json__parser {
|
||||||
const char *cur;
|
const char *cur;
|
||||||
@@ -136,27 +575,27 @@ typedef struct tg3json__buffer {
|
|||||||
|
|
||||||
static void tg3json__init_value(tg3json_value *value) {
|
static void tg3json__init_value(tg3json_value *value) {
|
||||||
if (!value) return;
|
if (!value) return;
|
||||||
memset(value, 0, sizeof(*value));
|
TINYGLTF_JSON_MEMSET(value, 0, sizeof(*value));
|
||||||
value->type = TG3JSON_NULL;
|
value->type = TG3JSON_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *tg3json__strndup_local(const char *src, size_t len) {
|
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 (!dst) return NULL;
|
||||||
if (len > 0) memcpy(dst, src, len);
|
if (len > 0) TINYGLTF_JSON_MEMCPY(dst, src, len);
|
||||||
dst[len] = '\0';
|
dst[len] = '\0';
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *tg3json__parser_alloc(tg3json__parser *parser, size_t size) {
|
static void *tg3json__parser_alloc(tg3json__parser *parser, size_t size) {
|
||||||
void *ptr;
|
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->max_single_alloc && size > parser->max_single_alloc) return NULL;
|
||||||
if (parser->memory_budget &&
|
if (parser->memory_budget &&
|
||||||
(size > parser->memory_budget || parser->allocated > parser->memory_budget - size)) {
|
(size > parser->memory_budget || parser->allocated > parser->memory_budget - size)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
ptr = malloc(size);
|
ptr = TINYGLTF_JSON_MALLOC(size);
|
||||||
if (!ptr) return NULL;
|
if (!ptr) return NULL;
|
||||||
parser->allocated += size;
|
parser->allocated += size;
|
||||||
return ptr;
|
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;
|
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;
|
if (!new_ptr) return 0;
|
||||||
*ptr = new_ptr;
|
*ptr = new_ptr;
|
||||||
*capacity = new_cap;
|
*capacity = new_cap;
|
||||||
@@ -215,7 +654,7 @@ static int tg3json__reserve_bytes_parser(tg3json__parser *parser, void **ptr,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
new_ptr = realloc(*ptr, new_bytes);
|
new_ptr = TINYGLTF_JSON_REALLOC(*ptr, new_bytes);
|
||||||
if (!new_ptr) return 0;
|
if (!new_ptr) return 0;
|
||||||
if (parser && new_bytes > old_bytes) parser->allocated += new_bytes - old_bytes;
|
if (parser && new_bytes > old_bytes) parser->allocated += new_bytes - old_bytes;
|
||||||
*ptr = new_ptr;
|
*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)) {
|
buf->len + len + 1, &buf->cap)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
memcpy(buf->data + buf->len, src, len);
|
TINYGLTF_JSON_MEMCPY(buf->data + buf->len, src, len);
|
||||||
buf->len += len;
|
buf->len += len;
|
||||||
buf->data[buf->len] = '\0';
|
buf->data[buf->len] = '\0';
|
||||||
return 1;
|
return 1;
|
||||||
@@ -300,7 +739,7 @@ static int tg3json__parse_string_raw(tg3json__parser *parser,
|
|||||||
char **out_str, size_t *out_len) {
|
char **out_str, size_t *out_len) {
|
||||||
tg3json__buffer buf;
|
tg3json__buffer buf;
|
||||||
const char *start;
|
const char *start;
|
||||||
memset(&buf, 0, sizeof(buf));
|
TINYGLTF_JSON_MEMSET(&buf, 0, sizeof(buf));
|
||||||
buf.parser = parser;
|
buf.parser = parser;
|
||||||
|
|
||||||
if (parser->cur >= parser->end || *parser->cur != '"') {
|
if (parser->cur >= parser->end || *parser->cur != '"') {
|
||||||
@@ -356,7 +795,7 @@ static int tg3json__parse_string_raw(tg3json__parser *parser,
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
tg3json__set_error(parser, parser->cur);
|
tg3json__set_error(parser, parser->cur);
|
||||||
free(buf.data);
|
TINYGLTF_JSON_FREE(buf.data);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
++parser->cur;
|
++parser->cur;
|
||||||
@@ -368,12 +807,12 @@ static int tg3json__parse_string_raw(tg3json__parser *parser,
|
|||||||
}
|
}
|
||||||
|
|
||||||
tg3json__set_error(parser, parser->cur);
|
tg3json__set_error(parser, parser->cur);
|
||||||
free(buf.data);
|
TINYGLTF_JSON_FREE(buf.data);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
oom:
|
oom:
|
||||||
tg3json__set_error(parser, parser->cur);
|
tg3json__set_error(parser, parser->cur);
|
||||||
free(buf.data);
|
TINYGLTF_JSON_FREE(buf.data);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,7 +895,7 @@ fail:
|
|||||||
--count;
|
--count;
|
||||||
tg3json_value_free(&items[count]);
|
tg3json_value_free(&items[count]);
|
||||||
}
|
}
|
||||||
free(items);
|
TINYGLTF_JSON_FREE(items);
|
||||||
tg3json__set_error(parser, parser->cur);
|
tg3json__set_error(parser, parser->cur);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -465,7 +904,7 @@ oom:
|
|||||||
--count;
|
--count;
|
||||||
tg3json_value_free(&items[count]);
|
tg3json_value_free(&items[count]);
|
||||||
}
|
}
|
||||||
free(items);
|
TINYGLTF_JSON_FREE(items);
|
||||||
tg3json__set_error(parser, parser->cur);
|
tg3json__set_error(parser, parser->cur);
|
||||||
return 0;
|
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;
|
if (!tg3json__parse_string_raw(parser, &key, &key_len)) goto fail;
|
||||||
parser->cur = tg3json__skip_ws(parser->cur, parser->end);
|
parser->cur = tg3json__skip_ws(parser->cur, parser->end);
|
||||||
if (parser->cur >= parser->end || *parser->cur != ':') {
|
if (parser->cur >= parser->end || *parser->cur != ':') {
|
||||||
free(key);
|
TINYGLTF_JSON_FREE(key);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
++parser->cur;
|
++parser->cur;
|
||||||
parser->cur = tg3json__skip_ws(parser->cur, parser->end);
|
parser->cur = tg3json__skip_ws(parser->cur, parser->end);
|
||||||
if (!tg3json__parse_value(parser, depth + 1, &value)) {
|
if (!tg3json__parse_value(parser, depth + 1, &value)) {
|
||||||
free(key);
|
TINYGLTF_JSON_FREE(key);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (!tg3json__reserve_bytes_parser(parser, (void **)&items,
|
if (!tg3json__reserve_bytes_parser(parser, (void **)&items,
|
||||||
sizeof(*items), count + 1, &cap)) {
|
sizeof(*items), count + 1, &cap)) {
|
||||||
free(key);
|
TINYGLTF_JSON_FREE(key);
|
||||||
tg3json_value_free(&value);
|
tg3json_value_free(&value);
|
||||||
goto oom;
|
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].key_len = key_len;
|
||||||
items[count].value = (tg3json_value *)tg3json__parser_alloc(parser, sizeof(tg3json_value));
|
items[count].value = (tg3json_value *)tg3json__parser_alloc(parser, sizeof(tg3json_value));
|
||||||
if (!items[count].value) {
|
if (!items[count].value) {
|
||||||
free(key);
|
TINYGLTF_JSON_FREE(key);
|
||||||
tg3json_value_free(&value);
|
tg3json_value_free(&value);
|
||||||
goto oom;
|
goto oom;
|
||||||
}
|
}
|
||||||
@@ -539,26 +978,26 @@ static int tg3json__parse_object(tg3json__parser *parser, size_t depth,
|
|||||||
fail:
|
fail:
|
||||||
while (count > 0) {
|
while (count > 0) {
|
||||||
--count;
|
--count;
|
||||||
free(items[count].key);
|
TINYGLTF_JSON_FREE(items[count].key);
|
||||||
if (items[count].value) {
|
if (items[count].value) {
|
||||||
tg3json_value_free(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);
|
tg3json__set_error(parser, parser->cur);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
oom:
|
oom:
|
||||||
while (count > 0) {
|
while (count > 0) {
|
||||||
--count;
|
--count;
|
||||||
free(items[count].key);
|
TINYGLTF_JSON_FREE(items[count].key);
|
||||||
if (items[count].value) {
|
if (items[count].value) {
|
||||||
tg3json_value_free(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);
|
tg3json__set_error(parser, parser->cur);
|
||||||
return 0;
|
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 *start = parser->cur;
|
||||||
const char *p = parser->cur;
|
const char *p = parser->cur;
|
||||||
int is_real = 0;
|
int is_real = 0;
|
||||||
size_t len;
|
|
||||||
char stack_buf[128];
|
|
||||||
char *num_buf = stack_buf;
|
|
||||||
|
|
||||||
if (*p == '-') ++p;
|
if (*p == '-') ++p;
|
||||||
if (p >= parser->end) goto fail;
|
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;
|
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;
|
if (!tg3json__parse_f64_c(start, p, &out_value->u.real)) goto fail;
|
||||||
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 (parser->parse_float32) out_value->u.real = (double)(float)out_value->u.real;
|
if (parser->parse_float32) out_value->u.real = (double)(float)out_value->u.real;
|
||||||
out_value->type = TG3JSON_REAL;
|
out_value->type = TG3JSON_REAL;
|
||||||
parser->cur = p;
|
parser->cur = p;
|
||||||
if (num_buf != stack_buf) free(num_buf);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
tg3json__set_error(parser, parser->cur);
|
tg3json__set_error(parser, parser->cur);
|
||||||
return 0;
|
return 0;
|
||||||
oom:
|
|
||||||
tg3json__set_error(parser, parser->cur);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tg3json__parse_value(tg3json__parser *parser, size_t depth,
|
static int tg3json__parse_value(tg3json__parser *parser, size_t depth,
|
||||||
tg3json_value *out_value) {
|
tg3json_value *out_value) {
|
||||||
tg3json__init_value(out_value);
|
if (parser->depth_limit && depth > parser->depth_limit) {
|
||||||
if (depth > parser->depth_limit) {
|
|
||||||
tg3json__set_error(parser, parser->cur);
|
tg3json__set_error(parser, parser->cur);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
parser->cur = tg3json__skip_ws(parser->cur, parser->end);
|
parser->cur = tg3json__skip_ws(parser->cur, parser->end);
|
||||||
if (parser->cur >= parser->end) {
|
if (parser->cur >= parser->end) {
|
||||||
tg3json__set_error(parser, parser->cur);
|
tg3json__set_error(parser, parser->cur);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (*parser->cur) {
|
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':
|
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;
|
out_value->type = TG3JSON_NULL;
|
||||||
parser->cur += 4;
|
parser->cur += 4;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 't':
|
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->type = TG3JSON_BOOL;
|
||||||
out_value->u.boolean = 1;
|
out_value->u.boolean = 1;
|
||||||
parser->cur += 4;
|
parser->cur += 4;
|
||||||
@@ -665,27 +1097,17 @@ static int tg3json__parse_value(tg3json__parser *parser, size_t depth,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'f':
|
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->type = TG3JSON_BOOL;
|
||||||
out_value->u.boolean = 0;
|
out_value->u.boolean = 0;
|
||||||
parser->cur += 5;
|
parser->cur += 5;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
if (*parser->cur == '-' || (*parser->cur >= '0' && *parser->cur <= '9')) {
|
return tg3json__parse_number(parser, out_value);
|
||||||
return tg3json__parse_number(parser, out_value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tg3json__set_error(parser, parser->cur);
|
tg3json__set_error(parser, parser->cur);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -695,40 +1117,50 @@ int tg3json_parse_n_opts(const char *data, size_t len,
|
|||||||
tg3json_value *out_value,
|
tg3json_value *out_value,
|
||||||
const char **out_error_pos) {
|
const char **out_error_pos) {
|
||||||
tg3json__parser parser;
|
tg3json__parser parser;
|
||||||
if (!data || !out_value) {
|
int ok;
|
||||||
if (out_error_pos) *out_error_pos = data;
|
|
||||||
return 0;
|
if (out_error_pos) *out_error_pos = NULL;
|
||||||
}
|
if (!out_value) return 0;
|
||||||
tg3json__init_value(out_value);
|
tg3json__init_value(out_value);
|
||||||
|
if (!data) return 0;
|
||||||
|
|
||||||
parser.cur = data;
|
parser.cur = data;
|
||||||
parser.end = data + len;
|
parser.end = data + len;
|
||||||
parser.error = NULL;
|
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.memory_budget = options ? options->memory_budget : 0;
|
||||||
parser.max_single_alloc = options ? options->max_single_alloc : 0;
|
parser.max_single_alloc = options ? options->max_single_alloc : 0;
|
||||||
parser.max_string_length = options ? options->max_string_length : 0;
|
parser.max_string_length = options ? options->max_string_length : 0;
|
||||||
parser.allocated = 0;
|
parser.allocated = 0;
|
||||||
parser.parse_float32 = options ? options->parse_float32 : 0;
|
parser.parse_float32 = options ? options->parse_float32 : 0;
|
||||||
|
|
||||||
if (!tg3json__parse_value(&parser, 0, out_value)) {
|
parser.cur = tg3json__skip_ws(parser.cur, parser.end);
|
||||||
if (out_error_pos) *out_error_pos = parser.error;
|
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);
|
tg3json_value_free(out_value);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
parser.cur = tg3json__skip_ws(parser.cur, parser.end);
|
parser.cur = tg3json__skip_ws(parser.cur, parser.end);
|
||||||
if (parser.cur != parser.end) {
|
if (parser.cur != parser.end) {
|
||||||
if (out_error_pos) *out_error_pos = parser.cur;
|
if (out_error_pos) *out_error_pos = parser.cur;
|
||||||
tg3json_value_free(out_value);
|
tg3json_value_free(out_value);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (out_error_pos) *out_error_pos = NULL;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tg3json_parse_n(const char *data, size_t len, size_t depth_limit,
|
int tg3json_parse_n(const char *data, size_t len, size_t depth_limit,
|
||||||
tg3json_value *out_value, const char **out_error_pos) {
|
tg3json_value *out_value, const char **out_error_pos) {
|
||||||
tg3json_parse_options options;
|
tg3json_parse_options options;
|
||||||
memset(&options, 0, sizeof(options));
|
TINYGLTF_JSON_MEMSET(&options, 0, sizeof(options));
|
||||||
options.depth_limit = depth_limit;
|
options.depth_limit = depth_limit;
|
||||||
return tg3json_parse_n_opts(data, len, &options, out_value, out_error_pos);
|
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;
|
if (!value) return;
|
||||||
switch (value->type) {
|
switch (value->type) {
|
||||||
case TG3JSON_STRING:
|
case TG3JSON_STRING:
|
||||||
free(value->u.string.ptr);
|
TINYGLTF_JSON_FREE(value->u.string.ptr);
|
||||||
break;
|
break;
|
||||||
case TG3JSON_ARRAY:
|
case TG3JSON_ARRAY:
|
||||||
for (i = 0; i < value->u.array.count; ++i) {
|
for (i = 0; i < value->u.array.count; ++i) {
|
||||||
tg3json_value_free(&value->u.array.items[i]);
|
tg3json_value_free(&value->u.array.items[i]);
|
||||||
}
|
}
|
||||||
free(value->u.array.items);
|
TINYGLTF_JSON_FREE(value->u.array.items);
|
||||||
break;
|
break;
|
||||||
case TG3JSON_OBJECT:
|
case TG3JSON_OBJECT:
|
||||||
for (i = 0; i < value->u.object.count; ++i) {
|
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) {
|
if (value->u.object.items[i].value) {
|
||||||
tg3json_value_free(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;
|
break;
|
||||||
case TG3JSON_NULL:
|
case TG3JSON_NULL:
|
||||||
case TG3JSON_BOOL:
|
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) {
|
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) {
|
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;
|
if (!object || object->type != TG3JSON_OBJECT) return NULL;
|
||||||
for (i = 0; i < object->u.object.count; ++i) {
|
for (i = 0; i < object->u.object.count; ++i) {
|
||||||
const tg3json_object_entry *entry = &object->u.object.items[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;
|
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 tg3json_value *tg3json_object_get(const tg3json_value *object,
|
||||||
const char *key) {
|
const char *key) {
|
||||||
if (!key) return NULL;
|
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,
|
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;
|
if (!object || object->type != TG3JSON_OBJECT) return NULL;
|
||||||
for (i = 0; i < object->u.object.count; ++i) {
|
for (i = 0; i < object->u.object.count; ++i) {
|
||||||
tg3json_object_entry *entry = &object->u.object.items[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;
|
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) {
|
tg3json_value *tg3json_object_get_mut(tg3json_value *object, const char *key) {
|
||||||
if (!key) return NULL;
|
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,
|
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);
|
entry->key = tg3json__strndup_local(key, key_len);
|
||||||
if (!entry->key) return 0;
|
if (!entry->key) return 0;
|
||||||
entry->key_len = key_len;
|
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) {
|
if (!entry->value) {
|
||||||
free(entry->key);
|
TINYGLTF_JSON_FREE(entry->key);
|
||||||
entry->key = NULL;
|
entry->key = NULL;
|
||||||
entry->key_len = 0;
|
entry->key_len = 0;
|
||||||
return 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,
|
int tg3json_object_set_take(tg3json_value *object, const char *key,
|
||||||
tg3json_value *value) {
|
tg3json_value *value) {
|
||||||
if (!key) return 0;
|
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,
|
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,
|
int tg3json_object_set_copy(tg3json_value *object, const char *key,
|
||||||
const tg3json_value *value) {
|
const tg3json_value *value) {
|
||||||
if (!key) return 0;
|
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) {
|
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)
|
return value->u.boolean ? tg3json__buf_append(buf, "true", 4)
|
||||||
: tg3json__buf_append(buf, "false", 5);
|
: tg3json__buf_append(buf, "false", 5);
|
||||||
case TG3JSON_INT:
|
case TG3JSON_INT:
|
||||||
snprintf(numbuf, sizeof(numbuf), "%lld", (long long)value->u.integer);
|
tg3json__itoa(value->u.integer, numbuf);
|
||||||
return tg3json__buf_append(buf, numbuf, strlen(numbuf));
|
return tg3json__buf_append(buf, numbuf, TINYGLTF_JSON_STRLEN(numbuf));
|
||||||
case TG3JSON_REAL:
|
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;
|
const char *b = numbuf;
|
||||||
if (*b == '-') ++b;
|
if (*b == '-') ++b;
|
||||||
if (*b == 'n' || *b == 'N' || *b == 'i' || *b == 'I') {
|
if (*b == 'n' || *b == 'N' || *b == 'i' || *b == 'I') {
|
||||||
return tg3json__buf_append(buf, "null", 4);
|
return tg3json__buf_append(buf, "null", 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!strchr(numbuf, '.') && !strchr(numbuf, 'e') && !strchr(numbuf, 'E')) {
|
return tg3json__buf_append(buf, numbuf, TINYGLTF_JSON_STRLEN(numbuf));
|
||||||
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));
|
|
||||||
case TG3JSON_STRING:
|
case TG3JSON_STRING:
|
||||||
if (!tg3json__buf_putc(buf, '"')) return 0;
|
if (!tg3json__buf_putc(buf, '"')) return 0;
|
||||||
for (i = 0; i < value->u.string.len; ++i) {
|
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;
|
case '\t': if (!tg3json__buf_append(buf, "\\t", 2)) return 0; break;
|
||||||
default:
|
default:
|
||||||
if (c < 0x20u) {
|
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;
|
if (!tg3json__buf_append(buf, numbuf, 6)) return 0;
|
||||||
} else {
|
} else {
|
||||||
if (!tg3json__buf_putc(buf, (char)c)) return 0;
|
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) {
|
char *tg3json_stringify(const tg3json_value *value, size_t *out_len) {
|
||||||
tg3json__buffer buf;
|
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)) {
|
if (!value || !tg3json__stringify_value_ex(&buf, value, -1, 0)) {
|
||||||
free(buf.data);
|
TINYGLTF_JSON_FREE(buf.data);
|
||||||
if (out_len) *out_len = 0;
|
if (out_len) *out_len = 0;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (!buf.data) {
|
if (!buf.data) {
|
||||||
buf.data = (char *)malloc(1);
|
buf.data = (char *)TINYGLTF_JSON_MALLOC(1);
|
||||||
if (!buf.data) {
|
if (!buf.data) {
|
||||||
if (out_len) *out_len = 0;
|
if (out_len) *out_len = 0;
|
||||||
return NULL;
|
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) {
|
char *tg3json_stringify_pretty(const tg3json_value *value, int indent, size_t *out_len) {
|
||||||
tg3json__buffer buf;
|
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)) {
|
if (!value || !tg3json__stringify_value_ex(&buf, value, indent, 0)) {
|
||||||
free(buf.data);
|
TINYGLTF_JSON_FREE(buf.data);
|
||||||
if (out_len) *out_len = 0;
|
if (out_len) *out_len = 0;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (!buf.data) {
|
if (!buf.data) {
|
||||||
buf.data = (char *)malloc(1);
|
buf.data = (char *)TINYGLTF_JSON_MALLOC(1);
|
||||||
if (!buf.data) {
|
if (!buf.data) {
|
||||||
if (out_len) *out_len = 0;
|
if (out_len) *out_len = 0;
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
Reference in New Issue
Block a user