bitcraft 1.0.0

A zero-cost, hardware-aligned bitfield and enumeration generator.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
# Atomics Implementation (`atomic_bitstruct!` & `atomic_bitenum!`)

The `atomic_bitstruct!` and `atomic_bitenum!` macros provide **zero-cost, lock-free concurrent mutation** of individual bitfields and enumerations packed inside standard Rust atomic integers.

This architecture allows developers to define memory-efficient flags, counters, and states that can be safely updated concurrently by multiple threads without the need for traditional synchronization primitives like `Mutex` or `RwLock`.

---

## 1. Supported Base & Field Types

The macros natively map to `portable_atomic` types, giving you full control over the underlying memory footprint:

- **Unsigned Atomics**: `AtomicU8`, `AtomicU16`, `AtomicU32`, `AtomicU64`, **`AtomicU128`**
- **Signed Atomics**: `AtomicI8`, `AtomicI16`, `AtomicI32`, `AtomicI64`, **`AtomicI128`**

`bitcraft` leverages `portable-atomic` to provide 128-bit atomic support even on platforms where the Rust standard library does not yet expose them natively.

Fields defined inside an `atomic_bitstruct!` inherit all the robust capabilities of the core `bitstruct!` engine. This includes:

1. `bool`: Single-bit boolean flags.
2. **Unsigned Integers** (`u8` to `u128`): Extracted using logical bitshifts.
3. **Signed Integers** (`i8` to `i128`): Safely extracted using automatic arithmetic shift sign-extension. Negative bounds testing and 2's complement handling are built-in.
4. **Custom Enums**: Derived via the `bitenum!` macro for type-safe state representations.

---

## 2. `atomic_bitenum!` — Atomic State Machines

The `atomic_bitenum!` macro generates a `#[repr(transparent)]` wrapper around an atomic integer that represents a strictly bounded enumeration. This is ideal for representing high-level states (e.g., `Status`, `Mode`, `ConnectionState`) that must be transitioned atomically.

### Example: Concurrent Connection State

```rust
use bitcraft::atomic_bitenum;
use portable_atomic::{AtomicU8, Ordering};

atomic_bitenum! {
    /// A lock-free atomic connection state tracker.
    pub enum ConnectionState(AtomicU8, 2) {
        DISCONNECTED = 0,
        CONNECTING = 1,
        CONNECTED = 2,
        ERROR = 3,
    }
}

let state = ConnectionState::new(ConnectionStateValue::DISCONNECTED);

// Atomically transition to CONNECTING
state.store(ConnectionStateValue::CONNECTING, Ordering::Release);
```

### Key API Surface

- **`load(order)`**: Returns the current `[Name]Value` snapshot.
- **`store(val, order)`**: Atomically overwrites the current variant.
- **`swap(val, order)`**: Atomically swaps and returns the previous variant.
- **`compare_exchange(current, new, success, failure)`**: Atomic CAS transition. Returns `Ok(previous)` if successful, or `Err(actual)` if the current value didn't match.
- **`update(set_order, fetch_order, closure)`**: Forces a CAS transition to a new variant.
- **`update_or_abort(set_order, fetch_order, closure)`**: Conditionally transitions or aborts based on business logic.

### Memory Layout & Safety

Because `atomic_bitenum!` is `#[repr(transparent)]`, it has the **exact same memory layout** as its underlying atomic primitive. This makes it safe to use in FFI or MMIO contexts where hardware expects a specific integer width.

---

## 3. Generated API Surface (`atomic_bitstruct!`)

When you use the `atomic_bitstruct!` macro, it generates an outer **Atomic Struct** and an inner **Snapshot Value Struct**. Here is a complete overview of the methods available to you.

```rust
use bitcraft::atomic_bitstruct;
use portable_atomic::{AtomicU32, Ordering};

atomic_bitstruct! {
    /// A lock-free task scheduler state.
    pub struct AtomicTaskState(AtomicU32) {
        pub is_running: bool = 1,
        pub retries: u8 = 7,
        pub task_id: u16 = 16,
    }
}
```

