-
Notifications
You must be signed in to change notification settings - Fork 1
/
pool.go
155 lines (141 loc) · 3.78 KB
/
pool.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
151
152
153
154
155
package store4
type pool struct {
// strToID maps strings to IDs.
strToID map[string]uint64
// idToStrInfo maps IDs to string info.
idToStrInfo map[uint64]*strInfo
// nextStrID holds the next string ID to issue.
nextStrID uint64
// itemToID maps non-string items to IDs.
itemToID map[interface{}]uint64
// idToItemInfo maps IDs to item info.
idToItemInfo map[uint64]*itemInfo
// nextItemID holds the next non-string ID to issue.
nextItemID uint64
}
func newPool() *pool {
s := &pool{}
s.strToID = make(map[string]uint64)
s.idToStrInfo = make(map[uint64]*strInfo)
s.itemToID = make(map[interface{}]uint64)
s.idToItemInfo = make(map[uint64]*itemInfo)
// We use "*" as a wildcard, so we give it ID 0
// to make things easy elsewhere.
s.strToID["*"] = 0
// Start string IDs from 1.
s.nextStrID = 1
// Start non-string IDs from 0 - with the highest bit set.
s.nextItemID = 1 << 63
return s
}
// strInfo holds details for each string.
type strInfo struct {
str string // The string itself.
refCount uint64 // Reference count.
}
// itemInfo holds details for each non-string.
type itemInfo struct {
item interface{} // The item itself.
refCount uint64 // Reference count.
}
// idToString returns the string for a given ID.
// The given ID must exist.
func (s *pool) idToString(id uint64) string {
return s.idToStrInfo[id].str
}
// idToAny returns the item for a given ID.
// The given ID must exist.
func (s *pool) idToAny(id uint64) interface{} {
if id&(1<<63) == 0 {
return s.idToStrInfo[id].str
}
return s.idToItemInfo[id].item
}
// stringToID returns the ID for a given string and true
// if the string exists, and 0 and false if it does not.
func (s *pool) stringToID(str string) (uint64, bool) {
id, ok := s.strToID[str]
return id, ok
}
// anyToID returns the ID for a given item and true
// if the item exists, and 0 and false if it does not.
func (s *pool) anyToID(item interface{}) (uint64, bool) {
if str, sok := item.(string); sok {
return s.stringToID(str)
}
id, ok := s.itemToID[item]
return id, ok
}
// getOrCreateIDString returns an ID for a given string.
// If no existing ID is present, it creates a new one.
// For any existing string, it also increments the reference count.
func (s *pool) getOrCreateIDString(str string) uint64 {
// This will issue bad IDs after 9,223,372,036,854,775,807 unique strings have been seen (64 bit wrap around).
id, ok := s.strToID[str]
if ok {
if id != 0 {
s.idToStrInfo[id].refCount++
}
} else {
id = s.nextStrID
s.nextStrID++
s.strToID[str] = id
s.idToStrInfo[id] = &strInfo{
str: str,
refCount: 1,
}
}
return id
}
// getOrCreateIDAny returns an ID for a given item.
func (s *pool) getOrCreateIDAny(item interface{}) uint64 {
// This will issue bad IDs after 9,223,372,036,854,775,807 unique items have been seen (64 bit wrap around).
if str, sok := item.(string); sok {
return s.getOrCreateIDString(str)
}
id, ok := s.itemToID[item]
if ok {
if id != 0 {
s.idToItemInfo[id].refCount++
}
} else {
id = s.nextItemID
s.nextItemID++
s.itemToID[item] = id
s.idToItemInfo[id] = &itemInfo{
item: item,
refCount: 1,
}
}
return id
}
// releaseRefString decrements a string's reference count.
// When a string is no longer referenced, it is removed
// from all maps.
// The given id must exist or releaseStringRef will aspolde.
func (s *pool) releaseRefString(id uint64) {
info := s.idToStrInfo[id]
c := info.refCount
c--
if c == 0 {
delete(s.strToID, info.str)
delete(s.idToStrInfo, id)
return
}
info.refCount = c
}
func (s *pool) releaseRefAny(id uint64) {
if id&(1<<63) == 0 {
s.releaseRefString(id)
return
}
info := s.idToItemInfo[id]
c := info.refCount
c--
if c == 0 {
delete(s.itemToID, info.item)
delete(s.idToItemInfo, id)
return
}
info.refCount = c
}