Skip to content

Commit

Permalink
feat: add SetIf and DeleteIf methods.
Browse files Browse the repository at this point in the history
These methods allow you to lazy create/initialize values and decide if 
the values should be set or deleted while the map shard is locked.

Signed-off-by: Hiram Chirino <hiram@hiramchirino.com>
  • Loading branch information
chirino committed Nov 5, 2023
1 parent 000b567 commit b6b42fb
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 1 deletion.
24 changes: 24 additions & 0 deletions concurrent_swiss_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,18 @@ func (m *CsMap[K, V]) Delete(key K) bool {
return shard.items.DeleteWithHash(key, hashShardPair.hash)
}

func (m *CsMap[K, V]) DeleteIf(key K, condition func(value V) bool) bool {
hashShardPair := m.getShard(key)
shard := hashShardPair.shard
shard.Lock()
defer shard.Unlock()
value, ok := shard.items.GetWithHash(key, hashShardPair.hash)
if ok && condition(value) {
return shard.items.DeleteWithHash(key, hashShardPair.hash)
}
return false
}

func (m *CsMap[K, V]) Load(key K) (V, bool) {
hashShardPair := m.getShard(key)
shard := hashShardPair.shard
Expand Down Expand Up @@ -127,6 +139,18 @@ func (m *CsMap[K, V]) SetIfAbsent(key K, value V) {
}
}

func (m *CsMap[K, V]) SetIf(key K, conditionFn func(previousVale V, previousFound bool) (value V, set bool)) {
hashShardPair := m.getShard(key)
shard := hashShardPair.shard
shard.Lock()
defer shard.Unlock()
value, found := shard.items.GetWithHash(key, hashShardPair.hash)
value, ok := conditionFn(value, found)
if ok {
shard.items.PutWithHash(key, value, hashShardPair.hash)
}
}

func (m *CsMap[K, V]) SetIfPresent(key K, value V) {
hashShardPair := m.getShard(key)
shard := hashShardPair.shard
Expand Down
60 changes: 59 additions & 1 deletion concurrent_swiss_map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ func TestSetIfAbsent(t *testing.T) {
t.Fatal("1 should be exist")
}
}

Check failure on line 54 in concurrent_swiss_map_test.go

View workflow job for this annotation

GitHub Actions / build

File is not `gofumpt`-ed (gofumpt)

func TestSetIfPresent(t *testing.T) {
myMap := csmap.Create[int, string]()
myMap.SetIfPresent(1, "test")
Expand All @@ -68,6 +67,65 @@ func TestSetIfPresent(t *testing.T) {
}
}

func TestSetIf(t *testing.T) {
myMap := csmap.Create[int, string]()
myMap.SetIf(1, func(previousVale string, previousFound bool) (value string, set bool) {
// operate like a SetIfAbsent...
if !previousFound {
return "value a", true

Check failure on line 75 in concurrent_swiss_map_test.go

View workflow job for this annotation

GitHub Actions / build

string `value a` has 3 occurrences, make it a constant (goconst)
}
return "", false
})
value, _ := myMap.Load(1)
if value != "value a" {
t.Fatal("value should value a")
}

myMap.SetIf(1, func(previousVale string, previousFound bool) (value string, set bool) {
// operate like a SetIfAbsent...
if !previousFound {
return "bad", true
}
return "", false
})
value, _ = myMap.Load(1)
if value != "value a" {
t.Fatal("value should value a")
}
}

func TestDeleteIf(t *testing.T) {
myMap := csmap.Create[int, string]()
myMap.Store(1, "value b")
ok1 := myMap.DeleteIf(20, func(value string) bool {
t.Fatal("condition function should not have been called")
return false
})
if ok1 {
t.Fatal("ok1 should be false")
}

ok2 := myMap.DeleteIf(1, func(value string) bool {
if value != "value b" {
t.Fatal("condition function arg should be tests")
}
return false // don't delete
})
if ok2 {
t.Fatal("ok1 should be false")
}

ok3 := myMap.DeleteIf(1, func(value string) bool {
if value != "value b" {
t.Fatal("condition function arg should be tests")
}
return true // delete the entry
})
if !ok3 {
t.Fatal("ok2 should be true")
}
}

func TestCount(t *testing.T) {
myMap := csmap.Create[int, string]()
myMap.SetIfAbsent(1, "test")
Expand Down

0 comments on commit b6b42fb

Please sign in to comment.