Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions gf2p16/internal/gen/gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package main

import (
"bytes"
"flag"
"fmt"
"go/format"
"io"
"io/ioutil"
"log"

"github.com/akalin/gopar/gf2"
)

const order = 1 << 16

var (
logTable [order - 1]uint16
expTable [order - 1]uint16
)

func buildTables() {
// m is the irreducible polynomial of degree 16 used to model
// GF(2^16). m was chosen to match the PAR2 spec.
const m gf2.Poly64 = 0x1100b

// g is a generator of GF(2^16).
const g = 3

x := uint16(1)
for p := 0; p < order-1; p++ {
if x == 1 && p != 0 {
panic("repeated power (1)")
} else if x != 1 && logTable[x-1] != 0 {
panic("repeated power")
}
if expTable[p] != 0 {
panic("repeated exponent")
}

logTable[x-1] = uint16(p)
expTable[p] = x
_, r := gf2.Poly64(x).Times(gf2.Poly64(g)).Div(m)
x = uint16(r)
}
}

func main() {
outFile := flag.String("out", "", "output file")
gen := flag.String("generator", "", "generator (logTable or expTable)")
flag.Parse()
if outFile == nil {
log.Fatal("-out is required")
}
if gen == nil {
log.Fatal("-generator is required")
}
buildTables()
var buf bytes.Buffer
switch *gen {
case "logTable":
genLogTable(&buf)
case "expTable":
genExpTable(&buf)
default:
log.Fatal("unknown generator:", *gen)
}
b, err := format.Source(buf.Bytes())
if err != nil {
log.Fatal(err)
}
err = ioutil.WriteFile(*outFile, b, 0600)
if err != nil {
log.Fatal(err)
}
}

func mustFprintf(w io.Writer, format string, a ...interface{}) {
_, err := fmt.Fprintf(w, format, a...)
if err != nil {
panic(err)
}
}

func genExpTable(w io.Writer) {
mustFprintf(w, `// Code generated by gf2p16/internal/gen. DO NOT EDIT.

package gf2p16

var expTable = [order - 1]T {`)

for i, u := range expTable {
if i%64 == 0 {
mustFprintf(w, "\n")
}
mustFprintf(w, "%d, ", u)
}
mustFprintf(w, "\n}\n")
}

func genLogTable(w io.Writer) {
mustFprintf(w, `// Code generated by gf2p16/internal/gen. DO NOT EDIT.

package gf2p16

var logTable = [order - 1]uint16 {`)
for i, u := range logTable {
if i%64 == 0 {
mustFprintf(w, "\n")
}
mustFprintf(w, "%d, ", u)
}
mustFprintf(w, "\n}\n")
}
8 changes: 4 additions & 4 deletions gf2p16/slice.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package gf2p16

