mirror of https://github.com/tidwall/tile38.git
186 lines
4.2 KiB
Go
186 lines
4.2 KiB
Go
|
// Copyright 2019 Joshua J Baker. All rights reserved.
|
||
|
// Use of this source code is governed by an ISC-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
package rhh
|
||
|
|
||
|
import (
|
||
|
"reflect"
|
||
|
"unsafe"
|
||
|
|
||
|
"github.com/cespare/xxhash"
|
||
|
)
|
||
|
|
||
|
type entryU64 struct {
|
||
|
hdib uint64 // bitfield { hash:48 dib:16 }
|
||
|
key uint64 // user key
|
||
|
value interface{} // user value
|
||
|
}
|
||
|
|
||
|
func (e *entryU64) dib() int {
|
||
|
return int(e.hdib & maxDIB)
|
||
|
}
|
||
|
func (e *entryU64) hash() int {
|
||
|
return int(e.hdib >> dibBitSize)
|
||
|
}
|
||
|
func (e *entryU64) setDIB(dib int) {
|
||
|
e.hdib = e.hdib>>dibBitSize<<dibBitSize | uint64(dib)&maxDIB
|
||
|
}
|
||
|
func (e *entryU64) setHash(hash int) {
|
||
|
e.hdib = uint64(hash)<<dibBitSize | e.hdib&maxDIB
|
||
|
}
|
||
|
|
||
|
// hash returns a 48-bit hash for 64-bit environments, or 32-bit hash for
|
||
|
// 32-bit environments.
|
||
|
func (m *MapU64) hash(key uint64) int {
|
||
|
return int(xxhash.Sum64(*(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||
|
Data: uintptr(unsafe.Pointer(&key)), Len: 8, Cap: 8,
|
||
|
}))) >> dibBitSize)
|
||
|
}
|
||
|
|
||
|
// MapU64 is a map. Like map[uint64]interface{}
|
||
|
type MapU64 struct {
|
||
|
cap int
|
||
|
length int
|
||
|
mask int
|
||
|
growAt int
|
||
|
shrinkAt int
|
||
|
buckets []entryU64
|
||
|
}
|
||
|
|
||
|
// NewU64 returns a new map. Like map[uint64]interface{}
|
||
|
func NewU64(cap int) *MapU64 {
|
||
|
m := new(MapU64)
|
||
|
m.cap = cap
|
||
|
sz := 8
|
||
|
for sz < m.cap {
|
||
|
sz *= 2
|
||
|
}
|
||
|
m.buckets = make([]entryU64, sz)
|
||
|
m.mask = len(m.buckets) - 1
|
||
|
m.growAt = int(float64(len(m.buckets)) * loadFactor)
|
||
|
m.shrinkAt = int(float64(len(m.buckets)) * (1 - loadFactor))
|
||
|
return m
|
||
|
}
|
||
|
|
||
|
func (m *MapU64) resize(newCap int) {
|
||
|
nmap := NewU64(newCap)
|
||
|
for i := 0; i < len(m.buckets); i++ {
|
||
|
if m.buckets[i].dib() > 0 {
|
||
|
nmap.set(m.buckets[i].hash(), m.buckets[i].key, m.buckets[i].value)
|
||
|
}
|
||
|
}
|
||
|
cap := m.cap
|
||
|
*m = *nmap
|
||
|
m.cap = cap
|
||
|
}
|
||
|
|
||
|
// Set assigns a value to a key.
|
||
|
// Returns the previous value, or false when no value was assigned.
|
||
|
func (m *MapU64) Set(key uint64, value interface{}) (interface{}, bool) {
|
||
|
if len(m.buckets) == 0 {
|
||
|
*m = *NewU64(0)
|
||
|
}
|
||
|
if m.length >= m.growAt {
|
||
|
m.resize(len(m.buckets) * 2)
|
||
|
}
|
||
|
return m.set(m.hash(key), key, value)
|
||
|
}
|
||
|
|
||
|
func (m *MapU64) set(hash int, key uint64, value interface{}) (interface{}, bool) {
|
||
|
e := entryU64{makeHDIB(hash, 1), key, value}
|
||
|
i := e.hash() & m.mask
|
||
|
for {
|
||
|
if m.buckets[i].dib() == 0 {
|
||
|
m.buckets[i] = e
|
||
|
m.length++
|
||
|
return nil, false
|
||
|
}
|
||
|
if e.hash() == m.buckets[i].hash() && e.key == m.buckets[i].key {
|
||
|
old := m.buckets[i].value
|
||
|
m.buckets[i].value = e.value
|
||
|
return old, true
|
||
|
}
|
||
|
if m.buckets[i].dib() < e.dib() {
|
||
|
e, m.buckets[i] = m.buckets[i], e
|
||
|
}
|
||
|
i = (i + 1) & m.mask
|
||
|
e.setDIB(e.dib() + 1)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Get returns a value for a key.
|
||
|
// Returns false when no value has been assign for key.
|
||
|
func (m *MapU64) Get(key uint64) (interface{}, bool) {
|
||
|
if len(m.buckets) == 0 {
|
||
|
return nil, false
|
||
|
}
|
||
|
hash := m.hash(key)
|
||
|
i := hash & m.mask
|
||
|
for {
|
||
|
if m.buckets[i].dib() == 0 {
|
||
|
return nil, false
|
||
|
}
|
||
|
if m.buckets[i].hash() == hash && m.buckets[i].key == key {
|
||
|
return m.buckets[i].value, true
|
||
|
}
|
||
|
i = (i + 1) & m.mask
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Len returns the number of values in map.
|
||
|
func (m *MapU64) Len() int {
|
||
|
return m.length
|
||
|
}
|
||
|
|
||
|
// Delete deletes a value for a key.
|
||
|
// Returns the deleted value, or false when no value was assigned.
|
||
|
func (m *MapU64) Delete(key uint64) (interface{}, bool) {
|
||
|
if len(m.buckets) == 0 {
|
||
|
return nil, false
|
||
|
}
|
||
|
hash := m.hash(key)
|
||
|
i := hash & m.mask
|
||
|
for {
|
||
|
if m.buckets[i].dib() == 0 {
|
||
|
return nil, false
|
||
|
}
|
||
|
if m.buckets[i].hash() == hash && m.buckets[i].key == key {
|
||
|
old := m.buckets[i].value
|
||
|
m.remove(i)
|
||
|
return old, true
|
||
|
}
|
||
|
i = (i + 1) & m.mask
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (m *MapU64) remove(i int) {
|
||
|
m.buckets[i].setDIB(0)
|
||
|
for {
|
||
|
pi := i
|
||
|
i = (i + 1) & m.mask
|
||
|
if m.buckets[i].dib() <= 1 {
|
||
|
m.buckets[pi] = entryU64{}
|
||
|
break
|
||
|
}
|
||
|
m.buckets[pi] = m.buckets[i]
|
||
|
m.buckets[pi].setDIB(m.buckets[pi].dib() - 1)
|
||
|
}
|
||
|
m.length--
|
||
|
if len(m.buckets) > m.cap && m.length <= m.shrinkAt {
|
||
|
m.resize(m.length)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Range iterates overall all key/values.
|
||
|
// It's not safe to call or Set or Delete while ranging.
|
||
|
func (m *MapU64) Range(iter func(key uint64, value interface{}) bool) {
|
||
|
for i := 0; i < len(m.buckets); i++ {
|
||
|
if m.buckets[i].dib() > 0 {
|
||
|
if !iter(m.buckets[i].key, m.buckets[i].value) {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|