Skip to content

Commit

Permalink
crc32
Browse files Browse the repository at this point in the history
  • Loading branch information
notJoon committed Nov 14, 2023
1 parent b1a53c0 commit 9f8628f
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 0 deletions.
76 changes: 76 additions & 0 deletions gnovm/stdlibs/hash/crc32/crc32.gno
Original file line number Diff line number Diff line change
@@ -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) }
77 changes: 77 additions & 0 deletions gnovm/stdlibs/hash/crc32/crc32_generic.gno
Original file line number Diff line number Diff line change
@@ -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)
}
12 changes: 12 additions & 0 deletions gnovm/stdlibs/hash/crc32/crc32_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package crc32_test

import (
"hash/crc32"
"testing"
)

func TestCastagnoli(t *testing.T) {
ieee := NewIEEE()
MakeTable(Castagnoli)
ieee.Write([]byte("hello"))
}

0 comments on commit 9f8628f

Please sign in to comment.