### Outer Atomic Methods (`AtomicTaskState`)

These methods interact directly with the shared memory location. Every method requires an explicit `portable_atomic::Ordering`.

- **Field Getters**: `state.is_running(Ordering::Acquire) -> bool`
- **Field Setters**: `state.set_retries(5, Ordering::Release)` (Executes a targeted CAS loop)
- **Snapshot `get()`**: `state.get(Ordering::Acquire) -> AtomicTaskStateValue`
- **Total `set()`**: `state.set(new_value, Ordering::Release)` (Direct `store()`, overwrites everything)
- **Batch `update()`**: `state.update(set_order, fetch_order, |v| { ... })`
- **Conditional `update_or_abort()`**: `state.update_or_abort(set_order, fetch_order, |v| { ... })`

### Inner Snapshot Methods (`AtomicTaskStateValue`)

The snapshot value (`v` in closures, or returned by `get()`) is a standard, non-atomic `bitstruct!`. It operates entirely in local CPU registers.

- **Field Getters**: `v.retries() -> u8`
- **Field Setters**: `v.set_retries(5)` (Instant bitwise mutation locally)
- **Safe Setters**: `v.try_set_retries(300)` (Returns `Err` if value overflows bit-width)
- **Builders**: `v.with_is_running(true)` (Chainable initialization)
- **Raw Interop**: `v.to_bits() -> u32` / `AtomicTaskStateValue::from_bits(raw)`

---

## 4. Architecture & Concurrency Model

### The Challenge of Atomic Bitfields

Updating an entire atomic integer is trivial. However, updating a specific 4-bit chunk inside a 32-bit integer is a fundamentally different challenge. Standard hardware atomic instructions like `fetch_add` or `fetch_or` lack the ability to clear specific bit ranges while simultaneously setting others.

### CAS Loop Synchronization (`fetch_update`)

To achieve field-level atomicity, `atomic_bitstruct!` relies entirely on **Compare-And-Swap (CAS)** loops via Rust's `fetch_update` primitive.

When you call a generated setter method (e.g., `set_status`), the macro automatically generates the optimal bitmask and executes the following underlying logic:

```rust
// Internally generated inside the macro:
self.0.fetch_update(set_order, portable_atomic::Ordering::Relaxed, |raw| {
    // 1. Mask out the old field value (clearing the bits)
    // 2. Shift and apply the new value
    Some((raw & !MASK) | (val_masked << OFFSET))
}).unwrap();
```

**Why this is safe:** If another thread mutates the atomic integer in the exact microsecond between the "read" and the "write" phases, the hardware CAS instruction fails. `fetch_update` will automatically spin, fetch the *newest* value, and retry the mask-and-shift operation. This ensures absolute correctness and zero data-loss under high contention.

---

## 5. Struct-Level Batch Updates (The `Value` Pattern)

Performing back-to-back single-field updates on an atomic struct is highly inefficient, as each individual setter triggers its own independent CAS loop. Furthermore, it breaks transactionality: another thread might observe the struct in a partially updated state.

To solve this cleanly, whenever you define an `atomic_bitstruct!`, the macro automatically generates a shadow, non-atomic type named `[StructName]Value`. This shadow type is a standard `bitstruct!` representing a disconnected, point-in-time snapshot of the bits.

The macro exposes three distinct methods for struct-level interactions using this `Value` type: `.get()`, `.set()`, and `.update()`.

### `get()`: Non-Atomic Snapshot

If you want to read all fields exactly as they exist at a specific point in time, you can fetch a `Value` snapshot directly.

```rust
// Fetches the entire atomic state in a single CPU instruction
let current_snapshot = f.get(Ordering::Acquire);

// You can now read multiple fields locally without further atomic overhead
if current_snapshot.is_ready() {
    println!("Limit is: {}", current_snapshot.limit());
}
```

