From 5bf3a2cfb5329180558e5d25911326e2ef25b5ff Mon Sep 17 00:00:00 2001
From: Silas Davis <silas@monax.io>
Date: Sun, 8 Apr 2018 18:03:37 +0100
Subject: [PATCH] Add RingMutex

Signed-off-by: Silas Davis <silas@monax.io>
---
 Gopkg.lock                                    |   8 +-
 sync/ring_mutex.go                            |  71 ++++++
 sync/ring_mutex_test.go                       |  95 +++++++
 vendor/github.com/OneOfOne/xxhash/LICENSE     | 187 ++++++++++++++
 vendor/github.com/OneOfOne/xxhash/xxhash.go   | 191 ++++++++++++++
 .../github.com/OneOfOne/xxhash/xxhash_go17.go | 160 ++++++++++++
 .../github.com/OneOfOne/xxhash/xxhash_safe.go | 183 ++++++++++++++
 .../OneOfOne/xxhash/xxhash_unsafe.go          | 234 ++++++++++++++++++
 8 files changed, 1128 insertions(+), 1 deletion(-)
 create mode 100644 sync/ring_mutex.go
 create mode 100644 sync/ring_mutex_test.go
 create mode 100644 vendor/github.com/OneOfOne/xxhash/LICENSE
 create mode 100644 vendor/github.com/OneOfOne/xxhash/xxhash.go
 create mode 100644 vendor/github.com/OneOfOne/xxhash/xxhash_go17.go
 create mode 100644 vendor/github.com/OneOfOne/xxhash/xxhash_safe.go
 create mode 100644 vendor/github.com/OneOfOne/xxhash/xxhash_unsafe.go

diff --git a/Gopkg.lock b/Gopkg.lock
index 99eff5ee..f0fafcb1 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -7,6 +7,12 @@
   revision = "b26d9c308763d68093482582cea63d69be07a0f0"
   version = "v0.3.0"
 
+[[projects]]
+  name = "github.com/OneOfOne/xxhash"
+  packages = ["."]
+  revision = "4e9e81466dc37c9fd94ce9ecf42020ef54112203"
+  version = "v1.2.1"
+
 [[projects]]
   branch = "master"
   name = "github.com/btcsuite/btcd"
@@ -474,6 +480,6 @@
 [solve-meta]
   analyzer-name = "dep"
   analyzer-version = 1
-  inputs-digest = "5db3ac43679cafd5f68104e93a248b62266d313aef2d8ba3bd7f3986a5a99834"
+  inputs-digest = "f8f72aa18c801bd7e61464decf6e8108d2f38410ec921a8e7015a1e5b8878db3"
   solver-name = "gps-cdcl"
   solver-version = 1
