├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ └── test.yml ├── .gitignore ├── Cargo.lock.msrv ├── Cargo.toml ├── LICENSE-MIT ├── CHANGELOG.md ├── benches └── mod.rs ├── examples └── seedrng.rs ├── README.md ├── practrand.out ├── src ├── lib.rs └── sip.rs └── LICENSE-APACHE /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: dhardy 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | /target 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | open-pull-requests-limit: 10 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "monthly" 12 | -------------------------------------------------------------------------------- /Cargo.lock.msrv: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "rand_core" 7 | version = "0.6.4" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 10 | 11 | [[package]] 12 | name = "rand_seeder" 13 | version = "0.3.0" 14 | dependencies = [ 15 | "rand_core", 16 | ] 17 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rand_seeder" 3 | version = "0.4.0" # NB: When modifying, also modify html_root_url in lib.rs 4 | authors = ["The Rand Project Developers"] 5 | license = "MIT OR Apache-2.0" 6 | readme = "README.md" 7 | repository = "https://github.com/rust-random/seeder" 8 | documentation = "https://docs.rs/rand_seeder" 9 | homepage = "https://rust-random.github.io/book/guide-seeding.html#a-string-or-any-hashable-data" 10 | description = """ 11 | A universal random number seeder based on SipHash. 12 | """ 13 | keywords = ["random", "rng", "seeder"] 14 | categories = ["algorithms", "no-std"] 15 | edition = "2021" 16 | rust-version = "1.63" 17 | 18 | [dependencies] 19 | rand_core = { version = "0.9.0", default-features = false } 20 | 21 | [lints.clippy] 22 | unit_arg = "allow" 23 | println_empty_string = "allow" 24 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright 2018 Developers of the Rand project 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | - Rename `Seeder::make_rng` → `Seeder::into_rng` 10 | 11 | ## [0.4.0] - 2025-01-27 12 | 13 | - Update to `rand_core` v0.9 14 | 15 | ## [0.3.0] - 2024-07-31 16 | 17 | * Update `rand_core` dependency to exactly `0.6`, dropping support for `rand_core <= 0.5`. This 18 | works around Cargo issues where unrelated updates can cause the `rand_core` dependency to "snap 19 | back" to older versions. 20 | 21 | ## [0.2.3] - 2022-02-24 22 | Fix undefined behaviour caused by narrowing provenance of slice pointer to a 23 | single element then reading multiple elements (#8). 24 | 25 | ## [0.2.2] - 2020-12-23 26 | Support `rand_core` v0.6 27 | Migrate to GitHub Actions CI 28 | 29 | ## [0.2.1] - 2020-05-22 30 | Add `Seeder::make_seed`. 31 | 32 | ## [0.2.0] - 2019-11-04 33 | Documentation reviewed and improved. 34 | 35 | ### Changes 36 | - Rename `SipHasher::make_rng` → `SipHasher::into_rng` 37 | - Rename `SipHasher::new_with_keys` → `SipHasher::from_keys` 38 | 39 | ## [0.1.0] - 2019-10-15 40 | Initial release 41 | -------------------------------------------------------------------------------- /benches/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Developers of the Rand project. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | #![feature(test)] 10 | 11 | extern crate rand_seeder; 12 | extern crate test; 13 | 14 | const RAND_BENCH_N: u64 = 1000; 15 | const BYTES_LEN: usize = 1024; 16 | 17 | use std::mem::size_of; 18 | use test::{black_box, Bencher}; 19 | 20 | use rand_seeder::rand_core::RngCore; 21 | use rand_seeder::{Seeder, SipRng}; 22 | 23 | #[bench] 24 | fn gen_bytes_siprng(b: &mut Bencher) { 25 | let mut rng: SipRng = Seeder::from("siprng").into_rng(); 26 | let mut buf = [0u8; BYTES_LEN]; 27 | b.iter(|| { 28 | for _ in 0..RAND_BENCH_N { 29 | rng.fill_bytes(&mut buf); 30 | black_box(buf); 31 | } 32 | }); 33 | b.bytes = BYTES_LEN as u64 * RAND_BENCH_N; 34 | } 35 | 36 | macro_rules! gen_uint { 37 | ($fnn:ident, $ty:ty, $m:ident, $gen:expr) => { 38 | #[bench] 39 | fn $fnn(b: &mut Bencher) { 40 | let mut rng: SipRng = $gen; 41 | b.iter(|| { 42 | let mut accum: $ty = 0; 43 | for _ in 0..RAND_BENCH_N { 44 | accum = accum.wrapping_add(rng.$m()); 45 | } 46 | accum 47 | }); 48 | b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; 49 | } 50 | }; 51 | } 52 | 53 | gen_uint!( 54 | gen_u32_siprng, 55 | u32, 56 | next_u32, 57 | Seeder::from("siprng").into_rng() 58 | ); 59 | gen_uint!( 60 | gen_u64_siprng, 61 | u64, 62 | next_u64, 63 | Seeder::from("siprng").into_rng() 64 | ); 65 | -------------------------------------------------------------------------------- /examples/seedrng.rs: -------------------------------------------------------------------------------- 1 | //! Test tool 2 | //! 3 | //! Can be used with practrand like so, where `HASHABLES` is optional input: 4 | //! ``` 5 | //! cargo run --release --example seedrng -- -s HASHABLES | practrand stdin64 6 | //! ``` 7 | 8 | extern crate rand_seeder; 9 | 10 | use rand_seeder::rand_core::RngCore; 11 | use rand_seeder::SipHasher; 12 | 13 | use std::env; 14 | use std::hash::Hasher; 15 | use std::io::{self, Write}; 16 | 17 | fn print_usage() { 18 | println!("Usage: seedrng [OPTIONS] SEED..."); 19 | println!("Constructs a pseudo-random number generator from the given"); 20 | println!("input and yields random output."); 21 | println!(""); 22 | println!("Options:"); 23 | println!("\t-h\tPrint this text"); 24 | println!( 25 | "\t-s\tOutput a continuous stream, suitable for use with Practrand (use stdin64 option)" 26 | ); 27 | } 28 | 29 | enum Output { 30 | Single, 31 | Stream, 32 | } 33 | 34 | fn main() { 35 | let mut output = Output::Single; 36 | let mut hasher = SipHasher::new(); 37 | 38 | for arg in env::args().skip(1) { 39 | if arg.starts_with("-") { 40 | match arg.as_str() { 41 | "-h" => return print_usage(), 42 | "-s" => output = Output::Stream, 43 | o => { 44 | eprintln!("Error: unrecognised option {}", o); 45 | return; 46 | } 47 | } 48 | } else { 49 | hasher.write(arg.as_bytes()); 50 | } 51 | } 52 | 53 | let mut rng = hasher.into_rng(); 54 | match output { 55 | Output::Single => { 56 | println!("{}", rng.next_u64()); 57 | } 58 | Output::Stream => { 59 | let stdout = io::stdout(); 60 | let mut handle = stdout.lock(); 61 | let mut buf = [0u8; 32]; 62 | 63 | loop { 64 | rng.fill_bytes(&mut buf); 65 | handle.write_all(&buf).unwrap(); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rand_seeder 2 | 3 | [![Build Status](https://github.com/rust-random/seeder/actions/workflows/test.yml/badge.svg)](https://github.com/rust-random/seeder/actions) 4 | [![Latest version](https://img.shields.io/crates/v/rand_seeder.svg)](https://crates.io/crates/rand_seeder) 5 | [![Documentation](https://docs.rs/rand_seeder/badge.svg)](https://docs.rs/rand_seeder) 6 | [![License](https://img.shields.io/crates/l/rand_seeder.svg)](https://github.com/rust-random/seeder#license) 7 | 8 | A universal seeder based on [SipHash]. 9 | 10 | This crate is designed for use with the [rand] crates, allowing any RNG 11 | supporting [`rand_core::SeedableRng`] to be seeded from any hashable value. 12 | It provides the following: 13 | 14 | - `SipHasher` is a portable implementation of SipHash-2-4. According to the 15 | authors, [SipHash] is a secure, fast and simple keyed hash function. 16 | - `SipRng` is a PRNG based on the `SipHash` state and mixing operations. 17 | It is statistically high-quality, passing practrand tests to at least 4 TiB. 18 | - `SipHasher::into_rng()` transitions a `SipHasher` into a `SipRng`, maintaining 19 | the full 256 bits of state. (This might break the hasher's security.) 20 | - `Seeder` is a convenience wrapper around the above (see example). 21 | 22 | Seeding is designed to be fast, robust, flexible and portable. This library is 23 | intended for use in simulations and games, allowing e.g. any keyword to 24 | reproduce a simulation or procedurally generated world. 25 | 26 | This library is not intended for cryptographic applications, and *definitely* 27 | not for password hashing. 28 | 29 | Example: 30 | 31 | ```rust 32 | use rand_core::RngCore; // for next_u32 33 | use rand_pcg::Pcg64; // or whatever you like 34 | use rand_seeder::Seeder; 35 | 36 | let mut rng: Pcg64 = Seeder::from("stripy zebra").into_rng(); 37 | println!("First value: {}", rng.next_u32()); 38 | ``` 39 | 40 | [Changelog](CHANGELOG.md) 41 | 42 | [SipHash]: https://en.wikipedia.org/wiki/SipHash 43 | [rand]: https://github.com/rust-random/rand 44 | [`rand_core::SeedableRng`]: https://docs.rs/rand_core/latest/rand_core/trait.SeedableRng.html 45 | 46 | # License 47 | 48 | `rand_seeder` is distributed under the terms of both the MIT license and the 49 | Apache License (Version 2.0). 50 | 51 | See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) for details. 52 | -------------------------------------------------------------------------------- /practrand.out: -------------------------------------------------------------------------------- 1 | $ cargo run --example seedrng a -s | practrand stdin64 2 | Finished dev [unoptimized + debuginfo] target(s) in 0.00s 3 | Running `target/debug/examples/seedrng a -s` 4 | RNG_test using PractRand version 0.93 5 | RNG = RNG_stdin64, seed = 0x1c863418 6 | test set = normal, folding = standard (64 bit) 7 | 8 | rng=RNG_stdin64, seed=0x1c863418 9 | length= 64 megabytes (2^26 bytes), time= 2.2 seconds 10 | no anomalies in 137 test result(s) 11 | 12 | rng=RNG_stdin64, seed=0x1c863418 13 | length= 128 megabytes (2^27 bytes), time= 4.8 seconds 14 | no anomalies in 148 test result(s) 15 | 16 | rng=RNG_stdin64, seed=0x1c863418 17 | length= 256 megabytes (2^28 bytes), time= 9.6 seconds 18 | Test Name Raw Processed Evaluation 19 | DC6-9x1Bytes-1 R= -4.1 p =1-5.2e-3 unusual 20 | ...and 158 test result(s) without anomalies 21 | 22 | rng=RNG_stdin64, seed=0x1c863418 23 | length= 512 megabytes (2^29 bytes), time= 18.7 seconds 24 | no anomalies in 169 test result(s) 25 | 26 | rng=RNG_stdin64, seed=0x1c863418 27 | length= 1 gigabyte (2^30 bytes), time= 36.5 seconds 28 | no anomalies in 180 test result(s) 29 | 30 | rng=RNG_stdin64, seed=0x1c863418 31 | length= 2 gigabytes (2^31 bytes), time= 73.7 seconds 32 | no anomalies in 191 test result(s) 33 | 34 | rng=RNG_stdin64, seed=0x1c863418 35 | length= 4 gigabytes (2^32 bytes), time= 152 seconds 36 | no anomalies in 201 test result(s) 37 | 38 | rng=RNG_stdin64, seed=0x1c863418 39 | length= 8 gigabytes (2^33 bytes), time= 306 seconds 40 | no anomalies in 212 test result(s) 41 | 42 | rng=RNG_stdin64, seed=0x1c863418 43 | length= 16 gigabytes (2^34 bytes), time= 611 seconds 44 | no anomalies in 223 test result(s) 45 | 46 | ^Erng=RNG_stdin64, seed=0x1c863418 47 | length= 32 gigabytes (2^35 bytes), time= 1226 seconds 48 | no anomalies in 233 test result(s) 49 | 50 | rng=RNG_stdin64, seed=0x1c863418 51 | length= 64 gigabytes (2^36 bytes), time= 2405 seconds 52 | no anomalies in 244 test result(s) 53 | 54 | rng=RNG_stdin64, seed=0x1c863418 55 | length= 128 gigabytes (2^37 bytes), time= 4643 seconds 56 | no anomalies in 255 test result(s) 57 | 58 | rng=RNG_stdin64, seed=0x1c863418 59 | length= 256 gigabytes (2^38 bytes), time= 9013 seconds 60 | no anomalies in 265 test result(s) 61 | 62 | rng=RNG_stdin64, seed=0x1c863418 63 | length= 512 gigabytes (2^39 bytes), time= 17788 seconds 64 | Test Name Raw Processed Evaluation 65 | [Low4/64]FPF-14+6/16:(1,14-0) R= -6.3 p =1-1.7e-5 unusual 66 | ...and 275 test result(s) without anomalies 67 | 68 | rng=RNG_stdin64, seed=0x1c863418 69 | length= 1 terabyte (2^40 bytes), time= 35308 seconds 70 | no anomalies in 287 test result(s) 71 | 72 | rng=RNG_stdin64, seed=0x1c863418 73 | length= 2 terabytes (2^41 bytes), time= 70681 seconds 74 | no anomalies in 297 test result(s) 75 | 76 | rng=RNG_stdin64, seed=0x1c863418 77 | length= 4 terabytes (2^42 bytes), time= 142581 seconds 78 | no anomalies in 308 test result(s) 79 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: [ master, '0.[0-9]+' ] 6 | pull_request: 7 | branches: [ master, '0.[0-9]+' ] 8 | schedule: 9 | - cron: '0 0 * * SUN' 10 | 11 | permissions: 12 | contents: read # to fetch code (actions/checkout) 13 | 14 | jobs: 15 | check-doc: 16 | name: Check doc, clippy, fmt 17 | runs-on: ubuntu-latest 18 | env: 19 | RUSTDOCFLAGS: "-Dwarnings --cfg docsrs -Zunstable-options --generate-link-to-definition" 20 | steps: 21 | - uses: actions/checkout@v6 22 | - name: Install toolchain 23 | uses: dtolnay/rust-toolchain@master 24 | with: 25 | toolchain: nightly 26 | components: clippy, rustfmt 27 | - name: Doc build 28 | run: cargo doc --all-features --no-deps 29 | - name: Clippy 30 | run: cargo clippy --workspace --all-targets -- -D warnings 31 | - name: rustfmt 32 | run: cargo fmt -- --check 33 | 34 | test: 35 | runs-on: ${{ matrix.os }} 36 | strategy: 37 | fail-fast: false 38 | matrix: 39 | include: 40 | - os: ubuntu-latest 41 | target: x86_64-unknown-linux-gnu 42 | toolchain: stable 43 | - os: macos-latest 44 | target: x86_64-apple-darwin 45 | toolchain: stable 46 | # TODO: also aarch64 / M1 47 | - os: windows-latest 48 | target: x86_64-pc-windows-gnu 49 | toolchain: stable 50 | - os: windows-latest 51 | target: x86_64-pc-windows-msvc 52 | toolchain: beta 53 | # Test both windows-gnu and windows-msvc; use beta rust on one 54 | - os: ubuntu-latest 55 | target: x86_64-unknown-linux-gnu 56 | variant: MSRV 57 | toolchain: 1.63.0 58 | - os: ubuntu-latest 59 | deps: sudo apt-get update ; sudo apt install gcc-multilib 60 | target: i686-unknown-linux-gnu 61 | toolchain: nightly 62 | - os: ubuntu-latest 63 | target: x86_64-unknown-linux-gnu 64 | toolchain: nightly 65 | variant: minimal_versions 66 | 67 | steps: 68 | - uses: actions/checkout@v6 69 | - name: MSRV 70 | if: ${{ matrix.variant == 'MSRV' }} 71 | run: cp Cargo.lock.msrv Cargo.lock 72 | - name: Install toolchain 73 | uses: dtolnay/rust-toolchain@master 74 | with: 75 | target: ${{ matrix.target }} 76 | toolchain: ${{ matrix.toolchain }} 77 | - run: ${{ matrix.deps }} 78 | - name: Maybe minimal versions 79 | if: ${{ matrix.variant == 'minimal_versions' }} 80 | run: | 81 | cargo generate-lockfile -Z minimal-versions 82 | - name: Test 83 | run: | 84 | cargo test --no-fail-fast --target ${{ matrix.target }} 85 | 86 | test-cross: 87 | runs-on: ${{ matrix.os }} 88 | strategy: 89 | fail-fast: false 90 | matrix: 91 | include: 92 | - os: ubuntu-latest 93 | target: powerpc-unknown-linux-gnu 94 | toolchain: stable 95 | 96 | steps: 97 | - uses: actions/checkout@v6 98 | - name: Install toolchain 99 | uses: dtolnay/rust-toolchain@master 100 | with: 101 | target: ${{ matrix.target }} 102 | toolchain: ${{ matrix.toolchain }} 103 | - name: Cache cargo plugins 104 | uses: actions/cache@v4 105 | with: 106 | path: ~/.cargo/bin/ 107 | key: ${{ runner.os }}-cargo-plugins 108 | - name: Install cross 109 | run: cargo install cross || true 110 | - name: Test 111 | run: | 112 | cross test --no-fail-fast 113 | 114 | test-miri: 115 | runs-on: ubuntu-latest 116 | steps: 117 | - uses: actions/checkout@v6 118 | - name: Install toolchain 119 | run: | 120 | rustup toolchain install nightly --component miri 121 | rustup override set nightly 122 | cargo miri setup 123 | - name: Test rand 124 | run: | 125 | cargo miri test 126 | 127 | test-no-std: 128 | runs-on: ubuntu-latest 129 | steps: 130 | - uses: actions/checkout@v6 131 | - name: Install toolchain 132 | uses: dtolnay/rust-toolchain@nightly 133 | with: 134 | target: thumbv6m-none-eabi 135 | - name: Build top-level only 136 | run: cargo build --target=thumbv6m-none-eabi --no-default-features 137 | 138 | test-ios: 139 | runs-on: macos-latest 140 | steps: 141 | - uses: actions/checkout@v6 142 | - name: Install toolchain 143 | uses: dtolnay/rust-toolchain@nightly 144 | with: 145 | target: aarch64-apple-ios 146 | - name: Build top-level only 147 | run: cargo build --target=aarch64-apple-ios 148 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Rust Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! A universal seeder based on [SipHash]. 12 | //! 13 | //! This crate is designed for use with the [rand] crates, allowing any RNG 14 | //! supporting [`SeedableRng`] to be seeded from any hashable value. 15 | //! 16 | //! Seeding is designed to be fast, robust, flexible and portable. This library 17 | //! is intended for use in simulations and games, allowing e.g. any keyword to 18 | //! reproduce a simulation or procedurally generated world. 19 | //! 20 | //! This library is not intended for cryptographic applications, and *definitely* 21 | //! not for password hashing. 22 | //! 23 | //! # Example 24 | //! 25 | //! ``` 26 | //! use rand_seeder::rand_core::RngCore; 27 | //! use rand_seeder::SipRng; // or any RNG supporting SeedableRng 28 | //! use rand_seeder::Seeder; 29 | //! 30 | //! let mut rng: SipRng = Seeder::from("stripy zebra").into_rng(); 31 | //! println!("A deterministic pseudo-random value: {}", rng.next_u32()); 32 | //! ``` 33 | //! 34 | //! [rand]: https://github.com/rust-random/rand 35 | //! [SipHash]: https://en.wikipedia.org/wiki/SipHash 36 | //! [`SeedableRng`]: rand_core::SeedableRng 37 | 38 | #![doc( 39 | html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", 40 | html_favicon_url = "https://www.rust-lang.org/favicon.ico", 41 | html_root_url = "https://docs.rs/rand_seeder/0.4.0" 42 | )] 43 | #![deny(missing_docs)] 44 | #![deny(missing_debug_implementations)] 45 | #![doc(test(attr(allow(unused_variables), deny(warnings))))] 46 | #![no_std] 47 | 48 | pub extern crate rand_core; 49 | 50 | mod sip; 51 | 52 | pub use sip::{SipHasher, SipRng}; 53 | 54 | use core::hash::Hash; 55 | use rand_core::{RngCore, SeedableRng}; 56 | 57 | /// A simple interface for universal seeding 58 | /// 59 | /// `Seeder` can be used to seed any [`SeedableRng`] from any hashable value. It 60 | /// is portable and reproducible, and should turn any input into a good RNG 61 | /// seed. It is intended for use in simulations and games where reproducibility 62 | /// is important. 63 | /// 64 | /// We do not recommend using `Seeder` for cryptographic applications and 65 | /// strongly advise against usage for authentication (password hashing). 66 | /// 67 | /// Example: 68 | /// 69 | /// ```rust 70 | /// # extern crate rand_core; 71 | /// # extern crate rand_seeder; 72 | /// use rand_core::RngCore; 73 | /// use rand_seeder::{Seeder, SipRng}; 74 | /// 75 | /// // Use any R: SeedableRng you like in place of SipRng: 76 | /// let mut rng: SipRng = Seeder::from("stripy zebra").into_rng(); 77 | /// println!("First value: {}", rng.next_u32()); 78 | /// ``` 79 | /// 80 | /// [`SeedableRng`]: rand_core::SeedableRng 81 | #[derive(Debug, Clone)] 82 | pub struct Seeder { 83 | rng: SipRng, 84 | } 85 | 86 | impl Seeder { 87 | /// Construct and seed a generator of any type `R: SeedableRng`. 88 | /// 89 | /// This function seeds a new RNG using an internal [`SipRng`] generator. 90 | /// Because of this, multiple independent RNGs may safely be constructed 91 | /// from a single [`Seeder`]. 92 | /// 93 | /// Alternatively, one can obtain a [`SipRng`] via 94 | /// `SipHasher::from(h).into_rng()`. 95 | #[inline] 96 | pub fn into_rng(&mut self) -> R { 97 | R::from_seed(self.make_seed()) 98 | } 99 | 100 | /// Make a seed 101 | /// 102 | /// This mutates the state internally, thus can be called multiple times to 103 | /// generate multiple independent seeds. 104 | #[inline] 105 | pub fn make_seed + Default>(&mut self) -> S { 106 | let mut seed = S::default(); 107 | self.rng.fill_bytes(seed.as_mut()); 108 | seed 109 | } 110 | } 111 | 112 | impl From for Seeder { 113 | #[inline] 114 | fn from(h: H) -> Seeder { 115 | let hasher = SipHasher::from(h); 116 | Seeder { 117 | rng: hasher.into_rng(), 118 | } 119 | } 120 | } 121 | 122 | #[cfg(test)] 123 | mod test { 124 | use super::*; 125 | 126 | #[test] 127 | fn make_seeder() { 128 | let _ = Seeder::from(0u64); 129 | let _ = Seeder::from("a static string"); 130 | let _ = Seeder::from([1u8, 2, 3]); 131 | } 132 | 133 | #[test] 134 | fn into_rng() { 135 | let mut seeder = Seeder::from("test string"); 136 | let mut rng = seeder.into_rng::(); 137 | assert_eq!(rng.next_u64(), 7267854722795183454); 138 | assert_eq!(rng.next_u64(), 602994585684902144); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | https://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | https://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/sip.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2012-2018 The Rust Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! An implementation of SipHash. 12 | //! 13 | //! Shamelessly stolen from the Rust std lib (libcore/hash/sip.rs) and adapted. 14 | //! 15 | //! Only the official variant, SipHash 2-4, is included. Other variants such 16 | //! as the reduced 1-3 (used by libstd's `DefaultHasher`) could be added easily. 17 | 18 | use core::marker::PhantomData; 19 | use core::{cmp, hash, mem, ptr, slice}; 20 | use rand_core::{impls, le, RngCore, SeedableRng}; 21 | 22 | /// A portable implementation of SipHash 2-4. 23 | /// 24 | /// This implementation will produce 8-byte (`u64`) output compliant with the 25 | /// reference implementation. Additionally, it can be extended into an RNG able 26 | /// to produce unlimited output via [`SipHasher::into_rng`]. 27 | /// 28 | /// [SipHash](https://en.wikipedia.org/wiki/SipHash) 29 | /// is a general-purpose hashing function: it runs at a good 30 | /// speed (competitive with Spooky and City) and permits strong keyed hashing. 31 | /// 32 | /// Although the SipHash algorithm is considered strong, it is not intended for 33 | /// cryptographic uses (e.g. authentication). 34 | #[derive(Debug, Clone, Default)] 35 | pub struct SipHasher { 36 | hasher: Hasher, 37 | } 38 | 39 | /// A generator built using SipHash's primitives. 40 | /// 41 | /// [`SipRng`] is statistically high-quality, passing practrand tests to at 42 | /// least 4 TiB. It is also reasonably fast, though not quite competitive with 43 | /// the best non-cryptographic RNGs or optimised block RNGs such as ChaCha. 44 | /// 45 | /// This implementation is fixed to use two "compression" rounds between output 46 | /// values (similar to SipHash 2-4). Construction via [`SipHasher::into_rng`] 47 | /// adds two extra rounds to maintain four rounds between final input 48 | /// consumption and the first output, however this first result is not identical 49 | /// to SipHash's result. 50 | /// 51 | /// Although this generator is heavily based on the design of SipHash, it has 52 | /// not been reviewed for cryptographic strength, and thus cannot be recommended 53 | /// for applications requiring this property. 54 | #[derive(Debug, Clone)] 55 | pub struct SipRng { 56 | state: State, 57 | adj: u64, 58 | } 59 | 60 | #[derive(Debug)] 61 | struct Hasher { 62 | k0: u64, 63 | k1: u64, 64 | length: usize, // how many bytes we've processed 65 | state: State, // hash State 66 | tail: u64, // unprocessed bytes le 67 | ntail: usize, // how many bytes in tail are valid 68 | _marker: PhantomData, 69 | } 70 | 71 | #[derive(Debug, Clone, Copy)] 72 | #[repr(C)] 73 | struct State { 74 | // v0, v2 and v1, v3 show up in pairs in the algorithm, 75 | // and simd implementations of SipHash will use vectors 76 | // of v02 and v13. By placing them in this order in the struct, 77 | // the compiler can pick up on just a few simd optimizations by itself. 78 | v0: u64, 79 | v2: u64, 80 | v1: u64, 81 | v3: u64, 82 | } 83 | 84 | macro_rules! compress { 85 | ($state:expr) => {{ 86 | compress!($state.v0, $state.v1, $state.v2, $state.v3) 87 | }}; 88 | ($v0:expr, $v1:expr, $v2:expr, $v3:expr) => {{ 89 | $v0 = $v0.wrapping_add($v1); 90 | $v1 = $v1.rotate_left(13); 91 | $v1 ^= $v0; 92 | $v0 = $v0.rotate_left(32); 93 | $v2 = $v2.wrapping_add($v3); 94 | $v3 = $v3.rotate_left(16); 95 | $v3 ^= $v2; 96 | $v0 = $v0.wrapping_add($v3); 97 | $v3 = $v3.rotate_left(21); 98 | $v3 ^= $v0; 99 | $v2 = $v2.wrapping_add($v1); 100 | $v1 = $v1.rotate_left(17); 101 | $v1 ^= $v2; 102 | $v2 = $v2.rotate_left(32); 103 | }}; 104 | } 105 | 106 | /// Loads an integer of the desired type from a byte stream, in LE order. Uses 107 | /// `copy_nonoverlapping` to let the compiler generate the most efficient way 108 | /// to load it from a possibly unaligned address. 109 | /// 110 | /// Unsafe because: unchecked indexing at i..i+size_of(int_ty) 111 | macro_rules! load_int_le { 112 | ($buf:expr, $i:expr, $int_ty:ident) => {{ 113 | debug_assert!($i + mem::size_of::<$int_ty>() <= $buf.len()); 114 | let mut data = 0 as $int_ty; 115 | ptr::copy_nonoverlapping( 116 | $buf.as_ptr().add($i), 117 | &mut data as *mut _ as *mut u8, 118 | mem::size_of::<$int_ty>(), 119 | ); 120 | data.to_le() 121 | }}; 122 | } 123 | 124 | /// Loads an u64 using up to 7 bytes of a byte slice. 125 | /// 126 | /// Unsafe because: unchecked indexing at start..start+len 127 | #[inline] 128 | unsafe fn u8to64_le(buf: &[u8], start: usize, len: usize) -> u64 { 129 | unsafe { 130 | debug_assert!(len < 8); 131 | let mut i = 0; // current byte index (from LSB) in the output u64 132 | let mut out = 0; 133 | if i + 3 < len { 134 | out = load_int_le!(buf, start + i, u32) as u64; 135 | i += 4; 136 | } 137 | if i + 1 < len { 138 | out |= (load_int_le!(buf, start + i, u16) as u64) << (i * 8); 139 | i += 2 140 | } 141 | if i < len { 142 | out |= (*buf.as_ptr().add(start + i) as u64) << (i * 8); 143 | i += 1; 144 | } 145 | debug_assert_eq!(i, len); 146 | out 147 | } 148 | } 149 | 150 | impl SipHasher { 151 | /// Create a new `SipHasher` (equivalent to `from_keys(0, 0)`). 152 | #[inline] 153 | pub fn new() -> Self { 154 | SipHasher::from_keys(0, 0) 155 | } 156 | 157 | /// Create a `SipHasher` using the given keys. 158 | #[inline] 159 | pub fn from_keys(key0: u64, key1: u64) -> Self { 160 | SipHasher { 161 | hasher: Hasher::from_keys(key0, key1), 162 | } 163 | } 164 | 165 | /// Finish writes and convert the hasher's core into a generator. 166 | /// 167 | /// This offers a fast, elegant transition from hash function to generator 168 | /// which maintains (up to) 256 bits of entropy. 169 | /// 170 | /// Note that this transition has not been reviewed for cryptographic 171 | /// strength, and might break SipHash's security. 172 | pub fn into_rng(self) -> SipRng { 173 | self.hasher.into_rng() 174 | } 175 | } 176 | 177 | impl SipRng { 178 | // private constructor 179 | fn from_state(state: State) -> SipRng { 180 | SipRng { state, adj: 0x13 } 181 | } 182 | } 183 | 184 | impl RngCore for SipRng { 185 | fn next_u32(&mut self) -> u32 { 186 | // Lazy way to implement. Good enough for seeding RNGs. 187 | self.next_u64() as u32 188 | } 189 | 190 | fn next_u64(&mut self) -> u64 { 191 | self.state.v2 ^= self.adj; 192 | self.adj = self.adj.wrapping_sub(0x11); 193 | 194 | Sip24Rounds::c_rounds(&mut self.state); 195 | 196 | self.state.v0 ^ self.state.v1 ^ self.state.v2 ^ self.state.v3 197 | } 198 | 199 | fn fill_bytes(&mut self, dest: &mut [u8]) { 200 | impls::fill_bytes_via_next(self, dest) 201 | } 202 | } 203 | 204 | impl SeedableRng for SipRng { 205 | type Seed = [u8; 32]; 206 | 207 | fn from_seed(seed: Self::Seed) -> Self { 208 | let mut keys = [0u64; 4]; 209 | le::read_u64_into(&seed, &mut keys); 210 | SipRng::from_state(State { 211 | v0: keys[0], 212 | v1: keys[1], 213 | v2: keys[2], 214 | v3: keys[3], 215 | }) 216 | } 217 | } 218 | 219 | impl Hasher { 220 | #[inline] 221 | fn from_keys(key0: u64, key1: u64) -> Hasher { 222 | let mut state = Hasher { 223 | k0: key0, 224 | k1: key1, 225 | length: 0, 226 | state: State { 227 | v0: 0, 228 | v1: 0, 229 | v2: 0, 230 | v3: 0, 231 | }, 232 | tail: 0, 233 | ntail: 0, 234 | _marker: PhantomData, 235 | }; 236 | state.reset(); 237 | state 238 | } 239 | 240 | #[inline] 241 | fn reset(&mut self) { 242 | self.length = 0; 243 | self.state.v0 = self.k0 ^ 0x736f6d6570736575; 244 | self.state.v1 = self.k1 ^ 0x646f72616e646f6d; 245 | self.state.v2 = self.k0 ^ 0x6c7967656e657261; 246 | self.state.v3 = self.k1 ^ 0x7465646279746573; 247 | self.ntail = 0; 248 | } 249 | 250 | // Specialized write function that is only valid for buffers with len <= 8. 251 | // It's used to force inlining of write_u8 and write_usize, those would normally be inlined 252 | // except for composite types (that includes slices and str hashing because of delimiter). 253 | // Without this extra push the compiler is very reluctant to inline delimiter writes, 254 | // degrading performance substantially for the most common use cases. 255 | #[inline] 256 | fn short_write(&mut self, msg: &[u8]) { 257 | debug_assert!(msg.len() <= 8); 258 | let length = msg.len(); 259 | self.length += length; 260 | 261 | let needed = 8 - self.ntail; 262 | let fill = cmp::min(length, needed); 263 | if fill == 8 { 264 | self.tail = unsafe { load_int_le!(msg, 0, u64) }; 265 | } else { 266 | self.tail |= unsafe { u8to64_le(msg, 0, fill) } << (8 * self.ntail); 267 | if length < needed { 268 | self.ntail += length; 269 | return; 270 | } 271 | } 272 | self.state.v3 ^= self.tail; 273 | S::c_rounds(&mut self.state); 274 | self.state.v0 ^= self.tail; 275 | 276 | // Buffered tail is now flushed, process new input. 277 | self.ntail = length - needed; 278 | self.tail = unsafe { u8to64_le(msg, needed, self.ntail) }; 279 | } 280 | 281 | fn into_rng(self) -> SipRng { 282 | let mut state = self.state; 283 | 284 | // Finish 285 | let b: u64 = ((self.length as u64 & 0xff) << 56) | self.tail; 286 | 287 | state.v3 ^= b; 288 | S::c_rounds(&mut state); 289 | state.v0 ^= b; 290 | 291 | // This is supposed to be d - c rounds (here 4 - 2 = 2) 292 | S::c_rounds(&mut state); 293 | 294 | SipRng::from_state(state) 295 | } 296 | } 297 | 298 | /// Implements the standard library's `Hasher` trait. 299 | /// 300 | /// Note that all methods are implemented directly to fix Endianness, unlike 301 | /// the default implementations in the standard library. 302 | impl hash::Hasher for SipHasher { 303 | #[inline] 304 | fn finish(&self) -> u64 { 305 | self.hasher.finish() 306 | } 307 | 308 | #[inline] 309 | fn write(&mut self, msg: &[u8]) { 310 | self.hasher.write(msg) 311 | } 312 | 313 | #[inline] 314 | fn write_u8(&mut self, i: u8) { 315 | self.write(&[i]) 316 | } 317 | #[inline] 318 | fn write_u16(&mut self, i: u16) { 319 | self.write(&i.to_le_bytes()) 320 | } 321 | #[inline] 322 | fn write_u32(&mut self, i: u32) { 323 | self.write(&i.to_le_bytes()) 324 | } 325 | #[inline] 326 | fn write_u64(&mut self, i: u64) { 327 | self.write(&i.to_le_bytes()) 328 | } 329 | #[inline] 330 | fn write_u128(&mut self, i: u128) { 331 | self.write(&i.to_le_bytes()) 332 | } 333 | 334 | /// For portability reasons, the `usize` input is interpreted as a `u128`. 335 | #[inline] 336 | fn write_usize(&mut self, i: usize) { 337 | self.write_u128(i as u128); 338 | } 339 | 340 | #[inline] 341 | fn write_i8(&mut self, i: i8) { 342 | self.write_u8(i as u8) 343 | } 344 | #[inline] 345 | fn write_i16(&mut self, i: i16) { 346 | self.write(&i.to_le_bytes()) 347 | } 348 | #[inline] 349 | fn write_i32(&mut self, i: i32) { 350 | self.write(&i.to_le_bytes()) 351 | } 352 | #[inline] 353 | fn write_i64(&mut self, i: i64) { 354 | self.write(&i.to_le_bytes()) 355 | } 356 | #[inline] 357 | fn write_i128(&mut self, i: i128) { 358 | self.write(&i.to_le_bytes()) 359 | } 360 | 361 | /// For portability reasons, the `isize` input is interpreted as a `i128`. 362 | #[inline] 363 | fn write_isize(&mut self, i: isize) { 364 | self.write_i128(i as i128); 365 | } 366 | } 367 | 368 | impl hash::Hasher for Hasher { 369 | // see short_write comment for explanation 370 | #[inline] 371 | fn write_usize(&mut self, i: usize) { 372 | let bytes = unsafe { 373 | slice::from_raw_parts(&i as *const usize as *const u8, mem::size_of::()) 374 | }; 375 | self.short_write(bytes); 376 | } 377 | 378 | // see short_write comment for explanation 379 | #[inline] 380 | fn write_u8(&mut self, i: u8) { 381 | self.short_write(&[i]); 382 | } 383 | 384 | #[inline] 385 | fn write(&mut self, msg: &[u8]) { 386 | let length = msg.len(); 387 | self.length += length; 388 | 389 | let mut needed = 0; 390 | 391 | if self.ntail != 0 { 392 | needed = 8 - self.ntail; 393 | self.tail |= unsafe { u8to64_le(msg, 0, cmp::min(length, needed)) } << (8 * self.ntail); 394 | if length < needed { 395 | self.ntail += length; 396 | return; 397 | } else { 398 | self.state.v3 ^= self.tail; 399 | S::c_rounds(&mut self.state); 400 | self.state.v0 ^= self.tail; 401 | self.ntail = 0; 402 | } 403 | } 404 | 405 | // Buffered tail is now flushed, process new input. 406 | let len = length - needed; 407 | let left = len & 0x7; 408 | 409 | let mut i = needed; 410 | while i < len - left { 411 | let mi = unsafe { load_int_le!(msg, i, u64) }; 412 | 413 | self.state.v3 ^= mi; 414 | S::c_rounds(&mut self.state); 415 | self.state.v0 ^= mi; 416 | 417 | i += 8; 418 | } 419 | 420 | self.tail = unsafe { u8to64_le(msg, i, left) }; 421 | self.ntail = left; 422 | } 423 | 424 | /// Reduces the state to a `u64` value, as in the reference implementation. 425 | #[inline] 426 | fn finish(&self) -> u64 { 427 | let mut state = self.state; 428 | 429 | let b: u64 = ((self.length as u64 & 0xff) << 56) | self.tail; 430 | 431 | state.v3 ^= b; 432 | S::c_rounds(&mut state); 433 | state.v0 ^= b; 434 | 435 | state.v2 ^= 0xff; 436 | S::d_rounds(&mut state); 437 | 438 | state.v0 ^ state.v1 ^ state.v2 ^ state.v3 439 | } 440 | } 441 | 442 | impl From for SipHasher { 443 | #[inline] 444 | fn from(h: H) -> SipHasher { 445 | let mut hasher = SipHasher::new(); 446 | h.hash(&mut hasher); 447 | hasher 448 | } 449 | } 450 | 451 | impl Clone for Hasher { 452 | #[inline] 453 | fn clone(&self) -> Hasher { 454 | Hasher { 455 | k0: self.k0, 456 | k1: self.k1, 457 | length: self.length, 458 | state: self.state, 459 | tail: self.tail, 460 | ntail: self.ntail, 461 | _marker: self._marker, 462 | } 463 | } 464 | } 465 | 466 | impl Default for Hasher { 467 | /// Creates a `Hasher` with the two initial keys set to 0. 468 | #[inline] 469 | fn default() -> Hasher { 470 | Hasher::from_keys(0, 0) 471 | } 472 | } 473 | 474 | #[doc(hidden)] 475 | trait Sip { 476 | fn c_rounds(_: &mut State); 477 | fn d_rounds(_: &mut State); 478 | } 479 | 480 | #[derive(Debug, Clone, Default)] 481 | struct Sip24Rounds; 482 | 483 | impl Sip for Sip24Rounds { 484 | #[inline] 485 | fn c_rounds(state: &mut State) { 486 | compress!(state); 487 | compress!(state); 488 | } 489 | 490 | #[inline] 491 | fn d_rounds(state: &mut State) { 492 | compress!(state); 493 | compress!(state); 494 | compress!(state); 495 | compress!(state); 496 | } 497 | } 498 | 499 | #[cfg(test)] 500 | mod test { 501 | use super::*; 502 | use core::hash::Hasher; 503 | 504 | #[test] 505 | fn test_hash_vectors() { 506 | let k_bytes = [0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; 507 | let (k0, k1) = unsafe { 508 | ( 509 | load_int_le!(&k_bytes, 0, u64), 510 | load_int_le!(&k_bytes, 8, u64), 511 | ) 512 | }; 513 | 514 | let mut msg = [0u8; 64]; 515 | for (pos, i) in msg.iter_mut().zip(0u8..64) { 516 | *pos = i; 517 | } 518 | 519 | // From reference vectors, converted to u64: 520 | let tests = [ 521 | (0, 0x726fdb47dd0e0e31), 522 | (1, 0x74f839c593dc67fd), 523 | (63, 0x958a324ceb064572), 524 | ]; 525 | 526 | for (len, val) in &tests { 527 | let mut hasher = SipHasher::from_keys(k0, k1); 528 | hasher.write(&msg[0..*len]); 529 | assert_eq!( 530 | hasher.finish(), 531 | *val, 532 | "mismatch with reference vector for i={}", 533 | *len 534 | ); 535 | } 536 | } 537 | 538 | #[test] 539 | fn test_rng_vectors() { 540 | // SipRng has no external reference. These vectors are defined here. 541 | 542 | let mut seed = [0u8; 32]; 543 | let mut rng = SipRng::from_seed(seed); 544 | 545 | let vector = [ 546 | 0x4c022e4ec04e602a, 547 | 0xc2c0399c269058d6, 548 | 0xf5c7399cde9c362c, 549 | 0x37e5b9491363680a, 550 | 0x9582782644903316, 551 | 0x2a9d2e160aad88d, 552 | 0x983958db9376e6f6, 553 | 0xdead8960b8524928, 554 | 0xcfa886c6642c1b2f, 555 | 0x8f8f91fcf7045f2a, 556 | 0x1bbda585fc387fb3, 557 | 0x242485d9cc54c688, 558 | 0x9be110f767d8cee, 559 | 0xd61076dfc3569ab3, 560 | 0x8f6092dd2692af57, 561 | 0xbdf362ab8e29260b, 562 | ]; 563 | // for _ in 0..8 { 564 | // println!("0x{:x}, 0x{:x},", rng.next_u64(), rng.next_u64()); 565 | // } 566 | for x in &vector { 567 | assert_eq!(rng.next_u64(), *x); 568 | } 569 | 570 | // set seed to 0, 1, 2, ..., 31 571 | for (pos, i) in seed.iter_mut().zip(0u8..32) { 572 | *pos = i; 573 | } 574 | let mut rng = SipRng::from_seed(seed); 575 | 576 | let vector = [ 577 | 0x479bf2823a7a923e, 578 | 0xf04e2cbc75d554d, 579 | 0xd589aceb3b65f36b, 580 | 0x91f8758ab30951a, 581 | 0x10d2bebadd90c381, 582 | 0xb3a6345b6273b101, 583 | 0xd05dbd603684e153, 584 | 0xabaaa983f818f5db, 585 | 0x2a063ed10d464bf2, 586 | 0x1d395c4c511e9073, 587 | 0x43011ca87ead4d7c, 588 | 0x22acb2bfbca6a069, 589 | 0xdd6b8dd2abb4d8f, 590 | 0xb3bc3889e7142461, 591 | 0x62cbac703609d15, 592 | 0x74aec28d9fdd44bf, 593 | ]; 594 | for x in &vector { 595 | assert_eq!(rng.next_u64(), *x); 596 | } 597 | } 598 | } 599 | --------------------------------------------------------------------------------