diff --git a/.gitignore b/.gitignore index 04964c9..10ee696 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /bindings/*/target /bench_helpers/target /jsonschema-test-suite/target +/jsonschema-csr/target .hypothesis .benchmarks /jsonschema/Cargo.lock diff --git a/jsonschema-csr/Cargo.lock b/jsonschema-csr/Cargo.lock new file mode 100644 index 0000000..9d406df --- /dev/null +++ b/jsonschema-csr/Cargo.lock @@ -0,0 +1,739 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bench_helpers" +version = "0.1.0" +dependencies = [ + "criterion", + "serde", + "serde_json", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + +[[package]] +name = "cast" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "bitflags", + "textwrap", + "unicode-width", +] + +[[package]] +name = "criterion" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10" +dependencies = [ + "atty", + "cast", + "clap", + "criterion-plot", + "csv", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +dependencies = [ + "cfg-if", + "lazy_static", +] + +[[package]] +name = "csv" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +dependencies = [ + "bstr", + "csv-core", + "itoa 0.4.8", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "js-sys" +version = "0.3.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonschema-csr" +version = "0.1.0" +dependencies = [ + "bench_helpers", + "criterion", + "serde_json", + "test-case", + "url", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "plotters" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" + +[[package]] +name = "plotters-svg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd249e82c21598a9a426a4e00dd7adc1d640b22445ec8545feef801d1a74c221" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f51245e1e62e1f1629cbfec37b5793bbabcaeb90f30e94d2ba03564687353e4" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "regex" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd" + +[[package]] +name = "serde" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +dependencies = [ + "itoa 1.0.1", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "test-case" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6344589a99d3971d6fa4e8314dbcbeca2df6273a6b642e46906971779159af1f" +dependencies = [ + "cfg-if", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" + +[[package]] +name = "web-sys" +version = "0.3.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/jsonschema-csr/Cargo.toml b/jsonschema-csr/Cargo.toml new file mode 100644 index 0000000..b4bd06d --- /dev/null +++ b/jsonschema-csr/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "jsonschema-csr" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde_json = "1.0.79" +url = "2.2.2" + +[dev-dependencies] +bench_helpers = { path = "../bench_helpers" } +criterion = "0.3.5" +test-case = "2.0.2" + +[[bench]] +harness = false +name = "resolver" diff --git a/jsonschema-csr/src/compilation/mod.rs b/jsonschema-csr/src/compilation/mod.rs new file mode 100644 index 0000000..5c362d7 --- /dev/null +++ b/jsonschema-csr/src/compilation/mod.rs @@ -0,0 +1,60 @@ +/// Compressed sparse row format for JSON Schema. +/// +/// Fast and cache efficient validation requires fast iteration over the schema, therefore a +/// representation like `serde_json::Value` should be converted to a more compact one. +/// +/// JSON Schema is a directed graph, where cycles could be represented via the `$ref` keyword. +use serde_json::Value; +pub mod resolver; + +use resolver::Resolver; + +pub fn build(schema: &Value) { + // The input graph may be incomplete: + // 1. Some nodes are absent because `$ref` can point to remote locations; + // 2. Edges coming from the `$ref` keywords are unknown as they require resolving; + // + // Build a vector of nodes where each node is a reference to a node from `schema` or to a + // loaded remote schema. The `$ref` keyword nodes are resolved during this process: + // - Local references are resolved in-place, so the resulting node is the actual target node + // - Remote references are loaded into a separate container, and processed as any other + // nodes. + // + // Remote schemas could also have references that should be resolved, therefore + // this step is applied recursively to all resolved schemas. + let resolver = Resolver::new(schema); + let mut output = vec![schema]; + let mut stack = vec![schema]; + while let Some(node) = stack.pop() { + match node { + Value::Object(object) => { + for (key, value) in object { + stack.push(value); + output.push(value); + } + } + Value::Array(array) => {} + _ => {} + } + } + for r in &output { + println!("REF: {:p}", *r as *const Value); + } + println!("{:?}", output); +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::{json, Value}; + + #[test] + fn it_works() { + let schema = json!({ + "properties": { + "foo": {"$ref": "#"} + } + }); + build(&schema); + } +} diff --git a/jsonschema-csr/src/compilation/resolver.rs b/jsonschema-csr/src/compilation/resolver.rs new file mode 100644 index 0000000..ab2d31a --- /dev/null +++ b/jsonschema-csr/src/compilation/resolver.rs @@ -0,0 +1,339 @@ +use serde_json::{Map, Value}; +use std::collections::HashMap; +use url::Url; + +pub(crate) type SchemaStore<'schema> = HashMap; +pub(crate) const DEFAULT_ROOT_URL: &str = "json-schema:///"; + +pub(crate) fn id_of(schema: &Value) -> Option<&str> { + if let serde_json::Value::Object(object) = schema { + id_of_object(object) + } else { + None + } +} +#[inline] +pub(crate) fn id_of_object(object: &Map) -> Option<&str> { + object.get("$id").and_then(Value::as_str) +} + +pub(crate) fn scope_of(schema: &Value) -> Url { + let url = id_of(schema).unwrap_or(DEFAULT_ROOT_URL); + Url::parse(url).unwrap() +} + +pub(crate) struct Resolver<'schema> { + root: &'schema Value, + schemas: SchemaStore<'schema>, +} + +impl<'schema> Resolver<'schema> { + pub(crate) fn new(root: &'schema Value) -> Self { + Self { + root, + schemas: collect_schemas(root), + } + } +} + +macro_rules! push_map { + ($stack:expr, $object:expr, $scope_idx:expr) => { + for (key, value) in $object { + if key == "enum" || key == "const" { + continue; + } + $stack.push(($scope_idx, value)); + } + }; +} + +macro_rules! push_array { + ($stack:expr, $array:expr, $scope_idx:expr) => { + for item in $array { + $stack.push(($scope_idx, item)); + } + }; +} + +macro_rules! push_map_unrolled { + ($stack:expr, $store:expr, $scopes:expr, $object:expr, $scope_idx:expr) => { + for (key, value) in $object { + if key == "enum" || key == "const" { + continue; + } + match value { + Value::Object(object) => { + if let Some(id) = id_of_object(object) { + new_schema!($store, $scopes, $scope_idx, id, value); + push_map!($stack, object, $scopes.len() - 1); + } else { + push_map!($stack, object, $scope_idx); + } + } + Value::Array(array) => { + push_array_unrolled!($stack, $store, $scopes, array, $scope_idx); + } + _ => {} + } + } + }; +} + +macro_rules! push_array_unrolled { + ($stack:expr, $store:expr, $scopes:expr, $array:expr, $scope_idx:expr) => { + for item in $array { + match item { + Value::Object(object) => { + if let Some(id) = id_of_object(object) { + new_schema!($store, $scopes, $scope_idx, id, item); + push_map!($stack, object, $scopes.len() - 1); + } else { + push_map!($stack, object, $scope_idx); + } + } + Value::Array(array) => { + push_array!($stack, array, $scope_idx); + } + _ => {} + } + } + }; +} + +macro_rules! new_schema { + ($store:expr, $scopes:expr, $scope_idx:expr, $id:expr, $value:expr) => { + let mut scope = $scopes[$scope_idx].join($id).unwrap(); + // Empty fragments are discouraged and are not distinguishable absent fragments + if let Some("") = scope.fragment() { + scope.set_fragment(None); + } + $store.insert(scope.to_string(), $value); + $scopes.push(scope); + }; +} + +fn collect_schemas(schema: &Value) -> SchemaStore { + let mut store = HashMap::new(); + let mut scopes = vec![scope_of(schema)]; + let mut stack = Vec::with_capacity(64); + stack.push((0_usize, schema)); + while let Some((scope_idx, value)) = stack.pop() { + match value { + Value::Object(object) => { + if let Some(id) = id_of_object(object) { + new_schema!(store, scopes, scope_idx, id, value); + push_map_unrolled!(stack, store, scopes, object, scopes.len() - 1); + } else { + push_map_unrolled!(stack, store, scopes, object, scope_idx); + } + } + Value::Array(array) => { + push_array_unrolled!(stack, store, scopes, array, scope_idx); + } + _ => {} + } + } + store +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::{json, Value}; + use test_case::test_case; + + fn default() -> Value { + json!({ + "allOf": [{ + "$ref": "#foo" + }], + "definitions": { + "A": { + "$id": "#foo", + "type": "integer" + } + } + }) + } + + fn absolute_uri() -> Value { + json!({ + "allOf": [{ + "$ref": "http://localhost:1234/bar#foo" + }], + "definitions": { + "A": { + "$id": "http://localhost:1234/bar#foo", + "type": "integer" + } + } + }) + } + + fn sub_schema_in_object() -> Value { + json!({ + "allOf": [{"$ref": "#foo"}], + "definitions": { + "A": {"$id": "#foo", "type": "integer"} + } + }) + } + + fn sub_schemas_in_array() -> Value { + json!({ + "definitions": { + "A": [ + {"$id": "#foo", "type": "integer"}, + {"$id": "#bar", "type": "string"}, + ] + } + }) + } + + fn root_schema_id() -> Value { + json!({ + "$id": "http://localhost:1234/tree", + "definitions": { + "node": { + "$id": "http://localhost:1234/node", + "description": "node", + "properties": { + "subtree": {"$ref": "tree"}, + "value": {"type": "number"} + }, + "required": ["value"], + "type": "object" + } + }, + "description": "tree of nodes", + "properties": { + "meta": {"type": "string"}, + "nodes": { + "items": {"$ref": "node"}, + "type": "array" + } + }, + "required": ["meta", "nodes"], + "type": "object" + }) + } + + fn base_uri_change_in_subschema() -> Value { + json!({ + "$id": "http://localhost:1234/root", + "allOf": [{ + "$ref": "http://localhost:1234/nested.json#foo" + }], + "definitions": { + "A": { + "$id": "nested.json", + "definitions": { + "B": { + "$id": "#foo", + "type": "integer" + } + } + } + } + }) + } + + fn base_uri_change() -> Value { + json!({ + "$id": "http://localhost:1234/", + "items": { + "$id":"folder/", + "items": {"$ref": "folderInteger.json"} + } + }) + } + + fn base_uri_change_folder() -> Value { + json!({ + "$id": "http://localhost:1234/scope_change_defs1.json", + "definitions": { + "baz": { + "$id": "folder/", + "items": {"$ref": "folderInteger.json"}, + "type":"array" + } + }, + "properties": { + "list": {"$ref": "#/definitions/baz"} + }, + "type": "object" + }) + } + + #[test_case(default(), &["json-schema:///#foo"], &["/definitions/A"])] + #[test_case(absolute_uri(), &["http://localhost:1234/bar#foo"], &["/definitions/A"])] + #[test_case( + sub_schema_in_object(), + &["json-schema:///#foo"], + &["/definitions/A"] + )] + #[test_case( + sub_schemas_in_array(), + &[ + "json-schema:///#foo", + "json-schema:///#bar", + ], + &[ + "/definitions/A/0", + "/definitions/A/1", + ] + )] + #[test_case( + root_schema_id(), + &[ + "http://localhost:1234/tree", + "http://localhost:1234/node", + ], + &[ + "", + "/definitions/node", + ] + )] + #[test_case( + base_uri_change_in_subschema(), + &[ + "http://localhost:1234/root", + "http://localhost:1234/nested.json", + "http://localhost:1234/nested.json#foo", + ], + &[ + "", + "/definitions/A", + "/definitions/A/definitions/B", + ] + )] + #[test_case( + base_uri_change(), + &[ + "http://localhost:1234/", + "http://localhost:1234/folder/" + ], + &[ + "", + "/items" + ] + )] + #[test_case( + base_uri_change_folder(), + &[ + "http://localhost:1234/scope_change_defs1.json", + "http://localhost:1234/folder/", + ], + &[ + "", + "/definitions/baz", + ] + )] + fn location_identifiers(schema: Value, ids: &[&str], pointers: &[&str]) { + let store = collect_schemas(&schema); + assert_eq!(store.len(), ids.len()); + for (id, pointer) in ids.into_iter().zip(pointers.into_iter()) { + assert_eq!(store[*id], schema.pointer(pointer).unwrap()); + } + } +} diff --git a/jsonschema-csr/src/lib.rs b/jsonschema-csr/src/lib.rs new file mode 100644 index 0000000..807c02d --- /dev/null +++ b/jsonschema-csr/src/lib.rs @@ -0,0 +1,3 @@ +mod compilation; + +pub use compilation::{build, resolver};