ids_service 2.0.2

Library that allows to generate unique Ids.
Documentation
![Licence](https://img.shields.io/crates/l/ids_service)
![Version](https://img.shields.io/crates/v/ids_service)
[![dependency status](https://deps.rs/repo/gitlab/kurdy/ids_service/status.svg)](https://deps.rs/repo/gitlab/kurdy/ids_service)
![Download](https://img.shields.io/crates/d/ids_service)
[![pipeline](https://gitlab.com/kurdy/ids_service/badges/master/pipeline.svg)](https://gitlab.com/kurdy/ids_service)
[![docs.rs](https://img.shields.io/docsrs/ids_service)](https://docs.rs/ids_service)

# ids_service

A Rust library for generating unique, cryptographically-sound IDs using a pre-filled,
multi-threaded cache. Background worker threads keep the cache full so that callers
receive a pre-computed ID instantly without blocking.

If the cache is temporarily exhausted (burst traffic), an ID is generated on the fly —
no error, no panic, just a small latency spike.

---

## How it works

```
  ┌──────────────────────────────────────────────────────┐
  │                    IdsService                        │
  │                                                      │
  │  ┌──────────┐   push    ┌─────────────────────────┐ │
  │  │ Worker 0 │ ────────► │                         │ │
  │  ├──────────┤           │  Arc<Mutex<VecDeque>>   │ │  get_id()
  │  │ Worker 1 │ ────────► │       (cache)           │ ├──────────► caller
  │  ├──────────┤           │                         │ │
  │  │ Worker N │ ────────► │  size = cache_size      │ │
  │  └──────────┘           └─────────────────────────┘ │
  │                                                      │
  │  Each ID = hash( random_bytes || timestamp_ns )      │
  └──────────────────────────────────────────────────────┘
```

Each ID is derived from:
- A random byte block (size = 2 × hash output length — see [Birthday problem]https://en.m.wikipedia.org/wiki/Birthday_problem#Probability_table)
- The current timestamp in nanoseconds since the Unix epoch

---

## Modules

### `crypto_hash`

Uses one of the SHA-3 family algorithms from the [`sha3`](https://crates.io/crates/sha3) crate:

| Mode | Output | Random block |
|---|---|---|
| `Sha3Mode::Sha3_224` | 28 bytes | 56 bytes |
| `Sha3Mode::Sha3_256` | 32 bytes | 64 bytes |
| `Sha3Mode::Sha3_384` | 48 bytes | 96 bytes |
| `Sha3Mode::Sha3_512` | 64 bytes | 128 bytes |

IDs can be encoded as:

| Method | Output |
|---|---|
| `.as_hex()` | Lowercase hexadecimal `String` |
| `.as_base64()` | Standard Base64 `String` |
| `.as_base64_url()` | URL-safe Base64 `String` |
| `.as_base32()` | Base32 `String` |
| `.as_json()` | JSON array of byte values |

### `rust_hash`

Uses [`std::collections::hash_map::DefaultHasher`](https://doc.rust-lang.org/std/collections/hash_map/struct.DefaultHasher.html)
(SipHash 1-3). IDs are `u64` values. Faster than `crypto_hash`, but **not**
cryptographically secure — suitable for non-security-sensitive use cases.

---

## Feature flags

| Flag | Default | Description |
|---|---|---|
| `all` || Enables both `crypto` and `rust` |
| `crypto` || Enables `crypto_hash` module (requires `sha3` dep) |
| `rust` || Enables `rust_hash` module |

To use only one module, disable default features in `Cargo.toml`:

```toml
# Only the rust hasher — no sha3 dependency
ids_service = { version = "2.0.2", default-features = false, features = ["rust"] }

# Only the crypto hasher
ids_service = { version = "2.0.2", default-features = false, features = ["crypto"] }
```

---

## Quick Start

### Cryptographic hash (`crypto_hash`)

```rust
use ids_service::crypto_hash::{IdsService, Sha3Mode};
use ids_service::common::{Encode, Service, Uids};

fn main() {
    // Cache size = 100 000, SHA3-256, thread count = number of logical CPUs
    let mut ids = IdsService::new(100_000, Sha3Mode::Sha3_256, None);
    ids.start();

    // Optional: block until the cache is at least 10 % full
    let _ = ids.filled_at_percent_event(10).recv();

    println!("hex:    {}", ids.get_id().as_hex());
    println!("base64: {}", ids.get_id().as_base64());
    println!("base32: {}", ids.get_id().as_base32());

    // Returns None when the cache is empty instead of generating on the fly
    if let Some(id) = ids.get_id_from_cache() {
        println!("from cache: {}", id.as_hex());
    }

    println!("cache len: {}", ids.get_cache_len());

    // Graceful shutdown — joins worker threads
    ids.stop();
}
```

Or use the default configuration (SHA3-256, 100 000 items, CPU-count threads):

```rust
use ids_service::crypto_hash::IdsService;
use ids_service::common::{Encode, Service, Uids};

fn main() {
    let mut ids = IdsService::default();
    ids.start();
    let _ = ids.filled_event().recv(); // wait for full cache
    println!("{}", ids.get_id().as_hex());
    ids.stop();
}
```

### Iterator interface

`IdsService` implements `Iterator`, yielding one ID per call:

```rust
use ids_service::crypto_hash::{IdsService, Sha3Mode};
use ids_service::common::{Encode, Service};

fn main() {
    let mut ids = IdsService::new(10_000, Sha3Mode::Sha3_512, None);
    ids.start();

    for id in ids.by_ref().take(5) {
        println!("{}", id.as_hex());
    }

    ids.stop();
}
```

### Standalone (no service, no cache)

```rust
use ids_service::crypto_hash::{create_id_as_sha256, create_id_as_sha512};

fn main() {
    println!("{}", create_id_as_sha256()); // hex-encoded SHA3-256
    println!("{}", create_id_as_sha512()); // hex-encoded SHA3-512
}
```

### Rust hasher (`rust_hash`)

```rust
use ids_service::rust_hash::IdsService;
use ids_service::common::{Service, Uids};

fn main() {
    // Cache size = 200 000, thread count = number of logical CPUs
    let mut ids = IdsService::new(200_000, None);
    ids.start();

    let _ = ids.filled_at_percent_event(10).recv();

    println!("id:         {}", ids.get_id());
    println!("from cache: {}", ids.get_id_from_cache().expect("cache non-empty"));
    println!("cache len:  {}", ids.get_cache_len());

    ids.stop();
}
```

### Standalone (no service, no cache)

```rust
use ids_service::rust_hash::create_id;

fn main() {
    println!("{}", create_id()); // u64
}
```

---

## Performance

Benchmarks run with [Criterion.rs](https://bheisler.github.io/criterion.rs/book/) on an
**AMD Ryzen AI 7 350** — `cargo bench`.

### Results

| Benchmark | Median time | Throughput |
|---|---|---|
| `crypto_hash/standalone/sha256` | ~379 ns | ~2.64 Melem/s |
| `crypto_hash/standalone/sha512` | ~926 ns | ~1.08 Melem/s |
| `crypto_hash/service/get_id/Sha3_256` | ~149–170 ns | ~5.9–6.8 Melem/s |
| `crypto_hash/service/get_id/Sha3_512` | ~102–120 ns | ~8.4–9.8 Melem/s |
| `crypto_hash/encoding/as_hex` | ~24.9 ns ||
| `crypto_hash/encoding/as_base64` | ~28.4 ns ||
| `crypto_hash/encoding/as_base32` | ~29.4 ns ||
| `rust_hash/standalone/create_id` | ~42.8 ns | ~23.3 Melem/s |
| `rust_hash/service/get_id` | ~118–157 ns | ~6.4–8.5 Melem/s |

> **`crypto_hash` cache hit ≈ 149–170 ns — 2–6× faster than standalone SHA3-256/SHA3-512
> generation (Mutex::lock + VecDeque::pop_front vs. full hash computation).**
>
> **`rust_hash` cache hit ≈ 118–157 ns — service overhead exceeds standalone (~43 ns);
> use `create_id()` directly when maximum throughput is the priority.**

Run the full benchmark suite:

```bash
cargo bench --bench throughput
```

Results are saved as HTML reports in `target/criterion/`.

---

## Examples

```bash
# Cryptographic hasher examples
cargo run --example quickstart
cargo run --example quickstart2
cargo run --example examples
cargo run --example iterator_example

# Rust hasher examples
cargo run --example quickstart_rh
cargo run --example quickstart2_rh
cargo run --example examples_rh
cargo run --example iterator_example_rh

# Generate 10 million IDs and measure throughput
cargo run --release --example ten_million
cargo run --release --example ten_million_rh
```

---

## Running tests

```bash
# All unit and doc tests
cargo test

# With a specific feature set
cargo test --no-default-features --features crypto
cargo test --no-default-features --features rust
```

---

## Platforms

Compiled and tested on:

1. x86_64 GNU/Linux (primary development + CI)
2. macOS (Apple Silicon M1)

---

## License

MIT — see [LICENSE](LICENSE).