### `set()`: Total Overwrite

If you have constructed a `Value` struct locally and wish to overwrite the *entire* atomic state at once, you can use `.set()`.

```rust
let mut new_state = ConcurrentFlagsValue::default();
new_state.set_is_ready(true);
new_state.set_limit(10);

// Instantly overwrites the atomic integer using a direct memory store.
// WARNING: Any concurrent changes made by other threads will be completely wiped out!
f.set(new_state, Ordering::Release);
```

`.set()` bypasses the CAS loop entirely, utilizing a direct atomic `store()`. It is extremely fast but offers no protection against overwriting concurrent modifications.

### `update()`: Transactional CAS Loop

If you want to modify a few fields simultaneously while **preserving** concurrent modifications made to *other* fields by different threads, you must use `.update()`.

The `.update()` method yields a mutable `Value` snapshot into a closure. Once the closure finishes, the new bits are pushed back in a *single* CAS transaction.

```rust
// We update multiple fields atomically in ONE single transaction!
let final_state = f.update(Ordering::SeqCst, Ordering::Relaxed, |v| {
    // 'v' is a mutable ConcurrentFlagsValue snapshot.
    // These calls are normal, non-atomic operations. They are extremely fast.
    v.set_is_ready(true);
    v.set_retries(-500);

    let current_limit = v.limit();
    v.set_limit(current_limit + 1);
});

// The atomic struct is now fully updated, guaranteed.
// Any concurrent changes to fields you didn't touch (e.g., if there was a fourth field)
// are safely preserved by the CAS retry mechanism!
assert_eq!(final_state.retries(), -500);
```

This ensures absolute atomicity across multi-field changes without intermediate states leaking to other threads, while strictly preserving concurrency!

### `update_or_abort()`: Lock-Free Business Logic & Partial Updates

While `.update()` blindly forces the CAS loop to eventually commit, many concurrent systems need to **conditionally abort** a transaction based on the concurrent state.

For example, you might only want to increment a connection counter if the pool is not full. The `.update_or_abort()` method takes a closure that returns an `Option<()>`. If you return `None`, the CAS loop safely aborts, no memory is written (saving CPU cache line invalidations), and it returns an `Err(original_snapshot)`.

#### The Power of "Partial Updates"

It is crucial to understand that the `v` parameter passed into your closure is a **complete snapshot** of the atomic state at that exact microsecond. Because it holds *all* the bits, you **only need to call setters for the fields you want to change**.

Any field you *don't* modify simply retains its value from the initial load. The CAS loop inherently guarantees that the unmodified fields will be perfectly preserved during the atomic swap.

```rust
let result = pool_flags.update_or_abort(Ordering::SeqCst, Ordering::Relaxed, |v| {
    // `v` contains the full state. Let's look at `active_connections`
    let prev_active = v.active_connections();

    // 1. Evaluate concurrent state against business logic
    if prev_active >= 100 {
        return None; // ABORT! The pool is full.
    }

    // 2. State is valid, mutate our local snapshot.
    // Notice we ONLY modify `active_connections`.
    // If this struct also has an `is_active` boolean or a `status` enum,
    // they are AUTOMATICALLY preserved because we didn't touch them!
    v.set_active_connections(prev_active + 1);

    // 3. Commit the transaction to the CAS loop
    Some(())
});

match result {
    Ok(final_state) => {
        println!("Success! New connection count: {}", final_state.active_connections());
    }
    Err(stalled_state) => {
        println!("Failed to connect: Pool is at max capacity ({})!", stalled_state.active_connections());
    }
}
```

#### Why this is better than Mutex/RwLock

1. **Zero Thread Blocking**: If multiple threads try to grab a connection at the exact same microsecond, they don't go to sleep waiting for a Mutex lock. The CAS loop simply spins, fetching the newly-updated value and re-running your closure instantly.
2. **Safe Back-Offs**: By returning `None`, your thread safely backs off without triggering expensive memory-bus locks or cache invalidations across the CPU cores.
3. **No Deadlocks**: Because there are no locks, there are no deadlocks.

