Skip to content

Commit 330847d

Browse files
committed
implement Memoize
1 parent 435a1c2 commit 330847d

File tree

2 files changed

+101
-2
lines changed

2 files changed

+101
-2
lines changed

Source/Base/Collections/Spring.Collections.Base.pas

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ TEnumerableBase<T> = class abstract(TEnumerableBase)
185185
function Min(const comparer: IComparer<T>): T; overload;
186186
function Min(const comparer: TComparison<T>): T; overload;
187187

188+
function Memoize: IEnumerable<T>;
189+
188190
function Ordered: IEnumerable<T>; overload;
189191
function Ordered(const comparer: IComparer<T>): IEnumerable<T>; overload;
190192
function Ordered(const comparer: TComparison<T>): IEnumerable<T>; overload;
@@ -260,7 +262,7 @@ TCollectionWrapper = class(TEnumerableWrapper, ICollection)
260262

261263
TIteratorKind = (
262264
Partition, &Array,
263-
Concat, Ordered, Reversed, Shuffled,
265+
Concat, Memoize, Ordered, Reversed, Shuffled,
264266
SkipWhile, SkipWhileIndex,
265267
TakeWhile, TakeWhileIndex,
266268
Where, WhereIndex, Select);
@@ -292,6 +294,7 @@ TIteratorBlock = record
292294

293295
function GetEnumerator: Boolean;
294296
function GetEnumeratorAndSkip: Boolean;
297+
function GetEnumeratorMemoize: Boolean;
295298

296299
function _Release: Integer; stdcall;
297300
function MoveNext: Boolean;
@@ -309,6 +312,15 @@ TIteratorBase = record
309312
function GetCountFast: Integer;
310313
end;
311314

315+
TMemoizeIterator<T> = record
316+
// only used via pointer internally
317+
// field layout has to match with TIteratorBase
318+
Source: IEnumerable;
319+
Enumerator: IEnumerator<T>;
320+
Items: TArray<T>;
321+
Index, Count: Integer;
322+
end;
323+
312324
TIteratorBase<T> = class abstract(TEnumerableBase<T>)
313325
private
314326
function HasLimit: Boolean; inline;
@@ -361,6 +373,7 @@ TIteratorBlock<T> = record
361373
function GetCurrent: T;
362374

363375
function MoveNextConcat: Boolean;
376+
function MoveNextMemoize: Boolean;
364377
function MoveNextOrdered: Boolean;
365378
function MoveNextReversed: Boolean;
366379
function MoveNextSkipWhile: Boolean;
@@ -1505,6 +1518,16 @@ function TEnumerableBase<T>.Min(const comparer: TComparison<T>): T;
15051518
Result := IEnumerable<T>(this).Min(IComparer<T>(PPointer(@comparer)^));
15061519
end;
15071520

