# ridgen
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:
| **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.