This completely replaces the need for heavy synchronization primitives, allowing you to build highly-contended, transactionally-safe architectures where threads can cleanly back off or fail based on localized business rules.

---

## 6. Bounds Checking & Safety in Atomic Contexts

Because `atomic_bitstruct!` is built directly on top of the `bitstruct!` engine, all field-level protections are strictly enforced even within concurrent CAS loops.

### Individual `try_set()`

When setting individual fields via standard setters (e.g., `set_limit(val, order)`), if the provided `val` exceeds the defined bit limits of the field, the macro will safely drop the overflowing bits to prevent adjacent field corruption.

If you need runtime validation, every field also generates a `try_set_[field_name]` method.

```rust
// Safely attempts to execute the CAS loop.
// If the value is too large, the CAS loop is aborted BEFORE execution
// and it returns a BitstructError.
f.try_set_limit(255, Ordering::SeqCst)?;
```

### Bounds Safety Inside `.update()`

When you use the `.update()` closure, the `Value` snapshot is a standard `bitstruct!`. All bounds checking is enforced locally inside the closure *before* the CAS loop attempts to commit the final state to memory.

```rust
f.update_or_abort(Ordering::SeqCst, Ordering::Relaxed, |v| {
    // If you use try_set inside the batch updater, you can catch bounds errors
    // locally before the atomic commit happens!
    if v.try_set_limit(999).is_err() {
        // Handle error...
        return None; // Abort CAS loop!
    }
    Some(())
});
```

---

## 7. Mechanical Sympathy & Memory Orderings

To provide "Mechanical Sympathy" with the underlying hardware, `atomic_bitstruct!` never hides synchronization costs. Every getter and setter explicitly requires a `portable_atomic::Ordering`.

This enforces that developers actively dictate the CPU-level memory barriers emitted by the compiler:

- **Getters** (`f.is_ready(Ordering::Acquire)`): Map directly to atomic `.load(order)`.
- **Setters** (`f.set_ready(true, Ordering::Release)`): The provided `order` acts as the `set_order` (the successful write path) for the CAS loop. The underlying failure/retry load path is hardcoded to `Ordering::Relaxed` internally, ensuring optimal performance by not emitting unnecessary CPU synchronization barriers when a loop iteration is aborted.
- **Batch Updates** (`f.update(set_order, fetch_order, closure)`): Allows full explicit configuration of both the success and the retry ordering constraints.

### Best Practices for Memory Ordering

1. **Counter Increments**: Use `Ordering::Acquire` for reading and `Ordering::Release` for writing. This ensures that any data modified by the thread *before* it updated the counter is visible to other threads that *see* the updated counter.
2. **Internal Flags**: If a bitfield is purely used for internal thread logic (e.g., a "dirty" flag), `Ordering::Relaxed` is often sufficient and avoids expensive CPU memory barriers.
3. **Global Synchronization**: Use `Ordering::SeqCst` if you need a total global order of operations across all CPUs. Note that this is the most expensive ordering.

---

## 8. Performance & Memory Safety

### CAS Retry Cost

Under high contention, the CAS loop may retry. However, because the closure executes in registers, a retry typically costs only a few nanoseconds. This is significantly cheaper than a context switch caused by a blocked `Mutex`.

### Zero Padding & Alignment

Since `bitcraft` uses `#[repr(transparent)]`, there is **zero padding**. Your atomic structs are bit-for-bit identical to raw atomic integers in memory.

### Thread Safety

All generated atomic structures automatically implement `Send` and `Sync`, provided the underlying atomic type is `Send + Sync`. This allows them to be shared across thread boundaries and accessed concurrently from multiple `Arc` references.

---

## 9. Real-World Example: Network Sequence Tracker

A common use case is tracking a network sequence number along with status flags in a single 64-bit word.

