152 lines
3.7 KiB
Go
152 lines
3.7 KiB
Go
|
/*
|
||
|
* 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
|
||
|
// }
|
||
|
}
|