forked from cornelk/hashmap
-
Notifications
You must be signed in to change notification settings - Fork 0
/
list.go
180 lines (150 loc) · 4.74 KB
/
list.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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
package hashmap
import (
"sync/atomic"
"unsafe"
)
// List is a sorted doubly linked list.
type List struct {
count uintptr
head *ListElement
}
// NewList returns an initialized list.
func NewList() *List {
return &List{head: &ListElement{}}
}
// Len returns the number of elements within the list.
func (l *List) Len() int {
if l == nil { // not initialized yet?
return 0
}
return int(atomic.LoadUintptr(&l.count))
}
// First returns the head item of the list.
func (l *List) Head() *ListElement {
if l == nil { // not initialized yet?
return nil
}
return l.head
}
// First returns the first item of the list.
func (l *List) First() *ListElement {
if l == nil { // not initialized yet?
return nil
}
return l.head.Next()
}
// Add adds an item to the list and returns false if an item for the hash existed.
// searchStart = nil will start to search at the head item
func (l *List) Add(element *ListElement, searchStart *ListElement) (existed bool, inserted bool) {
left, found, right := l.search(searchStart, element)
if found != nil { // existing item found
return true, false
}
return false, l.insertAt(element, left, right)
}
// AddOrUpdate adds or updates an item to the list.
func (l *List) AddOrUpdate(element *ListElement, searchStart *ListElement) bool {
left, found, right := l.search(searchStart, element)
if found != nil { // existing item found
found.setValue(element.value) // update the value
return true
}
return l.insertAt(element, left, right)
}
// Cas compares and swaps the value of an item in the list.
func (l *List) Cas(element *ListElement, oldValue interface{}, searchStart *ListElement) bool {
_, found, _ := l.search(searchStart, element)
if found == nil { // no existing item found
return false
}
if found.casValue(oldValue, element.value) {
return true
}
return false
}
func (l *List) search(searchStart *ListElement, item *ListElement) (left *ListElement, found *ListElement, right *ListElement) {
if searchStart != nil && item.keyHash < searchStart.keyHash { // key would remain left from item? {
searchStart = nil // start search at head
}
if searchStart == nil { // start search at head?
left = l.head
found = left.Next()
if found == nil { // no items beside head?
return nil, nil, nil
}
} else {
found = searchStart
}
for {
if item.keyHash == found.keyHash { // key already exists
return nil, found, nil
}
if item.keyHash < found.keyHash { // new item needs to be inserted before the found value
if l.head == left {
return nil, nil, found
}
return left, nil, found
}
// go to next element in sorted linked list
left = found
found = left.Next()
if found == nil { // no more items on the right
return left, nil, nil
}
}
}
func (l *List) insertAt(element *ListElement, left *ListElement, right *ListElement) bool {
if left == nil {
//element->previous = head
element.previousElement = unsafe.Pointer(l.head)
//element->next = right
element.nextElement = unsafe.Pointer(right)
// insert at head, head-->next = element
if !atomic.CompareAndSwapPointer(&l.head.nextElement, unsafe.Pointer(right), unsafe.Pointer(element)) {
return false // item was modified concurrently
}
//right->previous = element
if right != nil {
if !atomic.CompareAndSwapPointer(&right.previousElement, unsafe.Pointer(l.head), unsafe.Pointer(element)) {
return false // item was modified concurrently
}
}
} else {
element.previousElement = unsafe.Pointer(left)
element.nextElement = unsafe.Pointer(right)
if !atomic.CompareAndSwapPointer(&left.nextElement, unsafe.Pointer(right), unsafe.Pointer(element)) {
return false // item was modified concurrently
}
if right != nil {
if !atomic.CompareAndSwapPointer(&right.previousElement, unsafe.Pointer(left), unsafe.Pointer(element)) {
return false // item was modified concurrently
}
}
}
atomic.AddUintptr(&l.count, 1)
return true
}
// Delete deletes an element from the list.
func (l *List) Delete(element *ListElement) {
if !atomic.CompareAndSwapUintptr(&element.deleted, uintptr(0), uintptr(1)) {
return // concurrent delete of the item in progress
}
for {
left := element.Previous()
right := element.Next()
if left == nil { // element is first item in list?
if !atomic.CompareAndSwapPointer(&l.head.nextElement, unsafe.Pointer(element), unsafe.Pointer(right)) {
continue // now head item was inserted concurrently
}
} else {
if !atomic.CompareAndSwapPointer(&left.nextElement, unsafe.Pointer(element), unsafe.Pointer(right)) {
continue // item was modified concurrently
}
}
if right != nil {
atomic.CompareAndSwapPointer(&right.previousElement, unsafe.Pointer(element), unsafe.Pointer(left))
}
break
}
atomic.AddUintptr(&l.count, ^uintptr(0)) // decrease counter
}