-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
236 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// 版权 @2023 凹语言 作者。保留所有权利。 | ||
|
||
func TestTODO { | ||
// | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
// 版权 @2023 凹语言 作者。保留所有权利。 | ||
|
||
// Package crc64 implements the 64-bit cyclic redundancy check, or CRC-64, | ||
// checksum. See https://en.wikipedia.org/wiki/Cyclic_redundancy_check for | ||
// information. | ||
|
||
import ( | ||
"errors" | ||
"hash" | ||
) | ||
|
||
// The size of a CRC-64 checksum in bytes. | ||
const Size = 8 | ||
|
||
// Predefined polynomials. | ||
const ( | ||
// The ISO polynomial, defined in ISO 3309 and used in HDLC. | ||
ISO = 0xD800000000000000 | ||
|
||
// The ECMA polynomial, defined in ECMA 182. | ||
ECMA = 0xC96C5795D7870F42 | ||
) | ||
|
||
// Table is a 256-word table representing the polynomial for efficient processing. | ||
type Table [256]uint64 | ||
|
||
global ( | ||
slicing8TablesBuildOnce :bool | ||
slicing8TableISO :*[8]Table | ||
slicing8TableECMA :*[8]Table | ||
) | ||
|
||
func buildSlicing8TablesOnce() { | ||
if slicing8TablesBuildOnce { | ||
return | ||
} | ||
slicing8TablesBuildOnce = true | ||
buildSlicing8Tables() | ||
} | ||
|
||
func buildSlicing8Tables() { | ||
slicing8TableISO = makeSlicingBy8Table(makeTable(ISO)) | ||
slicing8TableECMA = makeSlicingBy8Table(makeTable(ECMA)) | ||
} | ||
|
||
// MakeTable returns a Table constructed from the specified polynomial. | ||
// The contents of this Table must not be modified. | ||
func MakeTable(poly: uint64) => *Table { | ||
buildSlicing8TablesOnce() | ||
switch poly { | ||
case ISO: | ||
return &slicing8TableISO[0] | ||
case ECMA: | ||
return &slicing8TableECMA[0] | ||
default: | ||
return makeTable(poly) | ||
} | ||
} | ||
|
||
func makeTable(poly: uint64) => *Table { | ||
t := new(Table) | ||
for i := 0; i < 256; i++ { | ||
crc := uint64(i) | ||
for j := 0; j < 8; j++ { | ||
if crc&1 == 1 { | ||
crc = (crc >> 1) ^ poly | ||
} else { | ||
crc >>= 1 | ||
} | ||
} | ||
t[i] = crc | ||
} | ||
return t | ||
} | ||
|
||
func makeSlicingBy8Table(t: *Table) => *[8]Table { | ||
helperTable: [8]Table | ||
helperTable[0] = *t | ||
for i := 0; i < 256; i++ { | ||
crc := t[i] | ||
for j := 1; j < 8; j++ { | ||
crc = t[crc&0xff] ^ (crc >> 8) | ||
helperTable[j][i] = crc | ||
} | ||
} | ||
return &helperTable | ||
} | ||
|
||
// digest represents the partial evaluation of a checksum. | ||
type digest struct { | ||
crc: uint64 | ||
tab: *Table | ||
} | ||
|
||
// New creates a new hash.Hash64 computing the CRC-64 checksum using the | ||
// polynomial represented by the Table. Its Sum method will lay the | ||
// value out in big-endian byte order. The returned Hash64 also | ||
// implements encoding.BinaryMarshaler and encoding.BinaryUnmarshaler to | ||
// marshal and unmarshal the internal state of the hash. | ||
func New(tab: *Table) => hash.Hash64 { return &digest{0, tab} } | ||
|
||
func digest.Size() => int { return Size } | ||
|
||
func digest.BlockSize() => int { return 1 } | ||
|
||
func digest.Reset() { this.crc = 0 } | ||
|
||
const ( | ||
magic = "crc\x02" | ||
marshaledSize = len(magic) + 8 + 8 | ||
) | ||
|
||
func digest.MarshalBinary() => ([]byte, error) { | ||
d := this | ||
b := make([]byte, 0, marshaledSize) | ||
b = append(b, magic...) | ||
b = appendUint64(b, tableSum(d.tab)) | ||
b = appendUint64(b, d.crc) | ||
return b, nil | ||
} | ||
|
||
func digest.UnmarshalBinary(b: []byte) => error { | ||
d := this | ||
if len(b) < len(magic) || string(b[:len(magic)]) != magic { | ||
return errors.New("hash/crc64: invalid hash state identifier") | ||
} | ||
if len(b) != marshaledSize { | ||
return errors.New("hash/crc64: invalid hash state size") | ||
} | ||
if tableSum(d.tab) != readUint64(b[4:]) { | ||
return errors.New("hash/crc64: tables do not match") | ||
} | ||
d.crc = readUint64(b[12:]) | ||
return nil | ||
} | ||
|
||
func appendUint64(b: []byte, x: uint64) => []byte { | ||
a := [8]byte{ | ||
byte(x >> 56), | ||
byte(x >> 48), | ||
byte(x >> 40), | ||
byte(x >> 32), | ||
byte(x >> 24), | ||
byte(x >> 16), | ||
byte(x >> 8), | ||
byte(x), | ||
} | ||
return append(b, a[:]...) | ||
} | ||
|
||
func readUint64(b: []byte) => uint64 { | ||
_ = b[7] | ||
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | | ||
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 | ||
} | ||
|
||
func update(crc: uint64, tab: *Table, p: []byte) => uint64 { | ||
buildSlicing8TablesOnce() | ||
crc = ^crc | ||
// Table comparison is somewhat expensive, so avoid it for small sizes | ||
for len(p) >= 64 { | ||
helperTable: *[8]Table | ||
if *tab == slicing8TableECMA[0] { | ||
helperTable = slicing8TableECMA | ||
} else if *tab == slicing8TableISO[0] { | ||
helperTable = slicing8TableISO | ||
// For smaller sizes creating extended table takes too much time | ||
} else if len(p) >= 2048 { | ||
// According to the tests between various x86 and arm CPUs, 2k is a reasonable | ||
// threshold for now. This may change in the future. | ||
helperTable = makeSlicingBy8Table(tab) | ||
} else { | ||
break | ||
} | ||
// Update using slicing-by-8 | ||
for len(p) > 8 { | ||
crc ^= uint64(p[0]) | uint64(p[1])<<8 | uint64(p[2])<<16 | uint64(p[3])<<24 | | ||
uint64(p[4])<<32 | uint64(p[5])<<40 | uint64(p[6])<<48 | uint64(p[7])<<56 | ||
crc = helperTable[7][crc&0xff] ^ | ||
helperTable[6][(crc>>8)&0xff] ^ | ||
helperTable[5][(crc>>16)&0xff] ^ | ||
helperTable[4][(crc>>24)&0xff] ^ | ||
helperTable[3][(crc>>32)&0xff] ^ | ||
helperTable[2][(crc>>40)&0xff] ^ | ||
helperTable[1][(crc>>48)&0xff] ^ | ||
helperTable[0][crc>>56] | ||
p = p[8:] | ||
} | ||
} | ||
// For reminders or small sizes | ||
for _, v := range p { | ||
crc = tab[byte(crc)^v] ^ (crc >> 8) | ||
} | ||
return ^crc | ||
} | ||
|
||
// Update returns the result of adding the bytes in p to the crc. | ||
func Update(crc: uint64, tab: *Table, p: []byte) => uint64 { | ||
return update(crc, tab, p) | ||
} | ||
|
||
func digest.Write(p: []byte) => (n: int, err: error) { | ||
d := this | ||
d.crc = update(d.crc, d.tab, p) | ||
return len(p), nil | ||
} | ||
|
||
func digest.Sum64() => uint64 { return this.crc } | ||
|
||
func digest.Sum(in: []byte) => []byte { | ||
d := this | ||
s := d.Sum64() | ||
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)) | ||
} | ||
|
||
// Checksum returns the CRC-64 checksum of data | ||
// using the polynomial represented by the Table. | ||
func Checksum(data: []byte, tab: *Table) => uint64 { return update(0, tab, data) } | ||
|
||
// tableSum returns the ISO checksum of table t. | ||
func tableSum(t: *Table) => uint64 { | ||
a: [2048]byte | ||
b := a[:0] | ||
if t != nil { | ||
for _, x := range t { | ||
b = appendUint64(b, x) | ||
} | ||
} | ||
return Checksum(b, MakeTable(ISO)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters