highway-1.2.0/.cargo_vcs_info.json0000644000000001360000000000100124740ustar { "git": { "sha1": "fd36b1e240aff1ad76ee0c5058854842be9922d0" }, "path_in_vcs": "" }highway-1.2.0/Cargo.lock0000644000000466350000000000100104650ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[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.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[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 = "clap" version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "bitflags", "textwrap", "unicode-width", ] [[package]] name = "console_error_panic_hook" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" dependencies = [ "cfg-if", "wasm-bindgen", ] [[package]] name = "criterion" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" dependencies = [ "atty", "cast", "clap", "criterion-plot", "csv", "itertools", "lazy_static", "num-traits", "oorandom", "plotters", "rayon", "regex", "serde", "serde_cbor", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" dependencies = [ "cast", "itertools", ] [[package]] name = "crossbeam-deque" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "csv" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" dependencies = [ "csv-core", "itoa", "ryu", "serde", ] [[package]] name = "csv-core" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" dependencies = [ "memchr", ] [[package]] name = "either" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "env_logger" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" dependencies = [ "log", "regex", ] [[package]] name = "getrandom" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", "libc", "wasi", "wasm-bindgen", ] [[package]] name = "half" version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" [[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "highway" version = "1.2.0" dependencies = [ "criterion", "getrandom", "no-panic", "quickcheck", "quickcheck_macros", "wasm-bindgen-test", ] [[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.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" 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.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "no-panic" version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8540b7d99a20166178b42a05776aef900cdbfec397f861dfc7819bf1d7760b3d" dependencies = [ "proc-macro2", "quote", "syn 2.0.67", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "plotters" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" dependencies = [ "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" [[package]] name = "plotters-svg" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" dependencies = [ "plotters-backend", ] [[package]] name = "proc-macro2" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "quickcheck" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" dependencies = [ "env_logger", "log", "rand", ] [[package]] name = "quickcheck_macros" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "quote" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "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.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] [[package]] name = "regex" version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[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 = "scoped-tls" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "serde" version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_cbor" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" dependencies = [ "half", "serde", ] [[package]] name = "serde_derive" version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", "syn 2.0.67", ] [[package]] name = "serde_json" version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ "unicode-width", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-width" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "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.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn 2.0.67", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", "syn 2.0.67", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-bindgen-test" version = "0.3.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b" dependencies = [ "console_error_panic_hook", "js-sys", "scoped-tls", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test-macro", ] [[package]] name = "wasm-bindgen-test-macro" version = "0.3.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" dependencies = [ "proc-macro2", "quote", "syn 2.0.67", ] [[package]] name = "web-sys" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" 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.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ "windows-sys", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] name = "windows_i686_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" highway-1.2.0/Cargo.toml0000644000000032310000000000100104710ustar # 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 = "2021" name = "highway" version = "1.2.0" authors = ["Nick Babcock "] include = [ "src/**/*.rs", "benches", ] description = "Native Rust port of Google's HighwayHash, which makes use of SIMD instructions for a fast and strong hash function" readme = "README.md" keywords = [ "HighwayHash", "hasher", "hash", "simd", "avx", ] categories = [ "algorithms", "cryptography", "no-std", ] license = "MIT" repository = "https://github.com/nickbabcock/highway-rs" [profile.release] lto = "fat" codegen-units = 1 [[bench]] name = "bench_hashes" harness = false [dev-dependencies.no-panic] version = "0.1" [dev-dependencies.quickcheck] version = "1" [dev-dependencies.quickcheck_macros] version = "1" [features] default = ["std"] std = [] [target."cfg(not(target_family = \"wasm\"))".dev-dependencies.criterion] version = "< 0.4.0" [target."cfg(target_family = \"wasm\")".dev-dependencies.criterion] version = "< 0.4.0" default-features = false [target."cfg(target_family = \"wasm\")".dev-dependencies.getrandom] version = "0.2" features = ["js"] [target."cfg(target_family = \"wasm\")".dev-dependencies.wasm-bindgen-test] version = "0.3" highway-1.2.0/Cargo.toml.orig000064400000000000000000000020131046102023000141470ustar 00000000000000[package] name = "highway" version = "1.2.0" authors = ["Nick Babcock "] license = "MIT" readme = "README.md" repository = "https://github.com/nickbabcock/highway-rs" categories = ["algorithms", "cryptography", "no-std"] description = "Native Rust port of Google's HighwayHash, which makes use of SIMD instructions for a fast and strong hash function" keywords = ["HighwayHash", "hasher", "hash", "simd", "avx"] include = ["src/**/*.rs", "benches"] edition = "2021" [features] default = ["std"] std = [] [dev-dependencies] quickcheck = "1" quickcheck_macros = "1" no-panic = "0.1" [target.'cfg(target_family = "wasm")'.dev-dependencies] criterion = { version = "< 0.4.0", default-features = false } wasm-bindgen-test = "0.3" getrandom = { version = "0.2", features = ["js"] } [target.'cfg(not(target_family = "wasm"))'.dev-dependencies] criterion = { version = "< 0.4.0" } [[bench]] name = "bench_hashes" harness = false # Required for the no_panic to work [profile.release] lto = "fat" codegen-units = 1 highway-1.2.0/README.md000064400000000000000000000221171046102023000125460ustar 00000000000000![ci](https://github.com/nickbabcock/highway-rs/workflows/ci/badge.svg) [![](https://docs.rs/highway/badge.svg)](https://docs.rs/highway) [![Rust](https://img.shields.io/badge/rust-1.59%2B-blue.svg?maxAge=3600)](https://github.com/nickbabcock/highway-rs) [![Version](https://img.shields.io/crates/v/highway.svg?style=flat-square)](https://crates.io/crates/highway) # Highway-rs This crate is a native Rust port of [Google's HighwayHash](https://github.com/google/highwayhash), which is a fast, keyed, and strong hash function, whose output is hardware independent. ## Features - ✔ pure / stable rust - ✔ zero dependencies - ✔ generate consistent 64, 128, and 256bit hashes across all hardware - ✔ > 10 GB/s with SIMD (SSE 4.1 AVX 2, NEON) aware instructions on x86 and aarch64 architectures - ✔ > 3 GB/s on Wasm with the Wasm SIMD extension - ✔ > 1 GB/s hardware agnostic implementation with zero unsafe code - ✔ incremental / streaming hashes - ✔ zero heap allocations - ✔ `no_std` compatible - ✔ fuzzed against reference implementation to ensure stability and compatibility ## Caution `HighwayHash` (the algorithm) has not undergone extensive cryptanalysis like SipHash (the default hashing algorithm in Rust), but according to the authors, HighwayHash output bits are uniformly distributed and should withstand differential and rotational attacks. Hence HighwayHash is referred to as a strong hash function, not a cryptographic hash function. I encourage anyone interested to [peruse the paper](https://arxiv.org/abs/1612.06257) to understand the risks. ## Examples The quickest way to get started: ```rust use highway::{HighwayHasher, HighwayHash}; let res: u64 = HighwayHasher::default().hash64(&[]); let res2: [u64; 2] = HighwayHasher::default().hash128(&[]); let res3: [u64; 4] = HighwayHasher::default().hash256(&[]); ``` A more complete tour of the API follows: ```rust use highway::{HighwayHasher, HighwayHash, Key}; // HighwayHash requires a key that should be hidden from attackers // to ensure outputs are unpredictable, so attackers can't mount // DoS attacks. let key = Key([1, 2, 3, 4]); // A HighwayHasher is the recommended approach to hashing, // as it will select the fastest algorithm available let mut hasher = HighwayHasher::new(key); // Append some data hasher.append(&[255]); // After all data has been appended, you ask for // 64, 128, or 256bit output. The hasher is consumed // after finalization. let res: u64 = hasher.finalize64(); assert_eq!(0x07858f24d_2d79b2b2, res); ``` Creating a 128bit and 256bit hash is just as simple. ```rust use highway::{HighwayHasher, HighwayHash, Key}; // Generate 128bit hash let key = Key([1, 2, 3, 4]); let mut hasher128 = HighwayHasher::new(key); hasher128.append(&[255]); let res128: [u64; 2] = hasher128.finalize128(); assert_eq!([0xbb007d2462e77f3c, 0x224508f916b3991f], res128); // Generate 256bit hash let key = Key([1, 2, 3, 4]); let mut hasher256 = HighwayHasher::new(key); hasher256.append(&[255]); let res256: [u64; 4] = hasher256.finalize256(); let expected: [u64; 4] = [ 0x7161cadbf7cd70e1, 0xaac4905de62b2f5e, 0x7b02b936933faa7, 0xc8efcfc45b239f8d, ]; assert_eq!(expected, res256); ``` Use highway hash in standard rust collections ```rust use std::collections::HashMap; use highway::{HighwayBuildHasher, Key}; let mut map = HashMap::with_hasher(HighwayBuildHasher::new(Key([ 0xcbf29ce484222325, 0xc3a5c85c97cb3127, 0xb492b66fbe98f273, 0x9ae16a3b2f90404f, ]))); map.insert(1, 2); assert_eq!(map.get(&1), Some(&2)); ``` Or if utilizing a key is not important, one can use the default ```rust use std::collections::HashMap; use std::hash::BuildHasherDefault; use highway::HighwayHasher; let mut map = HashMap::with_hasher(BuildHasherDefault::::default()); map.insert(1, 2); assert_eq!(map.get(&1), Some(&2)); ``` Hashing a file, or anything implementing `Read` ```rust use std::hash::Hasher; use highway::{PortableHash, HighwayHash}; let mut file = &b"hello world"[..]; // We're using the `PortableHash` to show importing a specific hashing // implementation (all hash outputs are already portable / hardware agnostic). // The main reason for directly using `PortableHash` would be if avoiding // `unsafe` code blocks is a top priority. let mut hasher = PortableHash::default(); std::io::copy(&mut file, &mut hasher)?; let hash64 = hasher.finish(); // core Hasher API let hash256 = hasher.finalize256(); // HighwayHash API ``` ## Use Cases `HighwayHash` can be used against untrusted user input where weak hashes can't be used due to exploitation, verified cryptographic hashes are too slow, and a strong hash function meets requirements. Some specific scenarios given by the authors of HighwayHash: - Use 64bit hashes to for authenticating short lived messages - Use 256bit hashes for checksums. Think file storage (S3) or any longer lived data where there is a need for strong guarantees against collisions. `HighwayHash` may not be a good fit if the payloads trend small (< 100 bytes) and speed is up of the utmost importance, as HighwayHash hits its stride at larger payloads. ## Wasm SIMD When deploying HighwayHash to a Wasm environment, one can opt into using the Wasm SIMD instructions by adding a Rust flag: ```bash RUSTFLAGS="-C target-feature=+simd128" wasm-pack build ``` Then `HighwayHasher` will automatically defer to the Wasm SIMD implementation via `WasmHash`. Once opted in, the execution environment must support Wasm SIMD instructions, which Chrome, Firefox, and Node LTS have stabilized since mid-2021. The opt in is required as there is not a way for Wasm to detect SIMD capabilities at runtime. The mere presence of Wasm SIMD instructions will cause incompatible environments to fail to compile, so it is recommended to provide two Wasm payloads to downstream users: one with SIMD enabled and one without. ### `no_std` crates This crate has a feature, `std`, that is enabled by default. To use this crate in a `no_std` context, add the following to your `Cargo.toml`: ```toml [dependencies] highway = { version = "x", default-features = false } ``` Be aware that the `no_std` version is unable to detect CPU features and so will always default to the portable implementation. If building for a known SSE 4.1 or AVX 2 machine (and the majority of machines in the last decade will support SSE 4.1), then explicitly enable the target feature: ```bash RUSTFLAGS="-C target-feature=+sse4.1" cargo test RUSTFLAGS="-C target-feature=+avx2" cargo test ``` ## Benchmarks Benchmarks are ran with the following command: ```bash (cd compare && cargo clean && RUSTFLAGS="-C target-cpu=native" cargo bench) find ./compare/target -wholename "*/new/raw.csv" -print0 | xargs -0 xsv cat rows > assets/highway.csv ``` And can be analyzed with the [R script](assets/analysis.R) found in the assets directory Keep in mind, benchmarks will vary by machine. Newer machines typically handle AVX payloads better than older. We'll first take a look at the throughput when calculating the 64bit hash of a varying payload with various implementations ![64bit-highwayhash.png](assets/64bit-highwayhash.png) Takeaways: - The lower left corner of the graph illustrates HighwayHash's weakness: small payloads, as with a bit of squinting, one can see that HighwayHash ranks amongst the bottom. - At larger payloads, HighwayHash can be competitive in performance as the CPU has room to stretch its proverbial SIMD legs on the input. - AHash and t1ha perform fantastically and should be in one's toolkit for in memory data structures. Now taking a look at calculating a 256bit hash value, we see a similar story. ![256bit-highwayhash.png](assets/256bit-highwayhash.png) Takeaways: - HighwayHash is by far the fastest compared to the other functions, but if one needs a cryptographic hash, then BLAKE3 should be chosen Even with the best eyesight, the differences are indistinguishable at smaller payloads, so let's look at the hash rate: ![256bit-highwayhash-rate.png](assets/256bit-highwayhash-rate.png) Takeaways: - At smaller payloads HighwayHash maintains its performance lead HighwayHash uses more rounds of permutation when finalizing the 256bit output compared to the 64bit and this is reflected in the following graphic: ![64bit-vs-256bit-highwayhash.png](assets/64bit-vs-256bit-highwayhash.png) Takeaways: - At max, the 64bit hash can be computed 33% faster than the 256bit output - After 64KiB there is no performance difference between 64bit and 256bit outputs For those more into numbers and are curious about specifics or want more details about the hash functions at small payloads size, here is a table that breaks down throughput (in GB/s) at all payload sizes ![highwayhash-table.png](assets/highwayhash-table.png) ### Builder Benchmarks Have fun running the builder benchmarks to see how performance differs with flags: *Default compilation* ```bash cargo bench -- highway-builder ``` *Explicitly disable avx2* ```bash RUSTFLAGS="-C target-feature=-avx2" cargo bench -- highway-builder ``` *Explicitly disable avx2 when targeting native cpu* ```bash RUSTFLAGS="-C target-cpu=native -C target-feature=+sse4.1,-avx2" \ cargo bench -- highway-builder ``` highway-1.2.0/benches/bench_hashes.rs000064400000000000000000000054101046102023000156530ustar 00000000000000use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; #[cfg(target_arch = "x86_64")] use highway::{AvxHash, SseHash}; use highway::{HighwayHash, HighwayHasher, Key, PortableHash}; fn bit64_hash(c: &mut Criterion) { let parameters = vec![1, 4, 16, 64, 256, 1024, 4096, 16384, 65536]; let key = Key([0, 0, 0, 0]); let mut group = c.benchmark_group("64bit"); for i in parameters.iter() { group.throughput(Throughput::Bytes(*i as u64)); group.bench_with_input(BenchmarkId::new("builder", i), i, |b, param| { let data = vec![0u8; *param]; b.iter(|| HighwayHasher::new(key).hash64(&data)) }); group.bench_with_input(BenchmarkId::new("portable", i), i, |b, param| { let data = vec![0u8; *param]; b.iter(|| PortableHash::new(key).hash64(&data)) }); #[cfg(target_arch = "x86_64")] { let key = Key([0, 0, 0, 0]); if AvxHash::new(key).is_some() { group.bench_with_input(BenchmarkId::new("avx", i), i, |b, param| { let data = vec![0u8; *param]; b.iter(|| unsafe { AvxHash::force_new(key) }.hash64(&data)) }); } if SseHash::new(key).is_some() { group.bench_with_input(BenchmarkId::new("sse", i), i, |b, param| { let data = vec![0u8; *param]; b.iter(|| unsafe { SseHash::force_new(key) }.hash64(&data)) }); } } } group.finish(); } fn bit256_hash(c: &mut Criterion) { let parameters = vec![1, 4, 16, 64, 256, 1024, 4096, 16384, 65536]; let key = Key([0, 0, 0, 0]); let mut group = c.benchmark_group("256bit"); for i in parameters.iter() { group.throughput(Throughput::Bytes(*i as u64)); group.bench_with_input(BenchmarkId::new("portable", i), i, |b, param| { let data = vec![0u8; *param]; b.iter(|| PortableHash::new(key).hash256(&data)) }); #[cfg(target_arch = "x86_64")] { if AvxHash::new(key).is_some() { group.bench_with_input(BenchmarkId::new("avx", i), i, |b, param| { let data = vec![0u8; *param]; b.iter(|| unsafe { AvxHash::force_new(key) }.hash256(&data)) }); } if SseHash::new(key).is_some() { group.bench_with_input(BenchmarkId::new("sse", i), i, |b, param| { let data = vec![0u8; *param]; b.iter(|| unsafe { SseHash::force_new(key) }.hash256(&data)) }); } } } group.finish(); } criterion_group!(benches, bit64_hash, bit256_hash); criterion_main!(benches); highway-1.2.0/src/aarch64.rs000064400000000000000000000336101046102023000136540ustar 00000000000000#![allow(unsafe_code)] use crate::internal::{unordered_load3, HashPacket, PACKET_SIZE}; use crate::{HighwayHash, Key}; use core::arch::aarch64::*; use core::ops::{ Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, SubAssign, }; /// HighwayHash powered by Neon instructions #[derive(Debug, Default, Clone)] pub struct NeonHash { buffer: HashPacket, v0L: V2x64U, v0H: V2x64U, v1L: V2x64U, v1H: V2x64U, mul0L: V2x64U, mul0H: V2x64U, mul1L: V2x64U, mul1H: V2x64U, } impl HighwayHash for NeonHash { #[inline] fn append(&mut self, data: &[u8]) { unsafe { self.append(data); } } #[inline] fn finalize64(mut self) -> u64 { unsafe { Self::finalize64(&mut self) } } #[inline] fn finalize128(mut self) -> [u64; 2] { unsafe { Self::finalize128(&mut self) } } #[inline] fn finalize256(mut self) -> [u64; 4] { unsafe { Self::finalize256(&mut self) } } } impl NeonHash { /// Creates a new `NeonHash` while circumventing any runtime checks. pub unsafe fn force_new(key: Key) -> Self { let init0L = V2x64U::new(0xa409_3822_299f_31d0, 0xdbe6_d5d5_fe4c_ce2f); let init0H = V2x64U::new(0x243f_6a88_85a3_08d3, 0x1319_8a2e_0370_7344); let init1L = V2x64U::new(0xc0ac_f169_b5f1_8a8c, 0x3bd3_9e10_cb0e_f593); let init1H = V2x64U::new(0x4528_21e6_38d0_1377, 0xbe54_66cf_34e9_0c6c); let keyL = V2x64U::new(key[1], key[0]); let keyH = V2x64U::new(key[3], key[2]); NeonHash { v0L: keyL ^ init0L, v0H: keyH ^ init0H, v1L: keyL.rotate_by_32() ^ init1L, v1H: keyH.rotate_by_32() ^ init1H, mul0L: init0L, mul0H: init0H, mul1L: init1L, mul1H: init1H, buffer: HashPacket::default(), } } unsafe fn zipper_merge(v: &V2x64U) -> V2x64U { let pos = [3, 12, 2, 5, 14, 1, 15, 0, 11, 4, 10, 13, 9, 6, 8, 7]; let tbl = vld1q_u8(pos.as_ptr()); let lookup = vqtbl1q_u8(vreinterpretq_u8_u64(v.0), tbl); V2x64U::from(lookup) } unsafe fn update(&mut self, (packetH, packetL): (V2x64U, V2x64U)) { self.v1L += packetL; self.v1H += packetH; self.v1L += self.mul0L; self.v1H += self.mul0H; self.mul0L ^= V2x64U(vmull_u32( vmovn_u64(self.v1L.0), vshrn_n_u64(self.v0L.0, 32), )); self.mul0H ^= V2x64U(vmull_u32( vmovn_u64(self.v1H.0), vshrn_n_u64(self.v0H.0, 32), )); self.v0L += self.mul1L; self.v0H += self.mul1H; self.mul1L ^= V2x64U(vmull_u32( vmovn_u64(self.v0L.0), vshrn_n_u64(self.v1L.0, 32), )); self.mul1H ^= V2x64U(vmull_u32( vmovn_u64(self.v0H.0), vshrn_n_u64(self.v1H.0, 32), )); self.v0L += NeonHash::zipper_merge(&self.v1L); self.v0H += NeonHash::zipper_merge(&self.v1H); self.v1L += NeonHash::zipper_merge(&self.v0L); self.v1H += NeonHash::zipper_merge(&self.v0H); } unsafe fn permute_and_update(&mut self) { let low = self.v0L.rotate_by_32(); let high = self.v0H.rotate_by_32(); self.update((low, high)); } pub(crate) unsafe fn finalize64(&mut self) -> u64 { if !self.buffer.is_empty() { self.update_remainder(); } for _i in 0..4 { self.permute_and_update(); } let sum0 = self.v0L + self.mul0L; let sum1 = self.v1L + self.mul1L; let hash = sum0 + sum1; hash.as_arr()[0] } pub(crate) unsafe fn finalize128(&mut self) -> [u64; 2] { if !self.buffer.is_empty() { self.update_remainder(); } for _i in 0..6 { self.permute_and_update(); } let sum0 = self.v0L + self.mul0L; let sum1 = self.v1H + self.mul1H; let hash = sum0 + sum1; hash.as_arr() } pub(crate) unsafe fn finalize256(&mut self) -> [u64; 4] { if !self.buffer.is_empty() { self.update_remainder(); } for _i in 0..10 { self.permute_and_update(); } let sum0L = self.v0L + self.mul0L; let sum1L = self.v1L + self.mul1L; let sum0H = self.v0H + self.mul0H; let sum1H = self.v1H + self.mul1H; let hashL = NeonHash::modular_reduction(&sum1L, &sum0L).as_arr(); let hashH = NeonHash::modular_reduction(&sum1H, &sum0H).as_arr(); [hashL[0], hashL[1], hashH[0], hashH[1]] } unsafe fn modular_reduction(x: &V2x64U, init: &V2x64U) -> V2x64U { let zero = vdupq_n_u32(0); let sign_bit128 = V2x64U::from(vsetq_lane_u32(0x8000_0000_u32, zero, 3)); let top_bits2 = V2x64U::from(vshrq_n_u64(x.0, 62)); let shifted1_unmasked = *x + *x; let top_bits1 = V2x64U::from(vshrq_n_u64(x.0, 63)); let shifted2 = shifted1_unmasked + shifted1_unmasked; let new_low_bits2 = V2x64U::from(_mm_slli_si128_8(top_bits2.0)); let shifted1 = shifted1_unmasked.and_not(&sign_bit128); let new_low_bits1 = V2x64U::from(_mm_slli_si128_8(top_bits1.0)); *init ^ shifted2 ^ new_low_bits2 ^ shifted1 ^ new_low_bits1 } unsafe fn load_multiple_of_four(bytes: &[u8], size: u64) -> V2x64U { let mut data = bytes; let mut mask4 = V2x64U::new(0, 0xFFFF_FFFF); let mut ret = if bytes.len() >= 8 { mask4 = V2x64U::from(_mm_slli_si128_8(mask4.0)); data = &bytes[8..]; let lo = u64::from_le_bytes(take::<8>(bytes)); V2x64U::new(0, lo) } else { V2x64U::new(0, 0) }; if size & 4 != 0 { let last4 = u32::from_le_bytes(take::<4>(data)); let broadcast = V2x64U::from(vdupq_n_u32(last4)); ret |= broadcast & mask4; } ret } unsafe fn remainder(bytes: &[u8]) -> (V2x64U, V2x64U) { let size_mod32 = bytes.len(); let size_mod4 = size_mod32 & 3; if size_mod32 & 16 != 0 { let packetL = V2x64U::from(vld1q_u8(bytes.as_ptr())); let packett = NeonHash::load_multiple_of_four(&bytes[16..], size_mod32 as u64); let remainder = &bytes[(size_mod32 & !3) + size_mod4 - 4..]; let last4 = u32::from_le_bytes([remainder[0], remainder[1], remainder[2], remainder[3]]); let packetH = V2x64U::from(vsetq_lane_u32(last4, vreinterpretq_u32_u64(packett.0), 3)); (packetH, packetL) } else { let remainder = &bytes[size_mod32 & !3..]; let packetL = NeonHash::load_multiple_of_four(bytes, size_mod32 as u64); let last4 = unordered_load3(remainder); let packetH = V2x64U::new(0, last4); (packetH, packetL) } } unsafe fn update_remainder(&mut self) { let size = self.buffer.len() as i32; let vsize_mod32 = V2x64U::from(vdupq_n_s32(size)); self.v0L += vsize_mod32; self.v0H += vsize_mod32; self.rotate_32_by(size); let packet = NeonHash::remainder(self.buffer.as_slice()); self.update(packet); } unsafe fn rotate_32_by(&mut self, count: i32) { let vL = &mut self.v1L; let vH = &mut self.v1H; let count_left = vdupq_n_s32(count); let count_right = vdupq_n_s32(count + (!32 + 1)); let shifted_leftL = V2x64U::from(vshlq_u32(vreinterpretq_u32_u64(vL.0), count_left)); let shifted_leftH = V2x64U::from(vshlq_u32(vreinterpretq_u32_u64(vH.0), count_left)); let shifted_rightL = V2x64U::from(vshlq_u32(vreinterpretq_u32_u64(vL.0), count_right)); let shifted_rightH = V2x64U::from(vshlq_u32(vreinterpretq_u32_u64(vH.0), count_right)); *vL = shifted_leftL | shifted_rightL; *vH = shifted_leftH | shifted_rightH; } #[inline] unsafe fn data_to_lanes(packet: &[u8]) -> (V2x64U, V2x64U) { let ptr = packet.as_ptr(); let packetL = V2x64U::from(vld1q_u8(ptr)); let packetH = V2x64U::from(vld1q_u8(ptr.offset(16))); (packetH, packetL) } unsafe fn append(&mut self, data: &[u8]) { if self.buffer.is_empty() { let mut chunks = data.chunks_exact(PACKET_SIZE); for chunk in chunks.by_ref() { self.update(Self::data_to_lanes(chunk)); } self.buffer.set_to(chunks.remainder()); } else if let Some(tail) = self.buffer.fill(data) { self.update(Self::data_to_lanes(self.buffer.inner())); let mut chunks = tail.chunks_exact(PACKET_SIZE); for chunk in chunks.by_ref() { self.update(Self::data_to_lanes(chunk)); } self.buffer.set_to(chunks.remainder()); } } } #[inline] fn take(data: &[u8]) -> [u8; N] { debug_assert!(data.len() >= N); unsafe { *(data.as_ptr() as *const [u8; N]) } } #[inline] unsafe fn _mm_slli_si128_8(a: uint64x2_t) -> uint64x2_t { // aka _mm_bslli_si128_8 let tmp = vreinterpretq_u8_u64(a); let rotated = vextq_u8(vdupq_n_u8(0), tmp, 8); vreinterpretq_u64_u8(rotated) } #[derive(Clone, Copy)] pub struct V2x64U(pub uint64x2_t); impl Default for V2x64U { fn default() -> Self { unsafe { V2x64U::zeroed() } } } impl core::fmt::Debug for V2x64U { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "V2x64U: {:?}", unsafe { self.as_arr() }) } } impl V2x64U { #[inline] unsafe fn zeroed() -> Self { V2x64U(vdupq_n_u64(0)) } #[inline] pub unsafe fn new(hi: u64, low: u64) -> Self { V2x64U(vld1q_u64([low, hi].as_ptr())) } pub unsafe fn as_arr(&self) -> [u64; 2] { let mut arr: [u64; 2] = [0, 0]; vst1q_u64(arr.as_mut_ptr(), self.0); arr } #[inline] pub unsafe fn rotate_by_32(&self) -> Self { let tmp = vreinterpretq_u32_u64(self.0); let rotated = vrev64q_u32(tmp); V2x64U(vreinterpretq_u64_u32(rotated)) } #[inline] pub unsafe fn and_not(&self, neg_mask: &V2x64U) -> Self { V2x64U::from(vbicq_u64(self.0, neg_mask.0)) } #[inline] unsafe fn add_assign(&mut self, other: Self) { self.0 = vaddq_u64(self.0, other.0) } #[inline] unsafe fn sub_assign(&mut self, other: Self) { self.0 = vsubq_u64(self.0, other.0) } #[inline] unsafe fn bitand_assign(&mut self, other: Self) { self.0 = vandq_u64(self.0, other.0) } #[inline] unsafe fn bitor_assign(&mut self, other: Self) { self.0 = vorrq_u64(self.0, other.0) } #[inline] unsafe fn bitxor_assign(&mut self, other: Self) { self.0 = veorq_u64(self.0, other.0) } } impl From for V2x64U { #[inline] fn from(v: uint64x2_t) -> Self { V2x64U(v) } } impl From for V2x64U { #[inline] fn from(v: uint32x4_t) -> Self { V2x64U(unsafe { vreinterpretq_u64_u32(v) }) } } impl From for V2x64U { #[inline] fn from(v: int32x4_t) -> Self { V2x64U(unsafe { vreinterpretq_u64_s32(v) }) } } impl From for V2x64U { #[inline] fn from(v: uint16x8_t) -> Self { V2x64U(unsafe { vreinterpretq_u64_u16(v) }) } } impl From for V2x64U { #[inline] fn from(v: uint8x16_t) -> Self { V2x64U(unsafe { vreinterpretq_u64_u8(v) }) } } impl AddAssign for V2x64U { #[inline] fn add_assign(&mut self, other: Self) { unsafe { self.add_assign(other) } } } impl SubAssign for V2x64U { #[inline] fn sub_assign(&mut self, other: Self) { unsafe { self.sub_assign(other) } } } impl BitAndAssign for V2x64U { #[inline] fn bitand_assign(&mut self, other: Self) { unsafe { self.bitand_assign(other) } } } impl BitAnd for V2x64U { type Output = Self; #[inline] fn bitand(self, other: Self) -> Self { let mut new = V2x64U(self.0); new &= other; new } } impl BitOrAssign for V2x64U { #[inline] fn bitor_assign(&mut self, other: Self) { unsafe { self.bitor_assign(other) } } } impl BitOr for V2x64U { type Output = Self; #[inline] fn bitor(self, other: Self) -> Self { let mut new = V2x64U(self.0); new |= other; new } } impl BitXorAssign for V2x64U { #[inline] fn bitxor_assign(&mut self, other: Self) { unsafe { self.bitxor_assign(other) } } } impl Add for V2x64U { type Output = Self; #[inline] fn add(self, other: Self) -> Self { let mut new = V2x64U(self.0); new += other; new } } impl BitXor for V2x64U { type Output = Self; #[inline] fn bitxor(self, other: Self) -> Self { let mut new = V2x64U(self.0); new ^= other; new } } #[cfg(test)] pub mod tests { use super::*; #[test] fn test_as_arr() { unsafe { let x = V2x64U::new(55, 1); let res = x.as_arr(); assert_eq!(res, [1, 55]); } } #[test] fn test_rotate_by_32() { unsafe { let x = V2x64U::new(0x0264_432C_CD8A_70E0, 0x0B28_E3EF_EBB3_172D); let y = x.rotate_by_32(); let res = y.as_arr(); assert_eq!(res, [0xEBB3_172D_0B28_E3EF, 0xCD8A_70E0_0264_432C]); } } #[test] fn test_add() { unsafe { let x = V2x64U::new(55, 1); let y = V2x64U::new(0x0264_432C_CD8A_70E0, 0x0B28_E3EF_EBB3_172D); let z = x + y; assert_eq!(z.as_arr(), [0x0B28_E3EF_EBB3_172E, 0x0264_432C_CD8A_7117]); } } #[test] fn test_mm_slli_si128_8() { unsafe { let x = V2x64U::new(0, 0xFFFF_FFFF); let y = V2x64U::from(_mm_slli_si128_8(x.0)); assert_eq!(y.as_arr(), [0, 0xFFFF_FFFF]); } } } highway-1.2.0/src/builder.rs000064400000000000000000000254371046102023000140620ustar 00000000000000#![allow(unsafe_code)] use crate::key::Key; use crate::traits::HighwayHash; use core::{default::Default, fmt::Debug, mem::ManuallyDrop}; #[cfg(target_arch = "aarch64")] use crate::aarch64::NeonHash; #[cfg(not(any( all(target_family = "wasm", target_feature = "simd128"), target_arch = "aarch64" )))] use crate::portable::PortableHash; #[cfg(all(target_family = "wasm", target_feature = "simd128"))] use crate::wasm::WasmHash; #[cfg(target_arch = "x86_64")] use crate::{AvxHash, SseHash}; /// This union is purely for performance. Originally it was an enum, but Rust / /// LLVM had a hard time optimizing it and would include memcpy's that would /// dominate profiles. union HighwayChoices { #[cfg(not(any( all(target_family = "wasm", target_feature = "simd128"), target_arch = "aarch64" )))] portable: ManuallyDrop, #[cfg(target_arch = "x86_64")] avx: ManuallyDrop, #[cfg(target_arch = "x86_64")] sse: ManuallyDrop, #[cfg(target_arch = "aarch64")] neon: ManuallyDrop, #[cfg(all(target_family = "wasm", target_feature = "simd128"))] wasm: ManuallyDrop, } /// `HighwayHash` implementation that selects best hash implementation at runtime. pub struct HighwayHasher { tag: u8, inner: HighwayChoices, } impl Debug for HighwayHasher { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let mut debug = f.debug_struct("HighwayHasher"); debug.field("tag", &self.tag); match self.tag { #[cfg(not(any( all(target_family = "wasm", target_feature = "simd128"), target_arch = "aarch64" )))] 0 => debug.field("hasher", unsafe { &self.inner.portable }), #[cfg(target_arch = "x86_64")] 1 => debug.field("hasher", unsafe { &self.inner.avx }), #[cfg(target_arch = "x86_64")] 2 => debug.field("hasher", unsafe { &self.inner.sse }), #[cfg(target_arch = "aarch64")] 3 => debug.field("hasher", unsafe { &self.inner.neon }), #[cfg(all(target_family = "wasm", target_feature = "simd128"))] 4 => debug.field("hasher", unsafe { &self.inner.wasm }), _ => unsafe { core::hint::unreachable_unchecked() }, }; debug.finish() } } impl Clone for HighwayHasher { fn clone(&self) -> Self { let tag = self.tag; match tag { #[cfg(not(any( all(target_family = "wasm", target_feature = "simd128"), target_arch = "aarch64" )))] 0 => HighwayHasher { tag, inner: HighwayChoices { portable: unsafe { self.inner.portable.clone() }, }, }, #[cfg(target_arch = "x86_64")] 1 => HighwayHasher { tag, inner: HighwayChoices { avx: unsafe { self.inner.avx.clone() }, }, }, #[cfg(target_arch = "x86_64")] 2 => HighwayHasher { tag, inner: HighwayChoices { sse: unsafe { self.inner.sse.clone() }, }, }, #[cfg(target_arch = "aarch64")] 3 => HighwayHasher { tag, inner: HighwayChoices { neon: unsafe { self.inner.neon.clone() }, }, }, #[cfg(all(target_family = "wasm", target_feature = "simd128"))] 4 => HighwayHasher { tag, inner: HighwayChoices { wasm: unsafe { self.inner.wasm.clone() }, }, }, _ => unsafe { core::hint::unreachable_unchecked() }, } } } impl HighwayHash for HighwayHasher { #[inline] fn append(&mut self, data: &[u8]) { self.append(data); } #[inline] fn finalize64(mut self) -> u64 { Self::finalize64(&mut self) } #[inline] fn finalize128(mut self) -> [u64; 2] { Self::finalize128(&mut self) } #[inline] fn finalize256(mut self) -> [u64; 4] { Self::finalize256(&mut self) } } impl HighwayHasher { /// Creates a new hasher based on compilation and runtime capabilities #[must_use] pub fn new(key: Key) -> Self { #[cfg(target_arch = "x86_64")] { if cfg!(target_feature = "avx2") { let avx = ManuallyDrop::new(unsafe { AvxHash::force_new(key) }); return HighwayHasher { tag: 1, inner: HighwayChoices { avx }, }; } else if cfg!(target_feature = "sse4.1") { let sse = ManuallyDrop::new(unsafe { SseHash::force_new(key) }); return HighwayHasher { tag: 2, inner: HighwayChoices { sse }, }; } else { // Ideally we'd use `AvxHash::new` here, but it triggers a memcpy, so we // duplicate the same logic to know if hasher can be enabled. #[cfg(feature = "std")] if is_x86_feature_detected!("avx2") { let avx = ManuallyDrop::new(unsafe { AvxHash::force_new(key) }); return HighwayHasher { tag: 1, inner: HighwayChoices { avx }, }; } #[cfg(feature = "std")] if is_x86_feature_detected!("sse4.1") { let sse = ManuallyDrop::new(unsafe { SseHash::force_new(key) }); return HighwayHasher { tag: 2, inner: HighwayChoices { sse }, }; } } } #[cfg(target_arch = "aarch64")] { // Based on discussions here: // https://github.com/nickbabcock/highway-rs/pull/51#discussion_r815247129 // // It seems reasonable to assume the aarch64 is neon capable. // If a case is found where that is not true, we can patch later. let neon = ManuallyDrop::new(unsafe { NeonHash::force_new(key) }); HighwayHasher { tag: 3, inner: HighwayChoices { neon }, } } #[cfg(all(target_family = "wasm", target_feature = "simd128"))] { let wasm = ManuallyDrop::new(WasmHash::new(key)); HighwayHasher { tag: 4, inner: HighwayChoices { wasm }, } } #[cfg(not(any( all(target_family = "wasm", target_feature = "simd128"), target_arch = "aarch64" )))] { let portable = ManuallyDrop::new(PortableHash::new(key)); HighwayHasher { tag: 0, inner: HighwayChoices { portable }, } } } fn append(&mut self, data: &[u8]) { match self.tag { #[cfg(not(any( all(target_family = "wasm", target_feature = "simd128"), target_arch = "aarch64" )))] 0 => unsafe { &mut self.inner.portable }.append(data), #[cfg(target_arch = "x86_64")] 1 => unsafe { &mut self.inner.avx }.append(data), #[cfg(target_arch = "x86_64")] 2 => unsafe { &mut self.inner.sse }.append(data), #[cfg(target_arch = "aarch64")] 3 => unsafe { &mut self.inner.neon }.append(data), #[cfg(all(target_family = "wasm", target_feature = "simd128"))] 4 => unsafe { &mut self.inner.wasm }.append(data), _ => unsafe { core::hint::unreachable_unchecked() }, } } fn finalize64(&mut self) -> u64 { match self.tag { #[cfg(not(any( all(target_family = "wasm", target_feature = "simd128"), target_arch = "aarch64" )))] 0 => unsafe { PortableHash::finalize64(&mut self.inner.portable) }, #[cfg(target_arch = "x86_64")] 1 => unsafe { AvxHash::finalize64(&mut self.inner.avx) }, #[cfg(target_arch = "x86_64")] 2 => unsafe { SseHash::finalize64(&mut self.inner.sse) }, #[cfg(target_arch = "aarch64")] 3 => unsafe { NeonHash::finalize64(&mut self.inner.neon) }, #[cfg(all(target_family = "wasm", target_feature = "simd128"))] 4 => unsafe { WasmHash::finalize64(&mut self.inner.wasm) }, _ => unsafe { core::hint::unreachable_unchecked() }, } } fn finalize128(&mut self) -> [u64; 2] { match self.tag { #[cfg(not(any( all(target_family = "wasm", target_feature = "simd128"), target_arch = "aarch64" )))] 0 => unsafe { PortableHash::finalize128(&mut self.inner.portable) }, #[cfg(target_arch = "x86_64")] 1 => unsafe { AvxHash::finalize128(&mut self.inner.avx) }, #[cfg(target_arch = "x86_64")] 2 => unsafe { SseHash::finalize128(&mut self.inner.sse) }, #[cfg(target_arch = "aarch64")] 3 => unsafe { NeonHash::finalize128(&mut self.inner.neon) }, #[cfg(all(target_family = "wasm", target_feature = "simd128"))] 4 => unsafe { WasmHash::finalize128(&mut self.inner.wasm) }, _ => unsafe { core::hint::unreachable_unchecked() }, } } fn finalize256(&mut self) -> [u64; 4] { match self.tag { #[cfg(not(any( all(target_family = "wasm", target_feature = "simd128"), target_arch = "aarch64" )))] 0 => unsafe { PortableHash::finalize256(&mut self.inner.portable) }, #[cfg(target_arch = "x86_64")] 1 => unsafe { AvxHash::finalize256(&mut self.inner.avx) }, #[cfg(target_arch = "x86_64")] 2 => unsafe { SseHash::finalize256(&mut self.inner.sse) }, #[cfg(target_arch = "aarch64")] 3 => unsafe { NeonHash::finalize256(&mut self.inner.neon) }, #[cfg(all(target_family = "wasm", target_feature = "simd128"))] 4 => unsafe { WasmHash::finalize256(&mut self.inner.wasm) }, _ => unsafe { core::hint::unreachable_unchecked() }, } } } impl Default for HighwayHasher { fn default() -> Self { HighwayHasher::new(Key::default()) } } impl_write!(HighwayHasher); impl_hasher!(HighwayHasher); #[cfg(test)] mod tests { use super::*; #[test] fn test_has_debug_representation_with_data() { let hasher = HighwayHasher::new(Key::default()); let output = format!("{:?}", &hasher); assert!(output.contains("hasher: ")); } } highway-1.2.0/src/hash.rs000064400000000000000000000010451046102023000133440ustar 00000000000000use crate::builder::HighwayHasher; use crate::key::Key; use core::hash::BuildHasher; /// Constructs a hasher used in rust collections #[derive(Debug, Default)] pub struct HighwayBuildHasher { key: Key, } impl HighwayBuildHasher { /// Creates a new hash builder with a given key #[must_use] pub fn new(key: Key) -> Self { HighwayBuildHasher { key } } } impl BuildHasher for HighwayBuildHasher { type Hasher = HighwayHasher; fn build_hasher(&self) -> Self::Hasher { HighwayHasher::new(self.key) } } highway-1.2.0/src/internal.rs000064400000000000000000000055671046102023000142520ustar 00000000000000#[cfg(any( target_arch = "x86_64", target_arch = "aarch64", all(target_family = "wasm", target_feature = "simd128") ))] pub fn unordered_load3(from: &[u8]) -> u64 { if from.is_empty() { return 0; } let size_mod4 = from.len() % 4; u64::from(from[0]) + (u64::from(from[size_mod4 >> 1]) << 8) + (u64::from(from[size_mod4 - 1]) << 16) } pub const PACKET_SIZE: usize = 32; /// The c layout is needed as we'll be interpretting the buffer as different types and passing it /// to simd instructions, so we need to subscribe to the whole "do what C does", else we will /// segfault. #[repr(C)] #[derive(Default, Debug, Clone, Copy)] pub struct HashPacket { buf: [u8; PACKET_SIZE], buf_index: usize, } impl HashPacket { #[inline] pub fn len(&self) -> usize { self.buf_index } #[inline] pub fn is_empty(&self) -> bool { self.buf_index == 0 } #[inline] pub fn as_slice(&self) -> &[u8] { debug_assert!(self.buf_index <= self.buf.len(), "buf index too long"); self.buf.get(..self.buf_index).unwrap_or(&self.buf) } #[inline] pub fn inner(&self) -> &[u8; PACKET_SIZE] { &self.buf } #[inline] pub fn fill<'a>(&mut self, data: &'a [u8]) -> Option<&'a [u8]> { let dest = self.buf.get_mut(self.buf_index..).unwrap_or_default(); if dest.len() > data.len() { dest[..data.len()].copy_from_slice(data); self.buf_index += data.len(); None } else { let (head, tail) = data.split_at(dest.len()); dest.copy_from_slice(head); self.buf_index = PACKET_SIZE; Some(tail) } } #[inline] pub fn set_to(&mut self, data: &[u8]) { self.buf_index = data.len(); if !data.is_empty() { self.buf[..data.len()].copy_from_slice(data); } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_hash_packet() { let mut packet: HashPacket = Default::default(); for i in 0..31 { assert_eq!(&vec![0; i as usize][..], packet.as_slice()); if let Some(_) = packet.fill(&[0]) { assert!(false); } assert_eq!(i + 1, packet.len() as u8); assert_eq!(&vec![0; (i + 1) as usize][..], packet.as_slice()); } } #[test] fn test_hash_cusp_full_packet() { let mut packet: HashPacket = Default::default(); assert_eq!(Some(&[][..]), packet.fill(&[0; 32])); assert_eq!(32, packet.len()); } #[test] fn test_hash_packet_set_to() { let mut packet: HashPacket = Default::default(); for i in 0..31 { let d = vec![0; i as usize]; packet.set_to(&d[..]); assert_eq!(&d[..], packet.as_slice()); assert_eq!(d.len(), packet.len()); } } } highway-1.2.0/src/key.rs000064400000000000000000000004731046102023000132150ustar 00000000000000use core::ops::Index; /// Key used in `HighwayHash` that will drastically change the hash outputs. #[derive(Debug, Default, Clone, Copy)] #[repr(align(32))] pub struct Key(pub [u64; 4]); impl Index for Key { type Output = u64; fn index(&self, index: usize) -> &u64 { &self.0[index] } } highway-1.2.0/src/lib.rs000064400000000000000000000144361046102023000131770ustar 00000000000000/*! This crate is a native Rust port of [Google's HighwayHash](https://github.com/google/highwayhash), which is a fast, keyed, and strong hash function, whose output is hardware independent. ## Caution `HighwayHash` (the algorithm) has not undergone extensive cryptanalysis like SipHash (the default hashing algorithm in Rust), but according to the authors, `HighwayHash` output bits are uniformly distributed and should withstand differential and rotational attacks. Hence `HighwayHash` is referred to as a strong hash function, not a cryptographic hash function. I encourage anyone interested to [peruse the paper](https://arxiv.org/abs/1612.06257) to understand the risks. ## Examples The quickest way to get started: ```rust use highway::{HighwayHasher, HighwayHash}; let res: u64 = HighwayHasher::default().hash64(&[]); let res2: [u64; 2] = HighwayHasher::default().hash128(&[]); let res3: [u64; 4] = HighwayHasher::default().hash256(&[]); ``` A more complete tour of the API follows: ```rust use highway::{HighwayHasher, HighwayHash, Key}; // HighwayHash requires a key that should be hidden from attackers // to ensure outputs are unpredictable, so attackers can't mount // DoS attacks. let key = Key([1, 2, 3, 4]); // A HighwayHasher is the recommended approach to hashing, // as it will select the fastest algorithm available let mut hasher = HighwayHasher::new(key); // Append some data hasher.append(&[255]); // After all data has been appended, you ask for // 64, 128, or 256bit output. The hasher is consumed // after finalization. let res: u64 = hasher.finalize64(); assert_eq!(0x07858f24d_2d79b2b2, res); ``` Creating a 128bit and 256bit hash is just as simple. ```rust use highway::{HighwayHasher, HighwayHash, Key}; // Generate 128bit hash let key = Key([1, 2, 3, 4]); let mut hasher128 = HighwayHasher::new(key); hasher128.append(&[255]); let res128: [u64; 2] = hasher128.finalize128(); assert_eq!([0xbb007d2462e77f3c, 0x224508f916b3991f], res128); // Generate 256bit hash let key = Key([1, 2, 3, 4]); let mut hasher256 = HighwayHasher::new(key); hasher256.append(&[255]); let res256: [u64; 4] = hasher256.finalize256(); let expected: [u64; 4] = [ 0x7161cadbf7cd70e1, 0xaac4905de62b2f5e, 0x7b02b936933faa7, 0xc8efcfc45b239f8d, ]; assert_eq!(expected, res256); ``` Use highway hash in standard rust collections ```rust # #[cfg(feature = "std")] # { use std::collections::HashMap; use highway::{HighwayBuildHasher, Key}; let mut map = HashMap::with_hasher(HighwayBuildHasher::new(Key([ 0xcbf29ce484222325, 0xc3a5c85c97cb3127, 0xb492b66fbe98f273, 0x9ae16a3b2f90404f, ]))); map.insert(1, 2); assert_eq!(map.get(&1), Some(&2)); # } ``` Or if utilizing a key is not important, one can use the default ```rust # #[cfg(feature = "std")] # { use std::collections::HashMap; use std::hash::BuildHasherDefault; use highway::HighwayHasher; let mut map = HashMap::with_hasher(BuildHasherDefault::::default()); map.insert(1, 2); assert_eq!(map.get(&1), Some(&2)); # } ``` Hashing a file, or anything implementing `Read` ```rust # #[cfg(not(feature = "std"))] fn main() { } # #[cfg(feature = "std")] # fn main() -> std::io::Result<()> { use std::hash::Hasher; use highway::{PortableHash, HighwayHash}; let mut file = &b"hello world"[..]; // We're using the `PortableHash` to show importing a specific hashing // implementation (all hash outputs are already portable / hardware agnostic). // The main reason for directly using `PortableHash` would be if avoiding // `unsafe` code blocks is a top priority. let mut hasher = PortableHash::default(); std::io::copy(&mut file, &mut hasher)?; let hash64 = hasher.finish(); // core Hasher API let hash256 = hasher.finalize256(); // HighwayHash API # Ok(()) # } ``` ## Use Cases `HighwayHash` can be used against untrusted user input where weak hashes can't be used due to exploitation, verified cryptographic hashes are too slow, and a strong hash function meets requirements. Some specific scenarios given by the authors of HighwayHash: - Use 64bit hashes to for authenticating short lived messages - Use 256bit hashes for checksums. Think file storage (S3) or any longer lived data where there is a need for strong guarantees against collisions. `HighwayHash` may not be a good fit if the payloads trend small (< 100 bytes) and speed is up of the utmost importance, as HighwayHash hits its stride at larger payloads. ## Wasm SIMD When deploying HighwayHash to a Wasm environment, one can opt into using the Wasm SIMD instructions by adding a Rust flag: ```bash RUSTFLAGS="-C target-feature=+simd128" wasm-pack build ``` Then `HighwayHasher` will automatically defer to the Wasm SIMD implementation via `WasmHash`. Once opted in, the execution environment must support Wasm SIMD instructions, which Chrome, Firefox, and Node LTS have stabilized since mid-2021. The opt in is required as there is not a way for Wasm to detect SIMD capabilities at runtime. The mere presence of Wasm SIMD instructions will cause incompatible environments to fail to compile, so it is recommended to provide two Wasm payloads to downstream users: one with SIMD enabled and one without. ### `no_std` crates Be aware that the `no_std` version is unable to detect CPU features and so will always default to the portable implementation. If building for a known SSE 4.1 or AVX 2 machine (and the majority of machines in the last decade will support SSE 4.1), then explicitly enable the target feature: ```bash RUSTFLAGS="-C target-feature=+sse4.1" cargo test RUSTFLAGS="-C target-feature=+avx2" cargo test ``` */ #![allow(non_snake_case)] #![cfg_attr(all(not(feature = "std"), not(test)), no_std)] #![warn(missing_docs)] #![deny(unsafe_code)] #[macro_use] mod macros; mod builder; mod hash; mod internal; mod key; mod portable; mod traits; pub use crate::builder::HighwayHasher; pub use crate::hash::HighwayBuildHasher; pub use crate::key::Key; pub use crate::portable::PortableHash; pub use crate::traits::HighwayHash; #[cfg(target_arch = "aarch64")] mod aarch64; #[cfg(all(target_family = "wasm", target_feature = "simd128"))] mod wasm; #[cfg(target_arch = "x86_64")] mod x86; #[cfg(target_arch = "aarch64")] pub use crate::aarch64::NeonHash; #[cfg(target_arch = "x86_64")] pub use crate::x86::{AvxHash, SseHash}; #[cfg(all(target_family = "wasm", target_feature = "simd128"))] pub use crate::wasm::WasmHash; highway-1.2.0/src/macros.rs000064400000000000000000000027071046102023000137130ustar 00000000000000macro_rules! impl_write { ($hasher_struct:ty) => { #[cfg(feature = "std")] impl ::std::io::Write for $hasher_struct { fn write(&mut self, bytes: &[u8]) -> ::std::io::Result { $crate::HighwayHash::append(self, bytes); Ok(bytes.len()) } fn flush(&mut self) -> ::std::io::Result<()> { Ok(()) } } }; } macro_rules! impl_hasher { ($hasher_struct:ty) => { impl ::core::hash::Hasher for $hasher_struct { fn write(&mut self, bytes: &[u8]) { $crate::HighwayHash::append(self, bytes); } fn finish(&self) -> u64 { // Reasons why we need to clone. finalize64` mutates internal state so either we need our // Hasher to consume itself or receive a mutable reference on `finish`. We receive neither, // due to finish being a misnomer (additional writes could be expected) and it's intended // for the hasher to merely return it's current state. The issue with HighwayHash is that // there are several rounds of permutations when finalizing a value, and internal state is // modified during that process. We work around these constraints by cloning the hasher and // finalizing that one. $crate::HighwayHash::finalize64(self.clone()) } } }; } highway-1.2.0/src/portable.rs000064400000000000000000000215521046102023000142360ustar 00000000000000use crate::internal::{HashPacket, PACKET_SIZE}; use crate::key::Key; use crate::traits::HighwayHash; /// Hardware agnostic HighwayHash implementation. /// /// "Portable" refers to being able to run on any platform Rust will run on, and /// is not referring to the output, as the HighwayHash is already hardware /// agnostic across all implementations. /// /// The main reason for directly using `PortableHash` would be if avoiding /// `unsafe` code blocks is a top priority. #[derive(Debug, Default, Clone)] pub struct PortableHash { v0: [u64; 4], v1: [u64; 4], mul0: [u64; 4], mul1: [u64; 4], buffer: HashPacket, } impl HighwayHash for PortableHash { #[inline] fn append(&mut self, data: &[u8]) { self.append(data); } #[inline] fn finalize64(mut self) -> u64 { Self::finalize64(&mut self) } #[inline] fn finalize128(mut self) -> [u64; 2] { Self::finalize128(&mut self) } #[inline] fn finalize256(mut self) -> [u64; 4] { Self::finalize256(&mut self) } } impl PortableHash { /// Create a new `PortableHash` from a `Key` #[must_use] pub fn new(key: Key) -> Self { let mul0 = [ 0xdbe6_d5d5_fe4c_ce2f, 0xa409_3822_299f_31d0, 0x1319_8a2e_0370_7344, 0x243f_6a88_85a3_08d3, ]; let mul1 = [ 0x3bd3_9e10_cb0e_f593, 0xc0ac_f169_b5f1_8a8c, 0xbe54_66cf_34e9_0c6c, 0x4528_21e6_38d0_1377, ]; PortableHash { v0: [ mul0[0] ^ key[0], mul0[1] ^ key[1], mul0[2] ^ key[2], mul0[3] ^ key[3], ], v1: [ mul1[0] ^ ((key[0] >> 32) | (key[0] << 32)), mul1[1] ^ ((key[1] >> 32) | (key[1] << 32)), mul1[2] ^ ((key[2] >> 32) | (key[2] << 32)), mul1[3] ^ ((key[3] >> 32) | (key[3] << 32)), ], mul0, mul1, buffer: HashPacket::default(), } } pub(crate) fn finalize64(&mut self) -> u64 { if !self.buffer.is_empty() { self.update_remainder(); } for _i in 0..4 { self.permute_and_update(); } self.v0[0] .wrapping_add(self.v1[0]) .wrapping_add(self.mul0[0]) .wrapping_add(self.mul1[0]) } pub(crate) fn finalize128(&mut self) -> [u64; 2] { if !self.buffer.is_empty() { self.update_remainder(); } for _i in 0..6 { self.permute_and_update(); } let low = self.v0[0] .wrapping_add(self.mul0[0]) .wrapping_add(self.v1[2]) .wrapping_add(self.mul1[2]); let high = self.v0[1] .wrapping_add(self.mul0[1]) .wrapping_add(self.v1[3]) .wrapping_add(self.mul1[3]); [low, high] } pub(crate) fn finalize256(&mut self) -> [u64; 4] { if !self.buffer.is_empty() { self.update_remainder(); } for _i in 0..10 { self.permute_and_update(); } let (lowest, low) = PortableHash::module_reduction( self.v1[1].wrapping_add(self.mul1[1]), self.v1[0].wrapping_add(self.mul1[0]), self.v0[1].wrapping_add(self.mul0[1]), self.v0[0].wrapping_add(self.mul0[0]), ); let (high, highest) = PortableHash::module_reduction( self.v1[3].wrapping_add(self.mul1[3]), self.v1[2].wrapping_add(self.mul1[2]), self.v0[3].wrapping_add(self.mul0[3]), self.v0[2].wrapping_add(self.mul0[2]), ); [lowest, low, high, highest] } fn module_reduction(a3_unmasked: u64, a2: u64, a1: u64, a0: u64) -> (u64, u64) { let a3 = a3_unmasked & 0x3FFF_FFFF_FFFF_FFFF; let high = a1 ^ ((a3 << 1) | (a2 >> 63)) ^ ((a3 << 2) | (a2 >> 62)); let low = a0 ^ (a2 << 1) ^ (a2 << 2); (low, high) } fn permute(v: &[u64; 4]) -> [u64; 4] { [ (v[2] >> 32) | (v[2] << 32), (v[3] >> 32) | (v[3] << 32), (v[0] >> 32) | (v[0] << 32), (v[1] >> 32) | (v[1] << 32), ] } fn permute_and_update(&mut self) { let permuted: [u64; 4] = PortableHash::permute(&self.v0); self.update(permuted); } fn update(&mut self, lanes: [u64; 4]) { for (i, lane) in lanes.iter().enumerate() { self.v1[i] = self.v1[i].wrapping_add(*lane); } for i in 0..4 { self.v1[i] = self.v1[i].wrapping_add(self.mul0[i]); } for i in 0..4 { self.mul0[i] ^= (self.v1[i] & 0xffff_ffff).wrapping_mul(self.v0[i] >> 32); } for i in 0..4 { self.v0[i] = self.v0[i].wrapping_add(self.mul1[i]); } for i in 0..4 { self.mul1[i] ^= (self.v0[i] & 0xffff_ffff).wrapping_mul(self.v1[i] >> 32); } PortableHash::zipper_merge_and_add(self.v1[1], self.v1[0], &mut self.v0, 1, 0); PortableHash::zipper_merge_and_add(self.v1[3], self.v1[2], &mut self.v0, 3, 2); PortableHash::zipper_merge_and_add(self.v0[1], self.v0[0], &mut self.v1, 1, 0); PortableHash::zipper_merge_and_add(self.v0[3], self.v0[2], &mut self.v1, 3, 2); } fn zipper_merge_and_add(v1: u64, v0: u64, lane: &mut [u64; 4], add1: usize, add0: usize) { lane[add0] = lane[add0].wrapping_add( (((v0 & 0xff00_0000) | (v1 & 0x00ff_0000_0000)) >> 24) | (((v0 & 0xff00_0000_0000) | (v1 & 0x00ff_0000_0000_0000)) >> 16) | (v0 & 0x00ff_0000) | ((v0 & 0xff00) << 32) | ((v1 & 0xff00_0000_0000_0000) >> 8) | (v0 << 56), ); lane[add1] = lane[add1].wrapping_add( (((v1 & 0xff00_0000) | (v0 & 0x00ff_0000_0000)) >> 24) | (v1 & 0x00ff_0000) | ((v1 & 0xff00_0000_0000) >> 16) | ((v1 & 0xff00) << 24) | ((v0 & 0x00ff_0000_0000_0000) >> 8) | ((v1 & 0xff) << 48) | (v0 & 0xff00_0000_0000_0000), ); } fn data_to_lanes(d: &[u8]) -> [u64; 4] { let mut result = [0u64; 4]; for (x, dest) in d.chunks_exact(8).zip(result.iter_mut()) { *dest = u64::from_le_bytes([x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7]]); } result } fn rotate_32_by(count: u64, lanes: &mut [u64; 4]) { for lane in lanes.iter_mut() { let half0: u32 = *lane as u32; let half1: u32 = (*lane >> 32) as u32; *lane = u64::from((half0 << count) | (half0 >> (32 - count))); *lane |= u64::from((half1 << count) | (half1 >> (32 - count))) << 32; } } fn update_lanes(&mut self, size: u64) { for i in 0..4 { self.v0[i] = self.v0[i].wrapping_add((size << 32) + size); } PortableHash::rotate_32_by(size, &mut self.v1); } fn remainder(bytes: &[u8]) -> [u8; 32] { let mut packet: [u8; 32] = [0u8; 32]; if bytes.len() > packet.len() { debug_assert!(false, "remainder bytes must be less than 32"); return packet; } let size_mod4 = bytes.len() & 3; let remainder_jump = bytes.len() & !3; let remainder = &bytes[remainder_jump..]; let size = bytes.len() as u64; packet[..remainder_jump].clone_from_slice(&bytes[..remainder_jump]); if size & 16 != 0 { let muxed = packet[28..] .iter_mut() .zip(&bytes[remainder_jump + size_mod4 - 4..]); for (p, b) in muxed { *p = *b; } } else if size_mod4 != 0 { packet[16] = remainder[0]; packet[16 + 1] = remainder[size_mod4 >> 1]; packet[16 + 2] = remainder[size_mod4 - 1]; } packet } fn update_remainder(&mut self) { let size = self.buffer.len() as u64; self.update_lanes(size); let packet = PortableHash::remainder(self.buffer.as_slice()); self.update(PortableHash::data_to_lanes(&packet)); } fn append(&mut self, data: &[u8]) { if self.buffer.is_empty() { let mut chunks = data.chunks_exact(PACKET_SIZE); for chunk in chunks.by_ref() { self.update(Self::data_to_lanes(chunk)); } self.buffer.set_to(chunks.remainder()); } else if let Some(tail) = self.buffer.fill(data) { self.update(Self::data_to_lanes(self.buffer.inner())); let mut chunks = tail.chunks_exact(PACKET_SIZE); for chunk in chunks.by_ref() { self.update(Self::data_to_lanes(chunk)); } self.buffer.set_to(chunks.remainder()); } } } impl_write!(PortableHash); impl_hasher!(PortableHash); highway-1.2.0/src/traits.rs000064400000000000000000000030741046102023000137330ustar 00000000000000/// The common set of methods for hashing data. pub trait HighwayHash: Sized { /// Convenience function for hashing all data in a single call and receiving a 64bit hash. /// Results are equivalent to appending the data manually. fn hash64(mut self, data: &[u8]) -> u64 { self.append(data); self.finalize64() } /// Convenience function for hashing all data in a single call and receiving a 128bit hash. /// Results are equivalent to appending the data manually. fn hash128(mut self, data: &[u8]) -> [u64; 2] { self.append(data); self.finalize128() } /// Convenience function for hashing all data in a single call and receiving a 256bit hash. /// Results are equivalent to appending the data manually. fn hash256(mut self, data: &[u8]) -> [u64; 4] { self.append(data); self.finalize256() } /// Adds data to be hashed. If it is important, the performance characteristics of this /// function differs depending on the amount of data previously hashed and the amount of /// data to be hashed. For instance, if one appends 50, 1 byte slices then appending the 32nd /// byte will have a performance outlier as the internal 32 byte block is complete and internally processed. fn append(&mut self, data: &[u8]); /// Consumes the hasher to return the 64bit hash fn finalize64(self) -> u64; /// Consumes the hasher to return the 128bit hash fn finalize128(self) -> [u64; 2]; /// Consumes the hasher to return the 256bit hash fn finalize256(self) -> [u64; 4]; } highway-1.2.0/src/wasm.rs000064400000000000000000000363621046102023000134020ustar 00000000000000use crate::internal::{unordered_load3, HashPacket, PACKET_SIZE}; use crate::{HighwayHash, Key}; use core::arch::wasm32::{self, v128}; use core::ops::{ Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, ShlAssign, ShrAssign, SubAssign, }; /// HighwayHash powered by Wasm SIMD instructions #[derive(Debug, Default, Clone)] pub struct WasmHash { v0L: V2x64U, v0H: V2x64U, v1L: V2x64U, v1H: V2x64U, mul0L: V2x64U, mul0H: V2x64U, mul1L: V2x64U, mul1H: V2x64U, buffer: HashPacket, } impl HighwayHash for WasmHash { #[inline] fn append(&mut self, data: &[u8]) { self.append(data); } #[inline] fn finalize64(mut self) -> u64 { Self::finalize64(&mut self) } #[inline] fn finalize128(mut self) -> [u64; 2] { Self::finalize128(&mut self) } #[inline] fn finalize256(mut self) -> [u64; 4] { Self::finalize256(&mut self) } } impl WasmHash { /// Creates a new `WasmHash` based on Wasm SIMD extension pub fn new(key: Key) -> Self { let init0L = V2x64U::new(0xa409_3822_299f_31d0, 0xdbe6_d5d5_fe4c_ce2f); let init0H = V2x64U::new(0x243f_6a88_85a3_08d3, 0x1319_8a2e_0370_7344); let init1L = V2x64U::new(0xc0ac_f169_b5f1_8a8c, 0x3bd3_9e10_cb0e_f593); let init1H = V2x64U::new(0x4528_21e6_38d0_1377, 0xbe54_66cf_34e9_0c6c); let keyL = V2x64U::new(key[1], key[0]); let keyH = V2x64U::new(key[3], key[2]); WasmHash { v0L: keyL ^ init0L, v0H: keyH ^ init0H, v1L: keyL.rotate_by_32() ^ init1L, v1H: keyH.rotate_by_32() ^ init1H, mul0L: init0L, mul0H: init0H, mul1L: init1L, mul1H: init1H, buffer: HashPacket::default(), } } fn zipper_merge(v: &V2x64U) -> V2x64U { let ignored = v.0; let res = wasm32::u8x16_shuffle::<3, 12, 2, 5, 1, 14, 0, 15, 11, 4, 10, 13, 6, 9, 7, 8>( v.0, ignored, ); V2x64U::from(res) } fn update(&mut self, (packetH, packetL): (V2x64U, V2x64U)) { self.v1L += packetL; self.v1H += packetH; self.v1L += self.mul0L; self.v1H += self.mul0H; self.mul0L ^= V2x64U(_mm_mul_epu32(self.v1L.0, self.v0L.rotate_by_32().0)); self.mul0H ^= V2x64U(_mm_mul_epu32(self.v1H.0, _mm_srli_epi64(self.v0H.0, 32))); self.v0L += self.mul1L; self.v0H += self.mul1H; self.mul1L ^= V2x64U(_mm_mul_epu32(self.v0L.0, self.v1L.rotate_by_32().0)); self.mul1H ^= V2x64U(_mm_mul_epu32(self.v0H.0, _mm_srli_epi64(self.v1H.0, 32))); self.v0L += WasmHash::zipper_merge(&self.v1L); self.v0H += WasmHash::zipper_merge(&self.v1H); self.v1L += WasmHash::zipper_merge(&self.v0L); self.v1H += WasmHash::zipper_merge(&self.v0H); } fn permute_and_update(&mut self) { let low = self.v0L.rotate_by_32(); let high = self.v0H.rotate_by_32(); self.update((low, high)); } pub(crate) fn finalize64(&mut self) -> u64 { if !self.buffer.is_empty() { self.update_remainder(); } for _i in 0..4 { self.permute_and_update(); } let sum0 = self.v0L + self.mul0L; let sum1 = self.v1L + self.mul1L; let hash = sum0 + sum1; wasm32::u64x2_extract_lane::<1>(hash.0) } pub(crate) fn finalize128(&mut self) -> [u64; 2] { if !self.buffer.is_empty() { self.update_remainder(); } for _i in 0..6 { self.permute_and_update(); } let sum0 = self.v0L + self.mul0L; let sum1 = self.v1H + self.mul1H; let hash = sum0 + sum1; [ wasm32::u64x2_extract_lane::<1>(hash.0), wasm32::u64x2_extract_lane::<0>(hash.0), ] } pub(crate) fn finalize256(&mut self) -> [u64; 4] { if !self.buffer.is_empty() { self.update_remainder(); } for _i in 0..10 { self.permute_and_update(); } let sum0L = self.v0L + self.mul0L; let sum1L = self.v1L + self.mul1L; let sum0H = self.v0H + self.mul0H; let sum1H = self.v1H + self.mul1H; let hashL = WasmHash::modular_reduction(&sum1L, &sum0L); let hashH = WasmHash::modular_reduction(&sum1H, &sum0H); [ wasm32::u64x2_extract_lane::<1>(hashL.0), wasm32::u64x2_extract_lane::<0>(hashL.0), wasm32::u64x2_extract_lane::<1>(hashH.0), wasm32::u64x2_extract_lane::<0>(hashH.0), ] } fn modular_reduction(x: &V2x64U, init: &V2x64U) -> V2x64U { let zero = V2x64U::default(); let repl = wasm32::i32x4_replace_lane::<1>(zero.0, 0x8000_0000_u32 as i32); let sign_bit128 = V2x64U::from(repl); let top_bits2 = V2x64U::from(_mm_srli_epi64(x.0, 62)); let shifted1_unmasked = *x + *x; let top_bits1 = V2x64U::from(_mm_srli_epi64(x.0, 63)); let shifted2 = shifted1_unmasked + shifted1_unmasked; let new_low_bits2 = V2x64U::from(_mm_slli_si128_8(top_bits2.0)); let shifted1 = shifted1_unmasked.and_not(&sign_bit128); let new_low_bits1 = V2x64U::from(_mm_slli_si128_8(top_bits1.0)); *init ^ shifted2 ^ new_low_bits2 ^ shifted1 ^ new_low_bits1 } fn load_multiple_of_four(bytes: &[u8]) -> V2x64U { let mut data = bytes; let mut mask4 = V2x64U::new(0, 0xFFFF_FFFF); let mut ret = if bytes.len() >= 8 { let lo = le_u64(bytes); mask4 = V2x64U::from(_mm_slli_si128_8(mask4.0)); data = &bytes[8..]; V2x64U::new(0, lo) } else { V2x64U::new(0, 0) }; if let Some(d) = data.get(..4) { let last4 = u32::from_le_bytes([d[0], d[1], d[2], d[3]]); let broadcast = V2x64U::from(wasm32::u32x4(last4, last4, last4, last4)); ret |= broadcast & mask4; } ret } fn remainder(bytes: &[u8]) -> (V2x64U, V2x64U) { let size_mod32 = bytes.len(); let size_mod4 = size_mod32 & 3; if bytes.len() > 32 { debug_assert!(false, "remainder bytes must be less than 32"); return (V2x64U::zeroed(), V2x64U::zeroed()); } if bytes.len() >= 16 { let packetLL = le_u64(bytes); let packetLH = le_u64(&bytes[8..]); let packetL = V2x64U::new(packetLH, packetLL); let packett = WasmHash::load_multiple_of_four(&bytes[16..]); let remainder = &bytes[(size_mod32 & !3) + size_mod4 - 4..]; let last4 = i32::from_le_bytes([remainder[0], remainder[1], remainder[2], remainder[3]]); let packetH = V2x64U::from(wasm32::i32x4_replace_lane::<1>(packett.0, last4)); (packetH, packetL) } else { let remainder = &bytes[size_mod32 & !3..]; let packetL = WasmHash::load_multiple_of_four(bytes); let last4 = unordered_load3(remainder); let packetH = V2x64U::new(0, last4); (packetH, packetL) } } fn update_remainder(&mut self) { let size = self.buffer.len() as i32; let vsize_mod32 = wasm32::i32x4(size, size, size, size); self.v0L += V2x64U::from(vsize_mod32); self.v0H += V2x64U::from(vsize_mod32); self.rotate_32_by(size as u32); let packet = WasmHash::remainder(self.buffer.as_slice()); self.update(packet); } fn rotate_32_by(&mut self, count: u32) { let vL = &mut self.v1L; let vH = &mut self.v1H; let count_left = count; let count_right = 32 - count; let shifted_leftL = V2x64U::from(_mm_sll_epi32(vL.0, count_left)); let shifted_leftH = V2x64U::from(_mm_sll_epi32(vH.0, count_left)); let shifted_rightL = V2x64U::from(_mm_srl_epi32(vL.0, count_right)); let shifted_rightH = V2x64U::from(_mm_srl_epi32(vH.0, count_right)); *vL = shifted_leftL | shifted_rightL; *vH = shifted_leftH | shifted_rightH; } #[inline] fn data_to_lanes(packet: &[u8]) -> (V2x64U, V2x64U) { let mut lanes = [0u64; 4]; for (x, dest) in packet.chunks_exact(8).zip(lanes.iter_mut()) { *dest = le_u64(x); } let hi = V2x64U::new(lanes[3], lanes[2]); let lo = V2x64U::new(lanes[1], lanes[0]); (hi, lo) } fn append(&mut self, data: &[u8]) { if self.buffer.is_empty() { let mut chunks = data.chunks_exact(PACKET_SIZE); for chunk in chunks.by_ref() { self.update(Self::data_to_lanes(chunk)); } self.buffer.set_to(chunks.remainder()); } else if let Some(tail) = self.buffer.fill(data) { self.update(Self::data_to_lanes(self.buffer.inner())); let mut chunks = tail.chunks_exact(PACKET_SIZE); for chunk in chunks.by_ref() { self.update(Self::data_to_lanes(chunk)); } self.buffer.set_to(chunks.remainder()); } } } impl_write!(WasmHash); impl_hasher!(WasmHash); #[inline] fn le_u64(x: &[u8]) -> u64 { u64::from_le_bytes([x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7]]) } #[inline] fn _mm_mul_epu32(a: wasm32::v128, b: wasm32::v128) -> wasm32::v128 { let mask = wasm32::u32x4(0xFFFF_FFFF, 0, 0xFFFF_FFFF, 0); let lo_a_0 = wasm32::v128_and(a, mask); let lo_b_0 = wasm32::v128_and(b, mask); wasm32::u64x2_mul(lo_a_0, lo_b_0) } #[inline] fn _mm_srli_epi64(a: wasm32::v128, amt: u32) -> wasm32::v128 { wasm32::u64x2_shr(a, amt) } #[inline] fn _mm_srl_epi32(a: wasm32::v128, amt: u32) -> wasm32::v128 { wasm32::u32x4_shr(a, amt) } #[inline] fn _mm_sll_epi32(a: wasm32::v128, amt: u32) -> wasm32::v128 { wasm32::u32x4_shl(a, amt) } #[inline] fn _mm_slli_si128_8(a: wasm32::v128) -> wasm32::v128 { // aka _mm_bslli_si128_8 let zero = wasm32::u64x2(0, 0); wasm32::u64x2_shuffle::<1, 2>(a, zero) } #[derive(Clone, Copy)] pub struct V2x64U(pub v128); impl Default for V2x64U { fn default() -> Self { V2x64U::zeroed() } } impl core::fmt::Debug for V2x64U { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "V2x64U: {:?}", self.as_arr()) } } impl V2x64U { #[inline] fn zeroed() -> Self { Self::new(0, 0) } #[inline] pub fn new(hi: u64, low: u64) -> Self { V2x64U(wasm32::u64x2(hi, low)) } fn as_arr(&self) -> [u64; 2] { let hi = wasm32::u64x2_extract_lane::<0>(self.0); let lo = wasm32::u64x2_extract_lane::<1>(self.0); [lo, hi] } #[inline] pub fn rotate_by_32(&self) -> Self { let ignored = self.0; let res = wasm32::u32x4_shuffle::<1, 0, 3, 2>(self.0, ignored); V2x64U::from(res) } #[inline] pub fn and_not(&self, neg_mask: &V2x64U) -> Self { V2x64U::from(wasm32::v128_andnot(self.0, neg_mask.0)) } #[inline] fn add_assign(&mut self, other: Self) { self.0 = wasm32::u64x2_add(self.0, other.0) } #[inline] fn sub_assign(&mut self, other: Self) { self.0 = wasm32::u64x2_sub(self.0, other.0) } #[inline] fn bitand_assign(&mut self, other: Self) { self.0 = wasm32::v128_and(self.0, other.0) } #[inline] fn bitor_assign(&mut self, other: Self) { self.0 = wasm32::v128_or(self.0, other.0) } #[inline] fn bitxor_assign(&mut self, other: Self) { self.0 = wasm32::v128_xor(self.0, other.0) } #[inline] fn shl_assign(&mut self, count: u32) { self.0 = wasm32::u64x2_shl(self.0, count) } #[inline] fn shr_assign(&mut self, count: u32) { self.0 = wasm32::u64x2_shr(self.0, count) } } impl From for V2x64U { #[inline] fn from(v: v128) -> Self { V2x64U(v) } } impl AddAssign for V2x64U { #[inline] fn add_assign(&mut self, other: Self) { self.add_assign(other) } } impl SubAssign for V2x64U { #[inline] fn sub_assign(&mut self, other: Self) { self.sub_assign(other) } } impl BitAndAssign for V2x64U { #[inline] fn bitand_assign(&mut self, other: Self) { self.bitand_assign(other) } } impl BitAnd for V2x64U { type Output = Self; #[inline] fn bitand(self, other: Self) -> Self { let mut new = V2x64U(self.0); new &= other; new } } impl BitOrAssign for V2x64U { #[inline] fn bitor_assign(&mut self, other: Self) { self.bitor_assign(other) } } impl BitOr for V2x64U { type Output = Self; #[inline] fn bitor(self, other: Self) -> Self { let mut new = V2x64U(self.0); new |= other; new } } impl BitXorAssign for V2x64U { #[inline] fn bitxor_assign(&mut self, other: Self) { self.bitxor_assign(other) } } impl Add for V2x64U { type Output = Self; #[inline] fn add(self, other: Self) -> Self { let mut new = V2x64U(self.0); new += other; new } } impl BitXor for V2x64U { type Output = Self; #[inline] fn bitxor(self, other: Self) -> Self { let mut new = V2x64U(self.0); new ^= other; new } } impl ShlAssign for V2x64U { #[inline] fn shl_assign(&mut self, count: u32) { self.shl_assign(count) } } impl ShrAssign for V2x64U { #[inline] fn shr_assign(&mut self, count: u32) { self.shr_assign(count) } } #[cfg(test)] pub mod tests { use super::*; use wasm_bindgen_test::*; #[wasm_bindgen_test] fn test_as_arr() { let x = V2x64U::new(55, 1); let res = x.as_arr(); assert_eq!(res, [1, 55]); } #[wasm_bindgen_test] fn test_rotate_by_32() { let x = V2x64U::new(0x0264_432C_CD8A_70E0, 0x0B28_E3EF_EBB3_172D); let y = x.rotate_by_32(); let res = y.as_arr(); assert_eq!(res, [0xEBB3_172D_0B28_E3EF, 0xCD8A_70E0_0264_432C]); } #[wasm_bindgen_test] fn test_add() { let x = V2x64U::new(55, 1); let y = V2x64U::new(0x0264_432C_CD8A_70E0, 0x0B28E_3EFE_BB3_172D); let z = x + y; assert_eq!(z.as_arr(), [0x0B28_E3EF_EBB3_172E, 0x2644_32CC_D8A7_117]); } #[wasm_bindgen_test] fn test_mm_srli_epi64() { let x = V2x64U::new(0x0264_432C_CD8A_70E0, 0x0B28E_3EFE_BB3_172D); let y = V2x64U::from(_mm_srli_epi64(x.0, 33)); assert_eq!(y.as_arr(), [0x0000_0000_0594_71F7, 0x0000_0000_0132_2196]); } #[wasm_bindgen_test] fn test_zipper_merge() { let x = V2x64U::new(0x0264_432C_CD8A_70E0, 0x0B28_E3EF_EBB3_172D); let y = WasmHash::zipper_merge(&x); assert_eq!(y.as_arr(), [0x2D02_1764_E3B3_2CEB, 0x0BE0_2870_438A_EFCD]); } #[wasm_bindgen_test] fn test_mm_mul_epu32() { let x = V2x64U::new(0x0264_432C_CD8A_70E0, 0x0B28_E3EF_EBB3_172D); let y = V2x64U::new(0x0B28_E3EF_EBB3_172D, 0x0264_432C_CD8A_70E0); let z = V2x64U::from(_mm_mul_epu32(x.0, y.0)); assert_eq!(z.as_arr(), [0xBD3D_E006_1E19_F760, 0xBD3D_E006_1E19_F760]); } #[wasm_bindgen_test] fn test_mm_slli_si128_8() { let x = V2x64U::new(0, 0xFFFF_FFFF); let y = V2x64U::from(_mm_slli_si128_8(x.0)); assert_eq!(y.as_arr(), [0, 0xFFFF_FFFF]); } } highway-1.2.0/src/x86/avx.rs000064400000000000000000000225621046102023000136530ustar 00000000000000#![allow(unsafe_code)] use super::{v2x64u::V2x64U, v4x64u::V4x64U}; use crate::internal::unordered_load3; use crate::internal::{HashPacket, PACKET_SIZE}; use crate::key::Key; use crate::traits::HighwayHash; use core::arch::x86_64::*; /// AVX empowered implementation that will only work on `x86_64` with avx2 enabled at the CPU /// level. #[derive(Debug, Default, Clone)] pub struct AvxHash { v0: V4x64U, v1: V4x64U, mul0: V4x64U, mul1: V4x64U, buffer: HashPacket, } impl HighwayHash for AvxHash { #[inline] fn append(&mut self, data: &[u8]) { unsafe { self.append(data); } } #[inline] fn finalize64(mut self) -> u64 { unsafe { Self::finalize64(&mut self) } } #[inline] fn finalize128(mut self) -> [u64; 2] { unsafe { Self::finalize128(&mut self) } } #[inline] fn finalize256(mut self) -> [u64; 4] { unsafe { Self::finalize256(&mut self) } } } impl AvxHash { /// Creates a new `AvxHash` while circumventing the runtime check for avx2. /// /// # Safety /// /// If called on a machine without avx2, a segfault will occur. Only use if you have /// control over the deployment environment and have either benchmarked that the runtime /// check is significant or are unable to check for avx2 capabilities #[must_use] #[target_feature(enable = "avx2")] pub unsafe fn force_new(key: Key) -> Self { let mul0 = V4x64U::new( 0x243f_6a88_85a3_08d3, 0x1319_8a2e_0370_7344, 0xa409_3822_299f_31d0, 0xdbe6_d5d5_fe4c_ce2f, ); let mul1 = V4x64U::new( 0x4528_21e6_38d0_1377, 0xbe54_66cf_34e9_0c6c, 0xc0ac_f169_b5f1_8a8c, 0x3bd3_9e10_cb0e_f593, ); let key = V4x64U::from(_mm256_load_si256(key.0.as_ptr().cast::<__m256i>())); AvxHash { v0: key ^ mul0, v1: key.rotate_by_32() ^ mul1, mul0, mul1, buffer: HashPacket::default(), } } /// Creates a new `AvxHash` if the avx2 feature is detected. #[must_use] pub fn new(key: Key) -> Option { #[cfg(feature = "std")] { if is_x86_feature_detected!("avx2") { Some(unsafe { Self::force_new(key) }) } else { None } } #[cfg(not(feature = "std"))] { let _key = key; None } } #[target_feature(enable = "avx2")] pub(crate) unsafe fn finalize64(&mut self) -> u64 { if !self.buffer.is_empty() { self.update_remainder(); } for _i in 0..4 { let permuted = AvxHash::permute(&self.v0); self.update(permuted); } let sum0 = V2x64U::from(_mm256_castsi256_si128((self.v0 + self.mul0).0)); let sum1 = V2x64U::from(_mm256_castsi256_si128((self.v1 + self.mul1).0)); let hash = sum0 + sum1; let mut result: u64 = 0; // Each lane is sufficiently mixed, so just truncate to 64 bits. _mm_storel_epi64(core::ptr::addr_of_mut!(result).cast::<__m128i>(), hash.0); result } #[target_feature(enable = "avx2")] pub(crate) unsafe fn finalize128(&mut self) -> [u64; 2] { if !self.buffer.is_empty() { self.update_remainder(); } for _i in 0..6 { let permuted = AvxHash::permute(&self.v0); self.update(permuted); } let sum0 = V2x64U::from(_mm256_castsi256_si128((self.v0 + self.mul0).0)); let sum1 = V2x64U::from(_mm256_extracti128_si256((self.v1 + self.mul1).0, 1)); let hash = sum0 + sum1; let mut result: [u64; 2] = [0; 2]; _mm_storeu_si128(result.as_mut_ptr().cast::<__m128i>(), hash.0); result } #[target_feature(enable = "avx2")] pub(crate) unsafe fn finalize256(&mut self) -> [u64; 4] { if !self.buffer.is_empty() { self.update_remainder(); } for _i in 0..10 { let permuted = AvxHash::permute(&self.v0); self.update(permuted); } let sum0 = self.v0 + self.mul0; let sum1 = self.v1 + self.mul1; let hash = AvxHash::modular_reduction(&sum1, &sum0); let mut result: [u64; 4] = [0; 4]; _mm256_storeu_si256(result.as_mut_ptr().cast::<__m256i>(), hash.0); result } #[inline] #[target_feature(enable = "avx2")] unsafe fn data_to_lanes(packet: &[u8]) -> V4x64U { V4x64U::from(_mm256_loadu_si256(packet.as_ptr().cast::<__m256i>())) } #[target_feature(enable = "avx2")] unsafe fn remainder(bytes: &[u8]) -> V4x64U { let size_mod32 = bytes.len(); let size256 = _mm256_broadcastd_epi32(_mm_cvtsi64_si128(size_mod32 as i64)); let size_mod4 = size_mod32 & 3; let size = _mm256_castsi256_si128(size256); if size_mod32 & 16 != 0 { let packetL = _mm_load_si128(bytes.as_ptr().cast::<__m128i>()); let int_mask = _mm_cmpgt_epi32(size, _mm_set_epi32(31, 27, 23, 19)); let int_lanes = _mm_maskload_epi32(bytes.as_ptr().offset(16).cast::(), int_mask); let remainder = &bytes[(size_mod32 & !3) + size_mod4 - 4..]; let last4 = i32::from_le_bytes([remainder[0], remainder[1], remainder[2], remainder[3]]); let packetH = _mm_insert_epi32(int_lanes, last4, 3); let packetL256 = _mm256_castsi128_si256(packetL); let packet = _mm256_inserti128_si256(packetL256, packetH, 1); V4x64U::from(packet) } else { let int_mask = _mm_cmpgt_epi32(size, _mm_set_epi32(15, 11, 7, 3)); let packetL = _mm_maskload_epi32(bytes.as_ptr().cast::(), int_mask); let remainder = &bytes[size_mod32 & !3..]; let last3 = unordered_load3(remainder); let packetH = _mm_cvtsi64_si128(last3 as i64); let packetL256 = _mm256_castsi128_si256(packetL); let packet = _mm256_inserti128_si256(packetL256, packetH, 1); V4x64U::from(packet) } } #[target_feature(enable = "avx2")] unsafe fn update_remainder(&mut self) { let size = self.buffer.len(); let size256 = _mm256_broadcastd_epi32(_mm_cvtsi64_si128(size as i64)); self.v0 += V4x64U::from(size256); let shifted_left = V4x64U::from(_mm256_sllv_epi32(self.v1.0, size256)); let tip = _mm256_broadcastd_epi32(_mm_cvtsi32_si128(32)); let shifted_right = V4x64U::from(_mm256_srlv_epi32(self.v1.0, _mm256_sub_epi32(tip, size256))); self.v1 = shifted_left | shifted_right; let packet = AvxHash::remainder(self.buffer.as_slice()); self.update(packet); } #[target_feature(enable = "avx2")] unsafe fn zipper_merge(v: &V4x64U) -> V4x64U { let hi = 0x0708_0609_0D0A_040B; let lo = 0x000F_010E_0502_0C03; v.shuffle(&V4x64U::new(hi, lo, hi, lo)) } #[target_feature(enable = "avx2")] unsafe fn update(&mut self, packet: V4x64U) { self.v1 += packet; self.v1 += self.mul0; self.mul0 ^= self.v1.mul_low32(&self.v0.shr_by_32()); self.v0 += self.mul1; self.mul1 ^= self.v0.mul_low32(&self.v1.shr_by_32()); self.v0 += AvxHash::zipper_merge(&self.v1); self.v1 += AvxHash::zipper_merge(&self.v0); } #[target_feature(enable = "avx2")] unsafe fn permute(v: &V4x64U) -> V4x64U { let indices = V4x64U::new( 0x0000_0002_0000_0003, 0x0000_0000_0000_0001, 0x0000_0006_0000_0007, 0x0000_0004_0000_0005, ); V4x64U::from(_mm256_permutevar8x32_epi32(v.0, indices.0)) } #[target_feature(enable = "avx2")] unsafe fn modular_reduction(x: &V4x64U, init: &V4x64U) -> V4x64U { let top_bits2 = V4x64U::from(_mm256_srli_epi64(x.0, 62)); let ones = V4x64U::from(_mm256_cmpeq_epi64(x.0, x.0)); let shifted1_unmasked = *x + *x; let top_bits1 = V4x64U::from(_mm256_srli_epi64(x.0, 63)); let upper_8bytes = V4x64U::from(_mm256_slli_si256(ones.0, 8)); let shifted2 = shifted1_unmasked + shifted1_unmasked; let upper_bit_of_128 = V4x64U::from(_mm256_slli_epi64(upper_8bytes.0, 63)); let zero = V4x64U::from(_mm256_setzero_si256()); let new_low_bits2 = V4x64U::from(_mm256_unpacklo_epi64(zero.0, top_bits2.0)); let shifted1 = shifted1_unmasked.and_not(&upper_bit_of_128); let new_low_bits1 = V4x64U::from(_mm256_unpacklo_epi64(zero.0, top_bits1.0)); *init ^ shifted2 ^ new_low_bits2 ^ shifted1 ^ new_low_bits1 } #[target_feature(enable = "avx2")] unsafe fn append(&mut self, data: &[u8]) { if self.buffer.is_empty() { let mut chunks = data.chunks_exact(PACKET_SIZE); for chunk in chunks.by_ref() { self.update(Self::data_to_lanes(chunk)); } self.buffer.set_to(chunks.remainder()); } else if let Some(tail) = self.buffer.fill(data) { self.update(Self::data_to_lanes(self.buffer.inner())); let mut chunks = tail.chunks_exact(PACKET_SIZE); for chunk in chunks.by_ref() { self.update(Self::data_to_lanes(chunk)); } self.buffer.set_to(chunks.remainder()); } } } impl_write!(AvxHash); impl_hasher!(AvxHash); highway-1.2.0/src/x86/macros.rs000064400000000000000000000007121046102023000143320ustar 00000000000000/// The function, [_MM_SHUFFLE](https://doc.rust-lang.org/core/arch/x86_64/fn._MM_SHUFFLE.html) is /// only supported on nightly and there has been [some controversy /// around](https://github.com/rust-lang-nursery/stdsimd/issues/522) it regarding the type /// signature, so the safe route here is to just go with our own macro. macro_rules! _mm_shuffle { ($z:expr, $y:expr, $x:expr, $w:expr) => { ($z << 6) | ($y << 4) | ($x << 2) | $w }; } highway-1.2.0/src/x86/mod.rs000064400000000000000000000001601046102023000136220ustar 00000000000000#[macro_use] mod macros; mod avx; mod sse; mod v2x64u; mod v4x64u; pub use avx::AvxHash; pub use sse::SseHash; highway-1.2.0/src/x86/sse.rs000064400000000000000000000253041046102023000136440ustar 00000000000000#![allow(unsafe_code)] use super::v2x64u::V2x64U; use crate::internal::unordered_load3; use crate::internal::{HashPacket, PACKET_SIZE}; use crate::key::Key; use crate::traits::HighwayHash; use core::arch::x86_64::*; /// SSE empowered implementation that will only work on `x86_64` with sse 4.1 enabled at the CPU /// level. #[derive(Debug, Default, Clone)] pub struct SseHash { v0L: V2x64U, v0H: V2x64U, v1L: V2x64U, v1H: V2x64U, mul0L: V2x64U, mul0H: V2x64U, mul1L: V2x64U, mul1H: V2x64U, buffer: HashPacket, } impl HighwayHash for SseHash { #[inline] fn append(&mut self, data: &[u8]) { unsafe { self.append(data); } } #[inline] fn finalize64(mut self) -> u64 { unsafe { Self::finalize64(&mut self) } } #[inline] fn finalize128(mut self) -> [u64; 2] { unsafe { Self::finalize128(&mut self) } } #[inline] fn finalize256(mut self) -> [u64; 4] { unsafe { Self::finalize256(&mut self) } } } impl SseHash { /// Creates a new `SseHash` while circumventing the runtime check for sse4.1. /// /// # Safety /// /// If called on a machine without sse4.1, a segfault will occur. Only use if you have /// control over the deployment environment and have either benchmarked that the runtime /// check is significant or are unable to check for sse4.1 capabilities #[must_use] #[target_feature(enable = "sse4.1")] pub unsafe fn force_new(key: Key) -> Self { let init0L = V2x64U::new(0xa409_3822_299f_31d0, 0xdbe6_d5d5_fe4c_ce2f); let init0H = V2x64U::new(0x243f_6a88_85a3_08d3, 0x1319_8a2e_0370_7344); let init1L = V2x64U::new(0xc0ac_f169_b5f1_8a8c, 0x3bd3_9e10_cb0e_f593); let init1H = V2x64U::new(0x4528_21e6_38d0_1377, 0xbe54_66cf_34e9_0c6c); let key_ptr = key.0.as_ptr().cast::<__m128i>(); let keyL = V2x64U::from(_mm_loadu_si128(key_ptr)); let keyH = V2x64U::from(_mm_loadu_si128(key_ptr.add(1))); SseHash { v0L: keyL ^ init0L, v0H: keyH ^ init0H, v1L: keyL.rotate_by_32() ^ init1L, v1H: keyH.rotate_by_32() ^ init1H, mul0L: init0L, mul0H: init0H, mul1L: init1L, mul1H: init1H, buffer: HashPacket::default(), } } /// Create a new `SseHash` if the sse4.1 feature is detected #[must_use] pub fn new(key: Key) -> Option { #[cfg(feature = "std")] { if is_x86_feature_detected!("sse4.1") { Some(unsafe { Self::force_new(key) }) } else { None } } #[cfg(not(feature = "std"))] { let _key = key; None } } #[target_feature(enable = "sse4.1")] unsafe fn zipper_merge(v: &V2x64U) -> V2x64U { v.shuffle(&V2x64U::new(0x0708_0609_0D0A_040B, 0x000F_010E_0502_0C03)) } #[target_feature(enable = "sse4.1")] unsafe fn update(&mut self, (packetH, packetL): (V2x64U, V2x64U)) { self.v1L += packetL; self.v1H += packetH; self.v1L += self.mul0L; self.v1H += self.mul0H; self.mul0L ^= V2x64U(_mm_mul_epu32(self.v1L.0, self.v0L.rotate_by_32().0)); self.mul0H ^= V2x64U(_mm_mul_epu32(self.v1H.0, _mm_srli_epi64(self.v0H.0, 32))); self.v0L += self.mul1L; self.v0H += self.mul1H; self.mul1L ^= V2x64U(_mm_mul_epu32(self.v0L.0, self.v1L.rotate_by_32().0)); self.mul1H ^= V2x64U(_mm_mul_epu32(self.v0H.0, _mm_srli_epi64(self.v1H.0, 32))); self.v0L += SseHash::zipper_merge(&self.v1L); self.v0H += SseHash::zipper_merge(&self.v1H); self.v1L += SseHash::zipper_merge(&self.v0L); self.v1H += SseHash::zipper_merge(&self.v0H); } #[target_feature(enable = "sse4.1")] unsafe fn permute_and_update(&mut self) { let low = self.v0L.rotate_by_32(); let high = self.v0H.rotate_by_32(); self.update((low, high)); } #[target_feature(enable = "sse4.1")] pub(crate) unsafe fn finalize64(&mut self) -> u64 { if !self.buffer.is_empty() { self.update_remainder(); } for _i in 0..4 { self.permute_and_update(); } let sum0 = self.v0L + self.mul0L; let sum1 = self.v1L + self.mul1L; let hash = sum0 + sum1; let mut result: u64 = 0; _mm_storel_epi64(core::ptr::addr_of_mut!(result).cast::<__m128i>(), hash.0); result } #[target_feature(enable = "sse4.1")] pub(crate) unsafe fn finalize128(&mut self) -> [u64; 2] { if !self.buffer.is_empty() { self.update_remainder(); } for _i in 0..6 { self.permute_and_update(); } let sum0 = self.v0L + self.mul0L; let sum1 = self.v1H + self.mul1H; let hash = sum0 + sum1; let mut result: [u64; 2] = [0; 2]; _mm_storeu_si128(result.as_mut_ptr().cast::<__m128i>(), hash.0); result } #[target_feature(enable = "sse4.1")] pub(crate) unsafe fn finalize256(&mut self) -> [u64; 4] { if !self.buffer.is_empty() { self.update_remainder(); } for _i in 0..10 { self.permute_and_update(); } let sum0L = self.v0L + self.mul0L; let sum1L = self.v1L + self.mul1L; let sum0H = self.v0H + self.mul0H; let sum1H = self.v1H + self.mul1H; let hashL = SseHash::modular_reduction(&sum1L, &sum0L); let hashH = SseHash::modular_reduction(&sum1H, &sum0H); let mut result: [u64; 4] = [0; 4]; let ptr = result.as_mut_ptr().cast::<__m128i>(); _mm_storeu_si128(ptr, hashL.0); _mm_storeu_si128(ptr.add(1), hashH.0); result } #[target_feature(enable = "sse4.1")] unsafe fn modular_reduction(x: &V2x64U, init: &V2x64U) -> V2x64U { let zero = V2x64U::default(); let sign_bit128 = V2x64U::from(_mm_insert_epi32(zero.0, 0x8000_0000_u32 as i32, 3)); let top_bits2 = V2x64U::from(_mm_srli_epi64(x.0, 62)); let shifted1_unmasked = *x + *x; let top_bits1 = V2x64U::from(_mm_srli_epi64(x.0, 63)); let shifted2 = shifted1_unmasked + shifted1_unmasked; let new_low_bits2 = V2x64U::from(_mm_slli_si128(top_bits2.0, 8)); let shifted1 = shifted1_unmasked.and_not(&sign_bit128); let new_low_bits1 = V2x64U::from(_mm_slli_si128(top_bits1.0, 8)); *init ^ shifted2 ^ new_low_bits2 ^ shifted1 ^ new_low_bits1 } #[target_feature(enable = "sse4.1")] unsafe fn load_multiple_of_four(bytes: &[u8]) -> V2x64U { let mut data = bytes; let mut mask4 = V2x64U::from(_mm_cvtsi64_si128(0xFFFF_FFFF)); let mut ret = if bytes.len() >= 8 { mask4 = V2x64U::from(_mm_slli_si128(mask4.0, 8)); data = &bytes[8..]; V2x64U::from(_mm_loadl_epi64(bytes.as_ptr().cast::<__m128i>())) } else { V2x64U::new(0, 0) }; if let Some(d) = data.get(..4) { let last4 = i32::from_le_bytes([d[0], d[1], d[2], d[3]]); let word2 = _mm_cvtsi32_si128(last4); let broadcast = V2x64U::from(_mm_shuffle_epi32(word2, 0)); ret |= broadcast & mask4; } ret } #[target_feature(enable = "sse4.1")] unsafe fn remainder(bytes: &[u8]) -> (V2x64U, V2x64U) { let size_mod32 = bytes.len(); let size_mod4 = size_mod32 & 3; if size_mod32 & 16 != 0 { let packetL = V2x64U::from(_mm_loadu_si128(bytes.as_ptr().cast::<__m128i>())); let packett = SseHash::load_multiple_of_four(&bytes[16..]); let remainder = &bytes[(size_mod32 & !3) + size_mod4 - 4..]; let last4 = i32::from_le_bytes([remainder[0], remainder[1], remainder[2], remainder[3]]); let packetH = V2x64U::from(_mm_insert_epi32(packett.0, last4, 3)); (packetH, packetL) } else { let remainder = &bytes[size_mod32 & !3..]; let packetL = SseHash::load_multiple_of_four(bytes); let last4 = unordered_load3(remainder); let packetH = V2x64U::from(_mm_cvtsi64_si128(last4 as i64)); (packetH, packetL) } } #[target_feature(enable = "sse4.1")] unsafe fn update_remainder(&mut self) { let size = self.buffer.len(); let vsize_mod32 = _mm_set1_epi32(size as i32); self.v0L += V2x64U::from(vsize_mod32); self.v0H += V2x64U::from(vsize_mod32); self.rotate_32_by(size as i64); let packet = SseHash::remainder(self.buffer.as_slice()); self.update(packet); } #[target_feature(enable = "sse4.1")] unsafe fn rotate_32_by(&mut self, count: i64) { let vL = &mut self.v1L; let vH = &mut self.v1H; let count_left = _mm_cvtsi64_si128(count); let count_right = _mm_cvtsi64_si128(32 - count); let shifted_leftL = V2x64U::from(_mm_sll_epi32(vL.0, count_left)); let shifted_leftH = V2x64U::from(_mm_sll_epi32(vH.0, count_left)); let shifted_rightL = V2x64U::from(_mm_srl_epi32(vL.0, count_right)); let shifted_rightH = V2x64U::from(_mm_srl_epi32(vH.0, count_right)); *vL = shifted_leftL | shifted_rightL; *vH = shifted_leftH | shifted_rightH; } #[inline] #[target_feature(enable = "sse4.1")] unsafe fn data_to_lanes(packet: &[u8]) -> (V2x64U, V2x64U) { let ptr = packet.as_ptr().cast::<__m128i>(); let packetL = V2x64U::from(_mm_loadu_si128(ptr)); let packetH = V2x64U::from(_mm_loadu_si128(ptr.add(1))); (packetH, packetL) } #[target_feature(enable = "sse4.1")] unsafe fn append(&mut self, data: &[u8]) { if self.buffer.is_empty() { let mut chunks = data.chunks_exact(PACKET_SIZE); for chunk in chunks.by_ref() { self.update(Self::data_to_lanes(chunk)); } self.buffer.set_to(chunks.remainder()); } else if let Some(tail) = self.buffer.fill(data) { self.update(Self::data_to_lanes(self.buffer.inner())); let mut chunks = tail.chunks_exact(PACKET_SIZE); for chunk in chunks.by_ref() { self.update(Self::data_to_lanes(chunk)); } self.buffer.set_to(chunks.remainder()); } } } impl_write!(SseHash); impl_hasher!(SseHash); #[cfg(test)] mod tests { use super::*; #[cfg_attr(miri, ignore)] #[test] fn test_zipper_merge() { unsafe { let x = V2x64U::new(0x0264_432C_CD8A_70E0, 0x0B28_E3EF_EBB3_172D); let y = SseHash::zipper_merge(&x); assert_eq!(y.as_arr(), [0x2D02_1764_E3B3_2CEB, 0x0BE0_2870_438A_EFCD]); } } } highway-1.2.0/src/x86/v2x64u.rs000064400000000000000000000145751046102023000141400ustar 00000000000000#![allow(unsafe_code)] use core::arch::x86_64::*; use core::ops::{ Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, ShlAssign, ShrAssign, SubAssign, }; #[derive(Clone, Copy)] pub struct V2x64U(pub __m128i); impl Default for V2x64U { #[inline] fn default() -> Self { unsafe { V2x64U::zeroed() } } } impl core::fmt::Debug for V2x64U { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "V2x64U: {:?}", unsafe { self.as_arr() }) } } impl V2x64U { #[inline] #[target_feature(enable = "sse4.1")] unsafe fn zeroed() -> Self { V2x64U(_mm_setzero_si128()) } #[inline] #[target_feature(enable = "sse4.1")] pub unsafe fn new(hi: u64, low: u64) -> Self { V2x64U(_mm_set_epi64x(hi as i64, low as i64)) } #[target_feature(enable = "sse4.1")] pub unsafe fn as_arr(&self) -> [u64; 2] { let mut arr: [u64; 2] = [0, 0]; _mm_storeu_si128(arr.as_mut_ptr().cast::<__m128i>(), self.0); arr } #[inline] #[target_feature(enable = "sse4.1")] pub unsafe fn rotate_by_32(&self) -> Self { V2x64U(_mm_shuffle_epi32(self.0, _mm_shuffle!(2, 3, 0, 1))) } #[inline] #[target_feature(enable = "sse4.1")] pub unsafe fn shuffle(&self, mask: &V2x64U) -> Self { V2x64U::from(_mm_shuffle_epi8(self.0, mask.0)) } #[inline] #[target_feature(enable = "sse4.1")] pub unsafe fn and_not(&self, neg_mask: &V2x64U) -> Self { V2x64U::from(_mm_andnot_si128(neg_mask.0, self.0)) } #[inline] #[target_feature(enable = "sse4.1")] unsafe fn add_assign(&mut self, other: Self) { self.0 = _mm_add_epi64(self.0, other.0); } #[inline] #[target_feature(enable = "sse4.1")] unsafe fn sub_assign(&mut self, other: Self) { self.0 = _mm_sub_epi64(self.0, other.0); } #[inline] #[target_feature(enable = "sse4.1")] unsafe fn bitand_assign(&mut self, other: Self) { self.0 = _mm_and_si128(self.0, other.0); } #[inline] #[target_feature(enable = "sse4.1")] unsafe fn bitor_assign(&mut self, other: Self) { self.0 = _mm_or_si128(self.0, other.0); } #[inline] #[target_feature(enable = "sse4.1")] unsafe fn bitxor_assign(&mut self, other: Self) { self.0 = _mm_xor_si128(self.0, other.0); } #[inline] #[target_feature(enable = "sse4.1")] unsafe fn shl_assign(&mut self, count: __m128i) { self.0 = _mm_sll_epi64(self.0, count); } #[inline] #[target_feature(enable = "sse4.1")] unsafe fn shr_assign(&mut self, count: __m128i) { self.0 = _mm_srl_epi64(self.0, count); } } impl From<__m128i> for V2x64U { #[inline] fn from(v: __m128i) -> Self { V2x64U(v) } } impl AddAssign for V2x64U { #[inline] fn add_assign(&mut self, other: Self) { unsafe { self.add_assign(other) } } } impl SubAssign for V2x64U { #[inline] fn sub_assign(&mut self, other: Self) { unsafe { self.sub_assign(other) } } } impl BitAndAssign for V2x64U { #[inline] fn bitand_assign(&mut self, other: Self) { unsafe { self.bitand_assign(other) } } } impl BitAnd for V2x64U { type Output = Self; #[inline] fn bitand(self, other: Self) -> Self { let mut new = V2x64U(self.0); new &= other; new } } impl BitOrAssign for V2x64U { #[inline] fn bitor_assign(&mut self, other: Self) { unsafe { self.bitor_assign(other) } } } impl BitOr for V2x64U { type Output = Self; #[inline] fn bitor(self, other: Self) -> Self { let mut new = V2x64U(self.0); new |= other; new } } impl BitXorAssign for V2x64U { #[inline] fn bitxor_assign(&mut self, other: Self) { unsafe { self.bitxor_assign(other) } } } impl Add for V2x64U { type Output = Self; #[inline] fn add(self, other: Self) -> Self { let mut new = V2x64U(self.0); new += other; new } } impl BitXor for V2x64U { type Output = Self; #[inline] fn bitxor(self, other: Self) -> Self { let mut new = V2x64U(self.0); new ^= other; new } } impl ShlAssign<__m128i> for V2x64U { #[inline] fn shl_assign(&mut self, count: __m128i) { unsafe { self.shl_assign(count) } } } impl ShrAssign<__m128i> for V2x64U { #[inline] fn shr_assign(&mut self, count: __m128i) { unsafe { self.shr_assign(count) } } } #[cfg(test)] pub mod tests { use super::*; #[cfg_attr(miri, ignore)] #[test] fn test_as_arr() { unsafe { let x = V2x64U::new(55, 1); let res = x.as_arr(); assert_eq!(res, [1, 55]); } } #[cfg_attr(miri, ignore)] #[test] fn test_rotate_by_32() { unsafe { let x = V2x64U::new(0x0264_432C_CD8A_70E0, 0x0B28_E3EF_EBB3_172D); let y = x.rotate_by_32(); let res = y.as_arr(); assert_eq!(res, [0xEBB3_172D_0B28_E3EF, 0xCD8A_70E0_0264_432C]); } } #[cfg_attr(miri, ignore)] #[test] fn test_add() { unsafe { let x = V2x64U::new(55, 1); let y = V2x64U::new(0x0264_432C_CD8A_70E0, 0x0B28E_3EFE_BB3_172D); let z = x + y; assert_eq!(z.as_arr(), [0x0B28_E3EF_EBB3_172E, 0x2644_32CC_D8A7_117]); } } #[cfg_attr(miri, ignore)] #[test] fn test_mm_srli_epi64() { unsafe { let x = V2x64U::new(0x0264_432C_CD8A_70E0, 0x0B28E_3EFE_BB3_172D); let y = V2x64U::from(_mm_srli_epi64(x.0, 33)); assert_eq!(y.as_arr(), [0x0000_0000_0594_71F7, 0x0000_0000_0132_2196]); } } #[cfg_attr(miri, ignore)] #[test] fn test_mm_mul_epu32() { unsafe { let x = V2x64U::new(0x0264_432C_CD8A_70E0, 0x0B28_E3EF_EBB3_172D); let y = V2x64U::new(0x0B28_E3EF_EBB3_172D, 0x0264_432C_CD8A_70E0); let z = V2x64U::from(_mm_mul_epu32(x.0, y.0)); assert_eq!(z.as_arr(), [0xBD3D_E006_1E19_F760, 0xBD3D_E006_1E19_F760]); } } #[cfg_attr(miri, ignore)] #[test] fn test_mm_slli_si128_8() { unsafe { let x = V2x64U::new(0, 0xFFFF_FFFF); let y = V2x64U::from(_mm_slli_si128(x.0, 8)); assert_eq!(y.as_arr(), [0, 0xFFFF_FFFF]); } } } highway-1.2.0/src/x86/v4x64u.rs000064400000000000000000000106671046102023000141400ustar 00000000000000#![allow(unsafe_code)] use core::arch::x86_64::*; use core::ops::{ Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, SubAssign, }; #[derive(Clone, Copy)] pub struct V4x64U(pub __m256i); impl Default for V4x64U { #[inline] fn default() -> Self { unsafe { V4x64U::zeroed() } } } impl core::fmt::Debug for V4x64U { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "V4x64U: {:?}", unsafe { self.as_arr() }) } } macro_rules! _mm_shuffle { ($z:expr, $y:expr, $x:expr, $w:expr) => { ($z << 6) | ($y << 4) | ($x << 2) | $w }; } impl V4x64U { #[inline] #[target_feature(enable = "avx2")] pub unsafe fn zeroed() -> Self { V4x64U(_mm256_setzero_si256()) } #[inline] #[target_feature(enable = "avx2")] pub unsafe fn new(highest: u64, high: u64, low: u64, lowest: u64) -> Self { V4x64U(_mm256_set_epi64x( highest as i64, high as i64, low as i64, lowest as i64, )) } #[target_feature(enable = "avx2")] unsafe fn as_arr(&self) -> [u64; 4] { let mut arr: [u64; 4] = [0; 4]; _mm256_storeu_si256(arr.as_mut_ptr().cast::<__m256i>(), self.0); arr } #[inline] #[target_feature(enable = "avx2")] pub unsafe fn rotate_by_32(&self) -> Self { V4x64U(_mm256_shuffle_epi32(self.0, _mm_shuffle!(2, 3, 0, 1))) } #[inline] #[target_feature(enable = "avx2")] pub unsafe fn shr_by_32(&self) -> Self { V4x64U(_mm256_srli_epi64(self.0, 32)) } #[inline] #[target_feature(enable = "avx2")] pub unsafe fn shuffle(&self, mask: &V4x64U) -> Self { V4x64U::from(_mm256_shuffle_epi8(self.0, mask.0)) } #[inline] #[target_feature(enable = "avx2")] pub unsafe fn mul_low32(&self, x: &V4x64U) -> Self { V4x64U::from(_mm256_mul_epu32(self.0, x.0)) } #[inline] #[target_feature(enable = "avx2")] pub unsafe fn and_not(&self, neg_mask: &V4x64U) -> Self { V4x64U::from(_mm256_andnot_si256(neg_mask.0, self.0)) } #[inline] #[target_feature(enable = "avx2")] unsafe fn add_assign(&mut self, other: Self) { self.0 = _mm256_add_epi64(self.0, other.0); } #[inline] #[target_feature(enable = "avx2")] unsafe fn sub_assign(&mut self, other: Self) { self.0 = _mm256_sub_epi64(self.0, other.0); } #[inline] #[target_feature(enable = "avx2")] unsafe fn bitand_assign(&mut self, other: Self) { self.0 = _mm256_and_si256(self.0, other.0); } #[inline] #[target_feature(enable = "avx2")] unsafe fn bitor_assign(&mut self, other: Self) { self.0 = _mm256_or_si256(self.0, other.0); } #[inline] #[target_feature(enable = "avx2")] unsafe fn bitxor_assign(&mut self, other: Self) { self.0 = _mm256_xor_si256(self.0, other.0); } } impl From<__m256i> for V4x64U { #[inline] fn from(v: __m256i) -> Self { V4x64U(v) } } impl AddAssign for V4x64U { #[inline] fn add_assign(&mut self, other: Self) { unsafe { self.add_assign(other) } } } impl SubAssign for V4x64U { #[inline] fn sub_assign(&mut self, other: Self) { unsafe { self.sub_assign(other) } } } impl BitAndAssign for V4x64U { #[inline] fn bitand_assign(&mut self, other: Self) { unsafe { self.bitand_assign(other) } } } impl BitAnd for V4x64U { type Output = Self; #[inline] fn bitand(self, other: Self) -> Self { let mut new = V4x64U(self.0); new &= other; new } } impl BitOrAssign for V4x64U { #[inline] fn bitor_assign(&mut self, other: Self) { unsafe { self.bitor_assign(other) } } } impl BitOr for V4x64U { type Output = Self; #[inline] fn bitor(self, other: Self) -> Self { let mut new = V4x64U(self.0); new |= other; new } } impl BitXorAssign for V4x64U { #[inline] fn bitxor_assign(&mut self, other: Self) { unsafe { self.bitxor_assign(other) } } } impl Add for V4x64U { type Output = Self; #[inline] fn add(self, other: Self) -> Self { let mut new = V4x64U(self.0); new += other; new } } impl BitXor for V4x64U { type Output = Self; #[inline] fn bitxor(self, other: Self) -> Self { let mut new = V4x64U(self.0); new ^= other; new } }