xoroshiro256-full

Crates.ioxoroshiro256-full
lib.rsxoroshiro256-full
version0.1.4
created_at2025-10-06 07:06:19.07771+00
updated_at2025-10-06 07:33:53.157799+00
description⚑ **Fast**, πŸ§ͺ **deterministic**, and 🧰 **ergonomic** implementation of **xoroshiro256**** with **no bit waste**.
homepage
repositoryhttps://github.com/Borderliner/xoroshiro256-full
max_upload_size
id1869832
size35,371
Mohammadreza Hajianpour (Borderliner)

documentation

README

xoroshiro256-full

Crates.io Docs.rs License CI

⚑ Fast, πŸ§ͺ deterministic, and 🧰 ergonomic implementation of xoroshiro256** with no bit waste. Not cryptographically secure. For CSPRNG, enable the crypto feature and use CryptoRng256 (ChaCha20).


✨ Highlights

  • πŸš€ xoroshiro256 core with 256-bit state (4 Γ— u64).

  • 🧩 Optional integrations via Cargo features:

    • rand_core β€” implements rand_core::RngCore and SeedableRng.
    • serde β€” enables Serialize/Deserialize for Xoroshiro256State (core state only).
    • crypto β€” provides CryptoRng256 (ChaCha20) for cryptographic use.
  • ♻️ No bit waste when rand_core is enabled:

    • next_u32() returns the high 32 bits first and caches the low 32 for the next call.
    • fill_bytes() caches any unused tail (≀ 7 bytes) and reuses them on the next call.
  • 🧱 #![forbid(unsafe_code)] β€” zero unsafe.

  • πŸ§ͺ Comprehensive tests incl. determinism, buffer behavior, and anti-waste checks.

ℹ️ The internal caches for anti-waste are not serialized with serde; only the 256-bit xoroshiro state is preserved. See details below.


πŸ“¦ Install

[dependencies]
xoroshiro256-full = "0.1"

Optional features

# Cargo.toml
[dependencies]
xoroshiro256-full = { version = "0.1", features = ["rand_core", "serde", "crypto"] }
Feature What you get
rand_core RngCore/SeedableRng impls, anti–bit-waste behavior for next_u32/fill_bytes
serde Serialize/Deserialize for Xoroshiro256State (caches are skipped)
crypto CryptoRng256 (ChaCha20) for secure randomness (requires rand_core)

🧭 Quick start

Fast deterministic numbers (no features required)

use xoroshiro256_full::Xoroshiro256State;

let mut rng = Xoroshiro256State::init([1, 2, 3, 4], 4);
let x = rng.next_u64(); // fast, deterministic

Integrate with rand ecosystem (enable rand_core)

# #[cfg(feature="rand_core")] {
use rand_core::{RngCore, SeedableRng};
use xoroshiro256_full::Xoroshiro256State;

let mut rng = Xoroshiro256State::seed_from_u64(42);
let _ = rng.next_u32();
# }

Cryptographically secure RNG (enable crypto)

# #[cfg(feature="crypto")] {
use xoroshiro256_full::CryptoRng256;

let mut crng = CryptoRng256::from_os();
let x = crng.next_u64();
# }

🧠 API Overview

Xoroshiro256State

  • State: parts: [u64; 4] (256 bits).

  • Constructors:

    • init([u64; 4], key_length: usize) β€” seed with up to 4 words; missing words are derived with an LCG. All-zero state is auto-fixed with a non-zero constant.
    • (with rand_core) seed_from_u64(u64) β€” SplitMix64-like expansion into 256 bits.
    • (with rand_core) from_seed([u8; 32]) β€” exact little-endian seeds.
  • Core step: next_u64() -> u64 β€” xoroshiro256** starstar output.

  • State dump: genrand_uint256_to_buf(&mut [u8]) β€” current 256-bit state in little-endian order (does not step the generator).

CryptoRng256 (feature = crypto)

  • Wrapper around ChaCha20Rng with:

    • from_os() β€” seed from OS entropy.
    • from_seed([u8; 32]) β€” deterministic seed.
    • fill_bytes, next_u64, next_u32 via RngCore.

πŸ” No Bit Waste β€” How it works