1521+
function TEnumerableBase<T>.Memoize: IEnumerable<T>;
1522+
begin
1523+
if (GetInterfaceEntry(ICollectionOfTGuid) <> nil)
1524+
or (GetInterfaceEntry(IReadOnlyCollectionOfTGuid) <> nil) then
1525+
Result := IEnumerable<T>(this)
1526+
else
1527+
Result := TEnumerableIterator<T>.Create(IEnumerable<T>(this),
1528+
0, 0, nil, TIteratorKind.Memoize);
1529+
end;
1530+
15081531
function TEnumerableBase<T>.Ordered: IEnumerable<T>;
15091532
begin
15101533
Result := TEnumerableIterator<T>.Create(IEnumerable<T>(this),
@@ -3040,6 +3063,21 @@ function TIteratorBlock.GetEnumeratorAndSkip: Boolean;
30403063
end;
30413064
end;
30423065

3066+
function TIteratorBlock.GetEnumeratorMemoize: Boolean;
3067+
begin
3068+
if PIteratorBase(Predicate).fCount = 0 then
3069+
begin
3070+
{$IFDEF MSWINDOWS}
3071+
IEnumerableInternal(Source).GetEnumerator(IEnumerator(PIteratorBase(Predicate).fPredicate));
3072+
{$ELSE}
3073+
IEnumerator(PIteratorBase(Predicate).fPredicate) := Source.GetEnumerator;
3074+
{$ENDIF}
3075+
PIteratorBase(Predicate).fCount := PIteratorBase(Predicate).fCount or not CountMask;
3076+
end;
3077+
DoMoveNext := Methods.MoveNext;
3078+
Result := DoMoveNext(@Self);
3079+
end;
3080+
30433081
function TIteratorBlock.MoveNextEmpty: Boolean;
30443082
begin
30453083
Result := False;
@@ -3072,6 +3110,8 @@ class function TIteratorBlock<T>.Create(const iterator: TIteratorBase<T>): IEnum
30723110
Count := iterator.fCount;
30733111
Kind := iterator.fKind;
30743112
Methods.Finalize := @TIteratorBlock<T>.Finalize;
3113+
if Kind = Memoize then
3114+
Pointer(Predicate) := @iterator.fSource;
30753115
end;
30763116
rec.InitMethods;
30773117
rec.InitVtable;
@@ -3118,6 +3158,16 @@ procedure TIteratorBlock<T>.InitMethods;
31183158
Methods.MoveNext := @TIteratorBlock<T>.MoveNextConcat;
31193159
DoMoveNext := @TIteratorBlock.GetEnumerator;
31203160
end;
3161+
TIteratorKind.Memoize:
3162+
begin
3163+
if Count = 0 then
3164+
begin
3165+
Methods.MoveNext := @TIteratorBlock<T>.MoveNextMemoize;
3166+
DoMoveNext := @TIteratorBlock.GetEnumeratorMemoize;
3167+
end
3168+
else
3169+
DoMoveNext := @TIteratorBlock<T>.MoveNextMemoize;
3170+
end;
31213171
TIteratorKind.Ordered:
31223172
begin
31233173
Methods.MoveNext := @TIteratorBlock<T>.MoveNextOrdered;
@@ -3179,7 +3229,10 @@ function TIteratorBlock<T>.Finalize: Boolean;
31793229
begin
31803230
Enumerator := nil;
31813231
Source := nil;
3182-
Predicate := nil;
3232+
if Kind <> Memoize then
3233+
Predicate := nil
3234+
else
3235+
Pointer(Predicate) := nil;
31833236
Items := nil;
31843237
{$IFDEF DELPHIXE7_UP}
31853238
if IsManagedType(T) then
@@ -3282,6 +3335,44 @@ function TIteratorBlock<T>.MoveNextIndexed: Boolean;
32823335
end;
32833336
end;
32843337

3338+
function TIteratorBlock<T>.MoveNextMemoize: Boolean;
3339+
var
3340+
iterator: ^TMemoizeIterator<T>;
3341+
count, capacity: NativeInt;
3342+
begin
3343+
iterator := Pointer(Predicate);
3344+
count := iterator.Count and CountMask;
3345+
if Index >= count then
3346+
begin
3347+
if iterator.Enumerator = nil then
3348+
Exit(False);
3349+
3350+
if not iterator.Enumerator.MoveNext then
3351+
begin
3352+
iterator.Enumerator := nil;
3353+
Exit(False);
3354+
end;
3355+
3356+
capacity := DynArrayLength(iterator.Items);
3357+
if count >= capacity then
3358+
begin
3359+
capacity := GrowCapacity(capacity);
3360+
SetLength(iterator.Items, capacity);
3361+
end;
3362+
{$IF defined(DELPHIXE7_UP) and defined(MSWINDOWS)}
3363+
if IsManagedType(T) then
3364+
IEnumeratorInternal(iterator.Enumerator).GetCurrent(iterator.Items[count])
3365+
else
3366+
{$IFEND}
3367+
iterator.Items[count] := iterator.Enumerator.Current;
3368+
Inc(iterator.Count);
3369+
end;
3370+
3371+
Current := iterator.Items[Index];
3372+
Inc(Index);
3373+
Result := True;
3374+
end;
3375+
32853376
function TIteratorBlock<T>.MoveNextOrdered: Boolean;
32863377
begin
32873378
if Index < Count then

Source/Base/Collections/Spring.Collections.pas

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,14 @@ interface
656656
function Min(const comparer: IComparer<T>): T; overload;
657657
function Min(const comparer: TComparison<T>): T; overload;
658658

659+
/// <summary>
660+
/// Creates a sequence that lazily caches the source as it is iterated
661+
/// for the first time, reusing the cache thereafter for future
662+
/// re-iterations. If the source is already cached or buffered then it
663+
/// is returned verbatim.
664+
/// </summary>
665+
function Memoize: IEnumerable<T>;
666+
659667
/// <summary>
660668
/// Sorts the elements of a sequence in ascending order using the default
661669
/// comparer for their type.

0 commit comments

Comments
 (0)