lz-str-0.2.1/.cargo_vcs_info.json0000644000000001360000000000100122670ustar { "git": { "sha1": "dc1d9cb87ca396b00f03773b89d596bf858bf373" }, "path_in_vcs": "" }lz-str-0.2.1/.gitignore000064400000000000000000000000070072674642500130740ustar 00000000000000/targetlz-str-0.2.1/CHANGELOG.md000064400000000000000000000022310072674642500127160ustar 00000000000000# Changelog ## [Unreleased] ## [0.2.1] - 2022-10-28 ### Added - Added `IntoWideIter` impl for `&String` - Add `rustc-hash` feature to optionally boost compression speed ## [0.2.0] - 2022-10-20 ### Added - Added all functions from lz-string - Added most of the tests from lz-string - Added basic Python Binding ### Removed - Removed `str_to_u32_vec` ### Changed - Renamed `compress_uri` to `compress_to_encoded_uri_component` - Renamed `decompress_uri` to `decompress_from_encoded_uri_component` - Renamed `compress_str` to `compress` - Renamed `decompress` to `decompress` - Renamed `compress` to `compress_internal` - Renamed `decompress` to `decompress_internal` - Changed many interfaces to use the `IntoWideIter` instead of u32 slices ### Fixed - Fix issues with compressing and decompressing long, nonrepeating inputs ## [0.1.0] - 2023-01-23 ### Added - Initial port of lz-string to Rust - Add `compress`/`decompress` functions - Add `compress_str`/`compress_str` functions - Add `compress_uri`/`compress_uri` functions - Add utility `str_to_u32_vec` function [0.1.0]: https://github.com/adumbidiot/lz-str-rs/releases/tag/0.1.0lz-str-0.2.1/Cargo.lock0000644000000405170000000000100102510ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[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 = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" version = "3.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "ciborium" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" dependencies = [ "ciborium-io", "ciborium-ll", "serde", ] [[package]] name = "ciborium-io" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" [[package]] name = "ciborium-ll" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" dependencies = [ "ciborium-io", "half", ] [[package]] name = "clap" version = "3.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" dependencies = [ "bitflags", "clap_lex", "indexmap", "textwrap", ] [[package]] name = "clap_lex" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" dependencies = [ "os_str_bytes", ] [[package]] name = "criterion" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" dependencies = [ "anes", "atty", "cast", "ciborium", "clap", "criterion-plot", "itertools", "lazy_static", "num-traits", "oorandom", "plotters", "rayon", "regex", "serde", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools", ] [[package]] name = "crossbeam-channel" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" dependencies = [ "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" dependencies = [ "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", "memoffset", "scopeguard", ] [[package]] name = "crossbeam-utils" version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" dependencies = [ "cfg-if", ] [[package]] name = "either" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "getrandom" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" 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 = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "indexmap" version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg", "hashbrown", ] [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" [[package]] name = "js-sys" version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] [[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.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" [[package]] name = "log" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] [[package]] name = "lz-str" version = "0.2.1" dependencies = [ "criterion", "rand", "rustc-hash", ] [[package]] name = "memoffset" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ "autocfg", ] [[package]] name = "num-traits" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ "hermit-abi", "libc", ] [[package]] name = "once_cell" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" [[package]] name = "oorandom" version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "os_str_bytes" version = "6.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9" [[package]] name = "plotters" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" dependencies = [ "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" [[package]] name = "plotters-svg" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" 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.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 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.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "rayon" version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" dependencies = [ "autocfg", "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" dependencies = [ "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", "num_cpus", ] [[package]] name = "regex" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "ryu" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[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 = "serde" version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "syn" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "textwrap" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[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-ident" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[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.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "web-sys" version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" 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" lz-str-0.2.1/Cargo.toml0000644000000025570000000000100102760ustar # 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 = "lz-str" version = "0.2.1" authors = ["adumbidiot "] exclude = [ "scripts/*", "js-test/*", ".github/*", "Makefile", "deny.toml", "bindings", "fuzz/*", "test_data/*", ] description = "A port of lz-string to Rust" homepage = "https://github.com/adumbidiot/lz-str-rs" readme = "README.md" keywords = [ "lz-string", "compression", ] categories = ["compression"] license = "MIT OR Apache-2.0" repository = "https://github.com/adumbidiot/lz-str-rs" [profile.release] opt-level = 3 lto = "fat" codegen-units = 1 strip = "symbols" [[bench]] name = "compress" harness = false [[bench]] name = "decompress" harness = false [dependencies.rustc-hash] version = "1.1.0" optional = true [dev-dependencies.criterion] version = "0.4.0" [dev-dependencies.rand] version = "0.8.3" [features] nightly = ["criterion/real_blackbox"] lz-str-0.2.1/Cargo.toml.orig000064400000000000000000000020150072674642500137740ustar 00000000000000[package] name = "lz-str" version = "0.2.1" authors = [ "adumbidiot " ] edition = "2018" description = "A port of lz-string to Rust" repository = "https://github.com/adumbidiot/lz-str-rs" homepage = "https://github.com/adumbidiot/lz-str-rs" license = "MIT OR Apache-2.0" readme = "README.md" keywords = [ "lz-string", "compression" ] categories = [ "compression" ] exclude = [ "scripts/*", "js-test/*", ".github/*", "Makefile", "deny.toml", "bindings", "fuzz/*", "test_data/*", ] [dependencies] rustc-hash = { version = "1.1.0", optional = true } [dev-dependencies] rand = "0.8.3" criterion = "0.4.0" [features] # This is currently only used for benchmarking. nightly = [ "criterion/real_blackbox" ] [[bench]] name = "compress" harness = false [[bench]] name = "decompress" harness = false [workspace] members = [ "bindings/*", ] [profile.release] opt-level = 3 lto = "fat" codegen-units = 1 strip = "symbols"lz-str-0.2.1/LICENSE-APACHE.md000064400000000000000000000264440072674642500134440ustar 00000000000000 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.lz-str-0.2.1/LICENSE-MIT.md000064400000000000000000000021050072674642500131400ustar 00000000000000MIT License Copyright (c) 2020, Nathaniel Daniel 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.lz-str-0.2.1/README.md000064400000000000000000000047470072674642500124020ustar 00000000000000# lz-str-rs [![crates.io](https://img.shields.io/crates/v/lz-str.svg)](https://crates.io/crates/lz-str) [![Documentation](https://docs.rs/lz-str/badge.svg)](https://docs.rs/lz-str) [![MIT/Apache-2 licensed](https://img.shields.io/crates/l/lz-str.svg)](./LICENSE-APACHE) ![Rust](https://github.com/adumbidiot/lz-str-rs/workflows/Rust/badge.svg) A port of [lz-string](https://github.com/pieroxy/lz-string) to Rust. ### Installing Add the following to your `Cargo.toml` file: ```toml [dependencies] lz-str = "0.1.0" ``` ## Getting Started ```rust // The demonstrated functions correspond with `LZString.compress` and `LZString.decompress` from the JS version. fn main() { let data = "The quick brown fox jumps over the lazy dog"; // Compress the data. This cannot fail. let compressed_data = lz_str::compress(data); // Decompress the data. // This may return `Option::None` if it fails. // Make sure to do error-checking in a real application to prevent crashes! let decompressed_data = lz_str::decompress(compressed_data).expect("`compressed_data` is invalid"); // The decompressed_data should be the same as data, except encoded as UTF16. // We undo that here. // In a real application, // you will want to do error checking to prevent users from causing crashes with invalid data. let decompressed_data = String::from_utf16(&decompressed_data).expect("`decompressed_data` is not valid UTF16"); assert!(data == decompressed_data); } ``` See the [examples](https://github.com/adumbidiot/lz-str-rs/tree/master/examples) directory for more examples. ## Features `rustc-hash`: This feature will replace some internal maps' hashers with rustc-hash, boosting performance at the cost of not using a DOS-resistant hasher. ## Testing ```bash cargo test ``` ## Benching ```bash cargo bench ``` ## Bindings [WebAssembly](bindings/lz-str-wasm) ## Authors adumbidiot (Nathaniel Daniel) ## License Licensed under either of * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. ## Contributing 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.lz-str-0.2.1/benches/compress.rs000064400000000000000000000032620072674642500147220ustar 00000000000000use criterion::criterion_group; use criterion::criterion_main; use criterion::BenchmarkId; use criterion::Criterion; const TEST_PHRASE: &str = "During tattooing, ink is injected into the skin, initiating an immune response, and cells called \"macrophages\" move into the area and \"eat up\" the ink. The macrophages carry some of the ink to the body\'s lymph nodes, but some that are filled with ink stay put, embedded in the skin. That\'s what makes the tattoo visible under the skin. Dalhousie Uiversity\'s Alec Falkenham is developing a topical cream that works by targeting the macrophages that have remained at the site of the tattoo. New macrophages move in to consume the previously pigment-filled macrophages and then migrate to the lymph nodes, eventually taking all the dye with them. \"When comparing it to laser-based tattoo removal, in which you see the burns, the scarring, the blisters, in this case, we\'ve designed a drug that doesn\'t really have much off-target effect,\" he said. \"We\'re not targeting any of the normal skin cells, so you won\'t see a lot of inflammation. In fact, based on the process that we\'re actually using, we don\'t think there will be any inflammation at all and it would actually be anti-inflammatory."; pub fn bench(c: &mut Criterion) { let test_phrase = TEST_PHRASE.encode_utf16().collect::>(); c.bench_with_input( BenchmarkId::new("compress", "Tattoo Description"), &test_phrase, |b, s| { b.iter(|| lz_str::compress(s)); }, ); } criterion_group! { name = benches; config = Criterion::default().sample_size(1000); targets = bench } criterion_main!(benches); lz-str-0.2.1/benches/decompress.rs000064400000000000000000000032310072674642500152270ustar 00000000000000use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; const TEST_PHRASE: &str = "During tattooing, ink is injected into the skin, initiating an immune response, and cells called \"macrophages\" move into the area and \"eat up\" the ink. The macrophages carry some of the ink to the body\'s lymph nodes, but some that are filled with ink stay put, embedded in the skin. That\'s what makes the tattoo visible under the skin. Dalhousie Uiversity\'s Alec Falkenham is developing a topical cream that works by targeting the macrophages that have remained at the site of the tattoo. New macrophages move in to consume the previously pigment-filled macrophages and then migrate to the lymph nodes, eventually taking all the dye with them. \"When comparing it to laser-based tattoo removal, in which you see the burns, the scarring, the blisters, in this case, we\'ve designed a drug that doesn\'t really have much off-target effect,\" he said. \"We\'re not targeting any of the normal skin cells, so you won\'t see a lot of inflammation. In fact, based on the process that we\'re actually using, we don\'t think there will be any inflammation at all and it would actually be anti-inflammatory."; pub fn bench(c: &mut Criterion) { let compressed = lz_str::compress(&TEST_PHRASE.encode_utf16().collect::>()); c.bench_with_input( BenchmarkId::new("decompress", "Tattoo Description"), &compressed, |b, s| { b.iter(|| lz_str::decompress(s)); }, ); } criterion_group! { name = benches; config = Criterion::default().sample_size(1000); targets = bench } criterion_main!(benches); lz-str-0.2.1/examples/binary-data.rs000064400000000000000000000023720072674642500154720ustar 00000000000000// Sometimes, you may need to compress or decompress binary data. // This shows a way to handle that. fn main() { let data = b"The quick brown fox jumps over the lazy dog"; // We pad every byte to make it a `u16`. let wide_data: Vec = data.iter().copied().map(u16::from).collect(); // Use the `compress_to_uint8_array` function to compress data to a `Vec`. // This has the same space efficiency as the `lz_str::compress` function. let compressed_data = lz_str::compress_to_uint8_array(wide_data); // Call the decompress function. // We use the `lz_str::decompress_from_uint8_array`, // as that is the correct pairing for the `lz_str::compress_from_uint8_array` function. // Make sure to do proper error checking in a real application. let decompressed_data = lz_str::decompress_from_uint8_array(&compressed_data) .expect("`compressed_data` is invalid"); // Remap the u16 to u8 via truncation. // We do this in this example only to prove we get the same bytes back from the beginning. let decompressed_data: Vec = decompressed_data.iter().copied().map(|v| v as u8).collect(); // We get the same bytes from the beginning. assert!(decompressed_data == data); } lz-str-0.2.1/examples/simple.rs000064400000000000000000000017260072674642500145720ustar 00000000000000// The demonstrated functions correspond with `LZString.compress` and `LZString.decompress` from the JS version. fn main() { let data = "The quick brown fox jumps over the lazy dog"; // Compress the data. This cannot fail. let compressed_data = lz_str::compress(data); // Decompress the data. // This may return `Option::None` if it fails. // Make sure to do error-checking in a real application to prevent crashes! let decompressed_data = lz_str::decompress(compressed_data).expect("`compressed_data` is invalid"); // The decompressed_data should be the same as data, except encoded as UTF16. // We undo that here. // In a real application, // you will want to do error checking to prevent users from causing crashes with invalid data. let decompressed_data = String::from_utf16(&decompressed_data).expect("`decompressed_data` is not valid UTF16"); assert!(data == decompressed_data); } lz-str-0.2.1/src/compress.rs000064400000000000000000000224740072674642500141100ustar 00000000000000use crate::constants::BASE64_KEY; use crate::constants::CLOSE_CODE; use crate::constants::START_CODE_BITS; use crate::constants::U16_CODE; use crate::constants::U8_CODE; use crate::constants::URI_KEY; use crate::IntoWideIter; use std::collections::hash_map::Entry as HashMapEntry; use std::convert::TryInto; #[cfg(not(feature = "rustc-hash"))] type HashMap = std::collections::HashMap; #[cfg(not(feature = "rustc-hash"))] type HashSet = std::collections::HashSet; #[cfg(feature = "rustc-hash")] type HashMap = rustc_hash::FxHashMap; #[cfg(feature = "rustc-hash")] type HashSet = rustc_hash::FxHashSet; /// The number of "base codes", /// the default codes of all streams. /// /// These are U8_CODE, U16_CODE, and CLOSE_CODE. const NUM_BASE_CODES: usize = 3; #[derive(Debug)] pub(crate) struct CompressContext<'a, F> { dictionary: HashMap<&'a [u16], u32>, dictionary_to_create: HashSet, /// The current word, w, /// in terms of indexes into the input. w_start_idx: usize, w_end_idx: usize, // The counter for increasing the current number of bits in a code. // The max size of this is 1 << max(num_bits) == 1 + u32::MAX, so we use u64. enlarge_in: u64, /// The input buffer. input: &'a [u16], /// The output buffer. output: Vec, /// The bit buffer. bit_buffer: u16, /// The current number of bits in a code. /// /// This is a u8, /// because we currently assume the max code size is 32 bits. /// 32 < u8::MAX num_bits: u8, /// The current bit position. bit_position: u8, /// The maximum # of bits per char. /// /// This value may not exceed 16, /// as the reference implementation will also not handle values over 16. bits_per_char: u8, /// A transformation function to map a u16 to another u16, /// before appending it to the output buffer. to_char: F, } impl<'a, F> CompressContext<'a, F> where F: Fn(u16) -> u16, { /// Make a new [`CompressContext`]. /// /// # Panics /// Panics if `bits_per_char` exceeds the number of bits in a u16. #[inline] pub fn new(input: &'a [u16], bits_per_char: u8, to_char: F) -> Self { assert!(usize::from(bits_per_char) <= std::mem::size_of::() * 8); CompressContext { dictionary: HashMap::default(), dictionary_to_create: HashSet::default(), w_start_idx: 0, w_end_idx: 0, enlarge_in: 2, input, output: Vec::with_capacity(input.len() >> 1), // Lowball, assume we can get a 50% reduction in size. bit_buffer: 0, num_bits: START_CODE_BITS, bit_position: 0, bits_per_char, to_char, } } #[inline] pub fn produce_w(&mut self) { let w = &self.input[self.w_start_idx..self.w_end_idx]; match w .first() .map(|first_w_char| self.dictionary_to_create.take(first_w_char)) { Some(Some(first_w_char)) => { if first_w_char < 256 { self.write_bits(self.num_bits, U8_CODE.into()); self.write_bits(8, first_w_char.into()); } else { self.write_bits(self.num_bits, U16_CODE.into()); self.write_bits(16, first_w_char.into()); } self.decrement_enlarge_in(); } None | Some(None) => { self.write_bits(self.num_bits, *self.dictionary.get(w).unwrap()); } } self.decrement_enlarge_in(); } /// Append the bit to the bit buffer. #[inline] pub fn write_bit(&mut self, bit: bool) { self.bit_buffer = (self.bit_buffer << 1) | u16::from(bit); self.bit_position += 1; if self.bit_position == self.bits_per_char { self.bit_position = 0; let output_char = (self.to_char)(self.bit_buffer); self.bit_buffer = 0; self.output.push(output_char); } } #[inline] pub fn write_bits(&mut self, n: u8, mut value: u32) { for _ in 0..n { self.write_bit(value & 1 == 1); value >>= 1; } } #[inline] pub fn decrement_enlarge_in(&mut self) { self.enlarge_in -= 1; if self.enlarge_in == 0 { self.enlarge_in = 1 << self.num_bits; self.num_bits += 1; } } /// Compress a `u16`. This represents a wide char. #[inline] pub fn write_u16(&mut self, i: usize) { let c = &self.input[i]; let dictionary_len = self.dictionary.len(); if let HashMapEntry::Vacant(entry) = self.dictionary.entry(std::slice::from_ref(c)) { entry.insert((dictionary_len + NUM_BASE_CODES).try_into().unwrap()); self.dictionary_to_create.insert(*c); } // wc = w + c. let wc = &self.input[self.w_start_idx..self.w_end_idx + 1]; let dictionary_len = self.dictionary.len(); match self.dictionary.entry(wc) { HashMapEntry::Occupied(_entry) => { // w = wc. self.w_end_idx += 1; } HashMapEntry::Vacant(entry) => { // Add wc to the dictionary. entry.insert((dictionary_len + NUM_BASE_CODES).try_into().unwrap()); // Originally, this was before adding wc to the dict. // However, we only use the dict for a lookup that will crash if it fails in produce_w. // Therefore, moving it here should be fine. self.produce_w(); // w = c. self.w_start_idx = i; self.w_end_idx = i + 1; } } } /// Finish the stream and get the final result. #[inline] pub fn finish(mut self) -> Vec { let w = &self.input[self.w_start_idx..self.w_end_idx]; // Output the code for w. if !w.is_empty() { self.produce_w(); } // Mark the end of the stream self.write_bits(self.num_bits, CLOSE_CODE.into()); let str_len = self.output.len(); // Flush the last char while self.output.len() == str_len { self.write_bit(false); } self.output } /// Perform the compression and return the result. pub fn compress(mut self) -> Vec { for i in 0..self.input.len() { self.write_u16(i); } self.finish() } } /// Compress a string into a [`Vec`]. /// /// The resulting [`Vec`] may contain invalid UTF16. #[inline] pub fn compress(data: impl IntoWideIter) -> Vec { let data: Vec = data.into_wide_iter().collect(); compress_internal(&data, 16, std::convert::identity) } /// Compress a string as a valid [`String`]. /// /// This function converts the result back into a Rust [`String`] since it is guaranteed to be valid UTF16. #[inline] pub fn compress_to_utf16(data: impl IntoWideIter) -> String { let data: Vec = data.into_wide_iter().collect(); let compressed = compress_internal(&data, 15, |n| n + 32); let mut compressed = String::from_utf16(&compressed).expect("`compress_to_utf16 output was not valid unicode`"); compressed.push(' '); compressed } /// Compress a string into a [`String`], which can be safely used in a uri. /// /// This function converts the result back into a Rust [`String`] since it is guaranteed to be valid unicode. #[inline] pub fn compress_to_encoded_uri_component(data: impl IntoWideIter) -> String { let data: Vec = data.into_wide_iter().collect(); let compressed = compress_internal(&data, 6, |n| u16::from(URI_KEY[usize::from(n)])); String::from_utf16(&compressed) .expect("`compress_to_encoded_uri_component` output was not valid unicode`") } /// Compress a string into a [`String`], which is valid base64. /// /// This function converts the result back into a Rust [`String`] since it is guaranteed to be valid unicode. pub fn compress_to_base64(data: impl IntoWideIter) -> String { let data: Vec = data.into_wide_iter().collect(); let mut compressed = compress_internal(&data, 6, |n| u16::from(BASE64_KEY[usize::from(n)])); let mod_4 = compressed.len() % 4; if mod_4 != 0 { for _ in mod_4..(4 + 1) { compressed.push(u16::from(b'=')); } } String::from_utf16(&compressed).expect("`compress_to_base64` output was not valid unicode`") } /// Compress a string into a [`Vec`]. pub fn compress_to_uint8_array(data: impl IntoWideIter) -> Vec { compress(data) .into_iter() .flat_map(|value| value.to_be_bytes()) .collect() } /// The internal function for compressing data. /// /// All other compression functions are built on top of this. /// It generally should not be used directly. #[inline] pub fn compress_internal(data: &[u16], bits_per_char: u8, to_char: F) -> Vec where F: Fn(u16) -> u16, { let ctx = CompressContext::new(data, bits_per_char, to_char); ctx.compress() } lz-str-0.2.1/src/constants.rs000064400000000000000000000010550072674642500142610ustar 00000000000000pub const URI_KEY: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$"; pub const BASE64_KEY: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; /// The stream code for a `u8`. pub const U8_CODE: u8 = 0; /// The stream code for a `u16`. pub const U16_CODE: u8 = 1; /// End of stream signal pub const CLOSE_CODE: u8 = 2; /// The starting size of a code. /// /// Compression starts with the following codes: /// 0: u8 /// 1: u16 /// 2: close stream pub const START_CODE_BITS: u8 = 2; lz-str-0.2.1/src/decompress.rs000064400000000000000000000173310072674642500144150ustar 00000000000000use crate::constants::BASE64_KEY; use crate::constants::CLOSE_CODE; use crate::constants::START_CODE_BITS; use crate::constants::U16_CODE; use crate::constants::U8_CODE; use crate::constants::URI_KEY; use crate::IntoWideIter; use std::convert::TryFrom; use std::convert::TryInto; #[derive(Debug)] pub struct DecompressContext { val: u16, compressed_data: I, position: u16, reset_val: u16, } impl DecompressContext where I: Iterator, { /// Make a new [`DecompressContext`]. /// /// # Errors /// Returns `None` if the iterator is empty. /// /// # Panics /// Panics if `bits_per_char` is greater than the number of bits in a `u16`. #[inline] pub fn new(mut compressed_data: I, bits_per_char: u8) -> Option { assert!(usize::from(bits_per_char) <= std::mem::size_of::() * 8); let reset_val_pow = bits_per_char - 1; // (1 << 15) <= u16::MAX let reset_val: u16 = 1 << reset_val_pow; Some(DecompressContext { val: compressed_data.next()?, compressed_data, position: reset_val, reset_val, }) } #[inline] pub fn read_bit(&mut self) -> Option { let res = self.val & self.position; self.position >>= 1; if self.position == 0 { self.position = self.reset_val; self.val = self.compressed_data.next()?; } Some(res != 0) } /// Read n bits. /// /// `u32` is the return type as we expect all possible codes to be within that type's range. #[inline] pub fn read_bits(&mut self, n: u8) -> Option { let mut res = 0; let max_power: u32 = 1 << n; let mut power: u32 = 1; while power != max_power { res |= u32::from(self.read_bit()?) * power; power <<= 1; } Some(res) } } /// Decompress a string into a [`Vec`]. /// The result contains possibly invalid UTF16. /// /// # Errors /// Returns `None` if the decompression fails. #[inline] pub fn decompress(compressed: impl IntoWideIter) -> Option> { decompress_internal(compressed.into_wide_iter(), 16) } /// Decompress a [`&str`] compressed with [`crate::compress_to_utf16`]. /// /// # Errors /// Returns an error if the compressed data could not be decompressed. #[inline] pub fn decompress_from_utf16(compressed: &str) -> Option> { decompress_internal(compressed.encode_utf16().map(|c| c - 32), 15) } /// Decompress a [`&str`] compressed with [`crate::compress_to_encoded_uri_component`]. /// /// # Errors /// Returns an error if the compressed data could not be decompressed. #[inline] pub fn decompress_from_encoded_uri_component(compressed: &str) -> Option> { let compressed: Option> = compressed .encode_utf16() .map(|c| { if c == u16::from(b' ') { u16::from(b'+') } else { c } }) .map(u32::from) .flat_map(|c| { URI_KEY .iter() .position(|k| u8::try_from(c) == Ok(*k)) .map(|n| u16::try_from(n).ok()) }) .collect(); decompress_internal(compressed?.into_iter(), 6) } /// Decompress a [`&str`] compressed with [`crate::compress_to_base64`]. /// /// # Errors /// Returns an error if the compressed data could not be decompressed. #[inline] pub fn decompress_from_base64(compressed: &str) -> Option> { let compressed: Option> = compressed .encode_utf16() .flat_map(|c| { BASE64_KEY .iter() .position(|k| u8::try_from(c) == Ok(*k)) .map(|n| u16::try_from(n).ok()) }) .collect(); decompress_internal(compressed?.into_iter(), 6) } /// Decompress a byte slice compressed with [`crate::compress_to_uint8_array`]. /// /// # Errors /// Returns an error if the compressed data could not be decompressed. #[inline] pub fn decompress_from_uint8_array(compressed: &[u8]) -> Option> { // The buffer is a UCS2 big endian encoded string. // If it is not a multiple of 2, it is invalid. let compressed_len = compressed.len(); if compressed_len & 1 == 1 { return None; } let buffer: Vec = compressed .chunks(2) .map(|slice| { // The slice is always guaranteed to be 2 here. // We check to see if the length is a multiple of 2 earlier. u16::from_be_bytes(slice.try_into().unwrap()) }) .collect(); decompress(&buffer) } /// The internal decompress function. /// /// All other decompress functions are built on top of this one. /// It generally should not be used directly. /// /// # Errors /// Returns an error if the compressed data could not be decompressed. /// /// # Panics /// Panics if `bits_per_char` is greater than the number of bits in a `u16`. #[inline] pub fn decompress_internal(compressed: I, bits_per_char: u8) -> Option> where I: Iterator, { let mut ctx = match DecompressContext::new(compressed, bits_per_char) { Some(ctx) => ctx, None => return Some(Vec::new()), }; let mut dictionary: Vec> = Vec::with_capacity(16); for i in 0_u16..3_u16 { dictionary.push(vec![i]); } // u8::MAX > u2::MAX let code = u8::try_from(ctx.read_bits(START_CODE_BITS)?).unwrap(); let first_entry = match code { U8_CODE | U16_CODE => { let bits_to_read = (code * 8) + 8; // bits_to_read == 8 or 16 <= 16 u16::try_from(ctx.read_bits(bits_to_read)?).unwrap() } CLOSE_CODE => return Some(Vec::new()), _ => return None, }; dictionary.push(vec![first_entry]); let mut w = vec![first_entry]; let mut result = vec![first_entry]; let mut num_bits: u8 = 3; let mut enlarge_in: u64 = 4; let mut entry; loop { let mut code = ctx.read_bits(num_bits)?; match u8::try_from(code) { Ok(code_u8 @ (U8_CODE | U16_CODE)) => { let bits_to_read = (code_u8 * 8) + 8; // if cc == 0 { // if (errorCount++ > 10000) return "Error"; // TODO: Error logic // } // bits_to_read == 8 or 16 <= 16 let bits = u16::try_from(ctx.read_bits(bits_to_read)?).unwrap(); dictionary.push(vec![bits]); code = u32::try_from(dictionary.len() - 1).ok()?; enlarge_in -= 1; } Ok(CLOSE_CODE) => return Some(result), _ => {} } if enlarge_in == 0 { enlarge_in = 1 << num_bits; num_bits += 1; } // Return error if code cannot be converted to dictionary index let code_usize = usize::try_from(code).ok()?; if let Some(entry_value) = dictionary.get(code_usize) { entry = entry_value.clone(); } else if code_usize == dictionary.len() { entry = w.clone(); entry.push(*w.first()?); } else { return None; } result.extend(&entry); // Add w+entry[0] to the dictionary. let mut to_be_inserted = w.clone(); to_be_inserted.push(*entry.first()?); dictionary.push(to_be_inserted); enlarge_in -= 1; w = entry; if enlarge_in == 0 { enlarge_in = 1 << num_bits; num_bits += 1; } } } lz-str-0.2.1/src/lib.rs000064400000000000000000000126150072674642500130170ustar 00000000000000#![forbid(unsafe_code)] #![deny(missing_docs)] #![warn(clippy::cast_lossless)] #![warn(clippy::cast_possible_wrap)] #![warn(clippy::cast_possible_truncation)] //! A port of [lz-string](https://github.com/pieroxy/lz-string) to Rust. //! //! //! # Example //! ```rust //! # // The demonstrated functions correspond with `LZString.compress` and `LZString.decompress` from the JS version. //! # fn main() { //! let data = "The quick brown fox jumps over the lazy dog"; //! //! // Compress the data. This cannot fail. //! let compressed_data = lz_str::compress(data); //! //! // Decompress the data. //! // This may return `Option::None` if it fails. //! // Make sure to do error-checking in a real application to prevent crashes! //! let decompressed_data = //! lz_str::decompress(compressed_data).expect("`compressed_data` is invalid"); //! //! // The decompressed_data should be the same as data, except encoded as UTF16. //! // We undo that here. //! // In a real application, //! // you will want to do error checking to prevent users from causing crashes with invalid data. //! let decompressed_data = //! String::from_utf16(&decompressed_data).expect("`decompressed_data` is not valid UTF16"); //! //! assert!(data == decompressed_data); //! # } //! ``` //! //! # Passing and Recieving Data //! The original library uses invalid UTF16 strings to represent data. //! To maintain compatability, this library uses a [`Vec`] of [`u16`]s instead of Rust strings where applicable. //! The [`IntoWideIter`] trait exists to ease the passing of data into functions. //! Most functions accept this generic parameter instead of a concrete type. //! Look at this trait's documentation to see what types this trait is implemented for. mod compress; mod constants; mod decompress; pub use crate::compress::compress; pub use crate::compress::compress_internal; pub use crate::compress::compress_to_base64; pub use crate::compress::compress_to_encoded_uri_component; pub use crate::compress::compress_to_uint8_array; pub use crate::compress::compress_to_utf16; pub use crate::decompress::decompress; pub use crate::decompress::decompress_from_base64; pub use crate::decompress::decompress_from_encoded_uri_component; pub use crate::decompress::decompress_from_uint8_array; pub use crate::decompress::decompress_from_utf16; pub use crate::decompress::decompress_internal; /// A trait to make it easier to pass arguments to functions. pub trait IntoWideIter { /// The Iterator type type Iter: Iterator; /// Convert this object into something that yields possibly invalid wide characters. fn into_wide_iter(self) -> Self::Iter; } impl<'a> IntoWideIter for &'a str { type Iter = std::str::EncodeUtf16<'a>; #[inline] fn into_wide_iter(self) -> Self::Iter { self.encode_utf16() } } impl<'a> IntoWideIter for &&'a str { type Iter = std::str::EncodeUtf16<'a>; #[inline] fn into_wide_iter(self) -> Self::Iter { self.encode_utf16() } } impl<'a> IntoWideIter for &'a String { type Iter = std::str::EncodeUtf16<'a>; #[inline] fn into_wide_iter(self) -> Self::Iter { self.as_str().encode_utf16() } } impl<'a> IntoWideIter for &'a [u16] { type Iter = std::iter::Copied>; #[inline] fn into_wide_iter(self) -> Self::Iter { self.iter().copied() } } // TODO: Remove this in the next version. // We do not benefit from taking ownership of the buffer. impl IntoWideIter for Vec { type Iter = std::vec::IntoIter; #[inline] fn into_wide_iter(self) -> Self::Iter { self.into_iter() } } impl<'a> IntoWideIter for &'a Vec { type Iter = std::iter::Copied>; #[inline] fn into_wide_iter(self) -> Self::Iter { self.iter().copied() } } #[cfg(test)] mod test { use super::*; #[test] fn into_wide_iter_check() { const DATA: &str = "test argument"; let expected: Vec = DATA.encode_utf16().collect(); fn check(arg: impl IntoWideIter, expected: &[u16]) { let arg: Vec = arg.into_wide_iter().collect(); assert!(arg == expected); } { let data: &str = DATA; check(data, &expected); } { let data: &&str = &DATA; check(data, &expected); } // TODO: Should IntoWideIter be implemented for String? // It's always better to pass an &str or an &String, so users should be forced to do that? // { // let data: String = DATA.into(); // check(data, &expected); // } { let data: String = DATA.into(); let data: &String = &data; check(data, &expected); } { let data: Vec = DATA.encode_utf16().collect(); let data: &[u16] = &data; check(data, &expected); } { let data: Vec = DATA.encode_utf16().collect(); check(data, &expected); } { let data: Vec = DATA.encode_utf16().collect(); let data: &Vec = &data; check(data, &expected); } } } lz-str-0.2.1/tests/invalid_inputs.rs000064400000000000000000000005110072674642500156440ustar 00000000000000use lz_str::decompress; #[test] fn invalid_decompress() { let invalid_data = &["bed123", "zed123", "ed[[[[d1d[[[[dF9]"]; for data in invalid_data { eprintln!("Decompressing '{}'", data); let arr: Vec = data.encode_utf16().collect(); assert!(decompress(&arr).is_none()); } } lz-str-0.2.1/tests/lz_string_spec_port.rs000064400000000000000000001230020072674642500167060ustar 00000000000000//! These tests were ported from https://github.com/pieroxy/lz-string/blob/83d7b59ebef47edc4cf0527bc03179b86e064f23/tests/lz-string-spec.js use rand::Rng; use std::{fmt::Write, string::FromUtf16Error}; fn compression_tests(compress: C, decompress: D, bytearray: bool) where C: Fn(&[u16]) -> ByteString, D: Fn(ByteString) -> Option, { eprintln!("Check if it compresses and decompresses \"Hello world!\""); let compressed = compress(&"Hello world!".encode_utf16().collect::>()); assert_ne!(compressed, "Hello world!"); let decompressed = decompress(compressed).expect("Valid Decompress"); assert_eq!(decompressed, "Hello world!"); /* it('compresses and decompresses null', function() { var compressed = compress(null); if (uint8array_mode===false){ expect(compressed).toBe(''); expect(typeof compressed).toBe(typeof ''); } else { //uint8array expect(compressed instanceof Uint8Array).toBe(true); expect(compressed.length).toBe(0); //empty array } var decompressed = decompress(compressed); expect(decompressed).toBe(null); }); */ /* it('compresses and decompresses undefined', function() { var compressed = compress(); if (uint8array_mode===false){ expect(compressed).toBe(''); expect(typeof compressed).toBe(typeof ''); } else { //uint8array expect(compressed instanceof Uint8Array).toBe(true); expect(compressed.length).toBe(0); //empty array } var decompressed = decompress(compressed); expect(decompressed).toBe(null); }); */ /* it('decompresses null', function() { var decompressed = decompress(null); expect(decompressed).toBe(''); }); */ eprintln!("Check if it compresses and decompresses an empty string"); let compressed = compress(&"".encode_utf16().collect::>()); if !bytearray { assert_ne!(compressed, ""); // expect(typeof compressed).toBe(typeof ''); } else { // expect(compressed instanceof Uint8Array).toBe(true); assert!(!compressed.is_empty()); } let decompressed = decompress(compressed).expect("Valid Decompress"); assert_eq!(decompressed, ""); eprintln!("Check if it compresses and decompresses all printable UTF-16 characters"); let mut test_string = String::new(); for i in 32..127 { test_string.push(std::char::from_u32(i).expect("Valid Char")); } for i in (128 + 32)..55296 { test_string.push(std::char::from_u32(i).expect("Valid Char")); } for i in 63744..65536 { test_string.push(std::char::from_u32(i).expect("Valid Char")); } let compressed = compress(&test_string.encode_utf16().collect::>()); assert_ne!(compressed, test_string.as_str()); let decompressed = decompress(compressed).expect("Valid Decompress"); assert_eq!(decompressed, test_string.as_str()); eprintln!("Check if it compresses and decompresses a string that repeats"); let test_string = "aaaaabaaaaacaaaaadaaaaaeaaaaa"; let compressed = compress(&test_string.encode_utf16().collect::>()); assert_ne!(compressed, test_string); assert!(compressed.len() < test_string.len()); let decompressed = decompress(compressed).expect("Valid Decompress"); assert_eq!(decompressed, test_string); eprintln!("Check if it compresses and decompresses a long string"); let mut test_string = String::new(); for _ in 0..1000 { write!(&mut test_string, "{} ", rand::thread_rng().gen::()) .expect("write rand float to string") } let compressed = compress(&test_string.encode_utf16().collect::>()); assert_ne!(compressed, test_string.as_str()); assert!(compressed.len() < test_string.len()); let decompressed = decompress(compressed).expect("Valid Decompress"); assert_eq!(decompressed, test_string.as_str()); } #[test] fn lz_string_base_64() { compression_tests( |s| lz_str::compress_to_base64(s).into(), |s| { lz_str::decompress_from_base64(&s.to_utf8_string().expect("Valid UTF16 String")) .map(ByteString::from) }, false, ); } #[test] fn lz_string_utf_16() { compression_tests( |s| ByteString::from(lz_str::compress_to_utf16(s)), |s| { lz_str::decompress_from_utf16(&s.to_utf8_string().expect("Valid UTF16 String")) .map(ByteString::from) }, false, ); } #[test] fn lz_string_uri_encoded() { compression_tests( |s| lz_str::compress_to_encoded_uri_component(s).into(), |s| { lz_str::decompress_from_encoded_uri_component( &s.to_utf8_string().expect("Valid UTF16 String"), ) .map(ByteString::from) }, false, ); } #[test] fn lz_string_uint8_array() { compression_tests( |s| { lz_str::compress_to_uint8_array(s) .into_iter() .map(u16::from) .collect::>() .into() }, |s| { lz_str::decompress_from_uint8_array( &s.0.into_iter().map(|el| el as u8).collect::>(), ) .map(ByteString::from) }, true, ); } #[test] fn lz_string_raw() { compression_tests( |s| lz_str::compress(s).into(), |s| lz_str::decompress(&s.0).map(ByteString::from), false, ); } #[test] fn specific_url_encoded() { eprintln!("check that all chars are URL safe"); let mut test_string = String::new(); for _ in 0..1000 { write!(&mut test_string, "{} ", rand::thread_rng().gen::()) .expect("write rand float to string") } let compressed = lz_str::compress_to_encoded_uri_component( &test_string.encode_utf16().collect::>(), ); assert!(!compressed.contains('=')); assert!(!compressed.contains('/')); let decompressed = lz_str::decompress_from_encoded_uri_component(&compressed).expect("Valid Decompress"); assert_eq!( decompressed, test_string.encode_utf16().collect::>() ); eprintln!("check that + and ' ' are interchangeable in decompression"); let decompressed = "During tattooing, ink is injected into the skin, initiating an immune response, and cells called \"macrophages\" move into the area and \"eat up\" the ink. The macrophages carry some of the ink to the body\'s lymph nodes, but some that are filled with ink stay put, embedded in the skin. That\'s what makes the tattoo visible under the skin. Dalhousie Uiversity\'s Alec Falkenham is developing a topical cream that works by targeting the macrophages that have remained at the site of the tattoo. New macrophages move in to consume the previously pigment-filled macrophages and then migrate to the lymph nodes, eventually taking all the dye with them. \"When comparing it to laser-based tattoo removal, in which you see the burns, the scarring, the blisters, in this case, we\'ve designed a drug that doesn\'t really have much off-target effect,\" he said. \"We\'re not targeting any of the normal skin cells, so you won\'t see a lot of inflammation. In fact, based on the process that we\'re actually using, we don\'t think there will be any inflammation at all and it would actually be anti-inflammatory."; let compressed = "CIVwTglgdg5gBAFwIYIQezdGAaO0DWeAznlAFYCmAxghQCanqIAWFcR 0u0ECEKWOEih4AtqJBQ2YCkQAOaKEQq5hDKhQA2mklSTb6cAESikVMGjnMkMWUbii0ANzbQmCVkJlIhUBkYoUOBA5ew9XKHwAOjgAFU9Tc0trW10kMDAAT3Y0UTY0ADMWCMJ3TwAjNDpMgHISTUzRKzgoKtlccpAEHLyWIPS2AogDBgB3XmZSQiJkbLku3ApRcvo6Q2hi9k4oGPiUOrhR627TfFlN5FQMOCcIIghyzTZJNbBNjmgY4H1mNBB7tgAVQgLjA9wQtRIAEEnlQ4AAxfRnKDWUTEOBrFyaSyCHzoOQQPSaODmQJojxBUZoMD4EjlbLIMC2PiwTaJCxWGznCndawuOAyUzQQxBcLsXj5Ipiy7oNAxAByFFGDjMHJS50c-I2TCoiiIIF6YrkMlufyIDTgBJgeSgCAAtEMRiqkpzUr4GOERKIIDAwCg2GU2A0mpNWmsiIsXLaQPoLchtvBY5tqmxxh5iqIYkYAOqsES6prpQS8RBoOCaJDKMB28qVwwy66C5z6bgiI6EyaZP7sCgBirgJS4MVEPQZLBDiqaO60MGtlh3El13CjCg1fnhn1SBg OhgEDwHkYtCyKA1brebTZPlsCRUSaFAp2xnMuAUAoFagIbD2TxEJAQOgs2zVcZBaNBumfCgWUTKBskKTZWjAUxiQ fMtB0XAiDLLsQEORQzx7NgfGxbp4OgAoK3EARFBiABJEQCjML84FrZQGEUTZjTQDQiBIQ8VxqUCmJjS9gnuWBlzYOh8Ig5gCGKUDxm0FiiNg0gKKQKi A4-plLUPBuipEBNG3GgRItFZfD4O1yMo0x0CyKIgAAA$$"; let decomp2 = lz_str::decompress_from_encoded_uri_component(compressed).expect("Valid Decompress"); assert_eq!(decompressed.encode_utf16().collect::>(), decomp2); } fn test_enc_bin_compat(comp: C, expected_dec: I, expected_comp: O) where C: Fn(I) -> O, O: std::fmt::Debug + std::cmp::PartialEq, { let compressed = comp(expected_dec); assert_eq!(compressed, expected_comp); } fn test_dec_bin_compat(dec: D, expected_dec: &[u16], expected_comp: I) where D: Fn(I) -> Option>, { let decompressed = dec(expected_comp).expect("Valid Decompress"); assert_eq!(decompressed, expected_dec); } #[test] fn binary_encoding_compatibility_tests_optional() { eprintln!("base64"); let base64_decompressed = "During tattooing, ink is injected into the skin, initiating an immune response, and cells called \"macrophages\" move into the area and \"eat up\" the ink. The macrophages carry some of the ink to the body\'s lymph nodes, but some that are filled with ink stay put, embedded in the skin. That\'s what makes the tattoo visible under the skin. Dalhousie Uiversity\'s Alec Falkenham is developing a topical cream that works by targeting the macrophages that have remained at the site of the tattoo. New macrophages move in to consume the previously pigment-filled macrophages and then migrate to the lymph nodes, eventually taking all the dye with them. \"When comparing it to laser-based tattoo removal, in which you see the burns, the scarring, the blisters, in this case, we\'ve designed a drug that doesn\'t really have much off-target effect,\" he said. \"We\'re not targeting any of the normal skin cells, so you won\'t see a lot of inflammation. In fact, based on the process that we\'re actually using, we don\'t think there will be any inflammation at all and it would actually be anti-inflammatory."; let base64_compressed = "CIVwTglgdg5gBAFwIYIQezdGAaO0DWeAznlAFYCmAxghQCanqIAWFcR+0u0ECEKWOEih4AtqJBQ2YCkQAOaKEQq5hDKhQA2mklSTb6cAESikVMGjnMkMWUbii0ANzbQmCVkJlIhUBkYoUOBA5ew9XKHwAOjgAFU9Tc0trW10kMDAAT3Y0UTY0ADMWCMJ3TwAjNDpMgHISTUzRKzgoKtlccpAEHLyWIPS2AogDBgB3XmZSQiJkbLku3ApRcvo6Q2hi9k4oGPiUOrhR627TfFlN5FQMOCcIIghyzTZJNbBNjmgY4H1mNBB7tgAVQgLjA9wQtRIAEEnlQ4AAxfRnKDWUTEOBrFyaSyCHzoOQQPSaODmQJojxBUZoMD4EjlbLIMC2PiwTaJCxWGznCndawuOAyUzQQxBcLsXj5Ipiy7oNAxAByFFGDjMHJS50c/I2TCoiiIIF6YrkMlufyIDTgBJgeSgCAAtEMRiqkpzUr4GOERKIIDAwCg2GU2A0mpNWmsiIsXLaQPoLchtvBY5tqmxxh5iqIYkYAOqsES6prpQS8RBoOCaJDKMB28qVwwy66C5z6bgiI6EyaZP7sCgBirgJS4MVEPQZLBDiqaO60MGtlh3El13CjCg1fnhn1SBg+OhgEDwHkYtCyKA1brebTZPlsCRUSaFAp2xnMuAUAoFagIbD2TxEJAQOgs2zVcZBaNBumfCgWUTKBskKTZWjAUxiQ+fMtB0XAiDLLsQEORQzx7NgfGxbp4OgAoK3EARFBiABJEQCjML84FrZQGEUTZjTQDQiBIQ8VxqUCmJjS9gnuWBlzYOh8Ig5gCGKUDxm0FiiNg0gKKQKi+A4/plLUPBuipEBNG3GgRItFZfD4O1yMo0x0CyKIgA"; test_enc_bin_compat( lz_str::compress_to_base64, &base64_decompressed.encode_utf16().collect::>(), base64_compressed.to_string(), ); eprintln!("uriEncoding"); let uri_encoding_decompressed = "During tattooing, ink is injected into the skin, initiating an immune response, and cells called \"macrophages\" move into the area and \"eat up\" the ink. The macrophages carry some of the ink to the body\'s lymph nodes, but some that are filled with ink stay put, embedded in the skin. That\'s what makes the tattoo visible under the skin. Dalhousie Uiversity\'s Alec Falkenham is developing a topical cream that works by targeting the macrophages that have remained at the site of the tattoo. New macrophages move in to consume the previously pigment-filled macrophages and then migrate to the lymph nodes, eventually taking all the dye with them. \"When comparing it to laser-based tattoo removal, in which you see the burns, the scarring, the blisters, in this case, we\'ve designed a drug that doesn\'t really have much off-target effect,\" he said. \"We\'re not targeting any of the normal skin cells, so you won\'t see a lot of inflammation. In fact, based on the process that we\'re actually using, we don\'t think there will be any inflammation at all and it would actually be anti-inflammatory."; let uri_encoding_compressed = "CIVwTglgdg5gBAFwIYIQezdGAaO0DWeAznlAFYCmAxghQCanqIAWFcR+0u0ECEKWOEih4AtqJBQ2YCkQAOaKEQq5hDKhQA2mklSTb6cAESikVMGjnMkMWUbii0ANzbQmCVkJlIhUBkYoUOBA5ew9XKHwAOjgAFU9Tc0trW10kMDAAT3Y0UTY0ADMWCMJ3TwAjNDpMgHISTUzRKzgoKtlccpAEHLyWIPS2AogDBgB3XmZSQiJkbLku3ApRcvo6Q2hi9k4oGPiUOrhR627TfFlN5FQMOCcIIghyzTZJNbBNjmgY4H1mNBB7tgAVQgLjA9wQtRIAEEnlQ4AAxfRnKDWUTEOBrFyaSyCHzoOQQPSaODmQJojxBUZoMD4EjlbLIMC2PiwTaJCxWGznCndawuOAyUzQQxBcLsXj5Ipiy7oNAxAByFFGDjMHJS50c-I2TCoiiIIF6YrkMlufyIDTgBJgeSgCAAtEMRiqkpzUr4GOERKIIDAwCg2GU2A0mpNWmsiIsXLaQPoLchtvBY5tqmxxh5iqIYkYAOqsES6prpQS8RBoOCaJDKMB28qVwwy66C5z6bgiI6EyaZP7sCgBirgJS4MVEPQZLBDiqaO60MGtlh3El13CjCg1fnhn1SBg+OhgEDwHkYtCyKA1brebTZPlsCRUSaFAp2xnMuAUAoFagIbD2TxEJAQOgs2zVcZBaNBumfCgWUTKBskKTZWjAUxiQ+fMtB0XAiDLLsQEORQzx7NgfGxbp4OgAoK3EARFBiABJEQCjML84FrZQGEUTZjTQDQiBIQ8VxqUCmJjS9gnuWBlzYOh8Ig5gCGKUDxm0FiiNg0gKKQKi+A4-plLUPBuipEBNG3GgRItFZfD4O1yMo0x0CyKIgA"; test_enc_bin_compat( lz_str::compress_to_encoded_uri_component, &uri_encoding_decompressed .encode_utf16() .collect::>(), uri_encoding_compressed.into(), ); eprintln!("UInt8Array"); let uint8_array_decompressed = "During tattooing, ink is injected into the skin, initiating an immune response, and cells called \"macrophages\" move into the area and \"eat up\" the ink. The macrophages carry some of the ink to the body\'s lymph nodes, but some that are filled with ink stay put, embedded in the skin. That\'s what makes the tattoo visible under the skin. Dalhousie Uiversity\'s Alec Falkenham is developing a topical cream that works by targeting the macrophages that have remained at the site of the tattoo. New macrophages move in to consume the previously pigment-filled macrophages and then migrate to the lymph nodes, eventually taking all the dye with them. \"When comparing it to laser-based tattoo removal, in which you see the burns, the scarring, the blisters, in this case, we\'ve designed a drug that doesn\'t really have much off-target effect,\" he said. \"We\'re not targeting any of the normal skin cells, so you won\'t see a lot of inflammation. In fact, based on the process that we\'re actually using, we don\'t think there will be any inflammation at all and it would actually be anti-inflammatory."; let uint8_array_compressed = &[ 8, 133, 112, 78, 9, 96, 118, 14, 96, 4, 1, 112, 33, 130, 16, 123, 55, 70, 1, 163, 180, 13, 103, 128, 206, 121, 64, 21, 128, 166, 3, 24, 33, 64, 38, 167, 168, 128, 22, 21, 196, 126, 210, 237, 4, 8, 66, 150, 56, 72, 161, 224, 11, 106, 36, 20, 54, 96, 41, 16, 0, 230, 138, 17, 10, 185, 132, 50, 161, 64, 13, 166, 146, 84, 147, 111, 167, 0, 17, 40, 164, 84, 193, 163, 156, 201, 12, 89, 70, 226, 139, 64, 13, 205, 180, 38, 9, 89, 9, 148, 136, 84, 6, 70, 40, 80, 224, 64, 229, 236, 61, 92, 161, 240, 0, 232, 224, 0, 85, 61, 77, 205, 45, 173, 109, 116, 144, 192, 192, 1, 61, 216, 209, 68, 216, 208, 0, 204, 88, 35, 9, 221, 60, 0, 140, 208, 233, 50, 1, 200, 73, 53, 51, 68, 172, 224, 160, 171, 101, 113, 202, 64, 16, 114, 242, 88, 131, 210, 216, 10, 32, 12, 24, 1, 221, 121, 153, 73, 8, 137, 145, 178, 228, 187, 112, 41, 69, 203, 232, 233, 13, 161, 139, 217, 56, 160, 99, 226, 80, 234, 225, 71, 173, 187, 77, 241, 101, 55, 145, 80, 48, 224, 156, 32, 136, 33, 203, 52, 217, 36, 214, 193, 54, 57, 160, 99, 129, 245, 152, 208, 65, 238, 216, 0, 85, 8, 11, 140, 15, 112, 66, 212, 72, 0, 65, 39, 149, 14, 0, 3, 23, 209, 156, 160, 214, 81, 49, 14, 6, 177, 114, 105, 44, 130, 31, 58, 14, 65, 3, 210, 104, 224, 230, 64, 154, 35, 196, 21, 25, 160, 192, 248, 18, 57, 91, 44, 131, 2, 216, 248, 176, 77, 162, 66, 197, 97, 179, 156, 41, 221, 107, 11, 142, 3, 37, 51, 65, 12, 65, 112, 187, 23, 143, 146, 41, 139, 46, 232, 52, 12, 64, 7, 33, 69, 24, 56, 204, 28, 148, 185, 209, 207, 200, 217, 48, 168, 138, 34, 8, 23, 166, 43, 144, 201, 110, 127, 34, 3, 78, 0, 73, 129, 228, 160, 8, 0, 45, 16, 196, 98, 170, 74, 115, 82, 190, 6, 56, 68, 74, 32, 128, 192, 192, 40, 54, 25, 77, 128, 210, 106, 77, 90, 107, 34, 34, 197, 203, 105, 3, 232, 45, 200, 109, 188, 22, 57, 182, 169, 177, 198, 30, 98, 168, 134, 36, 96, 3, 170, 176, 68, 186, 166, 186, 80, 75, 196, 65, 160, 224, 154, 36, 50, 140, 7, 111, 42, 87, 12, 50, 235, 160, 185, 207, 166, 224, 136, 142, 132, 201, 166, 79, 238, 192, 160, 6, 42, 224, 37, 46, 12, 84, 67, 208, 100, 176, 67, 138, 166, 142, 235, 67, 6, 182, 88, 119, 18, 93, 119, 10, 48, 160, 213, 249, 225, 159, 84, 129, 131, 227, 161, 128, 64, 240, 30, 70, 45, 11, 34, 128, 213, 186, 222, 109, 54, 79, 150, 192, 145, 81, 38, 133, 2, 157, 177, 156, 203, 128, 80, 10, 5, 106, 2, 27, 15, 100, 241, 16, 144, 16, 58, 11, 54, 205, 87, 25, 5, 163, 65, 186, 103, 194, 129, 101, 19, 40, 27, 36, 41, 54, 86, 140, 5, 49, 137, 15, 159, 50, 208, 116, 92, 8, 131, 44, 187, 16, 16, 228, 80, 207, 30, 205, 129, 241, 177, 110, 158, 14, 128, 10, 10, 220, 64, 17, 20, 24, 128, 4, 145, 16, 10, 51, 11, 243, 129, 107, 101, 1, 132, 81, 54, 99, 77, 0, 208, 136, 18, 16, 241, 92, 106, 80, 41, 137, 141, 47, 96, 158, 229, 129, 151, 54, 14, 135, 194, 32, 230, 0, 134, 41, 64, 241, 155, 65, 98, 136, 216, 52, 128, 162, 144, 42, 47, 128, 227, 250, 101, 45, 67, 193, 186, 42, 68, 4, 209, 183, 26, 4, 72, 180, 86, 95, 15, 131, 181, 200, 202, 52, 199, 64, 178, 40, 136, 0, 0, ]; test_enc_bin_compat( lz_str::compress_to_uint8_array, &uint8_array_decompressed .encode_utf16() .collect::>(), uint8_array_compressed.to_vec(), ); eprintln!("UTF16"); let utf16_decompressed = "During tattooing, ink is injected into the skin, initiating an immune response, and cells called \"macrophages\" move into the area and \"eat up\" the ink. The macrophages carry some of the ink to the body\'s lymph nodes, but some that are filled with ink stay put, embedded in the skin. That\'s what makes the tattoo visible under the skin. Dalhousie Uiversity\'s Alec Falkenham is developing a topical cream that works by targeting the macrophages that have remained at the site of the tattoo. New macrophages move in to consume the previously pigment-filled macrophages and then migrate to the lymph nodes, eventually taking all the dye with them. \"When comparing it to laser-based tattoo removal, in which you see the burns, the scarring, the blisters, in this case, we\'ve designed a drug that doesn\'t really have much off-target effect,\" he said. \"We\'re not targeting any of the normal skin cells, so you won\'t see a lot of inflammation. In fact, based on the process that we\'re actually using, we don\'t think there will be any inflammation at all and it would actually be anti-inflammatory."; let utf16_compressed = "\u{0462}\u{5C33}\u{414C}\u{0780}\u{7320}\u{1025}\u{6063}\u{0230}\u{3DBB}\u{51A0}\u{3496}\u{40F6}\u{3C26}\u{3A05}K\u{00C6}\u{01AC}\u{0870}\u{04F4}\u{7AA8}\u{00D0}\u{5731}\u{7DC5}\u{6D24}\u{0441}\u{25AE}\u{0934}\u{1E20}\u{5B71}\u{1070}\u{6CE0}\u{2930}\u{0093}\u{22A4}\u{2177}\u{1863}\u{152A}V\u{4D44}\u{54B3}\u{37F3}\u{4024}\u{2534}\u{456C}\u{0D3C}\u{7344}\u{18D2}\u{4702}\u{45C0}\u{0393}\u{36A4}\u{60B5}\u{486C}\u{5241}\u{282C}\u{4648}\u{2890}\u{1059}\u{3DA7}\u{55EA}\u{0FA0}\u{03C3}\u{4020}\u{555D}\u{2706}\u{4B8B}\u{2DCE}\u{492C}\u{0620}\u{0517}\u{31C2}\u{44F8}\u{6820}\u{3336}\u{0481}\u{1DF3}\u{6024}\u{3363}\u{5284}\u{01E8}\u{24BA}\u{4CF1}\u{15BC}\u{0A2A}\u{5B4B}\u{4749}@\u{7312}\u{2C61}\u{74D6}\u{0164}\u{00E1}\u{402E}\u{7606}\u{32B2}\u{08A9}\u{48F9}\u{394E}\u{6E25}\u{147C}\u{5F67}\u{2456}\u{4337}\u{5958}\u{5051}\u{78B4}\u{1D7C}\u{149A}\u{6DFA}\u{37E5}\u{4A8F}\u{1170}\u{1890}\u{2728}\u{1124}\u{1CD3}\u{26E9}\u{137B}\u{028C}\u{39C0}\u{31E0}\u{7D86}\u{1A28}\u{1F0D}\u{4022}\u{5440}\u{1738}\u{0F90}\u{218A}\u{1220}\u{0844}\u{7970}\u{7020}\u{0C7F}\u{2359}\u{20F6}\u{28B8}\u{43A1}\u{564E}\u{26B2}\u{6430}\u{7D08}\u{1CA2}\u{03F2}\u{3490}\u{39B0}\u{1364}\u{3C61}\u{28ED}\u{0323}\u{7044}\u{397B}\u{1661}\u{40D6}\u{1F36}\u{04FA}\u{1236}\u{15A6}\u{6758}\u{29FD}\u{35A5}\u{63A0}\u{64C6}\u{3430}\u{622B}\u{430C}\u{2F3F}\u{1249}\u{45B7}\u{3A2D}\u{01A8}\u{0092}\u{0A48}\u{6103}\u{1859}\u{14D9}\u{6907}\u{7256}\u{2635}\u{08C2}\u{1060}\u{5EB8}\u{5741}\u{498E}\u{3FB1}\u{00F3}\u{4029}\u{183E}\u{2520}\u{2020}\u{5A41}\u{4482}\u{5545}\u{1CF4}\u{57E0}\u{63A4}\u{2271}\u{0223}\u{01A0}\u{2856}\u{0CC6}\u{6054}\u{4D69}\u{55C6}\u{5931}\u{0B37}\u{16F2}\u{0408}\u{1704}\u{1B8F}\u{02E7}\u{1B8A}\u{4DAE}\u{1899}\u{4571}\u{0644}\u{3021}\u{6ACC}\u{08B7}\u{2A8B}\u{52A2}\u{2F31}\u{0361}\u{60BA}\u{1239}\u{2321}\u{6E05}\u{2590}\u{61B7}\u{2EA2}\u{73BF}\u{2700}\u{4467}\u{2152}\u{34E9}\u{7F0C}\u{0520}\u{18CB}\u{406A}\u{2E2C}\u{2A41}\u{7439}\u{1628}\u{38CA}\u{3497}\u{2D2C}\u{0D8C}\u{5897}\u{094E}\u{5DE2}\u{4634}\u{0D7F}\u{4F2C}\u{7D72}\u{0327}\u{63C1}\u{4040}\u{3C27}\u{48E5}\u{50D2}\u{1426}\u{570B}\u{3CFA}\u{366F}\u{4B80}\u{2474}\u{24F0}\u{5049}\u{6DAC}\u{734E}\u{00C0}\u{0A25}\u{3521}\u{06E3}\u{6CBE}\u{1129}\u{00A1}\u{684C}\u{6DBA}\u{5739}\u{02F1}\u{508E}\u{4D18}\u{2836}\u{28B9}\u{208C}\u{4872}\u{3676}\u{4622}\u{4C82}\u{2213}\u{734D}\u{03C2}\u{7042}\u{0679}\u{3B30}\u{0892}\u{1453}\u{63F9}\u{583F}\u{0DAB}\u{3A98}\u{1D20}\u{0A2A}\u{6E40}\u{0465}\u{0330}i\u{08A0}\u{28EC}\u{1807}\u{018B}\u{32A0}\u{6134}\u{26EC}\u{34F0}\u{06A4}\u{2068}\u{2202}\u{5C8A}\u{2834}\u{6283}\u{260C}\u{0A0E}\u{2C2C}\u{5CF8}\u{1D2F}\u{4240}\u{7320}\u{21AA}\u{283E}\u{19D4}\u{0B34}\u{2380}\u{6921}\u{22B0}\u{1537}\u{6058}\u{7F6C}\u{52F4}\u{1E2D}\u{68C9}\u{0829}\u{51D7}\u{0D22}\u{124D}\u{0AEB}\u{7118}\u{1DCE}\u{2348}\u{69AE}\u{40D2}\u{1464}\u{0020}\u{0020}"; test_enc_bin_compat( lz_str::compress_to_utf16, &utf16_decompressed.encode_utf16().collect::>(), utf16_compressed.into(), ); } #[test] fn binary_decoding_compatibility_tests_mandatory() { eprintln!("base64 - old encoding"); let base64_encoding_old_decompressed = "During tattooing, ink is injected into the skin, initiating an immune response, and cells called \"macrophages\" move into the area and \"eat up\" the ink. The macrophages carry some of the ink to the body\'s lymph nodes, but some that are filled with ink stay put, embedded in the skin. That\'s what makes the tattoo visible under the skin. Dalhousie Uiversity\'s Alec Falkenham is developing a topical cream that works by targeting the macrophages that have remained at the site of the tattoo. New macrophages move in to consume the previously pigment-filled macrophages and then migrate to the lymph nodes, eventually taking all the dye with them. \"When comparing it to laser-based tattoo removal, in which you see the burns, the scarring, the blisters, in this case, we\'ve designed a drug that doesn\'t really have much off-target effect,\" he said. \"We\'re not targeting any of the normal skin cells, so you won\'t see a lot of inflammation. In fact, based on the process that we\'re actually using, we don\'t think there will be any inflammation at all and it would actually be anti-inflammatory."; let base64_encoding_old_compressed = "CIVwTglgdg5gBAFwIYIQezdGAaO0DWeAznlAFYCmAxghQCanqIAWFcR+0u0ECEKWOEih4AtqJBQ2YCkQAOaKEQq5hDKhQA2mklSTb6cAESikVMGjnMkMWUbii0ANzbQmCVkJlIhUBkYoUOBA5ew9XKHwAOjgAFU9Tc0trW10kMDAAT3Y0UTY0ADMWCMJ3TwAjNDpMgHISTUzRKzgoKtlccpAEHLyWIPS2AogDBgB3XmZSQiJkbLku3ApRcvo6Q2hi9k4oGPiUOrhR627TfFlN5FQMOCcIIghyzTZJNbBNjmgY4H1mNBB7tgAVQgLjA9wQtRIAEEnlQ4AAxfRnKDWUTEOBrFyaSyCHzoOQQPSaODmQJojxBUZoMD4EjlbLIMC2PiwTaJCxWGznCndawuOAyUzQQxBcLsXj5Ipiy7oNAxAByFFGDjMHJS50c/I2TCoiiIIF6YrkMlufyIDTgBJgeSgCAAtEMRiqkpzUr4GOERKIIDAwCg2GU2A0mpNWmsiIsXLaQPoLchtvBY5tqmxxh5iqIYkYAOqsES6prpQS8RBoOCaJDKMB28qVwwy66C5z6bgiI6EyaZP7sCgBirgJS4MVEPQZLBDiqaO60MGtlh3El13CjCg1fnhn1SBg+OhgEDwHkYtCyKA1brebTZPlsCRUSaFAp2xnMuAUAoFagIbD2TxEJAQOgs2zVcZBaNBumfCgWUTKBskKTZWjAUxiQ+fMtB0XAiDLLsQEORQzx7NgfGxbp4OgAoK3EARFBiABJEQCjML84FrZQGEUTZjTQDQiBIQ8VxqUCmJjS9gnuWBlzYOh8Ig5gCGKUDxm0FiiNg0gKKQKi+A4/plLUPBuipEBNG3GgRItFZfD4O1yMo0x0CyKIgAAA=="; test_dec_bin_compat( lz_str::decompress_from_base64, &base64_encoding_old_decompressed .encode_utf16() .collect::>(), base64_encoding_old_compressed, ); eprintln!("base64"); let base64_encoding_decompressed = "During tattooing, ink is injected into the skin, initiating an immune response, and cells called \"macrophages\" move into the area and \"eat up\" the ink. The macrophages carry some of the ink to the body\'s lymph nodes, but some that are filled with ink stay put, embedded in the skin. That\'s what makes the tattoo visible under the skin. Dalhousie Uiversity\'s Alec Falkenham is developing a topical cream that works by targeting the macrophages that have remained at the site of the tattoo. New macrophages move in to consume the previously pigment-filled macrophages and then migrate to the lymph nodes, eventually taking all the dye with them. \"When comparing it to laser-based tattoo removal, in which you see the burns, the scarring, the blisters, in this case, we\'ve designed a drug that doesn\'t really have much off-target effect,\" he said. \"We\'re not targeting any of the normal skin cells, so you won\'t see a lot of inflammation. In fact, based on the process that we\'re actually using, we don\'t think there will be any inflammation at all and it would actually be anti-inflammatory."; let base64_encoding_compressed = "CIVwTglgdg5gBAFwIYIQezdGAaO0DWeAznlAFYCmAxghQCanqIAWFcR+0u0ECEKWOEih4AtqJBQ2YCkQAOaKEQq5hDKhQA2mklSTb6cAESikVMGjnMkMWUbii0ANzbQmCVkJlIhUBkYoUOBA5ew9XKHwAOjgAFU9Tc0trW10kMDAAT3Y0UTY0ADMWCMJ3TwAjNDpMgHISTUzRKzgoKtlccpAEHLyWIPS2AogDBgB3XmZSQiJkbLku3ApRcvo6Q2hi9k4oGPiUOrhR627TfFlN5FQMOCcIIghyzTZJNbBNjmgY4H1mNBB7tgAVQgLjA9wQtRIAEEnlQ4AAxfRnKDWUTEOBrFyaSyCHzoOQQPSaODmQJojxBUZoMD4EjlbLIMC2PiwTaJCxWGznCndawuOAyUzQQxBcLsXj5Ipiy7oNAxAByFFGDjMHJS50c/I2TCoiiIIF6YrkMlufyIDTgBJgeSgCAAtEMRiqkpzUr4GOERKIIDAwCg2GU2A0mpNWmsiIsXLaQPoLchtvBY5tqmxxh5iqIYkYAOqsES6prpQS8RBoOCaJDKMB28qVwwy66C5z6bgiI6EyaZP7sCgBirgJS4MVEPQZLBDiqaO60MGtlh3El13CjCg1fnhn1SBg+OhgEDwHkYtCyKA1brebTZPlsCRUSaFAp2xnMuAUAoFagIbD2TxEJAQOgs2zVcZBaNBumfCgWUTKBskKTZWjAUxiQ+fMtB0XAiDLLsQEORQzx7NgfGxbp4OgAoK3EARFBiABJEQCjML84FrZQGEUTZjTQDQiBIQ8VxqUCmJjS9gnuWBlzYOh8Ig5gCGKUDxm0FiiNg0gKKQKi+A4/plLUPBuipEBNG3GgRItFZfD4O1yMo0x0CyKIgA"; test_dec_bin_compat( lz_str::decompress_from_base64, &base64_encoding_decompressed .encode_utf16() .collect::>(), base64_encoding_compressed, ); eprintln!("uriEncoding - old encoding"); let uri_encoding_old_decompressed = "During tattooing, ink is injected into the skin, initiating an immune response, and cells called \"macrophages\" move into the area and \"eat up\" the ink. The macrophages carry some of the ink to the body\'s lymph nodes, but some that are filled with ink stay put, embedded in the skin. That\'s what makes the tattoo visible under the skin. Dalhousie Uiversity\'s Alec Falkenham is developing a topical cream that works by targeting the macrophages that have remained at the site of the tattoo. New macrophages move in to consume the previously pigment-filled macrophages and then migrate to the lymph nodes, eventually taking all the dye with them. \"When comparing it to laser-based tattoo removal, in which you see the burns, the scarring, the blisters, in this case, we\'ve designed a drug that doesn\'t really have much off-target effect,\" he said. \"We\'re not targeting any of the normal skin cells, so you won\'t see a lot of inflammation. In fact, based on the process that we\'re actually using, we don\'t think there will be any inflammation at all and it would actually be anti-inflammatory."; let uri_encoding_old_compressed = "CIVwTglgdg5gBAFwIYIQezdGAaO0DWeAznlAFYCmAxghQCanqIAWFcR+0u0ECEKWOEih4AtqJBQ2YCkQAOaKEQq5hDKhQA2mklSTb6cAESikVMGjnMkMWUbii0ANzbQmCVkJlIhUBkYoUOBA5ew9XKHwAOjgAFU9Tc0trW10kMDAAT3Y0UTY0ADMWCMJ3TwAjNDpMgHISTUzRKzgoKtlccpAEHLyWIPS2AogDBgB3XmZSQiJkbLku3ApRcvo6Q2hi9k4oGPiUOrhR627TfFlN5FQMOCcIIghyzTZJNbBNjmgY4H1mNBB7tgAVQgLjA9wQtRIAEEnlQ4AAxfRnKDWUTEOBrFyaSyCHzoOQQPSaODmQJojxBUZoMD4EjlbLIMC2PiwTaJCxWGznCndawuOAyUzQQxBcLsXj5Ipiy7oNAxAByFFGDjMHJS50c-I2TCoiiIIF6YrkMlufyIDTgBJgeSgCAAtEMRiqkpzUr4GOERKIIDAwCg2GU2A0mpNWmsiIsXLaQPoLchtvBY5tqmxxh5iqIYkYAOqsES6prpQS8RBoOCaJDKMB28qVwwy66C5z6bgiI6EyaZP7sCgBirgJS4MVEPQZLBDiqaO60MGtlh3El13CjCg1fnhn1SBg+OhgEDwHkYtCyKA1brebTZPlsCRUSaFAp2xnMuAUAoFagIbD2TxEJAQOgs2zVcZBaNBumfCgWUTKBskKTZWjAUxiQ+fMtB0XAiDLLsQEORQzx7NgfGxbp4OgAoK3EARFBiABJEQCjML84FrZQGEUTZjTQDQiBIQ8VxqUCmJjS9gnuWBlzYOh8Ig5gCGKUDxm0FiiNg0gKKQKi+A4-plLUPBuipEBNG3GgRItFZfD4O1yMo0x0CyKIgA"; test_dec_bin_compat( lz_str::decompress_from_encoded_uri_component, &uri_encoding_old_decompressed .encode_utf16() .collect::>(), uri_encoding_old_compressed, ); eprintln!("uriEncoding"); let uri_encoding_decompressed = "During tattooing, ink is injected into the skin, initiating an immune response, and cells called \"macrophages\" move into the area and \"eat up\" the ink. The macrophages carry some of the ink to the body\'s lymph nodes, but some that are filled with ink stay put, embedded in the skin. That\'s what makes the tattoo visible under the skin. Dalhousie Uiversity\'s Alec Falkenham is developing a topical cream that works by targeting the macrophages that have remained at the site of the tattoo. New macrophages move in to consume the previously pigment-filled macrophages and then migrate to the lymph nodes, eventually taking all the dye with them. \"When comparing it to laser-based tattoo removal, in which you see the burns, the scarring, the blisters, in this case, we\'ve designed a drug that doesn\'t really have much off-target effect,\" he said. \"We\'re not targeting any of the normal skin cells, so you won\'t see a lot of inflammation. In fact, based on the process that we\'re actually using, we don\'t think there will be any inflammation at all and it would actually be anti-inflammatory."; let uri_encoding_compressed = "CIVwTglgdg5gBAFwIYIQezdGAaO0DWeAznlAFYCmAxghQCanqIAWFcR+0u0ECEKWOEih4AtqJBQ2YCkQAOaKEQq5hDKhQA2mklSTb6cAESikVMGjnMkMWUbii0ANzbQmCVkJlIhUBkYoUOBA5ew9XKHwAOjgAFU9Tc0trW10kMDAAT3Y0UTY0ADMWCMJ3TwAjNDpMgHISTUzRKzgoKtlccpAEHLyWIPS2AogDBgB3XmZSQiJkbLku3ApRcvo6Q2hi9k4oGPiUOrhR627TfFlN5FQMOCcIIghyzTZJNbBNjmgY4H1mNBB7tgAVQgLjA9wQtRIAEEnlQ4AAxfRnKDWUTEOBrFyaSyCHzoOQQPSaODmQJojxBUZoMD4EjlbLIMC2PiwTaJCxWGznCndawuOAyUzQQxBcLsXj5Ipiy7oNAxAByFFGDjMHJS50c-I2TCoiiIIF6YrkMlufyIDTgBJgeSgCAAtEMRiqkpzUr4GOERKIIDAwCg2GU2A0mpNWmsiIsXLaQPoLchtvBY5tqmxxh5iqIYkYAOqsES6prpQS8RBoOCaJDKMB28qVwwy66C5z6bgiI6EyaZP7sCgBirgJS4MVEPQZLBDiqaO60MGtlh3El13CjCg1fnhn1SBg+OhgEDwHkYtCyKA1brebTZPlsCRUSaFAp2xnMuAUAoFagIbD2TxEJAQOgs2zVcZBaNBumfCgWUTKBskKTZWjAUxiQ+fMtB0XAiDLLsQEORQzx7NgfGxbp4OgAoK3EARFBiABJEQCjML84FrZQGEUTZjTQDQiBIQ8VxqUCmJjS9gnuWBlzYOh8Ig5gCGKUDxm0FiiNg0gKKQKi+A4-plLUPBuipEBNG3GgRItFZfD4O1yMo0x0CyKIgAAA$$"; test_dec_bin_compat( lz_str::decompress_from_encoded_uri_component, &uri_encoding_decompressed .encode_utf16() .collect::>(), uri_encoding_compressed, ); eprintln!("UInt8Array"); let uint8_array_decompressed = "During tattooing, ink is injected into the skin, initiating an immune response, and cells called \"macrophages\" move into the area and \"eat up\" the ink. The macrophages carry some of the ink to the body\'s lymph nodes, but some that are filled with ink stay put, embedded in the skin. That\'s what makes the tattoo visible under the skin. Dalhousie Uiversity\'s Alec Falkenham is developing a topical cream that works by targeting the macrophages that have remained at the site of the tattoo. New macrophages move in to consume the previously pigment-filled macrophages and then migrate to the lymph nodes, eventually taking all the dye with them. \"When comparing it to laser-based tattoo removal, in which you see the burns, the scarring, the blisters, in this case, we\'ve designed a drug that doesn\'t really have much off-target effect,\" he said. \"We\'re not targeting any of the normal skin cells, so you won\'t see a lot of inflammation. In fact, based on the process that we\'re actually using, we don\'t think there will be any inflammation at all and it would actually be anti-inflammatory."; let uint8_array_compressed = &[ 8, 133, 112, 78, 9, 96, 118, 14, 96, 4, 1, 112, 33, 130, 16, 123, 55, 70, 1, 163, 180, 13, 103, 128, 206, 121, 64, 21, 128, 166, 3, 24, 33, 64, 38, 167, 168, 128, 22, 21, 196, 126, 210, 237, 4, 8, 66, 150, 56, 72, 161, 224, 11, 106, 36, 20, 54, 96, 41, 16, 0, 230, 138, 17, 10, 185, 132, 50, 161, 64, 13, 166, 146, 84, 147, 111, 167, 0, 17, 40, 164, 84, 193, 163, 156, 201, 12, 89, 70, 226, 139, 64, 13, 205, 180, 38, 9, 89, 9, 148, 136, 84, 6, 70, 40, 80, 224, 64, 229, 236, 61, 92, 161, 240, 0, 232, 224, 0, 85, 61, 77, 205, 45, 173, 109, 116, 144, 192, 192, 1, 61, 216, 209, 68, 216, 208, 0, 204, 88, 35, 9, 221, 60, 0, 140, 208, 233, 50, 1, 200, 73, 53, 51, 68, 172, 224, 160, 171, 101, 113, 202, 64, 16, 114, 242, 88, 131, 210, 216, 10, 32, 12, 24, 1, 221, 121, 153, 73, 8, 137, 145, 178, 228, 187, 112, 41, 69, 203, 232, 233, 13, 161, 139, 217, 56, 160, 99, 226, 80, 234, 225, 71, 173, 187, 77, 241, 101, 55, 145, 80, 48, 224, 156, 32, 136, 33, 203, 52, 217, 36, 214, 193, 54, 57, 160, 99, 129, 245, 152, 208, 65, 238, 216, 0, 85, 8, 11, 140, 15, 112, 66, 212, 72, 0, 65, 39, 149, 14, 0, 3, 23, 209, 156, 160, 214, 81, 49, 14, 6, 177, 114, 105, 44, 130, 31, 58, 14, 65, 3, 210, 104, 224, 230, 64, 154, 35, 196, 21, 25, 160, 192, 248, 18, 57, 91, 44, 131, 2, 216, 248, 176, 77, 162, 66, 197, 97, 179, 156, 41, 221, 107, 11, 142, 3, 37, 51, 65, 12, 65, 112, 187, 23, 143, 146, 41, 139, 46, 232, 52, 12, 64, 7, 33, 69, 24, 56, 204, 28, 148, 185, 209, 207, 200, 217, 48, 168, 138, 34, 8, 23, 166, 43, 144, 201, 110, 127, 34, 3, 78, 0, 73, 129, 228, 160, 8, 0, 45, 16, 196, 98, 170, 74, 115, 82, 190, 6, 56, 68, 74, 32, 128, 192, 192, 40, 54, 25, 77, 128, 210, 106, 77, 90, 107, 34, 34, 197, 203, 105, 3, 232, 45, 200, 109, 188, 22, 57, 182, 169, 177, 198, 30, 98, 168, 134, 36, 96, 3, 170, 176, 68, 186, 166, 186, 80, 75, 196, 65, 160, 224, 154, 36, 50, 140, 7, 111, 42, 87, 12, 50, 235, 160, 185, 207, 166, 224, 136, 142, 132, 201, 166, 79, 238, 192, 160, 6, 42, 224, 37, 46, 12, 84, 67, 208, 100, 176, 67, 138, 166, 142, 235, 67, 6, 182, 88, 119, 18, 93, 119, 10, 48, 160, 213, 249, 225, 159, 84, 129, 131, 227, 161, 128, 64, 240, 30, 70, 45, 11, 34, 128, 213, 186, 222, 109, 54, 79, 150, 192, 145, 81, 38, 133, 2, 157, 177, 156, 203, 128, 80, 10, 5, 106, 2, 27, 15, 100, 241, 16, 144, 16, 58, 11, 54, 205, 87, 25, 5, 163, 65, 186, 103, 194, 129, 101, 19, 40, 27, 36, 41, 54, 86, 140, 5, 49, 137, 15, 159, 50, 208, 116, 92, 8, 131, 44, 187, 16, 16, 228, 80, 207, 30, 205, 129, 241, 177, 110, 158, 14, 128, 10, 10, 220, 64, 17, 20, 24, 128, 4, 145, 16, 10, 51, 11, 243, 129, 107, 101, 1, 132, 81, 54, 99, 77, 0, 208, 136, 18, 16, 241, 92, 106, 80, 41, 137, 141, 47, 96, 158, 229, 129, 151, 54, 14, 135, 194, 32, 230, 0, 134, 41, 64, 241, 155, 65, 98, 136, 216, 52, 128, 162, 144, 42, 47, 128, 227, 250, 101, 45, 67, 193, 186, 42, 68, 4, 209, 183, 26, 4, 72, 180, 86, 95, 15, 131, 181, 200, 202, 52, 199, 64, 178, 40, 136, 0, 0, ]; test_dec_bin_compat( lz_str::decompress_from_uint8_array, &uint8_array_decompressed .encode_utf16() .collect::>(), uint8_array_compressed, ); eprintln!("UTF16"); let utf16_encoding_decompressed = "During tattooing, ink is injected into the skin, initiating an immune response, and cells called \"macrophages\" move into the area and \"eat up\" the ink. The macrophages carry some of the ink to the body\'s lymph nodes, but some that are filled with ink stay put, embedded in the skin. That\'s what makes the tattoo visible under the skin. Dalhousie Uiversity\'s Alec Falkenham is developing a topical cream that works by targeting the macrophages that have remained at the site of the tattoo. New macrophages move in to consume the previously pigment-filled macrophages and then migrate to the lymph nodes, eventually taking all the dye with them. \"When comparing it to laser-based tattoo removal, in which you see the burns, the scarring, the blisters, in this case, we\'ve designed a drug that doesn\'t really have much off-target effect,\" he said. \"We\'re not targeting any of the normal skin cells, so you won\'t see a lot of inflammation. In fact, based on the process that we\'re actually using, we don\'t think there will be any inflammation at all and it would actually be anti-inflammatory."; let utf16_encoding_compressed = "\u{0462}\u{5C33}\u{414C}\u{0780}\u{7320}\u{1025}\u{6063}\u{0230}\u{3DBB}\u{51A0}\u{3496}\u{40F6}\u{3C26}\u{3A05}K\u{00C6}\u{01AC}\u{0870}\u{04F4}\u{7AA8}\u{00D0}\u{5731}\u{7DC5}\u{6D24}\u{0441}\u{25AE}\u{0934}\u{1E20}\u{5B71}\u{1070}\u{6CE0}\u{2930}\u{0093}\u{22A4}\u{2177}\u{1863}\u{152A}V\u{4D44}\u{54B3}\u{37F3}\u{4024}\u{2534}\u{456C}\u{0D3C}\u{7344}\u{18D2}\u{4702}\u{45C0}\u{0393}\u{36A4}\u{60B5}\u{486C}\u{5241}\u{282C}\u{4648}\u{2890}\u{1059}\u{3DA7}\u{55EA}\u{0FA0}\u{03C3}\u{4020}\u{555D}\u{2706}\u{4B8B}\u{2DCE}\u{492C}\u{0620}\u{0517}\u{31C2}\u{44F8}\u{6820}\u{3336}\u{0481}\u{1DF3}\u{6024}\u{3363}\u{5284}\u{01E8}\u{24BA}\u{4CF1}\u{15BC}\u{0A2A}\u{5B4B}\u{4749}@\u{7312}\u{2C61}\u{74D6}\u{0164}\u{00E1}\u{402E}\u{7606}\u{32B2}\u{08A9}\u{48F9}\u{394E}\u{6E25}\u{147C}\u{5F67}\u{2456}\u{4337}\u{5958}\u{5051}\u{78B4}\u{1D7C}\u{149A}\u{6DFA}\u{37E5}\u{4A8F}\u{1170}\u{1890}\u{2728}\u{1124}\u{1CD3}\u{26E9}\u{137B}\u{028C}\u{39C0}\u{31E0}\u{7D86}\u{1A28}\u{1F0D}\u{4022}\u{5440}\u{1738}\u{0F90}\u{218A}\u{1220}\u{0844}\u{7970}\u{7020}\u{0C7F}\u{2359}\u{20F6}\u{28B8}\u{43A1}\u{564E}\u{26B2}\u{6430}\u{7D08}\u{1CA2}\u{03F2}\u{3490}\u{39B0}\u{1364}\u{3C61}\u{28ED}\u{0323}\u{7044}\u{397B}\u{1661}\u{40D6}\u{1F36}\u{04FA}\u{1236}\u{15A6}\u{6758}\u{29FD}\u{35A5}\u{63A0}\u{64C6}\u{3430}\u{622B}\u{430C}\u{2F3F}\u{1249}\u{45B7}\u{3A2D}\u{01A8}\u{0092}\u{0A48}\u{6103}\u{1859}\u{14D9}\u{6907}\u{7256}\u{2635}\u{08C2}\u{1060}\u{5EB8}\u{5741}\u{498E}\u{3FB1}\u{00F3}\u{4029}\u{183E}\u{2520}\u{2020}\u{5A41}\u{4482}\u{5545}\u{1CF4}\u{57E0}\u{63A4}\u{2271}\u{0223}\u{01A0}\u{2856}\u{0CC6}\u{6054}\u{4D69}\u{55C6}\u{5931}\u{0B37}\u{16F2}\u{0408}\u{1704}\u{1B8F}\u{02E7}\u{1B8A}\u{4DAE}\u{1899}\u{4571}\u{0644}\u{3021}\u{6ACC}\u{08B7}\u{2A8B}\u{52A2}\u{2F31}\u{0361}\u{60BA}\u{1239}\u{2321}\u{6E05}\u{2590}\u{61B7}\u{2EA2}\u{73BF}\u{2700}\u{4467}\u{2152}\u{34E9}\u{7F0C}\u{0520}\u{18CB}\u{406A}\u{2E2C}\u{2A41}\u{7439}\u{1628}\u{38CA}\u{3497}\u{2D2C}\u{0D8C}\u{5897}\u{094E}\u{5DE2}\u{4634}\u{0D7F}\u{4F2C}\u{7D72}\u{0327}\u{63C1}\u{4040}\u{3C27}\u{48E5}\u{50D2}\u{1426}\u{570B}\u{3CFA}\u{366F}\u{4B80}\u{2474}\u{24F0}\u{5049}\u{6DAC}\u{734E}\u{00C0}\u{0A25}\u{3521}\u{06E3}\u{6CBE}\u{1129}\u{00A1}\u{684C}\u{6DBA}\u{5739}\u{02F1}\u{508E}\u{4D18}\u{2836}\u{28B9}\u{208C}\u{4872}\u{3676}\u{4622}\u{4C82}\u{2213}\u{734D}\u{03C2}\u{7042}\u{0679}\u{3B30}\u{0892}\u{1453}\u{63F9}\u{583F}\u{0DAB}\u{3A98}\u{1D20}\u{0A2A}\u{6E40}\u{0465}\u{0330}i\u{08A0}\u{28EC}\u{1807}\u{018B}\u{32A0}\u{6134}\u{26EC}\u{34F0}\u{06A4}\u{2068}\u{2202}\u{5C8A}\u{2834}\u{6283}\u{260C}\u{0A0E}\u{2C2C}\u{5CF8}\u{1D2F}\u{4240}\u{7320}\u{21AA}\u{283E}\u{19D4}\u{0B34}\u{2380}\u{6921}\u{22B0}\u{1537}\u{6058}\u{7F6C}\u{52F4}\u{1E2D}\u{68C9}\u{0829}\u{51D7}\u{0D22}\u{124D}\u{0AEB}\u{7118}\u{1DCE}\u{2348}\u{69AE}\u{40D2}\u{1464}\u{0020}\u{0020}"; test_dec_bin_compat( lz_str::decompress_from_utf16, &utf16_encoding_decompressed .encode_utf16() .collect::>(), utf16_encoding_compressed, ); } pub struct ByteString(Vec); impl ByteString { pub fn to_utf8_string(&self) -> Result { String::from_utf16(&self.0) } pub fn len(&self) -> usize { self.0.len() } pub fn is_empty(&self) -> bool { self.0.is_empty() } } impl From<&str> for ByteString { fn from(s: &str) -> ByteString { ByteString(s.encode_utf16().collect()) } } impl From for ByteString { fn from(s: String) -> ByteString { ByteString(s.encode_utf16().collect()) } } impl From> for ByteString { fn from(data: Vec) -> ByteString { ByteString(data) } } impl std::fmt::Debug for ByteString { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { for c in std::char::decode_utf16(self.0.iter().copied()) { c.unwrap_or(std::char::REPLACEMENT_CHARACTER).fmt(f)?; } Ok(()) } } impl PartialEq for ByteString { fn eq(&self, other: &str) -> bool { other.encode_utf16().eq(self.0.iter().copied()) } } impl PartialEq<&str> for ByteString { fn eq(&self, other: &&str) -> bool { other.encode_utf16().eq(self.0.iter().copied()) } } lz-str-0.2.1/tests/smoke.rs000064400000000000000000000045330072674642500137420ustar 00000000000000use lz_str::{ compress, compress_to_encoded_uri_component, compress_to_utf16, decompress, decompress_from_encoded_uri_component, decompress_from_utf16, }; const TEST_STR: &str = "The quick brown fox jumps over the lazy dog"; const TEST_STR_COMPRESSED: &[u16] = &[ 2688, 45222, 64, 36362, 57494, 1584, 13700, 1120, 9987, 55325, 49270, 4108, 54016, 15392, 2758, 364, 112, 6594, 19459, 29469, 2049, 30466, 108, 1072, 3008, 10116, 38, 38915, 39168, ]; #[test] pub fn round_test_uri() { let compressed = compress_to_encoded_uri_component(&TEST_STR.encode_utf16().collect::>()); assert_eq!( &compressed, "CoCwpgBAjgrglgYwNYQEYCcD2B3AdhAM0wA8IArGAWwAcBnCTANzHQgBdwIAbAQwC8AnhAAmmAOZA" ); let decompressed = decompress_from_encoded_uri_component(&compressed) .expect("`round_test_uri` valid decompress"); assert_eq!(TEST_STR.encode_utf16().collect::>(), decompressed); } #[test] pub fn round_test() { let compressed = compress(&TEST_STR.encode_utf16().collect::>()); let decompressed = decompress(&compressed).unwrap(); assert_eq!(TEST_STR.encode_utf16().collect::>(), decompressed); } #[test] pub fn compress_test() { let compressed = compress(&TEST_STR.encode_utf16().collect::>()); assert_eq!(TEST_STR_COMPRESSED, compressed); } #[test] pub fn compress_test_to_utf16() { let compressed = compress_to_utf16(&TEST_STR.encode_utf16().collect::>()); assert_eq!("ՠⱉ䀨ऀ圤堸悋Ф〳䄖Ϙށ†䰠硠૦Ö<͘ⓠ᮸瑀̎Ƞ㘢ఢ砤硠Ŕ怮㈠ ", compressed); } #[test] pub fn decompress_test_to_utf16() { let decompressed = decompress_from_utf16("ՠⱉ䀨ऀ圤堸悋Ф〳䄖Ϙށ†䰠硠૦Ö<͘ⓠ᮸瑀̎Ƞ㘢ఢ砤硠Ŕ怮㈠ ") .expect("Valid Decompress"); assert_eq!(TEST_STR.encode_utf16().collect::>(), decompressed); } #[test] pub fn compress_repeat() { let data = "aaaaabaaaaacaaaaadaaaaaeaaaaa"; let compressed = compress_to_encoded_uri_component(&data.encode_utf16().collect::>()); assert_eq!(&compressed, "IYkI1EGNOATWBTWQ"); } #[test] pub fn decompress_test() { let decompressed = decompress(TEST_STR_COMPRESSED).expect("Valid Decompress"); assert_eq!(TEST_STR.encode_utf16().collect::>(), decompressed); } lz-str-0.2.1/tests/valid_inputs.rs000064400000000000000000000026100072674642500153170ustar 00000000000000use lz_str::decompress; #[test] fn valid_decompress() { let valid_data: &[(&str, Vec)] = &[("red123", vec![0x80, 0x80]), ("腆퍂蚂荂", vec![0xD8A0])]; for (data, expected) in valid_data { let arr: Vec = data.encode_utf16().collect(); let decompressed = decompress(&arr).expect("decompression failed"); assert_eq!(&decompressed, expected); } } #[test] fn valid_long_input_round() { // let buffer = []; // for(let i = 0; i < 100000; i++){ // buffer.push(i % 65_535); // } // result = LZString144.compress(String.fromCharCode(...buffer)); // Array.from(result).map((v) => v.charCodeAt(0)); let data: Vec = (0u64..100_000u64) .map(|val| (val % u64::from(std::u16::MAX)) as u16) .collect(); let compressed = lz_str::compress(&data); let js_compressed = include_str!("../test_data/long_compressed_js.txt") .split(',') .map(|s| s.trim().parse::().unwrap()) .collect::>(); for (i, (a, b)) in compressed.iter().zip(js_compressed.iter()).enumerate() { if a != b { assert_eq!(a, b, "[index={}] {} != {}", i, a, b); } } assert_eq!(compressed, js_compressed); let decompressed = lz_str::decompress(&compressed).expect("decompression failed"); assert_eq!(decompressed, data); }