github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/hash/crc64/crc64.go (about) 1 // Copyright 2009 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 crc64 implements the 64-bit cyclic redundancy check, or CRC-64, 6 // checksum. See https://en.wikipedia.org/wiki/Cyclic_redundancy_check for 7 // information. 8 package crc64 9 10 import ( 11 "errors" 12 "hash" 13 "sync" 14 ) 15 16 // The size of a CRC-64 checksum in bytes. 17 const Size = 8 18 19 // Predefined polynomials. 20 const ( 21 // The ISO polynomial, defined in ISO 3309 and used in HDLC. 22 ISO = 0xD800000000000000 23 24 // The ECMA polynomial, defined in ECMA 182. 25 ECMA = 0xC96C5795D7870F42 26 ) 27 28 // Table is a 256-word table representing the polynomial for efficient processing. 29 type Table [256]uint64 30 31 var ( 32 slicing8TablesBuildOnce sync.Once 33 slicing8TableISO *[8]Table 34 slicing8TableECMA *[8]Table 35 ) 36 37 func buildSlicing8TablesOnce() { 38 slicing8TablesBuildOnce.Do(buildSlicing8Tables) 39 } 40 41 func buildSlicing8Tables() { 42 slicing8TableISO = makeSlicingBy8Table(makeTable(ISO)) 43 slicing8TableECMA = makeSlicingBy8Table(makeTable(ECMA)) 44 } 45 46 // MakeTable returns a [Table] constructed from the specified polynomial. 47 // The contents of this [Table] must not be modified. 48 func MakeTable(poly uint64) *Table { 49 buildSlicing8TablesOnce() 50 switch poly { 51 case ISO: 52 return &slicing8TableISO[0] 53 case ECMA: 54 return &slicing8TableECMA[0] 55 default: 56 return makeTable(poly) 57 } 58 } 59 60 func makeTable(poly uint64) *Table { 61 t := new(Table) 62 for i := 0; i < 256; i++ { 63 crc := uint64(i) 64 for j := 0; j < 8; j++ { 65 if crc&1 == 1 { 66 crc = (crc >> 1) ^ poly 67 } else { 68 crc >>= 1 69 } 70 } 71 t[i] = crc 72 } 73 return t 74 } 75 76 func makeSlicingBy8Table(t *Table) *[8]Table { 77 var helperTable [8]Table 78 helperTable[0] = *t 79 for i := 0; i < 256; i++ { 80 crc := t[i] 81 for j := 1; j < 8; j++ { 82 crc = t[crc&0xff] ^ (crc >> 8) 83 helperTable[j][i] = crc 84 } 85 } 86 return &helperTable 87 } 88 89 // digest represents the partial evaluation of a checksum. 90 type digest struct { 91 crc uint64 92 tab *Table 93 } 94 95 // New creates a new hash.Hash64 computing the CRC-64 checksum using the 96 // polynomial represented by the [Table]. Its Sum method will lay the 97 // value out in big-endian byte order. The returned Hash64 also 98 // implements [encoding.BinaryMarshaler] and [encoding.BinaryUnmarshaler] to 99 // marshal and unmarshal the internal state of the hash. 100 func New(tab *Table) hash.Hash64 { return &digest{0, tab} } 101 102 func (d *digest) Size() int { return Size } 103 104 func (d *digest) BlockSize() int { return 1 } 105 106 func (d *digest) Reset() { d.crc = 0 } 107 108 const ( 109 magic = "crc\x02" 110 marshaledSize = len(magic) + 8 + 8 111 ) 112 113 func (d *digest) MarshalBinary() ([]byte, error) { 114 b := make([]byte, 0, marshaledSize) 115 b = append(b, magic...) 116 b = appendUint64(b, tableSum(d.tab)) 117 b = appendUint64(b, d.crc) 118 return b, nil 119 } 120 121 func (d *digest) UnmarshalBinary(b []byte) error { 122 if len(b) < len(magic) || string(b[:len(magic)]) != magic { 123 return errors.New("hash/crc64: invalid hash state identifier") 124 } 125 if len(b) != marshaledSize { 126 return errors.New("hash/crc64: invalid hash state size") 127 } 128 if tableSum(d.tab) != readUint64(b[4:]) { 129 return errors.New("hash/crc64: tables do not match") 130 } 131 d.crc = readUint64(b[12:]) 132 return nil 133 } 134 135 // appendUint64 is semantically the same as [binary.BigEndian.AppendUint64] 136 // We copied this function because we can not import "encoding/binary" here. 137 func appendUint64(b []byte, x uint64) []byte { 138 return append(b, 139 byte(x>>56), 140 byte(x>>48), 141 byte(x>>40), 142 byte(x>>32), 143 byte(x>>24), 144 byte(x>>16), 145 byte(x>>8), 146 byte(x), 147 ) 148 } 149 150 // readUint64 is semantically the same as [binary.BigEndian.Uint64] 151 // We copied this function because we can not import "encoding/binary" here. 152 func readUint64(b []byte) uint64 { 153 _ = b[7] 154 return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | 155 uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 156 } 157 158 func update(crc uint64, tab *Table, p []byte) uint64 { 159 buildSlicing8TablesOnce() 160 crc = ^crc 161 // Table comparison is somewhat expensive, so avoid it for small sizes 162 for len(p) >= 64 { 163 var helperTable *[8]Table 164 if *tab == slicing8TableECMA[0] { 165 helperTable = slicing8TableECMA 166 } else if *tab == slicing8TableISO[0] { 167 helperTable = slicing8TableISO 168 // For smaller sizes creating extended table takes too much time 169 } else if len(p) >= 2048 { 170 // According to the tests between various x86 and arm CPUs, 2k is a reasonable 171 // threshold for now. This may change in the future. 172 helperTable = makeSlicingBy8Table(tab) 173 } else { 174 break 175 } 176 // Update using slicing-by-8 177 for len(p) > 8 { 178 crc ^= uint64(p[0]) | uint64(p[1])<<8 | uint64(p[2])<<16 | uint64(p[3])<<24 | 179 uint64(p[4])<<32 | uint64(p[5])<<40 | uint64(p[6])<<48 | uint64(p[7])<<56 180 crc = helperTable[7][crc&0xff] ^ 181 helperTable[6][(crc>>8)&0xff] ^ 182 helperTable[5][(crc>>16)&0xff] ^ 183 helperTable[4][(crc>>24)&0xff] ^ 184 helperTable[3][(crc>>32)&0xff] ^ 185 helperTable[2][(crc>>40)&0xff] ^ 186 helperTable[1][(crc>>48)&0xff] ^ 187 helperTable[0][crc>>56] 188 p = p[8:] 189 } 190 } 191 // For reminders or small sizes 192 for _, v := range p { 193 crc = tab[byte(crc)^v] ^ (crc >> 8) 194 } 195 return ^crc 196 } 197 198 // Update returns the result of adding the bytes in p to the crc. 199 func Update(crc uint64, tab *Table, p []byte) uint64 { 200 return update(crc, tab, p) 201 } 202 203 func (d *digest) Write(p []byte) (n int, err error) { 204 d.crc = update(d.crc, d.tab, p) 205 return len(p), nil 206 } 207 208 func (d *digest) Sum64() uint64 { return d.crc } 209 210 func (d *digest) Sum(in []byte) []byte { 211 s := d.Sum64() 212 return append(in, byte(s>>56), byte(s>>48), byte(s>>40), byte(s>>32), byte(s>>24), byte(s>>16), byte(s>>8), byte(s)) 213 } 214 215 // Checksum returns the CRC-64 checksum of data 216 // using the polynomial represented by the [Table]. 217 func Checksum(data []byte, tab *Table) uint64 { return update(0, tab, data) } 218 219 // tableSum returns the ISO checksum of table t. 220 func tableSum(t *Table) uint64 { 221 var a [2048]byte 222 b := a[:0] 223 if t != nil { 224 for _, x := range t { 225 b = appendUint64(b, x) 226 } 227 } 228 return Checksum(b, MakeTable(ISO)) 229 }