diff --git a/sync/ring_mutex.go b/sync/ring_mutex.go
new file mode 100644
index 00000000..01abecd9
--- /dev/null
+++ b/sync/ring_mutex.go
@@ -0,0 +1,71 @@
+package sync
+
+import (
+	"sync"
+
+	"github.com/OneOfOne/xxhash"
+)
+
+type RingMutex struct {
+	mtxs       []sync.RWMutex
+	hasherPool sync.Pool
+	mutexCount uint64
+}
+
+// Create a RW mutex that provides a pseudo-independent set of mutexes for addresses
+// where the address space is mapped into possibly much smaller set of backing
+// mutexes using the xxhash (non-cryptographic)
+// hash function // modulo size. If some addresses collide modulo size they will be unnecessary
+// contention between those addresses, but you can trade space against contention
+// as desired.
+func NewRingMutex(mutexCount int) *RingMutex {
+	return &RingMutex{
+		// max slice length is bounded by max(int) thus the argument type
+		mtxs: make([]sync.RWMutex, mutexCount, mutexCount),
+		hasherPool: sync.Pool{
+			New: func() interface{} {
+				return xxhash.New64()
+			},
+		},
+		mutexCount: uint64(mutexCount),
+	}
+}
+
+func (mtx *RingMutex) Lock(address []byte) {
+	mtx.Mutex(address).Lock()
+}
+
+func (mtx *RingMutex) Unlock(address []byte) {
+	mtx.Mutex(address).Unlock()
+}
+
+func (mtx *RingMutex) RLock(address []byte) {
+	mtx.Mutex(address).RLock()
+}
+
+func (mtx *RingMutex) RUnlock(address []byte) {
+	mtx.Mutex(address).RUnlock()
+}
+
+// Return the size of the underlying array of mutexes
+func (mtx *RingMutex) MutexCount() uint64 {
+	return mtx.mutexCount
+}
+
+func (mtx *RingMutex) Mutex(address []byte) *sync.RWMutex {
+	return &mtx.mtxs[mtx.index(address)]
+}
+
+func (mtx *RingMutex) index(address []byte) uint64 {
+	return mtx.hash(address) % mtx.mutexCount
+}
+
+func (mtx *RingMutex) hash(address []byte) uint64 {
+	h := mtx.hasherPool.Get().(*xxhash.XXHash64)
+	defer func() {
+		h.Reset()
+		mtx.hasherPool.Put(h)
+	}()
+	h.Write(address)
+	return h.Sum64()
+}
diff --git a/sync/ring_mutex_test.go b/sync/ring_mutex_test.go
new file mode 100644
index 00000000..7eea39b0
--- /dev/null
+++ b/sync/ring_mutex_test.go
@@ -0,0 +1,95 @@
+package sync
+
+import (
+	"encoding/base64"
+	"testing"
+
+	"time"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestRingMutex_Lock(t *testing.T) {
+	// Using fewer mutexes than addresses to lock against should cause contention
+	mutexCount := 10
+	numAddresses := byte(20)
+	mtx := NewRingMutex(mutexCount)
+	writeCh := make(chan []byte)
+	checksum := 0
+
+	// We'll try to acquire a locks on all of our unique addresses, knowing that
+	// some of them will share an underlying RWMutex
+	for i := byte(0); i < numAddresses; i++ {
+		address := []byte{i}
+		go func() {
+			mtx.Lock(address)
+			writeCh <- address
+		}()
+	}
+
+	// We should receive a message from all of those addresses for which we could
+	// acquire a lock, this should be almost surely deterministic since we are
+	// launching our goroutines sequentially from a single goroutine (if this bit
+	// breaks we can add a short pause between the 'go' statements above, for the
+	// purposes of the predictability of this test)
+	addresses := receiveAddresses(writeCh)
+	checksum += len(addresses)
+	// we hit lock contention on the tenth address so get 9 back
+	assert.Equal(t, 9, len(addresses))
+	// Unlock the 9 locked mutexes
+	unlockAddresses(mtx, addresses)
+
+	// Which should trigger another batch to make progress
+	addresses = receiveAddresses(writeCh)
+	checksum += len(addresses)
+	// Again the number we get back (but not the order) should be deterministic
+	// because we are unlocking sequentially from a single goroutine
+	assert.Equal(t, 7, len(addresses))
+	unlockAddresses(mtx, addresses)
+
+	// And again
+	addresses = receiveAddresses(writeCh)
+	checksum += len(addresses)
+	assert.Equal(t, 3, len(addresses))
+	unlockAddresses(mtx, addresses)
+
+	// And so on
+	addresses = receiveAddresses(writeCh)
+	checksum += len(addresses)
+	assert.Equal(t, 1, len(addresses))
+	unlockAddresses(mtx, addresses)
+
+	// Until we have unblocked all of the goroutines we released
+	addresses = receiveAddresses(writeCh)
+	checksum += len(addresses)
+	assert.Equal(t, 0, len(addresses))
+	unlockAddresses(mtx, addresses)
+	checksum += len(addresses)
+
+	// Check we've heard back from all of them
+	assert.EqualValues(t, numAddresses, checksum)
+}
+
+func TestRingMutex_hash(t *testing.T) {
+	mtx := NewRingMutex(10)
+	address, err := base64.StdEncoding.DecodeString("/+ulTkCzpYg2ePaZtqS8dycJBLY9387yZPst8LX5YL0=")
+	assert.NoError(t, err)
+	assert.EqualValues(t, 8509033946529530334, mtx.hash(address))
+}
+
+func receiveAddresses(returnCh chan []byte) [][]byte {
+	var addresses [][]byte
+	for {
+		select {
+		case address := <-returnCh:
+			addresses = append(addresses, address)
+		case <-time.After(50 * time.Millisecond):
+			return addresses
+		}
+	}
+}
+func unlockAddresses(mtx *RingMutex, addresses [][]byte) {
+	for _, address := range addresses {
+		mtx.Unlock(address)
+	}
+}
diff --git a/vendor/github.com/OneOfOne/xxhash/LICENSE b/vendor/github.com/OneOfOne/xxhash/LICENSE
new file mode 100644
index 00000000..9e30b4f3
--- /dev/null
+++ b/vendor/github.com/OneOfOne/xxhash/LICENSE
@@ -0,0 +1,187 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
diff --git a/vendor/github.com/OneOfOne/xxhash/xxhash.go b/vendor/github.com/OneOfOne/xxhash/xxhash.go
new file mode 100644
index 00000000..817783d1
--- /dev/null
+++ b/vendor/github.com/OneOfOne/xxhash/xxhash.go
@@ -0,0 +1,191 @@
+package xxhash
+
+const (
+	prime32x1 uint32 = 2654435761
+	prime32x2 uint32 = 2246822519
+	prime32x3 uint32 = 3266489917
+	prime32x4 uint32 = 668265263
+	prime32x5 uint32 = 374761393
+
+	prime64x1 uint64 = 11400714785074694791
+	prime64x2 uint64 = 14029467366897019727
+	prime64x3 uint64 = 1609587929392839161
+	prime64x4 uint64 = 9650029242287828579
+	prime64x5 uint64 = 2870177450012600261
+
+	maxInt32 int32 = (1<<31 - 1)
+)
+
+// Checksum32 returns the checksum of the input data with the seed set to 0.
+func Checksum32(in []byte) uint32 {
+	return Checksum32S(in, 0)
+}
+
+// ChecksumString32 returns the checksum of the input data, without creating a copy, with the seed set to 0.
+func ChecksumString32(s string) uint32 {
+	return ChecksumString32S(s, 0)
+}
+
+type XXHash32 struct {
+	mem            [16]byte
+	ln, memIdx     int32
+	v1, v2, v3, v4 uint32
+	seed           uint32
+}
+
+// Size returns the number of bytes Sum will return.
+func (xx *XXHash32) Size() int {
+	return 4
+}
+
+// BlockSize returns the hash's underlying block size.
+// The Write method must be able to accept any amount
+// of data, but it may operate more efficiently if all writes
+// are a multiple of the block size.
+func (xx *XXHash32) BlockSize() int {
+	return 16
+}
+
+// NewS32 creates a new hash.Hash32 computing the 32bit xxHash checksum starting with the specific seed.
+func NewS32(seed uint32) (xx *XXHash32) {
+	xx = &XXHash32{
+		seed: seed,
+	}
+	xx.Reset()
+	return
+}
+
+// New32 creates a new hash.Hash32 computing the 32bit xxHash checksum starting with the seed set to 0.
+func New32() *XXHash32 {
+	return NewS32(0)
+}
+
+func (xx *XXHash32) Reset() {
+	xx.v1 = xx.seed + prime32x1 + prime32x2
+	xx.v2 = xx.seed + prime32x2
+	xx.v3 = xx.seed
+	xx.v4 = xx.seed - prime32x1
+	xx.ln, xx.memIdx = 0, 0
+}
+
+// Sum appends the current hash to b and returns the resulting slice.
+// It does not change the underlying hash state.
+func (xx *XXHash32) Sum(in []byte) []byte {
+	s := xx.Sum32()
+	return append(in, byte(s>>24), byte(s>>16), byte(s>>8), byte(s))
+}
+
+// Checksum64 an alias for Checksum64S(in, 0)
+func Checksum64(in []byte) uint64 {
+	return Checksum64S(in, 0)
+}
+
+// ChecksumString64 returns the checksum of the input data, without creating a copy, with the seed set to 0.
+func ChecksumString64(s string) uint64 {
+	return ChecksumString64S(s, 0)
+}
+
+type XXHash64 struct {
+	v1, v2, v3, v4 uint64
+	seed           uint64
+	ln             uint64
+	mem            [32]byte
+	memIdx         int8
+}
+
+var zeroVs64 = [...]uint64{
+	// workaround static overflow checker
+	func(s uint64) uint64 { return s + prime64x1 + prime64x2 }(0),
+	prime64x2,
+	0,
+	func(s uint64) uint64 { return s - prime64x1 }(0),
+}
+
+// Size returns the number of bytes Sum will return.
+func (xx *XXHash64) Size() int {
+	return 8
+}
+
+// BlockSize returns the hash's underlying block size.
+// The Write method must be able to accept any amount
+// of data, but it may operate more efficiently if all writes
+// are a multiple of the block size.
+func (xx *XXHash64) BlockSize() int {
+	return 32
+}
+
+// NewS64 creates a new hash.Hash64 computing the 64bit xxHash checksum starting with the specific seed.
+func NewS64(seed uint64) (xx *XXHash64) {
+	xx = &XXHash64{
+		seed: seed,
+	}
+	xx.Reset()
+	return
+}
+
+// New64 creates a new hash.Hash64 computing the 64bit xxHash checksum starting with the seed set to 0x0.
+func New64() *XXHash64 {
+	return NewS64(0)
+}
+
+func (xx *XXHash64) Reset() {
+	xx.ln, xx.memIdx = 0, 0
+	xx.v1, xx.v2, xx.v3, xx.v4 = resetVs64(xx.seed)
+}
+
+// Sum appends the current hash to b and returns the resulting slice.
+// It does not change the underlying hash state.
+func (xx *XXHash64) Sum(in []byte) []byte {
+	s := xx.Sum64()
+	return append(in, byte(s>>56), byte(s>>48), byte(s>>40), byte(s>>32), byte(s>>24), byte(s>>16), byte(s>>8), byte(s))
+}
+
+// force the compiler to use ROTL instructions
+
+func rotl32_1(x uint32) uint32  { return (x << 1) | (x >> (32 - 1)) }
+func rotl32_7(x uint32) uint32  { return (x << 7) | (x >> (32 - 7)) }
+func rotl32_11(x uint32) uint32 { return (x << 11) | (x >> (32 - 11)) }
+func rotl32_12(x uint32) uint32 { return (x << 12) | (x >> (32 - 12)) }
+func rotl32_13(x uint32) uint32 { return (x << 13) | (x >> (32 - 13)) }
+func rotl32_17(x uint32) uint32 { return (x << 17) | (x >> (32 - 17)) }
+func rotl32_18(x uint32) uint32 { return (x << 18) | (x >> (32 - 18)) }
+
+func rotl64_1(x uint64) uint64  { return (x << 1) | (x >> (64 - 1)) }
+func rotl64_7(x uint64) uint64  { return (x << 7) | (x >> (64 - 7)) }
+func rotl64_11(x uint64) uint64 { return (x << 11) | (x >> (64 - 11)) }
+func rotl64_12(x uint64) uint64 { return (x << 12) | (x >> (64 - 12)) }
+func rotl64_18(x uint64) uint64 { return (x << 18) | (x >> (64 - 18)) }
+func rotl64_23(x uint64) uint64 { return (x << 23) | (x >> (64 - 23)) }
+func rotl64_27(x uint64) uint64 { return (x << 27) | (x >> (64 - 27)) }
+func rotl64_31(x uint64) uint64 { return (x << 31) | (x >> (64 - 31)) }
+
+func mix64(h uint64) uint64 {
+	h ^= h >> 33
+	h *= prime64x2
+	h ^= h >> 29
+	h *= prime64x3
+	h ^= h >> 32
+	return h
+}
+
+func resetVs64(seed uint64) (v1, v2, v3, v4 uint64) {
+	if seed == 0 {
+		return zeroVs64[0], zeroVs64[1], zeroVs64[2], zeroVs64[3]
+	}
+	return (seed + prime64x1 + prime64x2), (seed + prime64x2), (seed), (seed - prime64x1)
+}
+
+// borrowed from cespare
+func round64(h, v uint64) uint64 {
+	h += v * prime64x2
+	h = rotl64_31(h)
+	h *= prime64x1
+	return h
+}
+
+func mergeRound64(h, v uint64) uint64 {
+	v = round64(0, v)
+	h ^= v
+	h = h*prime64x1 + prime64x4
+	return h
+}
diff --git a/vendor/github.com/OneOfOne/xxhash/xxhash_go17.go b/vendor/github.com/OneOfOne/xxhash/xxhash_go17.go
new file mode 100644
index 00000000..d4ebc053
--- /dev/null
+++ b/vendor/github.com/OneOfOne/xxhash/xxhash_go17.go
@@ -0,0 +1,160 @@
+package xxhash
+
+func u32(in []byte) uint32 {
+	return uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24
+}
+func u64(in []byte) uint64 {
+	return uint64(in[0]) | uint64(in[1])<<8 | uint64(in[2])<<16 | uint64(in[3])<<24 | uint64(in[4])<<32 | uint64(in[5])<<40 | uint64(in[6])<<48 | uint64(in[7])<<56
+}
+
+// Checksum32S returns the checksum of the input bytes with the specific seed.
+func Checksum32S(in []byte, seed uint32) (h uint32) {
+	var i int
+
+	if len(in) > 15 {
+		var (
+			v1 = seed + prime32x1 + prime32x2
+			v2 = seed + prime32x2
+			v3 = seed + 0
+			v4 = seed - prime32x1
+		)
+		for ; i < len(in)-15; i += 16 {
+			in := in[i : i+16 : len(in)]
+			v1 += u32(in[0:4:len(in)]) * prime32x2
+			v1 = rotl32_13(v1) * prime32x1
+
+			v2 += u32(in[4:8:len(in)]) * prime32x2
+			v2 = rotl32_13(v2) * prime32x1
+
+			v3 += u32(in[8:12:len(in)]) * prime32x2
+			v3 = rotl32_13(v3) * prime32x1
+
+			v4 += u32(in[12:16:len(in)]) * prime32x2
+			v4 = rotl32_13(v4) * prime32x1
+		}
+
+		h = rotl32_1(v1) + rotl32_7(v2) + rotl32_12(v3) + rotl32_18(v4)
+
+	} else {
+		h = seed + prime32x5
+	}
+
+	h += uint32(len(in))
+	for ; i <= len(in)-4; i += 4 {
+		in := in[i : i+4 : len(in)]
+		h += u32(in[0:4:len(in)]) * prime32x3
+		h = rotl32_17(h) * prime32x4
+	}
+
+	for ; i < len(in); i++ {
+		h += uint32(in[i]) * prime32x5
+		h = rotl32_11(h) * prime32x1
+	}
+
+	h ^= h >> 15
+	h *= prime32x2
+	h ^= h >> 13
+	h *= prime32x3
+	h ^= h >> 16
+
+	return
+}
+
+func (xx *XXHash32) Write(in []byte) (n int, err error) {
+	i, ml := 0, int(xx.memIdx)
+	n = len(in)
+	xx.ln += int32(n)
+
+	if d := 16 - ml; ml > 0 && ml+len(in) > 16 {
+		xx.memIdx += int32(copy(xx.mem[xx.memIdx:], in[:d]))
+		ml, in = 16, in[d:len(in):len(in)]
+	} else if ml+len(in) < 16 {
+		xx.memIdx += int32(copy(xx.mem[xx.memIdx:], in))
+		return
+	}
+
+	if ml > 0 {
+		i += 16 - ml
+		xx.memIdx += int32(copy(xx.mem[xx.memIdx:len(xx.mem):len(xx.mem)], in))
+		in := xx.mem[:16:len(xx.mem)]
+
+		xx.v1 += u32(in[0:4:len(in)]) * prime32x2
+		xx.v1 = rotl32_13(xx.v1) * prime32x1
+
+		xx.v2 += u32(in[4:8:len(in)]) * prime32x2
+		xx.v2 = rotl32_13(xx.v2) * prime32x1
+
+		xx.v3 += u32(in[8:12:len(in)]) * prime32x2
+		xx.v3 = rotl32_13(xx.v3) * prime32x1
+
+		xx.v4 += u32(in[12:16:len(in)]) * prime32x2
+		xx.v4 = rotl32_13(xx.v4) * prime32x1
+
+		xx.memIdx = 0
+	}
+
+	for ; i <= len(in)-16; i += 16 {
+		in := in[i : i+16 : len(in)]
+		xx.v1 += u32(in[0:4:len(in)]) * prime32x2
+		xx.v1 = rotl32_13(xx.v1) * prime32x1
+
+		xx.v2 += u32(in[4:8:len(in)]) * prime32x2
+		xx.v2 = rotl32_13(xx.v2) * prime32x1
+
+		xx.v3 += u32(in[8:12:len(in)]) * prime32x2
+		xx.v3 = rotl32_13(xx.v3) * prime32x1
+
+		xx.v4 += u32(in[12:16:len(in)]) * prime32x2
+		xx.v4 = rotl32_13(xx.v4) * prime32x1
+	}
+
+	if len(in)-i != 0 {
+		xx.memIdx += int32(copy(xx.mem[xx.memIdx:], in[i:len(in):len(in)]))
+	}
+
+	return
+}
+
+func (xx *XXHash32) Sum32() (h uint32) {
+	var i int32
+	if xx.ln > 15 {
+		h = rotl32_1(xx.v1) + rotl32_7(xx.v2) + rotl32_12(xx.v3) + rotl32_18(xx.v4)
+	} else {
+		h = xx.seed + prime32x5
+	}
+
+	h += uint32(xx.ln)
+
+	if xx.memIdx > 0 {
+		for ; i < xx.memIdx-3; i += 4 {
+			in := xx.mem[i : i+4 : len(xx.mem)]
+			h += u32(in[0:4:len(in)]) * prime32x3
+			h = rotl32_17(h) * prime32x4
+		}
+
+		for ; i < xx.memIdx; i++ {
+			h += uint32(xx.mem[i]) * prime32x5
+			h = rotl32_11(h) * prime32x1
+		}
+	}
+	h ^= h >> 15
+	h *= prime32x2
+	h ^= h >> 13
+	h *= prime32x3
+	h ^= h >> 16
+
+	return
+}
+
+// Checksum64S returns the 64bit xxhash checksum for a single input
+func Checksum64S(in []byte, seed uint64) uint64 {
+	if len(in) == 0 && seed == 0 {
+		return 0xef46db3751d8e999
+	}
+
+	if len(in) > 31 {
+		return checksum64(in, seed)
+	}
+
+	return checksum64Short(in, seed)
+}
diff --git a/vendor/github.com/OneOfOne/xxhash/xxhash_safe.go b/vendor/github.com/OneOfOne/xxhash/xxhash_safe.go
new file mode 100644
index 00000000..5252b774
--- /dev/null
+++ b/vendor/github.com/OneOfOne/xxhash/xxhash_safe.go
@@ -0,0 +1,183 @@
+// +build appengine safe
+
+package xxhash
+
+// Backend returns the current version of xxhash being used.
+const Backend = "GoSafe"
+
+func ChecksumString32S(s string, seed uint32) uint32 {
+	return Checksum32S([]byte(s), seed)
+}
+
+func (xx *XXHash32) WriteString(s string) (int, error) {
+	if len(s) == 0 {
+		return 0, nil
+	}
+	return xx.Write([]byte(s))
+}
+
+func ChecksumString64S(s string, seed uint64) uint64 {
+	return Checksum64S([]byte(s), seed)
+}
+
+func (xx *XXHash64) WriteString(s string) (int, error) {
+	if len(s) == 0 {
+		return 0, nil
+	}
+	return xx.Write([]byte(s))
+}
+
+func checksum64(in []byte, seed uint64) (h uint64) {
+	var (
+		v1, v2, v3, v4 = resetVs64(seed)
+
+		i int
+	)
+
+	for ; i < len(in)-31; i += 32 {
+		in := in[i : i+32 : len(in)]
+		v1 = round64(v1, u64(in[0:8:len(in)]))
+		v2 = round64(v2, u64(in[8:16:len(in)]))
+		v3 = round64(v3, u64(in[16:24:len(in)]))
+		v4 = round64(v4, u64(in[24:32:len(in)]))
+	}
+
+	h = rotl64_1(v1) + rotl64_7(v2) + rotl64_12(v3) + rotl64_18(v4)
+
+	h = mergeRound64(h, v1)
+	h = mergeRound64(h, v2)
+	h = mergeRound64(h, v3)
+	h = mergeRound64(h, v4)
+
+	h += uint64(len(in))
+
+	for ; i < len(in)-7; i += 8 {
+		h ^= round64(0, u64(in[i:len(in):len(in)]))
+		h = rotl64_27(h)*prime64x1 + prime64x4
+	}
+
+	for ; i < len(in)-3; i += 4 {
+		h ^= uint64(u32(in[i:len(in):len(in)])) * prime64x1
+		h = rotl64_23(h)*prime64x2 + prime64x3
+	}
+
+	for ; i < len(in); i++ {
+		h ^= uint64(in[i]) * prime64x5
+		h = rotl64_11(h) * prime64x1
+	}
+
+	return mix64(h)
+}
+
+func checksum64Short(in []byte, seed uint64) uint64 {
+	var (
+		h = seed + prime64x5 + uint64(len(in))
+		i int
+	)
+
+	for ; i < len(in)-7; i += 8 {
+		k := u64(in[i : i+8 : len(in)])
+		h ^= round64(0, k)
+		h = rotl64_27(h)*prime64x1 + prime64x4
+	}
+
+	for ; i < len(in)-3; i += 4 {
+		h ^= uint64(u32(in[i:i+4:len(in)])) * prime64x1
+		h = rotl64_23(h)*prime64x2 + prime64x3
+	}
+
+	for ; i < len(in); i++ {
+		h ^= uint64(in[i]) * prime64x5
+		h = rotl64_11(h) * prime64x1
+	}
+
+	return mix64(h)
+}
+
+func (xx *XXHash64) Write(in []byte) (n int, err error) {
+	var (
+		ml = int(xx.memIdx)
+		d  = 32 - ml
+	)
+
+	n = len(in)
+	xx.ln += uint64(n)
+
+	if ml+len(in) < 32 {
+		xx.memIdx += int8(copy(xx.mem[xx.memIdx:len(xx.mem):len(xx.mem)], in))
+		return
+	}
+
+	i, v1, v2, v3, v4 := 0, xx.v1, xx.v2, xx.v3, xx.v4
+	if ml > 0 && ml+len(in) > 32 {
+		xx.memIdx += int8(copy(xx.mem[xx.memIdx:len(xx.mem):len(xx.mem)], in[:d:len(in)]))
+		in = in[d:len(in):len(in)]
+
+		in := xx.mem[0:32:len(xx.mem)]
+
+		v1 = round64(v1, u64(in[0:8:len(in)]))
+		v2 = round64(v2, u64(in[8:16:len(in)]))
+		v3 = round64(v3, u64(in[16:24:len(in)]))
+		v4 = round64(v4, u64(in[24:32:len(in)]))
+
+		xx.memIdx = 0
+	}
+
+	for ; i < len(in)-31; i += 32 {
+		in := in[i : i+32 : len(in)]
+		v1 = round64(v1, u64(in[0:8:len(in)]))
+		v2 = round64(v2, u64(in[8:16:len(in)]))
+		v3 = round64(v3, u64(in[16:24:len(in)]))
+		v4 = round64(v4, u64(in[24:32:len(in)]))
+	}
+
+	if len(in)-i != 0 {
+		xx.memIdx += int8(copy(xx.mem[xx.memIdx:], in[i:len(in):len(in)]))
+	}
+
+	xx.v1, xx.v2, xx.v3, xx.v4 = v1, v2, v3, v4
+
+	return
+}
+
+func (xx *XXHash64) Sum64() (h uint64) {
+	var i int
+	if xx.ln > 31 {
+		v1, v2, v3, v4 := xx.v1, xx.v2, xx.v3, xx.v4
+		h = rotl64_1(v1) + rotl64_7(v2) + rotl64_12(v3) + rotl64_18(v4)
+
+		h = mergeRound64(h, v1)
+		h = mergeRound64(h, v2)
+		h = mergeRound64(h, v3)
+		h = mergeRound64(h, v4)
+	} else {
+		h = xx.seed + prime64x5
+	}
+
+	h += uint64(xx.ln)
+	if xx.memIdx > 0 {
+		in := xx.mem[:xx.memIdx]
+		for ; i < int(xx.memIdx)-7; i += 8 {
+			in := in[i : i+8 : len(in)]
+			k := u64(in[0:8:len(in)])
+			k *= prime64x2
+			k = rotl64_31(k)
+			k *= prime64x1
+			h ^= k
+			h = rotl64_27(h)*prime64x1 + prime64x4
+		}
+
+		for ; i < int(xx.memIdx)-3; i += 4 {
+			in := in[i : i+4 : len(in)]
+			h ^= uint64(u32(in[0:4:len(in)])) * prime64x1
+			h = rotl64_23(h)*prime64x2 + prime64x3
+		}
+
+		for ; i < int(xx.memIdx); i++ {
+			h ^= uint64(in[i]) * prime64x5
+			h = rotl64_11(h) * prime64x1
+		}
+	}
+
+	return mix64(h)
+}
diff --git a/vendor/github.com/OneOfOne/xxhash/xxhash_unsafe.go b/vendor/github.com/OneOfOne/xxhash/xxhash_unsafe.go
new file mode 100644
index 00000000..59661249
--- /dev/null
+++ b/vendor/github.com/OneOfOne/xxhash/xxhash_unsafe.go
@@ -0,0 +1,234 @@
+// +build !safe
+// +build !appengine
+
+package xxhash
+
+import (
+	"reflect"
+	"unsafe"
+)
+
+// Backend returns the current version of xxhash being used.
+const Backend = "GoUnsafe"
+
+// ChecksumString32S returns the checksum of the input data, without creating a copy, with the specific seed.
+func ChecksumString32S(s string, seed uint32) uint32 {
+	if len(s) == 0 {
+		return Checksum32S(nil, seed)
+	}
+	ss := (*reflect.StringHeader)(unsafe.Pointer(&s))
+	return Checksum32S((*[maxInt32]byte)(unsafe.Pointer(ss.Data))[:len(s):len(s)], seed)
+}
+
+func (xx *XXHash32) WriteString(s string) (int, error) {
+	if len(s) == 0 {
+		return 0, nil
+	}
+
+	ss := (*reflect.StringHeader)(unsafe.Pointer(&s))
+	return xx.Write((*[maxInt32]byte)(unsafe.Pointer(ss.Data))[:len(s):len(s)])
+}
+
+// ChecksumString64S returns the checksum of the input data, without creating a copy, with the specific seed.
+func ChecksumString64S(s string, seed uint64) uint64 {
+	if len(s) == 0 {
+		return Checksum64S(nil, seed)
+	}
+
+	ss := (*reflect.StringHeader)(unsafe.Pointer(&s))
+	return Checksum64S((*[maxInt32]byte)(unsafe.Pointer(ss.Data))[:len(s):len(s)], seed)
+}
+
+func (xx *XXHash64) WriteString(s string) (int, error) {
+	if len(s) == 0 {
+		return 0, nil
+	}
+	ss := (*reflect.StringHeader)(unsafe.Pointer(&s))
+	return xx.Write((*[maxInt32]byte)(unsafe.Pointer(ss.Data))[:len(s)])
+}
+
+func checksum64(in []byte, seed uint64) uint64 {
+	var (
+		wordsLen = len(in) >> 3
+		words    = ((*[maxInt32 / 8]uint64)(unsafe.Pointer(&in[0])))[:wordsLen:wordsLen]
+
+		h uint64 = prime64x5
+
+		v1, v2, v3, v4 = resetVs64(seed)
+
+		i int
+	)
+
+	for ; i < len(words)-3; i += 4 {
+		words := (*[4]uint64)(unsafe.Pointer(&words[i]))
+
+		v1 = round64(v1, words[0])
+		v2 = round64(v2, words[1])
+		v3 = round64(v3, words[2])
+		v4 = round64(v4, words[3])
+	}
+
+	h = rotl64_1(v1) + rotl64_7(v2) + rotl64_12(v3) + rotl64_18(v4)
+
+	h = mergeRound64(h, v1)
+	h = mergeRound64(h, v2)
+	h = mergeRound64(h, v3)
+	h = mergeRound64(h, v4)
+
+	h += uint64(len(in))
+
+	for _, k := range words[i:] {
+		h ^= round64(0, k)
+		h = rotl64_27(h)*prime64x1 + prime64x4
+	}
+
+	if in = in[wordsLen<<3 : len(in) : len(in)]; len(in) > 3 {
+		words := (*[1]uint32)(unsafe.Pointer(&in[0]))
+		h ^= uint64(words[0]) * prime64x1
+		h = rotl64_23(h)*prime64x2 + prime64x3
+
+		in = in[4:len(in):len(in)]
+	}
+
+	for _, b := range in {
+		h ^= uint64(b) * prime64x5
+		h = rotl64_11(h) * prime64x1
+	}
+
+	return mix64(h)
+}
+
+func checksum64Short(in []byte, seed uint64) uint64 {
+	var (
+		h = seed + prime64x5 + uint64(len(in))
+		i int
+	)
+
+	if len(in) > 7 {
+		var (
+			wordsLen = len(in) >> 3
+			words    = ((*[maxInt32 / 8]uint64)(unsafe.Pointer(&in[0])))[:wordsLen:wordsLen]
+		)
+
+		for i := range words {
+			h ^= round64(0, words[i])
+			h = rotl64_27(h)*prime64x1 + prime64x4
+		}
+
+		i = wordsLen << 3
+	}
+
+	if in = in[i:len(in):len(in)]; len(in) > 3 {
+		words := (*[1]uint32)(unsafe.Pointer(&in[0]))
+		h ^= uint64(words[0]) * prime64x1
+		h = rotl64_23(h)*prime64x2 + prime64x3
+
+		in = in[4:len(in):len(in)]
+	}
+
+	for _, b := range in {
+		h ^= uint64(b) * prime64x5
+		h = rotl64_11(h) * prime64x1
+	}
+
+	return mix64(h)
+}
+
+func (xx *XXHash64) Write(in []byte) (n int, err error) {
+	mem, idx := xx.mem[:], int(xx.memIdx)
+
+	xx.ln, n = xx.ln+uint64(len(in)), len(in)
+
+	if idx+len(in) < 32 {
+		xx.memIdx += int8(copy(mem[idx:len(mem):len(mem)], in))
+		return
+	}
+
+	var (
+		v1, v2, v3, v4 = xx.v1, xx.v2, xx.v3, xx.v4
+
+		i int
+	)
+
+	if d := 32 - int(idx); d > 0 && int(idx)+len(in) > 31 {
+		copy(mem[idx:len(mem):len(mem)], in[:len(in):len(in)])
+
+		words := (*[4]uint64)(unsafe.Pointer(&mem[0]))
+
+		v1 = round64(v1, words[0])
+		v2 = round64(v2, words[1])
+		v3 = round64(v3, words[2])
+		v4 = round64(v4, words[3])
+
+		if in, xx.memIdx = in[d:len(in):len(in)], 0; len(in) == 0 {
+			goto RET
+		}
+	}
+
+	for ; i < len(in)-31; i += 32 {
+		words := (*[4]uint64)(unsafe.Pointer(&in[i]))
+
+		v1 = round64(v1, words[0])
+		v2 = round64(v2, words[1])
+		v3 = round64(v3, words[2])
+		v4 = round64(v4, words[3])
+	}
+
+	if len(in)-i != 0 {
+		xx.memIdx += int8(copy(mem[xx.memIdx:len(mem):len(mem)], in[i:len(in):len(in)]))
+	}
+
+RET:
+	xx.v1, xx.v2, xx.v3, xx.v4 = v1, v2, v3, v4
+
+	return
+}
+
+func (xx *XXHash64) Sum64() (h uint64) {
+	if seed := xx.seed; xx.ln > 31 {
+		v1, v2, v3, v4 := xx.v1, xx.v2, xx.v3, xx.v4
+		h = rotl64_1(v1) + rotl64_7(v2) + rotl64_12(v3) + rotl64_18(v4)
+
+		h = mergeRound64(h, v1)
+		h = mergeRound64(h, v2)
+		h = mergeRound64(h, v3)
+		h = mergeRound64(h, v4)
+	} else if seed == 0 {
+		h = prime64x5
+	} else {
+		h = seed + prime64x5
+	}
+
+	h += uint64(xx.ln)
+
+	if xx.memIdx == 0 {
+		return mix64(h)
+	}
+
+	var (
+		in       = xx.mem[:xx.memIdx:xx.memIdx]
+		wordsLen = len(in) >> 3
+		words    = ((*[maxInt32 / 8]uint64)(unsafe.Pointer(&in[0])))[:wordsLen:wordsLen]
+	)
+
+	for _, k := range words {
+		h ^= round64(0, k)
+		h = rotl64_27(h)*prime64x1 + prime64x4
+	}
+
+	if in = in[wordsLen<<3 : len(in) : len(in)]; len(in) > 3 {
+		words := (*[1]uint32)(unsafe.Pointer(&in[0]))
+
+		h ^= uint64(words[0]) * prime64x1
+		h = rotl64_23(h)*prime64x2 + prime64x3
+
+		in = in[4:len(in):len(in)]
+	}
+
+	for _, b := range in {
+		h ^= uint64(b) * prime64x5
+		h = rotl64_11(h) * prime64x1
+	}
+
+	return mix64(h)
+}
-- 
GitLab