```rust
atomic_bitstruct! {
    pub struct SequenceTracker(AtomicU64) {
        pub sequence: u48 = 48,
        pub is_acked: bool = 1,
        pub is_retransmitted: bool = 1,
        pub priority: u8 = 4,
        pub reserved: u16 = 10,
    }
}

let tracker = SequenceTracker::new(0);

// Atomically increment sequence while preserving flags
tracker.update(Ordering::SeqCst, Ordering::Relaxed, |v| {
    let next = v.sequence().wrapping_add(1);
    v.set_sequence(next);
});
```

---

## 10. Real-World Example: Worker Pool State Machine

`atomic_bitenum!` is perfect for managing the lifecycle of background workers in a thread pool.

```rust
atomic_bitenum! {
    /// High-level lifecycle states for a background worker.
    pub enum WorkerLifeCycle(AtomicU8, 3) {
        IDLE = 0,
        FETCHING = 1,
        PROCESSING = 2,
        BACKOFF = 3,
        STOPPING = 4,
        TERMINATED = 5,
    }
}

let worker_state = WorkerLifeCycle::new(WorkerLifeCycleValue::IDLE);

// Use update_or_abort to transition safely
let result = worker_state.update_or_abort(Ordering::SeqCst, Ordering::Relaxed, |current| {
    match current {
        WorkerLifeCycleValue::IDLE => Some(WorkerLifeCycleValue::FETCHING),
        WorkerLifeCycleValue::FETCHING => Some(WorkerLifeCycleValue::PROCESSING),
        WorkerLifeCycleValue::PROCESSING => Some(WorkerLifeCycleValue::IDLE),
        // If the worker is STOPPING, we cannot transition back to FETCHING
        _ => None,
    }
});

if result.is_ok() {
    println!("Worker successfully transitioned state!");
}
```

---

## 11. `atomic_bitarray!` — Atomic Packed Arrays

While `atomic_bitstruct!` is optimized for heterogeneous fields (e.g., a bool, a u8, and a u16), the `atomic_bitarray!` macro is specialized for **homogeneous packed data**. It allows you to define arrays of small bit-widths (like 1-bit flags, 4-bit nibbles, or 12-bit sensor data) that are backed by a single atomic integer.

### Example: Atomic Nibble Array

```rust
use bitcraft::{atomic_bitarray, Ordering};

atomic_bitarray! {
    /// 16 nibbles (4-bit integers) packed into a single AtomicU64.
    pub struct AtomicNibbles(u 4, 16);
}

let arr = AtomicNibbles::new(0);

// Atomically set the 5th nibble
arr.set(5, 0xA, Ordering::SeqCst);

// Read the 5th nibble
let val = arr.get(5, Ordering::Relaxed); // 0xA
```

### Automatic Snapshot Generation

Similar to `atomic_bitstruct!`, every `atomic_bitarray!` automatically generates a shadow `Value` struct (e.g., `AtomicNibblesValue`) which is a non-atomic `bitarray!`.

- **`get_snapshot(order)`**: Fetches the entire array into a local register-backed struct.
- **`set_snapshot(val, order)`**: Overwrites the entire array.
- **`update(...)`**: Performs a transactional multi-element update.

```rust
arr.update(Ordering::SeqCst, Ordering::Relaxed, |snap| {
    // Modify multiple elements in a single transaction
    let prev = snap.get(0);
    snap.set(0, prev + 1);
    snap.set(1, 0xF);
});
```

### Comparison: `atomic_bitstruct!` vs `atomic_bitarray!`

| Feature | `atomic_bitstruct!` | `atomic_bitarray!` |
| :--- | :--- | :--- |
| **Field Types** | Heterogeneous (bool, uN, iN, Enum) | Homogeneous (uN, iN, or bool) |
| **Access** | Named methods (`v.is_running()`) | Index-based (`v.get(i)`) |
| **Backing** | Explicit (`AtomicU64`) | Automatic (Smallest possible) |
| **Use Case** | Control registers, headers | Bit-maps, packed counters |