-
Notifications
You must be signed in to change notification settings - Fork 0
/
model.go
148 lines (128 loc) · 3.99 KB
/
model.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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
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
}
return crc
}
func (m *Model[T]) finalize(crc T) T {
if m.FinalInvert {
return ^crc
}
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.Sum()
}
// 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]
conf *instConf
}
// 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)
}
return &Inst[T]{
crc: m.initVal(),
tab: m.MakeTable(),
model: m,
impl: m.Poly.Impl(),
conf: &conf,
}
}
type InstOption func(*instConf)
type instConf struct {
appendSumSkipFinalXOR bool
}
// AppendSumSkipFinalXOR ensures that when calling
// AppendSum the final inversion or xor-ing will be skipped.
func AppendSumSkipFinalXOR() InstOption {
return func(c *instConf) {
c.appendSumSkipFinalXOR = true
}
}
// Reset sets the instance back to its initial state.
func (inst *Inst[T]) Reset() { inst.crc = 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)
}
// Sum returns the crc checksum; it does not change the current state.
func (inst *Inst[T]) Sum() T {
return inst.model.finalize(inst.crc)
}
// AppendSum appends the crc checksum to the provided slice and returns
// the resulting slice. If AppendSumXORed 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 AppendSum does not change the instance's current state.
func (inst *Inst[T]) AppendSum(in []byte) []byte {
crc := inst.crc
if !inst.conf.appendSumSkipFinalXOR {
crc = inst.model.finalize(crc)
}
return inst.impl.Append(in, crc)
}