aboutsummaryrefslogtreecommitdiff
path: root/src/hash
diff options
context:
space:
mode:
authorTim Cooper <tim.cooper@layeh.com>2017-09-27 19:46:58 -0300
committerIan Lance Taylor <iant@golang.org>2017-11-01 21:04:12 +0000
commit731b6321728eb53e6087dc877344878b06cc4b06 (patch)
tree05193f7e0aafe3624b42a6dbbc901f8c7178334a /src/hash
parent14bc4f5e5fd5a113c97bdf1455b4f7d36a81f54b (diff)
downloadgo-731b6321728eb53e6087dc877344878b06cc4b06.tar.gz
go-731b6321728eb53e6087dc877344878b06cc4b06.zip
crypto, hash: implement BinaryMarshaler, BinaryUnmarshaler in hash implementations
The marshal method allows the hash's internal state to be serialized and unmarshaled at a later time, without having the re-write the entire stream of data that was already written to the hash. Fixes #20573 Change-Id: I40bbb84702ac4b7c5662f99bf943cdf4081203e5 Reviewed-on: https://go-review.googlesource.com/66710 Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com> Reviewed-by: Ian Lance Taylor <iant@golang.org> Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
Diffstat (limited to 'src/hash')
-rw-r--r--src/hash/adler32/adler32.go43
-rw-r--r--src/hash/adler32/adler32_test.go125
-rw-r--r--src/hash/crc32/crc32.go55
-rw-r--r--src/hash/crc32/crc32_test.go150
-rw-r--r--src/hash/crc64/crc64.go64
-rw-r--r--src/hash/crc64/crc64_test.go154
-rw-r--r--src/hash/fnv/fnv.go161
-rw-r--r--src/hash/fnv/fnv_test.go113
-rw-r--r--src/hash/hash.go13
9 files changed, 732 insertions, 146 deletions
diff --git a/src/hash/adler32/adler32.go b/src/hash/adler32/adler32.go
index 21d6a2e1dc..149a6b889e 100644
--- a/src/hash/adler32/adler32.go
+++ b/src/hash/adler32/adler32.go
@@ -12,7 +12,10 @@
// significant-byte first (network) order.
package adler32
-import "hash"
+import (
+ "errors"
+ "hash"
+)
const (
// mod is the largest prime that is less than 65536.
@@ -44,6 +47,44 @@ func (d *digest) Size() int { return Size }
func (d *digest) BlockSize() int { return 4 }
+const (
+ magic = "adl\x01"
+ marshaledSize = len(magic) + 4
+)
+
+func (d *digest) MarshalBinary() ([]byte, error) {
+ b := make([]byte, 0, marshaledSize)
+ b = append(b, magic...)
+ b = appendUint32(b, uint32(*d))
+ return b, nil
+}
+
+func (d *digest) UnmarshalBinary(b []byte) error {
+ if len(b) < len(magic) || string(b[:len(magic)]) != magic {
+ return errors.New("hash/adler32: invalid hash state identifier")
+ }
+ if len(b) != marshaledSize {
+ return errors.New("hash/adler32: invalid hash state size")
+ }
+ *d = digest(readUint32(b[len(magic):]))
+ return nil
+}
+
+func appendUint32(b []byte, x uint32) []byte {
+ a := [4]byte{
+ byte(x >> 24),
+ byte(x >> 16),
+ byte(x >> 8),
+ byte(x),
+ }
+ return append(b, a[:]...)
+}
+
+func readUint32(b []byte) uint32 {
+ _ = b[3]
+ return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
+}
+
// Add p to the running checksum d.
func update(d digest, p []byte) digest {
s1, s2 := uint32(d&0xffff), uint32(d>>16)
diff --git a/src/hash/adler32/adler32_test.go b/src/hash/adler32/adler32_test.go
index 0e9c938d80..6bac802507 100644
--- a/src/hash/adler32/adler32_test.go
+++ b/src/hash/adler32/adler32_test.go
@@ -5,57 +5,60 @@
package adler32
import (
+ "encoding"
+ "io"
"strings"
"testing"
)
var golden = []struct {
- out uint32
- in string
+ out uint32
+ in string
+ halfState string // marshaled hash state after first half of in written, used by TestGoldenMarshal
}{
- {0x00000001, ""},
- {0x00620062, "a"},
- {0x012600c4, "ab"},
- {0x024d0127, "abc"},
- {0x03d8018b, "abcd"},
- {0x05c801f0, "abcde"},
- {0x081e0256, "abcdef"},
- {0x0adb02bd, "abcdefg"},
- {0x0e000325, "abcdefgh"},
- {0x118e038e, "abcdefghi"},
- {0x158603f8, "abcdefghij"},
- {0x3f090f02, "Discard medicine more than two years old."},
- {0x46d81477, "He who has a shady past knows that nice guys finish last."},
- {0x40ee0ee1, "I wouldn't marry him with a ten foot pole."},
- {0x16661315, "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"},
- {0x5b2e1480, "The days of the digital watch are numbered. -Tom Stoppard"},
- {0x8c3c09ea, "Nepal premier won't resign."},
- {0x45ac18fd, "For every action there is an equal and opposite government program."},
- {0x53c61462, "His money is twice tainted: 'taint yours and 'taint mine."},
- {0x7e511e63, "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"},
- {0xe4801a6a, "It's a tiny change to the code and not completely disgusting. - Bob Manchek"},
- {0x61b507df, "size: a.out: bad magic"},
- {0xb8631171, "The major problem is with sendmail. -Mark Horton"},
- {0x8b5e1904, "Give me a rock, paper and scissors and I will move the world. CCFestoon"},
- {0x7cc6102b, "If the enemy is within range, then so are you."},
- {0x700318e7, "It's well we cannot hear the screams/That we create in others' dreams."},
- {0x1e601747, "You remind me of a TV show, but that's all right: I watch it anyway."},
- {0xb55b0b09, "C is as portable as Stonehedge!!"},
- {0x39111dd0, "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"},
- {0x91dd304f, "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"},
- {0x2e5d1316, "How can you write a big system without C++? -Paul Glick"},
- {0xd0201df6, "'Invariant assertions' is the most elegant programming technique! -Tom Szymanski"},
- {0x211297c8, strings.Repeat("\xff", 5548) + "8"},
- {0xbaa198c8, strings.Repeat("\xff", 5549) + "9"},
- {0x553499be, strings.Repeat("\xff", 5550) + "0"},
- {0xf0c19abe, strings.Repeat("\xff", 5551) + "1"},
- {0x8d5c9bbe, strings.Repeat("\xff", 5552) + "2"},
- {0x2af69cbe, strings.Repeat("\xff", 5553) + "3"},
- {0xc9809dbe, strings.Repeat("\xff", 5554) + "4"},
- {0x69189ebe, strings.Repeat("\xff", 5555) + "5"},
- {0x86af0001, strings.Repeat("\x00", 1e5)},
- {0x79660b4d, strings.Repeat("a", 1e5)},
- {0x110588ee, strings.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1e4)},
+ {0x00000001, "", "adl\x01\x00\x00\x00\x01"},
+ {0x00620062, "a", "adl\x01\x00\x00\x00\x01"},
+ {0x012600c4, "ab", "adl\x01\x00b\x00b"},
+ {0x024d0127, "abc", "adl\x01\x00b\x00b"},
+ {0x03d8018b, "abcd", "adl\x01\x01&\x00\xc4"},
+ {0x05c801f0, "abcde", "adl\x01\x01&\x00\xc4"},
+ {0x081e0256, "abcdef", "adl\x01\x02M\x01'"},
+ {0x0adb02bd, "abcdefg", "adl\x01\x02M\x01'"},
+ {0x0e000325, "abcdefgh", "adl\x01\x03\xd8\x01\x8b"},
+ {0x118e038e, "abcdefghi", "adl\x01\x03\xd8\x01\x8b"},
+ {0x158603f8, "abcdefghij", "adl\x01\x05\xc8\x01\xf0"},
+ {0x3f090f02, "Discard medicine more than two years old.", "adl\x01NU\a\x87"},
+ {0x46d81477, "He who has a shady past knows that nice guys finish last.", "adl\x01\x89\x8e\t\xe9"},
+ {0x40ee0ee1, "I wouldn't marry him with a ten foot pole.", "adl\x01R\t\ag"},
+ {0x16661315, "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave", "adl\x01\u007f\xbb\t\x10"},
+ {0x5b2e1480, "The days of the digital watch are numbered. -Tom Stoppard", "adl\x01\x99:\n~"},
+ {0x8c3c09ea, "Nepal premier won't resign.", "adl\x01\"\x05\x05\x05"},
+ {0x45ac18fd, "For every action there is an equal and opposite government program.", "adl\x01\xcc\xfa\f\x00"},
+ {0x53c61462, "His money is twice tainted: 'taint yours and 'taint mine.", "adl\x01\x93\xa9\n\b"},
+ {0x7e511e63, "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977", "adl\x01e\xf5\x10\x14"},
+ {0xe4801a6a, "It's a tiny change to the code and not completely disgusting. - Bob Manchek", "adl\x01\xee\x00\f\xb2"},
+ {0x61b507df, "size: a.out: bad magic", "adl\x01\x1a\xfc\x04\x1d"},
+ {0xb8631171, "The major problem is with sendmail. -Mark Horton", "adl\x01mi\b\xdc"},
+ {0x8b5e1904, "Give me a rock, paper and scissors and I will move the world. CCFestoon", "adl\x01\xe3\n\f\x9f"},
+ {0x7cc6102b, "If the enemy is within range, then so are you.", "adl\x01_\xe0\b\x1e"},
+ {0x700318e7, "It's well we cannot hear the screams/That we create in others' dreams.", "adl\x01ۘ\f\x87"},
+ {0x1e601747, "You remind me of a TV show, but that's all right: I watch it anyway.", "adl\x01\xcc}\v\x83"},
+ {0xb55b0b09, "C is as portable as Stonehedge!!", "adl\x01,^\x05\xad"},
+ {0x39111dd0, "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley", "adl\x01M\xd1\x0e\xc8"},
+ {0x91dd304f, "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule", "adl\x01#\xd8\x17\xd7"},
+ {0x2e5d1316, "How can you write a big system without C++? -Paul Glick", "adl\x01\x8fU\n\x0f"},
+ {0xd0201df6, "'Invariant assertions' is the most elegant programming technique! -Tom Szymanski", "adl\x01/\x98\x0e\xc4"},
+ {0x211297c8, strings.Repeat("\xff", 5548) + "8", "adl\x01\x9a\xa6\xcb\xc1"},
+ {0xbaa198c8, strings.Repeat("\xff", 5549) + "9", "adl\x01gu\xcc\xc0"},
+ {0x553499be, strings.Repeat("\xff", 5550) + "0", "adl\x01gu\xcc\xc0"},
+ {0xf0c19abe, strings.Repeat("\xff", 5551) + "1", "adl\x015CͿ"},
+ {0x8d5c9bbe, strings.Repeat("\xff", 5552) + "2", "adl\x015CͿ"},
+ {0x2af69cbe, strings.Repeat("\xff", 5553) + "3", "adl\x01\x04\x10ξ"},
+ {0xc9809dbe, strings.Repeat("\xff", 5554) + "4", "adl\x01\x04\x10ξ"},
+ {0x69189ebe, strings.Repeat("\xff", 5555) + "5", "adl\x01\xd3\xcdϽ"},
+ {0x86af0001, strings.Repeat("\x00", 1e5), "adl\x01\xc3P\x00\x01"},
+ {0x79660b4d, strings.Repeat("a", 1e5), "adl\x01\x81k\x05\xa7"},
+ {0x110588ee, strings.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1e4), "adl\x01e\xd2\xc4p"},
}
// checksum is a slow but simple implementation of the Adler-32 checksum.
@@ -87,6 +90,38 @@ func TestGolden(t *testing.T) {
}
}
+func TestGoldenMarshal(t *testing.T) {
+ for _, g := range golden {
+ h := New()
+ h2 := New()
+
+ io.WriteString(h, g.in[:len(g.in)/2])
+
+ state, err := h.(encoding.BinaryMarshaler).MarshalBinary()
+ if err != nil {
+ t.Errorf("could not marshal: %v", err)
+ continue
+ }
+
+ if string(state) != g.halfState {
+ t.Errorf("checksum(%q) state = %q, want %q", g.in, state, g.halfState)
+ continue
+ }
+
+ if err := h2.(encoding.BinaryUnmarshaler).UnmarshalBinary(state); err != nil {
+ t.Errorf("could not unmarshal: %v", err)
+ continue
+ }
+
+ io.WriteString(h, g.in[len(g.in)/2:])
+ io.WriteString(h2, g.in[len(g.in)/2:])
+
+ if h.Sum32() != h2.Sum32() {
+ t.Errorf("checksum(%q) = 0x%x != marshaled (0x%x)", g.in, h.Sum32(), h2.Sum32())
+ }
+ }
+}
+
func BenchmarkAdler32KB(b *testing.B) {
b.SetBytes(1024)
data := make([]byte, 1024)
diff --git a/src/hash/crc32/crc32.go b/src/hash/crc32/crc32.go
index 8aa91b17e9..db05f124c4 100644
--- a/src/hash/crc32/crc32.go
+++ b/src/hash/crc32/crc32.go
@@ -13,6 +13,7 @@
package crc32
import (
+ "errors"
"hash"
"sync"
)
@@ -159,6 +160,48 @@ func (d *digest) BlockSize() int { return 1 }
func (d *digest) Reset() { d.crc = 0 }
+const (
+ magic = "crc\x01"
+ marshaledSize = len(magic) + 4 + 4
+)
+
+func (d *digest) MarshalBinary() ([]byte, error) {
+ b := make([]byte, 0, marshaledSize)
+ b = append(b, magic...)
+ b = appendUint32(b, tableSum(d.tab))
+ b = appendUint32(b, d.crc)
+ return b, nil
+}
+
+func (d *digest) UnmarshalBinary(b []byte) error {
+ if len(b) < len(magic) || string(b[:len(magic)]) != magic {
+ return errors.New("hash/crc32: invalid hash state identifier")
+ }
+ if len(b) != marshaledSize {
+ return errors.New("hash/crc32: invalid hash state size")
+ }
+ if tableSum(d.tab) != readUint32(b[4:]) {
+ return errors.New("hash/crc32: tables do not match")
+ }
+ d.crc = readUint32(b[8:])
+ return nil
+}
+
+func appendUint32(b []byte, x uint32) []byte {
+ a := [4]byte{
+ byte(x >> 24),
+ byte(x >> 16),
+ byte(x >> 8),
+ byte(x),
+ }
+ return append(b, a[:]...)
+}
+
+func readUint32(b []byte) uint32 {
+ _ = b[3]
+ return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
+}
+
// Update returns the result of adding the bytes in p to the crc.
func Update(crc uint32, tab *Table, p []byte) uint32 {
switch tab {
@@ -205,3 +248,15 @@ func ChecksumIEEE(data []byte) uint32 {
ieeeOnce.Do(ieeeInit)
return updateIEEE(0, data)
}
+
+// tableSum returns the IEEE checksum of table t.
+func tableSum(t *Table) uint32 {
+ var a [1024]byte
+ b := a[:0]
+ if t != nil {
+ for _, x := range t {
+ b = appendUint32(b, x)
+ }
+ }
+ return ChecksumIEEE(b)
+}
diff --git a/src/hash/crc32/crc32_test.go b/src/hash/crc32/crc32_test.go
index 0492f46e8c..4bdafaf8f5 100644
--- a/src/hash/crc32/crc32_test.go
+++ b/src/hash/crc32/crc32_test.go
@@ -5,49 +5,53 @@
package crc32
import (
+ "encoding"
"fmt"
"hash"
+ "io"
"math/rand"
"testing"
)
type test struct {
- ieee, castagnoli uint32
- in string
+ ieee, castagnoli uint32
+ in string
+ halfStateIEEE string // IEEE marshaled hash state after first half of in written, used by TestGoldenMarshal
+ halfStateCastagnoli string // Castagnoli marshaled hash state after first half of in written, used by TestGoldenMarshal
}
var golden = []test{
- {0x0, 0x0, ""},
- {0xe8b7be43, 0xc1d04330, "a"},
- {0x9e83486d, 0xe2a22936, "ab"},
- {0x352441c2, 0x364b3fb7, "abc"},
- {0xed82cd11, 0x92c80a31, "abcd"},
- {0x8587d865, 0xc450d697, "abcde"},
- {0x4b8e39ef, 0x53bceff1, "abcdef"},
- {0x312a6aa6, 0xe627f441, "abcdefg"},
- {0xaeef2a50, 0xa9421b7, "abcdefgh"},
- {0x8da988af, 0x2ddc99fc, "abcdefghi"},
- {0x3981703a, 0xe6599437, "abcdefghij"},
- {0x6b9cdfe7, 0xb2cc01fe, "Discard medicine more than two years old."},
- {0xc90ef73f, 0xe28207f, "He who has a shady past knows that nice guys finish last."},
- {0xb902341f, 0xbe93f964, "I wouldn't marry him with a ten foot pole."},
- {0x42080e8, 0x9e3be0c3, "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"},
- {0x154c6d11, 0xf505ef04, "The days of the digital watch are numbered. -Tom Stoppard"},
- {0x4c418325, 0x85d3dc82, "Nepal premier won't resign."},
- {0x33955150, 0xc5142380, "For every action there is an equal and opposite government program."},
- {0x26216a4b, 0x75eb77dd, "His money is twice tainted: 'taint yours and 'taint mine."},
- {0x1abbe45e, 0x91ebe9f7, "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"},
- {0xc89a94f7, 0xf0b1168e, "It's a tiny change to the code and not completely disgusting. - Bob Manchek"},
- {0xab3abe14, 0x572b74e2, "size: a.out: bad magic"},
- {0xbab102b6, 0x8a58a6d5, "The major problem is with sendmail. -Mark Horton"},
- {0x999149d7, 0x9c426c50, "Give me a rock, paper and scissors and I will move the world. CCFestoon"},
- {0x6d52a33c, 0x735400a4, "If the enemy is within range, then so are you."},
- {0x90631e8d, 0xbec49c95, "It's well we cannot hear the screams/That we create in others' dreams."},
- {0x78309130, 0xa95a2079, "You remind me of a TV show, but that's all right: I watch it anyway."},
- {0x7d0a377f, 0xde2e65c5, "C is as portable as Stonehedge!!"},
- {0x8c79fd79, 0x297a88ed, "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"},
- {0xa20b7167, 0x66ed1d8b, "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"},
- {0x8e0bb443, 0xdcded527, "How can you write a big system without C++? -Paul Glick"},
+ {0x0, 0x0, "", "crc\x01ʇ\x91M\x00\x00\x00\x00", "crc\x01wB\x84\x81\x00\x00\x00\x00"},
+ {0xe8b7be43, 0xc1d04330, "a", "crc\x01ʇ\x91M\x00\x00\x00\x00", "crc\x01wB\x84\x81\x00\x00\x00\x00"},
+ {0x9e83486d, 0xe2a22936, "ab", "crc\x01ʇ\x91M跾C", "crc\x01wB\x84\x81\xc1\xd0C0"},
+ {0x352441c2, 0x364b3fb7, "abc", "crc\x01ʇ\x91M跾C", "crc\x01wB\x84\x81\xc1\xd0C0"},
+ {0xed82cd11, 0x92c80a31, "abcd", "crc\x01ʇ\x91M\x9e\x83Hm", "crc\x01wB\x84\x81\xe2\xa2)6"},
+ {0x8587d865, 0xc450d697, "abcde", "crc\x01ʇ\x91M\x9e\x83Hm", "crc\x01wB\x84\x81\xe2\xa2)6"},
+ {0x4b8e39ef, 0x53bceff1, "abcdef", "crc\x01ʇ\x91M5$A\xc2", "crc\x01wB\x84\x816K?\xb7"},
+ {0x312a6aa6, 0xe627f441, "abcdefg", "crc\x01ʇ\x91M5$A\xc2", "crc\x01wB\x84\x816K?\xb7"},
+ {0xaeef2a50, 0xa9421b7, "abcdefgh", "crc\x01ʇ\x91M\xed\x82\xcd\x11", "crc\x01wB\x84\x81\x92\xc8\n1"},
+ {0x8da988af, 0x2ddc99fc, "abcdefghi", "crc\x01ʇ\x91M\xed\x82\xcd\x11", "crc\x01wB\x84\x81\x92\xc8\n1"},
+ {0x3981703a, 0xe6599437, "abcdefghij", "crc\x01ʇ\x91M\x85\x87\xd8e", "crc\x01wB\x84\x81\xc4P֗"},
+ {0x6b9cdfe7, 0xb2cc01fe, "Discard medicine more than two years old.", "crc\x01ʇ\x91M\xfd\xe5\xc2J", "crc\x01wB\x84\x81S\"(\xe0"},
+ {0xc90ef73f, 0xe28207f, "He who has a shady past knows that nice guys finish last.", "crc\x01ʇ\x91M\x01Nj+", "crc\x01wB\x84\x81'\xdaR\x15"},
+ {0xb902341f, 0xbe93f964, "I wouldn't marry him with a ten foot pole.", "crc\x01ʇ\x91M\x9d\x13\xce\x10", "crc\x01wB\x84\x81\xc3\xed\xabG"},
+ {0x42080e8, 0x9e3be0c3, "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave", "crc\x01ʇ\x91M-\xed\xf7\x94", "crc\x01wB\x84\x81\xce\xceb\x81"},
+ {0x154c6d11, 0xf505ef04, "The days of the digital watch are numbered. -Tom Stoppard", "crc\x01ʇ\x91MOa\xa5\r", "crc\x01wB\x84\x81\xd3s\x9dP"},
+ {0x4c418325, 0x85d3dc82, "Nepal premier won't resign.", "crc\x01ʇ\x91M\xa8S9\x85", "crc\x01wB\x84\x81{\x90\x8a\x14"},
+ {0x33955150, 0xc5142380, "For every action there is an equal and opposite government program.", "crc\x01ʇ\x91Ma\xe9>\x86", "crc\x01wB\x84\x81\xaa@\xc4\x1c"},
+ {0x26216a4b, 0x75eb77dd, "His money is twice tainted: 'taint yours and 'taint mine.", "crc\x01ʇ\x91M\\\x1an\x88", "crc\x01wB\x84\x81W\a8Z"},
+ {0x1abbe45e, 0x91ebe9f7, "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977", "crc\x01ʇ\x91M\xb7\xf5\xf2\xca", "crc\x01wB\x84\x81\xc4o\x9d\x85"},
+ {0xc89a94f7, 0xf0b1168e, "It's a tiny change to the code and not completely disgusting. - Bob Manchek", "crc\x01ʇ\x91M\x84g1\xe8", "crc\x01wB\x84\x81#\x98\f\xab"},
+ {0xab3abe14, 0x572b74e2, "size: a.out: bad magic", "crc\x01ʇ\x91M\x8a\x0f\xad\b", "crc\x01wB\x84\x81\x80\xc9n\xd8"},
+ {0xbab102b6, 0x8a58a6d5, "The major problem is with sendmail. -Mark Horton", "crc\x01ʇ\x91M\a\xf0\xb3\x15", "crc\x01wB\x84\x81liS\xcc"},
+ {0x999149d7, 0x9c426c50, "Give me a rock, paper and scissors and I will move the world. CCFestoon", "crc\x01ʇ\x91M\x0fa\xbc.", "crc\x01wB\x84\x81\xdb͏C"},
+ {0x6d52a33c, 0x735400a4, "If the enemy is within range, then so are you.", "crc\x01ʇ\x91My\x1b\x99\xf8", "crc\x01wB\x84\x81\xaaB\x037"},
+ {0x90631e8d, 0xbec49c95, "It's well we cannot hear the screams/That we create in others' dreams.", "crc\x01ʇ\x91M\bqfY", "crc\x01wB\x84\x81\x16y\xa1\xd2"},
+ {0x78309130, 0xa95a2079, "You remind me of a TV show, but that's all right: I watch it anyway.", "crc\x01ʇ\x91M\xbdO,\xc2", "crc\x01wB\x84\x81f&\xc5\xe4"},
+ {0x7d0a377f, 0xde2e65c5, "C is as portable as Stonehedge!!", "crc\x01ʇ\x91M\xf7\xd6\x00\xd5", "crc\x01wB\x84\x81de\\\xf8"},
+ {0x8c79fd79, 0x297a88ed, "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley", "crc\x01ʇ\x91Ml+\xb8\xa7", "crc\x01wB\x84\x81\xbf\xd6S\xdd"},
+ {0xa20b7167, 0x66ed1d8b, "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule", "crc\x01ʇ\x91M<lR[", "crc\x01wB\x84\x81{\xaco\xb1"},
+ {0x8e0bb443, 0xdcded527, "How can you write a big system without C++? -Paul Glick", "crc\x01ʇ\x91M\x0e\x88\x89\xed", "crc\x01wB\x84\x813\xd7C\u007f"},
}
// testGoldenIEEE verifies that the given function returns
@@ -105,6 +109,86 @@ func TestSimple(t *testing.T) {
})
}
+func TestGoldenMarshal(t *testing.T) {
+ t.Run("IEEE", func(t *testing.T) {
+ for _, g := range golden {
+ h := New(IEEETable)
+ h2 := New(IEEETable)
+
+ io.WriteString(h, g.in[:len(g.in)/2])
+
+ state, err := h.(encoding.BinaryMarshaler).MarshalBinary()
+ if err != nil {
+ t.Errorf("could not marshal: %v", err)
+ continue
+ }
+
+ if string(state) != g.halfStateIEEE {
+ t.Errorf("IEEE(%q) state = %q, want %q", g.in, state, g.halfStateIEEE)
+ continue
+ }
+
+ if err := h2.(encoding.BinaryUnmarshaler).UnmarshalBinary(state); err != nil {
+ t.Errorf("could not unmarshal: %v", err)
+ continue
+ }
+
+ io.WriteString(h, g.in[len(g.in)/2:])
+ io.WriteString(h2, g.in[len(g.in)/2:])
+
+ if h.Sum32() != h2.Sum32() {
+ t.Errorf("IEEE(%s) = 0x%x != marshaled 0x%x", g.in, h.Sum32(), h2.Sum32())
+ }
+ }
+ })
+ t.Run("Castagnoli", func(t *testing.T) {
+ table := MakeTable(Castagnoli)
+ for _, g := range golden {
+ h := New(table)
+ h2 := New(table)
+
+ io.WriteString(h, g.in[:len(g.in)/2])
+
+ state, err := h.(encoding.BinaryMarshaler).MarshalBinary()
+ if err != nil {
+ t.Errorf("could not marshal: %v", err)
+ continue
+ }
+
+ if string(state) != g.halfStateCastagnoli {
+ t.Errorf("Castagnoli(%q) state = %q, want %q", g.in, state, g.halfStateCastagnoli)
+ continue
+ }
+
+ if err := h2.(encoding.BinaryUnmarshaler).UnmarshalBinary(state); err != nil {
+ t.Errorf("could not unmarshal: %v", err)
+ continue
+ }
+
+ io.WriteString(h, g.in[len(g.in)/2:])
+ io.WriteString(h2, g.in[len(g.in)/2:])
+
+ if h.Sum32() != h2.Sum32() {
+ t.Errorf("Castagnoli(%s) = 0x%x != marshaled 0x%x", g.in, h.Sum32(), h2.Sum32())
+ }
+ }
+ })
+}
+
+func TestMarshalTableMismatch(t *testing.T) {
+ h1 := New(IEEETable)
+ h2 := New(MakeTable(Castagnoli))
+
+ state1, err := h1.(encoding.BinaryMarshaler).MarshalBinary()
+ if err != nil {
+ t.Errorf("could not marshal: %v", err)
+ }
+
+ if err := h2.(encoding.BinaryUnmarshaler).UnmarshalBinary(state1); err == nil {
+ t.Errorf("no error when one was expected")
+ }
+}
+
// TestSimple tests the slicing-by-8 algorithm.
func TestSlicing(t *testing.T) {
tab := slicingMakeTable(IEEE)
diff --git a/src/hash/crc64/crc64.go b/src/hash/crc64/crc64.go
index e939c2a06a..54c942c97a 100644
--- a/src/hash/crc64/crc64.go
+++ b/src/hash/crc64/crc64.go
@@ -7,7 +7,10 @@
// information.
package crc64
-import "hash"
+import (
+ "errors"
+ "hash"
+)
// The size of a CRC-64 checksum in bytes.
const Size = 8
@@ -88,6 +91,53 @@ func (d *digest) BlockSize() int { return 1 }
func (d *digest) Reset() { d.crc = 0 }
+const (
+ magic = "crc\x02"
+ marshaledSize = len(magic) + 8 + 8
+)
+
+func (d *digest) MarshalBinary() ([]byte, error) {
+ b := make([]byte, 0, marshaledSize)
+ b = append(b, magic...)
+ b = appendUint64(b, tableSum(d.tab))
+ b = appendUint64(b, d.crc)
+ return b, nil
+}
+
+func (d *digest) UnmarshalBinary(b []byte) error {
+ 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 {
crc = ^crc
// Table comparison is somewhat expensive, so avoid it for small sizes
@@ -145,3 +195,15 @@ func (d *digest) Sum(in []byte) []byte {
// 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 {
+ var a [2048]byte
+ b := a[:0]
+ if t != nil {
+ for _, x := range t {
+ b = appendUint64(b, x)
+ }
+ }
+ return Checksum(b, MakeTable(ISO))
+}
diff --git a/src/hash/crc64/crc64_test.go b/src/hash/crc64/crc64_test.go
index 480b150e13..9db05b02fe 100644
--- a/src/hash/crc64/crc64_test.go
+++ b/src/hash/crc64/crc64_test.go
@@ -5,49 +5,52 @@
package crc64
import (
+ "encoding"
"io"
"testing"
)
type test struct {
- outISO uint64
- outECMA uint64
- in string
+ outISO uint64
+ outECMA uint64
+ in string
+ halfStateISO string // ISO marshaled hash state after first half of in written, used by TestGoldenMarshal
+ halfStateECMA string // ECMA marshaled hash state after first half of in written, used by TestGoldenMarshal
}
var golden = []test{
- {0x0, 0x0, ""},
- {0x3420000000000000, 0x330284772e652b05, "a"},
- {0x36c4200000000000, 0xbc6573200e84b046, "ab"},
- {0x3776c42000000000, 0x2cd8094a1a277627, "abc"},
- {0x336776c420000000, 0x3c9d28596e5960ba, "abcd"},
- {0x32d36776c4200000, 0x40bdf58fb0895f2, "abcde"},
- {0x3002d36776c42000, 0xd08e9f8545a700f4, "abcdef"},
- {0x31b002d36776c420, 0xec20a3a8cc710e66, "abcdefg"},
- {0xe21b002d36776c4, 0x67b4f30a647a0c59, "abcdefgh"},
- {0x8b6e21b002d36776, 0x9966f6c89d56ef8e, "abcdefghi"},
- {0x7f5b6e21b002d367, 0x32093a2ecd5773f4, "abcdefghij"},
- {0x8ec0e7c835bf9cdf, 0x8a0825223ea6d221, "Discard medicine more than two years old."},
- {0xc7db1759e2be5ab4, 0x8562c0ac2ab9a00d, "He who has a shady past knows that nice guys finish last."},
- {0xfbf9d9603a6fa020, 0x3ee2a39c083f38b4, "I wouldn't marry him with a ten foot pole."},
- {0xeafc4211a6daa0ef, 0x1f603830353e518a, "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"},
- {0x3e05b21c7a4dc4da, 0x2fd681d7b2421fd, "The days of the digital watch are numbered. -Tom Stoppard"},
- {0x5255866ad6ef28a6, 0x790ef2b16a745a41, "Nepal premier won't resign."},
- {0x8a79895be1e9c361, 0x3ef8f06daccdcddf, "For every action there is an equal and opposite government program."},
- {0x8878963a649d4916, 0x49e41b2660b106d, "His money is twice tainted: 'taint yours and 'taint mine."},
- {0xa7b9d53ea87eb82f, 0x561cc0cfa235ac68, "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"},
- {0xdb6805c0966a2f9c, 0xd4fe9ef082e69f59, "It's a tiny change to the code and not completely disgusting. - Bob Manchek"},
- {0xf3553c65dacdadd2, 0xe3b5e46cd8d63a4d, "size: a.out: bad magic"},
- {0x9d5e034087a676b9, 0x865aaf6b94f2a051, "The major problem is with sendmail. -Mark Horton"},
- {0xa6db2d7f8da96417, 0x7eca10d2f8136eb4, "Give me a rock, paper and scissors and I will move the world. CCFestoon"},
- {0x325e00cd2fe819f9, 0xd7dd118c98e98727, "If the enemy is within range, then so are you."},
- {0x88c6600ce58ae4c6, 0x70fb33c119c29318, "It's well we cannot hear the screams/That we create in others' dreams."},
- {0x28c4a3f3b769e078, 0x57c891e39a97d9b7, "You remind me of a TV show, but that's all right: I watch it anyway."},
- {0xa698a34c9d9f1dca, 0xa1f46ba20ad06eb7, "C is as portable as Stonehedge!!"},
- {0xf6c1e2a8c26c5cfc, 0x7ad25fafa1710407, "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"},
- {0xd402559dfe9b70c, 0x73cef1666185c13f, "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"},
- {0xdb6efff26aa94946, 0xb41858f73c389602, "How can you write a big system without C++? -Paul Glick"},
- {0xe7fcf1006b503b61, 0x27db187fc15bbc72, "This is a test of the emergency broadcast system."},
+ {0x0, 0x0, "", "crc\x02s\xba\x84\x84\xbb\xcd]\xef\x00\x00\x00\x00\x00\x00\x00\x00", "crc\x02`&\x9aR\xe1\xb7\xfee\x00\x00\x00\x00\x00\x00\x00\x00"},
+ {0x3420000000000000, 0x330284772e652b05, "a", "crc\x02s\xba\x84\x84\xbb\xcd]\xef\x00\x00\x00\x00\x00\x00\x00\x00", "crc\x02`&\x9aR\xe1\xb7\xfee\x00\x00\x00\x00\x00\x00\x00\x00"},
+ {0x36c4200000000000, 0xbc6573200e84b046, "ab", "crc\x02s\xba\x84\x84\xbb\xcd]\xef4 \x00\x00\x00\x00\x00\x00", "crc\x02`&\x9aR\xe1\xb7\xfee3\x02\x84w.e+\x05"},
+ {0x3776c42000000000, 0x2cd8094a1a277627, "abc", "crc\x02s\xba\x84\x84\xbb\xcd]\xef4 \x00\x00\x00\x00\x00\x00", "crc\x02`&\x9aR\xe1\xb7\xfee3\x02\x84w.e+\x05"},
+ {0x336776c420000000, 0x3c9d28596e5960ba, "abcd", "crc\x02s\xba\x84\x84\xbb\xcd]\xef6\xc4 \x00\x00\x00\x00\x00", "crc\x02`&\x9aR\xe1\xb7\xfee\xbces \x0e\x84\xb0F"},
+ {0x32d36776c4200000, 0x40bdf58fb0895f2, "abcde", "crc\x02s\xba\x84\x84\xbb\xcd]\xef6\xc4 \x00\x00\x00\x00\x00", "crc\x02`&\x9aR\xe1\xb7\xfee\xbces \x0e\x84\xb0F"},
+ {0x3002d36776c42000, 0xd08e9f8545a700f4, "abcdef", "crc\x02s\xba\x84\x84\xbb\xcd]\xef7v\xc4 \x00\x00\x00\x00", "crc\x02`&\x9aR\xe1\xb7\xfee,\xd8\tJ\x1a'v'"},
+ {0x31b002d36776c420, 0xec20a3a8cc710e66, "abcdefg", "crc\x02s\xba\x84\x84\xbb\xcd]\xef7v\xc4 \x00\x00\x00\x00", "crc\x02`&\x9aR\xe1\xb7\xfee,\xd8\tJ\x1a'v'"},
+ {0xe21b002d36776c4, 0x67b4f30a647a0c59, "abcdefgh", "crc\x02s\xba\x84\x84\xbb\xcd]\xef3gv\xc4 \x00\x00\x00", "crc\x02`&\x9aR\xe1\xb7\xfee<\x9d(YnY`\xba"},
+ {0x8b6e21b002d36776, 0x9966f6c89d56ef8e, "abcdefghi", "crc\x02s\xba\x84\x84\xbb\xcd]\xef3gv\xc4 \x00\x00\x00", "crc\x02`&\x9aR\xe1\xb7\xfee<\x9d(YnY`\xba"},
+ {0x7f5b6e21b002d367, 0x32093a2ecd5773f4, "abcdefghij", "crc\x02s\xba\x84\x84\xbb\xcd]\xef2\xd3gv\xc4 \x00\x00", "crc\x02`&\x9aR\xe1\xb7\xfee\x04\v\xdfX\xfb\b\x95\xf2"},
+ {0x8ec0e7c835bf9cdf, 0x8a0825223ea6d221, "Discard medicine more than two years old.", "crc\x02s\xba\x84\x84\xbb\xcd]\xef\xc6\xc0\f\xac'\x11\x12\xd5", "crc\x02`&\x9aR\xe1\xb7\xfee\xfd%\xc0&\xa0R\xef\x95"},
+ {0xc7db1759e2be5ab4, 0x8562c0ac2ab9a00d, "He who has a shady past knows that nice guys finish last.", "crc\x02s\xba\x84\x84\xbb\xcd]\xef\t\xcb\xd15X[r\t", "crc\x02`&\x9aR\xe1\xb7\xfee\a\x02\xe8|+\xc1\x06\xe3"},
+ {0xfbf9d9603a6fa020, 0x3ee2a39c083f38b4, "I wouldn't marry him with a ten foot pole.", "crc\x02s\xba\x84\x84\xbb\xcd]\xef\x19\xc8d\xbe\x84\x14\x87_", "crc\x02`&\x9aR\xe1\xb7\xfee˷\xd3\xeeG\xdcE\x8c"},
+ {0xeafc4211a6daa0ef, 0x1f603830353e518a, "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave", "crc\x02s\xba\x84\x84\xbb\xcd]\xef\xad\x1b*\xc0\xb1\xf3i(", "crc\x02`&\x9aR\xe1\xb7\xfee\xa7\x8a\xdb\xf6\xd2R\t\x96"},
+ {0x3e05b21c7a4dc4da, 0x2fd681d7b2421fd, "The days of the digital watch are numbered. -Tom Stoppard", "crc\x02s\xba\x84\x84\xbb\xcd]\xefv78\x1ak\x02\x8f\xff", "crc\x02`&\x9aR\xe1\xb7\xfeeT\xcbl\x10\xfb\x87K*"},
+ {0x5255866ad6ef28a6, 0x790ef2b16a745a41, "Nepal premier won't resign.", "crc\x02s\xba\x84\x84\xbb\xcd]\xef\xcbf\x11R\xbfh\xde\xc9", "crc\x02`&\x9aR\xe1\xb7\xfee6\x13ُ\x06_\xbd\x9a"},
+ {0x8a79895be1e9c361, 0x3ef8f06daccdcddf, "For every action there is an equal and opposite government program.", "crc\x02s\xba\x84\x84\xbb\xcd]\xef\xf3pV\x01c_Wu", "crc\x02`&\x9aR\xe1\xb7\xfee\xe7\xc6\n\b\x12FL\xa0"},
+ {0x8878963a649d4916, 0x49e41b2660b106d, "His money is twice tainted: 'taint yours and 'taint mine.", "crc\x02s\xba\x84\x84\xbb\xcd]\xefñ\xff\xf1\xe0/Δ", "crc\x02`&\x9aR\xe1\xb7\xfeeOL/\xb1\xec\xa2\x14\x87"},
+ {0xa7b9d53ea87eb82f, 0x561cc0cfa235ac68, "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977", "crc\x02s\xba\x84\x84\xbb\xcd]\xefݸa\xe1\xb5\xf8\xb9W", "crc\x02`&\x9aR\xe1\xb7\xfee\x87)GQ\x03\xf4K\t"},
+ {0xdb6805c0966a2f9c, 0xd4fe9ef082e69f59, "It's a tiny change to the code and not completely disgusting. - Bob Manchek", "crc\x02s\xba\x84\x84\xbb\xcd]\xefV\xba\x12\x91\x81\x1fNU", "crc\x02`&\x9aR\xe1\xb7\xfee\n\xb8\x81v?\xdeL\xcb"},
+ {0xf3553c65dacdadd2, 0xe3b5e46cd8d63a4d, "size: a.out: bad magic", "crc\x02s\xba\x84\x84\xbb\xcd]\xefG\xad\xbc\xb2\xa8y\xc9\xdc", "crc\x02`&\x9aR\xe1\xb7\xfee\xcc\xce\xe5\xe6\x89p\x01\xb8"},
+ {0x9d5e034087a676b9, 0x865aaf6b94f2a051, "The major problem is with sendmail. -Mark Horton", "crc\x02s\xba\x84\x84\xbb\xcd]\uf8acn\x8aT;&\xd5", "crc\x02`&\x9aR\xe1\xb7\xfeeFf\x9c\x1f\xc9x\xbfa"},
+ {0xa6db2d7f8da96417, 0x7eca10d2f8136eb4, "Give me a rock, paper and scissors and I will move the world. CCFestoon", "crc\x02s\xba\x84\x84\xbb\xcd]\xef\xeb\x18\xbf\xf9}\x91\xe5|", "crc\x02`&\x9aR\xe1\xb7\xfeea\x9e\x05:\xce[\xe7\x19"},
+ {0x325e00cd2fe819f9, 0xd7dd118c98e98727, "If the enemy is within range, then so are you.", "crc\x02s\xba\x84\x84\xbb\xcd]\xef^5k\xd0Aj_{", "crc\x02`&\x9aR\xe1\xb7\xfee\v#\x99\xa8r\x83YR"},
+ {0x88c6600ce58ae4c6, 0x70fb33c119c29318, "It's well we cannot hear the screams/That we create in others' dreams.", "crc\x02s\xba\x84\x84\xbb\xcd]\xef|\xb5\x02\xdcw\x18/\x86", "crc\x02`&\x9aR\xe1\xb7\xfee]\x9d-\xed\x8c\xf9r9"},
+ {0x28c4a3f3b769e078, 0x57c891e39a97d9b7, "You remind me of a TV show, but that's all right: I watch it anyway.", "crc\x02s\xba\x84\x84\xbb\xcd]\xef\x03\x8bd\x1c\xb0_\x16\x98", "crc\x02`&\x9aR\xe1\xb7\xfee\xafW\x98\xaa\"\xe7\xd7|"},
+ {0xa698a34c9d9f1dca, 0xa1f46ba20ad06eb7, "C is as portable as Stonehedge!!", "crc\x02s\xba\x84\x84\xbb\xcd]\xef.P\xe1I\xc6pi\xdc", "crc\x02`&\x9aR\xe1\xb7\xfee֚\x06\x01(\xc0\x1e\x8b"},
+ {0xf6c1e2a8c26c5cfc, 0x7ad25fafa1710407, "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley", "crc\x02s\xba\x84\x84\xbb\xcd]\xef\xf7\xa04\x8a\xf2o\xe0;", "crc\x02`&\x9aR\xe1\xb7\xfee<[\xd2%\x9em\x94\x04"},
+ {0xd402559dfe9b70c, 0x73cef1666185c13f, "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule", "crc\x02s\xba\x84\x84\xbb\xcd]\xef\u007f\xae\xb9\xbaX=\x19v", "crc\x02`&\x9aR\xe1\xb7\xfee\xb2˦Y\xc5\xd0G\x03"},
+ {0xdb6efff26aa94946, 0xb41858f73c389602, "How can you write a big system without C++? -Paul Glick", "crc\x02s\xba\x84\x84\xbb\xcd]\xefa\xed$js\xb9\xa5A", "crc\x02`&\x9aR\xe1\xb7\xfeeZm\x96\x8a\xe2\xaf\x13p"},
+ {0xe7fcf1006b503b61, 0x27db187fc15bbc72, "This is a test of the emergency broadcast system.", "crc\x02s\xba\x84\x84\xbb\xcd]\xef}\xee[q\x16\xcb\xe4\x8d", "crc\x02`&\x9aR\xe1\xb7\xfee\xb1\x93] \xeb\xa9am"},
}
func TestGolden(t *testing.T) {
@@ -72,6 +75,87 @@ func TestGolden(t *testing.T) {
}
}
+func TestGoldenMarshal(t *testing.T) {
+ t.Run("ISO", func(t *testing.T) {
+ table := MakeTable(ISO)
+ for _, g := range golden {
+ h := New(table)
+ h2 := New(table)
+
+ io.WriteString(h, g.in[:len(g.in)/2])
+
+ state, err := h.(encoding.BinaryMarshaler).MarshalBinary()
+ if err != nil {
+ t.Errorf("could not marshal: %v", err)
+ continue
+ }
+
+ if string(state) != g.halfStateISO {
+ t.Errorf("ISO crc64(%q) state = %q, want %q", g.in, state, g.halfStateISO)
+ continue
+ }
+
+ if err := h2.(encoding.BinaryUnmarshaler).UnmarshalBinary(state); err != nil {
+ t.Errorf("could not unmarshal: %v", err)
+ continue
+ }
+
+ io.WriteString(h, g.in[len(g.in)/2:])
+ io.WriteString(h2, g.in[len(g.in)/2:])
+
+ if h.Sum64() != h2.Sum64() {
+ t.Errorf("ISO crc64(%s) = 0x%x != marshaled (0x%x)", g.in, h.Sum64(), h2.Sum64())
+ }
+ }
+ })
+ t.Run("ECMA", func(t *testing.T) {
+ table := MakeTable(ECMA)
+ for _, g := range golden {
+ h := New(table)
+ h2 := New(table)
+
+ io.WriteString(h, g.in[:len(g.in)/2])
+
+ state, err := h.(encoding.BinaryMarshaler).MarshalBinary()
+ if err != nil {
+ t.Errorf("could not marshal: %v", err)
+ continue
+ }
+
+ if string(state) != g.halfStateECMA {
+ t.Errorf("ECMA crc64(%q) state = %q, want %q", g.in, state, g.halfStateECMA)
+ continue
+ }
+
+ if err := h2.(encoding.BinaryUnmarshaler).UnmarshalBinary(state); err != nil {
+ t.Errorf("could not unmarshal: %v", err)
+ continue
+ }
+
+ io.WriteString(h, g.in[len(g.in)/2:])
+ io.WriteString(h2, g.in[len(g.in)/2:])
+
+ if h.Sum64() != h2.Sum64() {
+ t.Errorf("ECMA crc64(%s) = 0x%x != marshaled (0x%x)", g.in, h.Sum64(), h2.Sum64())
+ }
+ }
+ })
+}
+
+func TestMarshalTableMismatch(t *testing.T) {
+ h1 := New(MakeTable(ISO))
+ h2 := New(MakeTable(ECMA))
+
+ state1, err := h1.(encoding.BinaryMarshaler).MarshalBinary()
+ if err != nil {
+ t.Errorf("could not marshal: %v", err)
+ }
+
+ if err := h2.(encoding.BinaryUnmarshaler).UnmarshalBinary(state1); err == nil {
+ t.Errorf("no error when one was expected")
+ }
+}
+
func bench(b *testing.B, poly uint64, size int64) {
b.SetBytes(size)
data := make([]byte, size)
diff --git a/src/hash/fnv/fnv.go b/src/hash/fnv/fnv.go
index 3d2df73d3d..99c892000b 100644
--- a/src/hash/fnv/fnv.go
+++ b/src/hash/fnv/fnv.go
@@ -9,6 +9,7 @@
package fnv
import (
+ "errors"
"hash"
)
@@ -215,3 +216,163 @@ func (s *sum128a) Sum(in []byte) []byte {
byte(s[1]>>56), byte(s[1]>>48), byte(s[1]>>40), byte(s[1]>>32), byte(s[1]>>24), byte(s[1]>>16), byte(s[1]>>8), byte(s[1]),
)
}
+
+const (
+ magic32 = "fnv\x01"
+ magic32a = "fnv\x02"
+ magic64 = "fnv\x03"
+ magic64a = "fnv\x04"
+ magic128 = "fnv\x05"
+ magic128a = "fnv\x06"
+ marshaledSize32 = len(magic32) + 4
+ marshaledSize64 = len(magic64) + 8
+ marshaledSize128 = len(magic128) + 8*2
+)
+
+func (s *sum32) MarshalBinary() ([]byte, error) {
+ b := make([]byte, 0, marshaledSize32)
+ b = append(b, magic32...)
+ b = appendUint32(b, uint32(*s))
+ return b, nil
+}
+
+func (s *sum32a) MarshalBinary() ([]byte, error) {
+ b := make([]byte, 0, marshaledSize32)
+ b = append(b, magic32a...)
+ b = appendUint32(b, uint32(*s))
+ return b, nil
+}
+
+func (s *sum64) MarshalBinary() ([]byte, error) {
+ b := make([]byte, 0, marshaledSize64)
+ b = append(b, magic64...)
+ b = appendUint64(b, uint64(*s))
+ return b, nil
+
+}
+
+func (s *sum64a) MarshalBinary() ([]byte, error) {
+ b := make([]byte, 0, marshaledSize64)
+ b = append(b, magic64a...)
+ b = appendUint64(b, uint64(*s))
+ return b, nil
+}
+
+func (s *sum128) MarshalBinary() ([]byte, error) {
+ b := make([]byte, 0, marshaledSize128)
+ b = append(b, magic128...)
+ b = appendUint64(b, s[0])
+ b = appendUint64(b, s[1])
+ return b, nil
+}
+
+func (s *sum128a) MarshalBinary() ([]byte, error) {
+ b := make([]byte, 0, marshaledSize128)
+ b = append(b, magic128a...)
+ b = appendUint64(b, s[0])
+ b = appendUint64(b, s[1])
+ return b, nil
+}
+
+func (s *sum32) UnmarshalBinary(b []byte) error {
+ if len(b) < len(magic32) || string(b[:len(magic32)]) != magic32 {
+ return errors.New("hash/fnv: invalid hash state identifier")
+ }
+ if len(b) != marshaledSize32 {
+ return errors.New("hash/fnv: invalid hash state size")
+ }
+ *s = sum32(readUint32(b[4:]))
+ return nil
+}
+
+func (s *sum32a) UnmarshalBinary(b []byte) error {
+ if len(b) < len(magic32a) || string(b[:len(magic32a)]) != magic32a {
+ return errors.New("hash/fnv: invalid hash state identifier")
+ }
+ if len(b) != marshaledSize32 {
+ return errors.New("hash/fnv: invalid hash state size")
+ }
+ *s = sum32a(readUint32(b[4:]))
+ return nil
+}
+
+func (s *sum64) UnmarshalBinary(b []byte) error {
+ if len(b) < len(magic64) || string(b[:len(magic64)]) != magic64 {
+ return errors.New("hash/fnv: invalid hash state identifier")
+ }
+ if len(b) != marshaledSize64 {
+ return errors.New("hash/fnv: invalid hash state size")
+ }
+ *s = sum64(readUint64(b[4:]))
+ return nil
+}
+
+func (s *sum64a) UnmarshalBinary(b []byte) error {
+ if len(b) < len(magic64a) || string(b[:len(magic64a)]) != magic64a {
+ return errors.New("hash/fnv: invalid hash state identifier")
+ }
+ if len(b) != marshaledSize64 {
+ return errors.New("hash/fnv: invalid hash state size")
+ }
+ *s = sum64a(readUint64(b[4:]))
+ return nil
+}
+
+func (s *sum128) UnmarshalBinary(b []byte) error {
+ if len(b) < len(magic128) || string(b[:len(magic128)]) != magic128 {
+ return errors.New("hash/fnv: invalid hash state identifier")
+ }
+ if len(b) != marshaledSize128 {
+ return errors.New("hash/fnv: invalid hash state size")
+ }
+ s[0] = readUint64(b[4:])
+ s[1] = readUint64(b[12:])
+ return nil
+}
+
+func (s *sum128a) UnmarshalBinary(b []byte) error {
+ if len(b) < len(magic128a) || string(b[:len(magic128a)]) != magic128a {
+ return errors.New("hash/fnv: invalid hash state identifier")
+ }
+ if len(b) != marshaledSize128 {
+ return errors.New("hash/fnv: invalid hash state size")
+ }
+ s[0] = readUint64(b[4:])
+ s[1] = readUint64(b[12:])
+ return nil
+}
+
+func readUint32(b []byte) uint32 {
+ _ = b[3]
+ return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
+}
+
+func appendUint32(b []byte, x uint32) []byte {
+ a := [4]byte{
+ byte(x >> 24),
+ byte(x >> 16),
+ byte(x >> 8),
+ byte(x),
+ }
+ return append(b, a[:]...)
+}
+
+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
+}
diff --git a/src/hash/fnv/fnv_test.go b/src/hash/fnv/fnv_test.go
index 7da15ba9e8..7b1f7a32ea 100644
--- a/src/hash/fnv/fnv_test.go
+++ b/src/hash/fnv/fnv_test.go
@@ -6,56 +6,59 @@ package fnv
import (
"bytes"
+ "encoding"
"encoding/binary"
"hash"
+ "io"
"testing"
)
type golden struct {
- sum []byte
- text string
+ out []byte
+ in string
+ halfState string // marshaled hash state after first half of in written, used by TestGoldenMarshal
}
var golden32 = []golden{
- {[]byte{0x81, 0x1c, 0x9d, 0xc5}, ""},
- {[]byte{0x05, 0x0c, 0x5d, 0x7e}, "a"},
- {[]byte{0x70, 0x77, 0x2d, 0x38}, "ab"},
- {[]byte{0x43, 0x9c, 0x2f, 0x4b}, "abc"},
+ {[]byte{0x81, 0x1c, 0x9d, 0xc5}, "", "fnv\x01\x81\x1c\x9d\xc5"},
+ {[]byte{0x05, 0x0c, 0x5d, 0x7e}, "a", "fnv\x01\x81\x1c\x9d\xc5"},
+ {[]byte{0x70, 0x77, 0x2d, 0x38}, "ab", "fnv\x01\x05\f]~"},
+ {[]byte{0x43, 0x9c, 0x2f, 0x4b}, "abc", "fnv\x01\x05\f]~"},
}
var golden32a = []golden{
- {[]byte{0x81, 0x1c, 0x9d, 0xc5}, ""},
- {[]byte{0xe4, 0x0c, 0x29, 0x2c}, "a"},
- {[]byte{0x4d, 0x25, 0x05, 0xca}, "ab"},
- {[]byte{0x1a, 0x47, 0xe9, 0x0b}, "abc"},
+ {[]byte{0x81, 0x1c, 0x9d, 0xc5}, "", "fnv\x02\x81\x1c\x9d\xc5"},
+ {[]byte{0xe4, 0x0c, 0x29, 0x2c}, "a", "fnv\x02\x81\x1c\x9d\xc5"},
+ {[]byte{0x4d, 0x25, 0x05, 0xca}, "ab", "fnv\x02\xe4\f),"},
+ {[]byte{0x1a, 0x47, 0xe9, 0x0b}, "abc", "fnv\x02\xe4\f),"},
}
var golden64 = []golden{
- {[]byte{0xcb, 0xf2, 0x9c, 0xe4, 0x84, 0x22, 0x23, 0x25}, ""},
- {[]byte{0xaf, 0x63, 0xbd, 0x4c, 0x86, 0x01, 0xb7, 0xbe}, "a"},
- {[]byte{0x08, 0x32, 0x67, 0x07, 0xb4, 0xeb, 0x37, 0xb8}, "ab"},
- {[]byte{0xd8, 0xdc, 0xca, 0x18, 0x6b, 0xaf, 0xad, 0xcb}, "abc"},
+ {[]byte{0xcb, 0xf2, 0x9c, 0xe4, 0x84, 0x22, 0x23, 0x25}, "", "fnv\x03\xcb\xf2\x9c\xe4\x84\"#%"},
+ {[]byte{0xaf, 0x63, 0xbd, 0x4c, 0x86, 0x01, 0xb7, 0xbe}, "a", "fnv\x03\xcb\xf2\x9c\xe4\x84\"#%"},
+ {[]byte{0x08, 0x32, 0x67, 0x07, 0xb4, 0xeb, 0x37, 0xb8}, "ab", "fnv\x03\xafc\xbdL\x86\x01\xb7\xbe"},
+ {[]byte{0xd8, 0xdc, 0xca, 0x18, 0x6b, 0xaf, 0xad, 0xcb}, "abc", "fnv\x03\xafc\xbdL\x86\x01\xb7\xbe"},
}
var golden64a = []golden{
- {[]byte{0xcb, 0xf2, 0x9c, 0xe4, 0x84, 0x22, 0x23, 0x25}, ""},
- {[]byte{0xaf, 0x63, 0xdc, 0x4c, 0x86, 0x01, 0xec, 0x8c}, "a"},
- {[]byte{0x08, 0x9c, 0x44, 0x07, 0xb5, 0x45, 0x98, 0x6a}, "ab"},
- {[]byte{0xe7, 0x1f, 0xa2, 0x19, 0x05, 0x41, 0x57, 0x4b}, "abc"},
+ {[]byte{0xcb, 0xf2, 0x9c, 0xe4, 0x84, 0x22, 0x23, 0x25}, "", "fnv\x04\xcb\xf2\x9c\xe4\x84\"#%"},
+ {[]byte{0xaf, 0x63, 0xdc, 0x4c, 0x86, 0x01, 0xec, 0x8c}, "a", "fnv\x04\xcb\xf2\x9c\xe4\x84\"#%"},
+ {[]byte{0x08, 0x9c, 0x44, 0x07, 0xb5, 0x45, 0x98, 0x6a}, "ab", "fnv\x04\xafc\xdcL\x86\x01\xec\x8c"},
+ {[]byte{0xe7, 0x1f, 0xa2, 0x19, 0x05, 0x41, 0x57, 0x4b}, "abc", "fnv\x04\xafc\xdcL\x86\x01\xec\x8c"},
}
var golden128 = []golden{
- {[]byte{0x6c, 0x62, 0x27, 0x2e, 0x07, 0xbb, 0x01, 0x42, 0x62, 0xb8, 0x21, 0x75, 0x62, 0x95, 0xc5, 0x8d}, ""},
- {[]byte{0xd2, 0x28, 0xcb, 0x69, 0x10, 0x1a, 0x8c, 0xaf, 0x78, 0x91, 0x2b, 0x70, 0x4e, 0x4a, 0x14, 0x1e}, "a"},
- {[]byte{0x8, 0x80, 0x94, 0x5a, 0xee, 0xab, 0x1b, 0xe9, 0x5a, 0xa0, 0x73, 0x30, 0x55, 0x26, 0xc0, 0x88}, "ab"},
- {[]byte{0xa6, 0x8b, 0xb2, 0xa4, 0x34, 0x8b, 0x58, 0x22, 0x83, 0x6d, 0xbc, 0x78, 0xc6, 0xae, 0xe7, 0x3b}, "abc"},
+ {[]byte{0x6c, 0x62, 0x27, 0x2e, 0x07, 0xbb, 0x01, 0x42, 0x62, 0xb8, 0x21, 0x75, 0x62, 0x95, 0xc5, 0x8d}, "", "fnv\x05lb'.\a\xbb\x01Bb\xb8!ub\x95ō"},
+ {[]byte{0xd2, 0x28, 0xcb, 0x69, 0x10, 0x1a, 0x8c, 0xaf, 0x78, 0x91, 0x2b, 0x70, 0x4e, 0x4a, 0x14, 0x1e}, "a", "fnv\x05lb'.\a\xbb\x01Bb\xb8!ub\x95ō"},
+ {[]byte{0x8, 0x80, 0x94, 0x5a, 0xee, 0xab, 0x1b, 0xe9, 0x5a, 0xa0, 0x73, 0x30, 0x55, 0x26, 0xc0, 0x88}, "ab", "fnv\x05\xd2(\xcbi\x10\x1a\x8c\xafx\x91+pNJ\x14\x1e"},
+ {[]byte{0xa6, 0x8b, 0xb2, 0xa4, 0x34, 0x8b, 0x58, 0x22, 0x83, 0x6d, 0xbc, 0x78, 0xc6, 0xae, 0xe7, 0x3b}, "abc", "fnv\x05\xd2(\xcbi\x10\x1a\x8c\xafx\x91+pNJ\x14\x1e"},
}
var golden128a = []golden{
- {[]byte{0x6c, 0x62, 0x27, 0x2e, 0x07, 0xbb, 0x01, 0x42, 0x62, 0xb8, 0x21, 0x75, 0x62, 0x95, 0xc5, 0x8d}, ""},
- {[]byte{0xd2, 0x28, 0xcb, 0x69, 0x6f, 0x1a, 0x8c, 0xaf, 0x78, 0x91, 0x2b, 0x70, 0x4e, 0x4a, 0x89, 0x64}, "a"},
- {[]byte{0x08, 0x80, 0x95, 0x44, 0xbb, 0xab, 0x1b, 0xe9, 0x5a, 0xa0, 0x73, 0x30, 0x55, 0xb6, 0x9a, 0x62}, "ab"},
- {[]byte{0xa6, 0x8d, 0x62, 0x2c, 0xec, 0x8b, 0x58, 0x22, 0x83, 0x6d, 0xbc, 0x79, 0x77, 0xaf, 0x7f, 0x3b}, "abc"},
+ {[]byte{0x6c, 0x62, 0x27, 0x2e, 0x07, 0xbb, 0x01, 0x42, 0x62, 0xb8, 0x21, 0x75, 0x62, 0x95, 0xc5, 0x8d}, "", "fnv\x06lb'.\a\xbb\x01Bb\xb8!ub\x95ō"},
+ {[]byte{0xd2, 0x28, 0xcb, 0x69, 0x6f, 0x1a, 0x8c, 0xaf, 0x78, 0x91, 0x2b, 0x70, 0x4e, 0x4a, 0x89, 0x64}, "a", "fnv\x06lb'.\a\xbb\x01Bb\xb8!ub\x95ō"},
+ {[]byte{0x08, 0x80, 0x95, 0x44, 0xbb, 0xab, 0x1b, 0xe9, 0x5a, 0xa0, 0x73, 0x30, 0x55, 0xb6, 0x9a, 0x62}, "ab", "fnv\x06\xd2(\xcbio\x1a\x8c\xafx\x91+pNJ\x89d"},
+ {[]byte{0xa6, 0x8d, 0x62, 0x2c, 0xec, 0x8b, 0x58, 0x22, 0x83, 0x6d, 0xbc, 0x79, 0x77, 0xaf, 0x7f, 0x3b}, "abc", "fnv\x06\xd2(\xcbio\x1a\x8c\xafx\x91+pNJ\x89d"},
}
func TestGolden32(t *testing.T) {
@@ -85,19 +88,67 @@ func TestGolden128a(t *testing.T) {
func testGolden(t *testing.T, hash hash.Hash, gold []golden) {
for _, g := range gold {
hash.Reset()
- done, error := hash.Write([]byte(g.text))
+ done, error := hash.Write([]byte(g.in))
if error != nil {
t.Fatalf("write error: %s", error)
}
- if done != len(g.text) {
- t.Fatalf("wrote only %d out of %d bytes", done, len(g.text))
+ if done != len(g.in) {
+ t.Fatalf("wrote only %d out of %d bytes", done, len(g.in))
}
- if actual := hash.Sum(nil); !bytes.Equal(g.sum, actual) {
- t.Errorf("hash(%q) = 0x%x want 0x%x", g.text, actual, g.sum)
+ if actual := hash.Sum(nil); !bytes.Equal(g.out, actual) {
+ t.Errorf("hash(%q) = 0x%x want 0x%x", g.in, actual, g.out)
}
}
}
+func TestGoldenMarshal(t *testing.T) {
+ tests := []struct {
+ name string
+ newHash func() hash.Hash
+ gold []golden
+ }{
+ {"32", func() hash.Hash { return New32() }, golden32},
+ {"32a", func() hash.Hash { return New32a() }, golden32a},
+ {"64", func() hash.Hash { return New64() }, golden64},
+ {"64a", func() hash.Hash { return New64a() }, golden64a},
+ {"128", func() hash.Hash { return New128() }, golden128},
+ {"128a", func() hash.Hash { return New128a() }, golden128a},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ for _, g := range tt.gold {
+ h := tt.newHash()
+ h2 := tt.newHash()
+
+ io.WriteString(h, g.in[:len(g.in)/2])
+
+ state, err := h.(encoding.BinaryMarshaler).MarshalBinary()
+ if err != nil {
+ t.Errorf("could not marshal: %v", err)
+ continue
+ }
+
+ if string(state) != g.halfState {
+ t.Errorf("checksum(%q) state = %q, want %q", g.in, state, g.halfState)
+ continue
+ }
+
+ if err := h2.(encoding.BinaryUnmarshaler).UnmarshalBinary(state); err != nil {
+ t.Errorf("could not unmarshal: %v", err)
+ continue
+ }
+
+ io.WriteString(h, g.in[len(g.in)/2:])
+ io.WriteString(h2, g.in[len(g.in)/2:])
+
+ if actual, actual2 := h.Sum(nil), h2.Sum(nil); !bytes.Equal(actual, actual2) {
+ t.Errorf("hash(%q) = 0x%x != marshaled 0x%x", g.in, actual, actual2)
+ }
+ }
+ })
+ }
+}
+
func TestIntegrity32(t *testing.T) {
testIntegrity(t, New32())
}
diff --git a/src/hash/hash.go b/src/hash/hash.go
index 8d138d07f9..da2007877a 100644
--- a/src/hash/hash.go
+++ b/src/hash/hash.go
@@ -8,6 +8,19 @@ package hash
import "io"
// Hash is the common interface implemented by all hash functions.
+//
+// Hash implementations in the standard library (e.g. hash/crc32 and
+// crypto/sha256) implement the encoding.BinaryMarshaler and
+// encoding.BinaryUnmarshaler interfaces. Marshaling a hash implementation
+// allows its internal state to be saved and used for additional processing
+// later, without having to re-write the data previously written to the hash.
+//
+// Compatibility: Any future changes to hash or crypto packages will endeavor
+// to maintain compatibility with state encoded using previous versions.
+// That is, any released versions of the packages should be able to
+// decode data written with any previously released version,
+// subject to issues such as security fixes.
+// See the Go compatibility document for background: https://golang.org/doc/go1compat
type Hash interface {
// Write (via the embedded io.Writer interface) adds more data to the running hash.
// It never returns an error.