aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/hash
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2017-09-14 17:11:35 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2017-09-14 17:11:35 +0000
commitbc998d034f45d1828a8663b2eed928faf22a7d01 (patch)
tree8d262a22ca7318f4bcd64269fe8fe9e45bcf8d0f /libgo/go/hash
parenta41a6142df74219f596e612d3a7775f68ca6e96f (diff)
downloadgcc-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.go30
-rw-r--r--libgo/go/hash/crc32/crc32_amd64p32.go14
-rw-r--r--libgo/go/hash/crc32/crc32_arm64.go53
-rw-r--r--libgo/go/hash/crc32/crc32_otherarch.go2
-rw-r--r--libgo/go/hash/crc32/crc32_ppc64le.go89
-rw-r--r--libgo/go/hash/crc32/crc32_test.go86
-rw-r--r--libgo/go/hash/crc32/gen_const_ppc64le.go150
-rw-r--r--libgo/go/hash/fnv/fnv.go122
-rw-r--r--libgo/go/hash/fnv/fnv_test.go39
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)