research/erasure_code/share.cpp

52 lines
1.8 KiB
C++

#include "share.h"
// returns a * (x+1) in the Galois field
// (since (x+1) is a primitive root)
static constexpr std::uint8_t tpl(std::uint8_t a) {
return a ^ (a<<1) // a * (x+1)
^ ((a & (1<<7)) != 0
? // would overflow (have an x^8 term); reduce by the AES polynomial,
// x^8 + x^4 + x^3 + x + 1
0b00011011u
: 0
);
}
// constexpr functions to compute exp/log
// these are not intended to be fast, but they must be constexpr to populate a
// table at compile time
static constexpr std::uint8_t gexp(unsigned k) {
return k > 0 ? tpl(gexp(k-1)) : 1;
}
static constexpr std::uint8_t glog(unsigned k, unsigned i = 0, unsigned v = 1) {
return k == v ? i : glog(k, i+1, tpl(v));
}
// insane hack (courtesy of Xeo on stackoverflow): gen_seq<N> expands to a
// struct that derives from seq<0, 1, ..., N-1>
template<unsigned... I> struct seq{};
template<unsigned N, unsigned... I>
struct gen_seq : gen_seq<N-1, N-1, I...>{};
template<unsigned... I>
struct gen_seq<0, I...> : seq<I...>{};
// produce the actual tables in array form...
template<unsigned... I>
constexpr std::array<Galois, 255> exptbl(seq<I...>) {
return { { Galois(gexp(I))... } };
}
template<unsigned... I>
constexpr std::array<std::uint8_t, 256> logtbl(seq<I...>) {
// manually populate entry zero, for two reasons:
// - it makes glog simpler
// - it avoids clang++'s default template instantiation depth limit of 256
return { { 0, glog(I+1)... } };
}
// and initialize the static variables
const std::array<Galois, 255> Galois::exptable = exptbl(gen_seq<255>{});
const std::array<std::uint8_t, 256> Galois::logtable = logtbl(gen_seq<255>{});
// by populating everything at compile-time, we avoid a static initialization
// step and any possible associated static initialization "races"