When rand_core is enabled, this crate adds two ephemeral caches inside Xoroshiro256State:

  • u32_spare: Option<u32> β€”

    • next_u32() generates one u64 and returns the high 32 bits immediately, caching the low 32 for the next call. Two next_u32() calls equal one next_u64().
  • byte_spare: [u8; 8] + byte_spare_len: u8 β€”

    • fill_bytes() writes full u64 chunks, then uses a tail from a new u64 if needed. Any leftover bytes (≀ 7) are cached and prepended on the next fill_bytes() call.

Serialization note (serde): these caches are not part of the algorithmic state and are skipped during serialization. Only the 256-bit xoroshiro state is serialized, which is what you want for reproducibility.


βœ… Behavior & Guarantees

  • Determinism: Same seed β†’ same sequence. Tests validate equivalence across multiple draws.
  • Non-crypto: xoroshiro256** is *not* cryptographically secure. Use CryptoRng256 for security-sensitive contexts.
  • Endian: All byte conversions are little-endian.
  • Zero-unsafe: The crate forbids unsafe.

πŸ§ͺ Testing

This repository includes:

  • Seed determinism tests.
  • Buffer behavior tests.
  • rand_core integration tests for variable buffer lengths.
  • Anti–bit-waste tests ensuring next_u32() and fill_bytes() consume exactly what next_u64() would.
  • crypto tests for basic behavior and reproducibility with fixed seeds.

Run locally:

# All features (recommended)
cargo test --all-features

# Feature combinations (example)
cargo test --features serde,rand_core

CI runs formatting, clippy (deny warnings), OS matrix builds (Linux/macOS/Windows), nightly smoke tests, and a feature matrix. See ci.yml.


πŸ“„ Examples in Detail

1) Seeding with partial keys

use xoroshiro256_full::Xoroshiro256State;

// Only the first word provided; remaining words are derived via LCG.
let mut rng = Xoroshiro256State::init([0xDEAD_BEEF_CAFE_BABE, 0, 0, 0], 1);
let a = rng.next_u64();
let b = rng.next_u64();
assert_ne!(a, b);

2) Serialize/deserialize state (feature = serde)

# #[cfg(feature = "serde")] {
use xoroshiro256_full::Xoroshiro256State;
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct Save {
    rng: Xoroshiro256State,
}

let s = Save { rng: Xoroshiro256State::init([1,2,3,4], 4) };
let json = serde_json::to_string(&s).unwrap();
let mut s2: Save = serde_json::from_str(&json).unwrap();
let _ = s2.rng.next_u64();
# }

3) Streaming bytes with no waste (feature = rand_core)

# #[cfg(feature="rand_core")] {
use rand_core::RngCore;
use xoroshiro256_full::Xoroshiro256State;

let mut rng = Xoroshiro256State::seed_from_u64(2024);
let mut a = [0u8; 3];
let mut b = [0u8; 5];
let mut all = [0u8; 8];

rng.fill_bytes(&mut a);
rng.fill_bytes(&mut b); // reuses the 5 leftover bytes from the previous call

all[..3].copy_from_slice(&a);
all[3..].copy_from_slice(&b);

let mut control = [0u8; 8];
Xoroshiro256State::seed_from_u64(2024).fill_bytes(&mut control);
assert_eq!(all, control);
# }

4) next_u32() pairs to one u64 (feature = rand_core)

# #[cfg(feature="rand_core")] {
use rand_core::RngCore;
use xoroshiro256_full::Xoroshiro256State;

let mut a = Xoroshiro256State::seed_from_u64(9999);
let mut b = Xoroshiro256State::seed_from_u64(9999);
let x = a.next_u64();
let hi = b.next_u32() as u64;
let lo = b.next_u32() as u64;
assert_eq!((hi << 32) | lo, x);
# }

πŸ”§ MSRV & Toolchain

  • Built with stable Rust (see CI). No explicit MSRV pinned yet; generally tracks stable toolchain.
  • Uses edition = "2024".

πŸ”’ Security

  • Xoroshiro256State is not cryptographically secure.
  • For cryptographic use-cases, enable crypto and use CryptoRng256 (ChaCha20-based) seeded via from_os() when possible.

πŸ“š References & Thanks

  • xoroshiro / xoshiro family by David Blackman & Sebastiano Vigna.
  • xoroshiro256 no bit waste version by Fabian Druschke. Read more
  • Design principles and constants follow the reference implementations.

🀝 Contributing

PRs, issues, and suggestions are welcome! Please run:

cargo fmt --all
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features

πŸ“œ License

Licensed under Apache-2.0

Commit count: 0

cargo fmt