Skip to content

Commit fa8aff6

Browse files
committed
btreeg: add option to avoid allocations during delete range
Adds a DeleteRangeUnsafe function which takes an input parameter with a List[T] to avoid allocating the List across calls to delete range.
1 parent 82b45e9 commit fa8aff6

File tree

1 file changed

+23
-8
lines changed

1 file changed

+23
-8
lines changed

btreeg.go

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,13 @@ func (e *List[T]) Scan(iter func(item T) bool) {
675675
}
676676
}
677677

678+
func (e *List[T]) Clear() {
679+
e.less = nil
680+
e.q = e.q[:0]
681+
e.count = 0
682+
e.np = 0
683+
}
684+
678685
// Options for passing to the DeleteRange function
679686
type DeleteRangeOptions struct {
680687
// Do not return the deleted items.
@@ -689,14 +696,22 @@ type DeleteRangeOptions struct {
689696
// max (exclusive) sub-range.
690697
// Returns the deleted items as an ordered list that can be iterated over using
691698
// the list.Scan() method.
692-
func (tr *BTreeG[T]) DeleteRange(min, max T, opts *DeleteRangeOptions) (
693-
deleted List[T],
694-
) {
699+
func (tr *BTreeG[T]) DeleteRange(min, max T, opts *DeleteRangeOptions) (deleted List[T]) {
700+
return tr.DeleteRangeUnsafe(min, max, opts, List[T]{})
701+
}
702+
703+
// DeleteRangeUnsafe is the same as DeleteRange, but it takes a List as an argument to
704+
// avoid allocating/growing a new List on each call to DeleteRange. It is unsafe to use
705+
// the same List across concurrent calls to DeleteRange.
706+
func (tr *BTreeG[T]) DeleteRangeUnsafe(min, max T, opts *DeleteRangeOptions, deleted List[T]) List[T] {
695707
if tr.lock(true) {
696708
defer tr.unlock(true)
697709
}
698710
extract := opts == nil || !opts.NoReturn
699711
maxincl := opts != nil && opts.MaxInclusive
712+
// Clear just in case it hasn't been cleared yet.
713+
deleted.Clear()
714+
700715
maxstop := func(item T) bool {
701716
if maxincl {
702717
return tr.less(max, item)
@@ -715,7 +730,7 @@ func (tr *BTreeG[T]) DeleteRange(min, max T, opts *DeleteRangeOptions) (
715730
var stack []stackItem
716731
restart:
717732
if tr.root == nil {
718-
return
733+
return deleted
719734
}
720735
n := tr.isoLoad(&tr.root, true)
721736
stack = append(stack, stackItem{n, 0})
@@ -751,7 +766,7 @@ restart:
751766
if found {
752767
if maxstop(n.items[i]) {
753768
// stop
754-
return
769+
return deleted
755770
}
756771
pivot = n.items[i]
757772
if extract {
@@ -790,13 +805,13 @@ restart:
790805
}
791806
tr.count -= j
792807
if stop {
793-
return
808+
return deleted
794809
}
795810
}
796811
for ; i < len(n.items); i++ {
797812
if maxstop(n.items[i]) {
798813
// stop
799-
return
814+
return deleted
800815
}
801816
if len(n.items) > tr.min {
802817
if extract {
@@ -826,7 +841,7 @@ restart:
826841
stack = stack[:len(stack)-1]
827842
if len(stack) == 0 {
828843
// end of tree
829-
return
844+
return deleted
830845
}
831846
n = stack[len(stack)-1].node
832847
if i < len(n.items) {

0 commit comments

Comments
 (0)