Commit 731b6321 authored by Tim Cooper's avatar Tim Cooper Committed by Ian Lance Taylor

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/66710Reviewed-by: 's avatarJoe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: 's avatarIan Lance Taylor <iant@golang.org>
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 14bc4f5e
......@@ -420,12 +420,14 @@ var builddeps = map[string][]string{
"crypto/sha1": {
"crypto", // crypto/sha1
"errors", // crypto/sha1
"hash", // crypto/sha1
"internal/cpu", // crypto/sha1
},
"crypto/sha256": {
"crypto", // crypto/sha256
"errors", // crypto/sha256
"hash", // crypto/sha256
"internal/cpu", // crypto/sha256
},
......@@ -635,7 +637,8 @@ var builddeps = map[string][]string{
},
"hash/adler32": {
"hash", // hash/adler32
"errors", // hash/adler32
"hash", // hash/adler32
},
"internal/cpu": {
......
......@@ -12,6 +12,7 @@ package md5
import (
"crypto"
"errors"
"hash"
)
......@@ -50,6 +51,78 @@ func (d *digest) Reset() {
d.len = 0
}
const (
magic = "md5\x01"
marshaledSize = len(magic) + 4*4 + chunk + 8
)
func (d *digest) MarshalBinary() ([]byte, error) {
b := make([]byte, 0, marshaledSize)
b = append(b, magic...)
b = appendUint32(b, d.s[0])
b = appendUint32(b, d.s[1])
b = appendUint32(b, d.s[2])
b = appendUint32(b, d.s[3])
b = append(b, d.x[:]...)
b = appendUint64(b, d.len)
return b, nil
}
func (d *digest) UnmarshalBinary(b []byte) error {
if len(b) < len(magic) || string(b[:len(magic)]) != magic {
return errors.New("crypto/md5: invalid hash state identifier")
}
if len(b) != marshaledSize {
return errors.New("crypto/md5: invalid hash state size")
}
b = b[len(magic):]
b, d.s[0] = consumeUint32(b)
b, d.s[1] = consumeUint32(b)
b, d.s[2] = consumeUint32(b)
b, d.s[3] = consumeUint32(b)
b = b[copy(d.x[:], b):]
b, d.len = consumeUint64(b)
d.nx = int(d.len) % chunk
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 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 consumeUint64(b []byte) ([]byte, uint64) {
_ = b[7]
x := 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
return b[8:], x
}
func consumeUint32(b []byte) ([]byte, uint32) {
_ = b[3]
x := uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
return b[4:], x
}
// New returns a new hash.Hash computing the MD5 checksum.
func New() hash.Hash {
d := new(digest)
......
This diff is collapsed.
......@@ -10,6 +10,7 @@ package sha1
import (
"crypto"
"errors"
"hash"
)
......@@ -40,6 +41,68 @@ type digest struct {
len uint64
}
const (
magic = "sha\x01"
marshaledSize = len(magic) + 5*4 + chunk + 8
)
func (d *digest) MarshalBinary() ([]byte, error) {
b := make([]byte, 0, marshaledSize)
b = append(b, magic...)
b = appendUint32(b, d.h[0])
b = appendUint32(b, d.h[1])
b = appendUint32(b, d.h[2])
b = appendUint32(b, d.h[3])
b = appendUint32(b, d.h[4])
b = append(b, d.x[:]...)
b = appendUint64(b, d.len)
return b, nil
}
func (d *digest) UnmarshalBinary(b []byte) error {
if len(b) < len(magic) || string(b[:len(magic)]) != magic {
return errors.New("crypto/sha1: invalid hash state identifier")
}
if len(b) != marshaledSize {
return errors.New("crypto/sha1: invalid hash state size")
}
b = b[len(magic):]
b, d.h[0] = consumeUint32(b)
b, d.h[1] = consumeUint32(b)
b, d.h[2] = consumeUint32(b)
b, d.h[3] = consumeUint32(b)
b, d.h[4] = consumeUint32(b)
b = b[copy(d.x[:], b):]
b, d.len = consumeUint64(b)
d.nx = int(d.len) % chunk
return nil
}
func appendUint64(b []byte, x uint64) []byte {
var a [8]byte
putUint64(a[:], x)
return append(b, a[:]...)
}
func appendUint32(b []byte, x uint32) []byte {
var a [4]byte
putUint32(a[:], x)
return append(b, a[:]...)
}
func consumeUint64(b []byte) ([]byte, uint64) {
_ = b[7]
x := 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
return b[8:], x
}
func consumeUint32(b []byte) ([]byte, uint32) {
_ = b[3]
x := uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
return b[4:], x
}
func (d *digest) Reset() {
d.h[0] = init0
d.h[1] = init1
......
This diff is collapsed.
......@@ -8,6 +8,7 @@ package sha256
import (
"crypto"
"errors"
"hash"
)
......@@ -54,6 +55,91 @@ type digest struct {
is224 bool // mark if this digest is SHA-224
}
const (
magic224 = "sha\x02"
magic256 = "sha\x03"
marshaledSize = len(magic256) + 8*4 + chunk + 8
)
func (d *digest) MarshalBinary() ([]byte, error) {
b := make([]byte, 0, marshaledSize)
if d.is224 {
b = append(b, magic224...)
} else {
b = append(b, magic256...)
}
b = appendUint32(b, d.h[0])
b = appendUint32(b, d.h[1])
b = appendUint32(b, d.h[2])
b = appendUint32(b, d.h[3])
b = appendUint32(b, d.h[4])
b = appendUint32(b, d.h[5])
b = appendUint32(b, d.h[6])
b = appendUint32(b, d.h[7])
b = append(b, d.x[:]...)
b = appendUint64(b, d.len)
return b, nil
}
func (d *digest) UnmarshalBinary(b []byte) error {
if len(b) < len(magic224) || (d.is224 && string(b[:len(magic224)]) != magic224) || (!d.is224 && string(b[:len(magic256)]) != magic256) {
return errors.New("crypto/sha256: invalid hash state identifier")
}
if len(b) != marshaledSize {
return errors.New("crypto/sha256: invalid hash state size")
}
b = b[len(magic224):]
b, d.h[0] = consumeUint32(b)
b, d.h[1] = consumeUint32(b)
b, d.h[2] = consumeUint32(b)
b, d.h[3] = consumeUint32(b)
b, d.h[4] = consumeUint32(b)
b, d.h[5] = consumeUint32(b)
b, d.h[6] = consumeUint32(b)
b, d.h[7] = consumeUint32(b)
b = b[copy(d.x[:], b):]
b, d.len = consumeUint64(b)
d.nx = int(d.len) % chunk
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 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 consumeUint64(b []byte) ([]byte, uint64) {
_ = b[7]
x := 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
return b[8:], x
}
func consumeUint32(b []byte) ([]byte, uint32) {
_ = b[3]
x := uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
return b[4:], x
}
func (d *digest) Reset() {
if !d.is224 {
d.h[0] = init0
......
This diff is collapsed.
......@@ -8,6 +8,7 @@ package sha512
import (
"crypto"
"errors"
"hash"
)
......@@ -124,6 +125,92 @@ func (d *digest) Reset() {
d.len = 0
}
const (
magic384 = "sha\x04"
magic512_224 = "sha\x05"
magic512_256 = "sha\x06"
magic512 = "sha\x07"
marshaledSize = len(magic512) + 8*8 + chunk + 8
)
func (d *digest) MarshalBinary() ([]byte, error) {
b := make([]byte, 0, marshaledSize)
switch d.function {
case crypto.SHA384:
b = append(b, magic384...)
case crypto.SHA512_224:
b = append(b, magic512_224...)
case crypto.SHA512_256:
b = append(b, magic512_256...)
case crypto.SHA512:
b = append(b, magic512...)
default:
return nil, errors.New("crypto/sha512: invalid hash function")
}
b = appendUint64(b, d.h[0])
b = appendUint64(b, d.h[1])
b = appendUint64(b, d.h[2])
b = appendUint64(b, d.h[3])
b = appendUint64(b, d.h[4])
b = appendUint64(b, d.h[5])
b = appendUint64(b, d.h[6])
b = appendUint64(b, d.h[7])
b = append(b, d.x[:]...)
b = appendUint64(b, d.len)
return b, nil
}
func (d *digest) UnmarshalBinary(b []byte) error {
if len(b) < len(magic512) {
return errors.New("crypto/sha512: invalid hash state identifier")
}
switch {
case d.function == crypto.SHA384 && string(b[:len(magic384)]) == magic384:
case d.function == crypto.SHA512_224 && string(b[:len(magic512_224)]) == magic512_224:
case d.function == crypto.SHA512_256 && string(b[:len(magic512_256)]) == magic512_256:
case d.function == crypto.SHA512 && string(b[:len(magic512)]) == magic512:
default:
return errors.New("crypto/sha512: invalid hash state identifier")
}
if len(b) != marshaledSize {
return errors.New("crypto/sha512: invalid hash state size")
}
b = b[len(magic512):]
b, d.h[0] = consumeUint64(b)
b, d.h[1] = consumeUint64(b)
b, d.h[2] = consumeUint64(b)
b, d.h[3] = consumeUint64(b)
b, d.h[4] = consumeUint64(b)
b, d.h[5] = consumeUint64(b)
b, d.h[6] = consumeUint64(b)
b, d.h[7] = consumeUint64(b)
b = b[copy(d.x[:], b):]
b, d.len = consumeUint64(b)
d.nx = int(d.len) % chunk
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 consumeUint64(b []byte) ([]byte, uint64) {
_ = b[7]
x := 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
return b[8:], x
}
// New returns a new hash.Hash computing the SHA-512 checksum.
func New() hash.Hash {
d := &digest{function: crypto.SHA512}
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -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)
......
......@@ -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)
......
......@@ -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)
}
......@@ -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)
......
......@@ -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))
}
This diff is collapsed.
......@@ -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
}
......@@ -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())
}
......
......@@ -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.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment