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:
- Fill
core's gaps — Provide types and traits not yet stable or not planned incore(e.g.,u7, compile-time integer arithmetic, bitfield definitions). - Compile-time safety — Push error checking to compile time as much as possible. Bit range overflow in bitfields,
RIntvalue bounds violation, enum map index mismatch... all are compile errors. - 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:
use ;
use 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.
;
Key difference from
arbitrary-int:arbitrary-intalso providesu1..u127types, but stores them internally as byte arrays, preventingconst fnconstruction and arithmetic.ecore'sBitIntuses native integers as underlying storage (e.g.,u7→u8), natively supportingconstcontexts andconst_default, and integrates seamlessly withCInt.
bitint — Non-Standard-Width Integers (vs arbitrary-int)
use ;
let a = new.unwrap; // 3-bit unsigned: 0..=7
let b = new.unwrap; // 7-bit unsigned: 0..=127
let c = new.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-intsuits arbitrary-precision scenarios exceeding 128 bits;ecore::bitintis optimized for embedded/systems programming, aiming for the same performance and const capabilities as native integers.
bitfld — Bitfield Structs & Enums (vs bitbybit)
use *;
use ;
// Define a 16-bit register
let reg = MyReg
.enable.with
.mode.with
.value.with;
assert!;
assert_eq!;
Bitfield Enums — Variants with Fields (tag+payload Packing)
use *;
use ;
// 6-bit enum: 2-bit tag + 4-bit payload, automatically packed
// A 2-bit error code enum usable as a variant field
// Construct and inspect
let pkt = Read;
assert_eq!;
// 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.;
// tag at bits 4..6, payload at bits 10..14 in a u16
With
tag(u2)andpayload(u4), each variant's discriminant is packed into the tag bits, and its field value (if any) into the payload bits. TheBitsEnumReLayouttype enables zero-cost re-layout of tag/payload positions for different register formats.
Advanced Feature: Overlay Bitfields & NBool
use *;
use u4;
use NBool;
// overlay allows field overlap
let reg = CtrlReg; // all zeros
assert!; // 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:
bitbybitsuits simple bitfield packing scenarios;ecore::bitfldprovides complete register-level modeling (overlay, enums, dynamic bitfields), deeply integrated withbitint/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:
use Unchecked;
use 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!new.unwrap;
let raw: = checked.into; // store without validation
assert_eq!; // validate on read
Unchecked<T>is the recommended way to useRIntand 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:
# use *;
# use u7;
#
let reg = Reg;
let dyn_ref: & = 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
use VarInt;
// Encode a u32 into a stack-allocated buffer
let mut buf = ;
let bytes = encode;
assert_eq!; // Protobuf-style 7-bit encoding
// Decode back
let = .unwrap;
assert_eq!;
// Zigzag encoding for signed integers
let = .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
use ;
use ;
// Define a 7-bit storage type for values 0..=100
type Percentage = ;
let p = new.unwrap;
assert_eq!;
// Percentage::new(101); // rejected at compile time!
// rint! macro simplifies definition
type Volume = rint!; // 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..=100only needs 7 bits (u7), not a fullu8. 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:
RIntimplementsBitsCastand can be used directly as a#[bitfld]field type.
use *;
use ;
use ;
// Using RInt in bitfields: automatic bit-width compression
map_enum — Efficient Enum-to-Array Mapping
use ;
// EnumMap is an array indexed by enum variants
let map = map_new;
assert_eq!;
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:
const NAMES: = map_enum!;
-
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 withbytemuck::TransparentWrapperfor zero-cost newtype patterns.
# use ;
#
#
let map_a: = map_new;
let map_b: & = map_a.remap; // same memory, different key type
repr — Endianness & Alignment Adaptation
use ;
use AlterRepr;
// Fixed little-endian (regardless of target endianness)
let val = LEndian;
assert_eq!;
// Unaligned access (packed)
let unaligned: = Unalign;
// Combined: unaligned + little-endian
type PacketHeader = ;
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
use NBool;
// NBool(false) = true, NBool(true) = false
// Zero-initialized memory defaults to true (ideal for enable/active type flags)
let flag = new; // 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:
use *;
use ;
use ;
use Unalign;
use NBool;
use MapEnum;
// 1️⃣ Define opcodes with an enum (map_enum for later lookup table)
// 2️⃣ Define a 16-bit command word with bitfld (bitint + ranged + nbool combined)
// 3️⃣ Build a command
let cmd = default
.with_op
.with_ch // RInt requires Unchecked wrapper
.with_enable // NBool: true → stored as 0
.with_data
.with_parity;
// 4️⃣ EnumMap lookup table: opcode → description
const DESCS: = map_enum!;
// 5️⃣ Extract opcode from command word and look up description
let op_val = cmd.op as u8;
let op = from_index;
println!;
// 6️⃣ Read/write from register slots (repr handles unaligned access)
let mut buf: = Defaultdefault;
buf = Unalign;
Module collaboration relationships:
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
CIntprovides the "infrastructure" of compile-time computation for all modulesBasicInttrait allowsbitint's non-standard integers to be used indistinguishably from native integers in generic codebitint+ranged+nboolcan all directly serve asbitfldfield typesmap_enumpairs withbitfldenums to achieve zero-overhead register-value-to-semantic-description mappingreprhandles endianness/alignment, ensuringbitfld-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
use *;
use ;
// Define a 16-bit register
let reg = MyReg
.enable.with
.mode.with
.value.with;
assert!;
assert_eq!;
Modules
int—BasicInt/BasicUInt/BasicSIntgeneric traits;BitsOpbit-level operations;CIntcompile-time integer arithmeticbitint— Non-standard-width integersu1..u127,i1..i127, zero-overhead abstractionbitfld—#[bitfld]bitfield structs/enums, dynamic bitfieldsDynBitField, tag+payload re-layoutranged—RIntcompile-time value-range-constrained integers with storage compression and step supportmap_enum—EnumMapenum-to-array mapping,map_enum!const construction macrorepr—LEndian<T>/BEndian<T>/Unalign<T>endianness and alignment adaptationnbool—NBoolnegated boolean (zero value → true)varint—VarIntProtobuf-style variable-length integer encoding/decoding, allconst fnrange—BitsRangeefficient bit-range description and cross-byte copying
MSRV
Rust 1.87+
License
MIT — see repository for full license.