diff options
author | Ian Lance Taylor <iant@golang.org> | 2017-09-14 17:11:35 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2017-09-14 17:11:35 +0000 |
commit | bc998d034f45d1828a8663b2eed928faf22a7d01 (patch) | |
tree | 8d262a22ca7318f4bcd64269fe8fe9e45bcf8d0f /libgo/go/hash | |
parent | a41a6142df74219f596e612d3a7775f68ca6e96f (diff) | |
download | gcc-bc998d034f45d1828a8663b2eed928faf22a7d01.zip gcc-bc998d034f45d1828a8663b2eed928faf22a7d01.tar.gz gcc-bc998d034f45d1828a8663b2eed928faf22a7d01.tar.bz2 |
libgo: update to go1.9
Reviewed-on: https://go-review.googlesource.com/63753
From-SVN: r252767
Diffstat (limited to 'libgo/go/hash')
-rw-r--r-- | libgo/go/hash/crc32/crc32_amd64.go | 30 | ||||
-rw-r--r-- | libgo/go/hash/crc32/crc32_amd64p32.go | 14 | ||||
-rw-r--r-- | libgo/go/hash/crc32/crc32_arm64.go | 53 | ||||
-rw-r--r-- | libgo/go/hash/crc32/crc32_otherarch.go | 2 | ||||
-rw-r--r-- | libgo/go/hash/crc32/crc32_ppc64le.go | 89 | ||||
-rw-r--r-- | libgo/go/hash/crc32/crc32_test.go | 86 | ||||
-rw-r--r-- | libgo/go/hash/crc32/gen_const_ppc64le.go | 150 | ||||
-rw-r--r-- | libgo/go/hash/fnv/fnv.go | 122 | ||||
-rw-r--r-- | libgo/go/hash/fnv/fnv_test.go | 39 |
9 files changed, 475 insertions, 110 deletions
diff --git a/libgo/go/hash/crc32/crc32_amd64.go b/libgo/go/hash/crc32/crc32_amd64.go index 72844f0..b394fc0 100644 --- a/libgo/go/hash/crc32/crc32_amd64.go +++ b/libgo/go/hash/crc32/crc32_amd64.go @@ -10,23 +10,20 @@ package crc32 -import "unsafe" +import ( + "internal/cpu" + "unsafe" +) // This file contains the code to call the SSE 4.2 version of the Castagnoli // and IEEE CRC. -// haveSSE41/haveSSE42/haveCLMUL are defined in crc_amd64.s and use -// CPUID to test for SSE 4.1, 4.2 and CLMUL support. -func haveSSE41() bool -func haveSSE42() bool -func haveCLMUL() bool - -// castagnoliSSE42 is defined in crc32_amd64.s and uses the SSE4.2 CRC32 +// castagnoliSSE42 is defined in crc32_amd64.s and uses the SSE 4.2 CRC32 // instruction. //go:noescape func castagnoliSSE42(crc uint32, p []byte) uint32 -// castagnoliSSE42Triple is defined in crc32_amd64.s and uses the SSE4.2 CRC32 +// castagnoliSSE42Triple is defined in crc32_amd64.s and uses the SSE 4.2 CRC32 // instruction. //go:noescape func castagnoliSSE42Triple( @@ -40,9 +37,6 @@ func castagnoliSSE42Triple( //go:noescape func ieeeCLMUL(crc uint32, p []byte) uint32 -var sse42 = haveSSE42() -var useFastIEEE = haveCLMUL() && haveSSE41() - const castagnoliK1 = 168 const castagnoliK2 = 1344 @@ -52,11 +46,11 @@ var castagnoliSSE42TableK1 *sse42Table var castagnoliSSE42TableK2 *sse42Table func archAvailableCastagnoli() bool { - return sse42 + return cpu.X86.HasSSE42 } func archInitCastagnoli() { - if !sse42 { + if !cpu.X86.HasSSE42 { panic("arch-specific Castagnoli not available") } castagnoliSSE42TableK1 = new(sse42Table) @@ -88,7 +82,7 @@ func castagnoliShift(table *sse42Table, crc uint32) uint32 { } func archUpdateCastagnoli(crc uint32, p []byte) uint32 { - if !sse42 { + if !cpu.X86.HasSSE42 { panic("not available") } @@ -199,13 +193,13 @@ func archUpdateCastagnoli(crc uint32, p []byte) uint32 { } func archAvailableIEEE() bool { - return useFastIEEE + return cpu.X86.HasPCLMULQDQ && cpu.X86.HasSSE41 } var archIeeeTable8 *slicing8Table func archInitIEEE() { - if !useFastIEEE { + if !cpu.X86.HasPCLMULQDQ || !cpu.X86.HasSSE41 { panic("not available") } // We still use slicing-by-8 for small buffers. @@ -213,7 +207,7 @@ func archInitIEEE() { } func archUpdateIEEE(crc uint32, p []byte) uint32 { - if !useFastIEEE { + if !cpu.X86.HasPCLMULQDQ || !cpu.X86.HasSSE41 { panic("not available") } diff --git a/libgo/go/hash/crc32/crc32_amd64p32.go b/libgo/go/hash/crc32/crc32_amd64p32.go index 9d728fc..1ec44cb4 100644 --- a/libgo/go/hash/crc32/crc32_amd64p32.go +++ b/libgo/go/hash/crc32/crc32_amd64p32.go @@ -4,33 +4,29 @@ package crc32 +import "internal/cpu" + // This file contains the code to call the SSE 4.2 version of the Castagnoli // CRC. -// haveSSE42 is defined in crc32_amd64p32.s and uses CPUID to test for SSE 4.2 -// support. -func haveSSE42() bool - // castagnoliSSE42 is defined in crc32_amd64p32.s and uses the SSE4.2 CRC32 // instruction. //go:noescape func castagnoliSSE42(crc uint32, p []byte) uint32 -var sse42 = haveSSE42() - func archAvailableCastagnoli() bool { - return sse42 + return cpu.X86.HasSSE42 } func archInitCastagnoli() { - if !sse42 { + if !cpu.X86.HasSSE42 { panic("not available") } // No initialization necessary. } func archUpdateCastagnoli(crc uint32, p []byte) uint32 { - if !sse42 { + if !cpu.X86.HasSSE42 { panic("not available") } return castagnoliSSE42(crc, p) diff --git a/libgo/go/hash/crc32/crc32_arm64.go b/libgo/go/hash/crc32/crc32_arm64.go new file mode 100644 index 0000000..4c5cad1 --- /dev/null +++ b/libgo/go/hash/crc32/crc32_arm64.go @@ -0,0 +1,53 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// ARM64-specific hardware-assisted CRC32 algorithms. See crc32.go for a +// description of the interface that each architecture-specific file +// implements. + +package crc32 + +func supportsCRC32() bool +func castagnoliUpdate(crc uint32, p []byte) uint32 +func ieeeUpdate(crc uint32, p []byte) uint32 + +var hasCRC32 = supportsCRC32() + +func archAvailableCastagnoli() bool { + return hasCRC32 +} + +func archInitCastagnoli() { + if !hasCRC32 { + panic("arch-specific crc32 instruction for Catagnoli not available") + } +} + +func archUpdateCastagnoli(crc uint32, p []byte) uint32 { + if !hasCRC32 { + panic("arch-specific crc32 instruction for Castagnoli not available") + } + + return ^castagnoliUpdate(^crc, p) +} + +func archAvailableIEEE() bool { + return hasCRC32 +} + +func archInitIEEE() { + if !hasCRC32 { + panic("arch-specific crc32 instruction for IEEE not available") + } +} + +func archUpdateIEEE(crc uint32, p []byte) uint32 { + if !hasCRC32 { + panic("arch-specific crc32 instruction for IEEE not available") + } + + return ^ieeeUpdate(^crc, p) +} diff --git a/libgo/go/hash/crc32/crc32_otherarch.go b/libgo/go/hash/crc32/crc32_otherarch.go index 09c3389..c3acd25 100644 --- a/libgo/go/hash/crc32/crc32_otherarch.go +++ b/libgo/go/hash/crc32/crc32_otherarch.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// -build !amd64,!amd64p32,!s390x +// -build !amd64,!amd64p32,!s390x,!ppc64le,!arm64 package crc32 diff --git a/libgo/go/hash/crc32/crc32_ppc64le.go b/libgo/go/hash/crc32/crc32_ppc64le.go new file mode 100644 index 0000000..4211b5b --- /dev/null +++ b/libgo/go/hash/crc32/crc32_ppc64le.go @@ -0,0 +1,89 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package crc32 + +import ( + "unsafe" +) + +const ( + vecMinLen = 16 + vecAlignMask = 15 // align to 16 bytes + crcIEEE = 1 + crcCast = 2 +) + +//go:noescape +func ppc64SlicingUpdateBy8(crc uint32, table8 *slicing8Table, p []byte) uint32 + +// this function requires the buffer to be 16 byte aligned and > 16 bytes long +//go:noescape +func vectorCrc32(crc uint32, poly uint32, p []byte) uint32 + +var archCastagnoliTable8 *slicing8Table + +func archInitCastagnoli() { + archCastagnoliTable8 = slicingMakeTable(Castagnoli) +} + +func archUpdateCastagnoli(crc uint32, p []byte) uint32 { + if len(p) >= 4*vecMinLen { + // If not aligned then process the initial unaligned bytes + + if uint64(uintptr(unsafe.Pointer(&p[0])))&uint64(vecAlignMask) != 0 { + align := uint64(uintptr(unsafe.Pointer(&p[0]))) & uint64(vecAlignMask) + newlen := vecMinLen - align + crc = ppc64SlicingUpdateBy8(crc, archCastagnoliTable8, p[:newlen]) + p = p[newlen:] + } + // p should be aligned now + aligned := len(p) & ^vecAlignMask + crc = vectorCrc32(crc, crcCast, p[:aligned]) + p = p[aligned:] + } + if len(p) == 0 { + return crc + } + return ppc64SlicingUpdateBy8(crc, archCastagnoliTable8, p) +} + +func archAvailableIEEE() bool { + return true +} +func archAvailableCastagnoli() bool { + return true +} + +var archIeeeTable8 *slicing8Table + +func archInitIEEE() { + // We still use slicing-by-8 for small buffers. + archIeeeTable8 = slicingMakeTable(IEEE) +} + +// archUpdateIEEE calculates the checksum of p using vectorizedIEEE. +func archUpdateIEEE(crc uint32, p []byte) uint32 { + + // Check if vector code should be used. If not aligned, then handle those + // first up to the aligned bytes. + + if len(p) >= 4*vecMinLen { + if uint64(uintptr(unsafe.Pointer(&p[0])))&uint64(vecAlignMask) != 0 { + align := uint64(uintptr(unsafe.Pointer(&p[0]))) & uint64(vecAlignMask) + newlen := vecMinLen - align + crc = ppc64SlicingUpdateBy8(crc, archIeeeTable8, p[:newlen]) + p = p[newlen:] + } + aligned := len(p) & ^vecAlignMask + crc = vectorCrc32(crc, crcIEEE, p[:aligned]) + p = p[aligned:] + } + if len(p) == 0 { + return crc + } + return ppc64SlicingUpdateBy8(crc, archIeeeTable8, p) +} diff --git a/libgo/go/hash/crc32/crc32_test.go b/libgo/go/hash/crc32/crc32_test.go index 1356734..0492f46 100644 --- a/libgo/go/hash/crc32/crc32_test.go +++ b/libgo/go/hash/crc32/crc32_test.go @@ -5,6 +5,7 @@ package crc32 import ( + "fmt" "hash" "math/rand" "testing" @@ -75,8 +76,9 @@ func testCrossCheck(t *testing.T, crcFunc1, crcFunc2 func(crc uint32, b []byte) // The AMD64 implementation has some cutoffs at lengths 168*3=504 and // 1344*3=4032. We should make sure lengths around these values are in the // list. - lengths := []int{0, 1, 2, 3, 4, 5, 10, 16, 50, 100, 128, - 500, 501, 502, 503, 504, 505, 512, 1000, 1024, 2000, + lengths := []int{0, 1, 2, 3, 4, 5, 10, 16, 50, 63, 64, 65, 100, + 127, 128, 129, 255, 256, 257, 300, 312, 384, 416, 448, 480, + 500, 501, 502, 503, 504, 505, 512, 513, 1000, 1024, 2000, 4030, 4031, 4032, 4033, 4036, 4040, 4048, 4096, 5000, 10000} for _, length := range lengths { p := make([]byte, length) @@ -196,68 +198,28 @@ func TestGolden(t *testing.T) { } } -func BenchmarkIEEECrc40B(b *testing.B) { - benchmark(b, NewIEEE(), 40, 0) +func BenchmarkCRC32(b *testing.B) { + b.Run("poly=IEEE", benchmarkAll(NewIEEE())) + b.Run("poly=Castagnoli", benchmarkAll(New(MakeTable(Castagnoli)))) + b.Run("poly=Koopman", benchmarkAll(New(MakeTable(Koopman)))) } -func BenchmarkIEEECrc1KB(b *testing.B) { - benchmark(b, NewIEEE(), 1<<10, 0) -} - -func BenchmarkIEEECrc4KB(b *testing.B) { - benchmark(b, NewIEEE(), 4<<10, 0) -} - -func BenchmarkIEEECrc32KB(b *testing.B) { - benchmark(b, NewIEEE(), 32<<10, 0) -} - -func BenchmarkCastagnoliCrc15B(b *testing.B) { - benchmark(b, New(MakeTable(Castagnoli)), 15, 0) -} - -func BenchmarkCastagnoliCrc15BMisaligned(b *testing.B) { - benchmark(b, New(MakeTable(Castagnoli)), 15, 1) -} - -func BenchmarkCastagnoliCrc40B(b *testing.B) { - benchmark(b, New(MakeTable(Castagnoli)), 40, 0) -} - -func BenchmarkCastagnoliCrc40BMisaligned(b *testing.B) { - benchmark(b, New(MakeTable(Castagnoli)), 40, 1) -} - -func BenchmarkCastagnoliCrc512(b *testing.B) { - benchmark(b, New(MakeTable(Castagnoli)), 512, 0) -} - -func BenchmarkCastagnoliCrc512Misaligned(b *testing.B) { - benchmark(b, New(MakeTable(Castagnoli)), 512, 1) -} - -func BenchmarkCastagnoliCrc1KB(b *testing.B) { - benchmark(b, New(MakeTable(Castagnoli)), 1<<10, 0) -} - -func BenchmarkCastagnoliCrc1KBMisaligned(b *testing.B) { - benchmark(b, New(MakeTable(Castagnoli)), 1<<10, 1) -} - -func BenchmarkCastagnoliCrc4KB(b *testing.B) { - benchmark(b, New(MakeTable(Castagnoli)), 4<<10, 0) -} - -func BenchmarkCastagnoliCrc4KBMisaligned(b *testing.B) { - benchmark(b, New(MakeTable(Castagnoli)), 4<<10, 1) -} - -func BenchmarkCastagnoliCrc32KB(b *testing.B) { - benchmark(b, New(MakeTable(Castagnoli)), 32<<10, 0) -} - -func BenchmarkCastagnoliCrc32KBMisaligned(b *testing.B) { - benchmark(b, New(MakeTable(Castagnoli)), 32<<10, 1) +func benchmarkAll(h hash.Hash32) func(b *testing.B) { + return func(b *testing.B) { + for _, size := range []int{15, 40, 512, 1 << 10, 4 << 10, 32 << 10} { + name := fmt.Sprint(size) + if size >= 1024 { + name = fmt.Sprintf("%dkB", size/1024) + } + b.Run("size="+name, func(b *testing.B) { + for align := 0; align <= 1; align++ { + b.Run(fmt.Sprintf("align=%d", align), func(b *testing.B) { + benchmark(b, h, int64(size), int64(align)) + }) + } + }) + } + } } func benchmark(b *testing.B, h hash.Hash32, n, alignment int64) { diff --git a/libgo/go/hash/crc32/gen_const_ppc64le.go b/libgo/go/hash/crc32/gen_const_ppc64le.go new file mode 100644 index 0000000..bfb3b3a --- /dev/null +++ b/libgo/go/hash/crc32/gen_const_ppc64le.go @@ -0,0 +1,150 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// Generate the constant table associated with the poly used by the +// vpmsumd crc32 algorithm. +// +// go run gen_const_ppc64le.go +// +// generates crc32_table_ppc64le.s + +// The following is derived from code written by Anton Blanchard +// <anton@au.ibm.com> found at https://github.com/antonblanchard/crc32-vpmsum. +// The original is dual licensed under GPL and Apache 2. As the copyright holder +// for the work, IBM has contributed this new work under the golang license. + +// This code was written in Go based on the original C implementation. + +// This is a tool needed to generate the appropriate constants needed for +// the vpmsum algorithm. It is included to generate new constant tables if +// new polynomial values are included in the future. + +package main + +import ( + "bytes" + "fmt" + "io/ioutil" +) + +var blocking = 32 * 1024 + +func reflect_bits(b uint64, nr uint) uint64 { + var ref uint64 + + for bit := uint64(0); bit < uint64(nr); bit++ { + if (b & uint64(1)) == 1 { + ref |= (1 << (uint64(nr-1) - bit)) + } + b = (b >> 1) + } + return ref +} + +func get_remainder(poly uint64, deg uint, n uint) uint64 { + + rem, _ := xnmodp(n, poly, deg) + return rem +} + +func get_quotient(poly uint64, bits, n uint) uint64 { + + _, div := xnmodp(n, poly, bits) + return div +} + +// xnmodp returns two values, p and div: +// p is the representation of the binary polynomial x**n mod (x ** deg + "poly") +// That is p is the binary representation of the modulus polynomial except for its highest-order term. +// div is the binary representation of the polynomial x**n / (x ** deg + "poly") +func xnmodp(n uint, poly uint64, deg uint) (uint64, uint64) { + + var mod, mask, high, div uint64 + + if n < deg { + div = 0 + return poly, div + } + mask = 1<<deg - 1 + poly &= mask + mod = poly + div = 1 + deg-- + n-- + for n > deg { + high = (mod >> deg) & 1 + div = (div << 1) | high + mod <<= 1 + if high != 0 { + mod ^= poly + } + n-- + } + return mod & mask, div +} + +func main() { + w := new(bytes.Buffer) + + fmt.Fprintf(w, "// autogenerated: do not edit!\n") + fmt.Fprintf(w, "// generated from crc32/gen_const_ppc64le.go\n") + fmt.Fprintln(w) + fmt.Fprintf(w, "#include \"textflag.h\"\n") + + // These are the polynomials supported in vector now. + // If adding others, include the polynomial and a name + // to identify it. + + genCrc32ConstTable(w, 0xedb88320, "IEEE") + genCrc32ConstTable(w, 0x82f63b78, "Cast") + genCrc32ConstTable(w, 0xeb31d82e, "Koop") + b := w.Bytes() + + err := ioutil.WriteFile("crc32_table_ppc64le.s", b, 0666) + if err != nil { + fmt.Printf("can't write output: %s\n", err) + } +} + +func genCrc32ConstTable(w *bytes.Buffer, poly uint32, polyid string) { + + ref_poly := reflect_bits(uint64(poly), 32) + fmt.Fprintf(w, "\n\t/* Reduce %d kbits to 1024 bits */\n", blocking*8) + j := 0 + for i := (blocking * 8) - 1024; i > 0; i -= 1024 { + a := reflect_bits(get_remainder(ref_poly, 32, uint(i)), 32) << 1 + b := reflect_bits(get_remainder(ref_poly, 32, uint(i+64)), 32) << 1 + + fmt.Fprintf(w, "\t/* x^%d mod p(x)%s, x^%d mod p(x)%s */\n", uint(i+64), "", uint(i), "") + fmt.Fprintf(w, "DATA ·%sConst+%d(SB)/8,$0x%016x\n", polyid, j*8, b) + fmt.Fprintf(w, "DATA ·%sConst+%d(SB)/8,$0x%016x\n", polyid, (j+1)*8, a) + + j += 2 + fmt.Fprintf(w, "\n") + } + + for i := (1024 * 2) - 128; i >= 0; i -= 128 { + a := reflect_bits(get_remainder(ref_poly, 32, uint(i+32)), 32) + b := reflect_bits(get_remainder(ref_poly, 32, uint(i+64)), 32) + c := reflect_bits(get_remainder(ref_poly, 32, uint(i+96)), 32) + d := reflect_bits(get_remainder(ref_poly, 32, uint(i+128)), 32) + + fmt.Fprintf(w, "\t/* x^%d mod p(x)%s, x^%d mod p(x)%s, x^%d mod p(x)%s, x^%d mod p(x)%s */\n", i+128, "", i+96, "", i+64, "", i+32, "") + fmt.Fprintf(w, "DATA ·%sConst+%d(SB)/8,$0x%08x%08x\n", polyid, j*8, c, d) + fmt.Fprintf(w, "DATA ·%sConst+%d(SB)/8,$0x%08x%08x\n", polyid, (j+1)*8, a, b) + + j += 2 + fmt.Fprintf(w, "\n") + } + + fmt.Fprintf(w, "GLOBL ·%sConst(SB),RODATA,$4336\n", polyid) + fmt.Fprintf(w, "\n /* Barrett constant m - (4^32)/n */\n") + fmt.Fprintf(w, "DATA ·%sBarConst(SB)/8,$0x%016x\n", polyid, reflect_bits(get_quotient(ref_poly, 32, 64), 33)) + fmt.Fprintf(w, "DATA ·%sBarConst+8(SB)/8,$0x0000000000000000\n", polyid) + fmt.Fprintf(w, "DATA ·%sBarConst+16(SB)/8,$0x%016x\n", polyid, reflect_bits((uint64(1)<<32)|ref_poly, 33)) // reflected? + fmt.Fprintf(w, "DATA ·%sBarConst+24(SB)/8,$0x0000000000000000\n", polyid) + fmt.Fprintf(w, "GLOBL ·%sBarConst(SB),RODATA,$32\n", polyid) +} diff --git a/libgo/go/hash/fnv/fnv.go b/libgo/go/hash/fnv/fnv.go index f1fbb25..3d2df73 100644 --- a/libgo/go/hash/fnv/fnv.go +++ b/libgo/go/hash/fnv/fnv.go @@ -13,17 +13,23 @@ import ( ) type ( - sum32 uint32 - sum32a uint32 - sum64 uint64 - sum64a uint64 + sum32 uint32 + sum32a uint32 + sum64 uint64 + sum64a uint64 + sum128 [2]uint64 + sum128a [2]uint64 ) const ( - offset32 = 2166136261 - offset64 = 14695981039346656037 - prime32 = 16777619 - prime64 = 1099511628211 + offset32 = 2166136261 + offset64 = 14695981039346656037 + offset128Lower = 0x62b821756295c58d + offset128Higher = 0x6c62272e07bb0142 + prime32 = 16777619 + prime64 = 1099511628211 + prime128Lower = 0x13b + prime128Shift = 24 ) // New32 returns a new 32-bit FNV-1 hash.Hash. @@ -54,10 +60,30 @@ func New64a() hash.Hash64 { return &s } -func (s *sum32) Reset() { *s = offset32 } -func (s *sum32a) Reset() { *s = offset32 } -func (s *sum64) Reset() { *s = offset64 } -func (s *sum64a) Reset() { *s = offset64 } +// New128 returns a new 128-bit FNV-1 hash.Hash. +// Its Sum method will lay the value out in big-endian byte order. +func New128() hash.Hash { + var s sum128 + s[0] = offset128Higher + s[1] = offset128Lower + return &s +} + +// New128a returns a new 128-bit FNV-1a hash.Hash. +// Its Sum method will lay the value out in big-endian byte order. +func New128a() hash.Hash { + var s sum128a + s[0] = offset128Higher + s[1] = offset128Lower + return &s +} + +func (s *sum32) Reset() { *s = offset32 } +func (s *sum32a) Reset() { *s = offset32 } +func (s *sum64) Reset() { *s = offset64 } +func (s *sum64a) Reset() { *s = offset64 } +func (s *sum128) Reset() { s[0] = offset128Higher; s[1] = offset128Lower } +func (s *sum128a) Reset() { s[0] = offset128Higher; s[1] = offset128Lower } func (s *sum32) Sum32() uint32 { return uint32(*s) } func (s *sum32a) Sum32() uint32 { return uint32(*s) } @@ -104,15 +130,57 @@ func (s *sum64a) Write(data []byte) (int, error) { return len(data), nil } -func (s *sum32) Size() int { return 4 } -func (s *sum32a) Size() int { return 4 } -func (s *sum64) Size() int { return 8 } -func (s *sum64a) Size() int { return 8 } +func (s *sum128) Write(data []byte) (int, error) { + for _, c := range data { + // Compute the multiplication in 4 parts to simplify carrying + s1l := (s[1] & 0xffffffff) * prime128Lower + s1h := (s[1] >> 32) * prime128Lower + s0l := (s[0]&0xffffffff)*prime128Lower + (s[1]&0xffffffff)<<prime128Shift + s0h := (s[0]>>32)*prime128Lower + (s[1]>>32)<<prime128Shift + // Carries + s1h += s1l >> 32 + s0l += s1h >> 32 + s0h += s0l >> 32 + // Update the values + s[1] = (s1l & 0xffffffff) + (s1h << 32) + s[0] = (s0l & 0xffffffff) + (s0h << 32) + s[1] ^= uint64(c) + } + return len(data), nil +} + +func (s *sum128a) Write(data []byte) (int, error) { + for _, c := range data { + s[1] ^= uint64(c) + // Compute the multiplication in 4 parts to simplify carrying + s1l := (s[1] & 0xffffffff) * prime128Lower + s1h := (s[1] >> 32) * prime128Lower + s0l := (s[0]&0xffffffff)*prime128Lower + (s[1]&0xffffffff)<<prime128Shift + s0h := (s[0]>>32)*prime128Lower + (s[1]>>32)<<prime128Shift + // Carries + s1h += s1l >> 32 + s0l += s1h >> 32 + s0h += s0l >> 32 + // Update the values + s[1] = (s1l & 0xffffffff) + (s1h << 32) + s[0] = (s0l & 0xffffffff) + (s0h << 32) + } + return len(data), nil +} -func (s *sum32) BlockSize() int { return 1 } -func (s *sum32a) BlockSize() int { return 1 } -func (s *sum64) BlockSize() int { return 1 } -func (s *sum64a) BlockSize() int { return 1 } +func (s *sum32) Size() int { return 4 } +func (s *sum32a) Size() int { return 4 } +func (s *sum64) Size() int { return 8 } +func (s *sum64a) Size() int { return 8 } +func (s *sum128) Size() int { return 16 } +func (s *sum128a) Size() int { return 16 } + +func (s *sum32) BlockSize() int { return 1 } +func (s *sum32a) BlockSize() int { return 1 } +func (s *sum64) BlockSize() int { return 1 } +func (s *sum64a) BlockSize() int { return 1 } +func (s *sum128) BlockSize() int { return 1 } +func (s *sum128a) BlockSize() int { return 1 } func (s *sum32) Sum(in []byte) []byte { v := uint32(*s) @@ -133,3 +201,17 @@ func (s *sum64a) Sum(in []byte) []byte { v := uint64(*s) return append(in, byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) } + +func (s *sum128) Sum(in []byte) []byte { + return append(in, + byte(s[0]>>56), byte(s[0]>>48), byte(s[0]>>40), byte(s[0]>>32), byte(s[0]>>24), byte(s[0]>>16), byte(s[0]>>8), byte(s[0]), + 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]), + ) +} + +func (s *sum128a) Sum(in []byte) []byte { + return append(in, + byte(s[0]>>56), byte(s[0]>>48), byte(s[0]>>40), byte(s[0]>>32), byte(s[0]>>24), byte(s[0]>>16), byte(s[0]>>8), byte(s[0]), + 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]), + ) +} diff --git a/libgo/go/hash/fnv/fnv_test.go b/libgo/go/hash/fnv/fnv_test.go index 89d39b3..7da15ba 100644 --- a/libgo/go/hash/fnv/fnv_test.go +++ b/libgo/go/hash/fnv/fnv_test.go @@ -44,6 +44,20 @@ var golden64a = []golden{ {[]byte{0xe7, 0x1f, 0xa2, 0x19, 0x05, 0x41, 0x57, 0x4b}, "abc"}, } +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"}, +} + +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"}, +} + func TestGolden32(t *testing.T) { testGolden(t, New32(), golden32) } @@ -60,6 +74,14 @@ func TestGolden64a(t *testing.T) { testGolden(t, New64a(), golden64a) } +func TestGolden128(t *testing.T) { + testGolden(t, New128(), golden128) +} + +func TestGolden128a(t *testing.T) { + testGolden(t, New128a(), golden128a) +} + func testGolden(t *testing.T, hash hash.Hash, gold []golden) { for _, g := range gold { hash.Reset() @@ -91,6 +113,13 @@ func TestIntegrity64(t *testing.T) { func TestIntegrity64a(t *testing.T) { testIntegrity(t, New64a()) } +func TestIntegrity128(t *testing.T) { + testIntegrity(t, New128()) +} + +func TestIntegrity128a(t *testing.T) { + testIntegrity(t, New128a()) +} func testIntegrity(t *testing.T, h hash.Hash) { data := []byte{'1', '2', 3, 4, 5} @@ -129,6 +158,8 @@ func testIntegrity(t *testing.T, h hash.Hash) { if sum64 != binary.BigEndian.Uint64(sum) { t.Fatalf("Sum()=0x%x, but Sum64()=0x%x", sum, sum64) } + case 16: + // There's no Sum128 function, so we don't need to test anything here. } } @@ -148,6 +179,14 @@ func BenchmarkFnv64aKB(b *testing.B) { benchmarkKB(b, New64a()) } +func BenchmarkFnv128KB(b *testing.B) { + benchmarkKB(b, New128()) +} + +func BenchmarkFnv128aKB(b *testing.B) { + benchmarkKB(b, New128a()) +} + func benchmarkKB(b *testing.B, h hash.Hash) { b.SetBytes(1024) data := make([]byte, 1024) |