Skip to content

Commit 5f15ec7

Browse files
committed
add Swap and CompareAndSwap APIs
1 parent b16cff1 commit 5f15ec7

File tree

2 files changed

+81
-0
lines changed

2 files changed

+81
-0
lines changed

e2e_test.go

+44
Original file line numberDiff line numberDiff line change
@@ -308,3 +308,47 @@ func TestInfiniteLoop(t *testing.T) {
308308
}
309309
})
310310
}
311+
312+
// https://github.com/alphadose/haxmap/issues/18
313+
// test compare and swap
314+
func TestCAS(t *testing.T) {
315+
type custom struct {
316+
val int
317+
}
318+
m := New[string, custom]()
319+
m.Set("1", custom{val: 1})
320+
if m.CompareAndSwap("1", custom{val: 420}, custom{val: 2}) {
321+
t.Error("Invalid Compare and Swap")
322+
}
323+
if !m.CompareAndSwap("1", custom{val: 1}, custom{val: 2}) {
324+
t.Error("Compare and Swap Failed")
325+
}
326+
val, ok := m.Get("1")
327+
if !ok {
328+
t.Error("Key doesnt exists")
329+
}
330+
if val.val != 2 {
331+
t.Error("Invalid Compare and Swap value returned")
332+
}
333+
}
334+
335+
// https://github.com/alphadose/haxmap/issues/18
336+
// test swap
337+
func TestSwap(t *testing.T) {
338+
m := New[string, int]()
339+
m.Set("1", 1)
340+
val, swapped := m.Swap("1", 2)
341+
if !swapped {
342+
t.Error("Swap failed")
343+
}
344+
if val != 1 {
345+
t.Error("Old value not returned in swal")
346+
}
347+
val, ok := m.Get("1")
348+
if !ok {
349+
t.Error("Key doesnt exists")
350+
}
351+
if val != 2 {
352+
t.Error("New value not set")
353+
}
354+
}

map.go

+37
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,43 @@ func (m *Map[K, V]) GetOrSet(key K, value V) (actual V, loaded bool) {
229229
return
230230
}
231231

232+
// CompareAndSwap atomically updates a map entry given its key by comparing current value to `oldValue`
233+
// and setting it to `newValue` if the above comparison is successful
234+
// It returns a boolean indicating whether the CompareAndSwap was successful or not
235+
func (m *Map[K, V]) CompareAndSwap(key K, oldValue, newValue V) bool {
236+
var (
237+
h = m.hasher(key)
238+
existing = m.metadata.Load().indexElement(h)
239+
)
240+
if existing == nil || existing.keyHash > h {
241+
existing = m.listHead
242+
}
243+
if _, current, _ := existing.search(h, key); current != nil {
244+
if oldPtr := current.value.Load(); reflect.DeepEqual(*oldPtr, oldValue) {
245+
return current.value.CompareAndSwap(oldPtr, &newValue)
246+
}
247+
}
248+
return false
249+
}
250+
251+
// Swap atomically swaps the value of a map entry given its key
252+
// It returns the old value if swap was successful and a boolean `swapped` indicating whether the swap was successful or not
253+
func (m *Map[K, V]) Swap(key K, newValue V) (oldValue V, swapped bool) {
254+
var (
255+
h = m.hasher(key)
256+
existing = m.metadata.Load().indexElement(h)
257+
)
258+
if existing == nil || existing.keyHash > h {
259+
existing = m.listHead
260+
}
261+
if _, current, _ := existing.search(h, key); current != nil {
262+
oldValue, swapped = *current.value.Swap(&newValue), true
263+
} else {
264+
swapped = false
265+
}
266+
return
267+
}
268+
232269
// ForEach iterates over key-value pairs and executes the lambda provided for each such pair
233270
// lambda must return `true` to continue iteration and `false` to break iteration
234271
func (m *Map[K, V]) ForEach(lambda func(K, V) bool) {

0 commit comments

Comments
 (0)