-
Notifications
You must be signed in to change notification settings - Fork 1
/
htable.go
150 lines (128 loc) · 3.01 KB
/
htable.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package htable
import (
"encoding/binary"
"fmt"
"io"
)
const (
initialM = 16
fnvOffSetBasis uint64 = 14695981039346656037
fnvPrime = 1099511628211
loadFactorThreshold = 0.5
)
type IntKey int
type StringKey string
type PreHashable interface {
HashBytes() []byte
Equal(PreHashable) bool
}
func (i IntKey) HashBytes() []byte {
buf := make([]byte, binary.MaxVarintLen64)
n := binary.PutVarint(buf, int64(i))
return buf[:n]
}
func (i IntKey) Equal(other PreHashable) bool {
v, ok := other.(IntKey)
return ok && i == v
}
func (str StringKey) HashBytes() []byte {
return []byte(str)
}
func (str StringKey) Equal(other PreHashable) bool {
v, ok := other.(StringKey)
return ok && str == v
}
type Table struct {
length int
buckets [][]entry
}
type entry struct {
key PreHashable
value interface{}
}
// Create a new Hash Table hinting the desired number of buckets
func NewSized(initial int) Table {
return Table{
buckets: make([][]entry, initial),
}
}
// Create a new Hash Table
func New() Table {
return NewSized(initialM)
}
func hashValue(v PreHashable, limit int) int {
hash := fnvOffSetBasis
for _, b := range v.HashBytes() {
hash = hash ^ uint64(b)
hash = hash * fnvPrime
}
return int(hash % uint64(limit))
}
func (ht *Table) expandTable() error {
newTable := make([][]entry, len(ht.buckets)*2)
for _, bucket := range ht.buckets {
for _, e := range bucket {
newHash := hashValue(e.key, len(newTable))
newTable[newHash] = append(newTable[newHash], entry{e.key, e.value})
}
}
ht.buckets = newTable
return nil
}
func (ht *Table) loadFactor() float32 {
return float32(ht.length) / float32(len(ht.buckets))
}
func (ht *Table) Set(key PreHashable, value interface{}) {
hash := hashValue(key, len(ht.buckets))
// check if key is already added, if yes, just overwrite
for i, e := range ht.buckets[hash] {
if e.key == key {
ht.buckets[hash][i].value = value
return
}
}
ht.buckets[hash] = append(ht.buckets[hash], entry{key, value})
ht.length += 1
if ht.loadFactor() > loadFactorThreshold {
ht.expandTable()
}
}
func (ht *Table) Get(key PreHashable) (interface{}, bool) {
hash := hashValue(key, len(ht.buckets))
for _, v := range ht.buckets[hash] {
if v.key == key {
return v.value, true
}
}
return nil, false
}
func (ht *Table) Len() int {
return ht.length
}
func (ht *Table) Delete(key PreHashable) error {
hash := hashValue(key, len(ht.buckets))
for i, v := range ht.buckets[hash] {
if v.key == key {
current := ht.buckets[hash]
current[i] = current[len(current)-1]
current = current[:len(current)-1]
ht.length -= 1
ht.buckets[hash] = current
return nil
}
}
return fmt.Errorf("Key error")
}
func (ht *Table) Dump(w io.Writer) {
fmt.Fprintf(w, "length = %d\n", ht.length)
for i, entries := range ht.buckets {
fmt.Fprintf(w, "bucket %3d: ", i)
for j, entry := range entries {
fmt.Fprintf(w, "%s:%v", entry.key, entry.value)
if j < len(entries)-1 {
fmt.Fprintf(w, ", ")
}
}
fmt.Fprintln(w)
}
}