snowflake-gen 1.0.1

A configurable Snowflake ID generator with custom bit layouts, thread-local global generation, and ID decomposition
Documentation
# snowflake-gen

A configurable [Snowflake ID](https://en.wikipedia.org/wiki/Snowflake_ID) generator for Rust.

## Features

- **Configurable bit layout** -- tune the balance between timestamp range, throughput, and node count
- **Thread-local global API** -- zero-lock, zero-contention ID generation across threads
- **ID decomposition** -- decode any generated ID back into its timestamp, machine, node, and sequence parts
- **Buffered generation** -- `SnowflakeIdBucket` pre-generates a full sequence batch for maximum throughput
- **Custom epochs** -- use Discord-style, Twitter-style, or your own epoch

### Default layout (Twitter-compatible)

| Field | Bits | Max value |
|------------|------|-----------|
| Timestamp | 41 | ~69 years |
| Machine ID | 5 | 31 |
| Node ID | 5 | 31 |
| Sequence | 12 | 4,096/ms |

## Quick start

```rust
use snowflake_gen::SnowflakeIdGenerator;

let mut idgen= SnowflakeIdGenerator::new(1, 1).unwrap();
let id = idgen.generate().unwrap();
println!("{id}");
```

## Custom bit layout

```rust
use snowflake_gen::{BitLayout, SnowflakeIdGenerator};

// 10 sequence bits (1,023 IDs/ms), 8 machine + 7 node bits
let layout = BitLayout::new(38, 8, 7, 10).unwrap();
let mut idgen= SnowflakeIdGenerator::with_layout(1, 1, layout).unwrap();
let id = idgen.generate().unwrap();
```

## Thread-local global API

Initialize once, then call `next_id()` from any thread with no locking:

```rust
use snowflake_gen::{BitLayout, init, next_id};

fn main() {
    init(1, BitLayout::default()).unwrap();

    let id = next_id().unwrap();
    println!("{id}");
}
```

Each thread gets its own generator with a unique `node_id` assigned automatically.

## ID decomposition

```rust
use snowflake_gen::SnowflakeIdGenerator;

let mut idgen= SnowflakeIdGenerator::new(3, 7).unwrap();
let id = idgen.generate().unwrap();
let parts = idgen.decompose(id);

assert_eq!(parts.machine_id, 3);
assert_eq!(parts.node_id, 7);
```

## Buffered generation

`SnowflakeIdBucket` pre-generates a full sequence cycle and serves IDs from a buffer:

```rust
use snowflake_gen::SnowflakeIdBucket;

let mut bucket = SnowflakeIdBucket::new(1, 1).unwrap();
let id = bucket.get_id();
```

## Caveats

- **Thread count is limited by `node_id_bits`.** Each thread that calls `next_id()` is assigned a unique node ID. With the default 5 `node_id_bits`, at most 32 threads can generate IDs. Exceeding this returns `SnowflakeError::NodeIdExhausted`. If your application needs more threads, increase `node_id_bits` (and reduce `machine_id_bits` or `timestamp_bits` to compensate) when constructing your `BitLayout`.
- **`init()` can only be called once per process.** A second call returns `SnowflakeError::AlreadyInitialized`. Thread-local generators created by earlier threads retain the original configuration and cannot be updated in-place. To change the configuration, restart the process so all thread-local state is cleared and recreated.
- **Do not mix `lazy_generate` with clock-based methods** (`generate` / `real_time_generate`) on the same generator instance. `lazy_generate` advances the internal timestamp synthetically, so a later clock-based call may reuse a timestamp that `lazy_generate` already claimed, producing duplicate IDs. `SnowflakeIdBucket` uses `lazy_generate` internally on a dedicated generator and is safe to use alongside separate clock-based generators.

## License

MIT