# ridgen
軽量で高性能なRust製の安全でURL対応のユニークな文字列ID生成ライブラリ
[🌐 English](./README.md)
### JavaScriptライブラリとの比較
全てのテストは100万回のイテレーションで実行:
| **ridgen** | Rust | ~340 | **~3.0** | This implementation⚡ |
| uuid (v4) | JavaScript | 375 | ~2.7 | 標準的なUUID v4 |
| nanoid | JavaScript | 431 | ~2.3 | 広く普及しているオリジナル |
| shortid | JavaScript | 3,220 | ~0.3 | 非推奨、低速 |
| cuid2 | JavaScript | 34,274 | ~0.03 | 衝突耐性あり |
#### 主なポイント
- **ridgen** は **約300万ID/秒** のスループットを達成
- URL対応IDを提供しながら uuid v4 より **約11%高速**
- nanoid より **約30%高速**
- shortid より **9.5倍高速**
- cuid2 より **88倍高速**
- バッチ乱数生成により最高のパフォーマンスを実現
※ 本ベンチマークは Node.js 上で動作する JavaScript 実装との比較です。
Rust 実装の UUID は比較対象に含めていません。
## 特徴
- 🚀 **高速**: 約300万ID/秒
- 🔒 **セキュア**: 暗号論的に強力な乱数生成を使用
- 📦 **軽量**: 最小限の依存関係
- ✅ **シンプルなAPI**: エラーハンドリング付きで使いやすい
- ⚡ **最適化済み**: バッチ乱数生成による最高のパフォーマンス
## インストール
### Rustの場合
`Cargo.toml` に以下を追加:
```toml
[dependencies]
ridgen = "0.1.0"
```
### JavaScript/TypeScript (WASM) の場合
```bash
npm install ridgen
```
## 使用方法
### Rust
```rust
use ridgen::generate;
fn main() {
// 16文字のIDを生成
match generate(16) {
Ok(id) => println!("生成されたID: {}", id),
Err(e) => eprintln!("エラー: {:?}", e),
}
}
```
### JavaScript/TypeScript
このパッケージはWASMでビルドされており、モダンな環境をサポートしています。
```javascript
import { generate } from "ridgen";
const id = generate(16);
console.log(`生成されたID: ${id}`);
```
### サポート環境
- **Node.js**: (ESM) v14.16.0 以降
- **Bun**: ネイティブサポート
- **Deno**: サポート済み
- **モダンなバンドラ**: Vite, Webpack, Rollup
> [!IMPORTANT]
>
> - **ESM専用**: このパッケージはECMAScriptモジュール (ESM) のみをサポートしています。CommonJS (`require`) はサポートされていません。
> - **WASMの初期化**: BunやWebpackなどのTop-level WASMをサポートする環境では、インポートしてすぐに使用できます。その他の環境では、バンドラが `.wasm` ファイルを適切に処理できるように設定されていることを確認してください。
## ベンチマーク
### 実行環境
- **実施日**: 2026-01-20
- **CPU**: x86_64
- **Rust**: 1.92.0
- **ビルド**: release
- **Node.js**: v24.12.0
### Rust (ridgen)
100万回のイテレーションで16文字のIDを生成したパフォーマンステスト:
```
- 合計時間: 約340ms(平均)
- スループット: 約290~300万ID/秒
```
**備考**: 乱数生成には `thread_rng` (デフォルト) を使用しています。
**SmallRng を使用しない理由**: `SmallRng` は非常に高速ですが、暗号学的な安全性がありません。`ridgen` では予測不能なIDを生成するために安全性を優先しつつ、バッチ生成によって高いパフォーマンスを両立させています。
**最適化**: 各文字ごとに `gen_range()` を呼び出す代わりに、バッチ乱数生成(`RngCore::fill_bytes`)を使用することで、CPU命令数を削減し、パフォーマンスを約20-25%向上させています。
## 文字セット
このライブラリはURL安全な文字セットを使用:
- 小文字: `a-z`
- 大文字: `A-Z`
- 数字: `0-9`
合計: 62文字
## エラーハンドリング
`generate` 関数は `Result<String, RidgenError>` を返します:
- `Ok(String)`: ID生成成功
- `Err(RidgenError::InvalidLength)`: 長さが0の場合
## Safety と `unsafe` の使用
このライブラリはパフォーマンス上の理由から、少量の `unsafe` コードを使用しています。
具体的には、最終的なID文字列を構築する際に `String::from_utf8_unchecked` を使用しています。
### なぜこれは安全なのか?
- 文字セットはASCII文字(`a-zA-Z0-9`)のみで構成されている
- バッファに追加される各バイトは有効なUTF-8であることが保証されている
- ユーザー入力や外部データは一切関与していない
これらの不変条件が厳密に制御されているため、UTF-8検証をスキップしても安全であり、不要なランタイムチェックを回避できます。
### トレードオフ
- ✅ パフォーマンス向上(余分なUTF-8検証パスを回避)
- ⚠️ 文字セットを変更する場合は慎重なメンテナンスが必要
文字セットが非ASCII文字を含むように変更される場合、この `unsafe` の使用を再検討する必要があります。
## 分布特性
この実装では、ランダムバイトを文字セットにマッピングするために単純なモジュロ演算を使用しています:
```rust
index = random_byte % 62
```
### モジュロバイアス
256は62で割り切れないため、わずかな分布のバイアスが生じます:
- 文字セットの最初の8文字がわずかに高い確率を持つ
- バイアスは文字あたり約1/256
### なぜこれが許容されるか
- バイアスは非常に小さく、ほとんどのID生成ユースケースでは無視できる
- このアプローチは追加の分岐や棄却サンプリングを避け、パフォーマンスを向上させる
- 多くの実世界のIDシステムは速度のためにこのトレードオフを受け入れている
### セキュリティに関する注意
このライブラリは、完全に均一な分布よりもパフォーマンスとシンプルさを優先しています。厳密な暗号論的均一性が必要な場合は、棄却サンプリングを使用するか、ほかの方法を検討してください。
## ベンチマークの実行方法
### Rustベンチマーク
```bash
cargo run --release
```
### JavaScriptベンチマーク
```bash
cd ../js-nanoid-bench
npm install
node benchmark.js
```
## 実装の詳細
### 最適化アプローチ
このライブラリは以下の最適化技術を使用しています:
1. **バッチ乱数生成**: `RngCore::fill_bytes()` を使用して、一度に全ての乱数を取得
2. **直接バイト操作**: 文字変換のオーバーヘッドを削減するため、バイト列を直接操作
3. **安全なunsafe使用**: ASCII文字のみを使用するため、UTF-8検証をスキップ
```rust
// 乱数をまとめて取得
let mut random_bytes = vec![0u8; len];
rng.fill_bytes(&mut random_bytes);
// ID用バイト列を構築
for b in random_bytes {
let idx = (b as usize) % CHARSET_LEN;
id_bytes.push(CHARSET[idx]);
}
// SAFETY: CHARSETはASCII文字のみ
let id = unsafe { String::from_utf8_unchecked(id_bytes) };
```
## ライセンス
MIT
## コントリビューション
プルリクエストを歓迎します!お気軽にご投稿ください。