Add pushover notifications, this should be a super basic MVP
This commit is contained in:
parent
ed13a5994f
commit
d9917ab8b0
505 changed files with 195741 additions and 9 deletions
vendor/github.com/dgraph-io/ristretto
176
vendor/github.com/dgraph-io/ristretto/LICENSE
generated
vendored
Normal file
176
vendor/github.com/dgraph-io/ristretto/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,176 @@
|
|||
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
|
64
vendor/github.com/dgraph-io/ristretto/z/LICENSE
generated
vendored
Normal file
64
vendor/github.com/dgraph-io/ristretto/z/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
bbloom.go
|
||||
|
||||
// The MIT License (MIT)
|
||||
// Copyright (c) 2014 Andreas Briese, eduToolbox@Bri-C GmbH, Sarstedt
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
rtutil.go
|
||||
|
||||
// MIT License
|
||||
|
||||
// Copyright (c) 2019 Ewan Chou
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
Modifications:
|
||||
|
||||
/*
|
||||
* Copyright 2019 Dgraph Labs, Inc. and Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
129
vendor/github.com/dgraph-io/ristretto/z/README.md
generated
vendored
Normal file
129
vendor/github.com/dgraph-io/ristretto/z/README.md
generated
vendored
Normal file
|
@ -0,0 +1,129 @@
|
|||
## bbloom: a bitset Bloom filter for go/golang
|
||||
===
|
||||
|
||||
package implements a fast bloom filter with real 'bitset' and JSONMarshal/JSONUnmarshal to store/reload the Bloom filter.
|
||||
|
||||
NOTE: the package uses unsafe.Pointer to set and read the bits from the bitset. If you're uncomfortable with using the unsafe package, please consider using my bloom filter package at github.com/AndreasBriese/bloom
|
||||
|
||||
===
|
||||
|
||||
changelog 11/2015: new thread safe methods AddTS(), HasTS(), AddIfNotHasTS() following a suggestion from Srdjan Marinovic (github @a-little-srdjan), who used this to code a bloomfilter cache.
|
||||
|
||||
This bloom filter was developed to strengthen a website-log database and was tested and optimized for this log-entry mask: "2014/%02i/%02i %02i:%02i:%02i /info.html".
|
||||
Nonetheless bbloom should work with any other form of entries.
|
||||
|
||||
~~Hash function is a modified Berkeley DB sdbm hash (to optimize for smaller strings). sdbm http://www.cse.yorku.ca/~oz/hash.html~~
|
||||
|
||||
Found sipHash (SipHash-2-4, a fast short-input PRF created by Jean-Philippe Aumasson and Daniel J. Bernstein.) to be about as fast. sipHash had been ported by Dimtry Chestnyk to Go (github.com/dchest/siphash )
|
||||
|
||||
Minimum hashset size is: 512 ([4]uint64; will be set automatically).
|
||||
|
||||
###install
|
||||
|
||||
```sh
|
||||
go get github.com/AndreasBriese/bbloom
|
||||
```
|
||||
|
||||
###test
|
||||
+ change to folder ../bbloom
|
||||
+ create wordlist in file "words.txt" (you might use `python permut.py`)
|
||||
+ run 'go test -bench=.' within the folder
|
||||
|
||||
```go
|
||||
go test -bench=.
|
||||
```
|
||||
|
||||
~~If you've installed the GOCONVEY TDD-framework http://goconvey.co/ you can run the tests automatically.~~
|
||||
|
||||
using go's testing framework now (have in mind that the op timing is related to 65536 operations of Add, Has, AddIfNotHas respectively)
|
||||
|
||||
### usage
|
||||
|
||||
after installation add
|
||||
|
||||
```go
|
||||
import (
|
||||
...
|
||||
"github.com/AndreasBriese/bbloom"
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
at your header. In the program use
|
||||
|
||||
```go
|
||||
// create a bloom filter for 65536 items and 1 % wrong-positive ratio
|
||||
bf := bbloom.New(float64(1<<16), float64(0.01))
|
||||
|
||||
// or
|
||||
// create a bloom filter with 650000 for 65536 items and 7 locs per hash explicitly
|
||||
// bf = bbloom.New(float64(650000), float64(7))
|
||||
// or
|
||||
bf = bbloom.New(650000.0, 7.0)
|
||||
|
||||
// add one item
|
||||
bf.Add([]byte("butter"))
|
||||
|
||||
// Number of elements added is exposed now
|
||||
// Note: ElemNum will not be included in JSON export (for compatability to older version)
|
||||
nOfElementsInFilter := bf.ElemNum
|
||||
|
||||
// check if item is in the filter
|
||||
isIn := bf.Has([]byte("butter")) // should be true
|
||||
isNotIn := bf.Has([]byte("Butter")) // should be false
|
||||
|
||||
// 'add only if item is new' to the bloomfilter
|
||||
added := bf.AddIfNotHas([]byte("butter")) // should be false because 'butter' is already in the set
|
||||
added = bf.AddIfNotHas([]byte("buTTer")) // should be true because 'buTTer' is new
|
||||
|
||||
// thread safe versions for concurrent use: AddTS, HasTS, AddIfNotHasTS
|
||||
// add one item
|
||||
bf.AddTS([]byte("peanutbutter"))
|
||||
// check if item is in the filter
|
||||
isIn = bf.HasTS([]byte("peanutbutter")) // should be true
|
||||
isNotIn = bf.HasTS([]byte("peanutButter")) // should be false
|
||||
// 'add only if item is new' to the bloomfilter
|
||||
added = bf.AddIfNotHasTS([]byte("butter")) // should be false because 'peanutbutter' is already in the set
|
||||
added = bf.AddIfNotHasTS([]byte("peanutbuTTer")) // should be true because 'penutbuTTer' is new
|
||||
|
||||
// convert to JSON ([]byte)
|
||||
Json := bf.JSONMarshal()
|
||||
|
||||
// bloomfilters Mutex is exposed for external un-/locking
|
||||
// i.e. mutex lock while doing JSON conversion
|
||||
bf.Mtx.Lock()
|
||||
Json = bf.JSONMarshal()
|
||||
bf.Mtx.Unlock()
|
||||
|
||||
// restore a bloom filter from storage
|
||||
bfNew := bbloom.JSONUnmarshal(Json)
|
||||
|
||||
isInNew := bfNew.Has([]byte("butter")) // should be true
|
||||
isNotInNew := bfNew.Has([]byte("Butter")) // should be false
|
||||
|
||||
```
|
||||
|
||||
to work with the bloom filter.
|
||||
|
||||
### why 'fast'?
|
||||
|
||||
It's about 3 times faster than William Fitzgeralds bitset bloom filter https://github.com/willf/bloom . And it is about so fast as my []bool set variant for Boom filters (see https://github.com/AndreasBriese/bloom ) but having a 8times smaller memory footprint:
|
||||
|
||||
|
||||
Bloom filter (filter size 524288, 7 hashlocs)
|
||||
github.com/AndreasBriese/bbloom 'Add' 65536 items (10 repetitions): 6595800 ns (100 ns/op)
|
||||
github.com/AndreasBriese/bbloom 'Has' 65536 items (10 repetitions): 5986600 ns (91 ns/op)
|
||||
github.com/AndreasBriese/bloom 'Add' 65536 items (10 repetitions): 6304684 ns (96 ns/op)
|
||||
github.com/AndreasBriese/bloom 'Has' 65536 items (10 repetitions): 6568663 ns (100 ns/op)
|
||||
|
||||
github.com/willf/bloom 'Add' 65536 items (10 repetitions): 24367224 ns (371 ns/op)
|
||||
github.com/willf/bloom 'Test' 65536 items (10 repetitions): 21881142 ns (333 ns/op)
|
||||
github.com/dataence/bloom/standard 'Add' 65536 items (10 repetitions): 23041644 ns (351 ns/op)
|
||||
github.com/dataence/bloom/standard 'Check' 65536 items (10 repetitions): 19153133 ns (292 ns/op)
|
||||
github.com/cabello/bloom 'Add' 65536 items (10 repetitions): 131921507 ns (2012 ns/op)
|
||||
github.com/cabello/bloom 'Contains' 65536 items (10 repetitions): 131108962 ns (2000 ns/op)
|
||||
|
||||
(on MBPro15 OSX10.8.5 i7 4Core 2.4Ghz)
|
||||
|
||||
|
||||
With 32bit bloom filters (bloom32) using modified sdbm, bloom32 does hashing with only 2 bit shifts, one xor and one substraction per byte. smdb is about as fast as fnv64a but gives less collisions with the dataset (see mask above). bloom.New(float64(10 * 1<<16),float64(7)) populated with 1<<16 random items from the dataset (see above) and tested against the rest results in less than 0.05% collisions.
|
401
vendor/github.com/dgraph-io/ristretto/z/allocator.go
generated
vendored
Normal file
401
vendor/github.com/dgraph-io/ristretto/z/allocator.go
generated
vendored
Normal file
|
@ -0,0 +1,401 @@
|
|||
/*
|
||||
* Copyright 2020 Dgraph Labs, Inc. and Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package z
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/bits"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
)
|
||||
|
||||
// Allocator amortizes the cost of small allocations by allocating memory in
|
||||
// bigger chunks. Internally it uses z.Calloc to allocate memory. Once
|
||||
// allocated, the memory is not moved, so it is safe to use the allocated bytes
|
||||
// to unsafe cast them to Go struct pointers. Maintaining a freelist is slow.
|
||||
// Instead, Allocator only allocates memory, with the idea that finally we
|
||||
// would just release the entire Allocator.
|
||||
type Allocator struct {
|
||||
sync.Mutex
|
||||
compIdx uint64 // Stores bufIdx in 32 MSBs and posIdx in 32 LSBs.
|
||||
buffers [][]byte
|
||||
Ref uint64
|
||||
Tag string
|
||||
}
|
||||
|
||||
// allocs keeps references to all Allocators, so we can safely discard them later.
|
||||
var allocsMu *sync.Mutex
|
||||
var allocRef uint64
|
||||
var allocs map[uint64]*Allocator
|
||||
var calculatedLog2 []int
|
||||
|
||||
func init() {
|
||||
allocsMu = new(sync.Mutex)
|
||||
allocs = make(map[uint64]*Allocator)
|
||||
|
||||
// Set up a unique Ref per process.
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
allocRef = uint64(rand.Int63n(1<<16)) << 48
|
||||
|
||||
calculatedLog2 = make([]int, 1025)
|
||||
for i := 1; i <= 1024; i++ {
|
||||
calculatedLog2[i] = int(math.Log2(float64(i)))
|
||||
}
|
||||
}
|
||||
|
||||
// NewAllocator creates an allocator starting with the given size.
|
||||
func NewAllocator(sz int) *Allocator {
|
||||
ref := atomic.AddUint64(&allocRef, 1)
|
||||
// We should not allow a zero sized page because addBufferWithMinSize
|
||||
// will run into an infinite loop trying to double the pagesize.
|
||||
if sz < 512 {
|
||||
sz = 512
|
||||
}
|
||||
a := &Allocator{
|
||||
Ref: ref,
|
||||
buffers: make([][]byte, 64),
|
||||
}
|
||||
l2 := uint64(log2(sz))
|
||||
if bits.OnesCount64(uint64(sz)) > 1 {
|
||||
l2 += 1
|
||||
}
|
||||
a.buffers[0] = Calloc(1 << l2)
|
||||
|
||||
allocsMu.Lock()
|
||||
allocs[ref] = a
|
||||
allocsMu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Allocator) Reset() {
|
||||
atomic.StoreUint64(&a.compIdx, 0)
|
||||
}
|
||||
|
||||
func Allocators() string {
|
||||
allocsMu.Lock()
|
||||
tags := make(map[string]uint64)
|
||||
num := make(map[string]int)
|
||||
for _, ac := range allocs {
|
||||
tags[ac.Tag] += ac.Allocated()
|
||||
num[ac.Tag] += 1
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
for tag, sz := range tags {
|
||||
fmt.Fprintf(&buf, "Tag: %s Num: %d Size: %s . ", tag, num[tag], humanize.IBytes(sz))
|
||||
}
|
||||
allocsMu.Unlock()
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (a *Allocator) String() string {
|
||||
var s strings.Builder
|
||||
s.WriteString(fmt.Sprintf("Allocator: %x\n", a.Ref))
|
||||
var cum int
|
||||
for i, b := range a.buffers {
|
||||
cum += len(b)
|
||||
if len(b) == 0 {
|
||||
break
|
||||
}
|
||||
s.WriteString(fmt.Sprintf("idx: %d len: %d cum: %d\n", i, len(b), cum))
|
||||
}
|
||||
pos := atomic.LoadUint64(&a.compIdx)
|
||||
bi, pi := parse(pos)
|
||||
s.WriteString(fmt.Sprintf("bi: %d pi: %d\n", bi, pi))
|
||||
s.WriteString(fmt.Sprintf("Size: %d\n", a.Size()))
|
||||
return s.String()
|
||||
}
|
||||
|
||||
// AllocatorFrom would return the allocator corresponding to the ref.
|
||||
func AllocatorFrom(ref uint64) *Allocator {
|
||||
allocsMu.Lock()
|
||||
a := allocs[ref]
|
||||
allocsMu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
func parse(pos uint64) (bufIdx, posIdx int) {
|
||||
return int(pos >> 32), int(pos & 0xFFFFFFFF)
|
||||
}
|
||||
|
||||
// Size returns the size of the allocations so far.
|
||||
func (a *Allocator) Size() int {
|
||||
pos := atomic.LoadUint64(&a.compIdx)
|
||||
bi, pi := parse(pos)
|
||||
var sz int
|
||||
for i, b := range a.buffers {
|
||||
if i < bi {
|
||||
sz += len(b)
|
||||
continue
|
||||
}
|
||||
sz += pi
|
||||
return sz
|
||||
}
|
||||
panic("Size should not reach here")
|
||||
}
|
||||
|
||||
func log2(sz int) int {
|
||||
if sz < len(calculatedLog2) {
|
||||
return calculatedLog2[sz]
|
||||
}
|
||||
pow := 10
|
||||
sz >>= 10
|
||||
for sz > 1 {
|
||||
sz >>= 1
|
||||
pow++
|
||||
}
|
||||
return pow
|
||||
}
|
||||
|
||||
func (a *Allocator) Allocated() uint64 {
|
||||
var alloc int
|
||||
for _, b := range a.buffers {
|
||||
alloc += cap(b)
|
||||
}
|
||||
return uint64(alloc)
|
||||
}
|
||||
|
||||
func (a *Allocator) TrimTo(max int) {
|
||||
var alloc int
|
||||
for i, b := range a.buffers {
|
||||
if len(b) == 0 {
|
||||
break
|
||||
}
|
||||
alloc += len(b)
|
||||
if alloc < max {
|
||||
continue
|
||||
}
|
||||
Free(b)
|
||||
a.buffers[i] = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Release would release the memory back. Remember to make this call to avoid memory leaks.
|
||||
func (a *Allocator) Release() {
|
||||
if a == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var alloc int
|
||||
for _, b := range a.buffers {
|
||||
if len(b) == 0 {
|
||||
break
|
||||
}
|
||||
alloc += len(b)
|
||||
Free(b)
|
||||
}
|
||||
|
||||
allocsMu.Lock()
|
||||
delete(allocs, a.Ref)
|
||||
allocsMu.Unlock()
|
||||
}
|
||||
|
||||
const maxAlloc = 1 << 30
|
||||
|
||||
func (a *Allocator) MaxAlloc() int {
|
||||
return maxAlloc
|
||||
}
|
||||
|
||||
const nodeAlign = unsafe.Sizeof(uint64(0)) - 1
|
||||
|
||||
func (a *Allocator) AllocateAligned(sz int) []byte {
|
||||
tsz := sz + int(nodeAlign)
|
||||
out := a.Allocate(tsz)
|
||||
// We are reusing allocators. In that case, it's important to zero out the memory allocated
|
||||
// here. We don't always zero it out (in Allocate), because other functions would be immediately
|
||||
// overwriting the allocated slices anyway (see Copy).
|
||||
ZeroOut(out, 0, len(out))
|
||||
|
||||
addr := uintptr(unsafe.Pointer(&out[0]))
|
||||
aligned := (addr + nodeAlign) & ^nodeAlign
|
||||
start := int(aligned - addr)
|
||||
|
||||
return out[start : start+sz]
|
||||
}
|
||||
|
||||
func (a *Allocator) Copy(buf []byte) []byte {
|
||||
if a == nil {
|
||||
return append([]byte{}, buf...)
|
||||
}
|
||||
out := a.Allocate(len(buf))
|
||||
copy(out, buf)
|
||||
return out
|
||||
}
|
||||
|
||||
func (a *Allocator) addBufferAt(bufIdx, minSz int) {
|
||||
for {
|
||||
if bufIdx >= len(a.buffers) {
|
||||
panic(fmt.Sprintf("Allocator can not allocate more than %d buffers", len(a.buffers)))
|
||||
}
|
||||
if len(a.buffers[bufIdx]) == 0 {
|
||||
break
|
||||
}
|
||||
if minSz <= len(a.buffers[bufIdx]) {
|
||||
// No need to do anything. We already have a buffer which can satisfy minSz.
|
||||
return
|
||||
}
|
||||
bufIdx++
|
||||
}
|
||||
assert(bufIdx > 0)
|
||||
// We need to allocate a new buffer.
|
||||
// Make pageSize double of the last allocation.
|
||||
pageSize := 2 * len(a.buffers[bufIdx-1])
|
||||
// Ensure pageSize is bigger than sz.
|
||||
for pageSize < minSz {
|
||||
pageSize *= 2
|
||||
}
|
||||
// If bigger than maxAlloc, trim to maxAlloc.
|
||||
if pageSize > maxAlloc {
|
||||
pageSize = maxAlloc
|
||||
}
|
||||
|
||||
buf := Calloc(pageSize)
|
||||
assert(len(a.buffers[bufIdx]) == 0)
|
||||
a.buffers[bufIdx] = buf
|
||||
}
|
||||
|
||||
func (a *Allocator) Allocate(sz int) []byte {
|
||||
if a == nil {
|
||||
return make([]byte, sz)
|
||||
}
|
||||
if sz > maxAlloc {
|
||||
panic(fmt.Sprintf("Unable to allocate more than %d\n", maxAlloc))
|
||||
}
|
||||
if sz == 0 {
|
||||
return nil
|
||||
}
|
||||
for {
|
||||
pos := atomic.AddUint64(&a.compIdx, uint64(sz))
|
||||
bufIdx, posIdx := parse(pos)
|
||||
buf := a.buffers[bufIdx]
|
||||
if posIdx > len(buf) {
|
||||
a.Lock()
|
||||
newPos := atomic.LoadUint64(&a.compIdx)
|
||||
newBufIdx, _ := parse(newPos)
|
||||
if newBufIdx != bufIdx {
|
||||
a.Unlock()
|
||||
continue
|
||||
}
|
||||
a.addBufferAt(bufIdx+1, sz)
|
||||
atomic.StoreUint64(&a.compIdx, uint64((bufIdx+1)<<32))
|
||||
a.Unlock()
|
||||
// We added a new buffer. Let's acquire slice the right way by going back to the top.
|
||||
continue
|
||||
}
|
||||
data := buf[posIdx-sz : posIdx]
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
type AllocatorPool struct {
|
||||
numGets int64
|
||||
allocCh chan *Allocator
|
||||
closer *Closer
|
||||
}
|
||||
|
||||
func NewAllocatorPool(sz int) *AllocatorPool {
|
||||
a := &AllocatorPool{
|
||||
allocCh: make(chan *Allocator, sz),
|
||||
closer: NewCloser(1),
|
||||
}
|
||||
go a.freeupAllocators()
|
||||
return a
|
||||
}
|
||||
|
||||
func (p *AllocatorPool) Get(sz int) *Allocator {
|
||||
if p == nil {
|
||||
return NewAllocator(sz)
|
||||
}
|
||||
atomic.AddInt64(&p.numGets, 1)
|
||||
select {
|
||||
case alloc := <-p.allocCh:
|
||||
alloc.Reset()
|
||||
return alloc
|
||||
default:
|
||||
return NewAllocator(sz)
|
||||
}
|
||||
}
|
||||
func (p *AllocatorPool) Return(a *Allocator) {
|
||||
if a == nil {
|
||||
return
|
||||
}
|
||||
if p == nil {
|
||||
a.Release()
|
||||
return
|
||||
}
|
||||
a.TrimTo(400 << 20)
|
||||
|
||||
select {
|
||||
case p.allocCh <- a:
|
||||
return
|
||||
default:
|
||||
a.Release()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *AllocatorPool) Release() {
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
p.closer.SignalAndWait()
|
||||
}
|
||||
|
||||
func (p *AllocatorPool) freeupAllocators() {
|
||||
defer p.closer.Done()
|
||||
|
||||
ticker := time.NewTicker(2 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
releaseOne := func() bool {
|
||||
select {
|
||||
case alloc := <-p.allocCh:
|
||||
alloc.Release()
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var last int64
|
||||
for {
|
||||
select {
|
||||
case <-p.closer.HasBeenClosed():
|
||||
close(p.allocCh)
|
||||
for alloc := range p.allocCh {
|
||||
alloc.Release()
|
||||
}
|
||||
return
|
||||
|
||||
case <-ticker.C:
|
||||
gets := atomic.LoadInt64(&p.numGets)
|
||||
if gets != last {
|
||||
// Some retrievals were made since the last time. So, let's avoid doing a release.
|
||||
last = gets
|
||||
continue
|
||||
}
|
||||
releaseOne()
|
||||
}
|
||||
}
|
||||
}
|
210
vendor/github.com/dgraph-io/ristretto/z/bbloom.go
generated
vendored
Normal file
210
vendor/github.com/dgraph-io/ristretto/z/bbloom.go
generated
vendored
Normal file
|
@ -0,0 +1,210 @@
|
|||
// The MIT License (MIT)
|
||||
// Copyright (c) 2014 Andreas Briese, eduToolbox@Bri-C GmbH, Sarstedt
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
package z
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"math"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// helper
|
||||
var mask = []uint8{1, 2, 4, 8, 16, 32, 64, 128}
|
||||
|
||||
func getSize(ui64 uint64) (size uint64, exponent uint64) {
|
||||
if ui64 < uint64(512) {
|
||||
ui64 = uint64(512)
|
||||
}
|
||||
size = uint64(1)
|
||||
for size < ui64 {
|
||||
size <<= 1
|
||||
exponent++
|
||||
}
|
||||
return size, exponent
|
||||
}
|
||||
|
||||
func calcSizeByWrongPositives(numEntries, wrongs float64) (uint64, uint64) {
|
||||
size := -1 * numEntries * math.Log(wrongs) / math.Pow(float64(0.69314718056), 2)
|
||||
locs := math.Ceil(float64(0.69314718056) * size / numEntries)
|
||||
return uint64(size), uint64(locs)
|
||||
}
|
||||
|
||||
// NewBloomFilter returns a new bloomfilter.
|
||||
func NewBloomFilter(params ...float64) (bloomfilter *Bloom) {
|
||||
var entries, locs uint64
|
||||
if len(params) == 2 {
|
||||
if params[1] < 1 {
|
||||
entries, locs = calcSizeByWrongPositives(params[0], params[1])
|
||||
} else {
|
||||
entries, locs = uint64(params[0]), uint64(params[1])
|
||||
}
|
||||
} else {
|
||||
log.Fatal("usage: New(float64(number_of_entries), float64(number_of_hashlocations))" +
|
||||
" i.e. New(float64(1000), float64(3)) or New(float64(number_of_entries)," +
|
||||
" float64(number_of_hashlocations)) i.e. New(float64(1000), float64(0.03))")
|
||||
}
|
||||
size, exponent := getSize(entries)
|
||||
bloomfilter = &Bloom{
|
||||
sizeExp: exponent,
|
||||
size: size - 1,
|
||||
setLocs: locs,
|
||||
shift: 64 - exponent,
|
||||
}
|
||||
bloomfilter.Size(size)
|
||||
return bloomfilter
|
||||
}
|
||||
|
||||
// Bloom filter
|
||||
type Bloom struct {
|
||||
bitset []uint64
|
||||
ElemNum uint64
|
||||
sizeExp uint64
|
||||
size uint64
|
||||
setLocs uint64
|
||||
shift uint64
|
||||
}
|
||||
|
||||
// <--- http://www.cse.yorku.ca/~oz/hash.html
|
||||
// modified Berkeley DB Hash (32bit)
|
||||
// hash is casted to l, h = 16bit fragments
|
||||
// func (bl Bloom) absdbm(b *[]byte) (l, h uint64) {
|
||||
// hash := uint64(len(*b))
|
||||
// for _, c := range *b {
|
||||
// hash = uint64(c) + (hash << 6) + (hash << bl.sizeExp) - hash
|
||||
// }
|
||||
// h = hash >> bl.shift
|
||||
// l = hash << bl.shift >> bl.shift
|
||||
// return l, h
|
||||
// }
|
||||
|
||||
// Add adds hash of a key to the bloomfilter.
|
||||
func (bl *Bloom) Add(hash uint64) {
|
||||
h := hash >> bl.shift
|
||||
l := hash << bl.shift >> bl.shift
|
||||
for i := uint64(0); i < bl.setLocs; i++ {
|
||||
bl.Set((h + i*l) & bl.size)
|
||||
bl.ElemNum++
|
||||
}
|
||||
}
|
||||
|
||||
// Has checks if bit(s) for entry hash is/are set,
|
||||
// returns true if the hash was added to the Bloom Filter.
|
||||
func (bl Bloom) Has(hash uint64) bool {
|
||||
h := hash >> bl.shift
|
||||
l := hash << bl.shift >> bl.shift
|
||||
for i := uint64(0); i < bl.setLocs; i++ {
|
||||
if !bl.IsSet((h + i*l) & bl.size) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AddIfNotHas only Adds hash, if it's not present in the bloomfilter.
|
||||
// Returns true if hash was added.
|
||||
// Returns false if hash was already registered in the bloomfilter.
|
||||
func (bl *Bloom) AddIfNotHas(hash uint64) bool {
|
||||
if bl.Has(hash) {
|
||||
return false
|
||||
}
|
||||
bl.Add(hash)
|
||||
return true
|
||||
}
|
||||
|
||||
// TotalSize returns the total size of the bloom filter.
|
||||
func (bl *Bloom) TotalSize() int {
|
||||
// The bl struct has 5 members and each one is 8 byte. The bitset is a
|
||||
// uint64 byte slice.
|
||||
return len(bl.bitset)*8 + 5*8
|
||||
}
|
||||
|
||||
// Size makes Bloom filter with as bitset of size sz.
|
||||
func (bl *Bloom) Size(sz uint64) {
|
||||
bl.bitset = make([]uint64, sz>>6)
|
||||
}
|
||||
|
||||
// Clear resets the Bloom filter.
|
||||
func (bl *Bloom) Clear() {
|
||||
for i := range bl.bitset {
|
||||
bl.bitset[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets the bit[idx] of bitset.
|
||||
func (bl *Bloom) Set(idx uint64) {
|
||||
ptr := unsafe.Pointer(uintptr(unsafe.Pointer(&bl.bitset[idx>>6])) + uintptr((idx%64)>>3))
|
||||
*(*uint8)(ptr) |= mask[idx%8]
|
||||
}
|
||||
|
||||
// IsSet checks if bit[idx] of bitset is set, returns true/false.
|
||||
func (bl *Bloom) IsSet(idx uint64) bool {
|
||||
ptr := unsafe.Pointer(uintptr(unsafe.Pointer(&bl.bitset[idx>>6])) + uintptr((idx%64)>>3))
|
||||
r := ((*(*uint8)(ptr)) >> (idx % 8)) & 1
|
||||
return r == 1
|
||||
}
|
||||
|
||||
// bloomJSONImExport
|
||||
// Im/Export structure used by JSONMarshal / JSONUnmarshal
|
||||
type bloomJSONImExport struct {
|
||||
FilterSet []byte
|
||||
SetLocs uint64
|
||||
}
|
||||
|
||||
// NewWithBoolset takes a []byte slice and number of locs per entry,
|
||||
// returns the bloomfilter with a bitset populated according to the input []byte.
|
||||
func newWithBoolset(bs *[]byte, locs uint64) *Bloom {
|
||||
bloomfilter := NewBloomFilter(float64(len(*bs)<<3), float64(locs))
|
||||
for i, b := range *bs {
|
||||
*(*uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(&bloomfilter.bitset[0])) + uintptr(i))) = b
|
||||
}
|
||||
return bloomfilter
|
||||
}
|
||||
|
||||
// JSONUnmarshal takes JSON-Object (type bloomJSONImExport) as []bytes
|
||||
// returns bloom32 / bloom64 object.
|
||||
func JSONUnmarshal(dbData []byte) (*Bloom, error) {
|
||||
bloomImEx := bloomJSONImExport{}
|
||||
if err := json.Unmarshal(dbData, &bloomImEx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf := bytes.NewBuffer(bloomImEx.FilterSet)
|
||||
bs := buf.Bytes()
|
||||
bf := newWithBoolset(&bs, bloomImEx.SetLocs)
|
||||
return bf, nil
|
||||
}
|
||||
|
||||
// JSONMarshal returns JSON-object (type bloomJSONImExport) as []byte.
|
||||
func (bl Bloom) JSONMarshal() []byte {
|
||||
bloomImEx := bloomJSONImExport{}
|
||||
bloomImEx.SetLocs = bl.setLocs
|
||||
bloomImEx.FilterSet = make([]byte, len(bl.bitset)<<3)
|
||||
for i := range bloomImEx.FilterSet {
|
||||
bloomImEx.FilterSet[i] = *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&bl.bitset[0])) +
|
||||
uintptr(i)))
|
||||
}
|
||||
data, err := json.Marshal(bloomImEx)
|
||||
if err != nil {
|
||||
log.Fatal("json.Marshal failed: ", err)
|
||||
}
|
||||
return data
|
||||
}
|
585
vendor/github.com/dgraph-io/ristretto/z/btree.go
generated
vendored
Normal file
585
vendor/github.com/dgraph-io/ristretto/z/btree.go
generated
vendored
Normal file
|
@ -0,0 +1,585 @@
|
|||
/*
|
||||
* Copyright 2020 Dgraph Labs, Inc. and Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package z
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/dgraph-io/ristretto/z/simd"
|
||||
)
|
||||
|
||||
var (
|
||||
pageSize = os.Getpagesize()
|
||||
maxKeys = (pageSize / 16) - 1
|
||||
oneThird = int(float64(maxKeys) / 3)
|
||||
absoluteMax = uint64(math.MaxUint64 - 1)
|
||||
minSize = 1 << 20
|
||||
)
|
||||
|
||||
// Tree represents the structure for custom mmaped B+ tree.
|
||||
// It supports keys in range [1, math.MaxUint64-1] and values [1, math.Uint64].
|
||||
type Tree struct {
|
||||
data []byte
|
||||
nextPage uint64
|
||||
freePage uint64
|
||||
stats TreeStats
|
||||
}
|
||||
|
||||
func (t *Tree) initRootNode() {
|
||||
// This is the root node.
|
||||
t.newNode(0)
|
||||
// This acts as the rightmost pointer (all the keys are <= this key).
|
||||
t.Set(absoluteMax, 0)
|
||||
}
|
||||
|
||||
// NewTree returns a memory mapped B+ tree with given filename.
|
||||
func NewTree() *Tree {
|
||||
t := &Tree{}
|
||||
t.Reset()
|
||||
return t
|
||||
}
|
||||
|
||||
// Reset resets the tree and truncates it to maxSz.
|
||||
func (t *Tree) Reset() {
|
||||
t.nextPage = 1
|
||||
t.freePage = 0
|
||||
t.data = make([]byte, minSize)
|
||||
t.stats = TreeStats{}
|
||||
t.initRootNode()
|
||||
}
|
||||
|
||||
type TreeStats struct {
|
||||
Allocated int // Derived.
|
||||
Bytes int // Derived.
|
||||
NumLeafKeys int // Calculated.
|
||||
NumPages int // Derived.
|
||||
NumPagesFree int // Calculated.
|
||||
Occupancy float64 // Derived.
|
||||
PageSize int // Derived.
|
||||
}
|
||||
|
||||
// Stats returns stats about the tree.
|
||||
func (t *Tree) Stats() TreeStats {
|
||||
numPages := int(t.nextPage - 1)
|
||||
out := TreeStats{
|
||||
Bytes: numPages * pageSize,
|
||||
Allocated: cap(t.data),
|
||||
NumLeafKeys: t.stats.NumLeafKeys,
|
||||
NumPages: numPages,
|
||||
NumPagesFree: t.stats.NumPagesFree,
|
||||
PageSize: pageSize,
|
||||
}
|
||||
out.Occupancy = 100.0 * float64(out.NumLeafKeys) / float64(maxKeys*numPages)
|
||||
return out
|
||||
}
|
||||
|
||||
// BytesToU32Slice converts the given byte slice to uint32 slice
|
||||
func BytesToUint64Slice(b []byte) []uint64 {
|
||||
if len(b) == 0 {
|
||||
return nil
|
||||
}
|
||||
var u64s []uint64
|
||||
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&u64s))
|
||||
hdr.Len = len(b) / 8
|
||||
hdr.Cap = hdr.Len
|
||||
hdr.Data = uintptr(unsafe.Pointer(&b[0]))
|
||||
return u64s
|
||||
}
|
||||
|
||||
func (t *Tree) newNode(bit uint64) node {
|
||||
var pageId uint64
|
||||
if t.freePage > 0 {
|
||||
pageId = t.freePage
|
||||
t.stats.NumPagesFree--
|
||||
} else {
|
||||
pageId = t.nextPage
|
||||
t.nextPage++
|
||||
offset := int(pageId) * pageSize
|
||||
// Double the size with an upper cap of 1GB, if current buffer is insufficient.
|
||||
if offset+pageSize > len(t.data) {
|
||||
const oneGB = 1 << 30
|
||||
newSz := 2 * len(t.data)
|
||||
if newSz > len(t.data)+oneGB {
|
||||
newSz = len(t.data) + oneGB
|
||||
}
|
||||
out := make([]byte, newSz)
|
||||
copy(out, t.data)
|
||||
t.data = out
|
||||
}
|
||||
}
|
||||
n := t.node(pageId)
|
||||
if t.freePage > 0 {
|
||||
t.freePage = n.uint64(0)
|
||||
}
|
||||
zeroOut(n)
|
||||
n.setBit(bit)
|
||||
n.setAt(keyOffset(maxKeys), pageId)
|
||||
return n
|
||||
}
|
||||
|
||||
func getNode(data []byte) node {
|
||||
return node(BytesToUint64Slice(data))
|
||||
}
|
||||
|
||||
func zeroOut(data []uint64) {
|
||||
for i := 0; i < len(data); i++ {
|
||||
data[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tree) node(pid uint64) node {
|
||||
// page does not exist
|
||||
if pid == 0 {
|
||||
return nil
|
||||
}
|
||||
start := pageSize * int(pid)
|
||||
return getNode(t.data[start : start+pageSize])
|
||||
}
|
||||
|
||||
// Set sets the key-value pair in the tree.
|
||||
func (t *Tree) Set(k, v uint64) {
|
||||
if k == math.MaxUint64 || k == 0 {
|
||||
panic("Error setting zero or MaxUint64")
|
||||
}
|
||||
root := t.set(1, k, v)
|
||||
if root.isFull() {
|
||||
right := t.split(1)
|
||||
left := t.newNode(root.bits())
|
||||
// Re-read the root as the underlying buffer for tree might have changed during split.
|
||||
root = t.node(1)
|
||||
copy(left[:keyOffset(maxKeys)], root)
|
||||
left.setNumKeys(root.numKeys())
|
||||
|
||||
// reset the root node.
|
||||
zeroOut(root[:keyOffset(maxKeys)])
|
||||
root.setNumKeys(0)
|
||||
|
||||
// set the pointers for left and right child in the root node.
|
||||
root.set(left.maxKey(), left.pageID())
|
||||
root.set(right.maxKey(), right.pageID())
|
||||
}
|
||||
}
|
||||
|
||||
// For internal nodes, they contain <key, ptr>.
|
||||
// where all entries <= key are stored in the corresponding ptr.
|
||||
func (t *Tree) set(pid, k, v uint64) node {
|
||||
n := t.node(pid)
|
||||
if n.isLeaf() {
|
||||
t.stats.NumLeafKeys += n.set(k, v)
|
||||
return n
|
||||
}
|
||||
|
||||
// This is an internal node.
|
||||
idx := n.search(k)
|
||||
if idx >= maxKeys {
|
||||
panic("search returned index >= maxKeys")
|
||||
}
|
||||
// If no key at idx.
|
||||
if n.key(idx) == 0 {
|
||||
n.setAt(keyOffset(idx), k)
|
||||
n.setNumKeys(n.numKeys() + 1)
|
||||
}
|
||||
child := t.node(n.val(idx))
|
||||
if child == nil {
|
||||
child = t.newNode(bitLeaf)
|
||||
n = t.node(pid)
|
||||
n.setAt(valOffset(idx), child.pageID())
|
||||
}
|
||||
child = t.set(child.pageID(), k, v)
|
||||
// Re-read n as the underlying buffer for tree might have changed during set.
|
||||
n = t.node(pid)
|
||||
if child.isFull() {
|
||||
// Just consider the left sibling for simplicity.
|
||||
// if t.shareWithSibling(n, idx) {
|
||||
// return n
|
||||
// }
|
||||
|
||||
nn := t.split(child.pageID())
|
||||
// Re-read n and child as the underlying buffer for tree might have changed during split.
|
||||
n = t.node(pid)
|
||||
child = t.node(n.uint64(valOffset(idx)))
|
||||
// Set child pointers in the node n.
|
||||
// Note that key for right node (nn) already exist in node n, but the
|
||||
// pointer is updated.
|
||||
n.set(child.maxKey(), child.pageID())
|
||||
n.set(nn.maxKey(), nn.pageID())
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Get looks for key and returns the corresponding value.
|
||||
// If key is not found, 0 is returned.
|
||||
func (t *Tree) Get(k uint64) uint64 {
|
||||
if k == math.MaxUint64 || k == 0 {
|
||||
panic("Does not support getting MaxUint64/Zero")
|
||||
}
|
||||
root := t.node(1)
|
||||
return t.get(root, k)
|
||||
}
|
||||
|
||||
func (t *Tree) get(n node, k uint64) uint64 {
|
||||
if n.isLeaf() {
|
||||
return n.get(k)
|
||||
}
|
||||
// This is internal node
|
||||
idx := n.search(k)
|
||||
if idx == n.numKeys() || n.key(idx) == 0 {
|
||||
return 0
|
||||
}
|
||||
child := t.node(n.uint64(valOffset(idx)))
|
||||
assert(child != nil)
|
||||
return t.get(child, k)
|
||||
}
|
||||
|
||||
// DeleteBelow deletes all keys with value under ts.
|
||||
func (t *Tree) DeleteBelow(ts uint64) {
|
||||
root := t.node(1)
|
||||
t.stats.NumLeafKeys = 0
|
||||
t.compact(root, ts)
|
||||
assert(root.numKeys() >= 1)
|
||||
}
|
||||
|
||||
func (t *Tree) compact(n node, ts uint64) int {
|
||||
if n.isLeaf() {
|
||||
numKeys := n.compact(ts)
|
||||
t.stats.NumLeafKeys += numKeys
|
||||
return numKeys
|
||||
}
|
||||
// Not leaf.
|
||||
N := n.numKeys()
|
||||
for i := 0; i < N; i++ {
|
||||
assert(n.key(i) > 0)
|
||||
childID := n.uint64(valOffset(i))
|
||||
child := t.node(childID)
|
||||
if rem := t.compact(child, ts); rem == 0 && i < N-1 {
|
||||
// If no valid key is remaining we can drop this child. However, don't do that if this
|
||||
// is the max key.
|
||||
child.setAt(0, t.freePage)
|
||||
t.freePage = childID
|
||||
n.setAt(valOffset(i), 0)
|
||||
t.stats.NumPagesFree++
|
||||
}
|
||||
}
|
||||
// We use ts=1 here because we want to delete all the keys whose value is 0, which means they no
|
||||
// longer have a valid page for that key.
|
||||
return n.compact(1)
|
||||
}
|
||||
|
||||
func (t *Tree) iterate(n node, fn func(node)) {
|
||||
fn(n)
|
||||
if n.isLeaf() {
|
||||
return
|
||||
}
|
||||
// Explore children.
|
||||
for i := 0; i < maxKeys; i++ {
|
||||
if n.key(i) == 0 {
|
||||
return
|
||||
}
|
||||
childID := n.uint64(valOffset(i))
|
||||
assert(childID > 0)
|
||||
|
||||
child := t.node(childID)
|
||||
t.iterate(child, fn)
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate iterates over the tree and executes the fn on each node.
|
||||
func (t *Tree) Iterate(fn func(node)) {
|
||||
root := t.node(1)
|
||||
t.iterate(root, fn)
|
||||
}
|
||||
|
||||
func (t *Tree) print(n node, parentID uint64) {
|
||||
n.print(parentID)
|
||||
if n.isLeaf() {
|
||||
return
|
||||
}
|
||||
pid := n.pageID()
|
||||
for i := 0; i < maxKeys; i++ {
|
||||
if n.key(i) == 0 {
|
||||
return
|
||||
}
|
||||
childID := n.uint64(valOffset(i))
|
||||
child := t.node(childID)
|
||||
t.print(child, pid)
|
||||
}
|
||||
}
|
||||
|
||||
// Print iterates over the tree and prints all valid KVs.
|
||||
func (t *Tree) Print() {
|
||||
root := t.node(1)
|
||||
t.print(root, 0)
|
||||
}
|
||||
|
||||
// Splits the node into two. It moves right half of the keys from the original node to a newly
|
||||
// created right node. It returns the right node.
|
||||
func (t *Tree) split(pid uint64) node {
|
||||
n := t.node(pid)
|
||||
if !n.isFull() {
|
||||
panic("This should be called only when n is full")
|
||||
}
|
||||
|
||||
// Create a new node nn, copy over half the keys from n, and set the parent to n's parent.
|
||||
nn := t.newNode(n.bits())
|
||||
// Re-read n as the underlying buffer for tree might have changed during newNode.
|
||||
n = t.node(pid)
|
||||
rightHalf := n[keyOffset(maxKeys/2):keyOffset(maxKeys)]
|
||||
copy(nn, rightHalf)
|
||||
nn.setNumKeys(maxKeys - maxKeys/2)
|
||||
|
||||
// Remove entries from node n.
|
||||
zeroOut(rightHalf)
|
||||
n.setNumKeys(maxKeys / 2)
|
||||
return nn
|
||||
}
|
||||
|
||||
// shareWithSiblingXXX is unused for now. The idea is to move some keys to
|
||||
// sibling when a node is full. But, I don't see any special benefits in our
|
||||
// access pattern. It doesn't result in better occupancy ratios.
|
||||
func (t *Tree) shareWithSiblingXXX(n node, idx int) bool {
|
||||
if idx == 0 {
|
||||
return false
|
||||
}
|
||||
left := t.node(n.val(idx - 1))
|
||||
ns := left.numKeys()
|
||||
if ns >= maxKeys/2 {
|
||||
// Sibling is already getting full.
|
||||
return false
|
||||
}
|
||||
|
||||
right := t.node(n.val(idx))
|
||||
// Copy over keys from right child to left child.
|
||||
copied := copy(left[keyOffset(ns):], right[:keyOffset(oneThird)])
|
||||
copied /= 2 // Considering that key-val constitute one key.
|
||||
left.setNumKeys(ns + copied)
|
||||
|
||||
// Update the max key in parent node n for the left sibling.
|
||||
n.setAt(keyOffset(idx-1), left.maxKey())
|
||||
|
||||
// Now move keys to left for the right sibling.
|
||||
until := copy(right, right[keyOffset(oneThird):keyOffset(maxKeys)])
|
||||
right.setNumKeys(until / 2)
|
||||
zeroOut(right[until:keyOffset(maxKeys)])
|
||||
return true
|
||||
}
|
||||
|
||||
// Each node in the node is of size pageSize. Two kinds of nodes. Leaf nodes and internal nodes.
|
||||
// Leaf nodes only contain the data. Internal nodes would contain the key and the offset to the
|
||||
// child node.
|
||||
// Internal node would have first entry as
|
||||
// <0 offset to child>, <1000 offset>, <5000 offset>, and so on...
|
||||
// Leaf nodes would just have: <key, value>, <key, value>, and so on...
|
||||
// Last 16 bytes of the node are off limits.
|
||||
// | pageID (8 bytes) | metaBits (1 byte) | 3 free bytes | numKeys (4 bytes) |
|
||||
type node []uint64
|
||||
|
||||
func (n node) uint64(start int) uint64 { return n[start] }
|
||||
|
||||
// func (n node) uint32(start int) uint32 { return *(*uint32)(unsafe.Pointer(&n[start])) }
|
||||
|
||||
func keyOffset(i int) int { return 2 * i }
|
||||
func valOffset(i int) int { return 2*i + 1 }
|
||||
func (n node) numKeys() int { return int(n.uint64(valOffset(maxKeys)) & 0xFFFFFFFF) }
|
||||
func (n node) pageID() uint64 { return n.uint64(keyOffset(maxKeys)) }
|
||||
func (n node) key(i int) uint64 { return n.uint64(keyOffset(i)) }
|
||||
func (n node) val(i int) uint64 { return n.uint64(valOffset(i)) }
|
||||
func (n node) data(i int) []uint64 { return n[keyOffset(i):keyOffset(i+1)] }
|
||||
|
||||
func (n node) setAt(start int, k uint64) {
|
||||
n[start] = k
|
||||
}
|
||||
|
||||
func (n node) setNumKeys(num int) {
|
||||
idx := valOffset(maxKeys)
|
||||
val := n[idx]
|
||||
val &= 0xFFFFFFFF00000000
|
||||
val |= uint64(num)
|
||||
n[idx] = val
|
||||
}
|
||||
|
||||
func (n node) moveRight(lo int) {
|
||||
hi := n.numKeys()
|
||||
assert(hi != maxKeys)
|
||||
// copy works despite of overlap in src and dst.
|
||||
// See https://golang.org/pkg/builtin/#copy
|
||||
copy(n[keyOffset(lo+1):keyOffset(hi+1)], n[keyOffset(lo):keyOffset(hi)])
|
||||
}
|
||||
|
||||
const (
|
||||
bitLeaf = uint64(1 << 63)
|
||||
)
|
||||
|
||||
func (n node) setBit(b uint64) {
|
||||
vo := valOffset(maxKeys)
|
||||
val := n[vo]
|
||||
val &= 0xFFFFFFFF
|
||||
val |= b
|
||||
n[vo] = val
|
||||
}
|
||||
func (n node) bits() uint64 {
|
||||
return n.val(maxKeys) & 0xFF00000000000000
|
||||
}
|
||||
func (n node) isLeaf() bool {
|
||||
return n.bits()&bitLeaf > 0
|
||||
}
|
||||
|
||||
// isFull checks that the node is already full.
|
||||
func (n node) isFull() bool {
|
||||
return n.numKeys() == maxKeys
|
||||
}
|
||||
|
||||
// Search returns the index of a smallest key >= k in a node.
|
||||
func (n node) search(k uint64) int {
|
||||
N := n.numKeys()
|
||||
if N < 4 {
|
||||
for i := 0; i < N; i++ {
|
||||
if ki := n.key(i); ki >= k {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return N
|
||||
}
|
||||
return int(simd.Search(n[:2*N], k))
|
||||
// lo, hi := 0, N
|
||||
// // Reduce the search space using binary seach and then do linear search.
|
||||
// for hi-lo > 32 {
|
||||
// mid := (hi + lo) / 2
|
||||
// km := n.key(mid)
|
||||
// if k == km {
|
||||
// return mid
|
||||
// }
|
||||
// if k > km {
|
||||
// // key is greater than the key at mid, so move right.
|
||||
// lo = mid + 1
|
||||
// } else {
|
||||
// // else move left.
|
||||
// hi = mid
|
||||
// }
|
||||
// }
|
||||
// for i := lo; i <= hi; i++ {
|
||||
// if ki := n.key(i); ki >= k {
|
||||
// return i
|
||||
// }
|
||||
// }
|
||||
// return N
|
||||
}
|
||||
func (n node) maxKey() uint64 {
|
||||
idx := n.numKeys()
|
||||
// idx points to the first key which is zero.
|
||||
if idx > 0 {
|
||||
idx--
|
||||
}
|
||||
return n.key(idx)
|
||||
}
|
||||
|
||||
// compacts the node i.e., remove all the kvs with value < lo. It returns the remaining number of
|
||||
// keys.
|
||||
func (n node) compact(lo uint64) int {
|
||||
N := n.numKeys()
|
||||
mk := n.maxKey()
|
||||
var left, right int
|
||||
for right = 0; right < N; right++ {
|
||||
if n.val(right) < lo && n.key(right) < mk {
|
||||
// Skip over this key. Don't copy it.
|
||||
continue
|
||||
}
|
||||
// Valid data. Copy it from right to left. Advance left.
|
||||
if left != right {
|
||||
copy(n.data(left), n.data(right))
|
||||
}
|
||||
left++
|
||||
}
|
||||
// zero out rest of the kv pairs.
|
||||
zeroOut(n[keyOffset(left):keyOffset(right)])
|
||||
n.setNumKeys(left)
|
||||
|
||||
// If the only key we have is the max key, and its value is less than lo, then we can indicate
|
||||
// to the caller by returning a zero that it's OK to drop the node.
|
||||
if left == 1 && n.key(0) == mk && n.val(0) < lo {
|
||||
return 0
|
||||
}
|
||||
return left
|
||||
}
|
||||
|
||||
func (n node) get(k uint64) uint64 {
|
||||
idx := n.search(k)
|
||||
// key is not found
|
||||
if idx == n.numKeys() {
|
||||
return 0
|
||||
}
|
||||
if ki := n.key(idx); ki == k {
|
||||
return n.val(idx)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// set returns true if it added a new key.
|
||||
func (n node) set(k, v uint64) (numAdded int) {
|
||||
idx := n.search(k)
|
||||
ki := n.key(idx)
|
||||
if n.numKeys() == maxKeys {
|
||||
// This happens during split of non-root node, when we are updating the child pointer of
|
||||
// right node. Hence, the key should already exist.
|
||||
assert(ki == k)
|
||||
}
|
||||
if ki > k {
|
||||
// Found the first entry which is greater than k. So, we need to fit k
|
||||
// just before it. For that, we should move the rest of the data in the
|
||||
// node to the right to make space for k.
|
||||
n.moveRight(idx)
|
||||
}
|
||||
// If the k does not exist already, increment the number of keys.
|
||||
if ki != k {
|
||||
n.setNumKeys(n.numKeys() + 1)
|
||||
numAdded = 1
|
||||
}
|
||||
if ki == 0 || ki >= k {
|
||||
n.setAt(keyOffset(idx), k)
|
||||
n.setAt(valOffset(idx), v)
|
||||
return
|
||||
}
|
||||
panic("shouldn't reach here")
|
||||
}
|
||||
|
||||
func (n node) iterate(fn func(node, int)) {
|
||||
for i := 0; i < maxKeys; i++ {
|
||||
if k := n.key(i); k > 0 {
|
||||
fn(n, i)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n node) print(parentID uint64) {
|
||||
var keys []string
|
||||
n.iterate(func(n node, i int) {
|
||||
keys = append(keys, fmt.Sprintf("%d", n.key(i)))
|
||||
})
|
||||
if len(keys) > 8 {
|
||||
copy(keys[4:], keys[len(keys)-4:])
|
||||
keys[3] = "..."
|
||||
keys = keys[:8]
|
||||
}
|
||||
fmt.Printf("%d Child of: %d num keys: %d keys: %s\n",
|
||||
n.pageID(), parentID, n.numKeys(), strings.Join(keys, " "))
|
||||
}
|
520
vendor/github.com/dgraph-io/ristretto/z/buffer.go
generated
vendored
Normal file
520
vendor/github.com/dgraph-io/ristretto/z/buffer.go
generated
vendored
Normal file
|
@ -0,0 +1,520 @@
|
|||
/*
|
||||
* Copyright 2020 Dgraph Labs, Inc. and Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package z
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"sort"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Buffer is equivalent of bytes.Buffer without the ability to read. It is NOT thread-safe.
|
||||
//
|
||||
// In UseCalloc mode, z.Calloc is used to allocate memory, which depending upon how the code is
|
||||
// compiled could use jemalloc for allocations.
|
||||
//
|
||||
// In UseMmap mode, Buffer uses file mmap to allocate memory. This allows us to store big data
|
||||
// structures without using physical memory.
|
||||
//
|
||||
// MaxSize can be set to limit the memory usage.
|
||||
type Buffer struct {
|
||||
padding uint64
|
||||
offset uint64
|
||||
buf []byte
|
||||
curSz int
|
||||
maxSz int
|
||||
fd *os.File
|
||||
bufType BufferType
|
||||
autoMmapAfter int
|
||||
dir string
|
||||
}
|
||||
|
||||
type BufferType int
|
||||
|
||||
func (t BufferType) String() string {
|
||||
switch t {
|
||||
case UseCalloc:
|
||||
return "UseCalloc"
|
||||
case UseMmap:
|
||||
return "UseMmap"
|
||||
}
|
||||
return "invalid"
|
||||
}
|
||||
|
||||
const (
|
||||
UseCalloc BufferType = iota
|
||||
UseMmap
|
||||
UseInvalid
|
||||
)
|
||||
|
||||
// smallBufferSize is an initial allocation minimal capacity.
|
||||
const smallBufferSize = 64
|
||||
|
||||
// NewBuffer is a helper utility, which creates a virtually unlimited Buffer in UseCalloc mode.
|
||||
func NewBuffer(sz int) *Buffer {
|
||||
buf, err := NewBufferWithDir(sz, MaxBufferSize, UseCalloc, "")
|
||||
if err != nil {
|
||||
log.Fatalf("while creating buffer: %v", err)
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
// NewBufferWith would allocate a buffer of size sz upfront, with the total size of the buffer not
|
||||
// exceeding maxSz. Both sz and maxSz can be set to zero, in which case reasonable defaults would be
|
||||
// used. Buffer can't be used without initialization via NewBuffer.
|
||||
func NewBufferWith(sz, maxSz int, bufType BufferType) (*Buffer, error) {
|
||||
buf, err := NewBufferWithDir(sz, maxSz, bufType, "")
|
||||
return buf, err
|
||||
}
|
||||
|
||||
func BufferFrom(data []byte) *Buffer {
|
||||
return &Buffer{
|
||||
offset: uint64(len(data)),
|
||||
padding: 0,
|
||||
buf: data,
|
||||
bufType: UseInvalid,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Buffer) doMmap() error {
|
||||
curBuf := b.buf
|
||||
fd, err := ioutil.TempFile(b.dir, "buffer")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fd.Truncate(int64(b.curSz)); err != nil {
|
||||
return errors.Wrapf(err, "while truncating %s to size: %d", fd.Name(), b.curSz)
|
||||
}
|
||||
|
||||
buf, err := Mmap(fd, true, int64(b.maxSz)) // Mmap up to max size.
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "while mmapping %s with size: %d", fd.Name(), b.maxSz)
|
||||
}
|
||||
if len(curBuf) > 0 {
|
||||
assert(int(b.offset) == copy(buf, curBuf[:b.offset]))
|
||||
Free(curBuf)
|
||||
}
|
||||
b.buf = buf
|
||||
b.bufType = UseMmap
|
||||
b.fd = fd
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewBufferWithDir would allocate a buffer of size sz upfront, with the total size of the buffer
|
||||
// not exceeding maxSz. Both sz and maxSz can be set to zero, in which case reasonable defaults
|
||||
// would be used. Buffer can't be used without initialization via NewBuffer. The buffer is created
|
||||
// inside dir. The caller should take care of existence of dir.
|
||||
func NewBufferWithDir(sz, maxSz int, bufType BufferType, dir string) (*Buffer, error) {
|
||||
if sz == 0 {
|
||||
sz = smallBufferSize
|
||||
}
|
||||
if maxSz == 0 {
|
||||
maxSz = math.MaxInt32
|
||||
}
|
||||
if len(dir) == 0 {
|
||||
dir = tmpDir
|
||||
}
|
||||
b := &Buffer{
|
||||
padding: 8,
|
||||
offset: 8, // Use 8 bytes of padding so that the elements are aligned.
|
||||
curSz: sz,
|
||||
maxSz: maxSz,
|
||||
bufType: UseCalloc, // by default.
|
||||
dir: dir,
|
||||
}
|
||||
|
||||
switch bufType {
|
||||
case UseCalloc:
|
||||
b.buf = Calloc(sz)
|
||||
case UseMmap:
|
||||
if err := b.doMmap(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
log.Fatalf("Invalid bufType: %q\n", bufType)
|
||||
}
|
||||
|
||||
b.buf[0] = 0x00
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (b *Buffer) IsEmpty() bool {
|
||||
return int(b.offset) == b.StartOffset()
|
||||
}
|
||||
|
||||
// LenWithPadding would return the number of bytes written to the buffer so far
|
||||
// plus the padding at the start of the buffer.
|
||||
func (b *Buffer) LenWithPadding() int {
|
||||
return int(atomic.LoadUint64(&b.offset))
|
||||
}
|
||||
|
||||
// LenNoPadding would return the number of bytes written to the buffer so far
|
||||
// (without the padding).
|
||||
func (b *Buffer) LenNoPadding() int {
|
||||
return int(atomic.LoadUint64(&b.offset) - b.padding)
|
||||
}
|
||||
|
||||
// Bytes would return all the written bytes as a slice.
|
||||
func (b *Buffer) Bytes() []byte {
|
||||
off := atomic.LoadUint64(&b.offset)
|
||||
return b.buf[b.padding:off]
|
||||
}
|
||||
|
||||
func (b *Buffer) AutoMmapAfter(size int) {
|
||||
b.autoMmapAfter = size
|
||||
}
|
||||
|
||||
// Grow would grow the buffer to have at least n more bytes. In case the buffer is at capacity, it
|
||||
// would reallocate twice the size of current capacity + n, to ensure n bytes can be written to the
|
||||
// buffer without further allocation. In UseMmap mode, this might result in underlying file
|
||||
// expansion.
|
||||
func (b *Buffer) Grow(n int) {
|
||||
// In this case, len and cap are the same.
|
||||
if b.buf == nil {
|
||||
panic("z.Buffer needs to be initialized before using")
|
||||
}
|
||||
if b.maxSz-int(b.offset) < n {
|
||||
panic(fmt.Sprintf("Buffer max size exceeded: %d."+
|
||||
" Offset: %d. Grow: %d", b.maxSz, b.offset, n))
|
||||
}
|
||||
if b.curSz-int(b.offset) > n {
|
||||
return
|
||||
}
|
||||
|
||||
growBy := b.curSz + n
|
||||
if growBy > 1<<30 {
|
||||
growBy = 1 << 30
|
||||
}
|
||||
if n > growBy {
|
||||
// Always at least allocate n, even if it exceeds the 1GB limit above.
|
||||
growBy = n
|
||||
}
|
||||
b.curSz += growBy
|
||||
|
||||
switch b.bufType {
|
||||
case UseCalloc:
|
||||
if b.autoMmapAfter > 0 && b.curSz > b.autoMmapAfter {
|
||||
// This would do copy as well.
|
||||
check(b.doMmap())
|
||||
|
||||
} else {
|
||||
newBuf := Calloc(b.curSz)
|
||||
copy(newBuf, b.buf[:b.offset])
|
||||
Free(b.buf)
|
||||
b.buf = newBuf
|
||||
}
|
||||
case UseMmap:
|
||||
if err := b.fd.Truncate(int64(b.curSz)); err != nil {
|
||||
log.Fatalf("While trying to truncate file %s to size: %d error: %v\n",
|
||||
b.fd.Name(), b.curSz, err)
|
||||
}
|
||||
default:
|
||||
panic("Invalid bufType")
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate is a way to get a slice of size n back from the buffer. This slice can be directly
|
||||
// written to. Warning: Allocate is not thread-safe. The byte slice returned MUST be used before
|
||||
// further calls to Buffer.
|
||||
func (b *Buffer) Allocate(n int) []byte {
|
||||
b.Grow(n)
|
||||
off := b.offset
|
||||
b.offset += uint64(n)
|
||||
return b.buf[off:int(b.offset)]
|
||||
}
|
||||
|
||||
// AllocateOffset works the same way as allocate, but instead of returning a byte slice, it returns
|
||||
// the offset of the allocation.
|
||||
func (b *Buffer) AllocateOffset(n int) int {
|
||||
b.Grow(n)
|
||||
b.offset += uint64(n)
|
||||
return int(b.offset) - n
|
||||
}
|
||||
|
||||
func (b *Buffer) writeLen(sz int) {
|
||||
buf := b.Allocate(4)
|
||||
binary.BigEndian.PutUint32(buf, uint32(sz))
|
||||
}
|
||||
|
||||
// SliceAllocate would encode the size provided into the buffer, followed by a call to Allocate,
|
||||
// hence returning the slice of size sz. This can be used to allocate a lot of small buffers into
|
||||
// this big buffer.
|
||||
// Note that SliceAllocate should NOT be mixed with normal calls to Write.
|
||||
func (b *Buffer) SliceAllocate(sz int) []byte {
|
||||
b.Grow(4 + sz)
|
||||
b.writeLen(sz)
|
||||
return b.Allocate(sz)
|
||||
}
|
||||
|
||||
func (b *Buffer) StartOffset() int { return int(b.padding) }
|
||||
|
||||
func (b *Buffer) WriteSlice(slice []byte) {
|
||||
dst := b.SliceAllocate(len(slice))
|
||||
copy(dst, slice)
|
||||
}
|
||||
|
||||
func (b *Buffer) SliceIterate(f func(slice []byte) error) error {
|
||||
if b.IsEmpty() {
|
||||
return nil
|
||||
}
|
||||
slice, next := []byte{}, b.StartOffset()
|
||||
for next >= 0 {
|
||||
slice, next = b.Slice(next)
|
||||
if len(slice) == 0 {
|
||||
continue
|
||||
}
|
||||
if err := f(slice); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type LessFunc func(a, b []byte) bool
|
||||
type sortHelper struct {
|
||||
offsets []int
|
||||
b *Buffer
|
||||
tmp *Buffer
|
||||
less LessFunc
|
||||
small []int
|
||||
}
|
||||
|
||||
func (s *sortHelper) sortSmall(start, end int) {
|
||||
s.tmp.Reset()
|
||||
s.small = s.small[:0]
|
||||
next := start
|
||||
for next >= 0 && next < end {
|
||||
s.small = append(s.small, next)
|
||||
_, next = s.b.Slice(next)
|
||||
}
|
||||
|
||||
// We are sorting the slices pointed to by s.small offsets, but only moving the offsets around.
|
||||
sort.Slice(s.small, func(i, j int) bool {
|
||||
left, _ := s.b.Slice(s.small[i])
|
||||
right, _ := s.b.Slice(s.small[j])
|
||||
return s.less(left, right)
|
||||
})
|
||||
// Now we iterate over the s.small offsets and copy over the slices. The result is now in order.
|
||||
for _, off := range s.small {
|
||||
s.tmp.Write(rawSlice(s.b.buf[off:]))
|
||||
}
|
||||
assert(end-start == copy(s.b.buf[start:end], s.tmp.Bytes()))
|
||||
}
|
||||
|
||||
func assert(b bool) {
|
||||
if !b {
|
||||
log.Fatalf("%+v", errors.Errorf("Assertion failure"))
|
||||
}
|
||||
}
|
||||
func check(err error) {
|
||||
if err != nil {
|
||||
log.Fatalf("%+v", err)
|
||||
}
|
||||
}
|
||||
func check2(_ interface{}, err error) {
|
||||
check(err)
|
||||
}
|
||||
|
||||
func (s *sortHelper) merge(left, right []byte, start, end int) {
|
||||
if len(left) == 0 || len(right) == 0 {
|
||||
return
|
||||
}
|
||||
s.tmp.Reset()
|
||||
check2(s.tmp.Write(left))
|
||||
left = s.tmp.Bytes()
|
||||
|
||||
var ls, rs []byte
|
||||
|
||||
copyLeft := func() {
|
||||
assert(len(ls) == copy(s.b.buf[start:], ls))
|
||||
left = left[len(ls):]
|
||||
start += len(ls)
|
||||
}
|
||||
copyRight := func() {
|
||||
assert(len(rs) == copy(s.b.buf[start:], rs))
|
||||
right = right[len(rs):]
|
||||
start += len(rs)
|
||||
}
|
||||
|
||||
for start < end {
|
||||
if len(left) == 0 {
|
||||
assert(len(right) == copy(s.b.buf[start:end], right))
|
||||
return
|
||||
}
|
||||
if len(right) == 0 {
|
||||
assert(len(left) == copy(s.b.buf[start:end], left))
|
||||
return
|
||||
}
|
||||
ls = rawSlice(left)
|
||||
rs = rawSlice(right)
|
||||
|
||||
// We skip the first 4 bytes in the rawSlice, because that stores the length.
|
||||
if s.less(ls[4:], rs[4:]) {
|
||||
copyLeft()
|
||||
} else {
|
||||
copyRight()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sortHelper) sort(lo, hi int) []byte {
|
||||
assert(lo <= hi)
|
||||
|
||||
mid := lo + (hi-lo)/2
|
||||
loff, hoff := s.offsets[lo], s.offsets[hi]
|
||||
if lo == mid {
|
||||
// No need to sort, just return the buffer.
|
||||
return s.b.buf[loff:hoff]
|
||||
}
|
||||
|
||||
// lo, mid would sort from [offset[lo], offset[mid]) .
|
||||
left := s.sort(lo, mid)
|
||||
// Typically we'd use mid+1, but here mid represents an offset in the buffer. Each offset
|
||||
// contains a thousand entries. So, if we do mid+1, we'd skip over those entries.
|
||||
right := s.sort(mid, hi)
|
||||
|
||||
s.merge(left, right, loff, hoff)
|
||||
return s.b.buf[loff:hoff]
|
||||
}
|
||||
|
||||
// SortSlice is like SortSliceBetween but sorting over the entire buffer.
|
||||
func (b *Buffer) SortSlice(less func(left, right []byte) bool) {
|
||||
b.SortSliceBetween(b.StartOffset(), int(b.offset), less)
|
||||
}
|
||||
func (b *Buffer) SortSliceBetween(start, end int, less LessFunc) {
|
||||
if start >= end {
|
||||
return
|
||||
}
|
||||
if start == 0 {
|
||||
panic("start can never be zero")
|
||||
}
|
||||
|
||||
var offsets []int
|
||||
next, count := start, 0
|
||||
for next >= 0 && next < end {
|
||||
if count%1024 == 0 {
|
||||
offsets = append(offsets, next)
|
||||
}
|
||||
_, next = b.Slice(next)
|
||||
count++
|
||||
}
|
||||
assert(len(offsets) > 0)
|
||||
if offsets[len(offsets)-1] != end {
|
||||
offsets = append(offsets, end)
|
||||
}
|
||||
|
||||
szTmp := int(float64((end-start)/2) * 1.1)
|
||||
s := &sortHelper{
|
||||
offsets: offsets,
|
||||
b: b,
|
||||
less: less,
|
||||
small: make([]int, 0, 1024),
|
||||
tmp: NewBuffer(szTmp),
|
||||
}
|
||||
defer s.tmp.Release()
|
||||
|
||||
left := offsets[0]
|
||||
for _, off := range offsets[1:] {
|
||||
s.sortSmall(left, off)
|
||||
left = off
|
||||
}
|
||||
s.sort(0, len(offsets)-1)
|
||||
}
|
||||
|
||||
func rawSlice(buf []byte) []byte {
|
||||
sz := binary.BigEndian.Uint32(buf)
|
||||
return buf[:4+int(sz)]
|
||||
}
|
||||
|
||||
// Slice would return the slice written at offset.
|
||||
func (b *Buffer) Slice(offset int) ([]byte, int) {
|
||||
if offset >= int(b.offset) {
|
||||
return nil, -1
|
||||
}
|
||||
|
||||
sz := binary.BigEndian.Uint32(b.buf[offset:])
|
||||
start := offset + 4
|
||||
next := start + int(sz)
|
||||
res := b.buf[start:next]
|
||||
if next >= int(b.offset) {
|
||||
next = -1
|
||||
}
|
||||
return res, next
|
||||
}
|
||||
|
||||
// SliceOffsets is an expensive function. Use sparingly.
|
||||
func (b *Buffer) SliceOffsets() []int {
|
||||
next := b.StartOffset()
|
||||
var offsets []int
|
||||
for next >= 0 {
|
||||
offsets = append(offsets, next)
|
||||
_, next = b.Slice(next)
|
||||
}
|
||||
return offsets
|
||||
}
|
||||
|
||||
func (b *Buffer) Data(offset int) []byte {
|
||||
if offset > b.curSz {
|
||||
panic("offset beyond current size")
|
||||
}
|
||||
return b.buf[offset:b.curSz]
|
||||
}
|
||||
|
||||
// Write would write p bytes to the buffer.
|
||||
func (b *Buffer) Write(p []byte) (n int, err error) {
|
||||
b.Grow(len(p))
|
||||
n = copy(b.buf[b.offset:], p)
|
||||
b.offset += uint64(n)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Reset would reset the buffer to be reused.
|
||||
func (b *Buffer) Reset() {
|
||||
b.offset = uint64(b.StartOffset())
|
||||
}
|
||||
|
||||
// Release would free up the memory allocated by the buffer. Once the usage of buffer is done, it is
|
||||
// important to call Release, otherwise a memory leak can happen.
|
||||
func (b *Buffer) Release() error {
|
||||
switch b.bufType {
|
||||
case UseCalloc:
|
||||
Free(b.buf)
|
||||
|
||||
case UseMmap:
|
||||
fname := b.fd.Name()
|
||||
if err := Munmap(b.buf); err != nil {
|
||||
return errors.Wrapf(err, "while munmap file %s", fname)
|
||||
}
|
||||
if err := b.fd.Truncate(0); err != nil {
|
||||
return errors.Wrapf(err, "while truncating file %s", fname)
|
||||
}
|
||||
if err := b.fd.Close(); err != nil {
|
||||
return errors.Wrapf(err, "while closing file %s", fname)
|
||||
}
|
||||
if err := os.Remove(b.fd.Name()); err != nil {
|
||||
return errors.Wrapf(err, "while deleting file %s", fname)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
42
vendor/github.com/dgraph-io/ristretto/z/calloc.go
generated
vendored
Normal file
42
vendor/github.com/dgraph-io/ristretto/z/calloc.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
package z
|
||||
|
||||
import "sync/atomic"
|
||||
|
||||
var numBytes int64
|
||||
|
||||
// NumAllocBytes returns the number of bytes allocated using calls to z.Calloc. The allocations
|
||||
// could be happening via either Go or jemalloc, depending upon the build flags.
|
||||
func NumAllocBytes() int64 {
|
||||
return atomic.LoadInt64(&numBytes)
|
||||
}
|
||||
|
||||
// MemStats is used to fetch JE Malloc Stats. The stats are fetched from
|
||||
// the mallctl namespace http://jemalloc.net/jemalloc.3.html#mallctl_namespace.
|
||||
type MemStats struct {
|
||||
// Total number of bytes allocated by the application.
|
||||
// http://jemalloc.net/jemalloc.3.html#stats.allocated
|
||||
Allocated uint64
|
||||
// Total number of bytes in active pages allocated by the application. This
|
||||
// is a multiple of the page size, and greater than or equal to
|
||||
// Allocated.
|
||||
// http://jemalloc.net/jemalloc.3.html#stats.active
|
||||
Active uint64
|
||||
// Maximum number of bytes in physically resident data pages mapped by the
|
||||
// allocator, comprising all pages dedicated to allocator metadata, pages
|
||||
// backing active allocations, and unused dirty pages. This is a maximum
|
||||
// rather than precise because pages may not actually be physically
|
||||
// resident if they correspond to demand-zeroed virtual memory that has not
|
||||
// yet been touched. This is a multiple of the page size, and is larger
|
||||
// than stats.active.
|
||||
// http://jemalloc.net/jemalloc.3.html#stats.resident
|
||||
Resident uint64
|
||||
// Total number of bytes in virtual memory mappings that were retained
|
||||
// rather than being returned to the operating system via e.g. munmap(2) or
|
||||
// similar. Retained virtual memory is typically untouched, decommitted, or
|
||||
// purged, so it has no strongly associated physical memory (see extent
|
||||
// hooks http://jemalloc.net/jemalloc.3.html#arena.i.extent_hooks for
|
||||
// details). Retained memory is excluded from mapped memory statistics,
|
||||
// e.g. stats.mapped (http://jemalloc.net/jemalloc.3.html#stats.mapped).
|
||||
// http://jemalloc.net/jemalloc.3.html#stats.retained
|
||||
Retained uint64
|
||||
}
|
14
vendor/github.com/dgraph-io/ristretto/z/calloc_32bit.go
generated
vendored
Normal file
14
vendor/github.com/dgraph-io/ristretto/z/calloc_32bit.go
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2020 The LevelDB-Go and Pebble 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 386 amd64p32 arm armbe mips mipsle mips64p32 mips64p32le ppc sparc
|
||||
|
||||
package z
|
||||
|
||||
const (
|
||||
// MaxArrayLen is a safe maximum length for slices on this architecture.
|
||||
MaxArrayLen = 1<<31 - 1
|
||||
// MaxBufferSize is the size of virtually unlimited buffer on this architecture.
|
||||
MaxBufferSize = 1 << 30
|
||||
)
|
14
vendor/github.com/dgraph-io/ristretto/z/calloc_64bit.go
generated
vendored
Normal file
14
vendor/github.com/dgraph-io/ristretto/z/calloc_64bit.go
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2020 The LevelDB-Go and Pebble 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 amd64 arm64 arm64be ppc64 ppc64le mips64 mips64le s390x sparc64
|
||||
|
||||
package z
|
||||
|
||||
const (
|
||||
// MaxArrayLen is a safe maximum length for slices on this architecture.
|
||||
MaxArrayLen = 1<<50 - 1
|
||||
// MaxBufferSize is the size of virtually unlimited buffer on this architecture.
|
||||
MaxBufferSize = 256 << 30
|
||||
)
|
196
vendor/github.com/dgraph-io/ristretto/z/calloc_jemalloc.go
generated
vendored
Normal file
196
vendor/github.com/dgraph-io/ristretto/z/calloc_jemalloc.go
generated
vendored
Normal file
|
@ -0,0 +1,196 @@
|
|||
// Copyright 2020 The LevelDB-Go and Pebble 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 jemalloc
|
||||
|
||||
package z
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: /usr/local/lib/libjemalloc.a -L/usr/local/lib -Wl,-rpath,/usr/local/lib -ljemalloc -lm -lstdc++ -pthread -ldl
|
||||
#include <stdlib.h>
|
||||
#include <jemalloc/jemalloc.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
)
|
||||
|
||||
// The go:linkname directives provides backdoor access to private functions in
|
||||
// the runtime. Below we're accessing the throw function.
|
||||
|
||||
//go:linkname throw runtime.throw
|
||||
func throw(s string)
|
||||
|
||||
// New allocates a slice of size n. The returned slice is from manually managed
|
||||
// memory and MUST be released by calling Free. Failure to do so will result in
|
||||
// a memory leak.
|
||||
//
|
||||
// Compile jemalloc with ./configure --with-jemalloc-prefix="je_"
|
||||
// https://android.googlesource.com/platform/external/jemalloc_new/+/6840b22e8e11cb68b493297a5cd757d6eaa0b406/TUNING.md
|
||||
// These two config options seems useful for frequent allocations and deallocations in
|
||||
// multi-threaded programs (like we have).
|
||||
// JE_MALLOC_CONF="background_thread:true,metadata_thp:auto"
|
||||
//
|
||||
// Compile Go program with `go build -tags=jemalloc` to enable this.
|
||||
|
||||
type dalloc struct {
|
||||
f string
|
||||
no int
|
||||
sz int
|
||||
}
|
||||
|
||||
// Enabled via 'leak' build flag.
|
||||
var dallocsMu sync.Mutex
|
||||
var dallocs map[unsafe.Pointer]*dalloc
|
||||
|
||||
func init() {
|
||||
// By initializing dallocs, we can start tracking allocations and deallocations via z.Calloc.
|
||||
dallocs = make(map[unsafe.Pointer]*dalloc)
|
||||
}
|
||||
|
||||
func Calloc(n int) []byte {
|
||||
if n == 0 {
|
||||
return make([]byte, 0)
|
||||
}
|
||||
// We need to be conscious of the Cgo pointer passing rules:
|
||||
//
|
||||
// https://golang.org/cmd/cgo/#hdr-Passing_pointers
|
||||
//
|
||||
// ...
|
||||
// Note: the current implementation has a bug. While Go code is permitted
|
||||
// to write nil or a C pointer (but not a Go pointer) to C memory, the
|
||||
// current implementation may sometimes cause a runtime error if the
|
||||
// contents of the C memory appear to be a Go pointer. Therefore, avoid
|
||||
// passing uninitialized C memory to Go code if the Go code is going to
|
||||
// store pointer values in it. Zero out the memory in C before passing it
|
||||
// to Go.
|
||||
|
||||
ptr := C.je_calloc(C.size_t(n), 1)
|
||||
if ptr == nil {
|
||||
// NB: throw is like panic, except it guarantees the process will be
|
||||
// terminated. The call below is exactly what the Go runtime invokes when
|
||||
// it cannot allocate memory.
|
||||
throw("out of memory")
|
||||
}
|
||||
uptr := unsafe.Pointer(ptr)
|
||||
|
||||
if dallocs != nil {
|
||||
// If leak detection is enabled.
|
||||
for i := 1; ; i++ {
|
||||
_, f, l, ok := runtime.Caller(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if strings.Contains(f, "/ristretto") {
|
||||
continue
|
||||
}
|
||||
|
||||
dallocsMu.Lock()
|
||||
dallocs[uptr] = &dalloc{
|
||||
f: f,
|
||||
no: l,
|
||||
sz: n,
|
||||
}
|
||||
dallocsMu.Unlock()
|
||||
break
|
||||
}
|
||||
}
|
||||
atomic.AddInt64(&numBytes, int64(n))
|
||||
// Interpret the C pointer as a pointer to a Go array, then slice.
|
||||
return (*[MaxArrayLen]byte)(uptr)[:n:n]
|
||||
}
|
||||
|
||||
// CallocNoRef does the exact same thing as Calloc with jemalloc enabled.
|
||||
func CallocNoRef(n int) []byte {
|
||||
return Calloc(n)
|
||||
}
|
||||
|
||||
// Free frees the specified slice.
|
||||
func Free(b []byte) {
|
||||
if sz := cap(b); sz != 0 {
|
||||
b = b[:cap(b)]
|
||||
ptr := unsafe.Pointer(&b[0])
|
||||
C.je_free(ptr)
|
||||
atomic.AddInt64(&numBytes, -int64(sz))
|
||||
|
||||
if dallocs != nil {
|
||||
// If leak detection is enabled.
|
||||
dallocsMu.Lock()
|
||||
delete(dallocs, ptr)
|
||||
dallocsMu.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Leaks() string {
|
||||
if dallocs == nil {
|
||||
return "Leak detection disabled. Enable with 'leak' build flag."
|
||||
}
|
||||
dallocsMu.Lock()
|
||||
defer dallocsMu.Unlock()
|
||||
if len(dallocs) == 0 {
|
||||
return "NO leaks found."
|
||||
}
|
||||
m := make(map[string]int)
|
||||
for _, da := range dallocs {
|
||||
m[da.f+":"+strconv.Itoa(da.no)] += da.sz
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "Allocations:\n")
|
||||
for f, sz := range m {
|
||||
fmt.Fprintf(&buf, "%s at file: %s\n", humanize.IBytes(uint64(sz)), f)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// ReadMemStats populates stats with JE Malloc statistics.
|
||||
func ReadMemStats(stats *MemStats) {
|
||||
if stats == nil {
|
||||
return
|
||||
}
|
||||
// Call an epoch mallclt to refresh the stats data as mentioned in the docs.
|
||||
// http://jemalloc.net/jemalloc.3.html#epoch
|
||||
// Note: This epoch mallctl is as expensive as a malloc call. It takes up the
|
||||
// malloc_mutex_lock.
|
||||
epoch := 1
|
||||
sz := unsafe.Sizeof(&epoch)
|
||||
C.je_mallctl(
|
||||
(C.CString)("epoch"),
|
||||
unsafe.Pointer(&epoch),
|
||||
(*C.size_t)(unsafe.Pointer(&sz)),
|
||||
unsafe.Pointer(&epoch),
|
||||
(C.size_t)(unsafe.Sizeof(epoch)))
|
||||
stats.Allocated = fetchStat("stats.allocated")
|
||||
stats.Active = fetchStat("stats.active")
|
||||
stats.Resident = fetchStat("stats.resident")
|
||||
stats.Retained = fetchStat("stats.retained")
|
||||
}
|
||||
|
||||
// fetchStat is used to read a specific attribute from je malloc stats using mallctl.
|
||||
func fetchStat(s string) uint64 {
|
||||
var out uint64
|
||||
sz := unsafe.Sizeof(&out)
|
||||
C.je_mallctl(
|
||||
(C.CString)(s), // Query: eg: stats.allocated, stats.resident, etc.
|
||||
unsafe.Pointer(&out), // Variable to store the output.
|
||||
(*C.size_t)(unsafe.Pointer(&sz)), // Size of the output variable.
|
||||
nil, // Input variable used to set a value.
|
||||
0) // Size of the input variable.
|
||||
return out
|
||||
}
|
||||
|
||||
func StatsPrint() {
|
||||
opts := C.CString("mdablxe")
|
||||
C.je_malloc_stats_print(nil, nil, opts)
|
||||
C.free(unsafe.Pointer(opts))
|
||||
}
|
37
vendor/github.com/dgraph-io/ristretto/z/calloc_nojemalloc.go
generated
vendored
Normal file
37
vendor/github.com/dgraph-io/ristretto/z/calloc_nojemalloc.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2020 The LevelDB-Go and Pebble 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 !jemalloc !cgo
|
||||
|
||||
package z
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Provides versions of Calloc, CallocNoRef, etc when jemalloc is not available
|
||||
// (eg: build without jemalloc tag).
|
||||
|
||||
// Calloc allocates a slice of size n.
|
||||
func Calloc(n int) []byte {
|
||||
return make([]byte, n)
|
||||
}
|
||||
|
||||
// CallocNoRef will not give you memory back without jemalloc.
|
||||
func CallocNoRef(n int) []byte {
|
||||
// We do the add here just to stay compatible with a corresponding Free call.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Free does not do anything in this mode.
|
||||
func Free(b []byte) {}
|
||||
|
||||
func Leaks() string { return "Leaks: Using Go memory" }
|
||||
func StatsPrint() {
|
||||
fmt.Println("Using Go memory")
|
||||
}
|
||||
|
||||
// ReadMemStats doesn't do anything since all the memory is being managed
|
||||
// by the Go runtime.
|
||||
func ReadMemStats(_ *MemStats) { return }
|
217
vendor/github.com/dgraph-io/ristretto/z/file.go
generated
vendored
Normal file
217
vendor/github.com/dgraph-io/ristretto/z/file.go
generated
vendored
Normal file
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* Copyright 2020 Dgraph Labs, Inc. and Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package z
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// MmapFile represents an mmapd file and includes both the buffer to the data
|
||||
// and the file descriptor.
|
||||
type MmapFile struct {
|
||||
Data []byte
|
||||
Fd *os.File
|
||||
}
|
||||
|
||||
var NewFile = errors.New("Create a new file")
|
||||
|
||||
func OpenMmapFileUsing(fd *os.File, sz int, writable bool) (*MmapFile, error) {
|
||||
filename := fd.Name()
|
||||
fi, err := fd.Stat()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "cannot stat file: %s", filename)
|
||||
}
|
||||
|
||||
var rerr error
|
||||
fileSize := fi.Size()
|
||||
if sz > 0 && fileSize == 0 {
|
||||
// If file is empty, truncate it to sz.
|
||||
if err := fd.Truncate(int64(sz)); err != nil {
|
||||
return nil, errors.Wrapf(err, "error while truncation")
|
||||
}
|
||||
fileSize = int64(sz)
|
||||
rerr = NewFile
|
||||
}
|
||||
|
||||
// fmt.Printf("Mmaping file: %s with writable: %v filesize: %d\n", fd.Name(), writable, fileSize)
|
||||
buf, err := Mmap(fd, writable, fileSize) // Mmap up to file size.
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "while mmapping %s with size: %d", fd.Name(), fileSize)
|
||||
}
|
||||
|
||||
if fileSize == 0 {
|
||||
dir, _ := filepath.Split(filename)
|
||||
go SyncDir(dir)
|
||||
}
|
||||
return &MmapFile{
|
||||
Data: buf,
|
||||
Fd: fd,
|
||||
}, rerr
|
||||
}
|
||||
|
||||
// OpenMmapFile opens an existing file or creates a new file. If the file is
|
||||
// created, it would truncate the file to maxSz. In both cases, it would mmap
|
||||
// the file to maxSz and returned it. In case the file is created, z.NewFile is
|
||||
// returned.
|
||||
func OpenMmapFile(filename string, flag int, maxSz int) (*MmapFile, error) {
|
||||
// fmt.Printf("opening file %s with flag: %v\n", filename, flag)
|
||||
fd, err := os.OpenFile(filename, flag, 0666)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to open: %s", filename)
|
||||
}
|
||||
writable := true
|
||||
if flag == os.O_RDONLY {
|
||||
writable = false
|
||||
}
|
||||
return OpenMmapFileUsing(fd, maxSz, writable)
|
||||
}
|
||||
|
||||
type mmapReader struct {
|
||||
Data []byte
|
||||
offset int
|
||||
}
|
||||
|
||||
func (mr *mmapReader) Read(buf []byte) (int, error) {
|
||||
if mr.offset > len(mr.Data) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n := copy(buf, mr.Data[mr.offset:])
|
||||
mr.offset += n
|
||||
if n < len(buf) {
|
||||
return n, io.EOF
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (m *MmapFile) NewReader(offset int) io.Reader {
|
||||
return &mmapReader{
|
||||
Data: m.Data,
|
||||
offset: offset,
|
||||
}
|
||||
}
|
||||
|
||||
// Bytes returns data starting from offset off of size sz. If there's not enough data, it would
|
||||
// return nil slice and io.EOF.
|
||||
func (m *MmapFile) Bytes(off, sz int) ([]byte, error) {
|
||||
if len(m.Data[off:]) < sz {
|
||||
return nil, io.EOF
|
||||
}
|
||||
return m.Data[off : off+sz], nil
|
||||
}
|
||||
|
||||
// Slice returns the slice at the given offset.
|
||||
func (m *MmapFile) Slice(offset int) []byte {
|
||||
sz := binary.BigEndian.Uint32(m.Data[offset:])
|
||||
start := offset + 4
|
||||
next := start + int(sz)
|
||||
if next > len(m.Data) {
|
||||
return []byte{}
|
||||
}
|
||||
res := m.Data[start:next]
|
||||
return res
|
||||
}
|
||||
|
||||
// AllocateSlice allocates a slice of the given size at the given offset.
|
||||
func (m *MmapFile) AllocateSlice(sz, offset int) ([]byte, int, error) {
|
||||
start := offset + 4
|
||||
|
||||
// If the file is too small, double its size or increase it by 1GB, whichever is smaller.
|
||||
if start+sz > len(m.Data) {
|
||||
const oneGB = 1 << 30
|
||||
growBy := len(m.Data)
|
||||
if growBy > oneGB {
|
||||
growBy = oneGB
|
||||
}
|
||||
if growBy < sz+4 {
|
||||
growBy = sz + 4
|
||||
}
|
||||
if err := m.Truncate(int64(len(m.Data) + growBy)); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint32(m.Data[offset:], uint32(sz))
|
||||
return m.Data[start : start+sz], start + sz, nil
|
||||
}
|
||||
|
||||
func (m *MmapFile) Sync() error {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
return Msync(m.Data)
|
||||
}
|
||||
|
||||
func (m *MmapFile) Delete() error {
|
||||
// Badger can set the m.Data directly, without setting any Fd. In that case, this should be a
|
||||
// NOOP.
|
||||
if m.Fd == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := Munmap(m.Data); err != nil {
|
||||
return fmt.Errorf("while munmap file: %s, error: %v\n", m.Fd.Name(), err)
|
||||
}
|
||||
m.Data = nil
|
||||
if err := m.Fd.Truncate(0); err != nil {
|
||||
return fmt.Errorf("while truncate file: %s, error: %v\n", m.Fd.Name(), err)
|
||||
}
|
||||
if err := m.Fd.Close(); err != nil {
|
||||
return fmt.Errorf("while close file: %s, error: %v\n", m.Fd.Name(), err)
|
||||
}
|
||||
return os.Remove(m.Fd.Name())
|
||||
}
|
||||
|
||||
// Close would close the file. It would also truncate the file if maxSz >= 0.
|
||||
func (m *MmapFile) Close(maxSz int64) error {
|
||||
// Badger can set the m.Data directly, without setting any Fd. In that case, this should be a
|
||||
// NOOP.
|
||||
if m.Fd == nil {
|
||||
return nil
|
||||
}
|
||||
if err := m.Sync(); err != nil {
|
||||
return fmt.Errorf("while sync file: %s, error: %v\n", m.Fd.Name(), err)
|
||||
}
|
||||
if err := Munmap(m.Data); err != nil {
|
||||
return fmt.Errorf("while munmap file: %s, error: %v\n", m.Fd.Name(), err)
|
||||
}
|
||||
if maxSz >= 0 {
|
||||
if err := m.Fd.Truncate(maxSz); err != nil {
|
||||
return fmt.Errorf("while truncate file: %s, error: %v\n", m.Fd.Name(), err)
|
||||
}
|
||||
}
|
||||
return m.Fd.Close()
|
||||
}
|
||||
|
||||
func SyncDir(dir string) error {
|
||||
df, err := os.Open(dir)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "while opening %s", dir)
|
||||
}
|
||||
if err := df.Sync(); err != nil {
|
||||
return errors.Wrapf(err, "while syncing %s", dir)
|
||||
}
|
||||
if err := df.Close(); err != nil {
|
||||
return errors.Wrapf(err, "while closing %s", dir)
|
||||
}
|
||||
return nil
|
||||
}
|
39
vendor/github.com/dgraph-io/ristretto/z/file_default.go
generated
vendored
Normal file
39
vendor/github.com/dgraph-io/ristretto/z/file_default.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
// +build !linux
|
||||
|
||||
/*
|
||||
* Copyright 2020 Dgraph Labs, Inc. and Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package z
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Truncate would truncate the mmapped file to the given size. On Linux, we truncate
|
||||
// the underlying file and then call mremap, but on other systems, we unmap first,
|
||||
// then truncate, then re-map.
|
||||
func (m *MmapFile) Truncate(maxSz int64) error {
|
||||
if err := m.Sync(); err != nil {
|
||||
return fmt.Errorf("while sync file: %s, error: %v\n", m.Fd.Name(), err)
|
||||
}
|
||||
if err := Munmap(m.Data); err != nil {
|
||||
return fmt.Errorf("while munmap file: %s, error: %v\n", m.Fd.Name(), err)
|
||||
}
|
||||
if err := m.Fd.Truncate(maxSz); err != nil {
|
||||
return fmt.Errorf("while truncate file: %s, error: %v\n", m.Fd.Name(), err)
|
||||
}
|
||||
var err error
|
||||
m.Data, err = Mmap(m.Fd, true, maxSz) // Mmap up to max size.
|
||||
return err
|
||||
}
|
37
vendor/github.com/dgraph-io/ristretto/z/file_linux.go
generated
vendored
Normal file
37
vendor/github.com/dgraph-io/ristretto/z/file_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2020 Dgraph Labs, Inc. and Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package z
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Truncate would truncate the mmapped file to the given size. On Linux, we truncate
|
||||
// the underlying file and then call mremap, but on other systems, we unmap first,
|
||||
// then truncate, then re-map.
|
||||
func (m *MmapFile) Truncate(maxSz int64) error {
|
||||
if err := m.Sync(); err != nil {
|
||||
return fmt.Errorf("while sync file: %s, error: %v\n", m.Fd.Name(), err)
|
||||
}
|
||||
if err := m.Fd.Truncate(maxSz); err != nil {
|
||||
return fmt.Errorf("while truncate file: %s, error: %v\n", m.Fd.Name(), err)
|
||||
}
|
||||
|
||||
var err error
|
||||
m.Data, err = mremap(m.Data, int(maxSz)) // Mmap up to max size.
|
||||
return err
|
||||
}
|
151
vendor/github.com/dgraph-io/ristretto/z/histogram.go
generated
vendored
Normal file
151
vendor/github.com/dgraph-io/ristretto/z/histogram.go
generated
vendored
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright 2020 Dgraph Labs, Inc. and Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package z
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
)
|
||||
|
||||
// Creates bounds for an histogram. The bounds are powers of two of the form
|
||||
// [2^min_exponent, ..., 2^max_exponent].
|
||||
func HistogramBounds(minExponent, maxExponent uint32) []float64 {
|
||||
var bounds []float64
|
||||
for i := minExponent; i <= maxExponent; i++ {
|
||||
bounds = append(bounds, float64(int(1)<<i))
|
||||
}
|
||||
return bounds
|
||||
}
|
||||
|
||||
// HistogramData stores the information needed to represent the sizes of the keys and values
|
||||
// as a histogram.
|
||||
type HistogramData struct {
|
||||
Bounds []float64
|
||||
Count int64
|
||||
CountPerBucket []int64
|
||||
Min int64
|
||||
Max int64
|
||||
Sum int64
|
||||
}
|
||||
|
||||
// NewHistogramData returns a new instance of HistogramData with properly initialized fields.
|
||||
func NewHistogramData(bounds []float64) *HistogramData {
|
||||
return &HistogramData{
|
||||
Bounds: bounds,
|
||||
CountPerBucket: make([]int64, len(bounds)+1),
|
||||
Max: 0,
|
||||
Min: math.MaxInt64,
|
||||
}
|
||||
}
|
||||
|
||||
func (histogram *HistogramData) Copy() *HistogramData {
|
||||
if histogram == nil {
|
||||
return nil
|
||||
}
|
||||
return &HistogramData{
|
||||
Bounds: append([]float64{}, histogram.Bounds...),
|
||||
CountPerBucket: append([]int64{}, histogram.CountPerBucket...),
|
||||
Count: histogram.Count,
|
||||
Min: histogram.Min,
|
||||
Max: histogram.Max,
|
||||
Sum: histogram.Sum,
|
||||
}
|
||||
}
|
||||
|
||||
// Update changes the Min and Max fields if value is less than or greater than the current values.
|
||||
func (histogram *HistogramData) Update(value int64) {
|
||||
if histogram == nil {
|
||||
return
|
||||
}
|
||||
if value > histogram.Max {
|
||||
histogram.Max = value
|
||||
}
|
||||
if value < histogram.Min {
|
||||
histogram.Min = value
|
||||
}
|
||||
|
||||
histogram.Sum += value
|
||||
histogram.Count++
|
||||
|
||||
for index := 0; index <= len(histogram.Bounds); index++ {
|
||||
// Allocate value in the last buckets if we reached the end of the Bounds array.
|
||||
if index == len(histogram.Bounds) {
|
||||
histogram.CountPerBucket[index]++
|
||||
break
|
||||
}
|
||||
|
||||
if value < int64(histogram.Bounds[index]) {
|
||||
histogram.CountPerBucket[index]++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mean returns the mean value for the histogram.
|
||||
func (histogram *HistogramData) Mean() float64 {
|
||||
if histogram.Count == 0 {
|
||||
return 0
|
||||
}
|
||||
return float64(histogram.Sum) / float64(histogram.Count)
|
||||
}
|
||||
|
||||
// String converts the histogram data into human-readable string.
|
||||
func (histogram *HistogramData) String() string {
|
||||
if histogram == nil {
|
||||
return ""
|
||||
}
|
||||
var b strings.Builder
|
||||
|
||||
b.WriteString("\n -- Histogram: \n")
|
||||
b.WriteString(fmt.Sprintf("Min value: %d \n", histogram.Min))
|
||||
b.WriteString(fmt.Sprintf("Max value: %d \n", histogram.Max))
|
||||
b.WriteString(fmt.Sprintf("Mean: %.2f \n", histogram.Mean()))
|
||||
b.WriteString(fmt.Sprintf("Count: %d \n", histogram.Count))
|
||||
|
||||
numBounds := len(histogram.Bounds)
|
||||
for index, count := range histogram.CountPerBucket {
|
||||
if count == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// The last bucket represents the bucket that contains the range from
|
||||
// the last bound up to infinity so it's processed differently than the
|
||||
// other buckets.
|
||||
if index == len(histogram.CountPerBucket)-1 {
|
||||
lowerBound := uint64(histogram.Bounds[numBounds-1])
|
||||
page := float64(count*100) / float64(histogram.Count)
|
||||
b.WriteString(fmt.Sprintf("[%s, %s) %d %.2f%% \n",
|
||||
humanize.IBytes(lowerBound), "infinity", count, page))
|
||||
continue
|
||||
}
|
||||
|
||||
upperBound := uint64(histogram.Bounds[index])
|
||||
lowerBound := uint64(0)
|
||||
if index > 0 {
|
||||
lowerBound = uint64(histogram.Bounds[index-1])
|
||||
}
|
||||
|
||||
page := float64(count*100) / float64(histogram.Count)
|
||||
b.WriteString(fmt.Sprintf("[%s, %s) %d %.2f%% \n",
|
||||
humanize.IBytes(lowerBound), humanize.IBytes(upperBound), count, page))
|
||||
}
|
||||
b.WriteString(" --\n")
|
||||
return b.String()
|
||||
}
|
44
vendor/github.com/dgraph-io/ristretto/z/mmap.go
generated
vendored
Normal file
44
vendor/github.com/dgraph-io/ristretto/z/mmap.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2019 Dgraph Labs, Inc. and Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package z
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// Mmap uses the mmap system call to memory-map a file. If writable is true,
|
||||
// memory protection of the pages is set so that they may be written to as well.
|
||||
func Mmap(fd *os.File, writable bool, size int64) ([]byte, error) {
|
||||
return mmap(fd, writable, size)
|
||||
}
|
||||
|
||||
// Munmap unmaps a previously mapped slice.
|
||||
func Munmap(b []byte) error {
|
||||
return munmap(b)
|
||||
}
|
||||
|
||||
// Madvise uses the madvise system call to give advise about the use of memory
|
||||
// when using a slice that is memory-mapped to a file. Set the readahead flag to
|
||||
// false if page references are expected in random order.
|
||||
func Madvise(b []byte, readahead bool) error {
|
||||
return madvise(b, readahead)
|
||||
}
|
||||
|
||||
// Msync would call sync on the mmapped data.
|
||||
func Msync(b []byte) error {
|
||||
return msync(b)
|
||||
}
|
59
vendor/github.com/dgraph-io/ristretto/z/mmap_darwin.go
generated
vendored
Normal file
59
vendor/github.com/dgraph-io/ristretto/z/mmap_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2019 Dgraph Labs, Inc. and Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package z
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Mmap uses the mmap system call to memory-map a file. If writable is true,
|
||||
// memory protection of the pages is set so that they may be written to as well.
|
||||
func mmap(fd *os.File, writable bool, size int64) ([]byte, error) {
|
||||
mtype := unix.PROT_READ
|
||||
if writable {
|
||||
mtype |= unix.PROT_WRITE
|
||||
}
|
||||
return unix.Mmap(int(fd.Fd()), 0, int(size), mtype, unix.MAP_SHARED)
|
||||
}
|
||||
|
||||
// Munmap unmaps a previously mapped slice.
|
||||
func munmap(b []byte) error {
|
||||
return unix.Munmap(b)
|
||||
}
|
||||
|
||||
// This is required because the unix package does not support the madvise system call on OS X.
|
||||
func madvise(b []byte, readahead bool) error {
|
||||
advice := unix.MADV_NORMAL
|
||||
if !readahead {
|
||||
advice = unix.MADV_RANDOM
|
||||
}
|
||||
|
||||
_, _, e1 := syscall.Syscall(syscall.SYS_MADVISE, uintptr(unsafe.Pointer(&b[0])),
|
||||
uintptr(len(b)), uintptr(advice))
|
||||
if e1 != 0 {
|
||||
return e1
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func msync(b []byte) error {
|
||||
return unix.Msync(b, unix.MS_SYNC)
|
||||
}
|
101
vendor/github.com/dgraph-io/ristretto/z/mmap_linux.go
generated
vendored
Normal file
101
vendor/github.com/dgraph-io/ristretto/z/mmap_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright 2020 Dgraph Labs, Inc. and Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package z
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// mmap uses the mmap system call to memory-map a file. If writable is true,
|
||||
// memory protection of the pages is set so that they may be written to as well.
|
||||
func mmap(fd *os.File, writable bool, size int64) ([]byte, error) {
|
||||
mtype := unix.PROT_READ
|
||||
if writable {
|
||||
mtype |= unix.PROT_WRITE
|
||||
}
|
||||
return unix.Mmap(int(fd.Fd()), 0, int(size), mtype, unix.MAP_SHARED)
|
||||
}
|
||||
|
||||
// mremap is a Linux-specific system call to remap pages in memory. This can be used in place of munmap + mmap.
|
||||
func mremap(data []byte, size int) ([]byte, error) {
|
||||
// taken from <https://github.com/torvalds/linux/blob/f8394f232b1eab649ce2df5c5f15b0e528c92091/include/uapi/linux/mman.h#L8>
|
||||
const MREMAP_MAYMOVE = 0x1
|
||||
|
||||
header := (*reflect.SliceHeader)(unsafe.Pointer(&data))
|
||||
mmapAddr, mmapSize, errno := unix.Syscall6(
|
||||
unix.SYS_MREMAP,
|
||||
header.Data,
|
||||
uintptr(header.Len),
|
||||
uintptr(size),
|
||||
uintptr(MREMAP_MAYMOVE),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if errno != 0 {
|
||||
return nil, errno
|
||||
}
|
||||
if mmapSize != uintptr(size) {
|
||||
return nil, fmt.Errorf("mremap size mismatch: requested: %d got: %d", size, mmapSize)
|
||||
}
|
||||
|
||||
header.Data = mmapAddr
|
||||
header.Cap = size
|
||||
header.Len = size
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// munmap unmaps a previously mapped slice.
|
||||
//
|
||||
// unix.Munmap maintains an internal list of mmapped addresses, and only calls munmap
|
||||
// if the address is present in that list. If we use mremap, this list is not updated.
|
||||
// To bypass this, we call munmap ourselves.
|
||||
func munmap(data []byte) error {
|
||||
if len(data) == 0 || len(data) != cap(data) {
|
||||
return unix.EINVAL
|
||||
}
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_MUNMAP,
|
||||
uintptr(unsafe.Pointer(&data[0])),
|
||||
uintptr(len(data)),
|
||||
0,
|
||||
)
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// madvise uses the madvise system call to give advise about the use of memory
|
||||
// when using a slice that is memory-mapped to a file. Set the readahead flag to
|
||||
// false if page references are expected in random order.
|
||||
func madvise(b []byte, readahead bool) error {
|
||||
flags := unix.MADV_NORMAL
|
||||
if !readahead {
|
||||
flags = unix.MADV_RANDOM
|
||||
}
|
||||
return unix.Madvise(b, flags)
|
||||
}
|
||||
|
||||
// msync writes any modified data to persistent storage.
|
||||
func msync(b []byte) error {
|
||||
return unix.Msync(b, unix.MS_SYNC)
|
||||
}
|
44
vendor/github.com/dgraph-io/ristretto/z/mmap_plan9.go
generated
vendored
Normal file
44
vendor/github.com/dgraph-io/ristretto/z/mmap_plan9.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2020 Dgraph Labs, Inc. and Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package z
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Mmap uses the mmap system call to memory-map a file. If writable is true,
|
||||
// memory protection of the pages is set so that they may be written to as well.
|
||||
func mmap(fd *os.File, writable bool, size int64) ([]byte, error) {
|
||||
return nil, syscall.EPLAN9
|
||||
}
|
||||
|
||||
// Munmap unmaps a previously mapped slice.
|
||||
func munmap(b []byte) error {
|
||||
return syscall.EPLAN9
|
||||
}
|
||||
|
||||
// Madvise uses the madvise system call to give advise about the use of memory
|
||||
// when using a slice that is memory-mapped to a file. Set the readahead flag to
|
||||
// false if page references are expected in random order.
|
||||
func madvise(b []byte, readahead bool) error {
|
||||
return syscall.EPLAN9
|
||||
}
|
||||
|
||||
func msync(b []byte) error {
|
||||
return syscall.EPLAN9
|
||||
}
|
55
vendor/github.com/dgraph-io/ristretto/z/mmap_unix.go
generated
vendored
Normal file
55
vendor/github.com/dgraph-io/ristretto/z/mmap_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
// +build !windows,!darwin,!plan9,!linux
|
||||
|
||||
/*
|
||||
* Copyright 2019 Dgraph Labs, Inc. and Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package z
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Mmap uses the mmap system call to memory-map a file. If writable is true,
|
||||
// memory protection of the pages is set so that they may be written to as well.
|
||||
func mmap(fd *os.File, writable bool, size int64) ([]byte, error) {
|
||||
mtype := unix.PROT_READ
|
||||
if writable {
|
||||
mtype |= unix.PROT_WRITE
|
||||
}
|
||||
return unix.Mmap(int(fd.Fd()), 0, int(size), mtype, unix.MAP_SHARED)
|
||||
}
|
||||
|
||||
// Munmap unmaps a previously mapped slice.
|
||||
func munmap(b []byte) error {
|
||||
return unix.Munmap(b)
|
||||
}
|
||||
|
||||
// Madvise uses the madvise system call to give advise about the use of memory
|
||||
// when using a slice that is memory-mapped to a file. Set the readahead flag to
|
||||
// false if page references are expected in random order.
|
||||
func madvise(b []byte, readahead bool) error {
|
||||
flags := unix.MADV_NORMAL
|
||||
if !readahead {
|
||||
flags = unix.MADV_RANDOM
|
||||
}
|
||||
return unix.Madvise(b, flags)
|
||||
}
|
||||
|
||||
func msync(b []byte) error {
|
||||
return unix.Msync(b, unix.MS_SYNC)
|
||||
}
|
96
vendor/github.com/dgraph-io/ristretto/z/mmap_windows.go
generated
vendored
Normal file
96
vendor/github.com/dgraph-io/ristretto/z/mmap_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,96 @@
|
|||
// +build windows
|
||||
|
||||
/*
|
||||
* Copyright 2019 Dgraph Labs, Inc. and Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package z
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func mmap(fd *os.File, write bool, size int64) ([]byte, error) {
|
||||
protect := syscall.PAGE_READONLY
|
||||
access := syscall.FILE_MAP_READ
|
||||
|
||||
if write {
|
||||
protect = syscall.PAGE_READWRITE
|
||||
access = syscall.FILE_MAP_WRITE
|
||||
}
|
||||
fi, err := fd.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// In windows, we cannot mmap a file more than it's actual size.
|
||||
// So truncate the file to the size of the mmap.
|
||||
if fi.Size() < size {
|
||||
if err := fd.Truncate(size); err != nil {
|
||||
return nil, fmt.Errorf("truncate: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Open a file mapping handle.
|
||||
sizelo := uint32(size >> 32)
|
||||
sizehi := uint32(size) & 0xffffffff
|
||||
|
||||
handler, err := syscall.CreateFileMapping(syscall.Handle(fd.Fd()), nil,
|
||||
uint32(protect), sizelo, sizehi, nil)
|
||||
if err != nil {
|
||||
return nil, os.NewSyscallError("CreateFileMapping", err)
|
||||
}
|
||||
|
||||
// Create the memory map.
|
||||
addr, err := syscall.MapViewOfFile(handler, uint32(access), 0, 0, uintptr(size))
|
||||
if addr == 0 {
|
||||
return nil, os.NewSyscallError("MapViewOfFile", err)
|
||||
}
|
||||
|
||||
// Close mapping handle.
|
||||
if err := syscall.CloseHandle(syscall.Handle(handler)); err != nil {
|
||||
return nil, os.NewSyscallError("CloseHandle", err)
|
||||
}
|
||||
|
||||
// Slice memory layout
|
||||
// Copied this snippet from golang/sys package
|
||||
var sl = struct {
|
||||
addr uintptr
|
||||
len int
|
||||
cap int
|
||||
}{addr, int(size), int(size)}
|
||||
|
||||
// Use unsafe to turn sl into a []byte.
|
||||
data := *(*[]byte)(unsafe.Pointer(&sl))
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func munmap(b []byte) error {
|
||||
return syscall.UnmapViewOfFile(uintptr(unsafe.Pointer(&b[0])))
|
||||
}
|
||||
|
||||
func madvise(b []byte, readahead bool) error {
|
||||
// Do Nothing. We don’t care about this setting on Windows
|
||||
return nil
|
||||
}
|
||||
|
||||
func msync(b []byte) error {
|
||||
// TODO: Figure out how to do msync on Windows.
|
||||
return nil
|
||||
}
|
75
vendor/github.com/dgraph-io/ristretto/z/rtutil.go
generated
vendored
Normal file
75
vendor/github.com/dgraph-io/ristretto/z/rtutil.go
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2019 Ewan Chou
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package z
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// NanoTime returns the current time in nanoseconds from a monotonic clock.
|
||||
//go:linkname NanoTime runtime.nanotime
|
||||
func NanoTime() int64
|
||||
|
||||
// CPUTicks is a faster alternative to NanoTime to measure time duration.
|
||||
//go:linkname CPUTicks runtime.cputicks
|
||||
func CPUTicks() int64
|
||||
|
||||
type stringStruct struct {
|
||||
str unsafe.Pointer
|
||||
len int
|
||||
}
|
||||
|
||||
//go:noescape
|
||||
//go:linkname memhash runtime.memhash
|
||||
func memhash(p unsafe.Pointer, h, s uintptr) uintptr
|
||||
|
||||
// MemHash is the hash function used by go map, it utilizes available hardware instructions(behaves
|
||||
// as aeshash if aes instruction is available).
|
||||
// NOTE: The hash seed changes for every process. So, this cannot be used as a persistent hash.
|
||||
func MemHash(data []byte) uint64 {
|
||||
ss := (*stringStruct)(unsafe.Pointer(&data))
|
||||
return uint64(memhash(ss.str, 0, uintptr(ss.len)))
|
||||
}
|
||||
|
||||
// MemHashString is the hash function used by go map, it utilizes available hardware instructions
|
||||
// (behaves as aeshash if aes instruction is available).
|
||||
// NOTE: The hash seed changes for every process. So, this cannot be used as a persistent hash.
|
||||
func MemHashString(str string) uint64 {
|
||||
ss := (*stringStruct)(unsafe.Pointer(&str))
|
||||
return uint64(memhash(ss.str, 0, uintptr(ss.len)))
|
||||
}
|
||||
|
||||
// FastRand is a fast thread local random function.
|
||||
//go:linkname FastRand runtime.fastrand
|
||||
func FastRand() uint32
|
||||
|
||||
//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers
|
||||
func memclrNoHeapPointers(p unsafe.Pointer, n uintptr)
|
||||
|
||||
func Memclr(b []byte) {
|
||||
if len(b) == 0 {
|
||||
return
|
||||
}
|
||||
p := unsafe.Pointer(&b[0])
|
||||
memclrNoHeapPointers(p, uintptr(len(b)))
|
||||
}
|
0
vendor/github.com/dgraph-io/ristretto/z/rtutil.s
generated
vendored
Normal file
0
vendor/github.com/dgraph-io/ristretto/z/rtutil.s
generated
vendored
Normal file
127
vendor/github.com/dgraph-io/ristretto/z/simd/baseline.go
generated
vendored
Normal file
127
vendor/github.com/dgraph-io/ristretto/z/simd/baseline.go
generated
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
package simd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Search finds the key using the naive way
|
||||
func Naive(xs []uint64, k uint64) int16 {
|
||||
var i int
|
||||
for i = 0; i < len(xs); i += 2 {
|
||||
x := xs[i]
|
||||
if x >= k {
|
||||
return int16(i / 2)
|
||||
}
|
||||
}
|
||||
return int16(i / 2)
|
||||
}
|
||||
|
||||
func Clever(xs []uint64, k uint64) int16 {
|
||||
if len(xs) < 8 {
|
||||
return Naive(xs, k)
|
||||
}
|
||||
var twos, pk [4]uint64
|
||||
pk[0] = k
|
||||
pk[1] = k
|
||||
pk[2] = k
|
||||
pk[3] = k
|
||||
for i := 0; i < len(xs); i += 8 {
|
||||
twos[0] = xs[i]
|
||||
twos[1] = xs[i+2]
|
||||
twos[2] = xs[i+4]
|
||||
twos[3] = xs[i+6]
|
||||
if twos[0] >= pk[0] {
|
||||
return int16(i / 2)
|
||||
}
|
||||
if twos[1] >= pk[1] {
|
||||
return int16((i + 2) / 2)
|
||||
}
|
||||
if twos[2] >= pk[2] {
|
||||
return int16((i + 4) / 2)
|
||||
}
|
||||
if twos[3] >= pk[3] {
|
||||
return int16((i + 6) / 2)
|
||||
}
|
||||
|
||||
}
|
||||
return int16(len(xs) / 2)
|
||||
}
|
||||
|
||||
func Parallel(xs []uint64, k uint64) int16 {
|
||||
cpus := runtime.NumCPU()
|
||||
if cpus%2 != 0 {
|
||||
panic(fmt.Sprintf("odd number of CPUs %v", cpus))
|
||||
}
|
||||
sz := len(xs)/cpus + 1
|
||||
var wg sync.WaitGroup
|
||||
retChan := make(chan int16, cpus)
|
||||
for i := 0; i < len(xs); i += sz {
|
||||
end := i + sz
|
||||
if end >= len(xs) {
|
||||
end = len(xs)
|
||||
}
|
||||
chunk := xs[i:end]
|
||||
wg.Add(1)
|
||||
go func(hd int16, xs []uint64, k uint64, wg *sync.WaitGroup, ch chan int16) {
|
||||
for i := 0; i < len(xs); i += 2 {
|
||||
if xs[i] >= k {
|
||||
ch <- (int16(i) + hd) / 2
|
||||
break
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}(int16(i), chunk, k, &wg, retChan)
|
||||
}
|
||||
wg.Wait()
|
||||
close(retChan)
|
||||
var min int16 = (1 << 15) - 1
|
||||
for i := range retChan {
|
||||
if i < min {
|
||||
min = i
|
||||
}
|
||||
}
|
||||
if min == (1<<15)-1 {
|
||||
return int16(len(xs) / 2)
|
||||
}
|
||||
return min
|
||||
}
|
||||
|
||||
func Binary(keys []uint64, key uint64) int16 {
|
||||
return int16(sort.Search(len(keys), func(i int) bool {
|
||||
if i*2 >= len(keys) {
|
||||
return true
|
||||
}
|
||||
return keys[i*2] >= key
|
||||
}))
|
||||
}
|
||||
|
||||
func cmp2_native(twos, pk [2]uint64) int16 {
|
||||
if twos[0] == pk[0] {
|
||||
return 0
|
||||
}
|
||||
if twos[1] == pk[1] {
|
||||
return 1
|
||||
}
|
||||
return 2
|
||||
}
|
||||
|
||||
func cmp4_native(fours, pk [4]uint64) int16 {
|
||||
for i := range fours {
|
||||
if fours[i] >= pk[i] {
|
||||
return int16(i)
|
||||
}
|
||||
}
|
||||
return 4
|
||||
}
|
||||
|
||||
func cmp8_native(a [8]uint64, pk [4]uint64) int16 {
|
||||
for i := range a {
|
||||
if a[i] >= pk[0] {
|
||||
return int16(i)
|
||||
}
|
||||
}
|
||||
return 8
|
||||
}
|
51
vendor/github.com/dgraph-io/ristretto/z/simd/search.go
generated
vendored
Normal file
51
vendor/github.com/dgraph-io/ristretto/z/simd/search.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
// +build 386 arm armbe arm64 mips mipsle mips64p32 mips64p32le ppc ppc64 ppc64le sparc
|
||||
|
||||
/*
|
||||
* Copyright 2020 Dgraph Labs, Inc. and Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package simd
|
||||
|
||||
// Search uses the Clever search to find the correct key.
|
||||
func Search(xs []uint64, k uint64) int16 {
|
||||
if len(xs) < 8 {
|
||||
return Naive(xs, k)
|
||||
}
|
||||
var twos, pk [4]uint64
|
||||
pk[0] = k
|
||||
pk[1] = k
|
||||
pk[2] = k
|
||||
pk[3] = k
|
||||
for i := 0; i < len(xs); i += 8 {
|
||||
twos[0] = xs[i]
|
||||
twos[1] = xs[i+2]
|
||||
twos[2] = xs[i+4]
|
||||
twos[3] = xs[i+6]
|
||||
if twos[0] >= pk[0] {
|
||||
return int16(i / 2)
|
||||
}
|
||||
if twos[1] >= pk[1] {
|
||||
return int16((i + 2) / 2)
|
||||
}
|
||||
if twos[2] >= pk[2] {
|
||||
return int16((i + 4) / 2)
|
||||
}
|
||||
if twos[3] >= pk[3] {
|
||||
return int16((i + 6) / 2)
|
||||
}
|
||||
|
||||
}
|
||||
return int16(len(xs) / 2)
|
||||
}
|
60
vendor/github.com/dgraph-io/ristretto/z/simd/search_amd64.s
generated
vendored
Normal file
60
vendor/github.com/dgraph-io/ristretto/z/simd/search_amd64.s
generated
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
// Code generated by command: go run asm2.go -out search_amd64.s -stubs stub_search_amd64.go. DO NOT EDIT.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func Search(xs []uint64, k uint64) int16
|
||||
TEXT ·Search(SB), NOSPLIT, $0-34
|
||||
MOVQ xs_base+0(FP), AX
|
||||
MOVQ xs_len+8(FP), CX
|
||||
MOVQ k+24(FP), DX
|
||||
|
||||
// Save n
|
||||
MOVQ CX, BX
|
||||
|
||||
// Initialize idx register to zero.
|
||||
XORL BP, BP
|
||||
|
||||
loop:
|
||||
// Unroll1
|
||||
CMPQ (AX)(BP*8), DX
|
||||
JAE Found
|
||||
|
||||
// Unroll2
|
||||
CMPQ 16(AX)(BP*8), DX
|
||||
JAE Found2
|
||||
|
||||
// Unroll3
|
||||
CMPQ 32(AX)(BP*8), DX
|
||||
JAE Found3
|
||||
|
||||
// Unroll4
|
||||
CMPQ 48(AX)(BP*8), DX
|
||||
JAE Found4
|
||||
|
||||
// plus8
|
||||
ADDQ $0x08, BP
|
||||
CMPQ BP, CX
|
||||
JB loop
|
||||
JMP NotFound
|
||||
|
||||
Found2:
|
||||
ADDL $0x02, BP
|
||||
JMP Found
|
||||
|
||||
Found3:
|
||||
ADDL $0x04, BP
|
||||
JMP Found
|
||||
|
||||
Found4:
|
||||
ADDL $0x06, BP
|
||||
|
||||
Found:
|
||||
MOVL BP, BX
|
||||
|
||||
NotFound:
|
||||
MOVL BX, BP
|
||||
SHRL $0x1f, BP
|
||||
ADDL BX, BP
|
||||
SHRL $0x01, BP
|
||||
MOVL BP, ret+32(FP)
|
||||
RET
|
6
vendor/github.com/dgraph-io/ristretto/z/simd/stub_search_amd64.go
generated
vendored
Normal file
6
vendor/github.com/dgraph-io/ristretto/z/simd/stub_search_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
// Code generated by command: go run asm2.go -out search_amd64.s -stubs stub_search_amd64.go. DO NOT EDIT.
|
||||
|
||||
package simd
|
||||
|
||||
// Search finds the first idx for which xs[idx] >= k in xs.
|
||||
func Search(xs []uint64, k uint64) int16
|
151
vendor/github.com/dgraph-io/ristretto/z/z.go
generated
vendored
Normal file
151
vendor/github.com/dgraph-io/ristretto/z/z.go
generated
vendored
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright 2019 Dgraph Labs, Inc. and Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package z
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/cespare/xxhash"
|
||||
)
|
||||
|
||||
// TODO: Figure out a way to re-use memhash for the second uint64 hash, we
|
||||
// already know that appending bytes isn't reliable for generating a
|
||||
// second hash (see Ristretto PR #88).
|
||||
//
|
||||
// We also know that while the Go runtime has a runtime memhash128
|
||||
// function, it's not possible to use it to generate [2]uint64 or
|
||||
// anything resembling a 128bit hash, even though that's exactly what
|
||||
// we need in this situation.
|
||||
func KeyToHash(key interface{}) (uint64, uint64) {
|
||||
if key == nil {
|
||||
return 0, 0
|
||||
}
|
||||
switch k := key.(type) {
|
||||
case uint64:
|
||||
return k, 0
|
||||
case string:
|
||||
return MemHashString(k), xxhash.Sum64String(k)
|
||||
case []byte:
|
||||
return MemHash(k), xxhash.Sum64(k)
|
||||
case byte:
|
||||
return uint64(k), 0
|
||||
case int:
|
||||
return uint64(k), 0
|
||||
case int32:
|
||||
return uint64(k), 0
|
||||
case uint32:
|
||||
return uint64(k), 0
|
||||
case int64:
|
||||
return uint64(k), 0
|
||||
default:
|
||||
panic("Key type not supported")
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
dummyCloserChan <-chan struct{}
|
||||
tmpDir string
|
||||
)
|
||||
|
||||
// Closer holds the two things we need to close a goroutine and wait for it to
|
||||
// finish: a chan to tell the goroutine to shut down, and a WaitGroup with
|
||||
// which to wait for it to finish shutting down.
|
||||
type Closer struct {
|
||||
waiting sync.WaitGroup
|
||||
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
// SetTmpDir sets the temporary directory for the temporary buffers.
|
||||
func SetTmpDir(dir string) {
|
||||
tmpDir = dir
|
||||
}
|
||||
|
||||
// NewCloser constructs a new Closer, with an initial count on the WaitGroup.
|
||||
func NewCloser(initial int) *Closer {
|
||||
ret := &Closer{}
|
||||
ret.ctx, ret.cancel = context.WithCancel(context.Background())
|
||||
ret.waiting.Add(initial)
|
||||
return ret
|
||||
}
|
||||
|
||||
// AddRunning Add()'s delta to the WaitGroup.
|
||||
func (lc *Closer) AddRunning(delta int) {
|
||||
lc.waiting.Add(delta)
|
||||
}
|
||||
|
||||
// Ctx can be used to get a context, which would automatically get cancelled when Signal is called.
|
||||
func (lc *Closer) Ctx() context.Context {
|
||||
if lc == nil {
|
||||
return context.Background()
|
||||
}
|
||||
return lc.ctx
|
||||
}
|
||||
|
||||
// Signal signals the HasBeenClosed signal.
|
||||
func (lc *Closer) Signal() {
|
||||
// Todo(ibrahim): Change Signal to return error on next badger breaking change.
|
||||
lc.cancel()
|
||||
}
|
||||
|
||||
// HasBeenClosed gets signaled when Signal() is called.
|
||||
func (lc *Closer) HasBeenClosed() <-chan struct{} {
|
||||
if lc == nil {
|
||||
return dummyCloserChan
|
||||
}
|
||||
return lc.ctx.Done()
|
||||
}
|
||||
|
||||
// Done calls Done() on the WaitGroup.
|
||||
func (lc *Closer) Done() {
|
||||
if lc == nil {
|
||||
return
|
||||
}
|
||||
lc.waiting.Done()
|
||||
}
|
||||
|
||||
// Wait waits on the WaitGroup. (It waits for NewCloser's initial value, AddRunning, and Done
|
||||
// calls to balance out.)
|
||||
func (lc *Closer) Wait() {
|
||||
lc.waiting.Wait()
|
||||
}
|
||||
|
||||
// SignalAndWait calls Signal(), then Wait().
|
||||
func (lc *Closer) SignalAndWait() {
|
||||
lc.Signal()
|
||||
lc.Wait()
|
||||
}
|
||||
|
||||
// ZeroOut zeroes out all the bytes in the range [start, end).
|
||||
func ZeroOut(dst []byte, start, end int) {
|
||||
if start < 0 || start >= len(dst) {
|
||||
return // BAD
|
||||
}
|
||||
if end >= len(dst) {
|
||||
end = len(dst)
|
||||
}
|
||||
if end-start <= 0 {
|
||||
return
|
||||
}
|
||||
Memclr(dst[start:end])
|
||||
// b := dst[start:end]
|
||||
// for i := range b {
|
||||
// b[i] = 0x0
|
||||
// }
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue