ssdv_fec_gf_tables/
lib.rs

1//! # SSDV systematic erasure FEC: Galois field table generator procedural macros.
2//!
3//! This field contains procedural macros that are used to generate the tables
4//! for the Galois field arithmetic in the `ssdv-fec` crate.
5//!
6//! The Galois field GF(2⁸) is realized as the quotient
7//! GF(2)\[x\] / (x⁸ + x⁴ + x³ + x² + 1).
8//! Its arithmetic is implemented using a table of the exponential
9//! and a table of the logarithm, which are generated by this crate.
10//!
11//! An element a₇x⁷ + ⋯ + a₀ in GF(2⁸) is encoded as an element of `u8`, where
12//! the leading coefficient a₇ is placed in the most-significant bit and the
13//! independent term a₀ is placed in the least-significant bit.
14
15#![warn(missing_docs)]
16
17use proc_macro::TokenStream;
18use proc_macro2::Span;
19use quote::ToTokens;
20
21// Primitive polynomial p defining GF(2^8) as GF(2)[x] / (p)
22// p = x^8 + x^4 + x^3 + x^2 + 1
23const GF_POLY: u8 = 0b11101;
24
25#[derive(Clone, Eq, PartialEq, Hash)]
26struct Tables {
27    exp_table: [u8; 256],
28    log_table: [u8; 256],
29}
30
31impl Default for Tables {
32    fn default() -> Tables {
33        Tables {
34            exp_table: [0; 256],
35            log_table: [0; 256],
36        }
37    }
38}
39
40fn gf256_tables() -> Tables {
41    let mut tables = Tables::default();
42    let mut a = 1u8;
43    for power in 0..255 {
44        tables.exp_table[power] = a;
45        tables.log_table[usize::from(a)] = u8::try_from(power).unwrap();
46        a = if a & 0x80 != 0 {
47            (a << 1) ^ GF_POLY
48        } else {
49            a << 1
50        };
51    }
52    tables
53}
54
55macro_rules! impl_table {
56    ($f:ident, $table:expr, $doc:expr) => {
57        #[doc=$doc]
58        #[proc_macro]
59        pub fn $f(_: TokenStream) -> TokenStream {
60            let table = $table;
61            let mut elems = syn::punctuated::Punctuated::new();
62            for a in table.iter() {
63                let lit = syn::Lit::Int(syn::LitInt::new(&format!("{a}"), Span::call_site()));
64                elems.push_value(syn::Expr::Lit(syn::ExprLit { attrs: vec![], lit }));
65                elems.push_punct(syn::token::Comma {
66                    spans: [Span::call_site()],
67                });
68            }
69            let group = proc_macro2::Group::new(
70                proc_macro2::Delimiter::Bracket,
71                proc_macro2::TokenStream::new(),
72            );
73            let array = syn::Expr::Array(syn::ExprArray {
74                attrs: vec![],
75                bracket_token: syn::token::Bracket {
76                    span: group.delim_span(),
77                },
78                elems,
79            });
80            array.into_token_stream().into()
81        }
82    };
83}
84
85impl_table!(
86    gf256_exp_table,
87    gf256_tables().exp_table,
88    "Generates the exponential table for GF(2⁸).
89
90This macro returns an array expresion of type `[u8; 256]` that corresponds to
91the exponential table for GF(2⁸). The j-th entry of this table for j=0,...,254
92contains the element xʲ encoded as a `u8`. The 255-th entry is not used in
93practice and contains `0u8`."
94);
95
96impl_table!(
97    gf256_log_table,
98    gf256_tables().log_table,
99    "Generates the logarithm table for GF(2⁸).
100
101This macro returns an array expresion of type `[u8; 256]` that corresponds to
102the logarithm table for GF(2⁸). The j-th entry of this table for j=1,...,255
103contains the exponent k such that the element xᵏ encoded as a `u8` is equal to
104j. The 0-th entry contains `0u8`, since the logarithm of 0 is undefined."
105);