Skip to content

Commit ee26f30

Browse files
committed
Fix calculation for CRC algorithms with widths of less than 8 bits
1 parent d390ecf commit ee26f30

File tree

2 files changed

+103
-9
lines changed

2 files changed

+103
-9
lines changed

crc.go

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,26 +69,33 @@ func reflect(in uint64, count uint) uint64 {
6969
// It is relatively slow for large amounts of data, but does not require
7070
// any preparation steps. As a result, it might be faster in some cases
7171
// then building a table required for faster calculation.
72+
//
73+
// Note: this implementation follows section 8 ("A Straightforward CRC Implementation")
74+
// of Ross N. Williams paper as even though final/sample implementation of this algorithm
75+
// provided near the end of that paper (and followed by most other implementations)
76+
// is a bit faster, it does not work for polynomials shorter then 8 bits. And if you need
77+
// speed, you shoud probably be using table based implementation anyway.
7278
func CalculateCRC(crcParams *Parameters, data []byte) uint64 {
7379

7480
curValue := crcParams.Init
75-
topbit := uint64(1) << (crcParams.Width - 1)
76-
mask := (topbit << 1) - 1
81+
topBit := uint64(1) << (crcParams.Width - 1)
82+
mask := (topBit << 1) - 1
7783

7884
for i := 0; i < len(data); i++ {
7985
var curByte = uint64(data[i]) & 0x00FF
8086
if crcParams.ReflectIn {
8187
curByte = reflect(curByte, 8)
8288
}
83-
curValue ^= (curByte << (crcParams.Width - 8))
84-
for j := 0; j < 8; j++ {
85-
if (curValue & topbit) != 0 {
86-
curValue = (curValue << 1) ^ crcParams.Polynomial
87-
} else {
88-
curValue = (curValue << 1)
89+
for j := uint64(0x0080); j != 0; j >>= 1 {
90+
bit := curValue & topBit
91+
curValue <<= 1
92+
if (curByte & j) != 0 {
93+
bit = bit ^ topBit
94+
}
95+
if bit != 0 {
96+
curValue = curValue ^ crcParams.Polynomial
8997
}
9098
}
91-
9299
}
93100
if crcParams.ReflectOut {
94101
curValue = reflect(curValue, crcParams.Width)
@@ -153,6 +160,10 @@ func (h *Hash) Update(p []byte) {
153160
for _, v := range p {
154161
h.curValue = h.crctable[(byte(h.curValue)^v)&0xFF] ^ (h.curValue >> 8)
155162
}
163+
} else if h.crcParams.Width < 8 {
164+
for _, v := range p {
165+
h.curValue = h.crctable[((((byte)(h.curValue<<(8-h.crcParams.Width)))^v)&0xFF)] ^ (h.curValue << 8)
166+
}
156167
} else {
157168
for _, v := range p {
158169
h.curValue = h.crctable[((byte(h.curValue>>(h.crcParams.Width-8))^v)&0xFF)] ^ (h.curValue << 8)
@@ -199,6 +210,12 @@ func NewHash(crcParams *Parameters) *Hash {
199210
return ret
200211
}
201212

213+
// CRC8 is a convenience method to spare end users from explicit type conversion every time this package is used.
214+
// Underneath, it just calls CRC() method.
215+
func (h *Hash) CRC8() uint8 {
216+
return uint8(h.CRC())
217+
}
218+
202219
// CRC16 is a convenience method to spare end users from explicit type conversion every time this package is used.
203220
// Underneath, it just calls CRC() method.
204221
func (h *Hash) CRC16() uint16 {

crc_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,25 @@ func TestCRCAlgorithms(t *testing.T) {
3737
if calculated != crc {
3838
t.Errorf("Incorrect CRC 0x%04x calculated for %s (should be 0x%04x)", calculated, data, crc)
3939
}
40+
41+
// Test helper methods return correct values as well
42+
if crcParams.Width == 8 {
43+
crc8 := tableDriven.CRC8()
44+
if crc8 != uint8(crc&0x00FF) {
45+
t.Errorf("Incorrect CRC8 0x%02x retrived %s (should be 0x%02x)", crc8, data, crc)
46+
}
47+
} else if crcParams.Width == 16 {
48+
crc16 := tableDriven.CRC16()
49+
if crc16 != uint16(crc&0x00FFFF) {
50+
t.Errorf("Incorrect CRC16 0x%04x retrived %s (should be 0x%04x)", crc16, data, crc)
51+
}
52+
} else if crcParams.Width == 32 {
53+
crc32 := tableDriven.CRC32()
54+
if crc32 != uint32(crc&0x00FFFFFFFF) {
55+
t.Errorf("Incorrect CRC8 0x%08x retrived %s (should be 0x%08x)", crc32, data, crc)
56+
}
57+
}
58+
4059
}
4160

4261
doTest(X25, "123456789", 0x906E)
@@ -83,6 +102,64 @@ func TestCRCAlgorithms(t *testing.T) {
83102
doTest(CRC64ECMA, "12345678901234567890", 0x0DA1B82EF5085A4A)
84103
doTest(CRC64ECMA, "Introduction on CRC calculations", 0xCF8C40119AE90DCB)
85104
doTest(CRC64ECMA, "Whenever digital data is stored or interfaced, data corruption might occur. Since the beginning of computer science, people have been thinking of ways to deal with this type of problem. For serial data they came up with the solution to attach a parity bit to each sent byte. This simple detection mechanism works if an odd number of bits in a byte changes, but an even number of false bits in one byte will not be detected by the parity check. To overcome this problem people have searched for mathematical sound mechanisms to detect multiple false bits.", 0x31610F76CFB272A5)
105+
106+
// More tests for various CRC algorithms (copied from java version)
107+
longText := "Whenever digital data is stored or interfaced, data corruption might occur. Since the beginning of computer science, people have been thinking of ways to deal with this type of problem. For serial data they came up with the solution to attach a parity bit to each sent byte. This simple detection mechanism works if an odd number of bits in a byte changes, but an even number of false bits in one byte will not be detected by the parity check. To overcome this problem people have searched for mathematical sound mechanisms to detect multiple false bits."
108+
109+
testArrayData := make([]byte, 256)
110+
for i := 0; i < len(testArrayData); i++ {
111+
testArrayData[i] = byte(i & 0x0FF)
112+
}
113+
testArray := string(testArrayData)
114+
if len(testArray) != 256 {
115+
t.Fatalf("Logic error")
116+
}
117+
118+
// merely a helper to make copying Spock test sets from java version of this library a bit easier
119+
doTestWithParameters := func(width uint, polynomial uint64, init uint64, reflectIn bool, reflectOut bool, finalXor uint64, crc uint64, testData string) {
120+
doTest(&Parameters{Width: width, Polynomial: polynomial, Init: init, ReflectIn: reflectIn, ReflectOut: reflectOut, FinalXor: finalXor}, testData, crc)
121+
}
122+
123+
doTestWithParameters(3, 0x03, 0x00, false, false, 0x7, 0x04, "123456789") // CRC-3/GSM
124+
doTestWithParameters(3, 0x03, 0x00, false, false, 0x7, 0x06, longText)
125+
doTestWithParameters(3, 0x03, 0x00, false, false, 0x7, 0x02, testArray)
126+
doTestWithParameters(3, 0x03, 0x07, true, true, 0x0, 0x06, "123456789") // CRC-3/ROHC
127+
doTestWithParameters(3, 0x03, 0x07, true, true, 0x0, 0x03, longText)
128+
doTestWithParameters(4, 0x03, 0x00, true, true, 0x0, 0x07, "123456789") // CRC-4/ITU
129+
doTestWithParameters(4, 0x03, 0x0f, false, false, 0xf, 0x0b, "123456789") // CRC-4/INTERLAKEN
130+
doTestWithParameters(4, 0x03, 0x0f, false, false, 0xf, 0x01, longText) // CRC-4/INTERLAKEN
131+
doTestWithParameters(4, 0x03, 0x0f, false, false, 0xf, 0x07, testArray) // CRC-4/INTERLAKEN
132+
doTestWithParameters(5, 0x09, 0x09, false, false, 0x0, 0x00, "123456789") // CRC-5/EPC
133+
doTestWithParameters(5, 0x15, 0x00, true, true, 0x0, 0x07, "123456789") // CRC-5/ITU
134+
doTestWithParameters(6, 0x27, 0x3f, false, false, 0x0, 0x0d, "123456789") // CRC-6/CDMA2000-A
135+
doTestWithParameters(6, 0x07, 0x3f, false, false, 0x0, 0x3b, "123456789") // CRC-6/CDMA2000-B
136+
doTestWithParameters(6, 0x07, 0x3f, false, false, 0x0, 0x24, testArray) // CRC-6/CDMA2000-B
137+
doTestWithParameters(7, 0x09, 0x00, false, false, 0x0, 0x75, "123456789") // CRC-7
138+
doTestWithParameters(7, 0x09, 0x00, false, false, 0x0, 0x78, testArray) // CRC-7
139+
doTestWithParameters(7, 0x4f, 0x7f, true, true, 0x0, 0x53, "123456789") // CRC-7/ROHC
140+
141+
doTestWithParameters(8, 0x07, 0x00, false, false, 0x00, 0xf4, "123456789") // CRC-8
142+
doTestWithParameters(8, 0xa7, 0x00, true, true, 0x00, 0x26, "123456789") // CRC-8/BLUETOOTH
143+
doTestWithParameters(8, 0x07, 0x00, false, false, 0x55, 0xa1, "123456789") // CRC-8/ITU
144+
doTestWithParameters(8, 0x9b, 0x00, true, true, 0x00, 0x25, "123456789") // CRC-8/WCDMA
145+
doTestWithParameters(8, 0x31, 0x00, true, true, 0x00, 0xa1, "123456789") // CRC-8/MAXIM
146+
147+
doTestWithParameters(10, 0x233, 0x000, false, false, 0x000, 0x199, "123456789") // CRC-10
148+
149+
doTestWithParameters(12, 0xd31, 0x00, false, false, 0xfff, 0x0b34, "123456789") // CRC-12/GSM
150+
doTestWithParameters(12, 0x80f, 0x00, false, true, 0x00, 0x0daf, "123456789") // CRC-12/UMTS
151+
doTestWithParameters(13, 0x1cf5, 0x00, false, false, 0x00, 0x04fa, "123456789") // CRC-13/BBC
152+
doTestWithParameters(14, 0x0805, 0x00, true, true, 0x00, 0x082d, "123456789") // CRC-14/DARC
153+
doTestWithParameters(14, 0x202d, 0x00, false, false, 0x3fff, 0x30ae, "123456789") // CRC-14/GSM
154+
155+
doTestWithParameters(15, 0x4599, 0x00, false, false, 0x00, 0x059e, "123456789") // CRC-15
156+
doTestWithParameters(15, 0x4599, 0x00, false, false, 0x00, 0x2857, longText)
157+
doTestWithParameters(15, 0x6815, 0x00, false, false, 0x0001, 0x2566, "123456789") // CRC-15/MPT1327
158+
159+
doTestWithParameters(21, 0x102899, 0x000000, false, false, 0x000000, 0x0ed841, "123456789") // CRC-21/CAN-FD
160+
doTestWithParameters(24, 0x864cfb, 0xb704ce, false, false, 0x000000, 0x21cf02, "123456789") // CRC-24
161+
doTestWithParameters(24, 0x5d6dcb, 0xfedcba, false, false, 0x000000, 0x7979bd, "123456789") // CRC-24/FLEXRAY-A
162+
doTestWithParameters(31, 0x04c11db7, 0x7fffffff, false, false, 0x7fffffff, 0x0ce9e46c, "123456789") // CRC-31/PHILIPS
86163
}
87164

88165
func TestSizeMethods(t *testing.T) {

0 commit comments

Comments
 (0)