-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmodel.go
More file actions
229 lines (197 loc) · 5.74 KB
/
Copy pathmodel.go
File metadata and controls
229 lines (197 loc) · 5.74 KB
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
package crcutil
// Model defines how a concrete CRC calculation should be
// performed, based on a polynomial, and parameters.
// To calculate a crc value, an instance needs to be created
// using New, followed by calls to methods Write or Update,
// and if appropriate, Reset.
//
// A Model may also be used to create a [hash.Hash] using
// packages [github.com/knieriem/hash/crc8] or
// packages [github.com/knieriem/hash/crc16].
type Model[T Word] struct {
Poly *Poly[T]
// Table may be assigned an alternative table to be used for
// calculation. On default, MakeTable derives a table from Poly.
Table []T
// Initial is the start value to be used for crc calculation.
// If left empty, zero will be used.
Initial T
// InitialInvert may be set to true, if the initial value shall
// be inverted. If Initial is left empty, setting InitialInvert
// has the effect of an initial value with all bits set.
InitialInvert bool
// FinalInvert may be set to true, if a crc value shall
// be inverted before being returned as checksum.
// Alternatively, FinalXOR could be set to a value with all bits set
// for the same effect.
FinalInvert bool
// FinalXOR may contain a value to be XORed into the crc value
// before returning the checksum. In most cases leaving FinalXOR
// empty, and setting FinalInvert instead will suffice, as cases
// where the final step isn't either a no-op or an inversion,
// but an XOR-operation with a specific value seem rare.
FinalXOR T
}
// MakeTable returns the lookup table stored in the Table field.
// If this field is nil, a table generated by Poly.MakeTable() will
// be returned.
func (m *Model[T]) MakeTable() []T {
if m.Table != nil {
return m.Table
}
return m.Poly.MakeTable()
}
func (m *Model[T]) initVal() T {
crc := m.Initial
if m.InitialInvert {
crc = ^crc & m.Poly.mask()
}
return crc
}
func (m *Model[T]) finalize(crc T) T {
if m.FinalInvert {
return ^crc & m.Poly.mask()
}
if x := m.FinalXOR; x != 0 {
return crc ^ x
}
return crc
}
// Checksum returns the CRC checksum calculated
// over the bytes in the data slice.
func (m *Model[T]) Checksum(data []byte) T {
inst := m.New()
inst.Update(data)
return inst.Checksum()
}
// Residue calculates the constant value that will remain in the register
// after a checksum has been calculated over data followed by its checksum.
// For models without final xor values the residue is zero.
func (m *Model[T]) Residue() T {
return UpdateBitwise(m.Poly, 0, m.finalize(0), m.Poly.Width)
}
// Inst is an instance of a Model;
// it contains the current state of the crc calculation.
// current state of
type Inst[T Word] struct {
crc T
tab []T
model *Model[T]
impl Impl[T]
residue T
conf *instConf
adjustCRC func(crc T) T
}
// NewInst returns a new instance of the Model.
func (m *Model[T]) New(opts ...InstOption) *Inst[T] {
var conf instConf
for _, o := range opts {
o(&conf)
}
tab := m.Table
if tab == nil {
tab = m.Poly.MakeTable(conf.tabOpts()...)
}
adjustCRC := func(crc T) T {
return crc
}
if conf.compSwapInputNibbles {
adjustCRC = func(crc T) T {
return swapNibbles(crc)
}
}
return &Inst[T]{
crc: adjustCRC(m.initVal()),
tab: tab,
model: m,
impl: m.Poly.Impl(),
residue: m.Residue(),
conf: &conf,
adjustCRC: adjustCRC,
}
}
type InstOption func(*instConf)
type instConf struct {
appendSumSkipFinalXOR bool
compSwapInputNibbles bool
}
func (c *instConf) tabOpts() []TableOption {
var opts []TableOption
if c.compSwapInputNibbles {
opts = append(opts, withSwappedInputNibbles())
}
return opts
}
// SumSkipFinalXOR ensures that when calling Sum
// the final inversion or xor-ing will be skipped.
func SumSkipFinalXOR() InstOption {
return func(c *instConf) {
c.appendSumSkipFinalXOR = true
}
}
// WithSwappedInputNibbles ensures that bytes are
// processed with upper and lower nibbles swapped.
// This option is useful for 4-bit CRCs, when an
// LSBit-first implementation is used, and the input
// bytes shall be processed with high nibble first.
func WithSwappedInputNibbles() InstOption {
return func(c *instConf) {
c.compSwapInputNibbles = true
}
}
// Reset sets the instance back to its initial state.
func (inst *Inst[T]) Reset() {
inst.crc = inst.adjustCRC(inst.model.initVal())
}
// Write implements an io.Writer to add bytes to the crc.
func (inst *Inst[T]) Write(p []byte) (n int, err error) {
inst.crc = inst.impl.Update(inst.crc, inst.tab, p)
return len(p), nil
}
// Update adds bytes in p to the crc
func (inst *Inst[T]) Update(p []byte) {
inst.crc = inst.impl.Update(inst.crc, inst.tab, p)
}
// Checksum returns the crc checksum; it does not change the current state.
func (inst *Inst[T]) Checksum() T {
crc := inst.adjustCRC(inst.crc)
return inst.model.finalize(crc)
}
// Valid returns true if the current register value matches the
// model's residue constant.
func (inst *Inst[T]) Valid() bool {
return inst.crc == inst.residue
}
// Sum appends the crc checksum to the provided slice and returns
// the resulting slice. If SumXORed is set, it will apply,
// if specified, the final inversion or final xor value before appending the checksum,
// otherwise this step will be skipped.
// Calling Sum does not change the instance's current state.
func (inst *Inst[T]) Sum(in []byte) []byte {
crc := inst.adjustCRC(inst.crc)
if !inst.conf.appendSumSkipFinalXOR {
crc = inst.model.finalize(crc)
}
return inst.impl.Append(in, crc)
}
// Table exposes the lookup table used by the instance.
func (inst *Inst[T]) Table() []T {
return inst.tab
}
// BlockSize returns
func (inst *Inst[T]) BlockSize() int {
return 1
}
// Size returns
func (inst *Inst[T]) Size() int {
var x T
switch any(x).(type) {
case uint8:
return 1
case uint16:
return 2
case uint32:
return 4
}
panic("type not supported")
}