diff --git a/CMakeLists.txt b/CMakeLists.txt index acf5a42ee..7c9878919 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,6 @@ if(NOT JSONSCHEMA_PORTABLE OR APPLE) endif() find_package(Blaze REQUIRED) -find_package(JSONBinPack REQUIRED) add_subdirectory(src) diff --git a/DEPENDENCIES b/DEPENDENCIES index 1e8320598..388956aad 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,5 +1,4 @@ vendorpull https://github.com/sourcemeta/vendorpull 1dcbac42809cf87cb5b045106b863e17ad84ba02 -core https://github.com/sourcemeta/core 9889529cd1d863eb39fc77289581414a183cce19 -jsonbinpack https://github.com/sourcemeta/jsonbinpack 147050385dce7086a28ce93e18a4c3baa9719136 -blaze https://github.com/sourcemeta/blaze 53b3d89652504c1d61f908c8eb25ee3b46921e0b +core https://github.com/sourcemeta/core 7e3e5ee79ae79d2111a976d8d365cc6818661b47 +blaze https://github.com/sourcemeta/blaze 2c5efbd14f5f24dbe3860be8c8d80279edc20d10 ctrf https://github.com/ctrf-io/ctrf 93ea827d951390190171d37443bff169cf47c808 diff --git a/cmake/FindJSONBinPack.cmake b/cmake/FindJSONBinPack.cmake deleted file mode 100644 index 95c58f3ea..000000000 --- a/cmake/FindJSONBinPack.cmake +++ /dev/null @@ -1,5 +0,0 @@ -if(NOT JSONBinPack_FOUND) - set(JSONBINPACK_INSTALL OFF CACHE BOOL "disable installation") - add_subdirectory("${PROJECT_SOURCE_DIR}/vendor/jsonbinpack") - set(JSONBinPack_FOUND ON) -endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 689c42f1c..4894865fd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -40,8 +40,7 @@ target_link_libraries(jsonschema_cli PRIVATE sourcemeta::core::jsonschema) target_link_libraries(jsonschema_cli PRIVATE sourcemeta::core::openapi) target_link_libraries(jsonschema_cli PRIVATE sourcemeta::blaze::editor) target_link_libraries(jsonschema_cli PRIVATE sourcemeta::blaze::configuration) -target_link_libraries(jsonschema_cli PRIVATE sourcemeta::jsonbinpack::compiler) -target_link_libraries(jsonschema_cli PRIVATE sourcemeta::jsonbinpack::runtime) +target_link_libraries(jsonschema_cli PRIVATE sourcemeta::blaze::binpack) target_link_libraries(jsonschema_cli PRIVATE sourcemeta::blaze::compiler) target_link_libraries(jsonschema_cli PRIVATE sourcemeta::blaze::evaluator) target_link_libraries(jsonschema_cli PRIVATE sourcemeta::blaze::output) diff --git a/src/command_decode.cc b/src/command_decode.cc index df0487b1e..f886898b2 100644 --- a/src/command_decode.cc +++ b/src/command_decode.cc @@ -2,8 +2,7 @@ #include #include -#include -#include +#include #include // std::filesystem #include // std::ifstream @@ -50,9 +49,9 @@ auto sourcemeta::jsonschema::decode(const sourcemeta::core::Options &options) const auto &custom_resolver{ resolver(options, options.contains("http"), dialect, configuration)}; - sourcemeta::jsonbinpack::compile(schema, sourcemeta::core::schema_walker, - custom_resolver); - const auto encoding{sourcemeta::jsonbinpack::load(schema)}; + sourcemeta::blaze::binpack_compile(schema, sourcemeta::core::schema_walker, + custom_resolver); + const auto encoding{sourcemeta::blaze::binpack_load(schema)}; auto input_stream{sourcemeta::core::read_file(options.positional().front())}; @@ -60,7 +59,7 @@ auto sourcemeta::jsonschema::decode(const sourcemeta::core::Options &options) std::ofstream output_stream(sourcemeta::core::weakly_canonical(output), std::ios::binary); output_stream.exceptions(std::ios_base::badbit); - sourcemeta::jsonbinpack::Decoder decoder{input_stream}; + sourcemeta::blaze::BinPackDecoder decoder{input_stream}; try { if (output.extension() == ".jsonl") { diff --git a/src/command_encode.cc b/src/command_encode.cc index 84f21e515..ce31bcb41 100644 --- a/src/command_encode.cc +++ b/src/command_encode.cc @@ -4,8 +4,7 @@ #include #include -#include -#include +#include #include // std::filesystem #include // std::ofstream @@ -41,9 +40,9 @@ auto sourcemeta::jsonschema::encode(const sourcemeta::core::Options &options) const auto &custom_resolver{ resolver(options, options.contains("http"), dialect, configuration)}; - sourcemeta::jsonbinpack::compile(schema, sourcemeta::core::schema_walker, - custom_resolver); - const auto encoding{sourcemeta::jsonbinpack::load(schema)}; + sourcemeta::blaze::binpack_compile(schema, sourcemeta::core::schema_walker, + custom_resolver); + const auto encoding{sourcemeta::blaze::binpack_load(schema)}; const std::filesystem::path document{options.positional().front()}; const auto original_size{std::filesystem::file_size(document)}; @@ -60,7 +59,7 @@ auto sourcemeta::jsonschema::encode(const sourcemeta::core::Options &options) sourcemeta::core::weakly_canonical(options.positional().at(1)), std::ios::binary); output_stream.exceptions(std::ios_base::badbit); - sourcemeta::jsonbinpack::Encoder encoder{output_stream}; + sourcemeta::blaze::BinPackEncoder encoder{output_stream}; std::size_t count{0}; for (const auto &entry : sourcemeta::core::JSONL{stream}) { LOG_VERBOSE(options) << "Encoding entry #" << count << "\n"; @@ -87,7 +86,7 @@ auto sourcemeta::jsonschema::encode(const sourcemeta::core::Options &options) sourcemeta::core::weakly_canonical(options.positional().at(1)), std::ios::binary); output_stream.exceptions(std::ios_base::badbit); - sourcemeta::jsonbinpack::Encoder encoder{output_stream}; + sourcemeta::blaze::BinPackEncoder encoder{output_stream}; encoder.write(entry, encoding); output_stream.flush(); const auto total_size{static_cast(output_stream.tellp())}; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9736c80e9..17e37c09a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -440,6 +440,8 @@ add_jsonschema_test(validate/pass_entrypoint_pointer) add_jsonschema_test(validate/pass_entrypoint_pointer_fragment) add_jsonschema_test(validate/pass_entrypoint_pointer_space) add_jsonschema_test(validate/pass_entrypoint_pointer_fragment_space) +add_jsonschema_test(validate/pass_entrypoint_pointer_escaped) +add_jsonschema_test(validate/pass_entrypoint_pointer_fragment_escaped) add_jsonschema_test(validate/pass_entrypoint_uri) add_jsonschema_test(validate/fail_entrypoint_invalid_pointer) add_jsonschema_test(validate/fail_entrypoint_invalid_pointer_escape) @@ -997,6 +999,8 @@ add_jsonschema_test(compile/pass_entrypoint_pointer) add_jsonschema_test(compile/pass_entrypoint_pointer_fragment) add_jsonschema_test(compile/pass_entrypoint_pointer_space) add_jsonschema_test(compile/pass_entrypoint_pointer_fragment_space) +add_jsonschema_test(compile/pass_entrypoint_pointer_escaped) +add_jsonschema_test(compile/pass_entrypoint_pointer_fragment_escaped) add_jsonschema_test(compile/pass_entrypoint_uri) add_jsonschema_test(compile/fail_entrypoint_invalid_pointer) add_jsonschema_test(compile/fail_entrypoint_invalid_pointer_escape) diff --git a/test/compile/pass_entrypoint_pointer_escaped.clitest b/test/compile/pass_entrypoint_pointer_escaped.clitest new file mode 100644 index 000000000..65e294fb7 --- /dev/null +++ b/test/compile/pass_entrypoint_pointer_escaped.clitest @@ -0,0 +1,49 @@ +WRITE schema.json UNTIL EOF +{ + "$id": "https://example.com/schema", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "$defs": { + "My:Name": { + "type": "string", + "minLength": 1 + } + } +} +EOF + +RUN compile schema.json --entrypoint '/$$defs/My%3AName' STDIN /dev/null IN . INTO result.txt EXPECTING 0 + +WRITE expected.txt UNTIL EOF +1> [ +1> 9, +1> false, +1> true, +1> [ +1> [ +1> [ +1> 22, +1> [ "minLength" ], +1> [], +1> "https://example.com/schema#/$defs/My:Name/minLength", +1> 2, +1> 1, +1> [ 10, 0 ] +1> ], +1> [ +1> 11, +1> [ "type" ], +1> [], +1> "https://example.com/schema#/$defs/My:Name/type", +1> 2, +1> 1, +1> [ 8, 4 ] +1> ] +1> ] +1> ], +1> [], +1> [ "https://json-schema.org/draft/2020-12/vocab/validation" ] +1> ] +EOF + +COMPARE result.txt AGAINST expected.txt diff --git a/test/compile/pass_entrypoint_pointer_fragment_escaped.clitest b/test/compile/pass_entrypoint_pointer_fragment_escaped.clitest new file mode 100644 index 000000000..f4a9edd4d --- /dev/null +++ b/test/compile/pass_entrypoint_pointer_fragment_escaped.clitest @@ -0,0 +1,49 @@ +WRITE schema.json UNTIL EOF +{ + "$id": "https://example.com/schema", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "$defs": { + "My:Name": { + "type": "string", + "minLength": 1 + } + } +} +EOF + +RUN compile schema.json --entrypoint '#/$$defs/My%3AName' STDIN /dev/null IN . INTO result.txt EXPECTING 0 + +WRITE expected.txt UNTIL EOF +1> [ +1> 9, +1> false, +1> true, +1> [ +1> [ +1> [ +1> 22, +1> [ "minLength" ], +1> [], +1> "https://example.com/schema#/$defs/My:Name/minLength", +1> 2, +1> 1, +1> [ 10, 0 ] +1> ], +1> [ +1> 11, +1> [ "type" ], +1> [], +1> "https://example.com/schema#/$defs/My:Name/type", +1> 2, +1> 1, +1> [ 8, 4 ] +1> ] +1> ] +1> ], +1> [], +1> [ "https://json-schema.org/draft/2020-12/vocab/validation" ] +1> ] +EOF + +COMPARE result.txt AGAINST expected.txt diff --git a/test/validate/pass_entrypoint_pointer_escaped.clitest b/test/validate/pass_entrypoint_pointer_escaped.clitest new file mode 100644 index 000000000..c758b53a9 --- /dev/null +++ b/test/validate/pass_entrypoint_pointer_escaped.clitest @@ -0,0 +1,26 @@ +WRITE schema.json UNTIL EOF +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "$defs": { + "My:Name": { + "type": "integer", + "minimum": 1 + } + } +} +EOF + +WRITE instance.json UNTIL EOF +42 +EOF + +RUN validate schema.json instance.json --entrypoint '/$$defs/My%3AName' --verbose STDIN /dev/null IN . INTO result.txt EXPECTING 0 + +WRITE expected.txt UNTIL EOF +2> ok: instance.json +2> +2> 1 validated, 1 passed, 0 failed +EOF + +COMPARE result.txt AGAINST expected.txt diff --git a/test/validate/pass_entrypoint_pointer_fragment_escaped.clitest b/test/validate/pass_entrypoint_pointer_fragment_escaped.clitest new file mode 100644 index 000000000..2b6f1258e --- /dev/null +++ b/test/validate/pass_entrypoint_pointer_fragment_escaped.clitest @@ -0,0 +1,26 @@ +WRITE schema.json UNTIL EOF +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "$defs": { + "My:Name": { + "type": "integer", + "minimum": 1 + } + } +} +EOF + +WRITE instance.json UNTIL EOF +42 +EOF + +RUN validate schema.json instance.json --entrypoint '#/$$defs/My%3AName' --verbose STDIN /dev/null IN . INTO result.txt EXPECTING 0 + +WRITE expected.txt UNTIL EOF +2> ok: instance.json +2> +2> 1 validated, 1 passed, 0 failed +EOF + +COMPARE result.txt AGAINST expected.txt diff --git a/vendor/blaze/DEPENDENCIES b/vendor/blaze/DEPENDENCIES index 3feeb9892..2be881d73 100644 --- a/vendor/blaze/DEPENDENCIES +++ b/vendor/blaze/DEPENDENCIES @@ -1,3 +1,3 @@ vendorpull https://github.com/sourcemeta/vendorpull 1dcbac42809cf87cb5b045106b863e17ad84ba02 -core https://github.com/sourcemeta/core 9889529cd1d863eb39fc77289581414a183cce19 +core https://github.com/sourcemeta/core 7e3e5ee79ae79d2111a976d8d365cc6818661b47 jsonschema-test-suite https://github.com/json-schema-org/JSON-Schema-Test-Suite 6648e8194c69697b2e1a15fe76a06a480b183a51 diff --git a/vendor/core/src/core/jsonpointer/include/sourcemeta/core/jsonpointer.h b/vendor/core/src/core/jsonpointer/include/sourcemeta/core/jsonpointer.h index 20c92a82c..2ffe0b2d3 100644 --- a/vendor/core/src/core/jsonpointer/include/sourcemeta/core/jsonpointer.h +++ b/vendor/core/src/core/jsonpointer/include/sourcemeta/core/jsonpointer.h @@ -409,9 +409,10 @@ auto to_pointer( -> Pointer; /// @ingroup jsonpointer -/// Parse the URI fragment representation of a JSON Pointer, percent-decoding -/// the fragment data before interpretation as mandated by RFC 3986, Section -/// 2.4 and RFC 6901, Section 6. The result is not set if the URI has no +/// Parse the URI fragment representation of a JSON Pointer. Reference tokens +/// are separated before their octets are decoded, so a percent-encoded slash +/// neither separates two tokens, per RFC 3986, Section 2.2, nor belongs to +/// one, per RFC 6901, Section 3. The result is not set if the URI has no /// fragment or if its fragment is not a valid JSON Pointer. For example: /// /// ```cpp diff --git a/vendor/core/src/core/jsonpointer/jsonpointer.cc b/vendor/core/src/core/jsonpointer/jsonpointer.cc index 67470cbf0..983f5cc63 100644 --- a/vendor/core/src/core/jsonpointer/jsonpointer.cc +++ b/vendor/core/src/core/jsonpointer/jsonpointer.cc @@ -11,7 +11,6 @@ #include "stringify.h" #include // assert -#include // std::size_t #include // std::cbegin, std::cend, std::prev, std::advance #include // std::allocator #include // std::optional, std::nullopt @@ -24,6 +23,29 @@ namespace { +// The slash that belongs to a reference token is spelled `~1`, and the +// transformation evaluation applies to a token is alone in turning that back +// into one, so a token still holding a slash once its octets are decoded +// conforms to no reference token and is an error condition rather than a +// member name +// See https://www.rfc-editor.org/rfc/rfc6901#section-3 +// See https://www.rfc-editor.org/rfc/rfc6901#section-4 +auto append_reference_token(sourcemeta::core::JSON::String &output, + const std::string_view token) -> bool { + if (token.find('%') == std::string_view::npos) { + output.append(token); + return true; + } + + const auto decoded{sourcemeta::core::URI::unescape(token)}; + if (decoded.find('/') != std::string::npos) { + return false; + } + + output.append(decoded); + return true; +} + template