ecore 0.10.0

no_std bit-level integer types, bitfield structs/enums, ranged integers, and enum maps
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
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
# ecore

[![crates.io](https://img.shields.io/crates/v/ecore.svg)](https://crates.io/crates/ecore)
[![docs.rs](https://docs.rs/ecore/badge.svg)](https://docs.rs/ecore)

**`ecore`** is a supplement to the standard `core` crate, focused on `no_std` environments (especially embedded/bare-metal scenarios). It provides bit-level operation capabilities that the standard library does not yet cover or stabilize: arbitrary-width integers, bitfield structs/enums, range-constrained value types, enum-to-array mapping, endianness adaptation, and more.

In embedded development, registers are typically laid out as fixed-width bitfields, but the Rust standard library only provides byte-granularity types like `u8/u16/u32/...`, lacking native support for non-aligned bits and non-standard-width integers. `ecore` fills this gap and establishes tight integration across its modules.

---

## Design Philosophy

`ecore`'s core design revolves around three goals:

1. **Fill `core`'s gaps** — Provide types and traits not yet stable or not planned in `core` (e.g., `u7`, compile-time integer arithmetic, bitfield definitions).
2. **Compile-time safety** — Push error checking to compile time as much as possible. Bit range overflow in bitfields, `RInt` value bounds violation, enum map index mismatch... all are compile errors.
3. **Module synergy** — Modules are not isolated tools; they share a trait system (`BasicInt`, `BitsCast`, `CInt`), producing compounding benefits when used together.

---

## Module Details

### `int` — Generic Integer Trait System & Compile-Time Arithmetic

The `int` module is the **cornerstone** of `ecore`. It defines a complete trait hierarchy that allows generic code to uniformly operate on all integer types (native + non-standard-width):

| Trait | Scope | Purpose |
|-------|-------|---------|
| `BasicInt` | `u1..u128`, `i1..i128`, `usize`, `isize` | Top-level abstraction for generic integers, includes all arithmetic/bitwise operators |
| `BasicUInt` | Unsigned subset of above | Generic constraint for unsigned integers |
| `BasicSInt` | Signed subset of above | Generic constraint for signed integers |
| `PrimaryInt` | `u8/u16/u32/u64/u128/i8/i16/i32/i64/i128/usize/isize` | Native integers (machine word width), the core operand type for `CInt` |
| `BitsOp` | All `BasicInt` implementors | Bit-level read/write operations, the foundation of `bitfld` |
| `CalcFitted` | Types adapted to machine computation width | Reduces code bloat from generic monomorphization |

#### `CInt` — The "Backdoor" for Compile-Time Integer Arithmetic

`CInt` is a collection of pure `const fn` static methods. Before `const_trait_impl` is stabilized, you cannot call trait methods like `Add::add` or `Shl::shl` in const contexts, yet embedded development heavily relies on compile-time computation (masks, offsets, constant folding).

`CInt` works around trait constraints by internally dispatching to native operators of concrete primitive types. The real value emerges in **generic const contexts** — where `const_trait_impl` would normally be required:

```rust
use ecore::int::{CInt, PrimaryInt, BasicInt};
use core::ops::Range;

/// Fixed-point value stored in the lower `FRAC` bits of a generic integer `T`.
/// The integer part occupies the upper `T::BITS - FRAC` bits.
struct Fixed<T: PrimaryInt, const FRAC: u32>(T);

impl<T: PrimaryInt, const FRAC: u32> Fixed<T, FRAC> {
    // ❌ Without CInt, this would NOT compile in const context:
    //    const INT_MASK: T = T::MAX << FRAC;  // error: trait method `shl` not const-stable
    //
    // ✅ CInt dispatches to the concrete primitive behind `T` at compile time:
    const INT_MASK: T = CInt::shl(T::MAX, FRAC);
    const FRAC_MASK: T = CInt::sub(CInt::shl(T::ONE, FRAC), T::ONE);
    const FRAC_RANGE: Range<T> = (CInt::sub(CInt::shl(T::ONE, FRAC), T::ONE))..T::ZERO;
    const INT_RANGE: Range<T> = (CInt::sub(T::ONE, CInt::shl(T::ONE, T::BITS - FRAC)))..T::ZERO;

    pub const fn from_parts(int: T, frac: T) -> Self {
        Self(CInt::bitor(CInt::shl(int, FRAC), frac))
    }
}
```

> **Key difference from `arbitrary-int`**: `arbitrary-int` also provides `u1`..`u127` types, but stores them internally as byte arrays, preventing `const fn` construction and arithmetic. `ecore`'s `BitInt` uses native integers as underlying storage (e.g., `u7``u8`), natively supporting `const` contexts and `const_default`, and integrates seamlessly with `CInt`.

### `bitint` — Non-Standard-Width Integers (vs `arbitrary-int`)

```rust
use ecore::bitint::{u3, u7, i12};

let a = u3::new(5).unwrap();          // 3-bit unsigned: 0..=7
let b = u7::new(100).unwrap();        // 7-bit unsigned: 0..=127
let c = i12::new(-1000).unwrap();     // 12-bit signed: -2048..=2047
```

| Dimension | `ecore::bitint` | `arbitrary-int` |
|-----------|-----------------|-----------------|
| **Underlying storage** | Native integer (`u7``u8`, `u12``u16`) | `[u8; N]` byte array |
| **const construction** |`const fn new()` | ❌ No `const` support |
| **Alignment/size** | Same as native type (1/2/4/8/16 bytes) | Byte-aligned, may have padding |
| **Arithmetic performance** | Native instructions (single `ADD`/`SUB`) | Software-emulated multi-byte ops |
| **Trait system** | Implements `BasicInt`, participates in generics | Independent type system |
| **Bitfield integration** | Usable directly as `#[bitfld]` field types | Requires manual conversion |
| **Compile-time computation** | Fully supported via `CInt` | Not supported |
| **Interop with standard ints** | `cast_as()` / `cast_from_primary()` | Requires `try_from()` |

> **Summary**: `arbitrary-int` suits arbitrary-precision scenarios exceeding 128 bits; `ecore::bitint` is optimized for embedded/systems programming, aiming for the same performance and const capabilities as native integers.

### `bitfld` — Bitfield Structs & Enums (vs `bitbybit`)

```rust
use ecore::bitfld::prelude::*;
use ecore::bitint::{u3, u4, u12};

// Define a 16-bit register
#[bitfld(u16, relative)]
struct MyReg {
    pub enable: bitfld!(bool, 0),         // 1 bit  at pos 0
    pub mode:   bitfld!(u3, 1..4),        // 3 bits at pos 1..3
    pub value:  bitfld!(u12, 4..),        // 12 bits at pos 4..15
}

let reg = MyReg(0)
    .enable().with(true)
    .mode().with(u3::new(5).unwrap())
    .value().with(u12::new(2048).unwrap());

assert!(reg.enable().read());
assert_eq!(reg.mode().read().value(), 5);
```

#### Bitfield Enums — Variants with Fields (tag+payload Packing)

```rust
use ecore::bitfld::prelude::*;
use ecore::bitint::{u2, u3, u4, u6};

// 6-bit enum: 2-bit tag + 4-bit payload, automatically packed
#[bitfld(u6, tag(u2), payload(u4))]
#[derive(Debug, PartialEq, Eq)]
enum Packet {
    Ack,                      // tag=0, no payload
    Read(u3),                 // tag=1, payload carries a u3 address
    Write(Unchecked<ErrorCode>), // tag=2, payload carries an enum value
    Nop(bool),                // tag=3, payload carries a bool flag
}

// A 2-bit error code enum usable as a variant field
#[bitfld(u2)]
#[derive(Debug, PartialEq, Eq)]
enum ErrorCode {
    Timeout = 0,
    Overflow = 1,
    BusError = 2,
}

// Construct and inspect
let pkt = Packet::Read(u3::new(5).unwrap());
assert_eq!(pkt, Packet::Read(u3::new(5).unwrap()));

// Decompose into raw tag+payload, useful for serialization
let bits: u6 = pkt.into_bits(); // packed 6-bit representation

// Re-layout tag and payload to different bit positions at zero cost
let relocated = pkt.into_layout::<u16, 4, 10>();
// tag at bits 4..6, payload at bits 10..14 in a u16
```

> With `tag(u2)` and `payload(u4)`, each variant's discriminant is packed into the tag bits, and its field value (if any) into the payload bits. The `BitsEnumReLayout` type enables zero-cost re-layout of tag/payload positions for different register formats.

#### Advanced Feature: Overlay Bitfields & `NBool`

```rust
use ecore::bitfld::prelude::*;
use ecore::bitint::u4;
use ecore::nbool::NBool;

#[bitfld(u32, overlay)]  // overlay allows field overlap
struct CtrlReg {
    pub byte0:   bitfld!(u8, 0..=7),
    pub low_nib: bitfld!(u4, 0..=3),   // overlaps with byte0's low 4 bits
    pub enabled: bitfld!(NBool, 8),    // NBool: zero value → true (ideal for default-enabled flags)
}

let reg = CtrlReg(0);                  // all zeros
assert!(reg.enabled().read().value()); // NBool(0) = true!
```

| Dimension | `ecore::bitfld` | `bitbybit` |
|-----------|-----------------|------------|
| **Definition style** | `#[bitfld(u32)]` attribute macro | `bitfield!` macro or builder |
| **Field types** | Any `BitsCast` type (bool/u7/i12/RInt/enum/array/NBool) | Only `bool` and standard integers |
| **Overlay bitfields** | ✅ Supports field overlap (e.g., byte + nibble sharing bits) ||
| **Bitfield enums** | ✅ tag+payload packing ||
| **Dynamic bitfields** | `DynBitField` (runtime offset access) ||
| **Endian-awareness** | `relative`/`overlay` modes, integrated with `repr` module | Fixed LE/BE |
| **const read/write** |`const_read()` / `const_with()` | Limited support |
| **bytemuck integration** | Auto-derives `Pod`/`Zeroable` (primary-width types) | Manual implementation required |
| **Array fields** | `bitfld!([bool; 4], 8..=11)` ||
| **Generic bitfields** | `BitField2<S1, S2, F1, F2>` spanning two storage units ||

> **Summary**: `bitbybit` suits simple bitfield packing scenarios; `ecore::bitfld` provides complete register-level modeling (overlay, enums, dynamic bitfields), deeply integrated with `bitint`/`ranged`/`repr`.

#### `Uncheckable` / `Unchecked` — Deferred Validation

Many field types in bitfields store raw bits without runtime validation, deferring checks to read time. This is handled by the `Uncheckable` trait and `Unchecked<T>` wrapper:

```rust,ignore
use ecore::repr::Unchecked;
use ecore::int::ranged::rint;

// RInt values are `Uncheckable`, so `Unchecked<RInt<...>>` stores the raw
// bit pattern. Validation only occurs when `.get()` is called on read:
let checked = rint!(0..=100, bits=7)::new(50).unwrap();
let raw: Unchecked<_> = checked.into();       // store without validation
assert_eq!(raw.get(), Ok(checked));           // validate on read
```

> `Unchecked<T>` is the recommended way to use `RInt` and other constrained types as `#[bitfld]` fields, since the outer `#[bitfld]` machinery already guarantees bit-level correctness.

#### `DynBitField` — Runtime Dynamic Bit-Field Access

`DynBitField<FLD>` provides type-erased bit-field read/write at runtime-determined bit offsets, while `DynBitField<FLD, STRUCT>` is the typed variant (MIRI-safe). Both are accessible via `.as_ref()` / `.as_mut()` on any `BitField`:

```rust,ignore
# use ecore::bitfld::prelude::*;
# use ecore::bitint::u7;
# #[bitfld(u32)] struct Reg { pub f: bitfld!(u7, 0..=6) }
let reg = Reg(0);
let dyn_ref: &DynBitField<u7, Reg> = reg.f().as_ref(); // typed, MIRI-safe
```

#### `BitField2` — Split Fields Across Storage Units

When a single logical field spans two non-contiguous bit ranges (common in fragmented hardware registers), `#[bitfld]` auto-generates a `BitField2<S1, S2, F, START1, LAST1, START2, LAST2>` instead of `BitField`. Reading and writing transparently handles the split.

### `varint` — Variable-Length Integer Encoding

```rust,ignore
use ecore::int::VarInt;

// Encode a u32 into a stack-allocated buffer
let mut buf = [0u8; VarInt::max_bytes_of::<u32>()];
let bytes = VarInt::encode(300u32, &mut buf);
assert_eq!(bytes, &[0xAC, 0x02]); // Protobuf-style 7-bit encoding

// Decode back
let (val, len) = VarInt::decode_unsigned::<u32>(bytes).unwrap();
assert_eq!((val, len), (300, 2));

// Zigzag encoding for signed integers
let (val, len) = VarInt::decode_signed::<i32>(bytes).unwrap();
```

Uses a 7-bit-per-byte encoding scheme (high bit = continuation), compatible with Protocol Buffers varint. All encode/decode operations are `const fn`, suitable for compile-time protocol construction.

### `ranged` — Compile-Time Range-Constrained Value Type `RInt`

```rust
use ecore::bitint::{u4, u7};
use ecore::int::ranged::{RInt, RRU16, RRU8, rint};

// Define a 7-bit storage type for values 0..=100
type Percentage = RInt<u7, RRU16<0, 100, 0>>;

let p = Percentage::new(50).unwrap();
assert_eq!(p.value(), 50);
// Percentage::new(101); // rejected at compile time!

// rint! macro simplifies definition
type Volume = rint!(0..=11, bits=4); // 0..=11 stored in u4
```

**Key advantages**:

- **Compile-time bounds checking**: `new()` rejects out-of-range values at compile time.
- **Storage compression**: A value range of `0..=100` only needs 7 bits (`u7`), not a full `u8`. In `#[bitfld]`, this directly translates to more compact register layouts.
- **Step support**: `RInt<u7, RRU16<0, 100, 0, 5>>` represents `{0, 5, 10, ..., 100}`, ideal for enumerated configuration values.
- **Bitfield integration**: `RInt` implements `BitsCast` and can be used directly as a `#[bitfld]` field type.

```rust
use ecore::bitfld::prelude::*;
use ecore::bitint::{u4, u7};
use ecore::int::ranged::{RInt, RRU8, rint};

// Using RInt in bitfields: automatic bit-width compression
#[bitfld(u16)]
struct Config {
    pub volume: bitfld!(Unchecked<rint!(0..=11, bits=4)>, 0..=3),  // only 4 bits
    pub rate:   bitfld!(Unchecked<rint!(0..=100, bits=7)>, 4..=10), // only 7 bits
}
```

### `map_enum` — Efficient Enum-to-Array Mapping

```rust
use ecore::{EnumMap, MapEnum, map_enum};

#[derive(MapEnum)]
enum Color { Red, Green, Blue }

// EnumMap is an array indexed by enum variants
let map = EnumMap::<Color, &str>::map_new(|c| match c {
    Color::Red   => "Red",
    Color::Green => "Green",
    Color::Blue  => "Blue",
});

assert_eq!(map[Color::Green], "Green");
```

**Key advantages**:

- **Zero-overhead indexing**: Enum discriminant values serve directly as array indices, `O(1)` access with no hashing and no branching.
- **Compile-time safety**: The number of enum variants and array size are bound at compile time, preventing out-of-bounds access.
- **`map_enum!` macro**: Supports const-context lookup table construction:

```rust,ignore
const NAMES: EnumMap<Color, &str> = map_enum!(
    Color::Red   => "Red",
    Color::Green => "Green",
    Color::Blue  => "Blue",
);
```

- **`IterEnumDiscriminants`**: `#[derive(IterEnumDiscriminants)]` generates a compile-time iterator over all variant names and discriminant values, useful for debug formatting, serialization, and lookup table generation.

- **Bitfield enum synergy**: Bitfield enums also implement `MapEnum`, enabling direct construction of register-field-to-description lookup tables.
- **`remap` / `transparent_wrap`**: Reinterpret the same underlying array under a different enum key type (`remap`), or wrap/unwrap with `bytemuck::TransparentWrapper` for zero-cost newtype patterns.

```rust,ignore
# use ecore::{EnumMap, MapEnum};
# #[derive(MapEnum)] enum A { X, Y }
# #[derive(MapEnum)] enum B { P, Q }
let map_a: EnumMap<A, u32> = EnumMap::map_new(|_| 0);
let map_b: &EnumMap<B, u32> = map_a.remap(); // same memory, different key type
```

### `repr` — Endianness & Alignment Adaptation

```rust
use ecore::repr::{LEndian, BEndian, Unalign};
use ecore::repr::AlterRepr;

// Fixed little-endian (regardless of target endianness)
let val = LEndian(0x12345678u32);
assert_eq!(<LEndian<u32> as AlterRepr<u32>>::into_std_repr(val), 0x12345678);

// Unaligned access (packed)
let unaligned: Unalign<u32> = Unalign(42);

// Combined: unaligned + little-endian
type PacketHeader = Unalign<LEndian<u32>>;
```

In embedded scenarios, peripheral registers may use a different endianness than the CPU, or reside at unaligned addresses. The `repr` module provides compile-time endian conversion and `packed` representation, enabling bitfield modeling of registers with arbitrary endianness when combined with `bitfld`.

### `nbool` — Negated Boolean

```rust
use ecore::nbool::NBool;

// NBool(false) = true, NBool(true) = false
// Zero-initialized memory defaults to true (ideal for enable/active type flags)
let flag = NBool::new(true);  // internally stored as 0
```

In embedded registers, many "enable" flags reset to 1 (enabled). `NBool` allows zero-initialized memory to correctly express this semantic, avoiding forgotten critical enable bits.

---

## The Power of Module Synergy

The true power of these modules emerges when **combined**. Here is a real-world scenario:

```rust,ignore
use ecore::bitfld::prelude::*;
use ecore::bitint::{u3, u4, u7};
use ecore::int::ranged::{RInt, RRU16, rint};
use ecore::repr::Unalign;
use ecore::nbool::NBool;
use ecore::MapEnum;

// 1️⃣ Define opcodes with an enum (map_enum for later lookup table)
#[derive(MapEnum, Debug, PartialEq, Eq)]
enum OpCode {
    Nop   = 0,
    Read  = 1,
    Write = 2,
    Reset = 3,
}

// 2️⃣ Define a 16-bit command word with bitfld (bitint + ranged + nbool combined)
#[bitfld(u16)]
#[derive(Debug, Default)]
struct Command {
    pub op:     bitfld!(u3, 0..=2),                          // bitint: 3-bit unsigned
    pub ch:     bitfld!(Unchecked<rint!(0..=15, bits=4)>, 3..=6), // ranged: channel 0..15 compressed to 4 bits
    pub enable: bitfld!(NBool, 7),                            // nbool: zero → true
    pub data:   bitfld!(u7, 8..=14),                          // bitint: 7-bit data
    pub parity: bitfld!(bool, 15),                            // parity
}

// 3️⃣ Build a command
let cmd = Command::default()
    .with_op(u3::new(OpCode::Write as u8).unwrap())
    .with_ch(Unchecked::from(7u8))  // RInt requires Unchecked wrapper
    .with_enable(true)               // NBool: true → stored as 0
    .with_data(u7::new(100).unwrap())
    .with_parity(false);

// 4️⃣ EnumMap lookup table: opcode → description
const DESCS: EnumMap<OpCode, &str> = ecore::map_enum!(
    OpCode::Nop   => "No operation",
    OpCode::Read  => "Read",
    OpCode::Write => "Write",
    OpCode::Reset => "Reset",
);

// 5️⃣ Extract opcode from command word and look up description
let op_val = cmd.op() as u8;
let op = OpCode::from_index(op_val as usize);
println!("Executing: {}", DESCS[op]);

// 6️⃣ Read/write from register slots (repr handles unaligned access)
let mut buf: [Unalign<u16>; 4] = Default::default();
buf[0] = Unalign(cmd.0);
```

**Module collaboration relationships:**

```mermaid
graph TD
    CINT["int::CInt<br/>Compile-time arithmetic"] --> BITINT["bitint<br/>Non-standard-width ints"]
    CINT --> BITFLD["bitfld<br/>Bitfield structs"]
    CINT --> RANGED["ranged<br/>RInt"]
    BASIC["int::BasicInt<br/>Generic traits"] --> BITINT
    BASIC --> BITFLD
    BASIC --> RANGED
    BITINT --> BITFLD
    RANGED --> BITFLD
    MAP["map_enum<br/>EnumMap"] --> BITFLD
    NBOOL["nbool<br/>Negated bool"] --> BITFLD
    REPR["repr<br/>Endian/alignment"] --> BITFLD
```

- `CInt` provides the "infrastructure" of compile-time computation for all modules
- `BasicInt` trait allows `bitint`'s non-standard integers to be used indistinguishably from native integers in generic code
- `bitint` + `ranged` + `nbool` can all directly serve as `bitfld` field types
- `map_enum` pairs with `bitfld` enums to achieve zero-overhead register-value-to-semantic-description mapping
- `repr` handles endianness/alignment, ensuring `bitfld`-modeled registers match actual memory layout

---

## Relationship with the `core` Standard Library

| `core` provides | `ecore` supplements |
|-----------------|---------------------|
| `u8/u16/u32/u64/u128` native integers | `u1..u127` / `i1..i127` non-standard-width integers (`bitint`) |
| Integer traits (`Add`, `Shl`, ...) | Unified `BasicInt` trait system (`int`) |
| Runtime integer arithmetic | Compile-time `CInt` arithmetic (substitute before `const_trait_impl` stabilizes) |
| No built-in varint encoding | Protobuf-style `VarInt` const encode/decode (`varint` feature) |
| Enum `#[repr(usize)]` + array | `EnumMap<Enum, T>` type-safe mapping (`map_enum`) |
| Bit operations (`bitand`, `shl`, ...) | Bitfield `#[bitfld]` struct/enum (`bitfld`) |
| Memory endianness (`to_le`/`to_be` methods) | Endianness type wrappers (`repr::LEndian`/`BEndian`) |
| `bool` | `NBool` negated boolean (`nbool`) |
| `NonZero` family | Compile-time range-constrained `RInt` (`ranged`) |

---

## Features

| Feature | Default | Description |
|---------|---------|-------------|
| `bitint` || Non-standard-width integers: `u1`, `u3`, `u7`, `i12`, etc., backed by native primitives |
| `bitfld` || `#[bitfld(u32)]` bitfield structs/enums with overlay, dynamic bitfields, tag+payload |
| `ranged-int` || `RInt<STORE, RANGE>` — compile-time bounds checking and storage compression |
| `varint` | | Variable-length integer encoding/decoding (similar to Protobuf varint) |

## Quick Example

```rust
use ecore::bitfld::prelude::*;
use ecore::bitint::{u3, u12};

// Define a 16-bit register
#[bitfld(u16, relative)]
struct MyReg {
    pub enable: bitfld!(bool, 0),
    pub mode:   bitfld!(u3, 1..4),
    pub value:  bitfld!(u12, 4..),
}

let reg = MyReg(0)
    .enable().with(true)
    .mode().with(u3::new(5).unwrap())
    .value().with(u12::new(1024).unwrap());

assert!(reg.enable().read());
assert_eq!(reg.mode().read().value(), 5);
```

## Modules

- **`int`**`BasicInt`/`BasicUInt`/`BasicSInt` generic traits; `BitsOp` bit-level operations; `CInt` compile-time integer arithmetic
- **`bitint`** — Non-standard-width integers `u1`..`u127`, `i1`..`i127`, zero-overhead abstraction
- **`bitfld`**`#[bitfld]` bitfield structs/enums, dynamic bitfields `DynBitField`, tag+payload re-layout
- **`ranged`**`RInt` compile-time value-range-constrained integers with storage compression and step support
- **`map_enum`**`EnumMap` enum-to-array mapping, `map_enum!` const construction macro
- **`repr`**`LEndian<T>`/`BEndian<T>`/`Unalign<T>` endianness and alignment adaptation
- **`nbool`**`NBool` negated boolean (zero value → true)
- **`varint`**`VarInt` Protobuf-style variable-length integer encoding/decoding, all `const fn`
- **`range`**`BitsRange` efficient bit-range description and cross-byte copying

## MSRV

Rust 1.87+

## License

MIT — see [repository](https://github.com/pbl-pw/ecore) for full license.