-
Notifications
You must be signed in to change notification settings - Fork 0
/
table.go
125 lines (107 loc) · 2.58 KB
/
table.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package crcutil
import (
"fmt"
"sync"
"github.com/knieriem/crcutil/internal/impl"
)
// MakeTable creates a lookup table for the specified polynomial.
// The size of the table returned equals the length of
// the value range determined by the number of data bits.
func (poly *Poly[T]) MakeTable(opts ...TableOption) []T {
var conf tableConf
conf.dataWidth = 8
for _, o := range opts {
o(&conf)
}
k := tableCacheKey(poly, &conf)
tableCacheMu.RLock()
t := tableCache[k]
tableCacheMu.RUnlock()
if t != nil {
return t.([]T)
}
N := 1 << conf.dataWidth
updateBitwise := BitwiseUpdateFn[T, T](poly)
tab := make([]T, N)
for i := range tab {
tab[i] = updateBitwise(poly, T(conf.initial), T(i), conf.dataWidth)
if conf.reverseBits {
tab[i] = reverseBits(tab[i], poly.Width)
}
}
tableCacheMu.Lock()
defer tableCacheMu.Unlock()
tableCache[k] = tab
return tab
}
type TableOption func(*tableConf)
type tableConf struct {
initial uint32
dataWidth int
reverseBits bool
}
// WithDataWidth sets the data width to n bits,
// which will result in a table of 2^n entries for n-bit-wise processing.
// The default width is 8 bits for byte-wise processing.
func WithDataWidth(n int) TableOption {
return func(c *tableConf) {
c.dataWidth = n
}
}
// WithInitialValue ensures that when a table is created
// the specified initial value will be applied to the
// calculation of each table entry.
// In cases where a table later is used manually,
// like when a CRC is calulated over some bits only,
// this saves one XOR operation.
func WithInitialValue(initial uint32) TableOption {
return func(c *tableConf) {
c.initial = initial
}
}
// WithReversedBits table option mirrors the bits of each table entry.
func WithReversedBits() TableOption {
return func(c *tableConf) {
c.reverseBits = true
}
}
var tableCacheMu sync.RWMutex
var tableCache = map[string]any{}
func tableCacheKey[T Word](p *Poly[T], c *tableConf) string {
rep := ""
if p.Reversed {
rep += ".r"
}
if p.Reciprocal {
rep += ".R"
}
tabMod := ""
if c.reverseBits {
tabMod = ".r"
}
return fmt.Sprintf("%x%s-%x.%d%s",
p.Word, rep,
c.initial, c.dataWidth, tabMod)
}
type Impl[T Word] interface {
Update(crc T, tab []T, p []byte) T
Append(b []byte, crc T) []byte
}
func (p *Poly[T]) Impl() Impl[T] {
var x T
switch any(x).(type) {
case uint8:
return impl.Impl8[T]{}
case uint16:
if p.LSBitFirst() {
return impl.Impl16LSBitFirst[T]{}
}
return impl.Impl16[T]{}
case uint32:
if p.LSBitFirst() {
return impl.Impl32LSBitFirst[T]{}
}
return impl.Impl32[T]{}
}
panic("type not supported")
}