-
Notifications
You must be signed in to change notification settings - Fork 1
/
entry.go
110 lines (91 loc) · 2.82 KB
/
entry.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
package directcache
import (
"encoding/binary"
"math"
)
const (
deletedFlag = 1 // the entry was deleted
recentlyUsedFlag = 2 // the entry is recently accessed
)
// Entry presents the entry of a key-value pair.
type Entry interface {
Key() []byte
Value() []byte
RecentlyUsed() bool
}
// entry consists of header and body.
type entry []byte
// flag ops. flags are stored in the first 4 bits of e[0].
func (e entry) HasFlag(flag uint8) bool { return e[0]&(flag<<4) != 0 }
func (e entry) AddFlag(flag uint8) { e[0] |= (flag << 4) }
func (e entry) RemoveFlag(flag uint8) { e[0] &^= (flag << 4) }
// RecentlyUsed complies Entry interface.
func (e entry) RecentlyUsed() bool { return e.HasFlag(recentlyUsedFlag) }
// lw extracts the number of bytes to present key/val length.
// It's stored in the last 2 bits of e[0].
func (e entry) lw() int { return 1 << (e[0] & 3) }
func (e entry) hdrSize() int { return 1 + e.lw()*3 }
func (e entry) keyLen() int { return e.intAt(1, e.lw()) }
func (e entry) valLen() int { return e.intAt(1+e.lw(), e.lw()) }
func (e entry) spare() int { return e.intAt(1+e.lw()*2, e.lw()) }
// Size returns the entry size.
func (e entry) Size() int { return e.hdrSize() + e.keyLen() + e.valLen() + e.spare() }
// BodySize returns the sum of key, val length and spare.
func (e entry) BodySize() int { return e.keyLen() + e.valLen() + e.spare() }
// Key returns the key of the entry.
func (e entry) Key() []byte { return e[e.hdrSize():][:e.keyLen()] }
// Value returns the value of the entry.
func (e entry) Value() []byte { return e[e.hdrSize():][e.keyLen():][:e.valLen()] }
// Init initializes the entry and return the slice for value.
//
// The entry must be pre-alloced.
func (e entry) Init(key []byte, valLen int, spare int) []byte {
keyLen := len(key)
lb := bitw(keyLen + valLen + spare)
// init header
e[0] = lb
lw := 1 << lb
e.setIntAt(1, lw, keyLen)
e.setIntAt(1+lw, lw, valLen)
e.setIntAt(1+lw*2, lw, spare)
// init key and value
hdrSize := 1 + lw*3
copy(e[hdrSize:], key)
return e[hdrSize:][keyLen:][:valLen]
}
func (e entry) intAt(i int, w int) int {
switch w {
case 1:
return int(e[i])
case 2:
return int(binary.BigEndian.Uint16(e[i:]))
default:
return int(binary.BigEndian.Uint32(e[i:]))
}
}
func (e entry) setIntAt(i int, w int, n int) {
switch w {
case 1:
e[i] = byte(n)
case 2:
binary.BigEndian.PutUint16(e[i:], uint16(n))
default:
binary.BigEndian.PutUint32(e[i:], uint32(n))
}
}
// entrySize returns the size of an entry for given kv lengths.
func entrySize(keyLen, valLen, spare int) int {
return 1 + (3 << bitw(keyLen+valLen+spare)) + // hdr
keyLen + valLen + spare //body
}
// bitw where 1<<bitw is how many bytes needed to present n.
func bitw(n int) byte {
switch {
case n <= math.MaxUint8:
return 0
case n <= math.MaxUint16:
return 1
default:
return 2
}
}