summaryrefslogtreecommitdiffstats
path: root/vendor/kstring
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/kstring')
-rw-r--r--vendor/kstring/.cargo-checksum.json1
-rw-r--r--vendor/kstring/Cargo.lock814
-rw-r--r--vendor/kstring/Cargo.toml88
-rw-r--r--vendor/kstring/LICENSE-APACHE201
-rw-r--r--vendor/kstring/LICENSE-MIT21
-rw-r--r--vendor/kstring/README.md58
-rw-r--r--vendor/kstring/benches/access.rs134
-rw-r--r--vendor/kstring/benches/clone.rs139
-rw-r--r--vendor/kstring/examples/bench.rs18
-rw-r--r--vendor/kstring/src/backend.rs97
-rw-r--r--vendor/kstring/src/lib.rs78
-rw-r--r--vendor/kstring/src/stack.rs457
-rw-r--r--vendor/kstring/src/string.rs865
-rw-r--r--vendor/kstring/src/string_cow.rs383
-rw-r--r--vendor/kstring/src/string_ref.rs277
15 files changed, 3631 insertions, 0 deletions
diff --git a/vendor/kstring/.cargo-checksum.json b/vendor/kstring/.cargo-checksum.json
new file mode 100644
index 000000000..322069c76
--- /dev/null
+++ b/vendor/kstring/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.lock":"bb3f1dfc847c9f47c89a5c465a9e91ed68c42695bf80ae147c6c1ea4d3de9928","Cargo.toml":"d6aa48214500ad0cf5de9c904609af1dd6ea2fe2068b405237f9f2576df3c600","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6a5dfb0adf37850239f4b2388a79355c77b625d6c5542dea7743ac5033efaba2","README.md":"dcf7c8593f78a8e5d6cdb520c4c4499b229bdfc93bedcdfb22434b0e0b311d4a","benches/access.rs":"356362c7d1d6517e2e495be2c9edee5130db5aa8dee02dfab053b1374e91326a","benches/clone.rs":"ce6430a1da50da96d7f94b7d9912cf28f28ba4a1baa976f62d6b211c5729184e","examples/bench.rs":"01056ee7b3bca2f9525636080ccdc5d8b513934e5c001eb0b8beb6214c2bac88","src/backend.rs":"6f3ec727cae210c04c720b1ed77de3d4b21713a920aa3ee620861eeadd91ae31","src/lib.rs":"9932b5d3ed4440e2d167b1fff1f835ef0eb51ad0a47a64bde38c0bf5aee60c6d","src/stack.rs":"029dce8e4385f91b2e375cd50a81812b44a93db656890cfe30b81f59ff9af425","src/string.rs":"bca947a52972f59ec7211d9a573110a8b24942c6c1144ea4abd6d272bc07adf0","src/string_cow.rs":"80db9235eee78bc81ee50df1f1fed1863791cb41aa0bae00af3a36b586f5513a","src/string_ref.rs":"79d050ce3bc7145b40aa3f555ea89a830cd84f3ef002a3081f874d5425430937"},"package":"ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747"} \ No newline at end of file
diff --git a/vendor/kstring/Cargo.lock b/vendor/kstring/Cargo.lock
new file mode 100644
index 000000000..206479e6d
--- /dev/null
+++ b/vendor/kstring/Cargo.lock
@@ -0,0 +1,814 @@
+# 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 = "bit-set"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[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 = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[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 = "document-features"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b01c09fd63b5136fba41aa625c7b3254f0aa0a435ff6ec4b2c9a28d496c83c88"
+
+[[package]]
+name = "either"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
+
+[[package]]
+name = "fastrand"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
+dependencies = [
+ "instant",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "getrandom"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[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 = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[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.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "kstring"
+version = "2.0.0"
+dependencies = [
+ "criterion",
+ "document-features",
+ "proptest",
+ "serde",
+ "static_assertions",
+]
+
+[[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.121"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
+
+[[package]]
+name = "log"
+version = "0.4.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "memchr"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+
+[[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.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
+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 = "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 = "ppv-lite86"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "proptest"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5"
+dependencies = [
+ "bit-set",
+ "bitflags",
+ "byteorder",
+ "lazy_static",
+ "num-traits",
+ "quick-error 2.0.1",
+ "rand",
+ "rand_chacha",
+ "rand_xorshift",
+ "regex-syntax",
+ "rusty-fork",
+ "tempfile",
+]
+
+[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
+[[package]]
+name = "quick-error"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
+
+[[package]]
+name = "quote"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_xorshift"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "rayon"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
+dependencies = [
+ "autocfg",
+ "crossbeam-deque",
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-utils",
+ "lazy_static",
+ "num_cpus",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ae183fc1b06c149f0c1793e1eb447c8b04bfe46d48e9e48bfb8d2d7ed64ecf0"
+dependencies = [
+ "bitflags",
+]
+
+[[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 = "remove_dir_all"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rusty-fork"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
+dependencies = [
+ "fnv",
+ "quick-error 1.2.3",
+ "tempfile",
+ "wait-timeout",
+]
+
+[[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.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4"
+
+[[package]]
+name = "serde"
+version = "1.0.136"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
+
+[[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.136"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
+dependencies = [
+ "itoa 1.0.1",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "syn"
+version = "1.0.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "libc",
+ "redox_syscall",
+ "remove_dir_all",
+ "winapi",
+]
+
+[[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 = "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.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
+
+[[package]]
+name = "wait-timeout"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
+dependencies = [
+ "libc",
+]
+
+[[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 = "wasi"
+version = "0.10.2+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca"
+dependencies = [
+ "bumpalo",
+ "lazy_static",
+ "log",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2"
+
+[[package]]
+name = "web-sys"
+version = "0.3.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb"
+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/vendor/kstring/Cargo.toml b/vendor/kstring/Cargo.toml
new file mode 100644
index 000000000..87e11ed79
--- /dev/null
+++ b/vendor/kstring/Cargo.toml
@@ -0,0 +1,88 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "kstring"
+version = "2.0.0"
+authors = ["Ed Page <eopage@gmail.com>"]
+include = ["build.rs", "src/**/*", "Cargo.toml", "LICENSE*", "README.md", "benches/**/*", "examples/**/*"]
+description = "Key String: optimized for map keys"
+documentation = "https://docs.rs/kstring"
+readme = "README.md"
+keywords = ["serde", "serialization", "string"]
+categories = ["data-structures", "text-processing"]
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/cobalt-org/kstring"
+[package.metadata.docs.rs]
+all-features = true
+[[package.metadata.release.pre-release-replacements]]
+file = "CHANGELOG.md"
+min = 1
+replace = "{{version}}"
+search = "Unreleased"
+
+[[package.metadata.release.pre-release-replacements]]
+exactly = 1
+file = "CHANGELOG.md"
+replace = "...{{tag_name}}"
+search = "\\.\\.\\.HEAD"
+
+[[package.metadata.release.pre-release-replacements]]
+file = "CHANGELOG.md"
+min = 1
+replace = "{{date}}"
+search = "ReleaseDate"
+
+[[package.metadata.release.pre-release-replacements]]
+exactly = 1
+file = "CHANGELOG.md"
+replace = "<!-- next-header -->\n## [Unreleased] - ReleaseDate\n"
+search = "<!-- next-header -->"
+
+[[package.metadata.release.pre-release-replacements]]
+exactly = 1
+file = "CHANGELOG.md"
+replace = "<!-- next-url -->\n[Unreleased]: https://github.com/cobalt-org/kstring/compare/{{tag_name}}...HEAD"
+search = "<!-- next-url -->"
+[profile.release]
+debug = 1
+
+[[bench]]
+name = "clone"
+harness = false
+
+[[bench]]
+name = "access"
+harness = false
+[dependencies.document-features]
+version = "0.2"
+optional = true
+
+[dependencies.serde]
+version = "1.0"
+optional = true
+
+[dependencies.static_assertions]
+version = "1.1.0"
+[dev-dependencies.criterion]
+version = "0.3"
+
+[dev-dependencies.proptest]
+version = "1.0.0"
+
+[features]
+arc = []
+default = ["std", "unsafe"]
+max_inline = []
+std = []
+unsafe = []
+unstable_bench_subset = []
diff --git a/vendor/kstring/LICENSE-APACHE b/vendor/kstring/LICENSE-APACHE
new file mode 100644
index 000000000..16fe87b06
--- /dev/null
+++ b/vendor/kstring/LICENSE-APACHE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/vendor/kstring/LICENSE-MIT b/vendor/kstring/LICENSE-MIT
new file mode 100644
index 000000000..f1be6b7af
--- /dev/null
+++ b/vendor/kstring/LICENSE-MIT
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-2016 Ning Sun and tojson_macros 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
+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.
diff --git a/vendor/kstring/README.md b/vendor/kstring/README.md
new file mode 100644
index 000000000..91828d01d
--- /dev/null
+++ b/vendor/kstring/README.md
@@ -0,0 +1,58 @@
+KString
+===========
+
+> Key String: Optimized for map keys.
+
+[![Crates Status](https://img.shields.io/crates/v/kstring.svg)](https://crates.io/crates/kstring)
+
+## Background
+
+Considerations:
+- Large maps
+- Most keys live and drop without being used in any other way
+- Most keys are relatively small (single to double digit bytes)
+- Keys are immutable
+- Allow zero-cost abstractions between structs and maps (e.g. no allocating
+ when dealing with struct field names)
+
+Ramifications:
+- Inline small strings rather than going to the heap.
+- Preserve `&'static str` across strings (`KString`),
+ references (`KStringRef`), and lifetime abstractions (`KStringCow`) to avoid
+ allocating for struct field names.
+- Use `Box<str>` rather than `String` to use less memory.
+
+Features
+- `max_inline`: Instead of aligning the inline-string for performance (15 bytes + length on 64-bit), use the full width (22 bytes on 64-bit)
+- `arc`: Instead of using `Box<str>`, use `Arc<str>`. Note: allocations are fast enough that this can actually slow things down for small enough strings.
+
+Alternatives
+- [`smol_str`](https://crates.io/crates/smol_str)
+ - Size of String
+ - Always uses `Arc` instead of `Box`
+ - Always inlines 22 bytes
+ - Whitespace-only optimizations
+- [`smartstring`](https://crates.io/crates/smartstring)
+ - Size of String
+ - Allows mutability at the cost of relying on implementation details of `String`
+ - Always inlines 23 bytes
+- [`compact_str`](https://crates.io/crates/compact_str)
+ - Size of String
+ - Always uses `Arc` instead of `Box`
+ - Inlines 22-23 bytes, depending on implementation
+
+## License
+
+Licensed under either of
+
+ * Apache License, Version 2.0, (http://www.apache.org/licenses/LICENSE-2.0)
+ * MIT license (http://opensource.org/licenses/MIT)
+
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally
+submitted for inclusion in the work by you, as defined in the Apache-2.0
+license, shall be dual licensed as above, without any additional terms or
+conditions.
diff --git a/vendor/kstring/benches/access.rs b/vendor/kstring/benches/access.rs
new file mode 100644
index 000000000..a9b92d3d6
--- /dev/null
+++ b/vendor/kstring/benches/access.rs
@@ -0,0 +1,134 @@
+#![allow(
+ clippy::clone_on_copy,
+ clippy::useless_conversion,
+ clippy::clone_double_ref
+)]
+
+use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
+
+type StringCow<'s> = std::borrow::Cow<'s, str>;
+
+#[cfg(not(feature = "unstable_bench_subset"))]
+pub static FIXTURES: &[&str] = &[
+ "",
+ "0",
+ "01",
+ "012",
+ "0123",
+ "01234",
+ "012345",
+ "0123456",
+ "01234567",
+ "012345678",
+ "0123456789",
+ "01234567890123456789",
+ "0123456789012345678901234567890123456789",
+ "01234567890123456789012345678901234567890123456789012345678901234567890123456789",
+];
+
+#[cfg(feature = "unstable_bench_subset")]
+pub static FIXTURES: &[&str] = &[
+ "0123456789",
+ "01234567890123456789012345678901234567890123456789012345678901234567890123456789",
+];
+
+// Note: this is meant to measure the overhead for accessing the underlying str. We shouldn't try
+// to optimize *just* the case being measured here.
+fn bench_access(c: &mut Criterion) {
+ let mut group = c.benchmark_group("access");
+ for fixture in FIXTURES {
+ let len = fixture.len();
+ group.throughput(Throughput::Bytes(len as u64));
+ group.bench_with_input(
+ BenchmarkId::new("StringCow::Borrowed", len),
+ &len,
+ |b, _| {
+ let uut = StringCow::Borrowed(*fixture);
+ let uut = criterion::black_box(uut);
+ b.iter(|| uut.is_empty())
+ },
+ );
+ group.bench_with_input(BenchmarkId::new("StringCow::Owned", len), &len, |b, _| {
+ let uut = StringCow::Owned(String::from(*fixture));
+ let uut = criterion::black_box(uut);
+ b.iter(|| uut.is_empty())
+ });
+ group.bench_with_input(
+ BenchmarkId::new("KString::from_static", len),
+ &len,
+ |b, _| {
+ let uut = kstring::KString::from_static(*fixture);
+ let uut = criterion::black_box(uut);
+ b.iter(|| uut.is_empty())
+ },
+ );
+ group.bench_with_input(BenchmarkId::new("KString::from_ref", len), &len, |b, _| {
+ let uut = kstring::KString::from_ref(*fixture);
+ let uut = criterion::black_box(uut);
+ b.iter(|| uut.is_empty())
+ });
+ group.bench_with_input(
+ BenchmarkId::new("KString::from_string", len),
+ &len,
+ |b, _| {
+ let uut = kstring::KString::from_string(String::from(*fixture));
+ let uut = criterion::black_box(uut);
+ b.iter(|| uut.is_empty())
+ },
+ );
+ #[cfg(not(feature = "unstable_bench_subset"))]
+ group.bench_with_input(
+ BenchmarkId::new("KStringCow::from_static", len),
+ &len,
+ |b, _| {
+ let uut = kstring::KStringCow::from_static(*fixture);
+ let uut = criterion::black_box(uut);
+ b.iter(|| uut.is_empty())
+ },
+ );
+ #[cfg(not(feature = "unstable_bench_subset"))]
+ group.bench_with_input(
+ BenchmarkId::new("KStringCow::from_ref", len),
+ &len,
+ |b, _| {
+ let uut = kstring::KStringCow::from_ref(*fixture);
+ let uut = criterion::black_box(uut);
+ b.iter(|| uut.is_empty())
+ },
+ );
+ #[cfg(not(feature = "unstable_bench_subset"))]
+ group.bench_with_input(
+ BenchmarkId::new("KStringCow::from_string", len),
+ &len,
+ |b, _| {
+ let uut = kstring::KStringCow::from_string(String::from(*fixture));
+ let uut = criterion::black_box(uut);
+ b.iter(|| uut.is_empty())
+ },
+ );
+ #[cfg(not(feature = "unstable_bench_subset"))]
+ group.bench_with_input(
+ BenchmarkId::new("KStringRef::from_static", len),
+ &len,
+ |b, _| {
+ let uut = kstring::KStringRef::from_static(*fixture);
+ let uut = criterion::black_box(uut);
+ b.iter(|| uut.is_empty())
+ },
+ );
+ #[cfg(not(feature = "unstable_bench_subset"))]
+ group.bench_with_input(
+ BenchmarkId::new("KStringRef::from_ref", len),
+ &len,
+ |b, _| {
+ let uut = kstring::KStringRef::from_ref(*fixture);
+ let uut = criterion::black_box(uut);
+ b.iter(|| uut.is_empty())
+ },
+ );
+ }
+ group.finish();
+}
+
+criterion_group!(benches, bench_access);
+criterion_main!(benches);
diff --git a/vendor/kstring/benches/clone.rs b/vendor/kstring/benches/clone.rs
new file mode 100644
index 000000000..b0740bad9
--- /dev/null
+++ b/vendor/kstring/benches/clone.rs
@@ -0,0 +1,139 @@
+#![allow(
+ clippy::clone_on_copy,
+ clippy::useless_conversion,
+ clippy::clone_double_ref
+)]
+
+use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
+
+type StringCow<'s> = std::borrow::Cow<'s, str>;
+
+#[cfg(not(feature = "unstable_bench_subset"))]
+pub static FIXTURES: &[&str] = &[
+ // Empty handling
+ "",
+ // Barely used
+ "1",
+ // kstring's max small-string size
+ "123456789012345",
+ // Boundary conditions for most small-string optimizations
+ "1234567890123456789012",
+ "12345678901234567890123",
+ "123456789012345678901234",
+ "1234567890123456789012345",
+ // Small heap
+ "1234567890123456789012345678901234567890123456789012345678901234",
+ // Large heap
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
+];
+
+#[cfg(feature = "unstable_bench_subset")]
+pub static FIXTURES: &[&str] = &[
+ "0123456789",
+ "01234567890123456789012345678901234567890123456789012345678901234567890123456789",
+];
+
+fn bench_clone(c: &mut Criterion) {
+ let mut group = c.benchmark_group("clone");
+ for fixture in FIXTURES {
+ let len = fixture.len();
+ group.throughput(Throughput::Bytes(len as u64));
+ group.bench_with_input(
+ BenchmarkId::new("StringCow::Borrowed", len),
+ &len,
+ |b, _| {
+ let uut = StringCow::Borrowed(*fixture);
+ let uut = criterion::black_box(uut);
+ b.iter(|| uut.clone())
+ },
+ );
+ group.bench_with_input(BenchmarkId::new("StringCow::Owned", len), &len, |b, _| {
+ let fixture = String::from(*fixture);
+ let uut = StringCow::Owned(fixture);
+ let uut = criterion::black_box(uut);
+ b.iter(|| uut.clone())
+ });
+ group.bench_with_input(
+ BenchmarkId::new("KString::from_static", len),
+ &len,
+ |b, _| {
+ let uut = kstring::KString::from_static(*fixture);
+ let uut = criterion::black_box(uut);
+ b.iter(|| uut.clone())
+ },
+ );
+ group.bench_with_input(BenchmarkId::new("KString::from_ref", len), &len, |b, _| {
+ let fixture = String::from(*fixture);
+ let uut = kstring::KString::from_ref(&fixture);
+ let uut = criterion::black_box(uut);
+ b.iter(|| uut.clone())
+ });
+ group.bench_with_input(
+ BenchmarkId::new("KString::from_string", len),
+ &len,
+ |b, _| {
+ let fixture = String::from(*fixture);
+ let uut = kstring::KString::from_string(fixture);
+ let uut = criterion::black_box(uut);
+ b.iter(|| uut.clone())
+ },
+ );
+ #[cfg(not(feature = "unstable_bench_subset"))]
+ group.bench_with_input(
+ BenchmarkId::new("KStringCow::from_static", len),
+ &len,
+ |b, _| {
+ let uut = kstring::KStringCow::from_static(*fixture);
+ let uut = criterion::black_box(uut);
+ b.iter(|| uut.clone())
+ },
+ );
+ #[cfg(not(feature = "unstable_bench_subset"))]
+ group.bench_with_input(
+ BenchmarkId::new("KStringCow::from_ref", len),
+ &len,
+ |b, _| {
+ let fixture = String::from(*fixture);
+ let uut = kstring::KStringCow::from_ref(&fixture);
+ let uut = criterion::black_box(uut);
+ b.iter(|| uut.clone())
+ },
+ );
+ #[cfg(not(feature = "unstable_bench_subset"))]
+ group.bench_with_input(
+ BenchmarkId::new("KStringCow::from_string", len),
+ &len,
+ |b, _| {
+ let fixture = String::from(*fixture);
+ let uut = kstring::KStringCow::from_string(fixture);
+ let uut = criterion::black_box(uut);
+ b.iter(|| uut.clone())
+ },
+ );
+ #[cfg(not(feature = "unstable_bench_subset"))]
+ group.bench_with_input(
+ BenchmarkId::new("KStringRef::from_static", len),
+ &len,
+ |b, _| {
+ let uut = kstring::KStringRef::from_static(*fixture);
+ let uut = criterion::black_box(uut);
+ b.iter(|| uut.clone())
+ },
+ );
+ #[cfg(not(feature = "unstable_bench_subset"))]
+ group.bench_with_input(
+ BenchmarkId::new("KStringRef::from_ref", len),
+ &len,
+ |b, _| {
+ let fixture = String::from(*fixture);
+ let uut = kstring::KStringRef::from_ref(&fixture);
+ let uut = criterion::black_box(uut);
+ b.iter(|| uut.clone())
+ },
+ );
+ }
+ group.finish();
+}
+
+criterion_group!(benches, bench_clone);
+criterion_main!(benches);
diff --git a/vendor/kstring/examples/bench.rs b/vendor/kstring/examples/bench.rs
new file mode 100644
index 000000000..8d467da52
--- /dev/null
+++ b/vendor/kstring/examples/bench.rs
@@ -0,0 +1,18 @@
+fn main() {
+ let mut args = std::env::args();
+ let _ = args.next();
+ let method = args.next().unwrap_or_else(|| String::from("from_ref"));
+ let sample = args.next().unwrap_or_else(|| String::from("0123456789"));
+ let count = args
+ .next()
+ .unwrap_or_else(|| String::from("10000000"))
+ .parse::<usize>()
+ .unwrap();
+ #[allow(clippy::redundant_closure)] // Needed for consistent type
+ let method = match method.as_str() {
+ "from_ref" => |s| kstring::KString::from_ref(s),
+ "from_string" => |s| kstring::KString::from_string(String::from(s)),
+ _ => panic!("{:?} unsupported, try `from_ref`, `from_string`", method),
+ };
+ (0..count).map(|_| method(&sample)).last();
+}
diff --git a/vendor/kstring/src/backend.rs b/vendor/kstring/src/backend.rs
new file mode 100644
index 000000000..3827082f1
--- /dev/null
+++ b/vendor/kstring/src/backend.rs
@@ -0,0 +1,97 @@
+#[cfg(feature = "arc")]
+pub(crate) type DefaultStr = crate::backend::ArcStr;
+#[cfg(not(feature = "arc"))]
+pub(crate) type DefaultStr = crate::backend::BoxedStr;
+
+/// Fast allocations, O(n) clones
+pub type BoxedStr = Box<str>;
+static_assertions::assert_eq_size!(DefaultStr, BoxedStr);
+
+/// Cross-thread, O(1) clones
+pub type ArcStr = std::sync::Arc<str>;
+static_assertions::assert_eq_size!(DefaultStr, ArcStr);
+
+/// O(1) clones
+pub type RcStr = std::rc::Rc<str>;
+static_assertions::assert_eq_size!(DefaultStr, RcStr);
+
+/// Abstract over different type of heap-allocated strings
+pub trait HeapStr: std::fmt::Debug + Clone + private::Sealed {
+ fn from_str(other: &str) -> Self;
+ fn from_string(other: String) -> Self;
+ fn from_boxed_str(other: BoxedStr) -> Self;
+ fn as_str(&self) -> &str;
+}
+
+impl HeapStr for BoxedStr {
+ #[inline]
+ fn from_str(other: &str) -> Self {
+ other.into()
+ }
+
+ #[inline]
+ fn from_string(other: String) -> Self {
+ other.into_boxed_str()
+ }
+
+ #[inline]
+ fn from_boxed_str(other: BoxedStr) -> Self {
+ other
+ }
+
+ #[inline]
+ fn as_str(&self) -> &str {
+ self
+ }
+}
+
+impl HeapStr for ArcStr {
+ #[inline]
+ fn from_str(other: &str) -> Self {
+ other.into()
+ }
+
+ #[inline]
+ fn from_string(other: String) -> Self {
+ other.into_boxed_str().into()
+ }
+
+ #[inline]
+ fn from_boxed_str(other: BoxedStr) -> Self {
+ other.into()
+ }
+
+ #[inline]
+ fn as_str(&self) -> &str {
+ self
+ }
+}
+
+impl HeapStr for RcStr {
+ #[inline]
+ fn from_str(other: &str) -> Self {
+ other.into()
+ }
+
+ #[inline]
+ fn from_string(other: String) -> Self {
+ other.into_boxed_str().into()
+ }
+
+ #[inline]
+ fn from_boxed_str(other: BoxedStr) -> Self {
+ other.into()
+ }
+
+ #[inline]
+ fn as_str(&self) -> &str {
+ self
+ }
+}
+
+pub(crate) mod private {
+ pub trait Sealed {}
+ impl Sealed for super::BoxedStr {}
+ impl Sealed for super::ArcStr {}
+ impl Sealed for super::RcStr {}
+}
diff --git a/vendor/kstring/src/lib.rs b/vendor/kstring/src/lib.rs
new file mode 100644
index 000000000..dc1ffd608
--- /dev/null
+++ b/vendor/kstring/src/lib.rs
@@ -0,0 +1,78 @@
+//! Key String: Optimized for map keys.
+//!
+//! # Examples
+//!
+//! String creation
+//! ```rust
+//! // Explicit
+//! let literal = kstring::KString::from_static("literal");
+//! // Implicit
+//! let literal = kstring::KString::from("literal");
+//!
+//! // Explicit
+//! let inline = kstring::KString::try_inline("stack").unwrap();
+//! let inline = kstring::KString::from_ref("stack");
+//!
+//! let formatted: kstring::KStringCow = format!("Hello {} and {}", literal, inline).into();
+//! ```
+//!
+//! # Background
+//!
+//! Considerations:
+//! - Large maps
+//! - Most keys live and drop without being used in any other way
+//! - Most keys are relatively small (single to double digit bytes)
+//! - Keys are immutable
+//! - Allow zero-cost abstractions between structs and maps (e.g. no allocating
+//! when dealing with struct field names)
+//!
+//! Ramifications:
+//! - Inline small strings rather than going to the heap.
+//! - Preserve `&'static str` across strings ([`KString`]),
+//! references ([`KStringRef`]), and lifetime abstractions ([`KStringCow`]) to avoid
+//! allocating for struct field names.
+//! - Use `Box<str>` rather than `String` to use less memory.
+//!
+//! # Feature Flags
+//!
+#![cfg_attr(feature = "document-features", doc = document_features::document_features!())]
+#![cfg_attr(feature = "safe", forbid(unsafe_code))]
+
+#[cfg(not(feature = "std"))]
+compile_error!("`std` feature is required; reserved for future `no_std` support");
+
+mod stack;
+mod string;
+mod string_cow;
+mod string_ref;
+
+pub mod backend;
+
+pub use stack::StackString;
+pub use string::*;
+pub use string_cow::*;
+pub use string_ref::*;
+
+#[cfg(test)]
+mod test {
+ #[test]
+ fn test_size() {
+ println!(
+ "String: {}",
+ std::mem::size_of::<crate::string::StdString>()
+ );
+ println!(
+ "Box<str>: {}",
+ std::mem::size_of::<crate::backend::DefaultStr>()
+ );
+ println!(
+ "Box<Box<str>>: {}",
+ std::mem::size_of::<Box<crate::backend::DefaultStr>>()
+ );
+ println!("str: {}", std::mem::size_of::<&'static str>());
+ println!(
+ "Cow: {}",
+ std::mem::size_of::<std::borrow::Cow<'static, str>>()
+ );
+ }
+}
diff --git a/vendor/kstring/src/stack.rs b/vendor/kstring/src/stack.rs
new file mode 100644
index 000000000..93e2f0722
--- /dev/null
+++ b/vendor/kstring/src/stack.rs
@@ -0,0 +1,457 @@
+use std::fmt;
+
+pub(crate) type Len = u8;
+
+/// Fixed-size stack-allocated string
+#[derive(Copy, Clone)]
+pub struct StackString<const CAPACITY: usize> {
+ len: Len,
+ buffer: StrBuffer<CAPACITY>,
+}
+
+impl<const CAPACITY: usize> StackString<CAPACITY> {
+ pub const CAPACITY: usize = CAPACITY;
+ pub const EMPTY: Self = Self::empty();
+
+ const fn empty() -> Self {
+ Self {
+ len: 0,
+ buffer: StrBuffer::empty(),
+ }
+ }
+
+ /// Create a `StackString` from a `&str`, if it'll fit within `Self::CAPACITY`
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// let s = kstring::StackString::<3>::try_new("foo");
+ /// assert_eq!(s.as_deref(), Some("foo"));
+ /// let s = kstring::StackString::<3>::try_new("foobar");
+ /// assert_eq!(s, None);
+ /// ```
+ #[inline]
+ #[must_use]
+ pub fn try_new(s: &str) -> Option<Self> {
+ let len = s.as_bytes().len();
+ if len <= Self::CAPACITY {
+ #[cfg(feature = "unsafe")]
+ let stack = {
+ unsafe {
+ // SAFETY: We've confirmed `len` is within size
+ Self::new_unchecked(s)
+ }
+ };
+ #[cfg(not(feature = "unsafe"))]
+ let stack = { Self::new(s) };
+ Some(stack)
+ } else {
+ None
+ }
+ }
+
+ /// Create a `StackString` from a `&str`
+ ///
+ /// # Panic
+ ///
+ /// Calling this function with a string larger than `Self::CAPACITY` will panic
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// let s = kstring::StackString::<3>::new("foo");
+ /// assert_eq!(s, "foo");
+ /// ```
+ #[inline]
+ #[must_use]
+ pub fn new(s: &str) -> Self {
+ let len = s.as_bytes().len() as u8;
+ debug_assert!(Self::CAPACITY <= Len::MAX.into());
+ let buffer = StrBuffer::new(s);
+ Self { len, buffer }
+ }
+
+ /// Create a `StackString` from a `&str`
+ ///
+ /// # Safety
+ ///
+ /// Calling this function with a string larger than `Self::CAPACITY` is undefined behavior.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// let s = unsafe {
+ /// // SAFETY: Literal is short-enough
+ /// kstring::StackString::<3>::new_unchecked("foo")
+ /// };
+ /// assert_eq!(s, "foo");
+ /// ```
+ #[inline]
+ #[must_use]
+ #[cfg(feature = "unsafe")]
+ pub unsafe fn new_unchecked(s: &str) -> Self {
+ let len = s.as_bytes().len() as u8;
+ debug_assert!(Self::CAPACITY <= Len::MAX.into());
+ let buffer = StrBuffer::new_unchecked(s);
+ Self { len, buffer }
+ }
+
+ /// Extracts a string slice containing the entire `StackString`.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// let s = kstring::StackString::<3>::try_new("foo").unwrap();
+ ///
+ /// assert_eq!("foo", s.as_str());
+ /// ```
+ #[inline]
+ #[must_use]
+ pub fn as_str(&self) -> &str {
+ let len = self.len as usize;
+ #[cfg(feature = "unsafe")]
+ unsafe {
+ // SAFETY: Constructors guarantee that `buffer[..len]` is a `str`,
+ // and we don't mutate the data afterwards.
+ self.buffer.as_str_unchecked(len)
+ }
+ #[cfg(not(feature = "unsafe"))]
+ self.buffer.as_str(len)
+ }
+
+ /// Converts a `StackString` into a mutable string slice.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// let mut s = kstring::StackString::<6>::try_new("foobar").unwrap();
+ /// let s_mut_str = s.as_mut_str();
+ ///
+ /// s_mut_str.make_ascii_uppercase();
+ ///
+ /// assert_eq!("FOOBAR", s_mut_str);
+ /// ```
+ #[inline]
+ #[must_use]
+ pub fn as_mut_str(&mut self) -> &mut str {
+ let len = self.len as usize;
+ #[cfg(feature = "unsafe")]
+ unsafe {
+ // SAFETY: Constructors guarantee that `buffer[..len]` is a `str`,
+ // and we don't mutate the data afterwards.
+ self.buffer.as_mut_str_unchecked(len)
+ }
+ #[cfg(not(feature = "unsafe"))]
+ self.buffer.as_mut_str(len)
+ }
+
+ /// Returns the length of this `StasckString`, in bytes, not [`char`]s or
+ /// graphemes. In other words, it might not be what a human considers the
+ /// length of the string.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// let a = kstring::StackString::<3>::try_new("foo").unwrap();
+ /// assert_eq!(a.len(), 3);
+ ///
+ /// let fancy_f = kstring::StackString::<4>::try_new("Æ’oo").unwrap();
+ /// assert_eq!(fancy_f.len(), 4);
+ /// assert_eq!(fancy_f.chars().count(), 3);
+ /// ```
+ #[inline]
+ #[must_use]
+ pub fn len(&self) -> usize {
+ self.len as usize
+ }
+
+ /// Returns `true` if this `StackString` has a length of zero, and `false` otherwise.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// let mut v = kstring::StackString::<20>::EMPTY;
+ /// assert!(v.is_empty());
+ ///
+ /// let a = kstring::StackString::<3>::try_new("foo").unwrap();
+ /// assert!(!a.is_empty());
+ /// ```
+ #[inline]
+ #[must_use]
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
+ /// Truncates this `StackString`, removing all contents.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// let mut s = kstring::StackString::<3>::try_new("foo").unwrap();
+ ///
+ /// s.clear();
+ ///
+ /// assert!(s.is_empty());
+ /// assert_eq!(0, s.len());
+ /// ```
+ #[inline]
+ pub fn clear(&mut self) {
+ self.len = 0;
+ }
+
+ /// Shortens this `StackString` to the specified length.
+ ///
+ /// If `new_len` is greater than the string's current length, this has no
+ /// effect.
+ ///
+ /// Note that this method has no effect on the allocated capacity
+ /// of the string
+ ///
+ /// # Panics
+ ///
+ /// Panics if `new_len` does not lie on a [`char`] boundary.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// let mut s = kstring::StackString::<5>::try_new("hello").unwrap();
+ ///
+ /// s.truncate(2);
+ ///
+ /// assert_eq!(s, "he");
+ /// ```
+ #[inline]
+ pub fn truncate(&mut self, new_len: usize) {
+ if new_len <= self.len() {
+ assert!(self.is_char_boundary(new_len));
+ self.len = new_len as u8;
+ }
+ }
+}
+
+impl<const CAPACITY: usize> Default for StackString<CAPACITY> {
+ fn default() -> Self {
+ Self::empty()
+ }
+}
+
+impl<const CAPACITY: usize> std::ops::Deref for StackString<CAPACITY> {
+ type Target = str;
+
+ #[inline]
+ fn deref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl<const CAPACITY: usize> Eq for StackString<CAPACITY> {}
+
+impl<const C1: usize, const C2: usize> PartialEq<StackString<C1>> for StackString<C2> {
+ #[inline]
+ fn eq(&self, other: &StackString<C1>) -> bool {
+ PartialEq::eq(self.as_str(), other.as_str())
+ }
+}
+
+impl<const CAPACITY: usize> PartialEq<str> for StackString<CAPACITY> {
+ #[inline]
+ fn eq(&self, other: &str) -> bool {
+ PartialEq::eq(self.as_str(), other)
+ }
+}
+
+impl<'s, const CAPACITY: usize> PartialEq<&'s str> for StackString<CAPACITY> {
+ #[inline]
+ fn eq(&self, other: &&str) -> bool {
+ PartialEq::eq(self.as_str(), *other)
+ }
+}
+
+impl<const CAPACITY: usize> PartialEq<String> for StackString<CAPACITY> {
+ #[inline]
+ fn eq(&self, other: &String) -> bool {
+ PartialEq::eq(self.as_str(), other.as_str())
+ }
+}
+
+impl<const CAPACITY: usize> Ord for StackString<CAPACITY> {
+ #[inline]
+ fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+ self.as_str().cmp(other.as_str())
+ }
+}
+
+impl<const C1: usize, const C2: usize> PartialOrd<StackString<C1>> for StackString<C2> {
+ #[inline]
+ fn partial_cmp(&self, other: &StackString<C1>) -> Option<std::cmp::Ordering> {
+ self.as_str().partial_cmp(other.as_str())
+ }
+}
+
+impl<const CAPACITY: usize> PartialOrd<str> for StackString<CAPACITY> {
+ #[inline]
+ fn partial_cmp(&self, other: &str) -> Option<std::cmp::Ordering> {
+ self.as_str().partial_cmp(other)
+ }
+}
+
+impl<'s, const CAPACITY: usize> PartialOrd<&'s str> for StackString<CAPACITY> {
+ #[inline]
+ fn partial_cmp(&self, other: &&str) -> Option<std::cmp::Ordering> {
+ self.as_str().partial_cmp(other)
+ }
+}
+
+impl<const CAPACITY: usize> PartialOrd<String> for StackString<CAPACITY> {
+ #[inline]
+ fn partial_cmp(&self, other: &String) -> Option<std::cmp::Ordering> {
+ self.as_str().partial_cmp(other.as_str())
+ }
+}
+
+impl<const CAPACITY: usize> std::hash::Hash for StackString<CAPACITY> {
+ #[inline]
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ self.as_str().hash(state);
+ }
+}
+
+impl<const CAPACITY: usize> fmt::Debug for StackString<CAPACITY> {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(self.as_str(), f)
+ }
+}
+
+impl<const CAPACITY: usize> fmt::Display for StackString<CAPACITY> {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(self.as_str(), f)
+ }
+}
+
+impl<const CAPACITY: usize> AsRef<str> for StackString<CAPACITY> {
+ #[inline]
+ fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl<const CAPACITY: usize> AsRef<[u8]> for StackString<CAPACITY> {
+ #[inline]
+ fn as_ref(&self) -> &[u8] {
+ self.as_bytes()
+ }
+}
+
+impl<const CAPACITY: usize> AsRef<std::ffi::OsStr> for StackString<CAPACITY> {
+ #[inline]
+ fn as_ref(&self) -> &std::ffi::OsStr {
+ (&**self).as_ref()
+ }
+}
+
+impl<const CAPACITY: usize> AsRef<std::path::Path> for StackString<CAPACITY> {
+ #[inline]
+ fn as_ref(&self) -> &std::path::Path {
+ std::path::Path::new(self)
+ }
+}
+
+impl<const CAPACITY: usize> std::borrow::Borrow<str> for StackString<CAPACITY> {
+ #[inline]
+ fn borrow(&self) -> &str {
+ self.as_str()
+ }
+}
+
+#[derive(Copy, Clone)]
+#[repr(transparent)]
+pub(crate) struct StrBuffer<const CAPACITY: usize>([u8; CAPACITY]);
+
+impl<const CAPACITY: usize> StrBuffer<CAPACITY> {
+ pub(crate) const fn empty() -> Self {
+ let array = [0; CAPACITY];
+ StrBuffer(array)
+ }
+
+ #[inline]
+ pub(crate) fn new(s: &str) -> Self {
+ let len = s.as_bytes().len();
+ debug_assert!(len <= CAPACITY);
+ let mut buffer = Self::default();
+ if let Some(buffer) = buffer.0.get_mut(..len) {
+ buffer.copy_from_slice(s.as_bytes());
+ } else {
+ panic!("`{}` is larger than capacity {}", s, CAPACITY);
+ }
+ buffer
+ }
+
+ #[inline]
+ #[cfg(not(feature = "unsafe"))]
+ pub(crate) fn as_str(&self, len: usize) -> &str {
+ let slice = self.0.get(..len).unwrap();
+ std::str::from_utf8(slice).unwrap()
+ }
+
+ #[inline]
+ #[cfg(not(feature = "unsafe"))]
+ pub(crate) fn as_mut_str(&mut self, len: usize) -> &mut str {
+ let slice = self.0.get_mut(..len).unwrap();
+ std::str::from_utf8_mut(slice).unwrap()
+ }
+}
+
+impl<const CAPACITY: usize> StrBuffer<CAPACITY> {
+ #[inline]
+ #[cfg(feature = "unsafe")]
+ pub(crate) unsafe fn new_unchecked(s: &str) -> Self {
+ let len = s.as_bytes().len();
+ debug_assert!(len <= CAPACITY);
+ let mut buffer = Self::default();
+ buffer
+ .0
+ .get_unchecked_mut(..len)
+ .copy_from_slice(s.as_bytes());
+ buffer
+ }
+
+ #[inline]
+ #[cfg(feature = "unsafe")]
+ pub(crate) unsafe fn as_str_unchecked(&self, len: usize) -> &str {
+ let slice = self.0.get_unchecked(..len);
+ std::str::from_utf8_unchecked(slice)
+ }
+
+ #[inline]
+ #[cfg(feature = "unsafe")]
+ pub(crate) unsafe fn as_mut_str_unchecked(&mut self, len: usize) -> &mut str {
+ let slice = self.0.get_unchecked_mut(..len);
+ std::str::from_utf8_unchecked_mut(slice)
+ }
+}
+
+impl<const CAPACITY: usize> Default for StrBuffer<CAPACITY> {
+ fn default() -> Self {
+ Self::empty()
+ }
+}
diff --git a/vendor/kstring/src/string.rs b/vendor/kstring/src/string.rs
new file mode 100644
index 000000000..ed39d188a
--- /dev/null
+++ b/vendor/kstring/src/string.rs
@@ -0,0 +1,865 @@
+use std::{borrow::Cow, fmt};
+
+use crate::stack::StackString;
+use crate::KStringCowBase;
+use crate::KStringRef;
+
+pub(crate) type StdString = std::string::String;
+
+/// A UTF-8 encoded, immutable string.
+pub type KString = KStringBase<crate::backend::DefaultStr>;
+
+/// A UTF-8 encoded, immutable string.
+#[derive(Clone)]
+#[repr(transparent)]
+pub struct KStringBase<B> {
+ inner: KStringInner<B>,
+}
+
+impl<B> KStringBase<B> {
+ pub const EMPTY: Self = KStringBase::from_static("");
+
+ /// Create a new empty `KStringBase`.
+ #[inline]
+ #[must_use]
+ pub fn new() -> Self {
+ Self::EMPTY
+ }
+
+ /// Create a reference to a `'static` data.
+ #[inline]
+ #[must_use]
+ pub const fn from_static(other: &'static str) -> Self {
+ Self {
+ inner: KStringInner::from_static(other),
+ }
+ }
+
+ /// Create an inline string, if possible
+ #[inline]
+ #[must_use]
+ pub fn try_inline(other: &str) -> Option<Self> {
+ KStringInner::try_inline(other).map(|inner| Self { inner })
+ }
+}
+
+impl<B: crate::backend::HeapStr> KStringBase<B> {
+ /// Create an owned `KStringBase`.
+ #[inline]
+ #[must_use]
+ pub fn from_boxed(other: crate::backend::BoxedStr) -> Self {
+ Self {
+ inner: KStringInner::from_boxed(other),
+ }
+ }
+
+ /// Create an owned `KStringBase`.
+ #[inline]
+ #[must_use]
+ pub fn from_string(other: StdString) -> Self {
+ Self {
+ inner: KStringInner::from_string(other),
+ }
+ }
+
+ /// Create an owned `KStringBase` optimally from a reference.
+ #[inline]
+ #[must_use]
+ pub fn from_ref(other: &str) -> Self {
+ Self {
+ inner: KStringInner::from_ref(other),
+ }
+ }
+
+ /// Get a reference to the `KStringBase`.
+ #[inline]
+ #[must_use]
+ pub fn as_ref(&self) -> KStringRef<'_> {
+ self.inner.as_ref()
+ }
+
+ /// Extracts a string slice containing the entire `KStringBase`.
+ #[inline]
+ #[must_use]
+ pub fn as_str(&self) -> &str {
+ self.inner.as_str()
+ }
+
+ /// Convert to a mutable string type, cloning the data if necessary.
+ #[inline]
+ #[must_use]
+ pub fn into_string(self) -> StdString {
+ String::from(self.into_boxed_str())
+ }
+
+ /// Convert to a mutable string type, cloning the data if necessary.
+ #[inline]
+ #[must_use]
+ pub fn into_boxed_str(self) -> crate::backend::BoxedStr {
+ self.inner.into_boxed_str()
+ }
+
+ /// Convert to a Cow str
+ #[inline]
+ #[must_use]
+ pub fn into_cow_str(self) -> Cow<'static, str> {
+ self.inner.into_cow_str()
+ }
+}
+
+impl<B: crate::backend::HeapStr> std::ops::Deref for KStringBase<B> {
+ type Target = str;
+
+ #[inline]
+ fn deref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl<B: crate::backend::HeapStr> Eq for KStringBase<B> {}
+
+impl<'s, B: crate::backend::HeapStr> PartialEq<KStringBase<B>> for KStringBase<B> {
+ #[inline]
+ fn eq(&self, other: &Self) -> bool {
+ PartialEq::eq(self.as_str(), other.as_str())
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> PartialEq<str> for KStringBase<B> {
+ #[inline]
+ fn eq(&self, other: &str) -> bool {
+ PartialEq::eq(self.as_str(), other)
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> PartialEq<&'s str> for KStringBase<B> {
+ #[inline]
+ fn eq(&self, other: &&str) -> bool {
+ PartialEq::eq(self.as_str(), *other)
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> PartialEq<String> for KStringBase<B> {
+ #[inline]
+ fn eq(&self, other: &StdString) -> bool {
+ PartialEq::eq(self.as_str(), other.as_str())
+ }
+}
+
+impl<B: crate::backend::HeapStr> Ord for KStringBase<B> {
+ #[inline]
+ fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+ self.as_str().cmp(other.as_str())
+ }
+}
+
+impl<B: crate::backend::HeapStr> PartialOrd for KStringBase<B> {
+ #[inline]
+ fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+ self.as_str().partial_cmp(other.as_str())
+ }
+}
+
+impl<B: crate::backend::HeapStr> std::hash::Hash for KStringBase<B> {
+ #[inline]
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ self.as_str().hash(state);
+ }
+}
+
+impl<B: crate::backend::HeapStr> fmt::Debug for KStringBase<B> {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.as_str().fmt(f)
+ }
+}
+
+impl<B: crate::backend::HeapStr> fmt::Display for KStringBase<B> {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(self.as_str(), f)
+ }
+}
+
+impl<B: crate::backend::HeapStr> AsRef<str> for KStringBase<B> {
+ #[inline]
+ fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl<B: crate::backend::HeapStr> AsRef<[u8]> for KStringBase<B> {
+ #[inline]
+ fn as_ref(&self) -> &[u8] {
+ self.as_bytes()
+ }
+}
+
+impl<B: crate::backend::HeapStr> AsRef<std::ffi::OsStr> for KStringBase<B> {
+ #[inline]
+ fn as_ref(&self) -> &std::ffi::OsStr {
+ (&**self).as_ref()
+ }
+}
+
+impl<B: crate::backend::HeapStr> AsRef<std::path::Path> for KStringBase<B> {
+ #[inline]
+ fn as_ref(&self) -> &std::path::Path {
+ std::path::Path::new(self)
+ }
+}
+
+impl<B: crate::backend::HeapStr> std::borrow::Borrow<str> for KStringBase<B> {
+ #[inline]
+ fn borrow(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl<B: crate::backend::HeapStr> Default for KStringBase<B> {
+ #[inline]
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> From<KStringRef<'s>> for KStringBase<B> {
+ #[inline]
+ fn from(other: KStringRef<'s>) -> Self {
+ other.to_owned()
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> From<&'s KStringRef<'s>> for KStringBase<B> {
+ #[inline]
+ fn from(other: &'s KStringRef<'s>) -> Self {
+ other.to_owned()
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> From<KStringCowBase<'s, B>> for KStringBase<B> {
+ #[inline]
+ fn from(other: KStringCowBase<'s, B>) -> Self {
+ other.into_owned()
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> From<&'s KStringCowBase<'s, B>> for KStringBase<B> {
+ #[inline]
+ fn from(other: &'s KStringCowBase<'s, B>) -> Self {
+ other.clone().into_owned()
+ }
+}
+
+impl<B: crate::backend::HeapStr> From<StdString> for KStringBase<B> {
+ #[inline]
+ fn from(other: StdString) -> Self {
+ Self::from_string(other)
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> From<&'s StdString> for KStringBase<B> {
+ #[inline]
+ fn from(other: &'s StdString) -> Self {
+ Self::from_ref(other)
+ }
+}
+
+impl<B: crate::backend::HeapStr> From<crate::backend::BoxedStr> for KStringBase<B> {
+ #[inline]
+ fn from(other: crate::backend::BoxedStr) -> Self {
+ Self::from_boxed(other)
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> From<&'s crate::backend::BoxedStr> for KStringBase<B> {
+ #[inline]
+ fn from(other: &'s crate::backend::BoxedStr) -> Self {
+ Self::from_ref(other)
+ }
+}
+
+impl<B: crate::backend::HeapStr> From<&'static str> for KStringBase<B> {
+ #[inline]
+ fn from(other: &'static str) -> Self {
+ Self::from_static(other)
+ }
+}
+
+impl<B: crate::backend::HeapStr> std::str::FromStr for KStringBase<B> {
+ type Err = std::convert::Infallible;
+ #[inline]
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ Ok(Self::from_ref(s))
+ }
+}
+
+#[cfg(feature = "serde")]
+impl<B: crate::backend::HeapStr> serde::Serialize for KStringBase<B> {
+ #[inline]
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ serializer.serialize_str(self.as_str())
+ }
+}
+
+#[cfg(feature = "serde")]
+impl<'de, B: crate::backend::HeapStr> serde::Deserialize<'de> for KStringBase<B> {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ deserializer.deserialize_string(StringVisitor(std::marker::PhantomData))
+ }
+}
+
+#[cfg(feature = "serde")]
+struct StringVisitor<B>(std::marker::PhantomData<B>);
+
+#[cfg(feature = "serde")]
+impl<'de, B: crate::backend::HeapStr> serde::de::Visitor<'de> for StringVisitor<B> {
+ type Value = KStringBase<B>;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ formatter.write_str("a string")
+ }
+
+ fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ Ok(Self::Value::from_ref(v))
+ }
+
+ fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ Ok(Self::Value::from_string(v))
+ }
+
+ fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ match std::str::from_utf8(v) {
+ Ok(s) => Ok(Self::Value::from_ref(s)),
+ Err(_) => Err(serde::de::Error::invalid_value(
+ serde::de::Unexpected::Bytes(v),
+ &self,
+ )),
+ }
+ }
+
+ fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ match String::from_utf8(v) {
+ Ok(s) => Ok(Self::Value::from_string(s)),
+ Err(e) => Err(serde::de::Error::invalid_value(
+ serde::de::Unexpected::Bytes(&e.into_bytes()),
+ &self,
+ )),
+ }
+ }
+}
+
+use inner::KStringInner;
+
+#[cfg(not(feature = "unsafe"))]
+mod inner {
+ use super::*;
+
+ pub(super) enum KStringInner<B> {
+ Singleton(&'static str),
+ Inline(StackString<CAPACITY>),
+ Owned(B),
+ }
+
+ impl<B> KStringInner<B> {
+ /// Create a reference to a `'static` data.
+ #[inline]
+ pub const fn from_static(other: &'static str) -> Self {
+ Self::Singleton(other)
+ }
+
+ #[inline]
+ pub fn try_inline(other: &str) -> Option<Self> {
+ StackString::try_new(other).map(Self::Inline)
+ }
+ }
+
+ impl<B: crate::backend::HeapStr> KStringInner<B> {
+ #[inline]
+ pub(super) fn from_boxed(other: crate::backend::BoxedStr) -> Self {
+ #[allow(clippy::useless_conversion)]
+ Self::Owned(B::from_boxed_str(other))
+ }
+
+ #[inline]
+ pub(super) fn from_string(other: StdString) -> Self {
+ if (0..=CAPACITY).contains(&other.len()) {
+ let inline = { StackString::new(other.as_str()) };
+ Self::Inline(inline)
+ } else {
+ Self::from_boxed(other.into_boxed_str())
+ }
+ }
+
+ #[inline]
+ pub(super) fn from_ref(other: &str) -> Self {
+ if (0..=CAPACITY).contains(&other.len()) {
+ let inline = { StackString::new(other) };
+ Self::Inline(inline)
+ } else {
+ Self::Owned(B::from_str(other))
+ }
+ }
+
+ #[inline]
+ pub(super) fn as_ref(&self) -> KStringRef<'_> {
+ match self {
+ Self::Singleton(s) => KStringRef::from_static(s),
+ Self::Inline(s) => KStringRef::from_ref(s.as_str()),
+ Self::Owned(s) => KStringRef::from_ref(s.as_str()),
+ }
+ }
+
+ #[inline]
+ pub(super) fn as_str(&self) -> &str {
+ match self {
+ Self::Singleton(s) => s,
+ Self::Inline(s) => s.as_str(),
+ Self::Owned(s) => s.as_str(),
+ }
+ }
+
+ #[inline]
+ pub(super) fn into_boxed_str(self) -> crate::backend::BoxedStr {
+ match self {
+ Self::Singleton(s) => crate::backend::BoxedStr::from(s),
+ Self::Inline(s) => crate::backend::BoxedStr::from(s.as_str()),
+ Self::Owned(s) => crate::backend::BoxedStr::from(s.as_str()),
+ }
+ }
+
+ /// Convert to a Cow str
+ #[inline]
+ pub(super) fn into_cow_str(self) -> Cow<'static, str> {
+ match self {
+ Self::Singleton(s) => Cow::Borrowed(s),
+ Self::Inline(s) => Cow::Owned(s.as_str().into()),
+ Self::Owned(s) => Cow::Owned(s.as_str().into()),
+ }
+ }
+ }
+
+ // Explicit to avoid inlining which cuts clone times in half.
+ //
+ // An automatically derived `clone()` has 10ns overhead while the explicit `Deref`/`as_str` has
+ // none of that. Being explicit and removing the `#[inline]` attribute dropped the overhead to
+ // 5ns.
+ //
+ // My only guess is that the `clone()` calls we delegate to are just that much bigger than
+ // `as_str()` that, when combined with a jump table, is blowing the icache, slowing things down.
+ impl<B: Clone> Clone for KStringInner<B> {
+ fn clone(&self) -> Self {
+ match self {
+ Self::Singleton(s) => Self::Singleton(s),
+ Self::Inline(s) => Self::Inline(*s),
+ Self::Owned(s) => Self::Owned(s.clone()),
+ }
+ }
+ }
+
+ #[allow(unused)]
+ const LEN_SIZE: usize = std::mem::size_of::<crate::stack::Len>();
+
+ #[allow(unused)]
+ const TAG_SIZE: usize = std::mem::size_of::<u8>();
+
+ #[allow(unused)]
+ const MAX_CAPACITY: usize =
+ std::mem::size_of::<crate::string::StdString>() - TAG_SIZE - LEN_SIZE;
+
+ // Performance seems to slow down when trying to occupy all of the padding left by `String`'s
+ // discriminant. The question is whether faster len=1-16 "allocations" outweighs going to the heap
+ // for len=17-22.
+ #[allow(unused)]
+ const ALIGNED_CAPACITY: usize = std::mem::size_of::<crate::backend::DefaultStr>() - LEN_SIZE;
+
+ #[cfg(feature = "max_inline")]
+ const CAPACITY: usize = MAX_CAPACITY;
+ #[cfg(not(feature = "max_inline"))]
+ const CAPACITY: usize = ALIGNED_CAPACITY;
+}
+
+#[cfg(feature = "unsafe")]
+mod inner {
+ use super::*;
+
+ pub(super) union KStringInner<B> {
+ tag: TagVariant,
+ singleton: SingletonVariant,
+ owned: std::mem::ManuallyDrop<OwnedVariant<B>>,
+ inline: InlineVariant,
+ }
+
+ impl<B> KStringInner<B> {
+ /// Create a reference to a `'static` data.
+ #[inline]
+ pub const fn from_static(other: &'static str) -> Self {
+ Self {
+ singleton: SingletonVariant::new(other),
+ }
+ }
+
+ #[inline]
+ pub fn try_inline(other: &str) -> Option<Self> {
+ StackString::try_new(other).map(|inline| Self {
+ inline: InlineVariant::new(inline),
+ })
+ }
+
+ #[inline]
+ const fn tag(&self) -> Tag {
+ unsafe {
+ // SAFETY: `tag` is in the same spot in each variant
+ self.tag.tag
+ }
+ }
+ }
+
+ impl<B: crate::backend::HeapStr> KStringInner<B> {
+ #[inline]
+ pub(super) fn from_boxed(other: crate::backend::BoxedStr) -> Self {
+ #[allow(clippy::useless_conversion)]
+ let payload = B::from_boxed_str(other);
+ Self {
+ owned: std::mem::ManuallyDrop::new(OwnedVariant::new(payload)),
+ }
+ }
+
+ #[inline]
+ pub(super) fn from_string(other: StdString) -> Self {
+ if (0..=CAPACITY).contains(&other.len()) {
+ let payload = unsafe {
+ // SAFETY: range check ensured this is always safe
+ StackString::new_unchecked(other.as_str())
+ };
+ Self {
+ inline: InlineVariant::new(payload),
+ }
+ } else {
+ Self::from_boxed(other.into_boxed_str())
+ }
+ }
+
+ #[inline]
+ pub(super) fn from_ref(other: &str) -> Self {
+ if (0..=CAPACITY).contains(&other.len()) {
+ let payload = unsafe {
+ // SAFETY: range check ensured this is always safe
+ StackString::new_unchecked(other)
+ };
+ Self {
+ inline: InlineVariant::new(payload),
+ }
+ } else {
+ #[allow(clippy::useless_conversion)]
+ let payload = B::from_str(other);
+ Self {
+ owned: std::mem::ManuallyDrop::new(OwnedVariant::new(payload)),
+ }
+ }
+ }
+
+ #[inline]
+ pub(super) fn as_ref(&self) -> KStringRef<'_> {
+ let tag = self.tag();
+ unsafe {
+ // SAFETY: `tag` ensures access to correct variant
+ if tag.is_singleton() {
+ KStringRef::from_static(self.singleton.payload)
+ } else if tag.is_owned() {
+ KStringRef::from_ref(self.owned.payload.as_str())
+ } else {
+ debug_assert!(tag.is_inline());
+ KStringRef::from_ref(self.inline.payload.as_str())
+ }
+ }
+ }
+
+ #[inline]
+ pub(super) fn as_str(&self) -> &str {
+ let tag = self.tag();
+ unsafe {
+ // SAFETY: `tag` ensures access to correct variant
+ if tag.is_singleton() {
+ self.singleton.payload
+ } else if tag.is_owned() {
+ self.owned.payload.as_str()
+ } else {
+ debug_assert!(tag.is_inline());
+ self.inline.payload.as_str()
+ }
+ }
+ }
+
+ #[inline]
+ pub(super) fn into_boxed_str(self) -> crate::backend::BoxedStr {
+ let tag = self.tag();
+ unsafe {
+ // SAFETY: `tag` ensures access to correct variant
+ if tag.is_singleton() {
+ crate::backend::BoxedStr::from(self.singleton.payload)
+ } else if tag.is_owned() {
+ crate::backend::BoxedStr::from(self.owned.payload.as_str())
+ } else {
+ debug_assert!(tag.is_inline());
+ crate::backend::BoxedStr::from(self.inline.payload.as_ref())
+ }
+ }
+ }
+
+ /// Convert to a Cow str
+ #[inline]
+ pub(super) fn into_cow_str(self) -> Cow<'static, str> {
+ let tag = self.tag();
+ unsafe {
+ // SAFETY: `tag` ensures access to correct variant
+ if tag.is_singleton() {
+ Cow::Borrowed(self.singleton.payload)
+ } else if tag.is_owned() {
+ Cow::Owned(self.owned.payload.as_str().into())
+ } else {
+ debug_assert!(tag.is_inline());
+ Cow::Owned(self.inline.payload.as_str().into())
+ }
+ }
+ }
+ }
+
+ // Explicit to avoid inlining which cuts clone times in half.
+ //
+ // An automatically derived `clone()` has 10ns overhead while the explicit `Deref`/`as_str` has
+ // none of that. Being explicit and removing the `#[inline]` attribute dropped the overhead to
+ // 5ns.
+ //
+ // My only guess is that the `clone()` calls we delegate to are just that much bigger than
+ // `as_str()` that, when combined with a jump table, is blowing the icache, slowing things down.
+ impl<B: Clone> Clone for KStringInner<B> {
+ fn clone(&self) -> Self {
+ let tag = self.tag();
+ if tag.is_owned() {
+ unsafe {
+ // SAFETY: `tag` ensures access to correct variant
+ Self {
+ owned: std::mem::ManuallyDrop::new(OwnedVariant::new(
+ self.owned.payload.clone(),
+ )),
+ }
+ }
+ } else {
+ unsafe {
+ // SAFETY: `tag` ensures access to correct variant
+ // SAFETY: non-owned types are copyable
+ std::mem::transmute_copy(self)
+ }
+ }
+ }
+ }
+
+ impl<B> Drop for KStringInner<B> {
+ fn drop(&mut self) {
+ let tag = self.tag();
+ if tag.is_owned() {
+ unsafe {
+ // SAFETY: `tag` ensures we are using the right variant
+ std::mem::ManuallyDrop::drop(&mut self.owned)
+ }
+ }
+ }
+ }
+
+ #[allow(unused)]
+ const LEN_SIZE: usize = std::mem::size_of::<crate::stack::Len>();
+
+ #[allow(unused)]
+ const TAG_SIZE: usize = std::mem::size_of::<Tag>();
+
+ #[allow(unused)]
+ const PAYLOAD_SIZE: usize = std::mem::size_of::<crate::backend::DefaultStr>();
+ type Payload = Padding<PAYLOAD_SIZE>;
+
+ #[allow(unused)]
+ const TARGET_SIZE: usize = std::mem::size_of::<Target>();
+ type Target = crate::string::StdString;
+
+ #[allow(unused)]
+ const MAX_CAPACITY: usize = TARGET_SIZE - LEN_SIZE - TAG_SIZE;
+
+ // Performance seems to slow down when trying to occupy all of the padding left by `String`'s
+ // discriminant. The question is whether faster len=1-16 "allocations" outweighs going to the heap
+ // for len=17-22.
+ #[allow(unused)]
+ const ALIGNED_CAPACITY: usize = PAYLOAD_SIZE - LEN_SIZE;
+
+ #[cfg(feature = "max_inline")]
+ const CAPACITY: usize = MAX_CAPACITY;
+ #[cfg(not(feature = "max_inline"))]
+ const CAPACITY: usize = ALIGNED_CAPACITY;
+
+ const PAYLOAD_PAD_SIZE: usize = TARGET_SIZE - PAYLOAD_SIZE - TAG_SIZE;
+ const INLINE_PAD_SIZE: usize = TARGET_SIZE - CAPACITY - LEN_SIZE - TAG_SIZE;
+
+ #[derive(Copy, Clone)]
+ #[repr(C)]
+ struct TagVariant {
+ payload: Payload,
+ pad: Padding<PAYLOAD_PAD_SIZE>,
+ tag: Tag,
+ }
+ static_assertions::assert_eq_size!(Target, TagVariant);
+
+ #[derive(Copy, Clone)]
+ #[repr(C)]
+ struct SingletonVariant {
+ payload: &'static str,
+ pad: Padding<PAYLOAD_PAD_SIZE>,
+ tag: Tag,
+ }
+ static_assertions::assert_eq_size!(Payload, &'static str);
+ static_assertions::assert_eq_size!(Target, SingletonVariant);
+
+ impl SingletonVariant {
+ #[inline]
+ const fn new(payload: &'static str) -> Self {
+ Self {
+ payload,
+ pad: Padding::new(),
+ tag: Tag::SINGLETON,
+ }
+ }
+ }
+
+ impl std::fmt::Debug for SingletonVariant {
+ #[inline]
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.payload.fmt(f)
+ }
+ }
+
+ #[derive(Clone)]
+ #[repr(C)]
+ struct OwnedVariant<B> {
+ payload: B,
+ pad: Padding<PAYLOAD_PAD_SIZE>,
+ tag: Tag,
+ }
+ static_assertions::assert_eq_size!(Payload, crate::backend::DefaultStr);
+ static_assertions::assert_eq_size!(Target, OwnedVariant<crate::backend::DefaultStr>);
+
+ impl<B> OwnedVariant<B> {
+ #[inline]
+ const fn new(payload: B) -> Self {
+ Self {
+ payload,
+ pad: Padding::new(),
+ tag: Tag::OWNED,
+ }
+ }
+ }
+
+ impl<B: crate::backend::HeapStr> std::fmt::Debug for OwnedVariant<B> {
+ #[inline]
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.payload.fmt(f)
+ }
+ }
+
+ #[derive(Copy, Clone)]
+ #[repr(C)]
+ struct InlineVariant {
+ payload: StackString<CAPACITY>,
+ pad: Padding<INLINE_PAD_SIZE>,
+ tag: Tag,
+ }
+ static_assertions::assert_eq_size!(Target, InlineVariant);
+
+ impl InlineVariant {
+ #[inline]
+ const fn new(payload: StackString<CAPACITY>) -> Self {
+ Self {
+ payload,
+ pad: Padding::new(),
+ tag: Tag::INLINE,
+ }
+ }
+ }
+
+ impl std::fmt::Debug for InlineVariant {
+ #[inline]
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.payload.fmt(f)
+ }
+ }
+
+ #[derive(Copy, Clone, PartialEq, Eq)]
+ #[repr(transparent)]
+ struct Tag(u8);
+
+ impl Tag {
+ const SINGLETON: Tag = Tag(0);
+ const OWNED: Tag = Tag(u8::MAX);
+ const INLINE: Tag = Tag(1);
+
+ #[inline]
+ const fn is_singleton(self) -> bool {
+ self.0 == Self::SINGLETON.0
+ }
+
+ #[inline]
+ const fn is_owned(self) -> bool {
+ self.0 == Self::OWNED.0
+ }
+
+ #[inline]
+ const fn is_inline(self) -> bool {
+ !self.is_singleton() && !self.is_owned()
+ }
+ }
+
+ #[derive(Copy, Clone)]
+ #[repr(transparent)]
+ struct Padding<const L: usize>([std::mem::MaybeUninit<u8>; L]);
+
+ impl<const L: usize> Padding<L> {
+ const fn new() -> Self {
+ let padding = unsafe {
+ // SAFETY: Padding, never actually used
+ std::mem::MaybeUninit::uninit().assume_init()
+ };
+ Self(padding)
+ }
+ }
+
+ impl<const L: usize> Default for Padding<L> {
+ fn default() -> Self {
+ Self::new()
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_size() {
+ println!("KString: {}", std::mem::size_of::<KString>());
+ }
+}
diff --git a/vendor/kstring/src/string_cow.rs b/vendor/kstring/src/string_cow.rs
new file mode 100644
index 000000000..6a1b4b89a
--- /dev/null
+++ b/vendor/kstring/src/string_cow.rs
@@ -0,0 +1,383 @@
+use std::{borrow::Cow, fmt};
+
+use crate::KStringBase;
+use crate::KStringRef;
+use crate::KStringRefInner;
+
+type StdString = std::string::String;
+type BoxedStr = Box<str>;
+
+/// A reference to a UTF-8 encoded, immutable string.
+pub type KStringCow<'s> = KStringCowBase<'s, crate::backend::DefaultStr>;
+
+/// A reference to a UTF-8 encoded, immutable string.
+#[derive(Clone)]
+#[repr(transparent)]
+pub struct KStringCowBase<'s, B = crate::backend::DefaultStr> {
+ pub(crate) inner: KStringCowInner<'s, B>,
+}
+
+#[derive(Clone)]
+pub(crate) enum KStringCowInner<'s, B> {
+ Borrowed(&'s str),
+ Owned(KStringBase<B>),
+}
+
+impl<'s, B> KStringCowBase<'s, B> {
+ /// Create a new empty `KStringCowBase`.
+ #[inline]
+ #[must_use]
+ pub const fn new() -> Self {
+ Self::from_static("")
+ }
+
+ /// Create a reference to a `'static` data.
+ #[inline]
+ #[must_use]
+ pub const fn from_static(other: &'static str) -> Self {
+ Self {
+ inner: KStringCowInner::Owned(KStringBase::from_static(other)),
+ }
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> KStringCowBase<'s, B> {
+ /// Create an owned `KStringCowBase`.
+ #[inline]
+ #[must_use]
+ pub fn from_boxed(other: BoxedStr) -> Self {
+ Self {
+ inner: KStringCowInner::Owned(KStringBase::from_boxed(other)),
+ }
+ }
+
+ /// Create an owned `KStringCowBase`.
+ #[inline]
+ #[must_use]
+ pub fn from_string(other: StdString) -> Self {
+ Self {
+ inner: KStringCowInner::Owned(KStringBase::from_string(other)),
+ }
+ }
+
+ /// Create a reference to a borrowed data.
+ #[inline]
+ #[must_use]
+ pub fn from_ref(other: &'s str) -> Self {
+ Self {
+ inner: KStringCowInner::Borrowed(other),
+ }
+ }
+
+ /// Get a reference to the `KStringBase`.
+ #[inline]
+ #[must_use]
+ pub fn as_ref(&self) -> KStringRef<'_> {
+ self.inner.as_ref()
+ }
+
+ /// Clone the data into an owned-type.
+ #[inline]
+ #[must_use]
+ pub fn into_owned(self) -> KStringBase<B> {
+ self.inner.into_owned()
+ }
+
+ /// Extracts a string slice containing the entire `KStringCowBase`.
+ #[inline]
+ #[must_use]
+ pub fn as_str(&self) -> &str {
+ self.inner.as_str()
+ }
+
+ /// Convert to a mutable string type, cloning the data if necessary.
+ #[inline]
+ #[must_use]
+ pub fn into_string(self) -> StdString {
+ String::from(self.into_boxed_str())
+ }
+
+ /// Convert to a mutable string type, cloning the data if necessary.
+ #[inline]
+ #[must_use]
+ pub fn into_boxed_str(self) -> BoxedStr {
+ self.inner.into_boxed_str()
+ }
+
+ /// Convert to a Cow str
+ #[inline]
+ #[must_use]
+ pub fn into_cow_str(self) -> Cow<'s, str> {
+ self.inner.into_cow_str()
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> KStringCowInner<'s, B> {
+ #[inline]
+ fn as_ref(&self) -> KStringRef<'_> {
+ match self {
+ Self::Borrowed(s) => KStringRef::from_ref(s),
+ Self::Owned(s) => s.as_ref(),
+ }
+ }
+
+ #[inline]
+ fn into_owned(self) -> KStringBase<B> {
+ match self {
+ Self::Borrowed(s) => KStringBase::from_ref(s),
+ Self::Owned(s) => s,
+ }
+ }
+
+ #[inline]
+ fn as_str(&self) -> &str {
+ match self {
+ Self::Borrowed(s) => s,
+ Self::Owned(s) => s.as_str(),
+ }
+ }
+
+ #[inline]
+ fn into_boxed_str(self) -> BoxedStr {
+ match self {
+ Self::Borrowed(s) => BoxedStr::from(s),
+ Self::Owned(s) => s.into_boxed_str(),
+ }
+ }
+
+ /// Convert to a Cow str
+ #[inline]
+ fn into_cow_str(self) -> Cow<'s, str> {
+ match self {
+ Self::Borrowed(s) => Cow::Borrowed(s),
+ Self::Owned(s) => s.into_cow_str(),
+ }
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> std::ops::Deref for KStringCowBase<'s, B> {
+ type Target = str;
+
+ #[inline]
+ fn deref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> Eq for KStringCowBase<'s, B> {}
+
+impl<'s, B: crate::backend::HeapStr> PartialEq<KStringCowBase<'s, B>> for KStringCowBase<'s, B> {
+ #[inline]
+ fn eq(&self, other: &KStringCowBase<'s, B>) -> bool {
+ PartialEq::eq(self.as_str(), other.as_str())
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> PartialEq<str> for KStringCowBase<'s, B> {
+ #[inline]
+ fn eq(&self, other: &str) -> bool {
+ PartialEq::eq(self.as_str(), other)
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> PartialEq<&'s str> for KStringCowBase<'s, B> {
+ #[inline]
+ fn eq(&self, other: &&str) -> bool {
+ PartialEq::eq(self.as_str(), *other)
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> PartialEq<String> for KStringCowBase<'s, B> {
+ #[inline]
+ fn eq(&self, other: &StdString) -> bool {
+ PartialEq::eq(self.as_str(), other.as_str())
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> Ord for KStringCowBase<'s, B> {
+ #[inline]
+ fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+ self.as_str().cmp(other.as_str())
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> PartialOrd for KStringCowBase<'s, B> {
+ #[inline]
+ fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+ self.as_str().partial_cmp(other.as_str())
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> std::hash::Hash for KStringCowBase<'s, B> {
+ #[inline]
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ self.as_str().hash(state);
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> fmt::Debug for KStringCowBase<'s, B> {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.as_str().fmt(f)
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> fmt::Display for KStringCowBase<'s, B> {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(self.as_str(), f)
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> AsRef<str> for KStringCowBase<'s, B> {
+ #[inline]
+ fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> AsRef<[u8]> for KStringCowBase<'s, B> {
+ #[inline]
+ fn as_ref(&self) -> &[u8] {
+ self.as_bytes()
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> AsRef<std::ffi::OsStr> for KStringCowBase<'s, B> {
+ #[inline]
+ fn as_ref(&self) -> &std::ffi::OsStr {
+ (&**self).as_ref()
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> AsRef<std::path::Path> for KStringCowBase<'s, B> {
+ #[inline]
+ fn as_ref(&self) -> &std::path::Path {
+ std::path::Path::new(self)
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> std::borrow::Borrow<str> for KStringCowBase<'s, B> {
+ #[inline]
+ fn borrow(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl<'s, B> Default for KStringCowBase<'s, B> {
+ #[inline]
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> From<KStringBase<B>> for KStringCowBase<'s, B> {
+ #[inline]
+ fn from(other: KStringBase<B>) -> Self {
+ let inner = KStringCowInner::Owned(other);
+ Self { inner }
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> From<&'s KStringBase<B>> for KStringCowBase<'s, B> {
+ #[inline]
+ fn from(other: &'s KStringBase<B>) -> Self {
+ let other = other.as_ref();
+ other.into()
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> From<KStringRef<'s>> for KStringCowBase<'s, B> {
+ #[inline]
+ fn from(other: KStringRef<'s>) -> Self {
+ match other.inner {
+ KStringRefInner::Borrowed(s) => Self::from_ref(s),
+ KStringRefInner::Singleton(s) => Self::from_static(s),
+ }
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> From<&'s KStringRef<'s>> for KStringCowBase<'s, B> {
+ #[inline]
+ fn from(other: &'s KStringRef<'s>) -> Self {
+ match other.inner {
+ KStringRefInner::Borrowed(s) => Self::from_ref(s),
+ KStringRefInner::Singleton(s) => Self::from_static(s),
+ }
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> From<StdString> for KStringCowBase<'s, B> {
+ #[inline]
+ fn from(other: StdString) -> Self {
+ Self::from_string(other)
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> From<&'s StdString> for KStringCowBase<'s, B> {
+ #[inline]
+ fn from(other: &'s StdString) -> Self {
+ Self::from_ref(other.as_str())
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> From<BoxedStr> for KStringCowBase<'s, B> {
+ #[inline]
+ fn from(other: BoxedStr) -> Self {
+ // Since the memory is already allocated, don't bother moving it into a FixedString
+ Self::from_boxed(other)
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> From<&'s BoxedStr> for KStringCowBase<'s, B> {
+ #[inline]
+ fn from(other: &'s BoxedStr) -> Self {
+ Self::from_ref(other)
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> From<&'s str> for KStringCowBase<'s, B> {
+ #[inline]
+ fn from(other: &'s str) -> Self {
+ Self::from_ref(other)
+ }
+}
+
+impl<B: crate::backend::HeapStr> std::str::FromStr for KStringCowBase<'_, B> {
+ type Err = std::convert::Infallible;
+ #[inline]
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ Ok(Self::from_string(s.into()))
+ }
+}
+
+#[cfg(feature = "serde")]
+impl<'s, B: crate::backend::HeapStr> serde::Serialize for KStringCowBase<'s, B> {
+ #[inline]
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ serializer.serialize_str(self.as_str())
+ }
+}
+
+#[cfg(feature = "serde")]
+impl<'de, 's, B: crate::backend::HeapStr> serde::Deserialize<'de> for KStringCowBase<'s, B> {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ KStringBase::deserialize(deserializer).map(|s| s.into())
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_size() {
+ println!("KStringCow: {}", std::mem::size_of::<KStringCow<'static>>());
+ }
+}
diff --git a/vendor/kstring/src/string_ref.rs b/vendor/kstring/src/string_ref.rs
new file mode 100644
index 000000000..a79b9d46c
--- /dev/null
+++ b/vendor/kstring/src/string_ref.rs
@@ -0,0 +1,277 @@
+use std::fmt;
+
+use crate::KStringBase;
+use crate::KStringCowBase;
+
+type StdString = std::string::String;
+type BoxedStr = Box<str>;
+
+/// A reference to a UTF-8 encoded, immutable string.
+#[derive(Copy, Clone)]
+#[repr(transparent)]
+pub struct KStringRef<'s> {
+ pub(crate) inner: KStringRefInner<'s>,
+}
+
+#[derive(Copy, Clone, Debug)]
+pub(crate) enum KStringRefInner<'s> {
+ Borrowed(&'s str),
+ Singleton(&'static str),
+}
+
+impl<'s> KStringRef<'s> {
+ /// Create a new empty `KStringBase`.
+ #[inline]
+ #[must_use]
+ pub const fn new() -> Self {
+ Self::from_static("")
+ }
+
+ /// Create a reference to a `'static` data.
+ #[inline]
+ #[must_use]
+ pub const fn from_static(other: &'static str) -> Self {
+ Self {
+ inner: KStringRefInner::Singleton(other),
+ }
+ }
+
+ /// Create a reference to a borrowed data.
+ #[inline]
+ #[must_use]
+ pub fn from_ref(other: &'s str) -> Self {
+ Self {
+ inner: KStringRefInner::Borrowed(other),
+ }
+ }
+
+ /// Clone the data into an owned-type.
+ #[inline]
+ #[must_use]
+ #[allow(clippy::wrong_self_convention)]
+ pub fn to_owned<B: crate::backend::HeapStr>(&self) -> KStringBase<B> {
+ self.inner.to_owned()
+ }
+
+ /// Extracts a string slice containing the entire `KStringRef`.
+ #[inline]
+ #[must_use]
+ pub fn as_str(&self) -> &str {
+ self.inner.as_str()
+ }
+
+ /// Convert to a mutable string type, cloning the data if necessary.
+ #[inline]
+ #[must_use]
+ pub fn into_mut(self) -> StdString {
+ self.inner.into_mut()
+ }
+}
+
+impl<'s> KStringRefInner<'s> {
+ #[inline]
+ #[allow(clippy::wrong_self_convention)]
+ fn to_owned<B: crate::backend::HeapStr>(&self) -> KStringBase<B> {
+ match self {
+ Self::Borrowed(s) => KStringBase::from_ref(s),
+ Self::Singleton(s) => KStringBase::from_static(s),
+ }
+ }
+
+ #[inline]
+ fn as_str(&self) -> &str {
+ match self {
+ Self::Borrowed(s) => s,
+ Self::Singleton(s) => s,
+ }
+ }
+
+ #[inline]
+ fn into_mut(self) -> StdString {
+ self.as_str().to_owned()
+ }
+}
+
+impl<'s> std::ops::Deref for KStringRef<'s> {
+ type Target = str;
+
+ #[inline]
+ fn deref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl<'s> Eq for KStringRef<'s> {}
+
+impl<'s> PartialEq<KStringRef<'s>> for KStringRef<'s> {
+ #[inline]
+ fn eq(&self, other: &KStringRef<'s>) -> bool {
+ PartialEq::eq(self.as_str(), other.as_str())
+ }
+}
+
+impl<'s> PartialEq<str> for KStringRef<'s> {
+ #[inline]
+ fn eq(&self, other: &str) -> bool {
+ PartialEq::eq(self.as_str(), other)
+ }
+}
+
+impl<'s> PartialEq<&'s str> for KStringRef<'s> {
+ #[inline]
+ fn eq(&self, other: &&str) -> bool {
+ PartialEq::eq(self.as_str(), *other)
+ }
+}
+
+impl<'s> PartialEq<String> for KStringRef<'s> {
+ #[inline]
+ fn eq(&self, other: &StdString) -> bool {
+ PartialEq::eq(self.as_str(), other.as_str())
+ }
+}
+
+impl<'s> Ord for KStringRef<'s> {
+ #[inline]
+ fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+ self.as_str().cmp(other.as_str())
+ }
+}
+
+impl<'s> PartialOrd for KStringRef<'s> {
+ #[inline]
+ fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+ self.as_str().partial_cmp(other.as_str())
+ }
+}
+
+impl<'s> std::hash::Hash for KStringRef<'s> {
+ #[inline]
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ self.as_str().hash(state);
+ }
+}
+
+impl<'s> fmt::Debug for KStringRef<'s> {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(&self.inner, f)
+ }
+}
+
+impl<'s> fmt::Display for KStringRef<'s> {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(self.as_str(), f)
+ }
+}
+
+impl<'s> AsRef<str> for KStringRef<'s> {
+ #[inline]
+ fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl<'s> AsRef<[u8]> for KStringRef<'s> {
+ #[inline]
+ fn as_ref(&self) -> &[u8] {
+ self.as_bytes()
+ }
+}
+
+impl<'s> AsRef<std::ffi::OsStr> for KStringRef<'s> {
+ #[inline]
+ fn as_ref(&self) -> &std::ffi::OsStr {
+ (&**self).as_ref()
+ }
+}
+
+impl<'s> AsRef<std::path::Path> for KStringRef<'s> {
+ #[inline]
+ fn as_ref(&self) -> &std::path::Path {
+ std::path::Path::new(self)
+ }
+}
+
+impl<'s> std::borrow::Borrow<str> for KStringRef<'s> {
+ #[inline]
+ fn borrow(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl<'s> Default for KStringRef<'s> {
+ #[inline]
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> From<&'s KStringBase<B>> for KStringRef<'s> {
+ #[inline]
+ fn from(other: &'s KStringBase<B>) -> Self {
+ other.as_ref()
+ }
+}
+
+impl<'s, B: crate::backend::HeapStr> From<&'s KStringCowBase<'s, B>> for KStringRef<'s> {
+ #[inline]
+ fn from(other: &'s KStringCowBase<'s, B>) -> Self {
+ other.as_ref()
+ }
+}
+
+impl<'s> From<&'s StdString> for KStringRef<'s> {
+ #[inline]
+ fn from(other: &'s StdString) -> Self {
+ KStringRef::from_ref(other.as_str())
+ }
+}
+
+impl<'s> From<&'s BoxedStr> for KStringRef<'s> {
+ #[inline]
+ fn from(other: &'s BoxedStr) -> Self {
+ Self::from_ref(other)
+ }
+}
+
+impl<'s> From<&'s str> for KStringRef<'s> {
+ #[inline]
+ fn from(other: &'s str) -> Self {
+ KStringRef::from_ref(other)
+ }
+}
+
+#[cfg(feature = "serde")]
+impl<'s> serde::Serialize for KStringRef<'s> {
+ #[inline]
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ serializer.serialize_str(self.as_str())
+ }
+}
+
+#[cfg(feature = "serde")]
+impl<'de: 's, 's> serde::Deserialize<'de> for KStringRef<'s> {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ let s: &'s str = serde::Deserialize::deserialize(deserializer)?;
+ let s = KStringRef::from_ref(s);
+ Ok(s)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_size() {
+ println!("KStringRef: {}", std::mem::size_of::<KStringRef<'static>>());
+ }
+}