@@ -1287,10 +1287,31 @@ func batchCount(keys, values any) (int, error) {
1287
1287
//
1288
1288
// It's not possible to guarantee that all keys in a map will be
1289
1289
// returned if there are concurrent modifications to the map.
1290
+ //
1291
+ // Iterating a hash map from which keys are being deleted is not
1292
+ // safe. You may see the same key multiple times. Iteration may
1293
+ // also abort with an error, see IsIterationAborted.
1294
+ //
1295
+ // Iterating a queue/stack map returns an error (NextKey) as the
1296
+ // Map.Drain API should be used instead.
1290
1297
func (m * Map ) Iterate () * MapIterator {
1291
1298
return newMapIterator (m )
1292
1299
}
1293
1300
1301
+ // Drain traverses a map while also removing entries.
1302
+ //
1303
+ // It's safe to create multiple drainers at the same time,
1304
+ // but their respective outputs will differ.
1305
+ //
1306
+ // Iterating a map that does not support entry removal such as
1307
+ // an array return an error (Delete/LookupAndDelete) as the
1308
+ // Map.Iterate API should be used instead.
1309
+ func (m * Map ) Drain () * MapIterator {
1310
+ it := newMapIterator (m )
1311
+ it .drain = true
1312
+ return it
1313
+ }
1314
+
1294
1315
// Close the Map's underlying file descriptor, which could unload the
1295
1316
// Map from the kernel if it is not pinned or in use by a loaded Program.
1296
1317
func (m * Map ) Close () error {
@@ -1549,7 +1570,7 @@ type MapIterator struct {
1549
1570
// of []byte to avoid allocations.
1550
1571
cursor any
1551
1572
count , maxEntries uint32
1552
- done bool
1573
+ done , drain bool
1553
1574
err error
1554
1575
}
1555
1576
@@ -1562,10 +1583,6 @@ func newMapIterator(target *Map) *MapIterator {
1562
1583
1563
1584
// Next decodes the next key and value.
1564
1585
//
1565
- // Iterating a hash map from which keys are being deleted is not
1566
- // safe. You may see the same key multiple times. Iteration may
1567
- // also abort with an error, see IsIterationAborted.
1568
- //
1569
1586
// Returns false if there are no more entries. You must check
1570
1587
// the result of Err afterwards.
1571
1588
//
@@ -1574,26 +1591,28 @@ func (mi *MapIterator) Next(keyOut, valueOut interface{}) bool {
1574
1591
if mi .err != nil || mi .done {
1575
1592
return false
1576
1593
}
1594
+ if mi .drain {
1595
+ return mi .nextDrain (keyOut , valueOut )
1596
+ }
1597
+ return mi .nextIterate (keyOut , valueOut )
1598
+ }
1599
+
1600
+ func (mi * MapIterator ) nextIterate (keyOut , valueOut interface {}) bool {
1601
+ var key interface {}
1577
1602
1578
- // For array-like maps NextKey returns nil only after maxEntries
1579
- // iterations.
1603
+ // For array-like maps NextKey returns nil only after maxEntries iterations.
1580
1604
for mi .count <= mi .maxEntries {
1581
1605
if mi .cursor == nil {
1582
1606
// Pass nil interface to NextKey to make sure the Map's first key
1583
1607
// is returned. If we pass an uninitialized []byte instead, it'll see a
1584
1608
// non-nil interface and try to marshal it.
1585
1609
mi .cursor = make ([]byte , mi .target .keySize )
1586
- mi . err = mi . target . NextKey ( nil , mi . cursor )
1610
+ key = nil
1587
1611
} else {
1588
- mi . err = mi .target . NextKey ( mi . cursor , mi . cursor )
1612
+ key = mi .cursor
1589
1613
}
1590
1614
1591
- if errors .Is (mi .err , ErrKeyNotExist ) {
1592
- mi .done = true
1593
- mi .err = nil
1594
- return false
1595
- } else if mi .err != nil {
1596
- mi .err = fmt .Errorf ("get next key: %w" , mi .err )
1615
+ if ! mi .fetchNextKey (key ) {
1597
1616
return false
1598
1617
}
1599
1618
@@ -1615,20 +1634,84 @@ func (mi *MapIterator) Next(keyOut, valueOut interface{}) bool {
1615
1634
return false
1616
1635
}
1617
1636
1618
- buf := mi .cursor .([]byte )
1619
- if ptr , ok := keyOut .(unsafe.Pointer ); ok {
1620
- copy (unsafe .Slice ((* byte )(ptr ), len (buf )), buf )
1621
- } else {
1622
- mi .err = sysenc .Unmarshal (keyOut , buf )
1637
+ return mi .copyCursorToKeyOut (keyOut )
1638
+ }
1639
+
1640
+ mi .err = fmt .Errorf ("%w" , ErrIterationAborted )
1641
+ return false
1642
+ }
1643
+
1644
+ func (mi * MapIterator ) nextDrain (keyOut , valueOut interface {}) bool {
1645
+ // Handke keyless map, for which mi.cursor (key used in lookupAndDelete) should be nil
1646
+ if mi .isKeylessMap () {
1647
+ if keyOut != nil {
1648
+ mi .err = fmt .Errorf ("non-nil keyOut provided for map without a key, must be nil instead" )
1649
+ return false
1623
1650
}
1651
+ return mi .drainMapEntry (valueOut )
1652
+ }
1624
1653
1625
- return mi .err == nil
1654
+ // Allocate only once data for retrieving the next key in the map.
1655
+ if mi .cursor == nil {
1656
+ mi .cursor = make ([]byte , mi .target .keySize )
1657
+ }
1658
+
1659
+ // Always retrieve first key in the map. This should ensure that the whole map
1660
+ // is traversed, despite concurrent operations (ordering of items might differ).
1661
+ for mi .err == nil && mi .fetchNextKey (nil ) {
1662
+ if mi .drainMapEntry (valueOut ) {
1663
+ return mi .copyCursorToKeyOut (keyOut )
1664
+ }
1665
+ }
1666
+ return false
1667
+ }
1668
+
1669
+ func (mi * MapIterator ) isKeylessMap () bool {
1670
+ return mi .target .keySize == 0
1671
+ }
1672
+
1673
+ func (mi * MapIterator ) drainMapEntry (valueOut interface {}) bool {
1674
+ mi .err = mi .target .LookupAndDelete (mi .cursor , valueOut )
1675
+ if mi .err == nil {
1676
+ mi .count ++
1677
+ return true
1678
+ }
1679
+
1680
+ if errors .Is (mi .err , ErrKeyNotExist ) {
1681
+ mi .err = nil
1682
+ } else {
1683
+ mi .err = fmt .Errorf ("lookup_and_delete key: %w" , mi .err )
1626
1684
}
1627
1685
1628
- mi .err = fmt .Errorf ("%w" , ErrIterationAborted )
1629
1686
return false
1630
1687
}
1631
1688
1689
+ func (mi * MapIterator ) fetchNextKey (key interface {}) bool {
1690
+ mi .err = mi .target .NextKey (key , mi .cursor )
1691
+ if mi .err == nil {
1692
+ return true
1693
+ }
1694
+
1695
+ if errors .Is (mi .err , ErrKeyNotExist ) {
1696
+ mi .done = true
1697
+ mi .err = nil
1698
+ } else {
1699
+ mi .err = fmt .Errorf ("get next key: %w" , mi .err )
1700
+ }
1701
+
1702
+ return false
1703
+ }
1704
+
1705
+ func (mi * MapIterator ) copyCursorToKeyOut (keyOut interface {}) bool {
1706
+ buf := mi .cursor .([]byte )
1707
+ if ptr , ok := keyOut .(unsafe.Pointer ); ok {
1708
+ copy (unsafe .Slice ((* byte )(ptr ), len (buf )), buf )
1709
+ } else {
1710
+ mi .err = sysenc .Unmarshal (keyOut , buf )
1711
+ }
1712
+ return mi .err == nil
1713
+ }
1714
+
1632
1715
// Err returns any encountered error.
1633
1716
//
1634
1717
// The method must be called after Next returns nil.
0 commit comments