ridgen 0.1.0

A lightweight, high-performance Rust library for generating secure, URL-friendly unique string IDs, with WASM support.
Documentation
# ridgen


[日本語]./README.ja.md | English

A lightweight, high-performance Rust library for generating secure, URL-friendly unique string IDs.

### Comparison with JavaScript Libraries


All tests performed with 1,000,000 iterations:

| Library    | Language   | Time (ms) | Throughput (million IDs/sec) | Notes                          |
| ---------- | ---------- | --------- | ---------------------------- | ------------------------------ |
| **ridgen** | Rust       | ~340      | **~3.0**                     | This implementation ⚡         |
| uuid (v4)  | JavaScript | 375       | ~2.7                         | Standard UUID v4               |
| nanoid     | JavaScript | 431       | ~2.3                         | Widely used original      |
| shortid    | JavaScript | 3,220     | ~0.3                         | Deprecated, slower             |
| cuid2      | JavaScript | 34,274    | ~0.03                        | Collision-resistant            |

#### Key Takeaways


- **ridgen** achieves **~3.0 million IDs/sec** throughput
- **~11% faster** than uuid v4 while providing URL-friendly IDs
- **~30% faster** than nanoid
- **9.5x faster** than shortid
- **88x faster** than cuid2
- Achieves maximum performance through batch random number generation

Note: The comparison is against JavaScript implementations running on Node.js.
Rust implementations of UUID are not included in this benchmark.

## Features


- 🚀 **Fast**: ~3.0 million IDs/sec
- 🔒 **Secure**: Uses cryptographically strong random number generation
- 📦 **Lightweight**: Minimal dependencies
-**Simple API**: Easy to use with error handling
-**Optimized**: Batch random number generation for maximum performance

## Installation


### For Rust


Add this to your `Cargo.toml`:

```toml
[dependencies]
ridgen = "0.1.0"
```

### For JavaScript/TypeScript (WASM)


```bash
npm install ridgen
```

## Usage


### Rust


```rust
use ridgen::generate;

fn main() {
    // Generate a 16-character ID
    match generate(16) {
        Ok(id) => println!("Generated ID: {}", id),
        Err(e) => eprintln!("Error: {:?}", e),
    }
}
```

### JavaScript/TypeScript


This package is built with WASM and supports modern environments.

```javascript
import { generate } from "ridgen";

const id = generate(16);
console.log(`Generated ID: ${id}`);
```

### Supported Environments


- **Node.js**: (ESM) v14.16.0 or later
- **Bun**: Native support
- **Deno**: Supported
- **Modern bundlers**: Vite, Webpack, Rollup

> [!IMPORTANT]
>
> - **ESM Only**: This package only supports ECMAScript Modules (ESM). CommonJS (`require`) is not supported.
> - **WASM Initialization**: In environments that support top-level WASM (like Bun or Webpack), it works immediately. For other environments, ensure your bundler is configured to handle `.wasm` files.

## Benchmarks


### Environment


- **Date**: 2026-01-20
- **CPU**: x86_64
- **Rust**: 1.92.0
- **Build**: release
- **Node.js**: v24.12.0

### Rust (ridgen)


Performance test with 1,000,000 iterations generating 16-character IDs:

```
- Total time: ~340ms (average)
- Throughput: ~2.9-3.0 million IDs/sec
```

**Note**: Uses `thread_rng` (Default) for random number generation.
**Why no `SmallRng`?**: Although `SmallRng` is faster, it is not cryptographically secure. `ridgen` prioritizes security by using `thread_rng` to ensure IDs are unpredictable, while maintaining high performance through batch generation.
**Optimization**: Uses batch random number generation (`RngCore::fill_bytes`) instead of calling `gen_range()` for each character, reducing CPU instruction count and improving performance by ~20-25%.

## Character Set

The library uses a URL-safe character set:

- Lowercase letters: `a-z`
- Uppercase letters: `A-Z`
- Numbers: `0-9`

Total: 62 characters

## Error Handling

The `generate` function returns a `Result<String, RidgenError>`:

- `Ok(String)`: Successfully generated ID
- `Err(RidgenError::InvalidLength)`: If length is 0

## Safety and `unsafe` Usage

This library uses a small amount of `unsafe` code for performance reasons.

Specifically, `String::from_utf8_unchecked` is used when constructing the final ID string.

### Why is this safe?

- The character set consists only of ASCII characters (`a-zA-Z0-9`)
- Each byte pushed into the buffer is guaranteed to be valid UTF-8
- No user input or external data is involved

Because these invariants are strictly controlled, skipping UTF-8 validation is safe and avoids unnecessary runtime checks.

### Trade-off

- ✅ Improved performance (avoids an extra UTF-8 validation pass)
- ⚠️ Requires careful maintenance if the character set changes

If the character set is ever modified to include non-ASCII characters, this `unsafe` usage must be revisited.

## Distribution Characteristics

This implementation uses a simple modulo operation to map random bytes to the character set:

```rust
index = random_byte % 62
```

### Modulo Bias

Since 256 is not evenly divisible by 62, this introduces a small distribution bias:

- The first 8 characters in the charset have a slightly higher probability
- The bias is approximately 1/256 per character

### Why this is acceptable here


- The bias is extremely small and negligible for most ID generation use cases
- This approach avoids additional branching and rejection sampling, improving performance
- Many real-world ID systems accept this trade-off for speed

### Security Note


This library prioritizes performance and simplicity over perfect uniform distribution. If strict cryptographic uniformity is required, consider using rejection sampling or other methods.

## Running Benchmarks


### Rust Benchmark


```bash
cargo run --release
```

### JavaScript Benchmark


```bash
cd ../js-nanoid-bench
npm install
node benchmark.js
```

## License


MIT

## Contributing


Contributions are welcome! Please feel free to submit a Pull Request.