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