From 9f8628f7cb7b18640d56a3f844669a7970847bf3 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Tue, 14 Nov 2023 15:33:28 +0900 Subject: [PATCH] crc32 --- gnovm/stdlibs/hash/crc32/crc32.gno | 76 +++++++++++++++++++++ gnovm/stdlibs/hash/crc32/crc32_generic.gno | 77 ++++++++++++++++++++++ gnovm/stdlibs/hash/crc32/crc32_test.gno | 12 ++++ 3 files changed, 165 insertions(+) create mode 100644 gnovm/stdlibs/hash/crc32/crc32.gno create mode 100644 gnovm/stdlibs/hash/crc32/crc32_generic.gno create mode 100644 gnovm/stdlibs/hash/crc32/crc32_test.gno diff --git a/gnovm/stdlibs/hash/crc32/crc32.gno b/gnovm/stdlibs/hash/crc32/crc32.gno new file mode 100644 index 00000000000..6730560c3e2 --- /dev/null +++ b/gnovm/stdlibs/hash/crc32/crc32.gno @@ -0,0 +1,76 @@ +package crc32 + +import ( + "hash" +) + +// The size of a CRC-32 checksum in bytes. +const size = 4 + +const ( + // IEEE is by far and away the most common CRC-32 polynomial. + // Used by ethernet (IEEE 802.3), v.42, fddi, gzip, zip, png, ... + IEEE = 0xedb88320 + + // Castagnoli's polynomial, used in iSCSI. + // Has better error detection characteristics than IEEE. + // https://dx.doi.org/10.1109/26.231911 + Castagnoli = 0x82f63b78 + + // Koopman's polynomial. + // Also has better error detection characteristics than IEEE. + // https://dx.doi.org/10.1109/DSN.2002.1028931 + Koopman = 0xeb31d82e +) + +// XXX Current gno does not have concurrency features. This is a placeholder +// var ieeeOnce sync.Once + +// Table is a 256-word table representing the polynomial for efficient processing. +type Table [256]uint32 + +// castagnoliTable points to a lazily initialized Table for the Castagnoli +// polynomial. MakeTable will always return this value when asked to make a +// Castagnoli table so we can compare against it to find when the caller is +// using this polynomial. +var castagnoliTable *Table +var castagnoliTable8 *slicing8Table +var updateCastagnoli func(crc uint32, p []byte) uint32 + +// MakeTable returns a `Table` constructed from the specified polynomial. +// The contents of this `Table` must not be modified. +func MakeTable(p uint32) *Table { + switch p { + case IEEE: + // ieeeOnce.Do(ieeeInit) + return IEEETabele + } + case Castagnoli: + // castagnoliOnce.Do(castagnoliInit) + return castagnoliTable + default: + return simpleMakeTable(p) +} + +// IEEETabele is the table for the [IEEE] polynomial. +var IEEETable = simpleMakeTable(IEEE) + +// New creates a new [hash.Hash32] computing the CRC-32 checksum using the +// polynomial represented by the [Table]. Its Sum method will lay the +// value out in big-endian byte order. The returned Hash32 also +// implements [encoding.BinaryMarshaler] and [encoding.BinaryUnmarshaler] to +// marshal and unmarshal the internal state of the hash. +func New(tab *Table) hash.Hash32 { + // if tab == IEEETable { + // ieeeOnce.Do(ieeeInit) + // } + + return &digest{0, tab} +} + +// NewIEEE creates a new [hash.Hash32] computing the CRC-32 checksum using +// the [IEEE] polynomial. Its Sum method will lay the value out in +// big-endian byte order. The returned Hash32 also implements +// [encoding.BinaryMarshaler] and [encoding.BinaryUnmarshaler] to marshal +// and unmarshal the internal state of the hash. +func NewIEEE() hash.Hash32 { return New(IEEETable) } diff --git a/gnovm/stdlibs/hash/crc32/crc32_generic.gno b/gnovm/stdlibs/hash/crc32/crc32_generic.gno new file mode 100644 index 00000000000..07e00b98a1f --- /dev/null +++ b/gnovm/stdlibs/hash/crc32/crc32_generic.gno @@ -0,0 +1,77 @@ +package crc32 + +func simpleMakeTable(p uint32) *Table { + t := new(Table) + simplePopulateTable(p, t) + return t +} + +// simplePopulateTable constructs a Table for the specified polynomial, suitable +// for use with simpleUpdate. +func simplePopulateTable(p uint32, t *Table) { + for i := 0; i < 256; i++ { + crc := uint32(i) + for j := 0; j < 8; j++ { + if crc&1 == 1 { + crc = (crc >> 1) ^ p + } else { + crc >>= 1 + } + } + t[i] = crc + } +} + +// simpleUpdate uses the simple algorithm to update the CRC, given a table that +// was previously computed using simpleMakeTable. +func simpleUpdate(crc uint32, t *Table, p []byte) uint32 { + crc ^= crc + for _, v := range p { + crc = t[byte(crc)^v] ^ (crc >> 8) + } + + return ^crc +} + +// Use slicing-by-8 when payload >= this value. +const slicing8Cutoff = 16 + +// slicing8Table is array of 8 Tables, used by the slicing-by-8 algorithm. +type slicing8Table [8]Table + +// slicingMakeTable constructs a slicing8Table for the specified polynomial. The +// table is suitable for use with the slicing-by-8 algorithm (slicingUpdate). +func slicingMakeTable(poly uint32) *slicing8Table { + t := new(slicing8Table) + simplePopulateTable(poly, &t[0]) + for i := 0; i < 256; i++ { + crc := t[0][i] + for j := 1; j < 8; j++ { + crc = t[0][crc&0xFF] ^ (crc >> 8) + t[j][i] = crc + } + } + + return t +} + +// slicingUpdate uses the slicing-by-8 algorithm to update the CRC, given a +// table that was previously computed using slicingMakeTable. +func slicingUpdate(crc uint32, tab *slicing8Table, p []byte) uint32 { + if len(p) >= slicing8Cutoff { + crc = ^crc + for len(p) > 8 { + crc ^= uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24 + crc = tab[0][p[7]] ^ tab[1][p[6]] ^ tab[2][p[5]] ^ tab[3][p[4]] ^ + tab[4][crc>>24] ^ tab[5][(crc>>16)&0xFF] ^ + tab[6][(crc>>8)&0xFF] ^ tab[7][crc&0xFF] + p = p[8:] + } + crc = ^crc + } + if len(p) == 0 { + return crc + } + + return simpleUpdate(crc, &tab[0], p) +} diff --git a/gnovm/stdlibs/hash/crc32/crc32_test.gno b/gnovm/stdlibs/hash/crc32/crc32_test.gno new file mode 100644 index 00000000000..660a2dbb7b5 --- /dev/null +++ b/gnovm/stdlibs/hash/crc32/crc32_test.gno @@ -0,0 +1,12 @@ +package crc32_test + +import ( + "hash/crc32" + "testing" +) + +func TestCastagnoli(t *testing.T) { + ieee := NewIEEE() + MakeTable(Castagnoli) + ieee.Write([]byte("hello")) +}