From 86e2d6b07259baf3c33ebf8768b0cb71f5ee4cc9 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Wed, 13 Nov 2024 20:42:48 -0600 Subject: [PATCH 01/23] Add bad json input tests found by OSS-Fuzz These test cases all crash with their corresponding tests, allowing easy reproduction and eventual fixing. --- tests/CMakeLists.txt | 1 + ...se-instance_create_fuzzer-5599244505186304 | Bin 0 -> 718 bytes ...instance_enumerate_fuzzer-5126563864051712 | 3 + ...instance_enumerate_fuzzer-6308459683315712 | 246 ++++++++++++++++++ ...testcase-json_load_fuzzer-5258042868105216 | 0 ...testcase-json_load_fuzzer-5487817455960064 | 0 ...ed-instance_create_fuzzer-4558978302214144 | 99 +++++++ ...ed-instance_create_fuzzer-4568454561071104 | 6 + ...ed-instance_create_fuzzer-4820577276723200 | 50 ++++ ...ed-instance_create_fuzzer-5177827962454016 | 7 + ...ed-instance_create_fuzzer-5198773675425792 | 1 + ...ed-instance_create_fuzzer-5416197367070720 | 1 + ...ed-instance_create_fuzzer-5494771615137792 | 99 +++++++ ...ed-instance_create_fuzzer-5801855065915392 | 88 +++++++ ...instance_enumerate_fuzzer-6465902356791296 | 2 + ...inimized-json_load_fuzzer-4512865114259456 | Bin 0 -> 55 bytes ...inimized-json_load_fuzzer-4552015310880768 | 1 + ...inimized-json_load_fuzzer-5208693600747520 | 1 + ...inimized-json_load_fuzzer-5347670374612992 | 1 + ...inimized-json_load_fuzzer-5392928643547136 | 1 + ...inimized-json_load_fuzzer-5636386303049728 | 1 + ...inimized-json_load_fuzzer-6182254813249536 | 2 + ...inimized-json_load_fuzzer-6265355951996928 | Bin 0 -> 10341 bytes ...inimized-json_load_fuzzer-6363106126659584 | Bin 0 -> 577 bytes ...inimized-json_load_fuzzer-6482033715838976 | 1 + ...minimized-settings_fuzzer-4857714377818112 | 2 + ...minimized-settings_fuzzer-5123849246867456 | 99 +++++++ tests/framework/framework_config.h.in | 2 + tests/framework/test_environment.cpp | 16 ++ tests/framework/test_environment.h | 3 + tests/loader_alloc_callback_tests.cpp | 11 +- tests/loader_fuzz_tests.cpp | 245 +++++++++++++++++ 32 files changed, 981 insertions(+), 8 deletions(-) create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-instance_create_fuzzer-5599244505186304 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-instance_enumerate_fuzzer-5126563864051712 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-instance_enumerate_fuzzer-6308459683315712 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-json_load_fuzzer-5258042868105216 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-json_load_fuzzer-5487817455960064 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-4558978302214144 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-4568454561071104 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-4820577276723200 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-5177827962454016 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-5198773675425792 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-5416197367070720 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-5494771615137792 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-5801855065915392 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_enumerate_fuzzer-6465902356791296 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-4512865114259456 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-4552015310880768 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5208693600747520 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5347670374612992 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5392928643547136 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5636386303049728 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6182254813249536 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6265355951996928 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6363106126659584 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6482033715838976 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-settings_fuzzer-4857714377818112 create mode 100644 tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-settings_fuzzer-5123849246867456 create mode 100644 tests/loader_fuzz_tests.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fbc4fc3f2..7020df5da 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -84,6 +84,7 @@ add_executable( loader_envvar_tests.cpp loader_get_proc_addr_tests.cpp loader_debug_ext_tests.cpp + loader_fuzz_tests.cpp loader_handle_validation_tests.cpp loader_layer_tests.cpp loader_regression_tests.cpp diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-instance_create_fuzzer-5599244505186304 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-instance_create_fuzzer-5599244505186304 new file mode 100644 index 0000000000000000000000000000000000000000..a939fe81368af35bb7859e058a1471f63a8d845f GIT binary patch literal 718 zcmZ`$%SyvQ6wSIzzaY#~Y+4_y?rIg41`8DmB4wDGDIGF-Ff$E=l7(Bvg)X{Kkp7YW zi$CDK6I+vjcQucD&*Pp|%Y`CyAvv8VDq@CaTxpTSr00_M(O%a(BnM@a=Eexca;N9s z*%usL?M@bmr6~=0u3`GG2Y;|g#^ed94NP~p0z0O^RFdVOq6X3+idn=_;$%qu{_Xjg z4ll=}>(LDb4iPYe4oWb@#Udt8?Z|~cI_>+-UzUohb@1&qTSSyf9`V>%_-gldM;6t$hRo`8?46Y3(g+vC=plcb_!zmgI#Bg&S+FUBZ*$B7ET@B7^gy^E?3S_@_ xv>J4>r3_izmMKr$dD3=EH$y>yKMK}ie7 literal 0 HcmV?d00001 diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-4552015310880768 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-4552015310880768 new file mode 100644 index 000000000..f20d4ba7f --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-4552015310880768 @@ -0,0 +1 @@ +[18446744073709551616e-39065906811251e \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5208693600747520 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5208693600747520 new file mode 100644 index 000000000..5167c9f19 --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5208693600747520 @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[{}]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5347670374612992 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5347670374612992 new file mode 100644 index 000000000..810c539bc --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5347670374612992 @@ -0,0 +1 @@ +[3] \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5392928643547136 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5392928643547136 new file mode 100644 index 000000000..e2cb11d64 --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5392928643547136 @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["\u"\\DB1E\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[uD0f[[[B[[[[[[[[[[[[[[[[2[[[[[[[[[[[[[ \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5636386303049728 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5636386303049728 new file mode 100644 index 000000000..3c77fe4be --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5636386303049728 @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["\udf" [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6182254813249536 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6182254813249536 new file mode 100644 index 000000000..08cf6bdf0 --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6182254813249536 @@ -0,0 +1,2 @@ +{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":9223372036854775808,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":9223372036854775808,"{":{"f":1,"{":{"f":1,"{":{"f":0,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":2147483649,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":214748364,"{":{"f":1,"{":{"f":1,"{":{"f":-54,"{":{"f":1,"{{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":2147483649,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{{":{"f":2,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f{ +"4":2147483649,"{"0,"":4,4,":{"f":"f":1,"{":{"f \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6265355951996928 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6265355951996928 new file mode 100644 index 0000000000000000000000000000000000000000..91aea359d6bab3a2e1dde8a1ae4c8148ecd0c616 GIT binary patch literal 10341 zcmeI!!3{t#6b4{3aB?*P^OTze1BjIv!F0q9Bwq9Vho-0Y{a<=$Nb!@l&*)PYl&gWYfI47XM^=$%*7?}V80t5(TCNKyA0t5&I5U2voXhZ^; aeUkoI$0H*^fB=Df1j>A>XhMKM41pW)A-<0Q literal 0 HcmV?d00001 diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6363106126659584 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6363106126659584 new file mode 100644 index 0000000000000000000000000000000000000000..253b08499a7a8c0e384328b2af061e3bac65b635 GIT binary patch literal 577 zcma#9uGJYuWQKrJOsSC|gK|u%Ap--DZP>sFB9$2!fZ_}cNg(n6KoL`Qu zQ) + std::filesystem::path get_loader_path() { auto loader_path = std::filesystem::path(FRAMEWORK_VULKAN_LIBRARY_PATH); auto env_var_res = get_env_var("VK_LOADER_TEST_LOADER_PATH", false); @@ -796,6 +798,20 @@ void FrameworkEnvironment::update_loader_settings(const LoaderSettings& settings void FrameworkEnvironment::remove_loader_settings() { get_folder(ManifestLocation::settings_location).remove("vk_loader_settings.json"); } +void FrameworkEnvironment::write_file_from_source(const char* source_file, ManifestCategory category, ManifestLocation location, + std::string const& file_name) { + std::fstream file{source_file, std::ios_base::in}; + ASSERT_TRUE(file.is_open()); + std::stringstream file_stream; + file_stream << file.rdbuf(); + + auto out_path = get_folder(location).write_manifest(file_name, file_stream.str()); + + if (settings.secure_loader_settings) + platform_shim->add_manifest(category, out_path); + else + platform_shim->add_unsecured_manifest(category, out_path); +} TestICD& FrameworkEnvironment::get_test_icd(size_t index) noexcept { return icds[index].get_test_icd(); } TestICD& FrameworkEnvironment::reset_icd(size_t index) noexcept { return icds[index].reset_icd(); } diff --git a/tests/framework/test_environment.h b/tests/framework/test_environment.h index f3368c1c5..903bfd896 100644 --- a/tests/framework/test_environment.h +++ b/tests/framework/test_environment.h @@ -660,6 +660,9 @@ struct FrameworkEnvironment { void update_loader_settings(const LoaderSettings& loader_settings) noexcept; void remove_loader_settings(); + void write_file_from_source(const char* source_file, ManifestCategory category, ManifestLocation location, + std::string const& file_name); + TestICD& get_test_icd(size_t index = 0) noexcept; TestICD& reset_icd(size_t index = 0) noexcept; std::filesystem::path get_test_icd_path(size_t index = 0) noexcept; diff --git a/tests/loader_alloc_callback_tests.cpp b/tests/loader_alloc_callback_tests.cpp index f92dc41a6..e437158e6 100644 --- a/tests/loader_alloc_callback_tests.cpp +++ b/tests/loader_alloc_callback_tests.cpp @@ -743,14 +743,9 @@ TEST(Allocation, CreateInstanceDeviceIntentionalAllocFail) { .set_disable_environment("DISABLE_ENV")), "test_layer_" + std::to_string(i) + ".json"); } - std::fstream custom_json_file{COMPLEX_JSON_FILE, std::ios_base::in}; - ASSERT_TRUE(custom_json_file.is_open()); - std::stringstream custom_json_file_contents; - custom_json_file_contents << custom_json_file.rdbuf(); - - std::filesystem::path new_path = env.get_folder(ManifestLocation::explicit_layer) - .write_manifest("VK_LAYER_complex_file.json", custom_json_file_contents.str()); - env.platform_shim->add_manifest(ManifestCategory::explicit_layer, new_path); + // Throw in a complex json file to flex the json allocation routines + env.write_file_from_source(COMPLEX_JSON_FILE, ManifestCategory::explicit_layer, ManifestLocation::explicit_layer, + "VK_LAYER_complex_file.json"); size_t fail_index = 0; VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY; diff --git a/tests/loader_fuzz_tests.cpp b/tests/loader_fuzz_tests.cpp new file mode 100644 index 000000000..22f8be6e6 --- /dev/null +++ b/tests/loader_fuzz_tests.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2024 The Khronos Group Inc. + * Copyright (c) 2024 Valve Corporation + * Copyright (c) 2024 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + * Author: Charles Giessen + */ + +#include "test_environment.h" + +void execute_instance_enumerate_fuzzer(std::filesystem::path const& filename) { + FrameworkEnvironment env{}; + env.write_file_from_source((std::filesystem::path(CLUSTERFUZZ_TESTCASE_DIRECTORY) / filename).string().c_str(), + ManifestCategory::implicit_layer, ManifestLocation::implicit_layer, "complex_layer.json"); + env.write_file_from_source((std::filesystem::path(CLUSTERFUZZ_TESTCASE_DIRECTORY) / filename).string().c_str(), + ManifestCategory::settings, ManifestLocation::settings_location, "vk_loader_settings.json"); + + uint32_t pPropertyCount; + VkExtensionProperties pProperties = {0}; + + env.vulkan_functions.vkEnumerateInstanceExtensionProperties("test_auto", &pPropertyCount, &pProperties); +} +void execute_instance_create_fuzzer(std::filesystem::path const& filename) { + FrameworkEnvironment env{}; + env.write_file_from_source((std::filesystem::path(CLUSTERFUZZ_TESTCASE_DIRECTORY) / filename).string().c_str(), + ManifestCategory::implicit_layer, ManifestLocation::implicit_layer, "complex_layer.json"); + env.write_file_from_source((std::filesystem::path(CLUSTERFUZZ_TESTCASE_DIRECTORY) / filename).string().c_str(), + ManifestCategory::settings, ManifestLocation::settings_location, "vk_loader_settings.json"); + env.write_file_from_source((std::filesystem::path(CLUSTERFUZZ_TESTCASE_DIRECTORY) / filename).string().c_str(), + ManifestCategory::icd, ManifestLocation::driver, "icd_test.json"); + VkInstance inst = {0}; + const char* instance_layers[] = {"VK_LAYER_KHRONOS_validation", "VK_LAYER_test_layer_1", "VK_LAYER_test_layer_2"}; + VkApplicationInfo app{}; + app.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app.pNext = NULL; + app.pApplicationName = "TEST_APP"; + app.applicationVersion = 0; + app.pEngineName = "TEST_ENGINE"; + app.engineVersion = 0; + app.apiVersion = VK_API_VERSION_1_0; + + VkInstanceCreateInfo inst_info{}; + inst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + inst_info.pNext = NULL; + inst_info.pApplicationInfo = &app; + inst_info.enabledLayerCount = 1; + inst_info.ppEnabledLayerNames = (const char* const*)instance_layers; + inst_info.enabledExtensionCount = 0; + inst_info.ppEnabledExtensionNames = NULL; + + VkResult err = env.vulkan_functions.vkCreateInstance(&inst_info, NULL, &inst); + if (err != VK_SUCCESS) { + return; + } + + env.vulkan_functions.vkDestroyInstance(inst, NULL); +} + +void execute_json_load_fuzzer(std::filesystem::path const& filename) { + FrameworkEnvironment env{}; + + env.write_file_from_source((std::filesystem::path(CLUSTERFUZZ_TESTCASE_DIRECTORY) / filename).string().c_str(), + ManifestCategory::explicit_layer, ManifestLocation::explicit_layer, "complex_layer.json"); + uint32_t count = 0; + env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr); +} +void execute_setting_fuzzer(std::filesystem::path const& filename) { + FrameworkEnvironment env{}; + + env.write_file_from_source((std::filesystem::path(CLUSTERFUZZ_TESTCASE_DIRECTORY) / filename).string().c_str(), + ManifestCategory::settings, ManifestLocation::settings_location, "vk_loader_settings.json"); + + uint32_t version = 0; + env.vulkan_functions.vkEnumerateInstanceVersion(&version); +} + +TEST(BadJsonInput, ClusterFuzzTestCase_5599244505186304) { + // Doesn't crash with ASAN or UBSAN + // Doesn't reproducibly crash - instance_create_fuzzer: Abrt in loader_cJSON_Delete + execute_instance_create_fuzzer("clusterfuzz-testcase-instance_create_fuzzer-5599244505186304"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5126563864051712) { + // Doesn't crash with ASAN or UBSAN + // Doesn't reproducibly crash - instance_enumerate_fuzzer: Abrt in loader_cJSON_Delete + execute_instance_enumerate_fuzzer("clusterfuzz-testcase-instance_enumerate_fuzzer-5126563864051712"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_6308459683315712) { + // Doesn't crash with ASAN or UBSAN + // Doesn't reproducibly crash - instance_enumerate_fuzzer: Null-dereference READ in + // combine_settings_layers_with_regular_layers + execute_instance_enumerate_fuzzer("clusterfuzz-testcase-instance_enumerate_fuzzer-6308459683315712"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5258042868105216) { + // Doesn't crash with ASAN or UBSAN + // Doesn't reproducibly crash - json_load_fuzzer: Abrt in loader_cJSON_Delete + execute_json_load_fuzzer("clusterfuzz-testcase-json_load_fuzzer-5258042868105216"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5487817455960064) { + // Doesn't crash with ASAN or UBSAN + // Doesn't reproducibly crash - json_load_fuzzer: Abrt in std::__Fuzzer::vector, std::__ + execute_json_load_fuzzer("clusterfuzz-testcase-json_load_fuzzer-5487817455960064"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_4558978302214144) { + // Does crash with UBSAN and ASAN + // loader.c:287: VkResult loader_copy_to_new_str(const struct loader_instance *, const char *, char **): Assertion + // `source_str + // && dest_str' failed. + // instance_create_fuzzer: Null-dereference READ in loader_copy_to_new_str + execute_instance_create_fuzzer("clusterfuzz-testcase-minimized-instance_create_fuzzer-4558978302214144"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_4568454561071104) { + // Does crash with UBSAN and ASAN + // Causes hangs - instance_create_fuzzer: Timeout in instance_create_fuzzer + execute_instance_create_fuzzer("clusterfuzz-testcase-minimized-instance_create_fuzzer-4568454561071104"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_4820577276723200) { + // Does crash with UBSAN and ASAN + // instance_create_fuzzer: Crash in printf_common + execute_instance_create_fuzzer("clusterfuzz-testcase-minimized-instance_create_fuzzer-4820577276723200"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5177827962454016) { + // Does crash with UBSAN and ASAN + // free(): invalid next size (fast) + // instance_create_fuzzer: Abrt in instance_create_fuzzer + execute_instance_create_fuzzer("clusterfuzz-testcase-minimized-instance_create_fuzzer-5177827962454016"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5198773675425792) { + // Does crash with UBSAN and ASAN + // stack-overflow + // instance_create_fuzzer: Stack-overflow with empty stacktrace + execute_instance_create_fuzzer("clusterfuzz-testcase-minimized-instance_create_fuzzer-5198773675425792"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5416197367070720) { + // Does crash with UBSAN and ASAN + // free(): invalid next size (fast) + // instance_create_fuzzer: Overwrites-const-input in instance_create_fuzzer + execute_instance_create_fuzzer("clusterfuzz-testcase-minimized-instance_create_fuzzer-5416197367070720"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5494771615137792) { + // Does crash with UBSAN and ASAN + // stack-overflow + // instance_create_fuzzer: Stack-overflow in verify_meta_layer_component_layers + execute_instance_create_fuzzer("clusterfuzz-testcase-minimized-instance_create_fuzzer-5494771615137792"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5801855065915392) { + // Does crash with ASAN + // Doesn't crash with UBSAN + // Causes a leak - instance_create_fuzzer: Direct-leak in print_string_ptr + execute_instance_create_fuzzer("clusterfuzz-testcase-minimized-instance_create_fuzzer-5801855065915392"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_6465902356791296) { + // Does crash with UBSAN + // Doesn't crash with ASAN + // Causes an integer overflow - instance_enumerate_fuzzer: Integer-overflow in parse_value + execute_instance_enumerate_fuzzer("clusterfuzz-testcase-minimized-instance_enumerate_fuzzer-6465902356791296"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_4512865114259456) { + // Does crash with UBSAN and ASAN + // malloc(): invalid size (unsorted) + // json_load_fuzzer: Heap-buffer-overflow in parse_string + execute_json_load_fuzzer("clusterfuzz-testcase-minimized-json_load_fuzzer-4512865114259456"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_4552015310880768) { + // Does crash with UBSAN + // Doesn't crash with ASAN + // Causes an integer overflow + // json_load_fuzzer: Integer-overflow in parse_value + execute_json_load_fuzzer("clusterfuzz-testcase-minimized-json_load_fuzzer-4552015310880768"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5208693600747520) { + // Does crash with UBSAN and ASAN + // Stack overflow + // json_load_fuzzer: Stack-overflow in print_value + execute_json_load_fuzzer("clusterfuzz-testcase-minimized-json_load_fuzzer-5208693600747520"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5347670374612992) { + // Doesn't crash with ASAN or UBSAN + // No reported leaks in head, crashes in 1.3.269 & 1.3.250 + // Causes a leak - json_load_fuzzer: Direct-leak in parse_array + execute_json_load_fuzzer("clusterfuzz-testcase-minimized-json_load_fuzzer-5347670374612992"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5392928643547136) { + // Does crash with UBSAN and ASAN + // free(): corrupted unsorted chunks + // json_load_fuzzer: Abrt in std::__Fuzzer::basic_filebuf>::~basic_fil + execute_json_load_fuzzer("clusterfuzz-testcase-minimized-json_load_fuzzer-5392928643547136"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5636386303049728) { + // Does crash with UBSAN and ASAN + // terminate called after throwing an instance of 'std::bad_alloc' what(): std::bad_alloc + // json_load_fuzzer: Abrt in json_load_fuzzer + execute_json_load_fuzzer("clusterfuzz-testcase-minimized-json_load_fuzzer-5636386303049728"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_6182254813249536) { + // Doesn't crash with ASAN or UBSAN + // No leaks reported in main, 1.3.269, nor 1.3.250 + // Causes a leak - json_load_fuzzer: Indirect-leak in parse_object + execute_json_load_fuzzer("clusterfuzz-testcase-minimized-json_load_fuzzer-6182254813249536"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_6265355951996928) { + // Does crash with UBSAN and ASAN + // json_load_fuzzer: Null-dereference READ in json_load_fuzzer + execute_json_load_fuzzer("clusterfuzz-testcase-minimized-json_load_fuzzer-6265355951996928"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_6363106126659584) { + // Does crash with UBSAN and ASAN + // json_load_fuzzer: Overwrites-const-input in json_load_fuzzer + execute_json_load_fuzzer("clusterfuzz-testcase-minimized-json_load_fuzzer-6363106126659584"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_6482033715838976) { + // Does crash with UBSAN and ASAN + // json_load_fuzzer: Stack-overflow in parse_array + execute_json_load_fuzzer("clusterfuzz-testcase-minimized-json_load_fuzzer-6482033715838976"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_4857714377818112) { + // Does crash with UBSAN and ASAN + // settings_fuzzer: Abrt in settings_fuzzer + execute_setting_fuzzer("clusterfuzz-testcase-minimized-settings_fuzzer-4857714377818112"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5123849246867456) { + // Doesn't crash with ASAN or UBSAN + // No leaks reported in main, 1.3.269, nor 1.3.250 + // Causes a leak - settings_fuzzer: Direct-leak in loader_append_layer_property + execute_setting_fuzzer("clusterfuzz-testcase-minimized-settings_fuzzer-5123849246867456"); +} From 1259176e465248be859e866a5584dae248e5405d Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Fri, 22 Nov 2024 15:14:50 -0600 Subject: [PATCH 02/23] Add loader_json.h/.c for custom loader code Several functions have been added to the cJSON.h/.c files. While these functions are important, they do not belong in the cJSON source code files, as it makes it difficult to update cJSON without stomping all over the added code. This does require re-adding cJSON_Parse to the cJSON.h header file, which was previously removed as it was only used internally to cJSON.c. Now that it isn't the case, it is needed again. --- loader/CMakeLists.txt | 2 + loader/cJSON.c | 200 +--------------------------------- loader/cJSON.h | 31 +----- loader/loader.c | 1 + loader/loader_json.c | 242 ++++++++++++++++++++++++++++++++++++++++++ loader/loader_json.h | 56 ++++++++++ loader/settings.c | 1 + 7 files changed, 307 insertions(+), 226 deletions(-) create mode 100644 loader/loader_json.c create mode 100644 loader/loader_json.h diff --git a/loader/CMakeLists.txt b/loader/CMakeLists.txt index 99b6a393a..1497018c3 100644 --- a/loader/CMakeLists.txt +++ b/loader/CMakeLists.txt @@ -82,6 +82,8 @@ set(NORMAL_LOADER_SRCS loader.h log.c log.h + loader_json.c + loader_json.h settings.c settings.h terminator.c diff --git a/loader/cJSON.c b/loader/cJSON.c index 5f63bbc2d..a26eab212 100644 --- a/loader/cJSON.c +++ b/loader/cJSON.c @@ -491,7 +491,7 @@ static cJSON *cJSON_ParseWithOpts(const VkAllocationCallbacks *pAllocator, const return c; } /* Default options for cJSON_Parse */ -static cJSON *cJSON_Parse(const VkAllocationCallbacks *pAllocator, const char *value, bool *out_of_memory) { +cJSON *cJSON_Parse(const VkAllocationCallbacks *pAllocator, const char *value, bool *out_of_memory) { return cJSON_ParseWithOpts(pAllocator, value, 0, 0, out_of_memory); } @@ -954,201 +954,3 @@ cJSON *loader_cJSON_GetObjectItem(cJSON *object, const char *string) { while (c && NULL != c->string && strcmp(c->string, string)) c = c->next; return c; } - -#ifdef _WIN32 -static VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, char **out_buff) { - HANDLE file_handle = INVALID_HANDLE_VALUE; - DWORD len = 0, read_len = 0; - VkResult res = VK_SUCCESS; - BOOL read_ok = false; - - int filename_utf16_size = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0); - if (filename_utf16_size > 0) { - wchar_t *filename_utf16 = (wchar_t *)loader_stack_alloc(filename_utf16_size * sizeof(wchar_t)); - if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_utf16, filename_utf16_size) == filename_utf16_size) { - file_handle = - CreateFileW(filename_utf16, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - } - } - if (INVALID_HANDLE_VALUE == file_handle) { - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to open JSON file %s", filename); - res = VK_ERROR_INITIALIZATION_FAILED; - goto out; - } - len = GetFileSize(file_handle, NULL); - if (INVALID_FILE_SIZE == len) { - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read file size of JSON file %s", filename); - res = VK_ERROR_INITIALIZATION_FAILED; - goto out; - } - *out_buff = (char *)loader_instance_heap_calloc(inst, len + 1, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); - if (NULL == *out_buff) { - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to allocate memory to read JSON file %s", filename); - res = VK_ERROR_OUT_OF_HOST_MEMORY; - goto out; - } - read_ok = ReadFile(file_handle, *out_buff, len, &read_len, NULL); - if (len != read_len || false == read_ok) { - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read entire JSON file %s", filename); - res = VK_ERROR_INITIALIZATION_FAILED; - goto out; - } - (*out_buff)[len] = '\0'; - -out: - if (INVALID_HANDLE_VALUE != file_handle) { - CloseHandle(file_handle); - } - return res; -} -#elif COMMON_UNIX_PLATFORMS -static VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, char **out_buff) { - FILE *file = NULL; - struct stat stats = {0}; - VkResult res = VK_SUCCESS; - - file = fopen(filename, "rb"); - if (NULL == file) { - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to open JSON file %s", filename); - res = VK_ERROR_INITIALIZATION_FAILED; - goto out; - } - if (-1 == fstat(fileno(file), &stats)) { - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read file size of JSON file %s", filename); - res = VK_ERROR_INITIALIZATION_FAILED; - goto out; - } - *out_buff = (char *)loader_instance_heap_calloc(inst, stats.st_size + 1, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); - if (NULL == *out_buff) { - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to allocate memory to read JSON file %s", filename); - res = VK_ERROR_OUT_OF_HOST_MEMORY; - goto out; - } - if (stats.st_size != (long int)fread(*out_buff, sizeof(char), stats.st_size, file)) { - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read entire JSON file %s", filename); - res = VK_ERROR_INITIALIZATION_FAILED; - goto out; - } - (*out_buff)[stats.st_size] = '\0'; - -out: - if (NULL != file) { - fclose(file); - } - return res; -} -#else -#warning fopen not available on this platform -VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, char **out_buff) { - return VK_ERROR_INITIALIZATION_FAILED; -} -#endif - -VkResult loader_get_json(const struct loader_instance *inst, const char *filename, cJSON **json) { - char *json_buf = NULL; - VkResult res = VK_SUCCESS; - - assert(json != NULL); - - *json = NULL; - res = loader_read_entire_file(inst, filename, &json_buf); - if (VK_SUCCESS != res) goto out; - - // Parse text from file - bool out_of_memory = false; - *json = cJSON_Parse(inst ? &inst->alloc_callbacks : NULL, json_buf, &out_of_memory); - if (out_of_memory) { - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Out of Memory error occurred while parsing JSON file %s.", - filename); - res = VK_ERROR_OUT_OF_HOST_MEMORY; - goto out; - } else if (*json == NULL) { - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Invalid JSON file %s.", filename); - goto out; - } - -out: - loader_instance_heap_free(inst, json_buf); - if (res != VK_SUCCESS && *json != NULL) { - loader_cJSON_Delete(*json); - *json = NULL; - } - - return res; -} - -VkResult loader_parse_json_string_to_existing_str(const struct loader_instance *inst, cJSON *object, const char *key, - size_t out_str_len, char *out_string) { - cJSON *item = loader_cJSON_GetObjectItem(object, key); - if (NULL == item) { - return VK_ERROR_INITIALIZATION_FAILED; - } - - char *str = loader_cJSON_Print(item); - if (str == NULL) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - if (NULL != out_string) { - loader_strncpy(out_string, out_str_len, str, out_str_len); - if (out_str_len > 0) { - out_string[out_str_len - 1] = '\0'; - } - } - loader_instance_heap_free(inst, str); - return VK_SUCCESS; -} - -VkResult loader_parse_json_string(cJSON *object, const char *key, char **out_string) { - cJSON *item = loader_cJSON_GetObjectItem(object, key); - if (NULL == item) { - return VK_ERROR_INITIALIZATION_FAILED; - } - - char *str = loader_cJSON_Print(item); - if (str == NULL) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - if (NULL != out_string) { - *out_string = str; - } - return VK_SUCCESS; -} -VkResult loader_parse_json_array_of_strings(const struct loader_instance *inst, cJSON *object, const char *key, - struct loader_string_list *string_list) { - VkResult res = VK_SUCCESS; - cJSON *item = loader_cJSON_GetObjectItem(object, key); - if (NULL == item) { - return VK_ERROR_INITIALIZATION_FAILED; - } - - uint32_t count = loader_cJSON_GetArraySize(item); - if (count == 0) { - return VK_SUCCESS; - } - - res = create_string_list(inst, count, string_list); - if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { - goto out; - } - for (uint32_t i = 0; i < count; i++) { - cJSON *element = loader_cJSON_GetArrayItem(item, i); - if (element == NULL) { - return VK_ERROR_INITIALIZATION_FAILED; - } - char *out_data = loader_cJSON_Print(element); - if (out_data == NULL) { - res = VK_ERROR_OUT_OF_HOST_MEMORY; - goto out; - } - res = append_str_to_string_list(inst, string_list, out_data); - if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { - goto out; - } - } -out: - if (res == VK_ERROR_OUT_OF_HOST_MEMORY && NULL != string_list->list) { - free_string_list(inst, string_list); - } - - return res; -} diff --git a/loader/cJSON.h b/loader/cJSON.h index e02af8a71..673fa2f0c 100644 --- a/loader/cJSON.h +++ b/loader/cJSON.h @@ -26,7 +26,7 @@ #pragma once #include - +#include #include /* cJSON Types: */ @@ -62,6 +62,9 @@ typedef struct cJSON { VkAllocationCallbacks *pAllocator; } cJSON; +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. + * Call cJSON_Delete when finished. */ +cJSON *cJSON_Parse(const VkAllocationCallbacks *pAllocator, const char *value, bool *out_of_memory); /* Render a cJSON entity to text for transfer/storage. Free the char* when * finished. */ char *loader_cJSON_Print(cJSON *item); @@ -83,29 +86,3 @@ cJSON *loader_cJSON_GetObjectItem(cJSON *object, const char *string); * too. */ #define cJSON_SetIntValue(object, val) ((object) ? (object)->valueint = (object)->valuedouble = (val) : (val)) #define cJSON_SetNumberValue(object, val) ((object) ? (object)->valueint = (object)->valuedouble = (val) : (val)) - -// Helper functions to using JSON - -struct loader_instance; -struct loader_string_list; - -// Read a JSON file into a buffer. -// -// @return - A pointer to a cJSON object representing the JSON parse tree. -// This returned buffer should be freed by caller. -VkResult loader_get_json(const struct loader_instance *inst, const char *filename, cJSON **json); - -// Given a cJSON object, find the string associated with the key and puts an pre-allocated string into out_string. -// Length is given by out_str_len, and this function truncates the string with a null terminator if it the provided space isn't -// large enough. -VkResult loader_parse_json_string_to_existing_str(const struct loader_instance *inst, cJSON *object, const char *key, - size_t out_str_len, char *out_string); - -// Given a cJSON object, find the string associated with the key and puts an allocated string into out_string. -// It is the callers responsibility to free out_string. -VkResult loader_parse_json_string(cJSON *object, const char *key, char **out_string); - -// Given a cJSON object, find the array of strings associated with they key and writes the count into out_count and data into -// out_array_of_strings. It is the callers responsibility to free out_array_of_strings. -VkResult loader_parse_json_array_of_strings(const struct loader_instance *inst, cJSON *object, const char *key, - struct loader_string_list *string_list); diff --git a/loader/loader.c b/loader/loader.c index ca27fa648..6afa1de25 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -58,6 +58,7 @@ #include "cJSON.h" #include "debug_utils.h" #include "loader_environment.h" +#include "loader_json.h" #include "log.h" #include "unknown_function_handling.h" #include "vk_loader_platform.h" diff --git a/loader/loader_json.c b/loader/loader_json.c new file mode 100644 index 000000000..a92b90ebf --- /dev/null +++ b/loader/loader_json.c @@ -0,0 +1,242 @@ +/* + Copyright (c) 2015-2021 The Khronos Group Inc. + Copyright (c) 2015-2021 Valve Corporation + Copyright (c) 2015-2021 LunarG, Inc. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "loader_json.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "cJSON.h" + +#include "allocation.h" +#include "loader.h" +#include "log.h" + +#if COMMON_UNIX_PLATFORMS +#include +#include +#endif + +#ifdef _WIN32 +static VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, char **out_buff) { + HANDLE file_handle = INVALID_HANDLE_VALUE; + DWORD len = 0, read_len = 0; + VkResult res = VK_SUCCESS; + BOOL read_ok = false; + + int filename_utf16_size = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0); + if (filename_utf16_size > 0) { + wchar_t *filename_utf16 = (wchar_t *)loader_stack_alloc(filename_utf16_size * sizeof(wchar_t)); + if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_utf16, filename_utf16_size) == filename_utf16_size) { + file_handle = + CreateFileW(filename_utf16, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + } + } + if (INVALID_HANDLE_VALUE == file_handle) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to open JSON file %s", filename); + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + len = GetFileSize(file_handle, NULL); + if (INVALID_FILE_SIZE == len) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read file size of JSON file %s", filename); + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + *out_buff = (char *)loader_instance_heap_calloc(inst, len + 1, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); + if (NULL == *out_buff) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to allocate memory to read JSON file %s", filename); + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } + read_ok = ReadFile(file_handle, *out_buff, len, &read_len, NULL); + if (len != read_len || false == read_ok) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read entire JSON file %s", filename); + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + (*out_buff)[len] = '\0'; + +out: + if (INVALID_HANDLE_VALUE != file_handle) { + CloseHandle(file_handle); + } + return res; +} +#elif COMMON_UNIX_PLATFORMS +static VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, char **out_buff) { + FILE *file = NULL; + struct stat stats = {0}; + VkResult res = VK_SUCCESS; + + file = fopen(filename, "rb"); + if (NULL == file) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to open JSON file %s", filename); + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + if (-1 == fstat(fileno(file), &stats)) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read file size of JSON file %s", filename); + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + *out_buff = (char *)loader_instance_heap_calloc(inst, stats.st_size + 1, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); + if (NULL == *out_buff) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to allocate memory to read JSON file %s", filename); + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } + if (stats.st_size != (long int)fread(*out_buff, sizeof(char), stats.st_size, file)) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read entire JSON file %s", filename); + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + (*out_buff)[stats.st_size] = '\0'; + +out: + if (NULL != file) { + fclose(file); + } + return res; +} +#else +#warning fopen not available on this platform +VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, char **out_buff) { + return VK_ERROR_INITIALIZATION_FAILED; +} +#endif + +VkResult loader_get_json(const struct loader_instance *inst, const char *filename, cJSON **json) { + char *json_buf = NULL; + VkResult res = VK_SUCCESS; + + assert(json != NULL); + + *json = NULL; + res = loader_read_entire_file(inst, filename, &json_buf); + if (VK_SUCCESS != res) goto out; + + // Parse text from file + bool out_of_memory = false; + *json = cJSON_Parse(inst ? &inst->alloc_callbacks : NULL, json_buf, &out_of_memory); + if (out_of_memory) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Out of Memory error occurred while parsing JSON file %s.", + filename); + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } else if (*json == NULL) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Invalid JSON file %s.", filename); + goto out; + } + +out: + loader_instance_heap_free(inst, json_buf); + if (res != VK_SUCCESS && *json != NULL) { + loader_cJSON_Delete(*json); + *json = NULL; + } + + return res; +} + +VkResult loader_parse_json_string_to_existing_str(const struct loader_instance *inst, cJSON *object, const char *key, + size_t out_str_len, char *out_string) { + cJSON *item = loader_cJSON_GetObjectItem(object, key); + if (NULL == item) { + return VK_ERROR_INITIALIZATION_FAILED; + } + + char *str = loader_cJSON_Print(item); + if (str == NULL) { + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + if (NULL != out_string) { + loader_strncpy(out_string, out_str_len, str, out_str_len); + if (out_str_len > 0) { + out_string[out_str_len - 1] = '\0'; + } + } + loader_instance_heap_free(inst, str); + return VK_SUCCESS; +} + +VkResult loader_parse_json_string(cJSON *object, const char *key, char **out_string) { + cJSON *item = loader_cJSON_GetObjectItem(object, key); + if (NULL == item) { + return VK_ERROR_INITIALIZATION_FAILED; + } + + char *str = loader_cJSON_Print(item); + if (str == NULL) { + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + if (NULL != out_string) { + *out_string = str; + } + return VK_SUCCESS; +} +VkResult loader_parse_json_array_of_strings(const struct loader_instance *inst, cJSON *object, const char *key, + struct loader_string_list *string_list) { + VkResult res = VK_SUCCESS; + cJSON *item = loader_cJSON_GetObjectItem(object, key); + if (NULL == item) { + return VK_ERROR_INITIALIZATION_FAILED; + } + + uint32_t count = loader_cJSON_GetArraySize(item); + if (count == 0) { + return VK_SUCCESS; + } + + res = create_string_list(inst, count, string_list); + if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { + goto out; + } + for (uint32_t i = 0; i < count; i++) { + cJSON *element = loader_cJSON_GetArrayItem(item, i); + if (element == NULL) { + return VK_ERROR_INITIALIZATION_FAILED; + } + char *out_data = loader_cJSON_Print(element); + if (out_data == NULL) { + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } + res = append_str_to_string_list(inst, string_list, out_data); + if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { + goto out; + } + } +out: + if (res == VK_ERROR_OUT_OF_HOST_MEMORY && NULL != string_list->list) { + free_string_list(inst, string_list); + } + + return res; +} diff --git a/loader/loader_json.h b/loader/loader_json.h new file mode 100644 index 000000000..da9fe9e9a --- /dev/null +++ b/loader/loader_json.h @@ -0,0 +1,56 @@ +/* + Copyright (c) 2015-2021 The Khronos Group Inc. + Copyright (c) 2015-2021 Valve Corporation + Copyright (c) 2015-2021 LunarG, Inc. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#pragma once + +#include + +#include + +#include + +// Forward decls +struct loader_instance; +struct loader_string_list; + +// Read a JSON file into a buffer. +// +// @return - A pointer to a cJSON object representing the JSON parse tree. +// This returned buffer should be freed by caller. +VkResult loader_get_json(const struct loader_instance *inst, const char *filename, cJSON **json); + +// Given a cJSON object, find the string associated with the key and puts an pre-allocated string into out_string. +// Length is given by out_str_len, and this function truncates the string with a null terminator if it the provided space isn't +// large enough. +VkResult loader_parse_json_string_to_existing_str(const struct loader_instance *inst, cJSON *object, const char *key, + size_t out_str_len, char *out_string); + +// Given a cJSON object, find the string associated with the key and puts an allocated string into out_string. +// It is the callers responsibility to free out_string. +VkResult loader_parse_json_string(cJSON *object, const char *key, char **out_string); + +// Given a cJSON object, find the array of strings associated with they key and writes the count into out_count and data into +// out_array_of_strings. It is the callers responsibility to free out_array_of_strings. +VkResult loader_parse_json_array_of_strings(const struct loader_instance *inst, cJSON *object, const char *key, + struct loader_string_list *string_list); diff --git a/loader/settings.c b/loader/settings.c index 0b451b3ed..ea0273701 100644 --- a/loader/settings.c +++ b/loader/settings.c @@ -27,6 +27,7 @@ #include "cJSON.h" #include "loader.h" #include "loader_environment.h" +#include "loader_json.h" #if defined(WIN32) #include "loader_windows.h" #endif From 6aecffcd3155ea402f4dd94a90467f13a5981c02 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Fri, 22 Nov 2024 15:19:06 -0600 Subject: [PATCH 03/23] Update cJSON to latest version Sets CJSON_HIDE_SYMBOLS since that is the default we want. --- loader/cJSON.c | 3243 ++++++++++++++++++++++++++++++++++++------------ loader/cJSON.h | 334 ++++- 2 files changed, 2755 insertions(+), 822 deletions(-) diff --git a/loader/cJSON.c b/loader/cJSON.c index a26eab212..71a932b58 100644 --- a/loader/cJSON.c +++ b/loader/cJSON.c @@ -1,8 +1,5 @@ /* - Copyright (c) 2009 Dave Gamble - Copyright (c) 2015-2021 The Khronos Group Inc. - Copyright (c) 2015-2021 Valve Corporation - Copyright (c) 2015-2021 LunarG, Inc. + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -26,931 +23,2635 @@ /* cJSON */ /* JSON parser in C. */ -#include -#include -#include +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning(push) +/* disable warning about single line comments in system headers */ +#pragma warning(disable : 4001) +#endif + +#include #include +#include #include -#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif #include "cJSON.h" -#include "allocation.h" -#include "loader.h" -#include "log.h" +/* define our own boolean type */ +#ifdef true +#undef true +#endif +#define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif +#define false ((cJSON_bool)0) + +/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ +#ifndef isinf +#define isinf(d) (isnan((d - d)) && !isnan(d)) +#endif +#ifndef isnan +#define isnan(d) (d != d) +#endif + +#ifndef NAN +#ifdef _WIN32 +#define NAN sqrt(-1.0) +#else +#define NAN 0.0 / 0.0 +#endif +#endif + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = {NULL, 0}; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) { return (const char *)(global_error.json + global_error.position); } + +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON *const item) { + if (!cJSON_IsString(item)) { + return NULL; + } + + return item->valuestring; +} + +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON *const item) { + if (!cJSON_IsNumber(item)) { + return (double)NAN; + } + + return item->valuedouble; +} -#if COMMON_UNIX_PLATFORMS -#include -#include +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 18) +#error cJSON.h and cJSON.c have different versions. Make sure that both have the same. #endif -static void *cJSON_malloc(const VkAllocationCallbacks *pAllocator, size_t size) { - return loader_calloc(pAllocator, size, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); +CJSON_PUBLIC(const char *) cJSON_Version(void) { + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; } -static void *cJSON_malloc_instance_scope(const VkAllocationCallbacks *pAllocator, size_t size) { - return loader_calloc(pAllocator, size, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) { + if ((string1 == NULL) || (string2 == NULL)) { + return 1; + } + + if (string1 == string2) { + return 0; + } + + for (; tolower(*string1) == tolower(*string2); (void)string1++, string2++) { + if (*string1 == '\0') { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); } -static void cJSON_Free(const VkAllocationCallbacks *pAllocator, void *pMemory) { loader_free(pAllocator, pMemory); } +typedef struct internal_hooks { + void *(CJSON_CDECL *allocate)(size_t size); + void(CJSON_CDECL *deallocate)(void *pointer); + void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ +static void *CJSON_CDECL internal_malloc(size_t size) { return malloc(size); } +static void CJSON_CDECL internal_free(void *pointer) { free(pointer); } +static void *CJSON_CDECL internal_realloc(void *pointer, size_t size) { return realloc(pointer, size); } +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif -/* -// commented out as it is unused - static error code channel requires external locks to be used. -static const char *ep; +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) -const char *cJSON_GetErrorPtr(void) { return ep; } -*/ +static internal_hooks global_hooks = {internal_malloc, internal_free, internal_realloc}; + +static unsigned char *cJSON_strdup(const unsigned char *string, const internal_hooks *const hooks) { + size_t length = 0; + unsigned char *copy = NULL; -static char *cJSON_strdup(const VkAllocationCallbacks *pAllocator, const char *str) { - size_t len; - char *copy; + if (string == NULL) { + return NULL; + } + + length = strlen((const char *)string) + sizeof(""); + copy = (unsigned char *)hooks->allocate(length); + if (copy == NULL) { + return NULL; + } + memcpy(copy, string, length); - len = strlen(str) + 1; - copy = (char *)cJSON_malloc(pAllocator, len); - if (!copy) return 0; - memcpy(copy, str, len); return copy; } +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks *hooks) { + if (hooks == NULL) { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) { + global_hooks.reallocate = realloc; + } +} + /* Internal constructor. */ -static cJSON *cJSON_New_Item(const VkAllocationCallbacks *pAllocator) { - cJSON *node = (cJSON *)cJSON_malloc(pAllocator, sizeof(cJSON)); +static cJSON *cJSON_New_Item(const internal_hooks *const hooks) { + cJSON *node = (cJSON *)hooks->allocate(sizeof(cJSON)); if (node) { - memset(node, 0, sizeof(cJSON)); - node->pAllocator = (VkAllocationCallbacks *)pAllocator; + memset(node, '\0', sizeof(cJSON)); } + return node; } /* Delete a cJSON structure. */ -void loader_cJSON_Delete(cJSON *c) { - cJSON *next; - while (c) { - next = c->next; - if (!(c->type & cJSON_IsReference) && c->child) loader_cJSON_Delete(c->child); - if (!(c->type & cJSON_IsReference) && c->valuestring) cJSON_Free(c->pAllocator, c->valuestring); - if (!(c->type & cJSON_StringIsConst) && c->string) cJSON_Free(c->pAllocator, c->string); - cJSON_Free(c->pAllocator, c); - c = next; - } -} - -/* Parse the input text to generate a number, and populate the result into item. - */ -static const char *parse_number(cJSON *item, const char *num) { - double n = 0, sign = 1, scale = 0; - int subscale = 0, signsubscale = 1; - - if (*num == '-') sign = -1, num++; /* Has sign? */ - if (*num == '0') num++; /* is zero */ - if (*num >= '1' && *num <= '9') do - n = (n * 10.0) + (*num++ - '0'); - while (*num >= '0' && *num <= '9'); /* Number? */ - if (*num == '.' && num[1] >= '0' && num[1] <= '9') { - num++; - do n = (n * 10.0) + (*num++ - '0'), scale--; - while (*num >= '0' && *num <= '9'); - } /* Fractional part? */ - if (*num == 'e' || *num == 'E') /* Exponent? */ - { - num++; - if (*num == '+') - num++; - else if (*num == '-') - signsubscale = -1, num++; /* With sign? */ - while (*num >= '0' && *num <= '9') subscale = (subscale * 10) + (*num++ - '0'); /* Number? */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) { + cJSON *next = NULL; + while (item != NULL) { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) { + global_hooks.deallocate(item->valuestring); + item->valuestring = NULL; + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { + global_hooks.deallocate(item->string); + item->string = NULL; + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) { +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char)lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct { + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON *const item, parse_buffer *const input_buffer) { + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) { + switch (buffer_at_offset(input_buffer)[i]) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char *)number_c_string, (char **)&after_end); + if (number_c_string == after_end) { + return false; /* parse_error */ } - n = sign * n * pow(10.0, (scale + subscale * signsubscale)); /* number = +/- - number.fraction * - 10^+/- exponent */ + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) { + item->valueint = INT_MAX; + } else if (number <= (double)INT_MIN) { + item->valueint = INT_MIN; + } else { + item->valueint = (int)number; + } - item->valuedouble = n; - item->valueint = (int)n; item->type = cJSON_Number; - return num; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) { + if (number >= INT_MAX) { + object->valueint = INT_MAX; + } else if (number <= (double)INT_MIN) { + object->valueint = INT_MIN; + } else { + object->valueint = (int)number; + } + + return object->valuedouble = number; } -static size_t pow2gt(size_t x) { - --x; - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - x |= x >> 8; - x |= x >> 16; - return x + 1; +/* Note: when passing a NULL valuestring, cJSON_SetValuestring treats this as an error and return NULL */ +CJSON_PUBLIC(char *) cJSON_SetValuestring(cJSON *object, const char *valuestring) { + char *copy = NULL; + size_t v1_len; + size_t v2_len; + /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ + if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference)) { + return NULL; + } + /* return NULL if the object is corrupted or valuestring is NULL */ + if (object->valuestring == NULL || valuestring == NULL) { + return NULL; + } + + v1_len = strlen(valuestring); + v2_len = strlen(object->valuestring); + + if (v1_len <= v2_len) { + /* strcpy does not handle overlapping string: [X1, X2] [Y1, Y2] => X2 < Y1 or Y2 < X1 */ + if (!(valuestring + v1_len < object->valuestring || object->valuestring + v2_len < valuestring)) { + return NULL; + } + strcpy(object->valuestring, valuestring); + return object->valuestring; + } + copy = (char *)cJSON_strdup((const unsigned char *)valuestring, &global_hooks); + if (copy == NULL) { + return NULL; + } + if (object->valuestring != NULL) { + cJSON_free(object->valuestring); + } + object->valuestring = copy; + + return copy; } typedef struct { - char *buffer; + unsigned char *buffer; size_t length; size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; } printbuffer; -static char *ensure(const VkAllocationCallbacks *pAllocator, printbuffer *p, size_t needed) { - char *newbuffer; - size_t newsize; - if (!p || !p->buffer) return 0; - needed += p->offset; - if (needed <= p->length) return p->buffer + p->offset; - - newsize = pow2gt(needed); - newbuffer = (char *)cJSON_malloc(pAllocator, newsize); - if (!newbuffer) { - cJSON_Free(pAllocator, p->buffer); - p->length = 0, p->buffer = 0; - return 0; +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char *ensure(printbuffer *const p, size_t needed) { + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) { + newsize = INT_MAX; + } else { + return NULL; + } + } else { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) { + /* reallocate with realloc if available */ + newbuffer = (unsigned char *)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } else { + /* otherwise reallocate manually */ + newbuffer = (unsigned char *)p->hooks.allocate(newsize); + if (!newbuffer) { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + + memcpy(newbuffer, p->buffer, p->offset + 1); + p->hooks.deallocate(p->buffer); } - if (newbuffer) memcpy(newbuffer, p->buffer, p->length); - cJSON_Free(pAllocator, p->buffer); p->length = newsize; p->buffer = newbuffer; + return newbuffer + p->offset; } -static size_t cJSON_update(printbuffer *p) { - char *str; - if (!p || !p->buffer) return 0; - str = p->buffer + p->offset; - return p->offset + strlen(str); +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer *const buffer) { + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char *)buffer_pointer); +} + +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) { + double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + return (fabs(a - b) <= maxVal * DBL_EPSILON); } /* Render the number nicely from the given item into a string. */ -static char *print_number(cJSON *item, printbuffer *p) { - char *str = 0; - size_t str_buf_size; +static cJSON_bool print_number(const cJSON *const item, printbuffer *const output_buffer) { + unsigned char *output_pointer = NULL; double d = item->valuedouble; - if (d == 0) { - str_buf_size = 2; /* special case for 0. */ - if (p) - str = ensure(item->pAllocator, p, str_buf_size); - else - str = (char *)cJSON_malloc(item->pAllocator, str_buf_size); - if (str) loader_strncpy(str, str_buf_size, "0", 2); - } else if (fabs(((double)item->valueint) - d) <= DBL_EPSILON && d <= INT_MAX && d >= INT_MIN) { - str_buf_size = 21; /* 2^64+1 can be represented in 21 chars. */ - if (p) - str = ensure(item->pAllocator, p, str_buf_size); - else - str = (char *)cJSON_malloc(item->pAllocator, str_buf_size); - if (str) snprintf(str, str_buf_size, "%d", item->valueint); + int length = 0; + size_t i = 0; + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test = 0.0; + + if (output_buffer == NULL) { + return false; + } + + /* This checks for NaN and Infinity */ + if (isnan(d) || isinf(d)) { + length = sprintf((char *)number_buffer, "null"); + } else if (d == (double)item->valueint) { + length = sprintf((char *)number_buffer, "%d", item->valueint); } else { - str_buf_size = 64; /* This is a nice tradeoff. */ - if (p) - str = ensure(item->pAllocator, p, str_buf_size); - else - str = (char *)cJSON_malloc(item->pAllocator, str_buf_size); - if (str) { - if (fabs(floor(d) - d) <= DBL_EPSILON && fabs(d) < 1.0e60) - snprintf(str, str_buf_size, "%.0f", d); - else if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9) - snprintf(str, str_buf_size, "%e", d); - else - snprintf(str, str_buf_size, "%f", d); - } - } - return str; -} - -static unsigned parse_hex4(const char *str) { - unsigned h = 0; - if (*str >= '0' && *str <= '9') - h += (*str) - '0'; - else if (*str >= 'A' && *str <= 'F') - h += 10 + (*str) - 'A'; - else if (*str >= 'a' && *str <= 'f') - h += 10 + (*str) - 'a'; - else - return 0; - h = h << 4; - str++; - if (*str >= '0' && *str <= '9') - h += (*str) - '0'; - else if (*str >= 'A' && *str <= 'F') - h += 10 + (*str) - 'A'; - else if (*str >= 'a' && *str <= 'f') - h += 10 + (*str) - 'a'; - else - return 0; - h = h << 4; - str++; - if (*str >= '0' && *str <= '9') - h += (*str) - '0'; - else if (*str >= 'A' && *str <= 'F') - h += 10 + (*str) - 'A'; - else if (*str >= 'a' && *str <= 'f') - h += 10 + (*str) - 'a'; - else - return 0; - h = h << 4; - str++; - if (*str >= '0' && *str <= '9') - h += (*str) - '0'; - else if (*str >= 'A' && *str <= 'F') - h += 10 + (*str) - 'A'; - else if (*str >= 'a' && *str <= 'f') - h += 10 + (*str) - 'a'; - else - return 0; + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char *)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char *)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char *)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occurred */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if (output_pointer == NULL) { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) { + if (number_buffer[i] == decimal_point) { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char *const input) { + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) { + h += (unsigned int)input[i] - '0'; + } else if ((input[i] >= 'A') && (input[i] <= 'F')) { + h += (unsigned int)10 + input[i] - 'A'; + } else if ((input[i] >= 'a') && (input[i] <= 'f')) { + h += (unsigned int)10 + input[i] - 'a'; + } else /* invalid */ + { + return 0; + } + + if (i < 3) { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + return h; } -/* Parse the input text into an unescaped cstring, and populate item. */ -static const unsigned char firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC}; -static const char *parse_string(cJSON *item, const char *str, bool *out_of_memory) { - const char *ptr = str + 1; - char *ptr2; - char *out; - int len = 0; - unsigned uc, uc2; - if (*str != '\"') { - // ep = str; // commented out as it is unused - return 0; - } /* not a string! */ +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char *const input_pointer, const unsigned char *const input_end, + unsigned char **output_pointer) { + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) { + /* input ends unexpectedly */ + goto fail; + } - while (*ptr != '\"' && *ptr && ++len) - if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); - out = (char *)cJSON_malloc(item->pAllocator, len + 1); /* This is how long we need for the string, roughly. */ - if (!out) { - *out_of_memory = true; - return 0; + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) { + /* invalid second half of the surrogate pair */ + goto fail; + } + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } else { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } else if (codepoint < 0x800) { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } else if (codepoint < 0x10000) { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } else if (codepoint <= 0x10FFFF) { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } else { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } else { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON *const item, parse_buffer *const input_buffer) { + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) { + /* is escape sequence */ + if (input_end[0] == '\\') { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t)(input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char *)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) { + goto fail; /* allocation failure */ + } } - ptr = str + 1; - ptr2 = out; - while (*ptr != '\"' && *ptr) { - if (*ptr != '\\') - *ptr2++ = *ptr++; + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) { + if (*input_pointer != '\\') { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ else { - ptr++; - switch (*ptr) { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) { + goto fail; + } + + switch (input_pointer[1]) { case 'b': - *ptr2++ = '\b'; + *output_pointer++ = '\b'; break; case 'f': - *ptr2++ = '\f'; + *output_pointer++ = '\f'; break; case 'n': - *ptr2++ = '\n'; + *output_pointer++ = '\n'; break; case 'r': - *ptr2++ = '\r'; + *output_pointer++ = '\r'; break; case 't': - *ptr2++ = '\t'; + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; break; - case 'u': /* transcode utf16 to utf8. */ - uc = parse_hex4(ptr + 1); - ptr += 4; /* get the unicode char. */ - - if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) break; /* check for invalid. */ - - if (uc >= 0xD800 && uc <= 0xDBFF) /* UTF16 surrogate pairs. */ - { - if (ptr[1] != '\\' || ptr[2] != 'u') break; /* missing second-half of surrogate. */ - uc2 = parse_hex4(ptr + 3); - ptr += 6; - if (uc2 < 0xDC00 || uc2 > 0xDFFF) break; /* invalid second-half of surrogate. */ - uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF)); - } - len = 4; - if (uc < 0x80) - len = 1; - else if (uc < 0x800) - len = 2; - else if (uc < 0x10000) - len = 3; - ptr2 += len; - - for (size_t i = len; i > 0; i--) { - if (i == 1) { - *--ptr2 = ((unsigned char)uc | firstByteMark[len]); - } else if (i >= 2) { - *--ptr2 = ((uc | 0x80) & 0xBF); - uc >>= 6; - } + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; } - ptr2 += len; break; + default: - *ptr2++ = *ptr; - break; + goto fail; } - ptr++; + input_pointer += sequence_length; } } - *ptr2 = 0; - if (*ptr == '\"') ptr++; - item->valuestring = out; + + /* zero terminate the output */ + *output_pointer = '\0'; + item->type = cJSON_String; - return ptr; + item->valuestring = (char *)output; + + input_buffer->offset = (size_t)(input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) { + input_buffer->hooks.deallocate(output); + output = NULL; + } + + if (input_pointer != NULL) { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; } /* Render the cstring provided to an escaped version that can be printed. */ -static char *print_string_ptr(const VkAllocationCallbacks *pAllocator, const char *str, printbuffer *p) { - const char *ptr; - char *ptr2; - char *out; - size_t out_buf_size, len = 0, flag = 0; - unsigned char token; - - for (ptr = str; *ptr; ptr++) flag |= ((*ptr > 0 && *ptr < 32) || (*ptr == '\"') || (*ptr == '\\')) ? 1 : 0; - if (!flag) { - len = ptr - str; - out_buf_size = len + 1; - // out_buf_size = len + 3; // Modified to not put quotes around the string - if (p) - out = ensure(pAllocator, p, out_buf_size); - else - out = (char *)cJSON_malloc_instance_scope(pAllocator, out_buf_size); - if (!out) return 0; - ptr2 = out; - // *ptr2++ = '\"'; // Modified to not put quotes around the string - loader_strncpy(ptr2, out_buf_size, str, out_buf_size); - // ptr2[len] = '\"'; // Modified to not put quotes around the string - ptr2[len] = 0; // ptr2[len + 1] = 0; // Modified to not put quotes around the string - return out; - } - - if (!str) { - out_buf_size = 3; - if (p) - out = ensure(pAllocator, p, out_buf_size); - else - out = (char *)cJSON_malloc_instance_scope(pAllocator, out_buf_size); - if (!out) return 0; - loader_strncpy(out, out_buf_size, "\"\"", 3); - return out; - } - ptr = str; - token = *ptr; - while (token && ++len) { - if (strchr("\"\\\b\f\n\r\t", token)) - len++; - else if (token < 32) - len += 5; - ptr++; - token = *ptr; - } - - out_buf_size = len + 1; - // out_buf_size = len + 3; // Modified to not put quotes around the string - if (p) - out = ensure(pAllocator, p, out_buf_size); - else - out = (char *)cJSON_malloc_instance_scope(pAllocator, out_buf_size); - if (!out) return 0; - - ptr2 = out; - ptr = str; - // *ptr2++ = '\"'; // Modified to not put quotes around the string - while (*ptr) { - if ((unsigned char)*ptr > 31 && *ptr != '\"' && *ptr != '\\') - *ptr2++ = *ptr++; - else { - switch (token = *ptr++) { +static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer *const output_buffer) { + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) { + return false; + } + + /* empty string */ + if (input == NULL) { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) { + return false; + } + strcpy((char *)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) { + switch (*input_pointer) { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) { + /* normal character, copy */ + *output_pointer = *input_pointer; + } else { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) { case '\\': - *ptr2++ = '\\'; + *output_pointer = '\\'; break; case '\"': - *ptr2++ = '\"'; + *output_pointer = '\"'; break; case '\b': - *ptr2++ = '\b'; + *output_pointer = 'b'; break; case '\f': - *ptr2++ = '\f'; + *output_pointer = 'f'; break; case '\n': - *ptr2++ = '\n'; + *output_pointer = 'n'; break; case '\r': - *ptr2++ = '\r'; + *output_pointer = 'r'; break; case '\t': - *ptr2++ = '\t'; + *output_pointer = 't'; break; default: - snprintf(ptr2, out_buf_size - (ptr2 - out), "u%04x", token); - ptr2 += 5; - break; /* escape and print */ + /* escape and print as unicode codepoint */ + sprintf((char *)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; } } } - // *ptr2++ = '\"'; // Modified to not put quotes around the string - *ptr2++ = 0; - return out; + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; } + /* Invoke print_string_ptr (which is useful) on an item. */ -static char *print_string(cJSON *item, printbuffer *p) { return print_string_ptr(item->pAllocator, item->valuestring, p); } +static cJSON_bool print_string(const cJSON *const item, printbuffer *const p) { + return print_string_ptr((unsigned char *)item->valuestring, p); +} -/* Declare these prototypes. */ -static const char *parse_value(cJSON *item, const char *value, bool *out_of_memory); -static char *print_value(cJSON *item, int depth, int fmt, printbuffer *p); -static const char *parse_array(cJSON *item, const char *value, bool *out_of_memory); -static char *print_array(cJSON *item, int depth, int fmt, printbuffer *p); -static const char *parse_object(cJSON *item, const char *value, bool *out_of_memory); -static char *print_object(cJSON *item, int depth, int fmt, printbuffer *p); +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON *const item, parse_buffer *const input_buffer); +static cJSON_bool print_value(const cJSON *const item, printbuffer *const output_buffer); +static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffer); +static cJSON_bool print_array(const cJSON *const item, printbuffer *const output_buffer); +static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buffer); +static cJSON_bool print_object(const cJSON *const item, printbuffer *const output_buffer); /* Utility to jump whitespace and cr/lf */ -static const char *skip(const char *in) { - while (in && *in && (unsigned char)*in <= 32) in++; - return in; -} +static parse_buffer *buffer_skip_whitespace(parse_buffer *const buffer) { + if ((buffer == NULL) || (buffer->content == NULL)) { + return NULL; + } -/* Parse an object - create a new root, and populate. */ -static cJSON *cJSON_ParseWithOpts(const VkAllocationCallbacks *pAllocator, const char *value, const char **return_parse_end, - int require_null_terminated, bool *out_of_memory) { - const char *end = 0; - cJSON *c = cJSON_New_Item(pAllocator); - // ep = 0; // commented out as it is unused - if (!c) { - *out_of_memory = true; - return 0; /* memory fail */ - } - - end = parse_value(c, skip(value), out_of_memory); - if (!end) { - loader_cJSON_Delete(c); - return 0; - } /* parse failure. ep is set. */ + if (cannot_access_at_index(buffer, 0)) { + return buffer; + } - /* if we require null-terminated JSON without appended garbage, skip and - * then check for a null terminator */ - if (require_null_terminated) { - end = skip(end); - if (*end) { - loader_cJSON_Delete(c); - // ep = end; // commented out as it is unused - return 0; - } + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) { + buffer->offset++; } - if (return_parse_end) *return_parse_end = end; - return c; -} -/* Default options for cJSON_Parse */ -cJSON *cJSON_Parse(const VkAllocationCallbacks *pAllocator, const char *value, bool *out_of_memory) { - return cJSON_ParseWithOpts(pAllocator, value, 0, 0, out_of_memory); + + if (buffer->offset == buffer->length) { + buffer->offset--; + } + + return buffer; } -/* Render a cJSON item/entity/structure to text. */ -char *loader_cJSON_Print(cJSON *item) { return print_value(item, 0, 1, 0); } -char *loader_cJSON_PrintUnformatted(cJSON *item) { return print_value(item, 0, 0, 0); } +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer *const buffer) { + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) { + return NULL; + } -/* Parser core - when encountering text, process appropriately. */ -static const char *parse_value(cJSON *item, const char *value, bool *out_of_memory) { - if (!value) return 0; /* Fail on null. */ - if (!strncmp(value, "null", 4)) { - item->type = cJSON_NULL; - return value + 4; + if (can_access_at_index(buffer, 4) && (strncmp((const char *)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) { + buffer->offset += 3; } - if (!strncmp(value, "false", 5)) { - item->type = cJSON_False; - return value + 5; + + return buffer; +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) { + size_t buffer_length; + + if (NULL == value) { + return NULL; } - if (!strncmp(value, "true", 4)) { - item->type = cJSON_True; + + /* Adding null character size due to require_null_terminated. */ + buffer_length = strlen(value) + sizeof(""); + + return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) +cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, + cJSON_bool require_null_terminated) { + parse_buffer buffer = {0, 0, 0, 0, {0, 0, 0}}; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL || 0 == buffer_length) { + goto fail; + } + + buffer.content = (const unsigned char *)value; + buffer.length = buffer_length; + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') { + goto fail; + } + } + if (return_parse_end) { + *return_parse_end = (const char *)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) { + cJSON_Delete(item); + } + + if (value != NULL) { + error local_error; + local_error.json = (const unsigned char *)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) { + local_error.position = buffer.offset; + } else if (buffer.length > 0) { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) { + *return_parse_end = (const char *)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) { return cJSON_ParseWithOpts(value, 0, 0); } + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) { + return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); +} + +#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) + +static unsigned char *print(const cJSON *const item, cJSON_bool format, const internal_hooks *const hooks) { + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char *)hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) { + printed = (unsigned char *)hooks->reallocate(buffer->buffer, buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char *)hooks->allocate(buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + buffer->buffer = NULL; + } + + return printed; + +fail: + if (buffer->buffer != NULL) { + hooks->deallocate(buffer->buffer); + buffer->buffer = NULL; + } + + if (printed != NULL) { + hooks->deallocate(printed); + printed = NULL; + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) { return (char *)print(item, true, &global_hooks); } + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) { return (char *)print(item, false, &global_hooks); } + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) { + printbuffer p = {0, 0, 0, 0, 0, 0, {0, 0, 0}}; + + if (prebuffer < 0) { + return NULL; + } + + p.buffer = (unsigned char *)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) { + global_hooks.deallocate(p.buffer); + p.buffer = NULL; + return NULL; + } + + return (char *)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) { + printbuffer p = {0, 0, 0, 0, 0, 0, {0, 0, 0}}; + + if ((length < 0) || (buffer == NULL)) { + return false; + } + + p.buffer = (unsigned char *)buffer; + p.length = (size_t)length; + p.offset = 0; + p.noalloc = true; + p.format = format; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON *const item, parse_buffer *const input_buffer) { + if ((input_buffer == NULL) || (input_buffer->content == NULL)) { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char *)buffer_at_offset(input_buffer), "null", 4) == 0)) { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char *)buffer_at_offset(input_buffer), "false", 5) == 0)) { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char *)buffer_at_offset(input_buffer), "true", 4) == 0)) { + item->type = cJSON_True; item->valueint = 1; - return value + 4; + input_buffer->offset += 4; + return true; } - if (*value == '\"') { - return parse_string(item, value, out_of_memory); + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) { + return parse_string(item, input_buffer); } - if (*value == '-' || (*value >= '0' && *value <= '9')) { - return parse_number(item, value); + /* number */ + if (can_access_at_index(input_buffer, 0) && + ((buffer_at_offset(input_buffer)[0] == '-') || + ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) { + return parse_number(item, input_buffer); } - if (*value == '[') { - return parse_array(item, value, out_of_memory); + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) { + return parse_array(item, input_buffer); } - if (*value == '{') { - return parse_object(item, value, out_of_memory); + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) { + return parse_object(item, input_buffer); } - // ep = value; // commented out as it is unused - return 0; /* failure. */ + return false; } /* Render a value to text. */ -static char *print_value(cJSON *item, int depth, int fmt, printbuffer *p) { - char *out = 0; - if (!item) return 0; - if (p) { - switch ((item->type) & 255) { - case cJSON_NULL: { - out = ensure(item->pAllocator, p, 5); - if (out) loader_strncpy(out, 5, "null", 5); - break; +static cJSON_bool print_value(const cJSON *const item, printbuffer *const output_buffer) { + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) { + return false; + } + + switch ((item->type) & 0xFF) { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) { + return false; } - case cJSON_False: { - out = ensure(item->pAllocator, p, 6); - if (out) loader_strncpy(out, 6, "false", 6); - break; + strcpy((char *)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) { + return false; } - case cJSON_True: { - out = ensure(item->pAllocator, p, 5); - if (out) loader_strncpy(out, 5, "true", 5); - break; + strcpy((char *)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) { + return false; } - case cJSON_Number: - out = print_number(item, p); - break; - case cJSON_String: - out = print_string(item, p); - break; - case cJSON_Array: - out = print_array(item, depth, fmt, p); - break; - case cJSON_Object: - out = print_object(item, depth, fmt, p); - break; - } - } else { - switch ((item->type) & 255) { - case cJSON_NULL: - out = cJSON_strdup(item->pAllocator, "null"); - break; - case cJSON_False: - out = cJSON_strdup(item->pAllocator, "false"); - break; - case cJSON_True: - out = cJSON_strdup(item->pAllocator, "true"); - break; - case cJSON_Number: - out = print_number(item, 0); - break; - case cJSON_String: - out = print_string(item, 0); - break; - case cJSON_Array: - out = print_array(item, depth, fmt, 0); - break; - case cJSON_Object: - out = print_object(item, depth, fmt, 0); - break; + strcpy((char *)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: { + size_t raw_length = 0; + if (item->valuestring == NULL) { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; } - return out; } /* Build an array from input text. */ -static const char *parse_array(cJSON *item, const char *value, bool *out_of_memory) { - cJSON *child; - if (*value != '[') { - // ep = value; // commented out as it is unused - return 0; - } /* not an array! */ +static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffer) { + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; - item->type = cJSON_Array; - value = skip(value + 1); - if (*value == ']') return value + 1; /* empty array. */ + if (input_buffer->depth >= CJSON_NESTING_LIMIT) { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') { + /* not an array */ + goto fail; + } - item->child = child = cJSON_New_Item(item->pAllocator); - if (!item->child) { - *out_of_memory = true; - return 0; /* memory fail */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) { + /* empty array */ + goto success; } - value = skip(parse_value(child, skip(value), out_of_memory)); /* skip any spacing, get the value. */ - if (!value) return 0; - while (*value == ',') { - cJSON *new_item; - new_item = cJSON_New_Item(item->pAllocator); - if (!new_item) { - *out_of_memory = true; - return 0; /* memory fail */ + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) { + /* start the linked list */ + current_item = head = new_item; + } else { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; } - child->next = new_item; - new_item->prev = child; - child = new_item; - value = skip(parse_value(child, skip(value + 1), out_of_memory)); - if (!value) return 0; /* memory fail */ + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) { + cJSON_Delete(head); } - if (*value == ']') return value + 1; /* end of array */ - // ep = value; // commented out as it is unused - return 0; /* malformed. */ + return false; } /* Render an array to text */ -static char *print_array(cJSON *item, int depth, int fmt, printbuffer *p) { - char **entries; - char *out = 0, *ptr, *ret; - size_t len = 5; - cJSON *child = item->child; - int numentries = 0, fail = 0, j = 0; - size_t tmplen = 0, i = 0; - - /* How many entries in the array? */ - while (child) numentries++, child = child->next; - /* Explicitly handle numentries==0 */ - if (!numentries) { - if (p) - out = ensure(item->pAllocator, p, 3); - else - out = (char *)cJSON_malloc(item->pAllocator, 3); - if (out) loader_strncpy(out, 3, "[]", 3); - return out; - } - - if (p) { - /* Compose the output array. */ - i = p->offset; - ptr = ensure(item->pAllocator, p, 1); - if (!ptr) return 0; - *ptr = '['; - p->offset++; - child = item->child; - while (child && !fail) { - print_value(child, depth + 1, fmt, p); - p->offset = cJSON_update(p); - if (child->next) { - len = fmt ? 2 : 1; - ptr = ensure(item->pAllocator, p, len + 1); - if (!ptr) return 0; - *ptr++ = ','; - if (fmt) *ptr++ = ' '; - *ptr = 0; - p->offset += len; - } - child = child->next; - } - ptr = ensure(item->pAllocator, p, 2); - if (!ptr) return 0; - *ptr++ = ']'; - *ptr = 0; - out = (p->buffer) + i; - } else { - /* Allocate an array to hold the values for each */ - entries = (char **)cJSON_malloc(item->pAllocator, numentries * sizeof(char *)); - if (!entries) return 0; - memset(entries, 0, numentries * sizeof(char *)); - /* Retrieve all the results: */ - child = item->child; - while (child && !fail) { - ret = print_value(child, depth + 1, fmt, 0); - entries[i++] = ret; - if (ret) - len += strlen(ret) + 2 + (fmt ? 1 : 0); - else - fail = 1; - child = child->next; - } - - /* If we didn't fail, try to malloc the output string */ - if (!fail) out = (char *)cJSON_malloc(item->pAllocator, len); - /* If that fails, we fail. */ - if (!out) fail = 1; - - /* Handle failure. */ - if (fail) { - for (j = 0; j < numentries; j++) - if (entries[j]) cJSON_Free(item->pAllocator, entries[j]); - cJSON_Free(item->pAllocator, entries); - return 0; - } +static cJSON_bool print_array(const cJSON *const item, printbuffer *const output_buffer) { + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; - /* Compose the output array. */ - *out = '['; - ptr = out + 1; - *ptr = 0; - for (j = 0; j < numentries; j++) { - tmplen = strlen(entries[j]); - memcpy(ptr, entries[j], tmplen); - ptr += tmplen; - if (j != numentries - 1) { - *ptr++ = ','; - if (fmt) *ptr++ = ' '; - *ptr = 0; + while (current_element != NULL) { + if (!print_value(current_element, output_buffer)) { + return false; + } + update_offset(output_buffer); + if (current_element->next) { + length = (size_t)(output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) { + return false; + } + *output_pointer++ = ','; + if (output_buffer->format) { + *output_pointer++ = ' '; } - cJSON_Free(item->pAllocator, entries[j]); + *output_pointer = '\0'; + output_buffer->offset += length; } - cJSON_Free(item->pAllocator, entries); - *ptr++ = ']'; - *ptr++ = 0; + current_element = current_element->next; } - return out; + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; } /* Build an object from the text. */ -static const char *parse_object(cJSON *item, const char *value, bool *out_of_memory) { - cJSON *child; - if (*value != '{') { - // ep = value; // commented out as it is unused - return 0; - } /* not an object! */ +static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buffer) { + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; - item->type = cJSON_Object; - value = skip(value + 1); - if (*value == '}') return value + 1; /* empty array. */ + if (input_buffer->depth >= CJSON_NESTING_LIMIT) { + return false; /* to deeply nested */ + } + input_buffer->depth++; - item->child = child = cJSON_New_Item(item->pAllocator); - if (!item->child) { - *out_of_memory = true; - return 0; + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) { + goto fail; /* not an object */ } - value = skip(parse_string(child, skip(value), out_of_memory)); - if (!value) return 0; - child->string = child->valuestring; - child->valuestring = 0; - if (*value != ':') { - // ep = value; // commented out as it is unused - return 0; - } /* fail! */ - value = skip(parse_value(child, skip(value + 1), out_of_memory)); /* skip any spacing, get the value. */ - if (!value) return 0; - - while (*value == ',') { - cJSON *new_item; - new_item = cJSON_New_Item(item->pAllocator); - if (!new_item) { - *out_of_memory = true; - return 0; /* memory fail */ - } - child->next = new_item; - new_item->prev = child; - child = new_item; - value = skip(parse_string(child, skip(value + 1), out_of_memory)); - if (!value) return 0; - child->string = child->valuestring; - child->valuestring = 0; - if (*value != ':') { - // ep = value; // commented out as it is unused - return 0; - } /* fail! */ - value = skip(parse_value(child, skip(value + 1), out_of_memory)); /* skip any spacing, get the value. */ - if (!value) return 0; + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) { + /* start the linked list */ + current_item = head = new_item; + } else { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + if (cannot_access_at_index(input_buffer, 1)) { + goto fail; /* nothing comes after the comma */ + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) { + goto fail; /* failed to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) { + cJSON_Delete(head); } - if (*value == '}') return value + 1; /* end of array */ - // ep = value; // commented out as it is unused - return 0; /* malformed. */ + return false; } /* Render an object to text. */ -static char *print_object(cJSON *item, int depth, int fmt, printbuffer *p) { - char **entries = 0, **names = 0; - char *out = 0, *ptr, *ret, *str; - int j; - cJSON *child = item->child; - int numentries = 0, fail = 0, k; - size_t tmplen = 0, i = 0, len = 7; - /* Count the number of entries. */ - while (child) numentries++, child = child->next; - /* Explicitly handle empty object case */ - if (!numentries) { - if (p) - out = ensure(item->pAllocator, p, fmt ? depth + 4 : 3); - else - out = (char *)cJSON_malloc(item->pAllocator, fmt ? depth + 4 : 3); - if (!out) return 0; - ptr = out; - *ptr++ = '{'; - if (fmt) { - *ptr++ = '\n'; - for (j = 0; j < depth - 1; j++) *ptr++ = '\t'; - } - *ptr++ = '}'; - *ptr++ = 0; - return out; - } - if (p) { - /* Compose the output: */ - i = p->offset; - len = fmt ? 2 : 1; - ptr = ensure(item->pAllocator, p, len + 1); - if (!ptr) return 0; - *ptr++ = '{'; - if (fmt) *ptr++ = '\n'; - *ptr = 0; - p->offset += len; - child = item->child; - depth++; - while (child) { - if (fmt) { - ptr = ensure(item->pAllocator, p, depth); - if (!ptr) return 0; - for (j = 0; j < depth; j++) *ptr++ = '\t'; - p->offset += depth; +static cJSON_bool print_object(const cJSON *const item, printbuffer *const output_buffer) { + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) { + return false; + } + + /* Compose the output: */ + length = (size_t)(output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) { + if (output_buffer->format) { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) { + return false; } - print_string_ptr(item->pAllocator, child->string, p); - p->offset = cJSON_update(p); - - len = fmt ? 2 : 1; - ptr = ensure(item->pAllocator, p, len); - if (!ptr) return 0; - *ptr++ = ':'; - if (fmt) *ptr++ = '\t'; - p->offset += len; - - print_value(child, depth, fmt, p); - p->offset = cJSON_update(p); - - len = (fmt ? 1 : 0) + (child->next ? 1 : 0); - ptr = ensure(item->pAllocator, p, len + 1); - if (!ptr) return 0; - if (child->next) *ptr++ = ','; - if (fmt) *ptr++ = '\n'; - *ptr = 0; - p->offset += len; - child = child->next; - } - ptr = ensure(item->pAllocator, p, fmt ? (depth + 1) : 2); - if (!ptr) return 0; - if (fmt) - for (j = 0; j < depth - 1; j++) *ptr++ = '\t'; - *ptr++ = '}'; - *ptr = 0; - out = (p->buffer) + i; - } else { - /* Allocate space for the names and the objects */ - entries = (char **)cJSON_malloc(item->pAllocator, numentries * sizeof(char *)); - if (!entries) return 0; - names = (char **)cJSON_malloc(item->pAllocator, numentries * sizeof(char *)); - if (!names) { - cJSON_Free(item->pAllocator, entries); - return 0; - } - memset(entries, 0, sizeof(char *) * numentries); - memset(names, 0, sizeof(char *) * numentries); - - /* Collect all the results into our arrays: */ - child = item->child; - depth++; - if (fmt) len += depth; - while (child) { - names[i] = str = print_string_ptr(item->pAllocator, child->string, 0); - entries[i++] = ret = print_value(child, depth, fmt, 0); - if (str && ret) - len += strlen(ret) + strlen(str) + 2 + (fmt ? 2 + depth : 0); - else - fail = 1; - child = child->next; - } - - /* Try to allocate the output string */ - if (!fail) out = (char *)cJSON_malloc(item->pAllocator, len); - if (!out) fail = 1; - - /* Handle failure */ - if (fail) { - for (j = 0; j < numentries; j++) { - if (names[i]) cJSON_Free(item->pAllocator, names[j]); - if (entries[j]) cJSON_Free(item->pAllocator, entries[j]); + for (i = 0; i < output_buffer->depth; i++) { + *output_pointer++ = '\t'; } - cJSON_Free(item->pAllocator, names); - cJSON_Free(item->pAllocator, entries); - return 0; + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char *)current_item->string, output_buffer)) { + return false; + } + update_offset(output_buffer); + + length = (size_t)(output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) { + return false; + } + if (current_item->next) { + *output_pointer++ = ','; + } + + if (output_buffer->format) { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) { + return false; + } + if (output_buffer->format) { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) { + *output_pointer++ = '\t'; } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; - /* Compose the output: */ - *out = '{'; - ptr = out + 1; - if (fmt) *ptr++ = '\n'; - *ptr = 0; - for (j = 0; j < numentries; j++) { - if (fmt) - for (k = 0; k < depth; k++) *ptr++ = '\t'; - tmplen = strlen(names[j]); - memcpy(ptr, names[j], tmplen); - ptr += tmplen; - *ptr++ = ':'; - if (fmt) *ptr++ = '\t'; - size_t entries_size = strlen(entries[j]); - loader_strncpy(ptr, len - (ptr - out), entries[j], entries_size); - ptr += entries_size; - if (j != numentries - 1) *ptr++ = ','; - if (fmt) *ptr++ = '\n'; - *ptr = 0; - cJSON_Free(item->pAllocator, names[j]); - cJSON_Free(item->pAllocator, entries[j]); - } - - cJSON_Free(item->pAllocator, names); - cJSON_Free(item->pAllocator, entries); - if (fmt) - for (j = 0; j < depth - 1; j++) *ptr++ = '\t'; - *ptr++ = '}'; - *ptr++ = 0; - } - return out; + return true; } /* Get Array size/item / object item. */ -int loader_cJSON_GetArraySize(cJSON *array) { - cJSON *c = array->child; - int i = 0; - while (c) i++, c = c->next; - return i; -} -cJSON *loader_cJSON_GetArrayItem(cJSON *array, int item) { - cJSON *c = array->child; - while (c && item > 0) item--, c = c->next; - return c; -} -cJSON *loader_cJSON_GetObjectItem(cJSON *object, const char *string) { - cJSON *c = object->child; - while (c && NULL != c->string && strcmp(c->string, string)) c = c->next; - return c; +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) { + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) { + return 0; + } + + child = array->child; + + while (child != NULL) { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON *get_array_item(const cJSON *array, size_t index) { + cJSON *current_child = NULL; + + if (array == NULL) { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) { + if (index < 0) { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON *const object, const char *const name, const cJSON_bool case_sensitive) { + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) { + return NULL; + } + + current_element = object->child; + if (case_sensitive) { + while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) { + current_element = current_element->next; + } + } else { + while ((current_element != NULL) && + (case_insensitive_strcmp((const unsigned char *)name, (const unsigned char *)(current_element->string)) != 0)) { + current_element = current_element->next; + } + } + + if ((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON *const object, const char *const string) { + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON *const object, const char *const string) { + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) { + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) { + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON *create_reference(const cJSON *item, const internal_hooks *const hooks) { + cJSON *reference = NULL; + if (item == NULL) { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) { + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL) || (array == item)) { + return false; + } + + child = array->child; + /* + * To find the last item in array quickly, we use prev in array + */ + if (child == NULL) { + /* list is empty, start new one */ + array->child = item; + item->prev = item; + item->next = NULL; + } else { + /* append to the end */ + if (child->prev) { + suffix_object(child->prev, item); + array->child->prev = item; + } + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) { return add_item_to_array(array, item); } + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) +#pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void *cast_away_const(const void *string) { return (void *)string; } +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) +#pragma GCC diagnostic pop +#endif + +static cJSON_bool add_item_to_object(cJSON *const object, const char *const string, cJSON *const item, + const internal_hooks *const hooks, const cJSON_bool constant_key) { + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) { + return false; + } + + if (constant_key) { + new_key = (char *)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } else { + new_key = (char *)cJSON_strdup((const unsigned char *)string, hooks); + if (new_key == NULL) { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) { + return add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) { + return add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) { + if (array == NULL) { + return false; + } + + return add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) { + if ((object == NULL) || (string == NULL)) { + return false; + } + + return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_AddNullToObject(cJSON *const object, const char *const name) { + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_AddTrueToObject(cJSON *const object, const char *const name) { + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_AddFalseToObject(cJSON *const object, const char *const name) { + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_AddBoolToObject(cJSON *const object, const char *const name, const cJSON_bool boolean) { + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_AddNumberToObject(cJSON *const object, const char *const name, const double number) { + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_AddStringToObject(cJSON *const object, const char *const name, const char *const string) { + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_AddRawToObject(cJSON *const object, const char *const name, const char *const raw) { + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_AddObjectToObject(cJSON *const object, const char *const name) { + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_AddArrayToObject(cJSON *const object, const char *const name) { + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON *const item) { + if ((parent == NULL) || (item == NULL) || (item != parent->child && item->prev == NULL)) { + return NULL; + } + + if (item != parent->child) { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) { + /* first element */ + parent->child = item->next; + } else if (item->next == NULL) { + /* last element */ + parent->child->prev = item->prev; + } + + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) { + if (which < 0) { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) { cJSON_Delete(cJSON_DetachItemFromArray(array, which)); } + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) { + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) { + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) { + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) { + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) { + cJSON *after_inserted = NULL; + + if (which < 0 || newitem == NULL) { + return false; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) { + return add_item_to_array(array, newitem); + } + + if (after_inserted != array->child && after_inserted->prev == NULL) { + /* return false if after_inserted is a corrupted array item */ + return false; + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) { + array->child = newitem; + } else { + newitem->prev->next = newitem; + } + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON *const parent, cJSON *const item, cJSON *replacement) { + if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL)) { + return false; + } + + if (replacement == item) { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) { + replacement->next->prev = replacement; + } + if (parent->child == item) { + if (parent->child->prev == parent->child) { + replacement->prev = replacement; + } + parent->child = replacement; + } else { /* + * To find the last item in array quickly, we use prev in array. + * We can't modify the last item's next pointer where this item was the parent's child + */ + if (replacement->prev != NULL) { + replacement->prev->next = replacement; + } + if (replacement->next == NULL) { + parent->child->prev = replacement; + } + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) { + if (which < 0) { + return false; + } + + return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) { + if ((replacement == NULL) || (string == NULL)) { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) { + cJSON_free(replacement->string); + } + replacement->string = (char *)cJSON_strdup((const unsigned char *)string, &global_hooks); + if (replacement->string == NULL) { + return false; + } + + replacement->type &= ~cJSON_StringIsConst; + + return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) { + return replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) { + return replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) { + item->type = boolean ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) { + item->valueint = INT_MAX; + } else if (num <= (double)INT_MIN) { + item->valueint = INT_MIN; + } else { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) { + item->type = cJSON_String; + item->valuestring = (char *)cJSON_strdup((const unsigned char *)string, &global_hooks); + if (!item->valuestring) { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char *)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON *)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON *)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) { + item->type = cJSON_Raw; + item->valuestring = (char *)cJSON_strdup((const unsigned char *)raw, &global_hooks); + if (!item->valuestring) { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) { + item->type = cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) { + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateNumber(numbers[i]); + if (!n) { + cJSON_Delete(a); + return NULL; + } + if (!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) { + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateNumber((double)numbers[i]); + if (!n) { + cJSON_Delete(a); + return NULL; + } + if (!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) { + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateNumber(numbers[i]); + if (!n) { + cJSON_Delete(a); + return NULL; + } + if (!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) { + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateString(strings[i]); + if (!n) { + cJSON_Delete(a); + return NULL; + } + if (!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +/* Duplication */ +cJSON *cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse); + +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) { return cJSON_Duplicate_rec(item, 0, recurse); } + +cJSON *cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse) { + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) { + newitem->valuestring = (char *)cJSON_strdup((unsigned char *)item->valuestring, &global_hooks); + if (!newitem->valuestring) { + goto fail; + } + } + if (item->string) { + newitem->string = + (item->type & cJSON_StringIsConst) ? item->string : (char *)cJSON_strdup((unsigned char *)item->string, &global_hooks); + if (!newitem->string) { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) { + if (depth >= CJSON_CIRCULAR_LIMIT) { + goto fail; + } + newchild = cJSON_Duplicate_rec(child, depth + 1, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) { + goto fail; + } + if (next != NULL) { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } else { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + if (newitem && newitem->child) { + newitem->child->prev = newchild; + } + + return newitem; + +fail: + if (newitem != NULL) { + cJSON_Delete(newitem); + } + + return NULL; +} + +static void skip_oneline_comment(char **input) { + *input += static_strlen("//"); + + for (; (*input)[0] != '\0'; ++(*input)) { + if ((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; + } + } +} + +static void skip_multiline_comment(char **input) { + *input += static_strlen("/*"); + + for (; (*input)[0] != '\0'; ++(*input)) { + if (((*input)[0] == '*') && ((*input)[1] == '/')) { + *input += static_strlen("*/"); + return; + } + } +} + +static void minify_string(char **input, char **output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if ((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); + } + } +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) { + char *into = json; + + if (json == NULL) { + return; + } + + while (json[0] != '\0') { + switch (json[0]) { + case ' ': + case '\t': + case '\r': + case '\n': + json++; + break; + + case '/': + if (json[1] == '/') { + skip_oneline_comment(&json); + } else if (json[1] == '*') { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char **)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON *const item) { + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON *const item) { + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON *const item) { + if (item == NULL) { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON *const item) { + if (item == NULL) { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON *const item) { + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON *const item) { + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON *const item) { + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON *const item) { + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON *const item) { + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON *const item) { + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON *const a, const cJSON *const b, const cJSON_bool case_sensitive) { + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) { + return true; + } + + switch (a->type & 0xFF) { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (compare_double(a->valuedouble, b->valuedouble)) { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) { + return true; + } + + return false; + + case cJSON_Array: { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) { return global_hooks.allocate(size); } + +CJSON_PUBLIC(void) cJSON_free(void *object) { + global_hooks.deallocate(object); + object = NULL; } diff --git a/loader/cJSON.h b/loader/cJSON.h index 673fa2f0c..a929b806c 100644 --- a/loader/cJSON.h +++ b/loader/cJSON.h @@ -1,8 +1,5 @@ /* - Copyright (c) 2009 Dave Gamble - Copyright (c) 2015-2021 The Khronos Group Inc. - Copyright (c) 2015-2021 Valve Corporation - Copyright (c) 2015-2021 LunarG, Inc. + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23,66 +20,301 @@ THE SOFTWARE. */ -#pragma once +#ifndef cJSON__h +#define cJSON__h -#include -#include -#include +#ifdef __cplusplus +extern "C" { +#endif + +#define CJSON_HIDE_SYMBOLS + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project +with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 18 + +#include /* cJSON Types: */ -#define cJSON_False 0 -#define cJSON_True 1 -#define cJSON_NULL 2 -#define cJSON_Number 3 -#define cJSON_String 4 -#define cJSON_Array 5 -#define cJSON_Object 6 +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ #define cJSON_IsReference 256 #define cJSON_StringIsConst 512 /* The cJSON structure: */ typedef struct cJSON { - struct cJSON *next, *prev; /* next/prev allow you to walk array/object - chains. Alternatively, use - GetArraySize/GetArrayItem/GetObjectItem */ - struct cJSON *child; /* An array or object item will have a child pointer - pointing to a chain of the items in the - array/object. */ - - int type; /* The type of the item, as above. */ - - char *valuestring; /* The item's string, if type==cJSON_String */ - int valueint; /* The item's number, if type==cJSON_Number */ - double valuedouble; /* The item's number, if type==cJSON_Number */ - - char *string; /* The item's name string, if this item is the child of, or is - in the list of subitems of an object. */ - /* pointer to the allocation callbacks to use */ - VkAllocationCallbacks *pAllocator; + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; } cJSON; -/* Supply a block of JSON, and this returns a cJSON object you can interrogate. - * Call cJSON_Delete when finished. */ -cJSON *cJSON_Parse(const VkAllocationCallbacks *pAllocator, const char *value, bool *out_of_memory); -/* Render a cJSON entity to text for transfer/storage. Free the char* when - * finished. */ -char *loader_cJSON_Print(cJSON *item); -/* Render a cJSON entity to text for transfer/storage without any formatting. - * Free the char* when finished. */ -char *loader_cJSON_PrintUnformatted(cJSON *item); +typedef struct cJSON_Hooks { + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow + * passing those functions directly. */ + void *(CJSON_CDECL *malloc_fn)(size_t sz); + void(CJSON_CDECL *free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* Limits the length of circular references can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_CIRCULAR_LIMIT +#define CJSON_CIRCULAR_LIMIT 10000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char *) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks *hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and + * cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, + * where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte + * parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will + * match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); +CJSON_PUBLIC(cJSON *) +cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, + cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces + * reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on + * failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you + * actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); /* Delete a cJSON entity and all subentities. */ -void loader_cJSON_Delete(cJSON *c); +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); /* Returns the number of items in an array (or object). */ -int loader_cJSON_GetArraySize(cJSON *array); -/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. - */ -cJSON *loader_cJSON_GetArrayItem(cJSON *array, int item); +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); /* Get item "string" from object. Case insensitive. */ -cJSON *loader_cJSON_GetObjectItem(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON *const object, const char *const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON *const object, const char *const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make + * sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* Check item type and return its value */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON *const item); +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON *const item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON *const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/array that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); + +/* These utilities create an Array of count items. + * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of + * bounds.*/ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't + * want to corrupt your existing cJSON. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detach items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON *const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(cJSON_bool) +cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON *const parent, cJSON *const item, cJSON *replacement); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will + * need to be released. With recurse!=0, it will duplicate any children connected to the item. + * The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON *const a, const cJSON *const b, const cJSON_bool case_sensitive); + +/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. + * The input pointer json cannot point to a read-only address area, such as a string constant, + * but should point to a readable and writable address area. */ +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON *) cJSON_AddNullToObject(cJSON *const object, const char *const name); +CJSON_PUBLIC(cJSON *) cJSON_AddTrueToObject(cJSON *const object, const char *const name); +CJSON_PUBLIC(cJSON *) cJSON_AddFalseToObject(cJSON *const object, const char *const name); +CJSON_PUBLIC(cJSON *) cJSON_AddBoolToObject(cJSON *const object, const char *const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_AddNumberToObject(cJSON *const object, const char *const name, const double number); +CJSON_PUBLIC(cJSON *) cJSON_AddStringToObject(cJSON *const object, const char *const name, const char *const string); +CJSON_PUBLIC(cJSON *) cJSON_AddRawToObject(cJSON *const object, const char *const name, const char *const raw); +CJSON_PUBLIC(cJSON *) cJSON_AddObjectToObject(cJSON *const object, const char *const name); +CJSON_PUBLIC(cJSON *) cJSON_AddArrayToObject(cJSON *const object, const char *const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) +/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ +CJSON_PUBLIC(char *) cJSON_SetValuestring(cJSON *object, const char *valuestring); + +/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/ +#define cJSON_SetBoolValue(object, boolValue) \ + ((object != NULL && ((object)->type & (cJSON_False | cJSON_True))) \ + ? (object)->type = ((object)->type & (~(cJSON_False | cJSON_True))) | ((boolValue) ? cJSON_True : cJSON_False) \ + : cJSON_Invalid) + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) \ + for (element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif -/* When assigning an integer value, it needs to be propagated to valuedouble - * too. */ -#define cJSON_SetIntValue(object, val) ((object) ? (object)->valueint = (object)->valuedouble = (val) : (val)) -#define cJSON_SetNumberValue(object, val) ((object) ? (object)->valueint = (object)->valuedouble = (val) : (val)) +#endif From e0c7004884c0bed6cf10d23e98ffd904beb7ae43 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Sat, 23 Nov 2024 09:45:20 -0600 Subject: [PATCH 04/23] Removed unused functions from cJSON Remove cJSON functions that aren't used --- loader/cJSON.c | 825 ------------------------------------------------- loader/cJSON.h | 111 +------ 2 files changed, 2 insertions(+), 934 deletions(-) diff --git a/loader/cJSON.c b/loader/cJSON.c index 71a932b58..323e79329 100644 --- a/loader/cJSON.c +++ b/loader/cJSON.c @@ -114,13 +114,6 @@ CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON *const item) { #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. #endif -CJSON_PUBLIC(const char *) cJSON_Version(void) { - static char version[15]; - sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); - - return version; -} - /* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) { if ((string1 == NULL) || (string2 == NULL)) { @@ -162,50 +155,6 @@ static void *CJSON_CDECL internal_realloc(void *pointer, size_t size) { return r static internal_hooks global_hooks = {internal_malloc, internal_free, internal_realloc}; -static unsigned char *cJSON_strdup(const unsigned char *string, const internal_hooks *const hooks) { - size_t length = 0; - unsigned char *copy = NULL; - - if (string == NULL) { - return NULL; - } - - length = strlen((const char *)string) + sizeof(""); - copy = (unsigned char *)hooks->allocate(length); - if (copy == NULL) { - return NULL; - } - memcpy(copy, string, length); - - return copy; -} - -CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks *hooks) { - if (hooks == NULL) { - /* Reset hooks */ - global_hooks.allocate = malloc; - global_hooks.deallocate = free; - global_hooks.reallocate = realloc; - return; - } - - global_hooks.allocate = malloc; - if (hooks->malloc_fn != NULL) { - global_hooks.allocate = hooks->malloc_fn; - } - - global_hooks.deallocate = free; - if (hooks->free_fn != NULL) { - global_hooks.deallocate = hooks->free_fn; - } - - /* use realloc only if both free and malloc are used */ - global_hooks.reallocate = NULL; - if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) { - global_hooks.reallocate = realloc; - } -} - /* Internal constructor. */ static cJSON *cJSON_New_Item(const internal_hooks *const hooks) { cJSON *node = (cJSON *)hooks->allocate(sizeof(cJSON)); @@ -330,56 +279,6 @@ static cJSON_bool parse_number(cJSON *const item, parse_buffer *const input_buff return true; } -/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ -CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) { - if (number >= INT_MAX) { - object->valueint = INT_MAX; - } else if (number <= (double)INT_MIN) { - object->valueint = INT_MIN; - } else { - object->valueint = (int)number; - } - - return object->valuedouble = number; -} - -/* Note: when passing a NULL valuestring, cJSON_SetValuestring treats this as an error and return NULL */ -CJSON_PUBLIC(char *) cJSON_SetValuestring(cJSON *object, const char *valuestring) { - char *copy = NULL; - size_t v1_len; - size_t v2_len; - /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ - if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference)) { - return NULL; - } - /* return NULL if the object is corrupted or valuestring is NULL */ - if (object->valuestring == NULL || valuestring == NULL) { - return NULL; - } - - v1_len = strlen(valuestring); - v2_len = strlen(object->valuestring); - - if (v1_len <= v2_len) { - /* strcpy does not handle overlapping string: [X1, X2] [Y1, Y2] => X2 < Y1 or Y2 < X1 */ - if (!(valuestring + v1_len < object->valuestring || object->valuestring + v2_len < valuestring)) { - return NULL; - } - strcpy(object->valuestring, valuestring); - return object->valuestring; - } - copy = (char *)cJSON_strdup((const unsigned char *)valuestring, &global_hooks); - if (copy == NULL) { - return NULL; - } - if (object->valuestring != NULL) { - cJSON_free(object->valuestring); - } - object->valuestring = copy; - - return copy; -} - typedef struct { unsigned char *buffer; size_t length; @@ -1662,723 +1561,6 @@ CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *st return cJSON_GetObjectItem(object, string) ? 1 : 0; } -/* Utility for array list handling. */ -static void suffix_object(cJSON *prev, cJSON *item) { - prev->next = item; - item->prev = prev; -} - -/* Utility for handling references. */ -static cJSON *create_reference(const cJSON *item, const internal_hooks *const hooks) { - cJSON *reference = NULL; - if (item == NULL) { - return NULL; - } - - reference = cJSON_New_Item(hooks); - if (reference == NULL) { - return NULL; - } - - memcpy(reference, item, sizeof(cJSON)); - reference->string = NULL; - reference->type |= cJSON_IsReference; - reference->next = reference->prev = NULL; - return reference; -} - -static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) { - cJSON *child = NULL; - - if ((item == NULL) || (array == NULL) || (array == item)) { - return false; - } - - child = array->child; - /* - * To find the last item in array quickly, we use prev in array - */ - if (child == NULL) { - /* list is empty, start new one */ - array->child = item; - item->prev = item; - item->next = NULL; - } else { - /* append to the end */ - if (child->prev) { - suffix_object(child->prev, item); - array->child->prev = item; - } - } - - return true; -} - -/* Add item to array/object. */ -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) { return add_item_to_array(array, item); } - -#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) -#pragma GCC diagnostic push -#endif -#ifdef __GNUC__ -#pragma GCC diagnostic ignored "-Wcast-qual" -#endif -/* helper function to cast away const */ -static void *cast_away_const(const void *string) { return (void *)string; } -#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) -#pragma GCC diagnostic pop -#endif - -static cJSON_bool add_item_to_object(cJSON *const object, const char *const string, cJSON *const item, - const internal_hooks *const hooks, const cJSON_bool constant_key) { - char *new_key = NULL; - int new_type = cJSON_Invalid; - - if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) { - return false; - } - - if (constant_key) { - new_key = (char *)cast_away_const(string); - new_type = item->type | cJSON_StringIsConst; - } else { - new_key = (char *)cJSON_strdup((const unsigned char *)string, hooks); - if (new_key == NULL) { - return false; - } - - new_type = item->type & ~cJSON_StringIsConst; - } - - if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { - hooks->deallocate(item->string); - } - - item->string = new_key; - item->type = new_type; - - return add_item_to_array(object, item); -} - -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) { - return add_item_to_object(object, string, item, &global_hooks, false); -} - -/* Add an item to an object with constant string as key */ -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) { - return add_item_to_object(object, string, item, &global_hooks, true); -} - -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) { - if (array == NULL) { - return false; - } - - return add_item_to_array(array, create_reference(item, &global_hooks)); -} - -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) { - if ((object == NULL) || (string == NULL)) { - return false; - } - - return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); -} - -CJSON_PUBLIC(cJSON *) cJSON_AddNullToObject(cJSON *const object, const char *const name) { - cJSON *null = cJSON_CreateNull(); - if (add_item_to_object(object, name, null, &global_hooks, false)) { - return null; - } - - cJSON_Delete(null); - return NULL; -} - -CJSON_PUBLIC(cJSON *) cJSON_AddTrueToObject(cJSON *const object, const char *const name) { - cJSON *true_item = cJSON_CreateTrue(); - if (add_item_to_object(object, name, true_item, &global_hooks, false)) { - return true_item; - } - - cJSON_Delete(true_item); - return NULL; -} - -CJSON_PUBLIC(cJSON *) cJSON_AddFalseToObject(cJSON *const object, const char *const name) { - cJSON *false_item = cJSON_CreateFalse(); - if (add_item_to_object(object, name, false_item, &global_hooks, false)) { - return false_item; - } - - cJSON_Delete(false_item); - return NULL; -} - -CJSON_PUBLIC(cJSON *) cJSON_AddBoolToObject(cJSON *const object, const char *const name, const cJSON_bool boolean) { - cJSON *bool_item = cJSON_CreateBool(boolean); - if (add_item_to_object(object, name, bool_item, &global_hooks, false)) { - return bool_item; - } - - cJSON_Delete(bool_item); - return NULL; -} - -CJSON_PUBLIC(cJSON *) cJSON_AddNumberToObject(cJSON *const object, const char *const name, const double number) { - cJSON *number_item = cJSON_CreateNumber(number); - if (add_item_to_object(object, name, number_item, &global_hooks, false)) { - return number_item; - } - - cJSON_Delete(number_item); - return NULL; -} - -CJSON_PUBLIC(cJSON *) cJSON_AddStringToObject(cJSON *const object, const char *const name, const char *const string) { - cJSON *string_item = cJSON_CreateString(string); - if (add_item_to_object(object, name, string_item, &global_hooks, false)) { - return string_item; - } - - cJSON_Delete(string_item); - return NULL; -} - -CJSON_PUBLIC(cJSON *) cJSON_AddRawToObject(cJSON *const object, const char *const name, const char *const raw) { - cJSON *raw_item = cJSON_CreateRaw(raw); - if (add_item_to_object(object, name, raw_item, &global_hooks, false)) { - return raw_item; - } - - cJSON_Delete(raw_item); - return NULL; -} - -CJSON_PUBLIC(cJSON *) cJSON_AddObjectToObject(cJSON *const object, const char *const name) { - cJSON *object_item = cJSON_CreateObject(); - if (add_item_to_object(object, name, object_item, &global_hooks, false)) { - return object_item; - } - - cJSON_Delete(object_item); - return NULL; -} - -CJSON_PUBLIC(cJSON *) cJSON_AddArrayToObject(cJSON *const object, const char *const name) { - cJSON *array = cJSON_CreateArray(); - if (add_item_to_object(object, name, array, &global_hooks, false)) { - return array; - } - - cJSON_Delete(array); - return NULL; -} - -CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON *const item) { - if ((parent == NULL) || (item == NULL) || (item != parent->child && item->prev == NULL)) { - return NULL; - } - - if (item != parent->child) { - /* not the first element */ - item->prev->next = item->next; - } - if (item->next != NULL) { - /* not the last element */ - item->next->prev = item->prev; - } - - if (item == parent->child) { - /* first element */ - parent->child = item->next; - } else if (item->next == NULL) { - /* last element */ - parent->child->prev = item->prev; - } - - /* make sure the detached item doesn't point anywhere anymore */ - item->prev = NULL; - item->next = NULL; - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) { - if (which < 0) { - return NULL; - } - - return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); -} - -CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) { cJSON_Delete(cJSON_DetachItemFromArray(array, which)); } - -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) { - cJSON *to_detach = cJSON_GetObjectItem(object, string); - - return cJSON_DetachItemViaPointer(object, to_detach); -} - -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) { - cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); - - return cJSON_DetachItemViaPointer(object, to_detach); -} - -CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) { - cJSON_Delete(cJSON_DetachItemFromObject(object, string)); -} - -CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) { - cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); -} - -/* Replace array/object items with new ones. */ -CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) { - cJSON *after_inserted = NULL; - - if (which < 0 || newitem == NULL) { - return false; - } - - after_inserted = get_array_item(array, (size_t)which); - if (after_inserted == NULL) { - return add_item_to_array(array, newitem); - } - - if (after_inserted != array->child && after_inserted->prev == NULL) { - /* return false if after_inserted is a corrupted array item */ - return false; - } - - newitem->next = after_inserted; - newitem->prev = after_inserted->prev; - after_inserted->prev = newitem; - if (after_inserted == array->child) { - array->child = newitem; - } else { - newitem->prev->next = newitem; - } - return true; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON *const parent, cJSON *const item, cJSON *replacement) { - if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL)) { - return false; - } - - if (replacement == item) { - return true; - } - - replacement->next = item->next; - replacement->prev = item->prev; - - if (replacement->next != NULL) { - replacement->next->prev = replacement; - } - if (parent->child == item) { - if (parent->child->prev == parent->child) { - replacement->prev = replacement; - } - parent->child = replacement; - } else { /* - * To find the last item in array quickly, we use prev in array. - * We can't modify the last item's next pointer where this item was the parent's child - */ - if (replacement->prev != NULL) { - replacement->prev->next = replacement; - } - if (replacement->next == NULL) { - parent->child->prev = replacement; - } - } - - item->next = NULL; - item->prev = NULL; - cJSON_Delete(item); - - return true; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) { - if (which < 0) { - return false; - } - - return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); -} - -static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) { - if ((replacement == NULL) || (string == NULL)) { - return false; - } - - /* replace the name in the replacement */ - if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) { - cJSON_free(replacement->string); - } - replacement->string = (char *)cJSON_strdup((const unsigned char *)string, &global_hooks); - if (replacement->string == NULL) { - return false; - } - - replacement->type &= ~cJSON_StringIsConst; - - return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); -} - -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) { - return replace_item_in_object(object, string, newitem, false); -} - -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) { - return replace_item_in_object(object, string, newitem, true); -} - -/* Create basic types: */ -CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) { - cJSON *item = cJSON_New_Item(&global_hooks); - if (item) { - item->type = cJSON_NULL; - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) { - cJSON *item = cJSON_New_Item(&global_hooks); - if (item) { - item->type = cJSON_True; - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) { - cJSON *item = cJSON_New_Item(&global_hooks); - if (item) { - item->type = cJSON_False; - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) { - cJSON *item = cJSON_New_Item(&global_hooks); - if (item) { - item->type = boolean ? cJSON_True : cJSON_False; - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) { - cJSON *item = cJSON_New_Item(&global_hooks); - if (item) { - item->type = cJSON_Number; - item->valuedouble = num; - - /* use saturation in case of overflow */ - if (num >= INT_MAX) { - item->valueint = INT_MAX; - } else if (num <= (double)INT_MIN) { - item->valueint = INT_MIN; - } else { - item->valueint = (int)num; - } - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) { - cJSON *item = cJSON_New_Item(&global_hooks); - if (item) { - item->type = cJSON_String; - item->valuestring = (char *)cJSON_strdup((const unsigned char *)string, &global_hooks); - if (!item->valuestring) { - cJSON_Delete(item); - return NULL; - } - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) { - cJSON *item = cJSON_New_Item(&global_hooks); - if (item != NULL) { - item->type = cJSON_String | cJSON_IsReference; - item->valuestring = (char *)cast_away_const(string); - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) { - cJSON *item = cJSON_New_Item(&global_hooks); - if (item != NULL) { - item->type = cJSON_Object | cJSON_IsReference; - item->child = (cJSON *)cast_away_const(child); - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { - cJSON *item = cJSON_New_Item(&global_hooks); - if (item != NULL) { - item->type = cJSON_Array | cJSON_IsReference; - item->child = (cJSON *)cast_away_const(child); - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) { - cJSON *item = cJSON_New_Item(&global_hooks); - if (item) { - item->type = cJSON_Raw; - item->valuestring = (char *)cJSON_strdup((const unsigned char *)raw, &global_hooks); - if (!item->valuestring) { - cJSON_Delete(item); - return NULL; - } - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) { - cJSON *item = cJSON_New_Item(&global_hooks); - if (item) { - item->type = cJSON_Array; - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) { - cJSON *item = cJSON_New_Item(&global_hooks); - if (item) { - item->type = cJSON_Object; - } - - return item; -} - -/* Create Arrays: */ -CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) { - size_t i = 0; - cJSON *n = NULL; - cJSON *p = NULL; - cJSON *a = NULL; - - if ((count < 0) || (numbers == NULL)) { - return NULL; - } - - a = cJSON_CreateArray(); - - for (i = 0; a && (i < (size_t)count); i++) { - n = cJSON_CreateNumber(numbers[i]); - if (!n) { - cJSON_Delete(a); - return NULL; - } - if (!i) { - a->child = n; - } else { - suffix_object(p, n); - } - p = n; - } - - if (a && a->child) { - a->child->prev = n; - } - - return a; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) { - size_t i = 0; - cJSON *n = NULL; - cJSON *p = NULL; - cJSON *a = NULL; - - if ((count < 0) || (numbers == NULL)) { - return NULL; - } - - a = cJSON_CreateArray(); - - for (i = 0; a && (i < (size_t)count); i++) { - n = cJSON_CreateNumber((double)numbers[i]); - if (!n) { - cJSON_Delete(a); - return NULL; - } - if (!i) { - a->child = n; - } else { - suffix_object(p, n); - } - p = n; - } - - if (a && a->child) { - a->child->prev = n; - } - - return a; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) { - size_t i = 0; - cJSON *n = NULL; - cJSON *p = NULL; - cJSON *a = NULL; - - if ((count < 0) || (numbers == NULL)) { - return NULL; - } - - a = cJSON_CreateArray(); - - for (i = 0; a && (i < (size_t)count); i++) { - n = cJSON_CreateNumber(numbers[i]); - if (!n) { - cJSON_Delete(a); - return NULL; - } - if (!i) { - a->child = n; - } else { - suffix_object(p, n); - } - p = n; - } - - if (a && a->child) { - a->child->prev = n; - } - - return a; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) { - size_t i = 0; - cJSON *n = NULL; - cJSON *p = NULL; - cJSON *a = NULL; - - if ((count < 0) || (strings == NULL)) { - return NULL; - } - - a = cJSON_CreateArray(); - - for (i = 0; a && (i < (size_t)count); i++) { - n = cJSON_CreateString(strings[i]); - if (!n) { - cJSON_Delete(a); - return NULL; - } - if (!i) { - a->child = n; - } else { - suffix_object(p, n); - } - p = n; - } - - if (a && a->child) { - a->child->prev = n; - } - - return a; -} - -/* Duplication */ -cJSON *cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse); - -CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) { return cJSON_Duplicate_rec(item, 0, recurse); } - -cJSON *cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse) { - cJSON *newitem = NULL; - cJSON *child = NULL; - cJSON *next = NULL; - cJSON *newchild = NULL; - - /* Bail on bad ptr */ - if (!item) { - goto fail; - } - /* Create new item */ - newitem = cJSON_New_Item(&global_hooks); - if (!newitem) { - goto fail; - } - /* Copy over all vars */ - newitem->type = item->type & (~cJSON_IsReference); - newitem->valueint = item->valueint; - newitem->valuedouble = item->valuedouble; - if (item->valuestring) { - newitem->valuestring = (char *)cJSON_strdup((unsigned char *)item->valuestring, &global_hooks); - if (!newitem->valuestring) { - goto fail; - } - } - if (item->string) { - newitem->string = - (item->type & cJSON_StringIsConst) ? item->string : (char *)cJSON_strdup((unsigned char *)item->string, &global_hooks); - if (!newitem->string) { - goto fail; - } - } - /* If non-recursive, then we're done! */ - if (!recurse) { - return newitem; - } - /* Walk the ->next chain for the child. */ - child = item->child; - while (child != NULL) { - if (depth >= CJSON_CIRCULAR_LIMIT) { - goto fail; - } - newchild = cJSON_Duplicate_rec(child, depth + 1, true); /* Duplicate (with recurse) each item in the ->next chain */ - if (!newchild) { - goto fail; - } - if (next != NULL) { - /* If newitem->child already set, then crosswire ->prev and ->next and move on */ - next->next = newchild; - newchild->prev = next; - next = newchild; - } else { - /* Set newitem->child and move to it */ - newitem->child = newchild; - next = newchild; - } - child = child->next; - } - if (newitem && newitem->child) { - newitem->child->prev = newchild; - } - - return newitem; - -fail: - if (newitem != NULL) { - cJSON_Delete(newitem); - } - - return NULL; -} - static void skip_oneline_comment(char **input) { *input += static_strlen("//"); @@ -2648,10 +1830,3 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON *const a, const cJSON *const return false; } } - -CJSON_PUBLIC(void *) cJSON_malloc(size_t size) { return global_hooks.allocate(size); } - -CJSON_PUBLIC(void) cJSON_free(void *object) { - global_hooks.deallocate(object); - object = NULL; -} diff --git a/loader/cJSON.h b/loader/cJSON.h index a929b806c..10cf40754 100644 --- a/loader/cJSON.h +++ b/loader/cJSON.h @@ -144,14 +144,8 @@ typedef int cJSON_bool; #define CJSON_CIRCULAR_LIMIT 10000 #endif -/* returns the version of cJSON as a string */ -CJSON_PUBLIC(const char *) cJSON_Version(void); - -/* Supply malloc, realloc and free functions to cJSON */ -CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks *hooks); - -/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and - * cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, +/* Memory Management: the caller is always responsible to free instthe results from all variants of cJSON_Parse (with cJSON_Delete) + * and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, * where the caller has full responsibility of the buffer. */ /* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); @@ -208,111 +202,10 @@ CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON *const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON *const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON *const item); -/* These calls create a cJSON item of the appropriate type. */ -CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); -CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); -CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); -CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); -CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); -CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); -/* raw json */ -CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); -CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); -CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); - -/* Create a string where valuestring references a string so - * it will not be freed by cJSON_Delete */ -CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); -/* Create an object/array that only references it's elements so - * they will not be freed by cJSON_Delete */ -CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); -CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); - -/* These utilities create an Array of count items. - * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of - * bounds.*/ -CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); -CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); -CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); -CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); - -/* Append item to the specified array/object. */ -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); -/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. - * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before - * writing to `item->string` */ -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); -/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't - * want to corrupt your existing cJSON. */ -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); - -/* Remove/Detach items from Arrays/Objects. */ -CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON *const item); -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); -CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); -CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); -CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); - -/* Update array items. */ -CJSON_PUBLIC(cJSON_bool) -cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON *const parent, cJSON *const item, cJSON *replacement); -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem); -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem); - -/* Duplicate a cJSON item */ -CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); -/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will - * need to be released. With recurse!=0, it will duplicate any children connected to the item. - * The item->next and ->prev pointers are always zero on return from Duplicate. */ -/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. - * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ -CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON *const a, const cJSON *const b, const cJSON_bool case_sensitive); - -/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. - * The input pointer json cannot point to a read-only address area, such as a string constant, - * but should point to a readable and writable address area. */ -CJSON_PUBLIC(void) cJSON_Minify(char *json); - -/* Helper functions for creating and adding items to an object at the same time. - * They return the added item or NULL on failure. */ -CJSON_PUBLIC(cJSON *) cJSON_AddNullToObject(cJSON *const object, const char *const name); -CJSON_PUBLIC(cJSON *) cJSON_AddTrueToObject(cJSON *const object, const char *const name); -CJSON_PUBLIC(cJSON *) cJSON_AddFalseToObject(cJSON *const object, const char *const name); -CJSON_PUBLIC(cJSON *) cJSON_AddBoolToObject(cJSON *const object, const char *const name, const cJSON_bool boolean); -CJSON_PUBLIC(cJSON *) cJSON_AddNumberToObject(cJSON *const object, const char *const name, const double number); -CJSON_PUBLIC(cJSON *) cJSON_AddStringToObject(cJSON *const object, const char *const name, const char *const string); -CJSON_PUBLIC(cJSON *) cJSON_AddRawToObject(cJSON *const object, const char *const name, const char *const raw); -CJSON_PUBLIC(cJSON *) cJSON_AddObjectToObject(cJSON *const object, const char *const name); -CJSON_PUBLIC(cJSON *) cJSON_AddArrayToObject(cJSON *const object, const char *const name); - -/* When assigning an integer value, it needs to be propagated to valuedouble too. */ -#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) -/* helper for the cJSON_SetNumberValue macro */ -CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); -#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) -/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ -CJSON_PUBLIC(char *) cJSON_SetValuestring(cJSON *object, const char *valuestring); - -/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/ -#define cJSON_SetBoolValue(object, boolValue) \ - ((object != NULL && ((object)->type & (cJSON_False | cJSON_True))) \ - ? (object)->type = ((object)->type & (~(cJSON_False | cJSON_True))) | ((boolValue) ? cJSON_True : cJSON_False) \ - : cJSON_Invalid) - /* Macro for iterating over an array or object */ #define cJSON_ArrayForEach(element, array) \ for (element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) -/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ -CJSON_PUBLIC(void *) cJSON_malloc(size_t size); -CJSON_PUBLIC(void) cJSON_free(void *object); - #ifdef __cplusplus } #endif From af16f19d6c9ff27c5ed80194f899bddb0837c0d2 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Tue, 26 Nov 2024 20:14:32 -0600 Subject: [PATCH 05/23] Use snprintf in updated cJSON --- loader/cJSON.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/loader/cJSON.c b/loader/cJSON.c index 323e79329..c510597d7 100644 --- a/loader/cJSON.c +++ b/loader/cJSON.c @@ -392,21 +392,21 @@ static cJSON_bool print_number(const cJSON *const item, printbuffer *const outpu /* This checks for NaN and Infinity */ if (isnan(d) || isinf(d)) { - length = sprintf((char *)number_buffer, "null"); + length = snprintf((char *)number_buffer, 26, "null"); } else if (d == (double)item->valueint) { - length = sprintf((char *)number_buffer, "%d", item->valueint); + length = snprintf((char *)number_buffer, 26, "%d", item->valueint); } else { /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ - length = sprintf((char *)number_buffer, "%1.15g", d); + length = snprintf((char *)number_buffer, 26, "%1.15g", d); /* Check whether the original double can be recovered */ if ((sscanf((char *)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) { /* If not, print with 17 decimal places of precision */ - length = sprintf((char *)number_buffer, "%1.17g", d); + length = snprintf((char *)number_buffer, 26, "%1.17g", d); } } - /* sprintf failed or buffer overrun occurred */ + /* snprintf failed or buffer overrun occurred */ if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) { return false; } @@ -772,7 +772,7 @@ static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer break; default: /* escape and print as unicode codepoint */ - sprintf((char *)output_pointer, "u%04x", *input_pointer); + snprintf((char *)output_pointer, output_length - (size_t)(output_pointer - output), "u%04x", *input_pointer); output_pointer += 4; break; } From ad921ccb81d9a2e79ee0f078e8ab40ce6ee71a57 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Fri, 22 Nov 2024 10:03:23 -0600 Subject: [PATCH 06/23] Modify cJSON to not surround strings with "" This is a modification the loader does to reduce the number of string copies that are required. Since the loader doesn't care about the quotes, they are simply omitted, making usage of the strings much easier. Note that this change was done to the previously checked in version of cJSON, so these changes are the same conceptual changes made to the newer version of the cJSON library. --- loader/cJSON.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/loader/cJSON.c b/loader/cJSON.c index c510597d7..42676d335 100644 --- a/loader/cJSON.c +++ b/loader/cJSON.c @@ -691,11 +691,10 @@ static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer /* empty string */ if (input == NULL) { - output = ensure(output_buffer, sizeof("\"\"")); + output = ensure(output_buffer, 0); if (output == NULL) { return false; } - strcpy((char *)output, "\"\""); return true; } @@ -723,23 +722,20 @@ static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer } output_length = (size_t)(input_pointer - input) + escape_characters; - output = ensure(output_buffer, output_length + sizeof("\"\"")); + output = ensure(output_buffer, output_length + 1); if (output == NULL) { return false; } /* no characters have to be escaped */ if (escape_characters == 0) { - output[0] = '\"'; - memcpy(output + 1, input, output_length); - output[output_length + 1] = '\"'; - output[output_length + 2] = '\0'; + memcpy(output, input, output_length); + output[output_length] = '\0'; return true; } - output[0] = '\"'; - output_pointer = output + 1; + output_pointer = output; /* copy the string */ for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) { if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) { @@ -778,8 +774,7 @@ static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer } } } - output[output_length + 1] = '\"'; - output[output_length + 2] = '\0'; + output[output_length] = '\0'; return true; } From 147b5864ce235025e1e2c37221e222f93ddc1434 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Tue, 26 Nov 2024 15:14:22 -0600 Subject: [PATCH 07/23] Remove cJSON escaping backslashes This brings cJSON back into its pre-update form, as well as documenting that it is an intentional change. --- loader/cJSON.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/loader/cJSON.c b/loader/cJSON.c index 42676d335..5100131ce 100644 --- a/loader/cJSON.c +++ b/loader/cJSON.c @@ -742,8 +742,11 @@ static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer /* normal character, copy */ *output_pointer = *input_pointer; } else { + // Loader specific modification - don't add a backslash because that will 'double up' any existing back slashes. + // This change was added right after vulkan's public release, so while it may not be a problem, there are plenty + // of API calls made which might not work if the paths have "\\"" in them /* character needs to be escaped */ - *output_pointer++ = '\\'; + //*output_pointer++ = '\\'; switch (*input_pointer) { case '\\': *output_pointer = '\\'; @@ -752,19 +755,19 @@ static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer *output_pointer = '\"'; break; case '\b': - *output_pointer = 'b'; + *output_pointer = '\b'; break; case '\f': - *output_pointer = 'f'; + *output_pointer = '\f'; break; case '\n': - *output_pointer = 'n'; + *output_pointer = '\n'; break; case '\r': - *output_pointer = 'r'; + *output_pointer = '\r'; break; case '\t': - *output_pointer = 't'; + *output_pointer = '\t'; break; default: /* escape and print as unicode codepoint */ From e16cb1f2210b9a1ea3e2df5295196a3dfb3afeed Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Sun, 24 Nov 2024 16:51:30 -0600 Subject: [PATCH 08/23] Reimplement OOM and VkAllocationCallbacks in cJSON Also prepend loader_ to cJSON functions to reduce symbol collision. This was done before but removed while updating cJSON just to ensure that the code would still work (kinda, the tests failed but no catastrophic incompatibilities were introduced into cJSON). --- loader/cJSON.c | 333 ++++++++++++++++++++----------------------- loader/cJSON.h | 81 ++++++----- loader/loader.c | 37 +++-- loader/loader_json.c | 38 +++-- loader/settings.c | 7 +- 5 files changed, 248 insertions(+), 248 deletions(-) diff --git a/loader/cJSON.c b/loader/cJSON.c index 5100131ce..54062c3cb 100644 --- a/loader/cJSON.c +++ b/loader/cJSON.c @@ -58,6 +58,10 @@ #include "cJSON.h" +#include + +#include "allocation.h" + /* define our own boolean type */ #ifdef true #undef true @@ -93,16 +97,16 @@ static error global_error = {NULL, 0}; CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) { return (const char *)(global_error.json + global_error.position); } -CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON *const item) { - if (!cJSON_IsString(item)) { +CJSON_PUBLIC(char *) loader_cJSON_GetStringValue(const cJSON *const item) { + if (!loader_cJSON_IsString(item)) { return NULL; } return item->valuestring; } -CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON *const item) { - if (!cJSON_IsNumber(item)) { +CJSON_PUBLIC(double) loader_cJSON_GetNumberValue(const cJSON *const item) { + if (!loader_cJSON_IsNumber(item)) { return (double)NAN; } @@ -133,55 +137,35 @@ static int case_insensitive_strcmp(const unsigned char *string1, const unsigned return tolower(*string1) - tolower(*string2); } -typedef struct internal_hooks { - void *(CJSON_CDECL *allocate)(size_t size); - void(CJSON_CDECL *deallocate)(void *pointer); - void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); -} internal_hooks; - -#if defined(_MSC_VER) -/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ -static void *CJSON_CDECL internal_malloc(size_t size) { return malloc(size); } -static void CJSON_CDECL internal_free(void *pointer) { free(pointer); } -static void *CJSON_CDECL internal_realloc(void *pointer, size_t size) { return realloc(pointer, size); } -#else -#define internal_malloc malloc -#define internal_free free -#define internal_realloc realloc -#endif - /* strlen of character literals resolved at compile time */ #define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) -static internal_hooks global_hooks = {internal_malloc, internal_free, internal_realloc}; - /* Internal constructor. */ -static cJSON *cJSON_New_Item(const internal_hooks *const hooks) { - cJSON *node = (cJSON *)hooks->allocate(sizeof(cJSON)); - if (node) { - memset(node, '\0', sizeof(cJSON)); +static cJSON *cJSON_New_Item(const VkAllocationCallbacks *pAllocator) { + cJSON *node = (cJSON *)loader_calloc(pAllocator, sizeof(cJSON), VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); + if (NULL != node) { + node->pAllocator = pAllocator; } - return node; } /* Delete a cJSON structure. */ -CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) { +CJSON_PUBLIC(void) loader_cJSON_Delete(cJSON *item) { cJSON *next = NULL; while (item != NULL) { next = item->next; if (!(item->type & cJSON_IsReference) && (item->child != NULL)) { - cJSON_Delete(item->child); + loader_cJSON_Delete(item->child); } if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) { - global_hooks.deallocate(item->valuestring); + loader_free(item->pAllocator, item->valuestring); item->valuestring = NULL; } if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { - global_hooks.deallocate(item->string); + loader_free(item->pAllocator, item->string); item->string = NULL; } - global_hooks.deallocate(item); + loader_free(item->pAllocator, item); item = next; } } @@ -201,7 +185,7 @@ typedef struct { size_t length; size_t offset; size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ - internal_hooks hooks; + const VkAllocationCallbacks *pAllocator; } parse_buffer; /* check if the given size is left to read in a given parse buffer (starting with 1) */ @@ -286,11 +270,11 @@ typedef struct { size_t depth; /* current nesting depth (for formatted printing) */ cJSON_bool noalloc; cJSON_bool format; /* is this print a formatted print */ - internal_hooks hooks; + const VkAllocationCallbacks *pAllocator; } printbuffer; /* realloc printbuffer if necessary to have at least "needed" bytes more */ -static unsigned char *ensure(printbuffer *const p, size_t needed) { +static unsigned char *ensure(printbuffer *const p, size_t needed, bool *out_of_memory) { unsigned char *newbuffer = NULL; size_t newsize = 0; @@ -329,30 +313,16 @@ static unsigned char *ensure(printbuffer *const p, size_t needed) { newsize = needed * 2; } - if (p->hooks.reallocate != NULL) { - /* reallocate with realloc if available */ - newbuffer = (unsigned char *)p->hooks.reallocate(p->buffer, newsize); - if (newbuffer == NULL) { - p->hooks.deallocate(p->buffer); - p->length = 0; - p->buffer = NULL; - - return NULL; - } - } else { - /* otherwise reallocate manually */ - newbuffer = (unsigned char *)p->hooks.allocate(newsize); - if (!newbuffer) { - p->hooks.deallocate(p->buffer); - p->length = 0; - p->buffer = NULL; - - return NULL; - } + newbuffer = (unsigned char *)loader_realloc(p->pAllocator, p->buffer, p->length, newsize, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (newbuffer == NULL) { + *out_of_memory = true; + loader_free(p->pAllocator, p->buffer); + p->length = 0; + p->buffer = NULL; - memcpy(newbuffer, p->buffer, p->offset + 1); - p->hooks.deallocate(p->buffer); + return NULL; } + p->length = newsize; p->buffer = newbuffer; @@ -377,7 +347,7 @@ static cJSON_bool compare_double(double a, double b) { } /* Render the number nicely from the given item into a string. */ -static cJSON_bool print_number(const cJSON *const item, printbuffer *const output_buffer) { +static cJSON_bool print_number(const cJSON *const item, printbuffer *const output_buffer, bool *out_of_memory) { unsigned char *output_pointer = NULL; double d = item->valuedouble; int length = 0; @@ -412,7 +382,7 @@ static cJSON_bool print_number(const cJSON *const item, printbuffer *const outpu } /* reserve appropriate space in the output */ - output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + output_pointer = ensure(output_buffer, (size_t)length + sizeof(""), out_of_memory); if (output_pointer == NULL) { return false; } @@ -562,7 +532,7 @@ static unsigned char utf16_literal_to_utf8(const unsigned char *const input_poin } /* Parse the input text into an unescaped cinput, and populate item. */ -static cJSON_bool parse_string(cJSON *const item, parse_buffer *const input_buffer) { +static cJSON_bool parse_string(cJSON *const item, parse_buffer *const input_buffer, bool *out_of_memory) { const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; unsigned char *output_pointer = NULL; @@ -595,8 +565,10 @@ static cJSON_bool parse_string(cJSON *const item, parse_buffer *const input_buff /* This is at most how much we need for the output */ allocation_length = (size_t)(input_end - buffer_at_offset(input_buffer)) - skipped_bytes; - output = (unsigned char *)input_buffer->hooks.allocate(allocation_length + sizeof("")); + output = (unsigned char *)loader_calloc(input_buffer->pAllocator, allocation_length + sizeof(""), + VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); if (output == NULL) { + *out_of_memory = true; goto fail; /* allocation failure */ } } @@ -665,7 +637,7 @@ static cJSON_bool parse_string(cJSON *const item, parse_buffer *const input_buff fail: if (output != NULL) { - input_buffer->hooks.deallocate(output); + loader_free(input_buffer->pAllocator, output); output = NULL; } @@ -677,7 +649,7 @@ static cJSON_bool parse_string(cJSON *const item, parse_buffer *const input_buff } /* Render the cstring provided to an escaped version that can be printed. */ -static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer *const output_buffer) { +static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer *const output_buffer, bool *out_of_memory) { const unsigned char *input_pointer = NULL; unsigned char *output = NULL; unsigned char *output_pointer = NULL; @@ -691,7 +663,7 @@ static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer /* empty string */ if (input == NULL) { - output = ensure(output_buffer, 0); + output = ensure(output_buffer, sizeof(""), out_of_memory); if (output == NULL) { return false; } @@ -722,7 +694,7 @@ static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer } output_length = (size_t)(input_pointer - input) + escape_characters; - output = ensure(output_buffer, output_length + 1); + output = ensure(output_buffer, output_length + sizeof(""), out_of_memory); if (output == NULL) { return false; } @@ -783,17 +755,17 @@ static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer } /* Invoke print_string_ptr (which is useful) on an item. */ -static cJSON_bool print_string(const cJSON *const item, printbuffer *const p) { - return print_string_ptr((unsigned char *)item->valuestring, p); +static cJSON_bool print_string(const cJSON *const item, printbuffer *const p, bool *out_of_memory) { + return print_string_ptr((unsigned char *)item->valuestring, p, out_of_memory); } /* Predeclare these prototypes. */ -static cJSON_bool parse_value(cJSON *const item, parse_buffer *const input_buffer); -static cJSON_bool print_value(const cJSON *const item, printbuffer *const output_buffer); -static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffer); -static cJSON_bool print_array(const cJSON *const item, printbuffer *const output_buffer); -static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buffer); -static cJSON_bool print_object(const cJSON *const item, printbuffer *const output_buffer); +static cJSON_bool parse_value(cJSON *const item, parse_buffer *const input_buffer, bool *out_of_memory); +static cJSON_bool print_value(const cJSON *const item, printbuffer *const output_buffer, bool *out_of_memory); +static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffer, bool *out_of_memory); +static cJSON_bool print_array(const cJSON *const item, printbuffer *const output_buffer, bool *out_of_memory); +static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buffer, bool *out_of_memory); +static cJSON_bool print_object(const cJSON *const item, printbuffer *const output_buffer, bool *out_of_memory); /* Utility to jump whitespace and cr/lf */ static parse_buffer *buffer_skip_whitespace(parse_buffer *const buffer) { @@ -829,7 +801,9 @@ static parse_buffer *skip_utf8_bom(parse_buffer *const buffer) { return buffer; } -CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) { +CJSON_PUBLIC(cJSON *) +loader_cJSON_ParseWithOpts(const VkAllocationCallbacks *pAllocator, const char *value, const char **return_parse_end, + cJSON_bool require_null_terminated, bool *out_of_memory) { size_t buffer_length; if (NULL == value) { @@ -839,14 +813,15 @@ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return /* Adding null character size due to require_null_terminated. */ buffer_length = strlen(value) + sizeof(""); - return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); + return loader_cJSON_ParseWithLengthOpts(pAllocator, value, buffer_length, return_parse_end, require_null_terminated, + out_of_memory); } /* Parse an object - create a new root, and populate. */ CJSON_PUBLIC(cJSON *) -cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, - cJSON_bool require_null_terminated) { - parse_buffer buffer = {0, 0, 0, 0, {0, 0, 0}}; +loader_cJSON_ParseWithLengthOpts(const VkAllocationCallbacks *pAllocator, const char *value, size_t buffer_length, + const char **return_parse_end, cJSON_bool require_null_terminated, bool *out_of_memory) { + parse_buffer buffer = {0, 0, 0, 0, 0}; cJSON *item = NULL; /* reset error position */ @@ -860,15 +835,16 @@ cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char ** buffer.content = (const unsigned char *)value; buffer.length = buffer_length; buffer.offset = 0; - buffer.hooks = global_hooks; + buffer.pAllocator = pAllocator; - item = cJSON_New_Item(&global_hooks); + item = cJSON_New_Item(pAllocator); if (item == NULL) /* memory fail */ { + *out_of_memory = true; goto fail; } - if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) { + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)), out_of_memory)) { /* parse failure. ep is set. */ goto fail; } @@ -888,7 +864,7 @@ cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char ** fail: if (item != NULL) { - cJSON_Delete(item); + loader_cJSON_Delete(item); } if (value != NULL) { @@ -912,16 +888,20 @@ cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char ** return NULL; } -/* Default options for cJSON_Parse */ -CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) { return cJSON_ParseWithOpts(value, 0, 0); } +/* Default options for loader_cJSON_Parse */ +CJSON_PUBLIC(cJSON *) loader_cJSON_Parse(const VkAllocationCallbacks *pAllocator, const char *value, bool *out_of_memory) { + return loader_cJSON_ParseWithOpts(pAllocator, value, 0, 0, out_of_memory); +} -CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) { - return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); +CJSON_PUBLIC(cJSON *) +loader_cJSON_ParseWithLength(const VkAllocationCallbacks *pAllocator, const char *value, size_t buffer_length, + bool *out_of_memory) { + return loader_cJSON_ParseWithLengthOpts(pAllocator, value, buffer_length, 0, 0, out_of_memory); } #define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) -static unsigned char *print(const cJSON *const item, cJSON_bool format, const internal_hooks *const hooks) { +static unsigned char *print(const cJSON *const item, cJSON_bool format, bool *out_of_memory) { static const size_t default_buffer_size = 256; printbuffer buffer[1]; unsigned char *printed = NULL; @@ -929,51 +909,38 @@ static unsigned char *print(const cJSON *const item, cJSON_bool format, const in memset(buffer, 0, sizeof(buffer)); /* create buffer */ - buffer->buffer = (unsigned char *)hooks->allocate(default_buffer_size); + buffer->buffer = (unsigned char *)loader_calloc(item->pAllocator, default_buffer_size, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); buffer->length = default_buffer_size; buffer->format = format; - buffer->hooks = *hooks; + buffer->pAllocator = item->pAllocator; if (buffer->buffer == NULL) { goto fail; } /* print the value */ - if (!print_value(item, buffer)) { + if (!print_value(item, buffer, out_of_memory)) { goto fail; } update_offset(buffer); - /* check if reallocate is available */ - if (hooks->reallocate != NULL) { - printed = (unsigned char *)hooks->reallocate(buffer->buffer, buffer->offset + 1); - if (printed == NULL) { - goto fail; - } - buffer->buffer = NULL; - } else /* otherwise copy the JSON over to a new buffer */ - { - printed = (unsigned char *)hooks->allocate(buffer->offset + 1); - if (printed == NULL) { - goto fail; - } - memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); - printed[buffer->offset] = '\0'; /* just to be sure */ - - /* free the buffer */ - hooks->deallocate(buffer->buffer); - buffer->buffer = NULL; + printed = (unsigned char *)loader_realloc(item->pAllocator, buffer->buffer, buffer->length, buffer->offset + 1, + VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (printed == NULL) { + *out_of_memory = true; + goto fail; } + buffer->buffer = NULL; return printed; fail: if (buffer->buffer != NULL) { - hooks->deallocate(buffer->buffer); + loader_free(item->pAllocator, buffer->buffer); buffer->buffer = NULL; } if (printed != NULL) { - hooks->deallocate(printed); + loader_free(item->pAllocator, printed); printed = NULL; } @@ -981,18 +948,21 @@ static unsigned char *print(const cJSON *const item, cJSON_bool format, const in } /* Render a cJSON item/entity/structure to text. */ -CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) { return (char *)print(item, true, &global_hooks); } +CJSON_PUBLIC(char *) loader_cJSON_Print(const cJSON *item, bool *out_of_memory) { return (char *)print(item, true, out_of_memory); } -CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) { return (char *)print(item, false, &global_hooks); } +CJSON_PUBLIC(char *) loader_cJSON_PrintUnformatted(const cJSON *item, bool *out_of_memory) { + return (char *)print(item, false, out_of_memory); +} -CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) { - printbuffer p = {0, 0, 0, 0, 0, 0, {0, 0, 0}}; +CJSON_PUBLIC(char *) +loader_cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt, bool *out_of_memory) { + printbuffer p = {0, 0, 0, 0, 0, 0, 0}; if (prebuffer < 0) { return NULL; } - p.buffer = (unsigned char *)global_hooks.allocate((size_t)prebuffer); + p.buffer = (unsigned char *)loader_alloc(item->pAllocator, (size_t)prebuffer, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); if (!p.buffer) { return NULL; } @@ -1001,10 +971,10 @@ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON p.offset = 0; p.noalloc = false; p.format = fmt; - p.hooks = global_hooks; + p.pAllocator = item->pAllocator; - if (!print_value(item, &p)) { - global_hooks.deallocate(p.buffer); + if (!print_value(item, &p, out_of_memory)) { + loader_free(item->pAllocator, p.buffer); p.buffer = NULL; return NULL; } @@ -1012,8 +982,9 @@ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON return (char *)p.buffer; } -CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) { - printbuffer p = {0, 0, 0, 0, 0, 0, {0, 0, 0}}; +CJSON_PUBLIC(cJSON_bool) +loader_cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) { + printbuffer p = {0, 0, 0, 0, 0, 0, 0}; if ((length < 0) || (buffer == NULL)) { return false; @@ -1024,13 +995,13 @@ CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, cons p.offset = 0; p.noalloc = true; p.format = format; - p.hooks = global_hooks; - - return print_value(item, &p); + p.pAllocator = item->pAllocator; + bool out_of_memory = false; + return print_value(item, &p, &out_of_memory); } /* Parser core - when encountering text, process appropriately. */ -static cJSON_bool parse_value(cJSON *const item, parse_buffer *const input_buffer) { +static cJSON_bool parse_value(cJSON *const item, parse_buffer *const input_buffer, bool *out_of_memory) { if ((input_buffer == NULL) || (input_buffer->content == NULL)) { return false; /* no input */ } @@ -1057,7 +1028,7 @@ static cJSON_bool parse_value(cJSON *const item, parse_buffer *const input_buffe } /* string */ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) { - return parse_string(item, input_buffer); + return parse_string(item, input_buffer, out_of_memory); } /* number */ if (can_access_at_index(input_buffer, 0) && @@ -1067,18 +1038,18 @@ static cJSON_bool parse_value(cJSON *const item, parse_buffer *const input_buffe } /* array */ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) { - return parse_array(item, input_buffer); + return parse_array(item, input_buffer, out_of_memory); } /* object */ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) { - return parse_object(item, input_buffer); + return parse_object(item, input_buffer, out_of_memory); } return false; } /* Render a value to text. */ -static cJSON_bool print_value(const cJSON *const item, printbuffer *const output_buffer) { +static cJSON_bool print_value(const cJSON *const item, printbuffer *const output_buffer, bool *out_of_memory) { unsigned char *output = NULL; if ((item == NULL) || (output_buffer == NULL)) { @@ -1087,7 +1058,7 @@ static cJSON_bool print_value(const cJSON *const item, printbuffer *const output switch ((item->type) & 0xFF) { case cJSON_NULL: - output = ensure(output_buffer, 5); + output = ensure(output_buffer, 5, out_of_memory); if (output == NULL) { return false; } @@ -1095,7 +1066,7 @@ static cJSON_bool print_value(const cJSON *const item, printbuffer *const output return true; case cJSON_False: - output = ensure(output_buffer, 6); + output = ensure(output_buffer, 6, out_of_memory); if (output == NULL) { return false; } @@ -1103,7 +1074,7 @@ static cJSON_bool print_value(const cJSON *const item, printbuffer *const output return true; case cJSON_True: - output = ensure(output_buffer, 5); + output = ensure(output_buffer, 5, out_of_memory); if (output == NULL) { return false; } @@ -1111,7 +1082,7 @@ static cJSON_bool print_value(const cJSON *const item, printbuffer *const output return true; case cJSON_Number: - return print_number(item, output_buffer); + return print_number(item, output_buffer, out_of_memory); case cJSON_Raw: { size_t raw_length = 0; @@ -1120,7 +1091,7 @@ static cJSON_bool print_value(const cJSON *const item, printbuffer *const output } raw_length = strlen(item->valuestring) + sizeof(""); - output = ensure(output_buffer, raw_length); + output = ensure(output_buffer, raw_length, out_of_memory); if (output == NULL) { return false; } @@ -1129,13 +1100,13 @@ static cJSON_bool print_value(const cJSON *const item, printbuffer *const output } case cJSON_String: - return print_string(item, output_buffer); + return print_string(item, output_buffer, out_of_memory); case cJSON_Array: - return print_array(item, output_buffer); + return print_array(item, output_buffer, out_of_memory); case cJSON_Object: - return print_object(item, output_buffer); + return print_object(item, output_buffer, out_of_memory); default: return false; @@ -1143,7 +1114,7 @@ static cJSON_bool print_value(const cJSON *const item, printbuffer *const output } /* Build an array from input text. */ -static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffer) { +static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffer, bool *out_of_memory) { cJSON *head = NULL; /* head of the linked list */ cJSON *current_item = NULL; @@ -1175,8 +1146,9 @@ static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffe /* loop through the comma separated array elements */ do { /* allocate next item */ - cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + cJSON *new_item = cJSON_New_Item(input_buffer->pAllocator); if (new_item == NULL) { + *out_of_memory = true; goto fail; /* allocation failure */ } @@ -1194,7 +1166,7 @@ static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffe /* parse next value */ input_buffer->offset++; buffer_skip_whitespace(input_buffer); - if (!parse_value(current_item, input_buffer)) { + if (!parse_value(current_item, input_buffer, out_of_memory)) { goto fail; /* failed to parse value */ } buffer_skip_whitespace(input_buffer); @@ -1220,14 +1192,14 @@ static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffe fail: if (head != NULL) { - cJSON_Delete(head); + loader_cJSON_Delete(head); } return false; } /* Render an array to text */ -static cJSON_bool print_array(const cJSON *const item, printbuffer *const output_buffer) { +static cJSON_bool print_array(const cJSON *const item, printbuffer *const output_buffer, bool *out_of_memory) { unsigned char *output_pointer = NULL; size_t length = 0; cJSON *current_element = item->child; @@ -1238,7 +1210,7 @@ static cJSON_bool print_array(const cJSON *const item, printbuffer *const output /* Compose the output array. */ /* opening square bracket */ - output_pointer = ensure(output_buffer, 1); + output_pointer = ensure(output_buffer, 1, out_of_memory); if (output_pointer == NULL) { return false; } @@ -1248,13 +1220,13 @@ static cJSON_bool print_array(const cJSON *const item, printbuffer *const output output_buffer->depth++; while (current_element != NULL) { - if (!print_value(current_element, output_buffer)) { + if (!print_value(current_element, output_buffer, out_of_memory)) { return false; } update_offset(output_buffer); if (current_element->next) { length = (size_t)(output_buffer->format ? 2 : 1); - output_pointer = ensure(output_buffer, length + 1); + output_pointer = ensure(output_buffer, length + 1, out_of_memory); if (output_pointer == NULL) { return false; } @@ -1268,7 +1240,7 @@ static cJSON_bool print_array(const cJSON *const item, printbuffer *const output current_element = current_element->next; } - output_pointer = ensure(output_buffer, 2); + output_pointer = ensure(output_buffer, 2, out_of_memory); if (output_pointer == NULL) { return false; } @@ -1280,7 +1252,7 @@ static cJSON_bool print_array(const cJSON *const item, printbuffer *const output } /* Build an object from the text. */ -static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buffer) { +static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buffer, bool *out_of_memory) { cJSON *head = NULL; /* linked list head */ cJSON *current_item = NULL; @@ -1310,8 +1282,9 @@ static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buff /* loop through the comma separated array elements */ do { /* allocate next item */ - cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + cJSON *new_item = cJSON_New_Item(input_buffer->pAllocator); if (new_item == NULL) { + *out_of_memory = true; goto fail; /* allocation failure */ } @@ -1333,7 +1306,7 @@ static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buff /* parse the name of the child */ input_buffer->offset++; buffer_skip_whitespace(input_buffer); - if (!parse_string(current_item, input_buffer)) { + if (!parse_string(current_item, input_buffer, out_of_memory)) { goto fail; /* failed to parse name */ } buffer_skip_whitespace(input_buffer); @@ -1349,7 +1322,7 @@ static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buff /* parse the value */ input_buffer->offset++; buffer_skip_whitespace(input_buffer); - if (!parse_value(current_item, input_buffer)) { + if (!parse_value(current_item, input_buffer, out_of_memory)) { goto fail; /* failed to parse value */ } buffer_skip_whitespace(input_buffer); @@ -1374,14 +1347,14 @@ static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buff fail: if (head != NULL) { - cJSON_Delete(head); + loader_cJSON_Delete(head); } return false; } /* Render an object to text. */ -static cJSON_bool print_object(const cJSON *const item, printbuffer *const output_buffer) { +static cJSON_bool print_object(const cJSON *const item, printbuffer *const output_buffer, bool *out_of_memory) { unsigned char *output_pointer = NULL; size_t length = 0; cJSON *current_item = item->child; @@ -1392,7 +1365,7 @@ static cJSON_bool print_object(const cJSON *const item, printbuffer *const outpu /* Compose the output: */ length = (size_t)(output_buffer->format ? 2 : 1); /* fmt: {\n */ - output_pointer = ensure(output_buffer, length + 1); + output_pointer = ensure(output_buffer, length + 1, out_of_memory); if (output_pointer == NULL) { return false; } @@ -1407,7 +1380,7 @@ static cJSON_bool print_object(const cJSON *const item, printbuffer *const outpu while (current_item) { if (output_buffer->format) { size_t i; - output_pointer = ensure(output_buffer, output_buffer->depth); + output_pointer = ensure(output_buffer, output_buffer->depth, out_of_memory); if (output_pointer == NULL) { return false; } @@ -1418,13 +1391,13 @@ static cJSON_bool print_object(const cJSON *const item, printbuffer *const outpu } /* print key */ - if (!print_string_ptr((unsigned char *)current_item->string, output_buffer)) { + if (!print_string_ptr((unsigned char *)current_item->string, output_buffer, out_of_memory)) { return false; } update_offset(output_buffer); length = (size_t)(output_buffer->format ? 2 : 1); - output_pointer = ensure(output_buffer, length); + output_pointer = ensure(output_buffer, length, out_of_memory); if (output_pointer == NULL) { return false; } @@ -1435,14 +1408,14 @@ static cJSON_bool print_object(const cJSON *const item, printbuffer *const outpu output_buffer->offset += length; /* print value */ - if (!print_value(current_item, output_buffer)) { + if (!print_value(current_item, output_buffer, out_of_memory)) { return false; } update_offset(output_buffer); /* print comma if not last */ length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); - output_pointer = ensure(output_buffer, length + 1); + output_pointer = ensure(output_buffer, length + 1, out_of_memory); if (output_pointer == NULL) { return false; } @@ -1459,7 +1432,7 @@ static cJSON_bool print_object(const cJSON *const item, printbuffer *const outpu current_item = current_item->next; } - output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2, out_of_memory); if (output_pointer == NULL) { return false; } @@ -1477,7 +1450,7 @@ static cJSON_bool print_object(const cJSON *const item, printbuffer *const outpu } /* Get Array size/item / object item. */ -CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) { +CJSON_PUBLIC(int) loader_cJSON_GetArraySize(const cJSON *array) { cJSON *child = NULL; size_t size = 0; @@ -1513,7 +1486,7 @@ static cJSON *get_array_item(const cJSON *array, size_t index) { return current_child; } -CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) { +CJSON_PUBLIC(cJSON *) loader_cJSON_GetArrayItem(const cJSON *array, int index) { if (index < 0) { return NULL; } @@ -1547,16 +1520,16 @@ static cJSON *get_object_item(const cJSON *const object, const char *const name, return current_element; } -CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON *const object, const char *const string) { +CJSON_PUBLIC(cJSON *) loader_cJSON_GetObjectItem(const cJSON *const object, const char *const string) { return get_object_item(object, string, false); } -CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON *const object, const char *const string) { +CJSON_PUBLIC(cJSON *) loader_cJSON_GetObjectItemCaseSensitive(const cJSON *const object, const char *const string) { return get_object_item(object, string, true); } -CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) { - return cJSON_GetObjectItem(object, string) ? 1 : 0; +CJSON_PUBLIC(cJSON_bool) loader_cJSON_HasObjectItem(const cJSON *object, const char *string) { + return loader_cJSON_GetObjectItem(object, string) ? 1 : 0; } static void skip_oneline_comment(char **input) { @@ -1602,7 +1575,7 @@ static void minify_string(char **input, char **output) { } } -CJSON_PUBLIC(void) cJSON_Minify(char *json) { +CJSON_PUBLIC(void) loader_cJSON_Minify(char *json) { char *into = json; if (json == NULL) { @@ -1643,7 +1616,7 @@ CJSON_PUBLIC(void) cJSON_Minify(char *json) { *into = '\0'; } -CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON *const item) { +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsInvalid(const cJSON *const item) { if (item == NULL) { return false; } @@ -1651,7 +1624,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON *const item) { return (item->type & 0xFF) == cJSON_Invalid; } -CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON *const item) { +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsFalse(const cJSON *const item) { if (item == NULL) { return false; } @@ -1659,7 +1632,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON *const item) { return (item->type & 0xFF) == cJSON_False; } -CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON *const item) { +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsTrue(const cJSON *const item) { if (item == NULL) { return false; } @@ -1667,14 +1640,14 @@ CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON *const item) { return (item->type & 0xff) == cJSON_True; } -CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON *const item) { +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsBool(const cJSON *const item) { if (item == NULL) { return false; } return (item->type & (cJSON_True | cJSON_False)) != 0; } -CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON *const item) { +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsNull(const cJSON *const item) { if (item == NULL) { return false; } @@ -1682,7 +1655,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON *const item) { return (item->type & 0xFF) == cJSON_NULL; } -CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON *const item) { +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsNumber(const cJSON *const item) { if (item == NULL) { return false; } @@ -1690,7 +1663,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON *const item) { return (item->type & 0xFF) == cJSON_Number; } -CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON *const item) { +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsString(const cJSON *const item) { if (item == NULL) { return false; } @@ -1698,7 +1671,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON *const item) { return (item->type & 0xFF) == cJSON_String; } -CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON *const item) { +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsArray(const cJSON *const item) { if (item == NULL) { return false; } @@ -1714,7 +1687,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON *const item) { return (item->type & 0xFF) == cJSON_Object; } -CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON *const item) { +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsRaw(const cJSON *const item) { if (item == NULL) { return false; } @@ -1722,7 +1695,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON *const item) { return (item->type & 0xFF) == cJSON_Raw; } -CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON *const a, const cJSON *const b, const cJSON_bool case_sensitive) { +CJSON_PUBLIC(cJSON_bool) loader_cJSON_Compare(const cJSON *const a, const cJSON *const b, const cJSON_bool case_sensitive) { if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) { return false; } @@ -1777,7 +1750,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON *const a, const cJSON *const cJSON *b_element = b->child; for (; (a_element != NULL) && (b_element != NULL);) { - if (!cJSON_Compare(a_element, b_element, case_sensitive)) { + if (!loader_cJSON_Compare(a_element, b_element, case_sensitive)) { return false; } @@ -1803,7 +1776,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON *const a, const cJSON *const return false; } - if (!cJSON_Compare(a_element, b_element, case_sensitive)) { + if (!loader_cJSON_Compare(a_element, b_element, case_sensitive)) { return false; } } @@ -1816,7 +1789,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON *const a, const cJSON *const return false; } - if (!cJSON_Compare(b_element, a_element, case_sensitive)) { + if (!loader_cJSON_Compare(b_element, a_element, case_sensitive)) { return false; } } diff --git a/loader/cJSON.h b/loader/cJSON.h index 10cf40754..85f6f07d7 100644 --- a/loader/cJSON.h +++ b/loader/cJSON.h @@ -86,6 +86,7 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ #define CJSON_VERSION_PATCH 18 #include +#include /* cJSON Types: */ #define cJSON_Invalid (0) @@ -101,6 +102,10 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ #define cJSON_IsReference 256 #define cJSON_StringIsConst 512 +/* loader type declarations for allocation hooks */ +typedef struct VkAllocationCallbacks VkAllocationCallbacks; +struct loader_instance; + /* The cJSON structure: */ typedef struct cJSON { /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ @@ -121,15 +126,10 @@ typedef struct cJSON { /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ char *string; + /* pointer to the allocation callbacks to use */ + const VkAllocationCallbacks *pAllocator; } cJSON; -typedef struct cJSON_Hooks { - /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow - * passing those functions directly. */ - void *(CJSON_CDECL *malloc_fn)(size_t sz); - void(CJSON_CDECL *free_fn)(void *ptr); -} cJSON_Hooks; - typedef int cJSON_bool; /* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. @@ -144,63 +144,68 @@ typedef int cJSON_bool; #define CJSON_CIRCULAR_LIMIT 10000 #endif -/* Memory Management: the caller is always responsible to free instthe results from all variants of cJSON_Parse (with cJSON_Delete) - * and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, - * where the caller has full responsibility of the buffer. */ +/* Memory Management: the caller is always responsible to free instthe results from all variants of loader_cJSON_Parse (with + * loader_cJSON_Delete) and loader_loader_cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The + * exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ /* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ -CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); -CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); +CJSON_PUBLIC(cJSON *) loader_cJSON_Parse(const VkAllocationCallbacks *pAllocator, const char *value, bool *out_of_memory); +CJSON_PUBLIC(cJSON *) +loader_cJSON_ParseWithLength(const VkAllocationCallbacks *pAllocator, const char *value, size_t buffer_length, bool *out_of_memory); /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte * parsed. */ /* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will * match cJSON_GetErrorPtr(). */ -CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); CJSON_PUBLIC(cJSON *) -cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, - cJSON_bool require_null_terminated); +loader_cJSON_ParseWithOpts(const VkAllocationCallbacks *pAllocator, const char *value, const char **return_parse_end, + cJSON_bool require_null_terminated, bool *out_of_memory); +CJSON_PUBLIC(cJSON *) +loader_cJSON_ParseWithLengthOpts(const VkAllocationCallbacks *pAllocator, const char *value, size_t buffer_length, + const char **return_parse_end, cJSON_bool require_null_terminated, bool *out_of_memory); /* Render a cJSON entity to text for transfer/storage. */ -CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +CJSON_PUBLIC(char *) loader_cJSON_Print(const cJSON *item, bool *out_of_memory); /* Render a cJSON entity to text for transfer/storage without any formatting. */ -CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +CJSON_PUBLIC(char *) loader_cJSON_PrintUnformatted(const cJSON *item, bool *out_of_memory); /* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces * reallocation. fmt=0 gives unformatted, =1 gives formatted */ -CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +CJSON_PUBLIC(char *) +loader_cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt, bool *out_of_memory); /* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on * failure. */ /* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you * actually need */ -CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +CJSON_PUBLIC(cJSON_bool) +loader_cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); /* Delete a cJSON entity and all subentities. */ -CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); +CJSON_PUBLIC(void) loader_cJSON_Delete(cJSON *item); /* Returns the number of items in an array (or object). */ -CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +CJSON_PUBLIC(int) loader_cJSON_GetArraySize(const cJSON *array); /* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ -CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +CJSON_PUBLIC(cJSON *) loader_cJSON_GetArrayItem(const cJSON *array, int index); /* Get item "string" from object. Case insensitive. */ -CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON *const object, const char *const string); -CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON *const object, const char *const string); -CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) loader_cJSON_GetObjectItem(const cJSON *const object, const char *const string); +CJSON_PUBLIC(cJSON *) loader_cJSON_GetObjectItemCaseSensitive(const cJSON *const object, const char *const string); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_HasObjectItem(const cJSON *object, const char *string); /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make - * sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ + * sense of it. Defined when loader_cJSON_Parse() returns 0. 0 when loader_cJSON_Parse() succeeds. */ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); /* Check item type and return its value */ -CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON *const item); -CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON *const item); +CJSON_PUBLIC(char *) loader_cJSON_GetStringValue(const cJSON *const item); +CJSON_PUBLIC(double) loader_cJSON_GetNumberValue(const cJSON *const item); /* These functions check the type of an item */ -CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON *const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON *const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON *const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON *const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON *const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON *const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON *const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON *const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON *const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsInvalid(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsFalse(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsTrue(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsBool(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsNull(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsNumber(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsString(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsArray(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsObject(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsRaw(const cJSON *const item); /* Macro for iterating over an array or object */ #define cJSON_ArrayForEach(element, array) \ diff --git a/loader/loader.c b/loader/loader.c index 6afa1de25..65fe7b284 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -2514,11 +2514,15 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade result = loader_copy_to_new_str(inst, filename, &props.manifest_file_name); if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; - char *library_path_str = loader_cJSON_Print(library_path); + bool out_of_memory = false; + char *library_path_str = loader_cJSON_Print(library_path, &out_of_memory); + if (out_of_memory) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } if (NULL == library_path_str) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, "Skipping layer due to problem accessing the library_path value in manifest JSON file %s", filename); - result = VK_ERROR_OUT_OF_HOST_MEMORY; goto out; } @@ -2830,8 +2834,10 @@ VkResult loader_add_layer_properties(const struct loader_instance *inst, struct if (item == NULL) { goto out; } - file_vers = loader_cJSON_PrintUnformatted(item); - if (NULL == file_vers) { + + bool out_of_memory = false; + file_vers = loader_cJSON_PrintUnformatted(item, &out_of_memory); + if (out_of_memory) { result = VK_ERROR_OUT_OF_HOST_MEMORY; goto out; } @@ -3571,8 +3577,9 @@ VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *fil goto out; } - file_vers_str = loader_cJSON_Print(item); - if (NULL == file_vers_str) { + bool out_of_memory = false; + file_vers_str = loader_cJSON_Print(item, &out_of_memory); + if (out_of_memory) { // Only reason the print can fail is if there was an allocation issue loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "loader_parse_icd_manifest: Failed retrieving ICD JSON %s \'file_format_version\' field. Skipping ICD JSON", @@ -3608,8 +3615,9 @@ VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *fil res = VK_ERROR_INCOMPATIBLE_DRIVER; goto out; } - char *library_path = loader_cJSON_Print(item); - if (!library_path) { + + char *library_path = loader_cJSON_Print(item, &out_of_memory); + if (out_of_memory) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "loader_parse_icd_manifest: Failed retrieving ICD JSON %s \'library_path\' field. Skipping ICD JSON.", file_str); res = VK_ERROR_OUT_OF_HOST_MEMORY; @@ -3638,8 +3646,8 @@ VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *fil res = VK_ERROR_INCOMPATIBLE_DRIVER; goto out; } - version_str = loader_cJSON_Print(item); - if (NULL == version_str) { + version_str = loader_cJSON_Print(item, &out_of_memory); + if (out_of_memory) { // Only reason the print can fail is if there was an allocation issue loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "loader_parse_icd_manifest: Failed retrieving ICD JSON %s \'api_version\' field. Skipping ICD JSON.", file_str); @@ -3671,7 +3679,11 @@ VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *fil item = loader_cJSON_GetObjectItem(itemICD, "library_arch"); if (item != NULL) { - library_arch_str = loader_cJSON_Print(item); + library_arch_str = loader_cJSON_Print(item, &out_of_memory); + if (out_of_memory) { + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } if (NULL != library_arch_str) { // cJSON includes the quotes by default, so we need to look for those here if ((strncmp(library_arch_str, "32", 4) == 0 && sizeof(void *) != 4) || @@ -3682,9 +3694,6 @@ VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *fil res = VK_ERROR_INCOMPATIBLE_DRIVER; goto out; } - } else { - res = VK_ERROR_OUT_OF_HOST_MEMORY; - goto out; } } out: diff --git a/loader/loader_json.c b/loader/loader_json.c index a92b90ebf..883704d10 100644 --- a/loader/loader_json.c +++ b/loader/loader_json.c @@ -44,7 +44,8 @@ #endif #ifdef _WIN32 -static VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, char **out_buff) { +static VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, size_t *out_len, + char **out_buff) { HANDLE file_handle = INVALID_HANDLE_VALUE; DWORD len = 0, read_len = 0; VkResult res = VK_SUCCESS; @@ -81,6 +82,7 @@ static VkResult loader_read_entire_file(const struct loader_instance *inst, cons res = VK_ERROR_INITIALIZATION_FAILED; goto out; } + *out_len = len + 1; (*out_buff)[len] = '\0'; out: @@ -90,7 +92,8 @@ static VkResult loader_read_entire_file(const struct loader_instance *inst, cons return res; } #elif COMMON_UNIX_PLATFORMS -static VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, char **out_buff) { +static VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, size_t *out_len, + char **out_buff) { FILE *file = NULL; struct stat stats = {0}; VkResult res = VK_SUCCESS; @@ -117,6 +120,7 @@ static VkResult loader_read_entire_file(const struct loader_instance *inst, cons res = VK_ERROR_INITIALIZATION_FAILED; goto out; } + *out_len = stats.st_size + 1; (*out_buff)[stats.st_size] = '\0'; out: @@ -127,7 +131,7 @@ static VkResult loader_read_entire_file(const struct loader_instance *inst, cons } #else #warning fopen not available on this platform -VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, char **out_buff) { +VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, size_t *out_len, char **out_buff) { return VK_ERROR_INITIALIZATION_FAILED; } #endif @@ -138,13 +142,15 @@ VkResult loader_get_json(const struct loader_instance *inst, const char *filenam assert(json != NULL); + size_t json_len = 0; *json = NULL; - res = loader_read_entire_file(inst, filename, &json_buf); - if (VK_SUCCESS != res) goto out; - - // Parse text from file + res = loader_read_entire_file(inst, filename, &json_len, &json_buf); + if (VK_SUCCESS != res) { + goto out; + } bool out_of_memory = false; - *json = cJSON_Parse(inst ? &inst->alloc_callbacks : NULL, json_buf, &out_of_memory); + // Parse text from file + *json = loader_cJSON_ParseWithLength(inst ? &inst->alloc_callbacks : NULL, json_buf, json_len, &out_of_memory); if (out_of_memory) { loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Out of Memory error occurred while parsing JSON file %s.", filename); @@ -171,9 +177,9 @@ VkResult loader_parse_json_string_to_existing_str(const struct loader_instance * if (NULL == item) { return VK_ERROR_INITIALIZATION_FAILED; } - - char *str = loader_cJSON_Print(item); - if (str == NULL) { + bool out_of_memory = false; + char *str = loader_cJSON_Print(item, &out_of_memory); + if (out_of_memory) { return VK_ERROR_OUT_OF_HOST_MEMORY; } if (NULL != out_string) { @@ -192,8 +198,9 @@ VkResult loader_parse_json_string(cJSON *object, const char *key, char **out_str return VK_ERROR_INITIALIZATION_FAILED; } - char *str = loader_cJSON_Print(item); - if (str == NULL) { + bool out_of_memory = false; + char *str = loader_cJSON_Print(item, &out_of_memory); + if (out_of_memory || NULL == str) { return VK_ERROR_OUT_OF_HOST_MEMORY; } if (NULL != out_string) { @@ -223,8 +230,9 @@ VkResult loader_parse_json_array_of_strings(const struct loader_instance *inst, if (element == NULL) { return VK_ERROR_INITIALIZATION_FAILED; } - char *out_data = loader_cJSON_Print(element); - if (out_data == NULL) { + bool out_of_memory = false; + char *out_data = loader_cJSON_Print(element, &out_of_memory); + if (out_of_memory) { res = VK_ERROR_OUT_OF_HOST_MEMORY; goto out; } diff --git a/loader/settings.c b/loader/settings.c index ea0273701..d45c4cb5c 100644 --- a/loader/settings.c +++ b/loader/settings.c @@ -394,7 +394,12 @@ VkResult get_loader_settings(const struct loader_instance* inst, loader_settings if (NULL == app_key_json) { continue; } - char* app_key = loader_cJSON_Print(app_key_json); + bool out_of_memory = false; + char* app_key = loader_cJSON_Print(app_key_json, &out_of_memory); + if (out_of_memory) { + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } if (NULL == app_key) { continue; } From 048f3fb2a7e13a55b7e7aedfd64545c4c20d0043 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Fri, 22 Nov 2024 10:05:08 -0600 Subject: [PATCH 09/23] Pass error string using "%s" format specifier Passing the error string directly to loader_log() as the format argument can cause crashes since the string comes from JSON files on the system and may contain format specifiers which vsnprintf will try to use and subsequently fail. --- loader/loader.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/loader/loader.c b/loader/loader.c index 65fe7b284..94e8159ce 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -221,7 +221,7 @@ void loader_handle_load_library_error(const struct loader_instance *inst, const } else if (NULL != lib_status) { *lib_status = LOADER_LAYER_LIB_ERROR_FAILED_TO_LOAD; } - loader_log(inst, err_flag, 0, error_message); + loader_log(inst, err_flag, 0, "%s", error_message); } VKAPI_ATTR VkResult VKAPI_CALL vkSetInstanceDispatch(VkInstance instance, void *object) { @@ -2586,7 +2586,8 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade goto out; } - if (!disable_environment->child || disable_environment->child->type != cJSON_String) { + if (!disable_environment->child || disable_environment->child->type != cJSON_String || + !disable_environment->child->string || !disable_environment->child->valuestring) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, "Didn't find required layer child value disable_environment in manifest JSON file, skipping this layer " "(Policy #LLP_LAYER_9)"); @@ -2827,7 +2828,7 @@ VkResult loader_add_layer_properties(const struct loader_instance *inst, struct loader_api_version json_version = {0, 0, 0}; char *file_vers = NULL; // Make sure sure the top level json value is an object - if (!json || json->type != 6) { + if (!json || json->type != cJSON_Object) { goto out; } item = loader_cJSON_GetObjectItem(json, "file_format_version"); From 08838f40741ae813283e2b7db11f69beffacde21 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Fri, 22 Nov 2024 11:21:30 -0600 Subject: [PATCH 10/23] Add compiler checking to loader_log arguments By adding a per-compiler __attribute, we can tell compilers to check the variable arguments passed into loader_log using the format argument. This caught several instances of more parameters being passed in than were in the format string. --- loader/loader.c | 5 ++--- loader/loader_windows.c | 20 ++++++++++---------- loader/log.c | 3 ++- loader/log.h | 12 ++++++++++-- loader/settings.c | 2 +- 5 files changed, 25 insertions(+), 17 deletions(-) diff --git a/loader/loader.c b/loader/loader.c index 94e8159ce..ed454f13d 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -1077,7 +1077,7 @@ bool loader_implicit_layer_is_enabled(const struct loader_instance *inst, const loader_free_getenv(env_value, inst); } else if ((prop->type_flags & VK_LAYER_TYPE_FLAG_EXPLICIT_LAYER) == 0) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, - "Implicit layer \"%s\" missing disabled environment variable!", prop->info.layerName, VK_LAYERS_DISABLE_ENV_VAR); + "Implicit layer \"%s\" missing disabled environment variable!", prop->info.layerName); } // Enable this layer if it is included in the override layer @@ -1778,8 +1778,7 @@ VkResult loader_scanned_icd_add(const struct loader_instance *inst, struct loade // This shouldn't happen, but the check is necessary because dlopen returns a handle to the main program when // filename is NULL if (filename == NULL) { - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_scanned_icd_add: A NULL filename was used, skipping this ICD", - filename); + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_scanned_icd_add: A NULL filename was used, skipping this ICD"); res = VK_ERROR_INCOMPATIBLE_DRIVER; goto out; } diff --git a/loader/loader_windows.c b/loader/loader_windows.c index bf00508a5..f73659c09 100644 --- a/loader/loader_windows.c +++ b/loader/loader_windows.c @@ -145,7 +145,7 @@ bool windows_add_json_entry(const struct loader_instance *inst, loader_instance_heap_realloc(inst, *reg_data, *total_size, *total_size * 2, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); if (NULL == new_ptr) { loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, - "windows_add_json_entry: Failed to reallocate space for registry value of size %d for key %s", + "windows_add_json_entry: Failed to reallocate space for registry value of size %ld for key %s", *total_size * 2, json_path); *result = VK_ERROR_OUT_OF_HOST_MEMORY; return false; @@ -183,7 +183,7 @@ bool windows_get_device_registry_entry(const struct loader_instance *inst, char CONFIGRET status = CM_Open_DevNode_Key(dev_id, KEY_QUERY_VALUE, 0, RegDisposition_OpenExisting, &hkrKey, CM_REGISTRY_SOFTWARE); if (status != CR_SUCCESS) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, - "windows_get_device_registry_entry: Failed to open registry key for DeviceID(%d)", dev_id); + "windows_get_device_registry_entry: Failed to open registry key for DeviceID(%ld)", dev_id); *result = VK_ERROR_INCOMPATIBLE_DRIVER; return false; } @@ -194,10 +194,10 @@ bool windows_get_device_registry_entry(const struct loader_instance *inst, char if (ret != ERROR_SUCCESS) { if (ret == ERROR_FILE_NOT_FOUND) { loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, - "windows_get_device_registry_entry: Device ID(%d) Does not contain a value for \"%s\"", dev_id, value_name); + "windows_get_device_registry_entry: Device ID(%ld) Does not contain a value for \"%s\"", dev_id, value_name); } else { loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, - "windows_get_device_registry_entry: DeviceID(%d) Failed to obtain %s size", dev_id, value_name); + "windows_get_device_registry_entry: DeviceID(%ld) Failed to obtain %s size", dev_id, value_name); } goto out; } @@ -214,7 +214,7 @@ bool windows_get_device_registry_entry(const struct loader_instance *inst, char if (ret != ERROR_SUCCESS) { loader_log(inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_DRIVER_BIT, 0, - "windows_get_device_registry_entry: DeviceID(%d) Failed to obtain %s", value_name); + "windows_get_device_registry_entry: DeviceID(%ld) Failed to obtain %s", dev_id, value_name); *result = VK_ERROR_INCOMPATIBLE_DRIVER; goto out; } @@ -308,7 +308,7 @@ VkResult windows_get_device_registry_files(const struct loader_instance *inst, u status = CM_Get_Child(&childID, devID, 0); if (status != CR_SUCCESS) { loader_log(inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0, - "windows_get_device_registry_files: unable to open child-device error:%d", status); + "windows_get_device_registry_files: unable to open child-device error:%ld", status); continue; } @@ -317,12 +317,12 @@ VkResult windows_get_device_registry_files(const struct loader_instance *inst, u CM_Get_Device_IDW(childID, buffer, MAX_DEVICE_ID_LEN, 0); loader_log(inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0, - "windows_get_device_registry_files: Opening child device %d - %ls", childID, buffer); + "windows_get_device_registry_files: Opening child device %ld - %ls", childID, buffer); status = CM_Get_DevNode_Registry_PropertyW(childID, CM_DRP_CLASSGUID, NULL, &childGuid, &childGuidSize, 0); if (status != CR_SUCCESS) { loader_log(inst, VULKAN_LOADER_ERROR_BIT | log_target_flag, 0, - "windows_get_device_registry_files: unable to obtain GUID for:%d error:%d", childID, status); + "windows_get_device_registry_files: unable to obtain GUID for:%ld error:%ld", childID, status); result = VK_ERROR_INCOMPATIBLE_DRIVER; continue; @@ -330,7 +330,7 @@ VkResult windows_get_device_registry_files(const struct loader_instance *inst, u if (wcscmp(childGuid, softwareComponentGUID) != 0) { loader_log(inst, VULKAN_LOADER_DEBUG_BIT | log_target_flag, 0, - "windows_get_device_registry_files: GUID for %d is not SoftwareComponent skipping", childID); + "windows_get_device_registry_files: GUID for %ld is not SoftwareComponent skipping", childID); continue; } @@ -451,7 +451,7 @@ VkResult windows_get_registry_files(const struct loader_instance *inst, char *lo if (NULL == new_ptr) { loader_log( inst, VULKAN_LOADER_ERROR_BIT | log_target_flag, 0, - "windows_get_registry_files: Failed to reallocate space for registry value of size %d for key %s", + "windows_get_registry_files: Failed to reallocate space for registry value of size %ld for key %s", *reg_data_size * 2, name); RegCloseKey(key); result = VK_ERROR_OUT_OF_HOST_MEMORY; diff --git a/loader/log.c b/loader/log.c index d404366ed..2501c634f 100644 --- a/loader/log.c +++ b/loader/log.c @@ -142,7 +142,8 @@ void generate_debug_flag_str(VkFlags msg_type, size_t cmd_line_size, char *cmd_l #undef STRNCAT_TO_BUFFER } -void loader_log(const struct loader_instance *inst, VkFlags msg_type, int32_t msg_code, const char *format, ...) { +void DECORATE_PRINTF(4, 5) + loader_log(const struct loader_instance *inst, VkFlags msg_type, int32_t msg_code, const char *format, ...) { (void)msg_code; char msg[512] = {0}; diff --git a/loader/log.h b/loader/log.h index 1880c5797..6f69b43b4 100644 --- a/loader/log.h +++ b/loader/log.h @@ -65,10 +65,18 @@ void generate_debug_flag_str(VkFlags msg_type, size_t cmd_line_size, char *cmd_l #define ASM_NAME(name) #endif +#if defined(__clang__) +#define DECORATE_PRINTF(_fmt_argnum, _first_param_num) __attribute__((format(printf, _fmt_argnum, _first_param_num))) +#elif defined(__GNUC__) +#define DECORATE_PRINTF(_fmt_argnum, _first_param_num) __attribute__((format(gnu_printf, _fmt_argnum, _first_param_num))) +#else +#define DECORATE_PRINTF(_fmt_num, _first_param_num) +#endif + // Logs a message to stderr // May output to DebugUtils if the instance isn't null and the extension is enabled. -void loader_log(const struct loader_instance *inst, VkFlags msg_type, int32_t msg_code, const char *format, ...) - ASM_NAME("loader_log"); +void DECORATE_PRINTF(4, 5) loader_log(const struct loader_instance *inst, VkFlags msg_type, int32_t msg_code, const char *format, + ...) ASM_NAME("loader_log"); // Used for the assembly code to emit an specific error message // This is a work around for linux 32 bit error handling not passing relocatable strings correctly diff --git a/loader/settings.c b/loader/settings.c index d45c4cb5c..a1b759474 100644 --- a/loader/settings.c +++ b/loader/settings.c @@ -421,7 +421,7 @@ VkResult get_loader_settings(const struct loader_instance* inst, loader_settings loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Loader settings file from %s missing global settings and none of the app specific settings matched the " "current application - no loader settings will be active", - settings_file_path, settings_file_path); + settings_file_path); goto out; // No global settings were found - exit } else { index_to_use = global_settings_index; // Global settings are present - use it From 5562c4b3ecaa9b2618cb9a3daa2c0ca474514ca7 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Fri, 22 Nov 2024 11:40:15 -0600 Subject: [PATCH 11/23] Fix O(n^2) json parsing by not using cJSON_GetArrayItem The implementation of cJSON_GetArrayItem is very simple - it finds the node at an index by iterating the linked-list of child nodes index number of times. This is problematic because it means using cJSON_GetArrayItem(node, i) to iterate all elements becomes a O(n^2). The fix is to use the helper function cJSON_ArrayForEach which directly iterates on the linked list. This change takes time to refactor the get_loader_settings() logic that handles whether the file contains just the settings object or an array of settings objects, making the logic easier to follow. --- loader/loader.c | 55 +++++++++++--------- loader/loader_json.c | 6 +-- loader/settings.c | 116 +++++++++++++++++++------------------------ 3 files changed, 86 insertions(+), 91 deletions(-) diff --git a/loader/loader.c b/loader/loader.c index ed454f13d..a50dc5d93 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -2649,11 +2649,14 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade // } cJSON *instance_extensions = loader_cJSON_GetObjectItem(layer_node, "instance_extensions"); - if (instance_extensions != NULL) { - int count = loader_cJSON_GetArraySize(instance_extensions); - for (int i = 0; i < count; i++) { + if (instance_extensions != NULL && instance_extensions->type == cJSON_Array) { + cJSON *ext_item = NULL; + cJSON_ArrayForEach(ext_item, instance_extensions) { + if (ext_item->type != cJSON_Object) { + continue; + } + VkExtensionProperties ext_prop = {0}; - cJSON *ext_item = loader_cJSON_GetArrayItem(instance_extensions, i); result = loader_parse_json_string_to_existing_str(inst, ext_item, "name", VK_MAX_EXTENSION_NAME_SIZE, ext_prop.extensionName); if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; @@ -2679,13 +2682,14 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade // entrypoints // } cJSON *device_extensions = loader_cJSON_GetObjectItem(layer_node, "device_extensions"); - if (device_extensions != NULL) { - int count = loader_cJSON_GetArraySize(device_extensions); - for (int i = 0; i < count; i++) { - VkExtensionProperties ext_prop = {0}; - - cJSON *ext_item = loader_cJSON_GetArrayItem(device_extensions, i); + if (device_extensions != NULL && device_extensions->type == cJSON_Array) { + cJSON *ext_item = NULL; + cJSON_ArrayForEach(ext_item, device_extensions) { + if (ext_item->type != cJSON_Object) { + continue; + } + VkExtensionProperties ext_prop = {0}; result = loader_parse_json_string_to_existing_str(inst, ext_item, "name", VK_MAX_EXTENSION_NAME_SIZE, ext_prop.extensionName); if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; @@ -2823,14 +2827,12 @@ VkResult loader_add_layer_properties(const struct loader_instance *inst, struct // - If more than one "layer" object are used, then the "layers" array is // required VkResult result = VK_ERROR_INITIALIZATION_FAILED; - cJSON *item, *layers_node, *layer_node; - loader_api_version json_version = {0, 0, 0}; char *file_vers = NULL; // Make sure sure the top level json value is an object if (!json || json->type != cJSON_Object) { goto out; } - item = loader_cJSON_GetObjectItem(json, "file_format_version"); + cJSON *item = loader_cJSON_GetObjectItem(json, "file_format_version"); if (item == NULL) { goto out; } @@ -2840,10 +2842,15 @@ VkResult loader_add_layer_properties(const struct loader_instance *inst, struct if (out_of_memory) { result = VK_ERROR_OUT_OF_HOST_MEMORY; goto out; + } else if (NULL == file_vers) { + loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, + "loader_add_layer_properties: Manifest %s missing required field file_format_version", filename); + goto out; } + loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, "Found manifest file %s (file version %s)", filename, file_vers); // Get the major/minor/and patch as integers for easier comparison - json_version = loader_make_full_version(loader_parse_version_string(file_vers)); + loader_api_version json_version = loader_make_full_version(loader_parse_version_string(file_vers)); if (!is_valid_layer_json_version(&json_version)) { loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_LAYER_BIT, 0, @@ -2852,9 +2859,8 @@ VkResult loader_add_layer_properties(const struct loader_instance *inst, struct } // If "layers" is present, read in the array of layer objects - layers_node = loader_cJSON_GetObjectItem(json, "layers"); + cJSON *layers_node = loader_cJSON_GetObjectItem(json, "layers"); if (layers_node != NULL) { - int numItems = loader_cJSON_GetArraySize(layers_node); // Supported versions started in 1.0.1, so anything newer if (!loader_check_version_meets_required(loader_combine_version(1, 0, 1), json_version)) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, @@ -2862,20 +2868,21 @@ VkResult loader_add_layer_properties(const struct loader_instance *inst, struct "version %s", filename, file_vers); } - for (int curLayer = 0; curLayer < numItems; curLayer++) { - layer_node = loader_cJSON_GetArrayItem(layers_node, curLayer); - if (layer_node == NULL) { - loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, - "loader_add_layer_properties: Can not find 'layers' array element %d object in manifest JSON file %s. " - "Skipping this file", - curLayer, filename); + cJSON *layer_node = NULL; + cJSON_ArrayForEach(layer_node, layers_node) { + if (layer_node->type != cJSON_Object) { + loader_log( + inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, + "loader_add_layer_properties: Array element in \"layers\" field in manifest JSON file %s is not an object. " + "Skipping this file", + filename); goto out; } result = loader_read_layer_json(inst, layer_instance_list, layer_node, json_version, is_implicit, filename); } } else { // Otherwise, try to read in individual layers - layer_node = loader_cJSON_GetObjectItem(json, "layer"); + cJSON *layer_node = loader_cJSON_GetObjectItem(json, "layer"); if (layer_node == NULL) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, "loader_add_layer_properties: Can not find 'layer' object in manifest JSON file %s. Skipping this file.", diff --git a/loader/loader_json.c b/loader/loader_json.c index 883704d10..481e9f466 100644 --- a/loader/loader_json.c +++ b/loader/loader_json.c @@ -225,9 +225,9 @@ VkResult loader_parse_json_array_of_strings(const struct loader_instance *inst, if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { goto out; } - for (uint32_t i = 0; i < count; i++) { - cJSON *element = loader_cJSON_GetArrayItem(item, i); - if (element == NULL) { + cJSON *element = NULL; + cJSON_ArrayForEach(element, item) { + if (element->type != cJSON_String) { return VK_ERROR_INITIALIZATION_FAILED; } bool out_of_memory = false; diff --git a/loader/settings.c b/loader/settings.c index a1b759474..65e929047 100644 --- a/loader/settings.c +++ b/loader/settings.c @@ -181,13 +181,14 @@ VkResult parse_layer_configurations(const struct loader_instance* inst, cJSON* s goto out; } - for (uint32_t i = 0; i < layer_configurations_count; i++) { - cJSON* layer = loader_cJSON_GetArrayItem(layer_configurations, i); - if (NULL == layer) { + cJSON* layer = NULL; + size_t i = 0; + cJSON_ArrayForEach(layer, layer_configurations) { + if (layer->type != cJSON_Object) { res = VK_ERROR_INITIALIZATION_FAILED; goto out; } - res = parse_layer_configuration(inst, layer, &(loader_settings->layer_configurations[i])); + res = parse_layer_configuration(inst, layer, &(loader_settings->layer_configurations[i++])); if (VK_SUCCESS != res) { goto out; } @@ -195,8 +196,8 @@ VkResult parse_layer_configurations(const struct loader_instance* inst, cJSON* s out: if (res != VK_SUCCESS) { if (loader_settings->layer_configurations) { - for (uint32_t i = 0; i < loader_settings->layer_configuration_count; i++) { - free_layer_configuration(inst, &(loader_settings->layer_configurations[i])); + for (size_t index = 0; index < loader_settings->layer_configuration_count; index++) { + free_layer_configuration(inst, &(loader_settings->layer_configurations[index])); } loader_settings->layer_configuration_count = 0; loader_instance_heap_free(inst, loader_settings->layer_configurations); @@ -334,7 +335,7 @@ VkResult get_loader_settings(const struct loader_instance* inst, loader_settings res = loader_get_json(inst, settings_file_path, &json); // Make sure sure the top level json value is an object - if (res != VK_SUCCESS || NULL == json || json->type != 6) { + if (res != VK_SUCCESS || NULL == json || json->type != cJSON_Object) { goto out; } @@ -348,15 +349,17 @@ VkResult get_loader_settings(const struct loader_instance* inst, loader_settings } goto out; } - uint32_t settings_array_count = 0; - bool has_multi_setting_file = false; + + // Because the file may contain either a "settings_array" or a single "settings" object, we need to create a cJSON so that we + // can iterate on both cases with common code + cJSON settings_iter_parent = {0}; + cJSON* settings_array = loader_cJSON_GetObjectItem(json, "settings_array"); cJSON* single_settings_object = loader_cJSON_GetObjectItem(json, "settings"); if (NULL != settings_array) { - has_multi_setting_file = true; - settings_array_count = loader_cJSON_GetArraySize(settings_array); + memcpy(&settings_iter_parent, settings_array, sizeof(cJSON)); } else if (NULL != single_settings_object) { - settings_array_count = 1; + settings_iter_parent.child = single_settings_object; } else if (settings_array == NULL && single_settings_object) { loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Loader settings file from %s missing required settings objects: Either one of the \"settings\" or " @@ -367,31 +370,33 @@ VkResult get_loader_settings(const struct loader_instance* inst, loader_settings } // Corresponds to the settings object that has no app keys - int global_settings_index = -1; + cJSON* global_settings = NULL; // Corresponds to the settings object which has a matching app key - int index_to_use = -1; + cJSON* settings_to_use = NULL; char current_process_path[1024]; bool valid_exe_path = NULL != loader_platform_executable_path(current_process_path, 1024); - for (int i = 0; i < (int)settings_array_count; i++) { - if (has_multi_setting_file) { - single_settings_object = loader_cJSON_GetArrayItem(settings_array, i); + cJSON* settings_object_iter = NULL; + cJSON_ArrayForEach(settings_object_iter, &settings_iter_parent) { + if (settings_object_iter->type != cJSON_Object) { + loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, + "Loader settings file from %s has a settings element that is not an object", settings_file_path); + break; } - cJSON* app_keys = loader_cJSON_GetObjectItem(single_settings_object, "app_keys"); + cJSON* app_keys = loader_cJSON_GetObjectItem(settings_object_iter, "app_keys"); if (NULL == app_keys) { - if (global_settings_index == -1) { - global_settings_index = i; // use the first 'global' settings that has no app keys as the global one + if (global_settings == NULL) { + global_settings = settings_object_iter; // use the first 'global' settings that has no app keys as the global one } continue; } else if (valid_exe_path) { - int app_key_count = loader_cJSON_GetArraySize(app_keys); - if (app_key_count == 0) { + if (loader_cJSON_GetArraySize(app_keys) == 0) { continue; // empty array } - for (int j = 0; j < app_key_count; j++) { - cJSON* app_key_json = loader_cJSON_GetArrayItem(app_keys, j); - if (NULL == app_key_json) { + cJSON* app_key_json = NULL; + cJSON_ArrayForEach(app_key_json, app_keys) { + if (app_key_json->type != cJSON_String) { continue; } bool out_of_memory = false; @@ -405,10 +410,10 @@ VkResult get_loader_settings(const struct loader_instance* inst, loader_settings } if (strcmp(current_process_path, app_key) == 0) { - index_to_use = i; + settings_to_use = settings_object_iter; } - loader_instance_heap_free(inst, app_key); - if (index_to_use == i) { + loader_instance_heap_free(inst, (void*)app_key); + if (settings_to_use != NULL) { break; // break only after freeing the app key } } @@ -416,29 +421,15 @@ VkResult get_loader_settings(const struct loader_instance* inst, loader_settings } // No app specific settings match - either use global settings or exit - if (index_to_use == -1) { - if (global_settings_index == -1) { + if (settings_to_use == NULL) { + if (global_settings == NULL) { loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Loader settings file from %s missing global settings and none of the app specific settings matched the " "current application - no loader settings will be active", settings_file_path); goto out; // No global settings were found - exit } else { - index_to_use = global_settings_index; // Global settings are present - use it - } - } - - // Now get the actual settings object to use - already have it if there is only one settings object - // If there are multiple settings, just need to set single_settings_object to the desired settings object - if (has_multi_setting_file) { - single_settings_object = loader_cJSON_GetArrayItem(settings_array, index_to_use); - if (NULL == single_settings_object) { - loader_log( - inst, VULKAN_LOADER_DEBUG_BIT, 0, - "Loader settings file from %s failed to get the settings object at index %d - no loader settings will be active ", - settings_file_path, index_to_use); - res = VK_ERROR_INITIALIZATION_FAILED; - goto out; + settings_to_use = global_settings; // Global settings are present - use it } } @@ -446,7 +437,7 @@ VkResult get_loader_settings(const struct loader_instance* inst, loader_settings cJSON* stderr_filter = loader_cJSON_GetObjectItem(single_settings_object, "stderr_log"); if (NULL != stderr_filter) { struct loader_string_list stderr_log = {0}; - res = loader_parse_json_array_of_strings(inst, single_settings_object, "stderr_log", &stderr_log); + res = loader_parse_json_array_of_strings(inst, settings_to_use, "stderr_log", &stderr_log); if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { goto out; } @@ -455,30 +446,27 @@ VkResult get_loader_settings(const struct loader_instance* inst, loader_settings } // optional - cJSON* logs_to_use = loader_cJSON_GetObjectItem(single_settings_object, "log_locations"); + cJSON* logs_to_use = loader_cJSON_GetObjectItem(settings_to_use, "log_locations"); if (NULL != logs_to_use) { - int log_count = loader_cJSON_GetArraySize(logs_to_use); - for (int i = 0; i < log_count; i++) { - cJSON* log_element = loader_cJSON_GetArrayItem(logs_to_use, i); + cJSON* log_element = NULL; + cJSON_ArrayForEach(log_element, logs_to_use) { // bool is_valid = true; - if (NULL != log_element) { - struct loader_string_list log_destinations = {0}; - res = loader_parse_json_array_of_strings(inst, log_element, "destinations", &log_destinations); - if (res != VK_SUCCESS) { - // is_valid = false; - } - free_string_list(inst, &log_destinations); - struct loader_string_list log_filters = {0}; - res = loader_parse_json_array_of_strings(inst, log_element, "filters", &log_filters); - if (res != VK_SUCCESS) { - // is_valid = false; - } - free_string_list(inst, &log_filters); + struct loader_string_list log_destinations = {0}; + res = loader_parse_json_array_of_strings(inst, log_element, "destinations", &log_destinations); + if (res != VK_SUCCESS) { + // is_valid = false; + } + free_string_list(inst, &log_destinations); + struct loader_string_list log_filters = {0}; + res = loader_parse_json_array_of_strings(inst, log_element, "filters", &log_filters); + if (res != VK_SUCCESS) { + // is_valid = false; } + free_string_list(inst, &log_filters); } } - res = parse_layer_configurations(inst, single_settings_object, loader_settings); + res = parse_layer_configurations(inst, settings_to_use, loader_settings); if (res != VK_SUCCESS) { goto out; } From 1b4b015b657a4e74c08aa996ffebce56b1b8ed49 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Fri, 22 Nov 2024 12:35:56 -0600 Subject: [PATCH 12/23] Detect when recursive meta-layer's are present If meta-layer A has meta-layer B in its component layer list, and meta-layer B has meta-layer A in its component layer list, this would cause a stack overflow in verify_meta_layer_component_layers() since it would recursively check the meta layer of the other layer. Fixing it means checking that the a component layer is a meta layer, and if so, checking the component layer's component layers for the meta layer being verified. Granted, this check is only 1 deep so more complex recursive checks may be needed. --- loader/loader.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/loader/loader.c b/loader/loader.c index a50dc5d93..af1ad5590 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -2205,6 +2205,16 @@ bool verify_meta_layer_component_layers(const struct loader_instance *inst, stru return false; } if (comp_prop->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER) { + for (uint32_t sub_comp_layer = 0; sub_comp_layer < comp_prop->component_layer_names.count; sub_comp_layer++) { + if (!strcmp(prop->info.layerName, comp_prop->component_layer_names.list[sub_comp_layer])) { + loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, + "verify_meta_layer_component_layers: Recursive depedency between Meta-layer %s and Meta-layer %s. " + "Skipping this layer.", + prop->info.layerName, prop->component_layer_names.list[comp_layer]); + return false; + } + } + loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, "verify_meta_layer_component_layers: Adding meta-layer %s which also contains meta-layer %s", prop->info.layerName, comp_prop->info.layerName); From daeb8681d08593db27522ee921460ec944140e9e Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Fri, 22 Nov 2024 14:56:44 -0600 Subject: [PATCH 13/23] Log the layer name with manifest loading messages --- loader/loader.c | 35 +++++++++++++++++++------------ tests/loader_layer_tests.cpp | 3 ++- tests/loader_regression_tests.cpp | 3 ++- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/loader/loader.c b/loader/loader.c index af1ad5590..dc9e793f0 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -2440,7 +2440,8 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade // Add list entry if (!strcmp(type, "DEVICE")) { - loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, "Device layers are deprecated. Skipping this layer"); + loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, "Device layers are deprecated. Skipping layer %s", + filename); result = VK_ERROR_INITIALIZATION_FAILED; goto out; } @@ -2513,9 +2514,11 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade if (NULL != library_path) { if (NULL != loader_cJSON_GetObjectItem(layer_node, "component_layers")) { - loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, - "Indicating meta-layer-specific component_layers, but also defining layer library path. Both are not " - "compatible, so skipping this layer"); + loader_log( + inst, VULKAN_LOADER_WARN_BIT, 0, + "Layer \"%s\" contains meta-layer-specific component_layers, but also defining layer library path. Both are not " + "compatible, so skipping this layer", + filename); result = VK_ERROR_INITIALIZATION_FAILED; goto out; } @@ -2531,7 +2534,8 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade } if (NULL == library_path_str) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, - "Skipping layer due to problem accessing the library_path value in manifest JSON file %s", filename); + "Skipping layer \"%s\" due to problem accessing the library_path value in the manifest JSON file", filename); + result = VK_ERROR_OUT_OF_HOST_MEMORY; goto out; } @@ -2545,7 +2549,7 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade if (NULL == library_path) { if (!loader_check_version_meets_required(LOADER_VERSION_1_1_0, version)) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, - "Indicating meta-layer-specific component_layers, but using older JSON file version."); + "Layer \"%s\" contains meta-layer-specific component_layers, but using older JSON file version.", filename); } result = loader_parse_json_array_of_strings(inst, layer_node, "component_layers", &(props.component_layer_names)); @@ -2554,8 +2558,9 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade } if (VK_ERROR_INITIALIZATION_FAILED == result) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, - "Layer missing both library_path and component_layers fields. One or the other MUST be defined. Skipping " - "this layer"); + "Layer \"%s\" is missing both library_path and component_layers fields. One or the other MUST be defined. " + "Skipping this layer", + filename); goto out; } // This is now, officially, a meta-layer @@ -2581,7 +2586,7 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade } if (NULL != props.override_paths.list && !loader_check_version_meets_required(loader_combine_version(1, 1, 0), version)) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, - "Indicating meta-layer-specific override paths, but using older JSON file version."); + "Layer \"%s\" contains meta-layer-specific override paths, but using older JSON file version.", filename); } // Parse disable_environment @@ -2590,7 +2595,9 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade cJSON *disable_environment = loader_cJSON_GetObjectItem(layer_node, "disable_environment"); if (disable_environment == NULL) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, - "Didn't find required layer object disable_environment in manifest JSON file, skipping this layer"); + "Layer \"%s\" doesn't contain required layer object disable_environment in the manifest JSON file, skipping " + "this layer", + filename); result = VK_ERROR_INITIALIZATION_FAILED; goto out; } @@ -2598,8 +2605,9 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade if (!disable_environment->child || disable_environment->child->type != cJSON_String || !disable_environment->child->string || !disable_environment->child->valuestring) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, - "Didn't find required layer child value disable_environment in manifest JSON file, skipping this layer " - "(Policy #LLP_LAYER_9)"); + "Layer \"%s\" doesn't contain required child value in object disable_environment in the manifest JSON file, " + "skipping this layer (Policy #LLP_LAYER_9)", + filename); result = VK_ERROR_INITIALIZATION_FAILED; goto out; } @@ -2786,7 +2794,8 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade if ((strncmp(library_arch, "32", 2) == 0 && sizeof(void *) != 4) || (strncmp(library_arch, "64", 2) == 0 && sizeof(void *) != 8)) { loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, - "Layer library architecture doesn't match the current running architecture, skipping this layer"); + "The library architecture in layer %s doesn't match the current running architecture, skipping this layer", + filename); loader_instance_heap_free(inst, library_arch); result = VK_ERROR_INITIALIZATION_FAILED; goto out; diff --git a/tests/loader_layer_tests.cpp b/tests/loader_layer_tests.cpp index f2cc7bd8a..c8d23581c 100644 --- a/tests/loader_layer_tests.cpp +++ b/tests/loader_layer_tests.cpp @@ -1856,7 +1856,8 @@ TEST(OverrideMetaLayer, ManifestFileFormatVersionTooOld) { FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); inst.CheckCreate(); ASSERT_TRUE(env.debug_log.find(std::string("Insert instance layer \"") + regular_layer_name)); - ASSERT_TRUE(env.debug_log.find("Indicating meta-layer-specific override paths, but using older JSON file version.")); + ASSERT_TRUE(env.debug_log.find(std::string("Layer \"") + lunarg_meta_layer_name + + "\" contains meta-layer-specific override paths, but using older JSON file version.")); env.layers.clear(); } diff --git a/tests/loader_regression_tests.cpp b/tests/loader_regression_tests.cpp index de507564e..e7717d59f 100644 --- a/tests/loader_regression_tests.cpp +++ b/tests/loader_regression_tests.cpp @@ -1663,7 +1663,8 @@ TEST(TryLoadWrongBinaries, WrongArchLayer) { FillDebugUtilsCreateDetails(inst.create_info, log); inst.create_info.add_layer(layer_name); inst.CheckCreate(VK_ERROR_LAYER_NOT_PRESENT); - ASSERT_TRUE(log.find("Layer library architecture doesn't match the current running architecture, skipping this layer")); + ASSERT_TRUE(log.find(std::string("The library architecture in layer ") + env.get_shimmed_layer_manifest_path(0).string() + + " doesn't match the current running architecture, skipping this layer")); } TEST(EnumeratePhysicalDeviceGroups, OneCall) { From 354a21f31716f1e9d64673ce42b72de932ba0b5b Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Fri, 22 Nov 2024 17:45:24 -0600 Subject: [PATCH 14/23] Fix tests not respecting dll unloading env-var --- tests/framework/test_util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/framework/test_util.h b/tests/framework/test_util.h index 6df613fb6..2884a01ea 100644 --- a/tests/framework/test_util.h +++ b/tests/framework/test_util.h @@ -272,8 +272,8 @@ inline void loader_platform_close_library(loader_platform_dl_handle library) { char* loader_disable_dynamic_library_unloading_env_var = getenv("VK_LOADER_DISABLE_DYNAMIC_LIBRARY_UNLOADING"); if (NULL == loader_disable_dynamic_library_unloading_env_var || 0 != strncmp(loader_disable_dynamic_library_unloading_env_var, "1", 2)) { + dlclose(library); } - dlclose(library); } inline void* loader_platform_get_proc_address(loader_platform_dl_handle library, const char* name) { assert(library); From a5f67f265896b0912103dc6fd183fdc34eaf5761 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Sat, 23 Nov 2024 12:34:41 -0600 Subject: [PATCH 15/23] Add assert for unknown free's in test's MemoryTracker --- tests/loader_alloc_callback_tests.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/loader_alloc_callback_tests.cpp b/tests/loader_alloc_callback_tests.cpp index e437158e6..1701bfb92 100644 --- a/tests/loader_alloc_callback_tests.cpp +++ b/tests/loader_alloc_callback_tests.cpp @@ -104,7 +104,10 @@ class MemoryTracker { void free(void* pMemory) { if (pMemory == nullptr) return; auto elem = allocations.find(pMemory); - if (elem == allocations.end()) return; + if (elem == allocations.end()) { + assert(false && "Should never be freeing memory that wasn't allocated by the MemoryTracker!"); + return; + } allocations.erase(elem); assert(allocation_count != 0 && "Cant free when there are no valid allocations"); allocation_count--; From f30c98fb91940ef5a567f496f4673d297b1cfe3c Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Sun, 24 Nov 2024 16:54:46 -0600 Subject: [PATCH 16/23] Simplify json string copy routine loader_parse_json_string_to_existing_str() doesn't need to allocate any temporary memory, as cJSON_PrintPreallocated allows dumping directly to the char array that is the final destination. --- loader/loader.c | 12 +++++------- loader/loader_json.c | 20 +++++++++++--------- loader/loader_json.h | 3 +-- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/loader/loader.c b/loader/loader.c index dc9e793f0..a191078c6 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -2408,7 +2408,7 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade // Parse name - result = loader_parse_json_string_to_existing_str(inst, layer_node, "name", VK_MAX_EXTENSION_NAME_SIZE, props.info.layerName); + result = loader_parse_json_string_to_existing_str(layer_node, "name", VK_MAX_EXTENSION_NAME_SIZE, props.info.layerName); if (VK_ERROR_OUT_OF_HOST_MEMORY == result) goto out; if (VK_ERROR_INITIALIZATION_FAILED == result) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, @@ -2496,8 +2496,8 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade // Parse description - result = loader_parse_json_string_to_existing_str(inst, layer_node, "description", VK_MAX_EXTENSION_NAME_SIZE, - props.info.description); + result = + loader_parse_json_string_to_existing_str(layer_node, "description", VK_MAX_EXTENSION_NAME_SIZE, props.info.description); if (VK_ERROR_OUT_OF_HOST_MEMORY == result) goto out; if (VK_ERROR_INITIALIZATION_FAILED == result) { loader_log( @@ -2675,8 +2675,7 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade } VkExtensionProperties ext_prop = {0}; - result = loader_parse_json_string_to_existing_str(inst, ext_item, "name", VK_MAX_EXTENSION_NAME_SIZE, - ext_prop.extensionName); + result = loader_parse_json_string_to_existing_str(ext_item, "name", VK_MAX_EXTENSION_NAME_SIZE, ext_prop.extensionName); if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; if (result == VK_ERROR_INITIALIZATION_FAILED) continue; char *spec_version = NULL; @@ -2708,8 +2707,7 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade } VkExtensionProperties ext_prop = {0}; - result = loader_parse_json_string_to_existing_str(inst, ext_item, "name", VK_MAX_EXTENSION_NAME_SIZE, - ext_prop.extensionName); + result = loader_parse_json_string_to_existing_str(ext_item, "name", VK_MAX_EXTENSION_NAME_SIZE, ext_prop.extensionName); if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; char *spec_version = NULL; diff --git a/loader/loader_json.c b/loader/loader_json.c index 481e9f466..2112b419b 100644 --- a/loader/loader_json.c +++ b/loader/loader_json.c @@ -171,24 +171,26 @@ VkResult loader_get_json(const struct loader_instance *inst, const char *filenam return res; } -VkResult loader_parse_json_string_to_existing_str(const struct loader_instance *inst, cJSON *object, const char *key, - size_t out_str_len, char *out_string) { +VkResult loader_parse_json_string_to_existing_str(cJSON *object, const char *key, size_t out_str_len, char *out_string) { + if (NULL == key) { + return VK_ERROR_INITIALIZATION_FAILED; + } cJSON *item = loader_cJSON_GetObjectItem(object, key); if (NULL == item) { return VK_ERROR_INITIALIZATION_FAILED; } + + if (item->type != cJSON_String || item->valuestring == NULL) { + return VK_ERROR_INITIALIZATION_FAILED; + } bool out_of_memory = false; - char *str = loader_cJSON_Print(item, &out_of_memory); + bool success = loader_cJSON_PrintPreallocated(item, out_string, (int)out_str_len, cJSON_False); if (out_of_memory) { return VK_ERROR_OUT_OF_HOST_MEMORY; } - if (NULL != out_string) { - loader_strncpy(out_string, out_str_len, str, out_str_len); - if (out_str_len > 0) { - out_string[out_str_len - 1] = '\0'; - } + if (!success) { + return VK_ERROR_INITIALIZATION_FAILED; } - loader_instance_heap_free(inst, str); return VK_SUCCESS; } diff --git a/loader/loader_json.h b/loader/loader_json.h index da9fe9e9a..824019b90 100644 --- a/loader/loader_json.h +++ b/loader/loader_json.h @@ -43,8 +43,7 @@ VkResult loader_get_json(const struct loader_instance *inst, const char *filenam // Given a cJSON object, find the string associated with the key and puts an pre-allocated string into out_string. // Length is given by out_str_len, and this function truncates the string with a null terminator if it the provided space isn't // large enough. -VkResult loader_parse_json_string_to_existing_str(const struct loader_instance *inst, cJSON *object, const char *key, - size_t out_str_len, char *out_string); +VkResult loader_parse_json_string_to_existing_str(cJSON *object, const char *key, size_t out_str_len, char *out_string); // Given a cJSON object, find the string associated with the key and puts an allocated string into out_string. // It is the callers responsibility to free out_string. From 55f16dd00a9720d26561498adbcfba6844a12fe7 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Sun, 24 Nov 2024 16:57:49 -0600 Subject: [PATCH 17/23] Refactor get_loader_settings to not allocate app_keys The cJSON_GetStringValue function allows us to read the contents of the cJSON string without printing, which requires a memory allocation. This simplifies the logic by not needing to free anything. --- loader/settings.c | 52 +++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/loader/settings.c b/loader/settings.c index 65e929047..136ec5e58 100644 --- a/loader/settings.c +++ b/loader/settings.c @@ -384,39 +384,31 @@ VkResult get_loader_settings(const struct loader_instance* inst, loader_settings "Loader settings file from %s has a settings element that is not an object", settings_file_path); break; } + cJSON* app_keys = loader_cJSON_GetObjectItem(settings_object_iter, "app_keys"); if (NULL == app_keys) { + // use the first 'global' settings that has no app keys as the global one if (global_settings == NULL) { - global_settings = settings_object_iter; // use the first 'global' settings that has no app keys as the global one + global_settings = settings_object_iter; } continue; - } else if (valid_exe_path) { - if (loader_cJSON_GetArraySize(app_keys) == 0) { - continue; // empty array + } + // No sense iterating if we couldn't get the executable path + if (!valid_exe_path) { + break; + } + cJSON* app_key = NULL; + cJSON_ArrayForEach(app_keys, settings_object_iter) { + char* app_key_str = loader_cJSON_GetStringValue(app_key); + if (strcmp(current_process_path, app_key_str) == 0) { + settings_to_use = settings_object_iter; + break; } - cJSON* app_key_json = NULL; - cJSON_ArrayForEach(app_key_json, app_keys) { - if (app_key_json->type != cJSON_String) { - continue; - } - bool out_of_memory = false; - char* app_key = loader_cJSON_Print(app_key_json, &out_of_memory); - if (out_of_memory) { - res = VK_ERROR_OUT_OF_HOST_MEMORY; - goto out; - } - if (NULL == app_key) { - continue; - } + } - if (strcmp(current_process_path, app_key) == 0) { - settings_to_use = settings_object_iter; - } - loader_instance_heap_free(inst, (void*)app_key); - if (settings_to_use != NULL) { - break; // break only after freeing the app key - } - } + // Break if we have found a matching current_process_path + if (NULL != settings_to_use) { + break; } } @@ -434,7 +426,7 @@ VkResult get_loader_settings(const struct loader_instance* inst, loader_settings } // optional - cJSON* stderr_filter = loader_cJSON_GetObjectItem(single_settings_object, "stderr_log"); + cJSON* stderr_filter = loader_cJSON_GetObjectItem(settings_to_use, "stderr_log"); if (NULL != stderr_filter) { struct loader_string_list stderr_log = {0}; res = loader_parse_json_array_of_strings(inst, settings_to_use, "stderr_log", &stderr_log); @@ -629,7 +621,8 @@ VkResult get_settings_layers(const struct loader_instance* inst, struct loader_l struct loader_layer_properties* newly_added_layer = &settings_layers->list[settings_layers->count - 1]; newly_added_layer->settings_control_value = layer_config->control; - // If the manifest file found has a name that differs from the one in the settings, remove this layer from consideration + // If the manifest file found has a name that differs from the one in the settings, remove this layer from + // consideration bool should_remove = false; if (strncmp(newly_added_layer->info.layerName, layer_config->name, VK_MAX_EXTENSION_NAME_SIZE) != 0) { should_remove = true; @@ -819,7 +812,8 @@ VkResult enable_correct_layers_from_settings(const struct loader_instance* inst, // First look for the old-fashion layers forced on with VK_INSTANCE_LAYERS if (!enable_layer && vk_instance_layers_env && vk_instance_layers_env_copy && vk_instance_layers_env_len > 0) { // Copy the env-var on each iteration, so that loader_get_next_path can correctly find the separators - // This solution only needs one stack allocation ahead of time rather than an allocation per layer in the env-var + // This solution only needs one stack allocation ahead of time rather than an allocation per layer in the + // env-var loader_strncpy(vk_instance_layers_env_copy, vk_instance_layers_env_len, vk_instance_layers_env, vk_instance_layers_env_len); From c637e364fdadd625813c45037217f8a890b72de4 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Sun, 24 Nov 2024 16:58:29 -0600 Subject: [PATCH 18/23] Better error checking of loader_parse_json functions --- loader/loader_json.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/loader/loader_json.c b/loader/loader_json.c index 2112b419b..6b63666c2 100644 --- a/loader/loader_json.c +++ b/loader/loader_json.c @@ -195,8 +195,12 @@ VkResult loader_parse_json_string_to_existing_str(cJSON *object, const char *key } VkResult loader_parse_json_string(cJSON *object, const char *key, char **out_string) { + if (NULL == key) { + return VK_ERROR_INITIALIZATION_FAILED; + } + cJSON *item = loader_cJSON_GetObjectItem(object, key); - if (NULL == item) { + if (NULL == item || NULL == item->valuestring) { return VK_ERROR_INITIALIZATION_FAILED; } @@ -212,7 +216,9 @@ VkResult loader_parse_json_string(cJSON *object, const char *key, char **out_str } VkResult loader_parse_json_array_of_strings(const struct loader_instance *inst, cJSON *object, const char *key, struct loader_string_list *string_list) { - VkResult res = VK_SUCCESS; + if (NULL == key) { + return VK_ERROR_INITIALIZATION_FAILED; + } cJSON *item = loader_cJSON_GetObjectItem(object, key); if (NULL == item) { return VK_ERROR_INITIALIZATION_FAILED; @@ -223,7 +229,7 @@ VkResult loader_parse_json_array_of_strings(const struct loader_instance *inst, return VK_SUCCESS; } - res = create_string_list(inst, count, string_list); + VkResult res = create_string_list(inst, count, string_list); if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { goto out; } From 5d3e05c90a5f67b2b7443e619f8ef7c6f1905986 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Sun, 24 Nov 2024 17:01:21 -0600 Subject: [PATCH 19/23] Fix append_str_to_string_list leaking during OOM --- loader/loader.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/loader/loader.c b/loader/loader.c index a191078c6..d7c8352a8 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -312,6 +312,7 @@ VkResult append_str_to_string_list(const struct loader_instance *inst, struct lo string_list->list = loader_instance_heap_calloc(inst, sizeof(char *) * string_list->allocated_count, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); if (NULL == string_list->list) { + loader_instance_heap_free(inst, str); // Must clean up in case of failure return VK_ERROR_OUT_OF_HOST_MEMORY; } } else if (string_list->count + 1 > string_list->allocated_count) { @@ -319,6 +320,7 @@ VkResult append_str_to_string_list(const struct loader_instance *inst, struct lo string_list->list = loader_instance_heap_realloc(inst, string_list->list, sizeof(char *) * string_list->allocated_count, sizeof(char *) * new_allocated_count, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); if (NULL == string_list->list) { + loader_instance_heap_free(inst, str); // Must clean up in case of failure return VK_ERROR_OUT_OF_HOST_MEMORY; } // Null out the new space @@ -338,12 +340,7 @@ VkResult copy_str_to_string_list(const struct loader_instance *inst, struct load } loader_strncpy(new_str, sizeof(char *) * str_len + 1, str, str_len); new_str[str_len] = '\0'; - VkResult res = append_str_to_string_list(inst, string_list, new_str); - if (res != VK_SUCCESS) { - // Cleanup new_str if the append failed - as append_str_to_string_list takes ownership but not if the function fails - loader_instance_heap_free(inst, new_str); - } - return res; + return append_str_to_string_list(inst, string_list, new_str); } void free_string_list(const struct loader_instance *inst, struct loader_string_list *string_list) { From d22ab8555dd2f52c631074971cae970805573cf3 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Sun, 24 Nov 2024 17:02:27 -0600 Subject: [PATCH 20/23] Refactor manifest parsing to use less allocations By using cJSON_GetStringValue, many allocations can be avoided, which simplifies the logic by removing potential error paths. --- loader/loader.c | 181 ++++++++++++++++++---------------------------- loader/settings.c | 12 ++- 2 files changed, 77 insertions(+), 116 deletions(-) diff --git a/loader/loader.c b/loader/loader.c index d7c8352a8..0c123ff24 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -2397,16 +2397,18 @@ void remove_all_non_valid_override_layers(struct loader_instance *inst, struct l VkResult loader_read_layer_json(const struct loader_instance *inst, struct loader_layer_list *layer_instance_list, cJSON *layer_node, loader_api_version version, bool is_implicit, char *filename) { assert(layer_instance_list); - char *type = NULL; - char *api_version = NULL; - char *implementation_version = NULL; + char *library_path = NULL; VkResult result = VK_SUCCESS; struct loader_layer_properties props = {0}; + result = loader_copy_to_new_str(inst, filename, &props.manifest_file_name); + if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { + goto out; + } + // Parse name result = loader_parse_json_string_to_existing_str(layer_node, "name", VK_MAX_EXTENSION_NAME_SIZE, props.info.layerName); - if (VK_ERROR_OUT_OF_HOST_MEMORY == result) goto out; if (VK_ERROR_INITIALIZATION_FAILED == result) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, "Layer located at %s didn't find required layer value \"name\" in manifest JSON file, skipping this layer", @@ -2425,10 +2427,8 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade } // Parse type - - result = loader_parse_json_string(layer_node, "type", &type); - if (VK_ERROR_OUT_OF_HOST_MEMORY == result) goto out; - if (VK_ERROR_INITIALIZATION_FAILED == result) { + char *type = loader_cJSON_GetStringValue(loader_cJSON_GetObjectItem(layer_node, "type")); + if (NULL == type) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, "Layer located at %s didn't find required layer value \"type\" in manifest JSON file, skipping this layer", filename); @@ -2438,7 +2438,7 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade // Add list entry if (!strcmp(type, "DEVICE")) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, "Device layers are deprecated. Skipping layer %s", - filename); + props.info.layerName); result = VK_ERROR_INITIALIZATION_FAILED; goto out; } @@ -2455,10 +2455,8 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade } // Parse api_version - - result = loader_parse_json_string(layer_node, "api_version", &api_version); - if (VK_ERROR_OUT_OF_HOST_MEMORY == result) goto out; - if (VK_ERROR_INITIALIZATION_FAILED == result) { + char *api_version = loader_cJSON_GetStringValue(loader_cJSON_GetObjectItem(layer_node, "api_version")); + if (NULL == api_version) { loader_log( inst, VULKAN_LOADER_WARN_BIT, 0, "Layer located at %s didn't find required layer value \"api_version\" in manifest JSON file, skipping this layer", @@ -2479,10 +2477,8 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade } // Parse implementation_version - - result = loader_parse_json_string(layer_node, "implementation_version", &implementation_version); - if (VK_ERROR_OUT_OF_HOST_MEMORY == result) goto out; - if (VK_ERROR_INITIALIZATION_FAILED == result) { + char *implementation_version = loader_cJSON_GetStringValue(loader_cJSON_GetObjectItem(layer_node, "implementation_version")); + if (NULL == implementation_version) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, "Layer located at %s didn't find required layer value \"implementation_version\" in manifest JSON file, " "skipping this layer", @@ -2495,7 +2491,6 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade result = loader_parse_json_string_to_existing_str(layer_node, "description", VK_MAX_EXTENSION_NAME_SIZE, props.info.description); - if (VK_ERROR_OUT_OF_HOST_MEMORY == result) goto out; if (VK_ERROR_INITIALIZATION_FAILED == result) { loader_log( inst, VULKAN_LOADER_WARN_BIT, 0, @@ -2507,37 +2502,34 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade // Parse library_path // Library path no longer required unless component_layers is also not defined - cJSON *library_path = loader_cJSON_GetObjectItem(layer_node, "library_path"); - + result = loader_parse_json_string(layer_node, "library_path", &library_path); + if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { + loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, + "Skipping layer \"%s\" due to problem accessing the library_path value in the manifest JSON file", + props.info.layerName); + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } if (NULL != library_path) { if (NULL != loader_cJSON_GetObjectItem(layer_node, "component_layers")) { loader_log( inst, VULKAN_LOADER_WARN_BIT, 0, "Layer \"%s\" contains meta-layer-specific component_layers, but also defining layer library path. Both are not " "compatible, so skipping this layer", - filename); + props.info.layerName); result = VK_ERROR_INITIALIZATION_FAILED; + loader_instance_heap_free(inst, library_path); goto out; } result = loader_copy_to_new_str(inst, filename, &props.manifest_file_name); - if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; - - bool out_of_memory = false; - char *library_path_str = loader_cJSON_Print(library_path, &out_of_memory); - if (out_of_memory) { - result = VK_ERROR_OUT_OF_HOST_MEMORY; - goto out; - } - if (NULL == library_path_str) { - loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, - "Skipping layer \"%s\" due to problem accessing the library_path value in the manifest JSON file", filename); - result = VK_ERROR_OUT_OF_HOST_MEMORY; + if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { + loader_instance_heap_free(inst, library_path); goto out; } // This function takes ownership of library_path_str - so we don't need to clean it up - result = combine_manifest_directory_and_library_path(inst, library_path_str, filename, &props.lib_name); + result = combine_manifest_directory_and_library_path(inst, library_path, filename, &props.lib_name); if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; } @@ -2546,7 +2538,8 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade if (NULL == library_path) { if (!loader_check_version_meets_required(LOADER_VERSION_1_1_0, version)) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, - "Layer \"%s\" contains meta-layer-specific component_layers, but using older JSON file version.", filename); + "Layer \"%s\" contains meta-layer-specific component_layers, but using older JSON file version.", + props.info.layerName); } result = loader_parse_json_array_of_strings(inst, layer_node, "component_layers", &(props.component_layer_names)); @@ -2557,7 +2550,7 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, "Layer \"%s\" is missing both library_path and component_layers fields. One or the other MUST be defined. " "Skipping this layer", - filename); + props.info.layerName); goto out; } // This is now, officially, a meta-layer @@ -2583,7 +2576,8 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade } if (NULL != props.override_paths.list && !loader_check_version_meets_required(loader_combine_version(1, 1, 0), version)) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, - "Layer \"%s\" contains meta-layer-specific override paths, but using older JSON file version.", filename); + "Layer \"%s\" contains meta-layer-specific override paths, but using older JSON file version.", + props.info.layerName); } // Parse disable_environment @@ -2594,7 +2588,7 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, "Layer \"%s\" doesn't contain required layer object disable_environment in the manifest JSON file, skipping " "this layer", - filename); + props.info.layerName); result = VK_ERROR_INITIALIZATION_FAILED; goto out; } @@ -2604,7 +2598,7 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, "Layer \"%s\" doesn't contain required child value in object disable_environment in the manifest JSON file, " "skipping this layer (Policy #LLP_LAYER_9)", - filename); + props.info.layerName); result = VK_ERROR_INITIALIZATION_FAILED; goto out; } @@ -2673,8 +2667,9 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade VkExtensionProperties ext_prop = {0}; result = loader_parse_json_string_to_existing_str(ext_item, "name", VK_MAX_EXTENSION_NAME_SIZE, ext_prop.extensionName); - if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; - if (result == VK_ERROR_INITIALIZATION_FAILED) continue; + if (result == VK_ERROR_INITIALIZATION_FAILED) { + continue; + } char *spec_version = NULL; result = loader_parse_json_string(ext_item, "spec_version", &spec_version); if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; @@ -2705,7 +2700,9 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade VkExtensionProperties ext_prop = {0}; result = loader_parse_json_string_to_existing_str(ext_item, "name", VK_MAX_EXTENSION_NAME_SIZE, ext_prop.extensionName); - if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; + if (result == VK_ERROR_INITIALIZATION_FAILED) { + continue; + } char *spec_version = NULL; result = loader_parse_json_string(ext_item, "spec_version", &spec_version); @@ -2782,20 +2779,16 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; } - char *library_arch = NULL; - result = loader_parse_json_string(layer_node, "library_arch", &library_arch); - if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; - if (library_arch != NULL) { + char *library_arch = loader_cJSON_GetStringValue(loader_cJSON_GetObjectItem(layer_node, "library_arch")); + if (NULL != library_arch) { if ((strncmp(library_arch, "32", 2) == 0 && sizeof(void *) != 4) || (strncmp(library_arch, "64", 2) == 0 && sizeof(void *) != 8)) { loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, "The library architecture in layer %s doesn't match the current running architecture, skipping this layer", filename); - loader_instance_heap_free(inst, library_arch); result = VK_ERROR_INITIALIZATION_FAILED; goto out; } - loader_instance_heap_free(inst, library_arch); } result = VK_SUCCESS; @@ -2809,9 +2802,6 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade if (VK_SUCCESS != result) { loader_free_layer_properties(inst, &props); } - loader_instance_heap_free(inst, type); - loader_instance_heap_free(inst, api_version); - loader_instance_heap_free(inst, implementation_version); return result; } @@ -2841,22 +2831,12 @@ VkResult loader_add_layer_properties(const struct loader_instance *inst, struct // - If more than one "layer" object are used, then the "layers" array is // required VkResult result = VK_ERROR_INITIALIZATION_FAILED; - char *file_vers = NULL; // Make sure sure the top level json value is an object if (!json || json->type != cJSON_Object) { goto out; } - cJSON *item = loader_cJSON_GetObjectItem(json, "file_format_version"); - if (item == NULL) { - goto out; - } - - bool out_of_memory = false; - file_vers = loader_cJSON_PrintUnformatted(item, &out_of_memory); - if (out_of_memory) { - result = VK_ERROR_OUT_OF_HOST_MEMORY; - goto out; - } else if (NULL == file_vers) { + char *file_vers = loader_cJSON_GetStringValue(loader_cJSON_GetObjectItem(json, "file_format_version")); + if (NULL == file_vers) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, "loader_add_layer_properties: Manifest %s missing required field file_format_version", filename); goto out; @@ -2929,7 +2909,6 @@ VkResult loader_add_layer_properties(const struct loader_instance *inst, struct } out: - loader_instance_heap_free(inst, file_vers); return result; } @@ -3571,26 +3550,23 @@ struct ICDManifestInfo { VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *file_str, struct ICDManifestInfo *icd, bool *skipped_portability_drivers) { VkResult res = VK_SUCCESS; - cJSON *json = NULL; - char *file_vers_str = NULL; - char *library_arch_str = NULL; - char *version_str = NULL; + cJSON *icd_manifest_json = NULL; if (file_str == NULL) { goto out; } - res = loader_get_json(inst, file_str, &json); + res = loader_get_json(inst, file_str, &icd_manifest_json); if (res == VK_ERROR_OUT_OF_HOST_MEMORY) { goto out; } - if (res != VK_SUCCESS || NULL == json) { + if (res != VK_SUCCESS || NULL == icd_manifest_json) { res = VK_ERROR_INCOMPATIBLE_DRIVER; goto out; } - cJSON *item = loader_cJSON_GetObjectItem(json, "file_format_version"); - if (item == NULL) { + cJSON *file_format_version_json = loader_cJSON_GetObjectItem(icd_manifest_json, "file_format_version"); + if (file_format_version_json == NULL) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "loader_parse_icd_manifest: ICD JSON %s does not have a \'file_format_version\' field. Skipping ICD JSON.", file_str); @@ -3598,14 +3574,12 @@ VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *fil goto out; } - bool out_of_memory = false; - file_vers_str = loader_cJSON_Print(item, &out_of_memory); - if (out_of_memory) { + char *file_vers_str = loader_cJSON_GetStringValue(file_format_version_json); + if (NULL == file_vers_str) { // Only reason the print can fail is if there was an allocation issue loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "loader_parse_icd_manifest: Failed retrieving ICD JSON %s \'file_format_version\' field. Skipping ICD JSON", file_str); - res = VK_ERROR_OUT_OF_HOST_MEMORY; goto out; } loader_log(inst, VULKAN_LOADER_DRIVER_BIT, 0, "Found ICD manifest file %s, version %s", file_str, file_vers_str); @@ -3620,7 +3594,7 @@ VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *fil json_file_version.major, json_file_version.minor, json_file_version.patch); } - cJSON *itemICD = loader_cJSON_GetObjectItem(json, "ICD"); + cJSON *itemICD = loader_cJSON_GetObjectItem(icd_manifest_json, "ICD"); if (itemICD == NULL) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "loader_parse_icd_manifest: Can not find \'ICD\' object in ICD JSON file %s. Skipping ICD JSON", file_str); @@ -3628,16 +3602,16 @@ VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *fil goto out; } - item = loader_cJSON_GetObjectItem(itemICD, "library_path"); - if (item == NULL) { + cJSON *library_path_json = loader_cJSON_GetObjectItem(itemICD, "library_path"); + if (library_path_json == NULL) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "loader_parse_icd_manifest: Failed to find \'library_path\' object in ICD JSON file %s. Skipping ICD JSON.", file_str); res = VK_ERROR_INCOMPATIBLE_DRIVER; goto out; } - - char *library_path = loader_cJSON_Print(item, &out_of_memory); + bool out_of_memory = false; + char *library_path = loader_cJSON_Print(library_path_json, &out_of_memory); if (out_of_memory) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "loader_parse_icd_manifest: Failed retrieving ICD JSON %s \'library_path\' field. Skipping ICD JSON.", file_str); @@ -3645,7 +3619,7 @@ VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *fil goto out; } - if (strlen(library_path) == 0) { + if (!library_path || strlen(library_path) == 0) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "loader_parse_icd_manifest: ICD JSON %s \'library_path\' field is empty. Skipping ICD JSON.", file_str); res = VK_ERROR_INCOMPATIBLE_DRIVER; @@ -3660,20 +3634,19 @@ VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *fil goto out; } - item = loader_cJSON_GetObjectItem(itemICD, "api_version"); - if (item == NULL) { + cJSON *api_version_json = loader_cJSON_GetObjectItem(itemICD, "api_version"); + if (api_version_json == NULL) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "loader_parse_icd_manifest: ICD JSON %s does not have an \'api_version\' field. Skipping ICD JSON.", file_str); res = VK_ERROR_INCOMPATIBLE_DRIVER; goto out; } - version_str = loader_cJSON_Print(item, &out_of_memory); - if (out_of_memory) { + char *version_str = loader_cJSON_GetStringValue(api_version_json); + if (NULL == version_str) { // Only reason the print can fail is if there was an allocation issue loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "loader_parse_icd_manifest: Failed retrieving ICD JSON %s \'api_version\' field. Skipping ICD JSON.", file_str); - res = VK_ERROR_OUT_OF_HOST_MEMORY; goto out; } icd->version = loader_parse_version_string(version_str); @@ -3689,8 +3662,8 @@ VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *fil // Skip over ICD's which contain a true "is_portability_driver" value whenever the application doesn't enable // portability enumeration. - item = loader_cJSON_GetObjectItem(itemICD, "is_portability_driver"); - if (item != NULL && item->type == cJSON_True && inst && !inst->portability_enumeration_enabled) { + cJSON *is_portability_driver_json = loader_cJSON_GetObjectItem(itemICD, "is_portability_driver"); + if (loader_cJSON_IsTrue(is_portability_driver_json) && inst && !inst->portability_enumeration_enabled) { if (skipped_portability_drivers) { *skipped_portability_drivers = true; } @@ -3698,30 +3671,20 @@ VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *fil goto out; } - item = loader_cJSON_GetObjectItem(itemICD, "library_arch"); - if (item != NULL) { - library_arch_str = loader_cJSON_Print(item, &out_of_memory); - if (out_of_memory) { - res = VK_ERROR_OUT_OF_HOST_MEMORY; + char *library_arch_str = loader_cJSON_GetStringValue(loader_cJSON_GetObjectItem(itemICD, "library_arch")); + if (library_arch_str != NULL) { + // cJSON includes the quotes by default, so we need to look for those here + if ((strncmp(library_arch_str, "32", 4) == 0 && sizeof(void *) != 4) || + (strncmp(library_arch_str, "64", 4) == 0 && sizeof(void *) != 8)) { + loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, + "loader_parse_icd_manifest: Driver library architecture doesn't match the current running " + "architecture, skipping this driver"); + res = VK_ERROR_INCOMPATIBLE_DRIVER; goto out; } - if (NULL != library_arch_str) { - // cJSON includes the quotes by default, so we need to look for those here - if ((strncmp(library_arch_str, "32", 4) == 0 && sizeof(void *) != 4) || - (strncmp(library_arch_str, "64", 4) == 0 && sizeof(void *) != 8)) { - loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, - "loader_parse_icd_manifest: Driver library architecture doesn't match the current running " - "architecture, skipping this driver"); - res = VK_ERROR_INCOMPATIBLE_DRIVER; - goto out; - } - } } out: - loader_cJSON_Delete(json); - loader_instance_heap_free(inst, file_vers_str); - loader_instance_heap_free(inst, version_str); - loader_instance_heap_free(inst, library_arch_str); + loader_cJSON_Delete(icd_manifest_json); return res; } diff --git a/loader/settings.c b/loader/settings.c index 136ec5e58..24f408f57 100644 --- a/loader/settings.c +++ b/loader/settings.c @@ -109,17 +109,15 @@ uint32_t parse_log_filters_from_strings(struct loader_string_list* log_filters) return filters; } -bool parse_json_enable_disable_option(const struct loader_instance* inst, cJSON* object, const char* key) { - char* str = NULL; - VkResult res = loader_parse_json_string(object, key, &str); - if (res != VK_SUCCESS || NULL == str) { +bool parse_json_enable_disable_option(cJSON* object) { + char* str = loader_cJSON_GetStringValue(object); + if (NULL == str) { return false; } bool enable = false; if (strcmp(str, "enabled") == 0) { enable = true; } - loader_instance_heap_free(inst, str); return enable; } @@ -398,9 +396,9 @@ VkResult get_loader_settings(const struct loader_instance* inst, loader_settings break; } cJSON* app_key = NULL; - cJSON_ArrayForEach(app_keys, settings_object_iter) { + cJSON_ArrayForEach(app_key, app_keys) { char* app_key_str = loader_cJSON_GetStringValue(app_key); - if (strcmp(current_process_path, app_key_str) == 0) { + if (app_key_str && strcmp(current_process_path, app_key_str) == 0) { settings_to_use = settings_object_iter; break; } From 3fbc9fbfb546fc1cad3a2cf8287b56c358ce6e9b Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Mon, 25 Nov 2024 10:19:56 -0600 Subject: [PATCH 21/23] Add bounds check to memmove in loader_remove_layer_in_list Makes sure we are only moving valid elements around, and not iterating past the end of the allocation. --- loader/loader.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/loader/loader.c b/loader/loader.c index 0c123ff24..321060894 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -539,9 +539,10 @@ void loader_remove_layer_in_list(const struct loader_instance *inst, struct load // Remove the current invalid meta-layer from the layer list. Use memmove since we are // overlapping the source and destination addresses. - memmove(&layer_list->list[layer_to_remove], &layer_list->list[layer_to_remove + 1], - sizeof(struct loader_layer_properties) * (layer_list->count - 1 - layer_to_remove)); - + if (layer_to_remove + 1 <= layer_list->count) { + memmove(&layer_list->list[layer_to_remove], &layer_list->list[layer_to_remove + 1], + sizeof(struct loader_layer_properties) * (layer_list->count - 1 - layer_to_remove)); + } // Decrement the count (because we now have one less) and decrement the loop index since we need to // re-check this index. layer_list->count--; @@ -2522,12 +2523,6 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade goto out; } - result = loader_copy_to_new_str(inst, filename, &props.manifest_file_name); - if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { - loader_instance_heap_free(inst, library_path); - goto out; - } - // This function takes ownership of library_path_str - so we don't need to clean it up result = combine_manifest_directory_and_library_path(inst, library_path, filename, &props.lib_name); if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; @@ -4002,7 +3997,7 @@ VkResult loader_scan_for_implicit_layers(struct loader_instance *inst, struct lo goto out; } - // Remove layers from settings file that are off, are implicit, or are implicit layers that aren't active + // Remove layers from settings file that are off, are explicit, or are implicit layers that aren't active for (uint32_t i = 0; i < settings_layers.count; ++i) { if (settings_layers.list[i].settings_control_value == LOADER_SETTINGS_LAYER_CONTROL_OFF || settings_layers.list[i].settings_control_value == LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION || From 970b973a52def9bc19c7b69b41060c4a18a0e0ee Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Mon, 25 Nov 2024 10:33:59 -0600 Subject: [PATCH 22/23] Free ICD "library_path" when it contains an empty string If "library_path" contained nothing, loader_parse_json_string returns the string "", which still needs to be freed. --- loader/loader.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/loader/loader.c b/loader/loader.c index 321060894..3eade9560 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -3612,12 +3612,11 @@ VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *fil "loader_parse_icd_manifest: Failed retrieving ICD JSON %s \'library_path\' field. Skipping ICD JSON.", file_str); res = VK_ERROR_OUT_OF_HOST_MEMORY; goto out; - } - - if (!library_path || strlen(library_path) == 0) { + } else if (!library_path || strlen(library_path) == 0) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "loader_parse_icd_manifest: ICD JSON %s \'library_path\' field is empty. Skipping ICD JSON.", file_str); res = VK_ERROR_INCOMPATIBLE_DRIVER; + loader_instance_heap_free(inst, library_path); goto out; } From 40bcfb2c98f7abd793e4d81c6c90746faf5bf39c Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Mon, 25 Nov 2024 15:55:49 -0600 Subject: [PATCH 23/23] Move narrow() & escape_backslashes_for_json() into common code Because these functions are requied on windows for paths to behave correct, it is good to put them in common code functions (like EnvVarWrapper) instead of requiring all code to remember to call them. Having JsonWriter handle the path fixup means less changes of double application occuring. --- tests/framework/json_writer.h | 29 +++++++++++++++++++-- tests/framework/test_environment.cpp | 26 +++++++++---------- tests/framework/test_util.cpp | 22 +++------------- tests/framework/test_util.h | 38 +++++++++++++++++----------- tests/loader_layer_tests.cpp | 6 ++--- tests/loader_regression_tests.cpp | 2 +- tests/loader_settings_tests.cpp | 2 +- 7 files changed, 72 insertions(+), 53 deletions(-) diff --git a/tests/framework/json_writer.h b/tests/framework/json_writer.h index 6d2595806..f22db2892 100644 --- a/tests/framework/json_writer.h +++ b/tests/framework/json_writer.h @@ -79,13 +79,25 @@ struct JsonWriter { void AddKeyedString(std::string const& key, std::string const& value) { CommaAndNewLine(); Indent(); - output += "\"" + key + "\": \"" + value + "\""; + output += "\"" + key + "\": \"" + escape(value) + "\""; } void AddString(std::string const& value) { CommaAndNewLine(); Indent(); - output += "\"" + value + "\""; + output += "\"" + escape(value) + "\""; } +#if defined(WIN32) + void AddKeyedString(std::string const& key, std::wstring const& value) { + CommaAndNewLine(); + Indent(); + output += "\"" + key + "\": \"" + escape(narrow(value)) + "\""; + } + void AddString(std::wstring const& value) { + CommaAndNewLine(); + Indent(); + output += "\"" + escape(narrow(value)) + "\""; + } +#endif void AddKeyedBool(std::string const& key, bool value) { CommaAndNewLine(); @@ -98,6 +110,19 @@ struct JsonWriter { output += std::string(value ? "true" : "false"); } + // Json doesn't allow `\` in strings, it must be escaped. Thus we have to convert '\\' to '\\\\' in strings + static std::string escape(std::string const& in_path) { + std::string out; + for (auto& c : in_path) { + if (c == '\\') + out += "\\\\"; + else + out += c; + } + return out; + } + static std::string escape(std::filesystem::path const& in_path) { return escape(narrow(in_path.native())); } + private: void CommaAndNewLine() { if (stack.size() > 0) { diff --git a/tests/framework/test_environment.cpp b/tests/framework/test_environment.cpp index 6a6f505b9..efa4706fe 100644 --- a/tests/framework/test_environment.cpp +++ b/tests/framework/test_environment.cpp @@ -528,17 +528,17 @@ TestICD& FrameworkEnvironment::add_icd(TestICDDetails icd_details) noexcept { break; case (ManifestDiscoveryType::env_var): if (icd_details.is_dir) { - env_var_vk_icd_filenames.add_to_list(narrow(folder->location())); + env_var_vk_icd_filenames.add_to_list(folder->location()); } else { - env_var_vk_icd_filenames.add_to_list(narrow(folder->location() / new_manifest_path)); + env_var_vk_icd_filenames.add_to_list(folder->location() / new_manifest_path); } platform_shim->add_known_path(folder->location()); break; case (ManifestDiscoveryType::add_env_var): if (icd_details.is_dir) { - add_env_var_vk_icd_filenames.add_to_list(narrow(folder->location())); + add_env_var_vk_icd_filenames.add_to_list(folder->location()); } else { - add_env_var_vk_icd_filenames.add_to_list(narrow(folder->location() / new_manifest_path)); + add_env_var_vk_icd_filenames.add_to_list(folder->location() / new_manifest_path); } platform_shim->add_known_path(folder->location()); break; @@ -590,17 +590,17 @@ void FrameworkEnvironment::add_layer_impl(TestLayerDetails layer_details, Manife if (category == ManifestCategory::explicit_layer) { fs_ptr = &get_folder(ManifestLocation::explicit_layer_env_var); if (layer_details.is_dir) { - env_var_vk_layer_paths.add_to_list(narrow(fs_ptr->location())); + env_var_vk_layer_paths.add_to_list(fs_ptr->location()); } else { - env_var_vk_layer_paths.add_to_list(narrow(fs_ptr->location() / layer_details.json_name)); + env_var_vk_layer_paths.add_to_list(fs_ptr->location() / layer_details.json_name); } } if (category == ManifestCategory::implicit_layer) { fs_ptr = &get_folder(ManifestLocation::implicit_layer_env_var); if (layer_details.is_dir) { - env_var_vk_implicit_layer_paths.add_to_list(narrow(fs_ptr->location())); + env_var_vk_implicit_layer_paths.add_to_list(fs_ptr->location()); } else { - env_var_vk_implicit_layer_paths.add_to_list(narrow(fs_ptr->location() / layer_details.json_name)); + env_var_vk_implicit_layer_paths.add_to_list(fs_ptr->location() / layer_details.json_name); } } platform_shim->add_known_path(fs_ptr->location()); @@ -609,17 +609,17 @@ void FrameworkEnvironment::add_layer_impl(TestLayerDetails layer_details, Manife if (category == ManifestCategory::explicit_layer) { fs_ptr = &get_folder(ManifestLocation::explicit_layer_add_env_var); if (layer_details.is_dir) { - add_env_var_vk_layer_paths.add_to_list(narrow(fs_ptr->location())); + add_env_var_vk_layer_paths.add_to_list(fs_ptr->location()); } else { - add_env_var_vk_layer_paths.add_to_list(narrow(fs_ptr->location() / layer_details.json_name)); + add_env_var_vk_layer_paths.add_to_list(fs_ptr->location() / layer_details.json_name); } } if (category == ManifestCategory::implicit_layer) { fs_ptr = &get_folder(ManifestLocation::implicit_layer_add_env_var); if (layer_details.is_dir) { - add_env_var_vk_implicit_layer_paths.add_to_list(narrow(fs_ptr->location())); + add_env_var_vk_implicit_layer_paths.add_to_list(fs_ptr->location()); } else { - add_env_var_vk_implicit_layer_paths.add_to_list(narrow(fs_ptr->location() / layer_details.json_name)); + add_env_var_vk_implicit_layer_paths.add_to_list(fs_ptr->location() / layer_details.json_name); } } platform_shim->add_known_path(fs_ptr->location()); @@ -740,7 +740,7 @@ std::string get_loader_settings_file_contents(const LoaderSettings& loader_setti for (const auto& config : setting.layer_configurations) { writer.StartObject(); writer.AddKeyedString("name", config.name); - writer.AddKeyedString("path", escape_backslashes_for_json(config.path)); + writer.AddKeyedString("path", config.path.native()); writer.AddKeyedString("control", config.control); writer.AddKeyedBool("treat_as_implicit_manifest", config.treat_as_implicit_manifest); writer.EndObject(); diff --git a/tests/framework/test_util.cpp b/tests/framework/test_util.cpp index 58a712562..2a433c703 100644 --- a/tests/framework/test_util.cpp +++ b/tests/framework/test_util.cpp @@ -117,7 +117,7 @@ void print_vector_of_strings(JsonWriter& writer, const char* object_name, std::v if (strings.size() == 0) return; writer.StartKeyedArray(object_name); for (auto const& str : strings) { - writer.AddString(escape_backslashes_for_json(str)); + writer.AddString(std::filesystem::path(str).native()); } writer.EndArray(); } @@ -125,7 +125,7 @@ void print_vector_of_strings(JsonWriter& writer, const char* object_name, std::v if (paths.size() == 0) return; writer.StartKeyedArray(object_name); for (auto const& path : paths) { - writer.AddString(escape_backslashes_for_json(path)); + writer.AddString(path.native()); } writer.EndArray(); } @@ -137,7 +137,7 @@ std::string ManifestICD::get_manifest_str() const { writer.StartObject(); writer.AddKeyedString("file_format_version", file_format_version.get_version_str()); writer.StartKeyedObject("ICD"); - writer.AddKeyedString("library_path", escape_backslashes_for_json(lib_path)); + writer.AddKeyedString("library_path", lib_path.native()); writer.AddKeyedString("api_version", version_to_string(api_version)); writer.AddKeyedBool("is_portability_driver", is_portability_driver); if (!library_arch.empty()) writer.AddKeyedString("library_arch", library_arch); @@ -159,7 +159,7 @@ void ManifestLayer::LayerDescription::get_manifest_str(JsonWriter& writer) const writer.AddKeyedString("name", name); writer.AddKeyedString("type", get_type_str(type)); if (!lib_path.empty()) { - writer.AddKeyedString("library_path", escape_backslashes_for_json(lib_path)); + writer.AddKeyedString("library_path", lib_path.native()); } writer.AddKeyedString("api_version", version_to_string(api_version)); writer.AddKeyedString("implementation_version", std::to_string(implementation_version)); @@ -217,20 +217,6 @@ std::string ManifestLayer::get_manifest_str() const { return writer.output; } -// Json doesn't allow `\` in strings, it must be escaped. Thus we have to convert '\\' to '\\\\' in strings -std::string escape_backslashes_for_json(std::string const& in_path) { - std::string out; - for (auto& c : in_path) { - if (c == '\\') - out += "\\\\"; - else - out += c; - } - return out; -} -std::string escape_backslashes_for_json(std::filesystem::path const& in_path) { - return escape_backslashes_for_json(narrow(in_path.native())); -} namespace fs { // internal implementation helper for per-platform creating & destroying folders int create_folder(std::filesystem::path const& path) { diff --git a/tests/framework/test_util.h b/tests/framework/test_util.h index 2884a01ea..cf8a43532 100644 --- a/tests/framework/test_util.h +++ b/tests/framework/test_util.h @@ -98,6 +98,19 @@ #define FRAMEWORK_EXPORT #endif +// Define it here so that json_writer.h has access to these functions +#if defined(WIN32) +// Convert an UTF-16 wstring to an UTF-8 string +std::string narrow(const std::wstring& utf16); +// Convert an UTF-8 string to an UTF-16 wstring +std::wstring widen(const std::string& utf8); +#else +// Do nothing passthrough for the sake of Windows & UTF-16 +std::string narrow(const std::string& utf16); +// Do nothing passthrough for the sake of Windows & UTF-16 +std::string widen(const std::string& utf8); +#endif + #include "json_writer.h" // get_env_var() - returns a std::string of `name`. if report_failure is true, then it will log to stderr that it didn't find the @@ -145,6 +158,15 @@ struct EnvVarWrapper { cur_value += list_item; set_env_var(); } +#if defined(WIN32) + void add_to_list(std::wstring const& list_item) { + if (!cur_value.empty()) { + cur_value += OS_ENV_VAR_LIST_SEPARATOR; + } + cur_value += narrow(list_item); + set_env_var(); + } +#endif void remove_value() const { remove_env_var(); } const char* get() const { return name.c_str(); } const char* value() const { return cur_value.c_str(); } @@ -176,8 +198,6 @@ void print_error_message(LSTATUS status, const char* function_name, std::string struct ManifestICD; // forward declaration for FolderManager::write struct ManifestLayer; // forward declaration for FolderManager::write -std::string escape_backslashes_for_json(std::string const& in_path); -std::string escape_backslashes_for_json(std::filesystem::path const& in_path); namespace fs { int create_folder(std::filesystem::path const& path); @@ -221,18 +241,6 @@ class FolderManager { // size_dst - number of characters in the dst array inline void copy_string_to_char_array(std::string const& src, char* dst, size_t size_dst) { dst[src.copy(dst, size_dst - 1)] = 0; } -#if defined(WIN32) -// Convert an UTF-16 wstring to an UTF-8 string -std::string narrow(const std::wstring& utf16); -// Convert an UTF-8 string to an UTF-16 wstring -std::wstring widen(const std::string& utf8); -#else -// Do nothing passthrough for the sake of Windows & UTF-16 -std::string narrow(const std::string& utf16); -// Do nothing passthrough for the sake of Windows & UTF-16 -std::string widen(const std::string& utf8); -#endif - #if defined(WIN32) typedef HMODULE loader_platform_dl_handle; inline loader_platform_dl_handle loader_platform_open_library(const wchar_t* lib_path) { @@ -993,7 +1001,7 @@ inline std::string test_platform_executable_path() { if (ret > buffer.size()) return NULL; buffer.resize(ret); buffer[ret] = '\0'; - return buffer; + return narrow(std::filesystem::path(buffer).native()); } #endif diff --git a/tests/loader_layer_tests.cpp b/tests/loader_layer_tests.cpp index c8d23581c..156d8ee95 100644 --- a/tests/loader_layer_tests.cpp +++ b/tests/loader_layer_tests.cpp @@ -1149,7 +1149,7 @@ TEST(ImplicitLayers, DuplicateLayersInVK_ADD_IMPLICIT_LAYER_PATH) { auto& layer1 = env.get_test_layer(0); layer1.set_description("actually_layer_1"); layer1.set_make_spurious_log_in_create_instance("actually_layer_1"); - env.add_env_var_vk_implicit_layer_paths.add_to_list(narrow(env.get_folder(ManifestLocation::override_layer).location())); + env.add_env_var_vk_implicit_layer_paths.add_to_list(env.get_folder(ManifestLocation::override_layer).location()); env.add_implicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} .set_name(same_layer_name_1) @@ -2460,7 +2460,7 @@ TEST(ExplicitLayers, DuplicateLayersInVK_LAYER_PATH) { auto& layer1 = env.get_test_layer(0); layer1.set_description("actually_layer_1"); layer1.set_make_spurious_log_in_create_instance("actually_layer_1"); - env.env_var_vk_layer_paths.add_to_list(narrow(env.get_folder(ManifestLocation::override_layer).location())); + env.env_var_vk_layer_paths.add_to_list(env.get_folder(ManifestLocation::override_layer).location()); env.add_explicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} .set_name(same_layer_name_1) @@ -2534,7 +2534,7 @@ TEST(ExplicitLayers, DuplicateLayersInVK_ADD_LAYER_PATH) { auto& layer1 = env.get_test_layer(0); layer1.set_description("actually_layer_1"); layer1.set_make_spurious_log_in_create_instance("actually_layer_1"); - env.add_env_var_vk_layer_paths.add_to_list(narrow(env.get_folder(ManifestLocation::override_layer).location())); + env.add_env_var_vk_layer_paths.add_to_list(env.get_folder(ManifestLocation::override_layer).location()); env.add_explicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} .set_name(same_layer_name_1) diff --git a/tests/loader_regression_tests.cpp b/tests/loader_regression_tests.cpp index e7717d59f..4c6ee6cc0 100644 --- a/tests/loader_regression_tests.cpp +++ b/tests/loader_regression_tests.cpp @@ -3882,7 +3882,7 @@ TEST(DuplicateRegistryEntries, Drivers) { TEST(LibraryLoading, SystemLocations) { FrameworkEnvironment env{}; EnvVarWrapper ld_library_path("LD_LIBRARY_PATH", env.get_folder(ManifestLocation::driver).location().string()); - ld_library_path.add_to_list(narrow(env.get_folder(ManifestLocation::explicit_layer).location())); + ld_library_path.add_to_list(env.get_folder(ManifestLocation::explicit_layer).location()); auto& driver = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2).set_library_path_type(LibraryPathType::default_search_paths)) .add_physical_device({}); diff --git a/tests/loader_settings_tests.cpp b/tests/loader_settings_tests.cpp index 0fe88a69c..1c77c70a8 100644 --- a/tests/loader_settings_tests.cpp +++ b/tests/loader_settings_tests.cpp @@ -243,7 +243,7 @@ TEST(SettingsFile, SupportsMultipleSettingsSimultaneously) { } env.debug_log.clear(); // Set one set to contain the current executable path - env.loader_settings.app_specific_settings.at(0).add_app_key(escape_backslashes_for_json(test_platform_executable_path())); + env.loader_settings.app_specific_settings.at(0).add_app_key(test_platform_executable_path()); env.update_loader_settings(env.loader_settings); { auto layer_props = env.GetLayerProperties(1);