3 releases

0.1.2 Oct 4, 2024
0.1.1 Sep 10, 2024
0.1.0 Sep 7, 2024

#1047 in Procedural macros

MIT/Apache

10KB
116 lines

Constable

Generate lookup tables at compile time using attribute macros on const functions.

Example

#[constable::lookup]
const fn foo(packed: u8) -> bool {
   // divide an 8-bit integer into 4 2-bit values
   // return true if the xor of the first 2 is equal
   // to the xor of the second 2
   let u0 = packed & 0b11;
   let u1 = (packed >> 2) & 0b11;
   let u2 = (packed >> 4) & 0b11;
   let u3 = (packed >> 6) & 0b11;
   (u0 ^ u1) == (u2 ^ u3)
}

fn main() {
    let x = foo(5);
}

The code gets expanded to an inner function definition and building a const lookup table. (note the example below is simplified and not valid const code)

const fn foo(value: u8) -> bool {
  const fn foo_orig(value: u8) -> { .. }

  const LOOKUP_TABLE: [bool; 256] = const {
    let mut table = [false; 256];
    for i in 0..256 {
      table[i] = foo_orig(i);
    }
  };

  LOOKUP_TABLE[value]
}

Also in cases where the return value is a bool, the table is bit-packed so that the lookup table takes up less memory (and therefore more cache efficient).

$ cargo bench --bench bench
    Finished `bench` profile [optimized] target(s) in 0.03s
     Running benches/bench.rs (target/release/deps/bench-ad4b147a8cee6c74)
Timer precision: 12 ns
bench            fastest       │ slowest       │ median        │ mean          │ samples │ iters
├─ computed      1.037 µs      │ 10.51 µs      │ 1.202 µs      │ 1.656 µs      │ 100     │ 200
╰─ lookup_table  672.7 ns      │ 10.1 µs       │ 772.7 ns      │ 1.191 µs      │ 100     │ 200

Note that the fastest for computed and lookup_table are different units.

Dependencies

~220–660KB
~16K SLoC