├── .travis.yml ├── LICENSE ├── README.md ├── crc16.go ├── crc16_test.go └── hash.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](https://godoc.org/github.com/howeyc/crc16?status.svg)](https://godoc.org/github.com/howeyc/crc16) [![Build Status](https://secure.travis-ci.org/howeyc/crc16.png?branch=master)](http://travis-ci.org/howeyc/crc16) 2 | 3 | # CRC16 4 | A Go package implementing the 16-bit Cyclic Redundancy Check, or CRC-16, checksum. 5 | 6 | ## Usage 7 | To generate the hash of a byte slice, use the [`crc16.Checksum()`](https://godoc.org/github.com/howeyc/crc16#Checksum) function: 8 | ```golang 9 | import "github.com/howeyc/crc16" 10 | 11 | data := byte("test") 12 | checksum := crc16.Checksum(data, crc16.IBMTable) 13 | ``` 14 | 15 | The package provides [the following](https://godoc.org/github.com/howeyc/crc16#pkg-variables) hashing tables. For each of these tables, a shorthand can be used. 16 | ```golang 17 | // This is the same as crc16.Checksum(data, crc16.IBMTable) 18 | checksum := crc16.ChecksumIBM(data) 19 | ``` 20 | 21 | Using the [hash.Hash](https://godoc.org/hash#Hash) interface also works. 22 | ```go 23 | h := crc16.New(crc16.IBMTable) 24 | data := byte("test") 25 | data2 := byte("data") 26 | h.Write(data) 27 | h.Write(data2) 28 | checksum := h.Sum(nil) 29 | ``` 30 | 31 | ## Changelog 32 | * 2017.03.27 - Added MBus checksum 33 | * 2017.05.27 - Added checksum function without XOR 34 | * 2017.12.08 - Implement encoding.BinaryMarshaler and encoding.BinaryUnmarshaler to allow saving and recreating their internal state. 35 | -------------------------------------------------------------------------------- /crc16.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package crc16 6 | 7 | // Predefined polynomials. 8 | const ( 9 | // IBM is used by Bisync, Modbus, USB, ANSI X3.28, SIA DC-07, ... 10 | IBM = 0xA001 11 | 12 | // CCITT is used by X.25, V.41, HDLC FCS, XMODEM, Bluetooth, PACTOR, SD, ... 13 | // CCITT forward is 0x8408. Reverse is 0x1021. 14 | CCITT = 0x8408 15 | CCITTFalse = 0x1021 16 | 17 | // SCSI is used by SCSI 18 | SCSI = 0xEDD1 19 | 20 | // MBUS is used by Meter-Bus, DNP, ... 21 | MBUS = 0x3D65 22 | ) 23 | 24 | // Table is a 256-word table representing the polynomial for efficient processing. 25 | type Table struct { 26 | entries [256]uint16 27 | reversed bool 28 | noXOR bool 29 | } 30 | 31 | // IBMTable is the table for the IBM polynomial. 32 | var IBMTable = makeTable(IBM) 33 | 34 | // CCITTTable is the table for the CCITT polynomial. 35 | var CCITTTable = makeTable(CCITT) 36 | 37 | // CCITTFalseTable is the table for CCITT-FALSE. 38 | var CCITTFalseTable = makeBitsReversedTable(CCITTFalse) 39 | 40 | // SCSITable is the table for the SCSI polynomial. 41 | var SCSITable = makeTable(SCSI) 42 | 43 | // MBusTable is the tabe used for Meter-Bus polynomial. 44 | var MBusTable = makeBitsReversedTable(MBUS) 45 | 46 | // MakeTable returns the Table constructed from the specified polynomial. 47 | func MakeTable(poly uint16) *Table { 48 | return makeTable(poly) 49 | } 50 | 51 | // MakeBitsReversedTable returns the Table constructed from the specified polynomial. 52 | func MakeBitsReversedTable(poly uint16) *Table { 53 | return makeBitsReversedTable(poly) 54 | } 55 | 56 | // MakeTableNoXOR returns the Table constructed from the specified polynomial. 57 | // Updates happen without XOR in and XOR out. 58 | func MakeTableNoXOR(poly uint16) *Table { 59 | tab := makeTable(poly) 60 | tab.noXOR = true 61 | return tab 62 | } 63 | 64 | // makeTable returns the Table constructed from the specified polynomial. 65 | func makeBitsReversedTable(poly uint16) *Table { 66 | t := &Table{ 67 | reversed: true, 68 | } 69 | width := uint16(16) 70 | for i := uint16(0); i < 256; i++ { 71 | crc := i << (width - 8) 72 | for j := 0; j < 8; j++ { 73 | if crc&(1<<(width-1)) != 0 { 74 | crc = (crc << 1) ^ poly 75 | } else { 76 | crc <<= 1 77 | } 78 | } 79 | t.entries[i] = crc 80 | } 81 | return t 82 | } 83 | 84 | func makeTable(poly uint16) *Table { 85 | t := &Table{ 86 | reversed: false, 87 | } 88 | for i := 0; i < 256; i++ { 89 | crc := uint16(i) 90 | for j := 0; j < 8; j++ { 91 | if crc&1 == 1 { 92 | crc = (crc >> 1) ^ poly 93 | } else { 94 | crc >>= 1 95 | } 96 | } 97 | t.entries[i] = crc 98 | } 99 | return t 100 | } 101 | 102 | func updateBitsReversed(crc uint16, tab *Table, p []byte) uint16 { 103 | for _, v := range p { 104 | crc = tab.entries[byte(crc>>8)^v] ^ (crc << 8) 105 | } 106 | return crc 107 | } 108 | 109 | func update(crc uint16, tab *Table, p []byte) uint16 { 110 | crc = ^crc 111 | 112 | for _, v := range p { 113 | crc = tab.entries[byte(crc)^v] ^ (crc >> 8) 114 | } 115 | 116 | return ^crc 117 | } 118 | 119 | func updateNoXOR(crc uint16, tab *Table, p []byte) uint16 { 120 | for _, v := range p { 121 | crc = tab.entries[byte(crc)^v] ^ (crc >> 8) 122 | } 123 | 124 | return crc 125 | } 126 | 127 | // Update returns the result of adding the bytes in p to the crc. 128 | func Update(crc uint16, tab *Table, p []byte) uint16 { 129 | if tab.reversed { 130 | return updateBitsReversed(crc, tab, p) 131 | } else if tab.noXOR { 132 | return updateNoXOR(crc, tab, p) 133 | } else { 134 | return update(crc, tab, p) 135 | } 136 | } 137 | 138 | // Checksum returns the CRC-16 checksum of data 139 | // using the polynomial represented by the Table. 140 | func Checksum(data []byte, tab *Table) uint16 { return Update(0, tab, data) } 141 | 142 | // ChecksumIBM returns the CRC-16 checksum of data 143 | // using the IBM polynomial. 144 | func ChecksumIBM(data []byte) uint16 { return Update(0, IBMTable, data) } 145 | 146 | // ChecksumCCITTFalse returns the CRC-16 checksum using 147 | // what some call the CCITT-False polynomial, which matches what is used 148 | // by Perl Digest/CRC and Boost for example. 149 | func ChecksumCCITTFalse(data []byte) uint16 { return Update(0xffff, CCITTFalseTable, data) } 150 | 151 | // ChecksumCCITT returns the CRC-16 checksum of data 152 | // using the CCITT polynomial. 153 | func ChecksumCCITT(data []byte) uint16 { return Update(0, CCITTTable, data) } 154 | 155 | // ChecksumSCSI returns the CRC-16 checksum of data 156 | // using the SCSI polynomial. 157 | func ChecksumSCSI(data []byte) uint16 { return Update(0, SCSITable, data) } 158 | 159 | // ChecksumMBus returns the CRC-16 checksum of data 160 | // using the MBus polynomial. 161 | func ChecksumMBus(data []byte) uint16 { return Update(0, MBusTable, data) } 162 | -------------------------------------------------------------------------------- /crc16_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package crc16 6 | 7 | import ( 8 | "bytes" 9 | "encoding" 10 | "testing" 11 | ) 12 | 13 | type testCase struct { 14 | Message []byte 15 | CRC uint16 16 | } 17 | 18 | func TestARC(t *testing.T) { 19 | tests := []testCase{ 20 | {[]byte("123456789"), 0xBB3D}} 21 | table := MakeTable(IBM) 22 | for _, testcase := range tests { 23 | result := ^Update(0xFFFF, table, testcase.Message) 24 | if testcase.CRC != result { 25 | t.Fatalf("ARC CRC-16 value is incorrect, expected %x, received %x.", testcase.CRC, result) 26 | } 27 | } 28 | } 29 | 30 | func TestModbus(t *testing.T) { 31 | tests := []testCase{ 32 | {[]byte{0xEA, 0x03, 0x00, 0x00, 0x00, 0x64}, 0x3A53}, 33 | {[]byte{0x4B, 0x03, 0x00, 0x2C, 0x00, 0x37}, 0xBFCB}, 34 | {[]byte("123456789"), 0x4B37}, 35 | {[]byte{0x0D, 0x01, 0x00, 0x62, 0x00, 0x33}, 0x0DDD}, 36 | {[]byte{0x01, 0x03, 0x00, 0x85, 0x00, 0x01}, 0xE395}, 37 | } 38 | for _, testcase := range tests { 39 | result := ^ChecksumIBM(testcase.Message) 40 | if testcase.CRC != result { 41 | t.Fatalf("Modbus CRC-16 value is incorrect, expected %X, received %X.", testcase.CRC, result) 42 | } 43 | } 44 | } 45 | 46 | func BenchmarkChecksumIBM(b *testing.B) { 47 | for i := 0; i < b.N; i++ { 48 | ChecksumIBM([]byte{0xEA, 0x03, 0x00, 0x00, 0x00, 0x64}) 49 | } 50 | } 51 | 52 | func BenchmarkMakeTable(b *testing.B) { 53 | for i := 0; i < b.N; i++ { 54 | MakeTable(IBM) 55 | } 56 | } 57 | 58 | func TestCCITTFalse(t *testing.T) { 59 | data := []byte("testdata") 60 | target := uint16(0xDC7C) 61 | 62 | actual := ChecksumCCITTFalse(data) 63 | if actual != target { 64 | t.Fatalf("CCITT checksum did not return the correct value, expected %x, received %x", target, actual) 65 | } 66 | } 67 | 68 | func TestBinaryMarshal(t *testing.T) { 69 | input1 := "The tunneling gopher digs downwards, " 70 | input2 := "unaware of what he will find." 71 | 72 | first := New(IBMTable) 73 | firstrev := New(MBusTable) 74 | first.Write([]byte(input1)) 75 | firstrev.Write([]byte(input1)) 76 | 77 | marshaler, ok := first.(encoding.BinaryMarshaler) 78 | if !ok { 79 | t.Fatal("first does not implement encoding.BinaryMarshaler") 80 | } 81 | state, err := marshaler.MarshalBinary() 82 | if err != nil { 83 | t.Fatal("unable to marshal hash:", err) 84 | } 85 | marshalerrev, rok := firstrev.(encoding.BinaryMarshaler) 86 | if !rok { 87 | t.Fatal("firstrev does not implement encoding.BinaryMarshaler") 88 | } 89 | staterev, err := marshalerrev.MarshalBinary() 90 | if err != nil { 91 | t.Fatal("unable to marshal hash:", err) 92 | } 93 | 94 | second := New(IBMTable) 95 | secondrev := New(MBusTable) 96 | 97 | unmarshaler, ok := second.(encoding.BinaryUnmarshaler) 98 | if !ok { 99 | t.Fatal("second does not implement encoding.BinaryUnmarshaler") 100 | } 101 | if err := unmarshaler.UnmarshalBinary(state); err != nil { 102 | t.Fatal("unable to unmarshal hash:", err) 103 | } 104 | 105 | unmarshalerrev, rok := secondrev.(encoding.BinaryUnmarshaler) 106 | if !rok { 107 | t.Fatal("secondrev does not implement encoding.BinaryUnmarshaler") 108 | } 109 | if err := unmarshalerrev.UnmarshalBinary(staterev); err != nil { 110 | t.Fatal("unable to unmarshal hash:", err) 111 | } 112 | 113 | first.Write([]byte(input2)) 114 | second.Write([]byte(input2)) 115 | firstrev.Write([]byte(input2)) 116 | secondrev.Write([]byte(input2)) 117 | 118 | if !bytes.Equal(first.Sum(nil), second.Sum(nil)) { 119 | t.Fatalf("does not match!") 120 | } 121 | if !bytes.Equal(firstrev.Sum(nil), secondrev.Sum(nil)) { 122 | t.Fatalf("does not match!") 123 | } 124 | if bytes.Equal(first.Sum(nil), secondrev.Sum(nil)) { 125 | t.Fatalf("should not match!") 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /hash.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package crc16 6 | 7 | import ( 8 | "errors" 9 | "hash" 10 | ) 11 | 12 | // Hash16 is the common interface implemented by all 16-bit hash functions. 13 | type Hash16 interface { 14 | hash.Hash 15 | Sum16() uint16 16 | } 17 | 18 | // New creates a new Hash16 computing the CRC-16 checksum 19 | // using the polynomial represented by the Table. 20 | func New(tab *Table) Hash16 { return &digest{0, tab} } 21 | 22 | // NewIBM creates a new Hash16 computing the CRC-16 checksum 23 | // using the IBM polynomial. 24 | func NewIBM() Hash16 { return New(IBMTable) } 25 | 26 | // NewCCITT creates a new hash.Hash16 computing the CRC-16 checksum 27 | // using the CCITT polynomial. 28 | func NewCCITT() Hash16 { return New(CCITTTable) } 29 | 30 | // NewSCSI creates a new Hash16 computing the CRC-16 checksum 31 | // using the SCSI polynomial. 32 | func NewSCSI() Hash16 { return New(SCSITable) } 33 | 34 | // digest represents the partial evaluation of a checksum. 35 | type digest struct { 36 | crc uint16 37 | tab *Table 38 | } 39 | 40 | func (d *digest) Size() int { return 2 } 41 | 42 | func (d *digest) BlockSize() int { return 1 } 43 | 44 | func (d *digest) Reset() { d.crc = 0 } 45 | 46 | func (d *digest) Write(p []byte) (n int, err error) { 47 | d.crc = Update(d.crc, d.tab, p) 48 | return len(p), nil 49 | } 50 | 51 | func (d *digest) Sum16() uint16 { return d.crc } 52 | 53 | func (d *digest) Sum(in []byte) []byte { 54 | s := d.Sum16() 55 | return append(in, byte(s>>8), byte(s)) 56 | } 57 | 58 | const ( 59 | magic = "crc16\x01" 60 | marshaledSize = len(magic) + 2 + 2 + 1 61 | ) 62 | 63 | func (d *digest) MarshalBinary() ([]byte, error) { 64 | b := make([]byte, 0, marshaledSize) 65 | b = append(b, magic...) 66 | b = appendUint16(b, tableSum(d.tab)) 67 | b = appendUint16(b, d.crc) 68 | if d.tab.reversed { 69 | b = append(b, byte(0x01)) 70 | } else { 71 | b = append(b, byte(0x00)) 72 | } 73 | return b, nil 74 | } 75 | 76 | func (d *digest) UnmarshalBinary(b []byte) error { 77 | if len(b) < len(magic) || string(b[:len(magic)]) != magic { 78 | return errors.New("hash/crc16: invalid hash state identifier") 79 | } 80 | if len(b) != marshaledSize { 81 | return errors.New("hash/crc16: invalid hash state size") 82 | } 83 | if tableSum(d.tab) != readUint16(b[6:]) { 84 | return errors.New("hash/crc16: tables do not match") 85 | } 86 | d.crc = readUint16(b[8:]) 87 | if b[10] == 0x01 { 88 | d.tab.reversed = true 89 | } 90 | return nil 91 | } 92 | 93 | func appendUint16(b []byte, x uint16) []byte { 94 | a := [2]byte{ 95 | byte(x >> 8), 96 | byte(x), 97 | } 98 | return append(b, a[:]...) 99 | } 100 | 101 | func readUint16(b []byte) uint16 { 102 | _ = b[1] 103 | return uint16(b[1]) | uint16(b[0])<<8 104 | } 105 | 106 | // tableSum returns the IBM checksum of table t. 107 | func tableSum(t *Table) uint16 { 108 | var a [1024]byte 109 | b := a[:0] 110 | if t != nil { 111 | for _, x := range t.entries { 112 | b = appendUint16(b, x) 113 | } 114 | } 115 | return ChecksumIBM(b) 116 | } 117 | --------------------------------------------------------------------------------