Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 24 additions & 24 deletions common/types/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,16 +126,7 @@ func (l *baseList) Add(other ref.Val) ref.Val {
if !ok {
return MaybeNoSuchOverloadErr(other)
}
if l.Size() == IntZero {
return other
}
if otherList.Size() == IntZero {
return l
}
return &concatList{
Adapter: l.Adapter,
prevList: l,
nextList: otherList}
return newConcatList(l.Adapter, l, otherList)
}

// Contains implements the traits.Container interface method.
Expand Down Expand Up @@ -353,9 +344,27 @@ func (l *mutableList) ToImmutableList() traits.Lister {
// The `Adapter` enables native type to CEL type conversions.
type concatList struct {
Adapter
value any
prevList traits.Lister
nextList traits.Lister
value any
prevList traits.Lister
nextList traits.Lister
cachedSize ref.Val
}

func newConcatList(adapter Adapter, prevList, nextList traits.Lister) ref.Val {
prevSize := prevList.Size().(Int)
nextSize := nextList.Size().(Int)
if prevSize == IntZero {
return nextList.(ref.Val)
}
if nextSize == IntZero {
return prevList.(ref.Val)
}
return &concatList{
Adapter: adapter,
prevList: prevList,
nextList: nextList,
cachedSize: prevSize.Add(nextSize),
}
}

// Add implements the traits.Adder interface method.
Expand All @@ -364,16 +373,7 @@ func (l *concatList) Add(other ref.Val) ref.Val {
if !ok {
return MaybeNoSuchOverloadErr(other)
}
if l.Size() == IntZero {
return other
}
if otherList.Size() == IntZero {
return l
}
return &concatList{
Adapter: l.Adapter,
prevList: l,
nextList: otherList}
return newConcatList(l.Adapter, l, otherList)
}

// Contains implements the traits.Container interface method.
Expand Down Expand Up @@ -477,7 +477,7 @@ func (l *concatList) Iterator() traits.Iterator {

// Size implements the traits.Sizer interface method.
func (l *concatList) Size() ref.Val {
return l.prevList.Size().(Int).Add(l.nextList.Size())
Comment thread
Flo354 marked this conversation as resolved.
return l.cachedSize
}

// String converts the concatenated list to a human-readable string.
Expand Down
20 changes: 20 additions & 0 deletions common/types/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -910,3 +910,23 @@ func validateIterator123(t *testing.T, list traits.Lister) {
t.Errorf("Iterator did not iterate until last value")
}
}

func TestConcatListSizeCached(t *testing.T) {
reg := newTestRegistry(t)
// Build a deep chain of concat lists
var list traits.Lister = NewDynamicList(reg, []int64{0})
for i := 0; i < 200; i++ {
list = list.Add(NewDynamicList(reg, []int64{0})).(traits.Lister)
}
// Size() should return instantly since it's precomputed
size := list.Size()
if size != Int(201) {
t.Errorf("Expected size 201, got %v", size)
}
// Call Size() many times to confirm no quadratic behavior
for i := 0; i < 1000; i++ {
if list.Size() != Int(201) {
t.Errorf("Size() returned inconsistent value on call %d", i)
}
}
}