func mulByteSliceLEGeneric(c T, in, out []byte) {
cEntry := &mulTable[c]
cEntry := c.mulTableEntry()
for i := 0; i < len(in); i += 2 {
cx := cEntry.s0[in[i]] ^ cEntry.s8[in[i+1]]
out[i] = byte(cx)
Expand All @@ -10,7 +10,7 @@ func mulByteSliceLEGeneric(c T, in, out []byte) {
}

func mulAndAddByteSliceLEGeneric(c T, in, out []byte) {
cEntry := &mulTable[c]
cEntry := c.mulTableEntry()
for i := 0; i < len(in); i += 2 {
cx := cEntry.s0[in[i]] ^ cEntry.s8[in[i+1]]
out[i] ^= byte(cx)
Expand All @@ -20,15 +20,15 @@ func mulAndAddByteSliceLEGeneric(c T, in, out []byte) {

// mulSliceGeneric sets each out[i] to c.Times(in[i]).
func mulSliceGeneric(c T, in, out []T) {
cEntry := &mulTable[c]
cEntry := c.mulTableEntry()
for i := 0; i < len(in); i++ {
out[i] = cEntry.s0[in[i]&0xff] ^ cEntry.s8[in[i]>>8]
}
}

// mulAndAddSliceGeneric adds c.Times(in[i]) to out[i], for each i.
func mulAndAddSliceGeneric(c T, in, out []T) {
cEntry := &mulTable[c]
cEntry := c.mulTableEntry()
for i := 0; i < len(in); i++ {
out[i] ^= cEntry.s0[in[i]&0xff] ^ cEntry.s8[in[i]>>8]
}
Expand Down
8 changes: 4 additions & 4 deletions gf2p16/slice_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ func mulByteSliceLE(c T, in, out []byte, useSSSE3 bool) {
}
start := 0
if useSSSE3 && len(in) >= 32 {
mulSliceSSSE3Unsafe(&mulTable64[c], in, out)
mulSliceSSSE3Unsafe(c.mulTable64Entry(), in, out)
start = len(in) - (len(in) % 32)
if start == len(in) {
return
}
}
mulByteSliceLEUnsafe(&mulTable[c], in[start:], out[start:])
mulByteSliceLEUnsafe(c.mulTableEntry(), in[start:], out[start:])
}

// MulAndAddByteSliceLE treats in and out as arrays of Ts stored in
Expand All @@ -53,13 +53,13 @@ func mulAndAddByteSliceLE(c T, in, out []byte, useSSSE3 bool) {
}
start := 0
if useSSSE3 && len(in) >= 32 {
mulAndAddSliceSSSE3Unsafe(&mulTable64[c], in, out)
mulAndAddSliceSSSE3Unsafe(c.mulTable64Entry(), in, out)
start = len(in) - (len(in) % 32)
if start == len(in) {
return
}
}
mulAndAddByteSliceLEUnsafe(&mulTable[c], in[start:], out[start:])
mulAndAddByteSliceLEUnsafe(c.mulTableEntry(), in[start:], out[start:])
}

func castTToByteSlice(ts []T) []byte {
Expand Down
12 changes: 6 additions & 6 deletions gf2p16/slice_amd64_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ func TestMulAltMapSSSE3Unsafe(t *testing.T) {

outLow := [2][16]byte{filler, filler}
outHigh := [2][16]byte{filler, filler}
mulAltMapSSSE3Unsafe(&mulTable64[c], &inLow, &inHigh, &outLow[0], &outHigh[0])
mulAltMapSSSE3Unsafe(c.mulTable64Entry(), &inLow, &inHigh, &outLow[0], &outHigh[0])

require.Equal(t, expectedOutLow, outLow)
require.Equal(t, expectedOutHigh, outHigh)
Expand All @@ -221,7 +221,7 @@ func TestMulSliceAltMapSSSE3Unsafe(t *testing.T) {
mulByteSliceLEGeneric(c, inStandard, expectedOut[:len(in)])
standardToAltMapSliceSSSE3Unsafe(expectedOut, expectedOut)

mulSliceAltMapSSSE3Unsafe(&mulTable64[c], in, out)
mulSliceAltMapSSSE3Unsafe(c.mulTable64Entry(), in, out)

require.Equal(t, expectedOut, out)
}
Expand All @@ -246,7 +246,7 @@ func TestMulSSSE3Unsafe(t *testing.T) {

out0 := [2][16]byte{filler, filler}
out1 := [2][16]byte{filler, filler}
mulSSSE3Unsafe(&mulTable64[c], &in0, &in1, &out0[0], &out1[0])
mulSSSE3Unsafe(c.mulTable64Entry(), &in0, &in1, &out0[0], &out1[0])

require.Equal(t, expectedOut0, out0)
require.Equal(t, expectedOut1, out1)
Expand All @@ -267,7 +267,7 @@ func TestMulSliceSSSE3Unsafe(t *testing.T) {

mulByteSliceLEGeneric(c, in, expectedOut[:len(in)])

mulSliceSSSE3Unsafe(&mulTable64[c], in, out)
mulSliceSSSE3Unsafe(c.mulTable64Entry(), in, out)

require.Equal(t, expectedOut, out)
}
Expand All @@ -292,7 +292,7 @@ func TestMulAndAddSSSE3Unsafe(t *testing.T) {

out0 := [2][16]byte{filler, filler}
out1 := out0
mulAndAddSSSE3Unsafe(&mulTable64[c], &in0, &in1, &out0[0], &out1[0])
mulAndAddSSSE3Unsafe(c.mulTable64Entry(), &in0, &in1, &out0[0], &out1[0])

require.Equal(t, expectedOut0, out0)
require.Equal(t, expectedOut1, out1)
Expand All @@ -313,7 +313,7 @@ func TestMulAndAddSliceSSSE3Unsafe(t *testing.T) {

mulAndAddByteSliceLEGeneric(c, in, expectedOut[:len(in)])

mulAndAddSliceSSSE3Unsafe(&mulTable64[c], in, out)
mulAndAddSliceSSSE3Unsafe(c.mulTable64Entry(), in, out)

require.Equal(t, expectedOut, out)
}
2 changes: 2 additions & 0 deletions gf2p16/slice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ func benchMulByteSliceLE(b *testing.B, byteCount int) {
}

func BenchmarkMulByteSliceLE(b *testing.B) {
LoadCaches()
b.ResetTimer()
runMulBenchmark(b, benchMulByteSliceLE)
}

Expand Down
81 changes: 41 additions & 40 deletions gf2p16/t.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,41 @@
package gf2p16

import "github.com/akalin/gopar/gf2"
import (
"sync"
)

//go:generate go run ./internal/gen -generator logTable -out t_logtable_gen.go
//go:generate go run ./internal/gen -generator expTable -out t_exptable_gen.go

// T is an element of GF(2^16).
type T uint16

var mulTableCache [1 << 16]*mulTableEntry
var mulTableCacheMutex sync.RWMutex

func (t T) mulTableEntry() *mulTableEntry {
if cachesLoaded {
return mulTableCache[t]
}
mulTableCacheMutex.RLock()
entry := mulTableCache[t]
if entry != nil {
mulTableCacheMutex.RUnlock()
return entry
}
mulTableCacheMutex.RUnlock()
mulTableCacheMutex.Lock()
defer mulTableCacheMutex.Unlock()
if mulTableCache[t] == nil {
mulTableCache[t] = &mulTableEntry{}
for j := 0; j < 256; j++ {
mulTableCache[t].s0[j] = t.Times(T(j))
mulTableCache[t].s8[j] = t.Times(T(j << 8))
}
}
return mulTableCache[t]
}

// Plus returns the sum of t and u as elements of GF(2^16), which is
// just the bitwise xor of the two.
func (t T) Plus(u T) T {
Expand All @@ -19,52 +50,22 @@ func (t T) Minus(u T) T {

const order = 1 << 16

var logTable [order - 1]uint16
var expTable [order - 1]T

type mulTableEntry struct {
s0, s8 [1 << 8]T
}

var mulTable [1 << 16]mulTableEntry
var cachesLoaded bool

func init() {
// TODO: Generate tables at compile time.

// m is the irreducible polynomial of degree 16 used to model
// GF(2^16). m was chosen to match the PAR2 spec.
const m gf2.Poly64 = 0x1100b

// g is a generator of GF(2^16).
const g T = 3

x := T(1)
for p := 0; p < order-1; p++ {
if x == 1 && p != 0 {
panic("repeated power (1)")
} else if x != 1 && logTable[x-1] != 0 {
panic("repeated power")
}
if expTable[p] != 0 {
panic("repeated exponent")
}

logTable[x-1] = uint16(p)
expTable[p] = x
_, r := gf2.Poly64(x).Times(gf2.Poly64(g)).Div(m)
x = T(r)
// LoadCaches preloads mulTableCache and mulTable64Cache
func LoadCaches() {
if cachesLoaded {
return
}

// Since we've filled in logTable and expTable, we can use
// T.Times below.
for i := 0; i < len(mulTable); i++ {
for j := 0; j < len(mulTable[i].s0); j++ {
mulTable[i].s0[j] = T(i).Times(T(j))
mulTable[i].s8[j] = T(i).Times(T(j << 8))
}
for i := range mulTableCache {
T(i).mulTableEntry()
}

platformInit()
platformPreloadCaches()
cachesLoaded = true
}

// Times returns the product of t and u as elements of GF(2^16).
Expand Down
Loading