@@ -2,6 +2,7 @@ package haxmap
2
2
3
3
import (
4
4
"reflect"
5
+ "sort"
5
6
"strconv"
6
7
"sync/atomic"
7
8
"unsafe"
@@ -49,6 +50,12 @@ type (
49
50
resizing atomicUint32
50
51
numItems atomicUintptr
51
52
}
53
+
54
+ // used in deletion of map elements
55
+ deletionRequest [K hashable ] struct {
56
+ keyHash uintptr
57
+ key K
58
+ }
52
59
)
53
60
54
61
// New returns a new HashMap instance with an optional specific initialization size
@@ -64,48 +71,42 @@ func New[K hashable, V any](size ...uintptr) *Map[K, V] {
64
71
return m
65
72
}
66
73
67
- // Del lazily deletes the key from the map
68
- // does nothing if key is absemt
69
- func (m * Map [K , V ]) Del (key K ) {
70
- var (
71
- h = m .hasher (key )
72
- elem = m .metadata .Load ().indexElement (h )
73
- )
74
-
75
- loop:
76
- for ; elem != nil ; elem = elem .next () {
77
- if elem .keyHash == h && elem .key == key {
78
- break loop
79
- }
80
- if elem .keyHash > h {
81
- return
82
- }
83
- }
84
- if elem == nil {
74
+ // Del deletes key/keys from the map
75
+ // Bulk deletion is more efficient than deleting keys one by one
76
+ func (m * Map [K , V ]) Del (keys ... K ) {
77
+ if len (keys ) == 0 {
85
78
return
86
79
}
87
- // mark node for deletion
88
- elem .remove ()
89
-
90
- for iter := m .listHead .next (); iter != nil ; iter = iter .next () {
80
+ var (
81
+ size = len (keys )
82
+ delQ = make ([]deletionRequest [K ], size )
83
+ elem = m .listHead .next ()
84
+ iter = 0
85
+ )
86
+ for idx := 0 ; idx < size ; idx ++ {
87
+ delQ [idx ].keyHash , delQ [idx ].key = m .hasher (keys [idx ]), keys [idx ]
91
88
}
92
89
93
- // remove node from map index if exists
94
- for {
95
- data := m .metadata .Load ()
96
- index := elem .keyHash >> data .keyshifts
97
- ptr := (* unsafe .Pointer )(unsafe .Pointer (uintptr (data .data ) + index * intSizeBytes ))
98
-
99
- next := elem .next ()
100
- if next != nil && elem .keyHash >> data .keyshifts != index {
101
- next = nil // do not set index to next item if it's not the same slice index
90
+ // sort in ascending order of keyhash
91
+ sort .Slice (delQ , func (i , j int ) bool {
92
+ return delQ [i ].keyHash < delQ [j ].keyHash
93
+ })
94
+
95
+ for elem != nil && iter < size {
96
+ if elem .keyHash == delQ [iter ].keyHash && elem .key == delQ [iter ].key {
97
+ elem .remove () // mark node for deletion
98
+ m .removeItemFromIndex (elem )
99
+ iter ++
100
+ elem = elem .next ()
101
+ } else if elem .keyHash > delQ [iter ].keyHash {
102
+ iter ++
103
+ } else {
104
+ elem = elem .next ()
102
105
}
103
- atomic . CompareAndSwapPointer ( ptr , unsafe . Pointer ( elem ), unsafe . Pointer ( next ))
106
+ }
104
107
105
- if data == m .metadata .Load () { // check that no resize happened
106
- m .numItems .Add (marked )
107
- return
108
- }
108
+ // iterate list from start to end to remove the marked nodes
109
+ for iter := m .listHead ; iter != nil ; iter = iter .next () {
109
110
}
110
111
}
111
112
@@ -218,6 +219,26 @@ func (m *Map[K, V]) fillIndexItems(mapData *metadata[K, V]) {
218
219
}
219
220
}
220
221
222
+ // removeItemFromIndex removes an item from the map index
223
+ func (m * Map [K , V ]) removeItemFromIndex (item * element [K , V ]) {
224
+ for {
225
+ data := m .metadata .Load ()
226
+ index := item .keyHash >> data .keyshifts
227
+ ptr := (* unsafe .Pointer )(unsafe .Pointer (uintptr (data .data ) + index * intSizeBytes ))
228
+
229
+ next := item .next ()
230
+ if next != nil && item .keyHash >> data .keyshifts != index {
231
+ next = nil // do not set index to next item if it's not the same slice index
232
+ }
233
+ atomic .CompareAndSwapPointer (ptr , unsafe .Pointer (item ), unsafe .Pointer (next ))
234
+
235
+ if data == m .metadata .Load () { // check that no resize happened
236
+ m .numItems .Add (marked )
237
+ return
238
+ }
239
+ }
240
+ }
241
+
221
242
// grow to the new size
222
243
func (m * Map [K , V ]) grow (newSize uintptr ) {
223
244
for {
0 commit comments