@@ -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)^));
15061519end ;
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+
15081531function TEnumerableBase <T>.Ordered: IEnumerable<T>;
15091532begin
15101533 Result := TEnumerableIterator<T>.Create(IEnumerable<T>(this),
@@ -3040,6 +3063,21 @@ function TIteratorBlock.GetEnumeratorAndSkip: Boolean;
30403063 end ;
30413064end ;
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+
30433081function TIteratorBlock.MoveNextEmpty : Boolean;
30443082begin
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;
31793229begin
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 ;
32833336end ;
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+
32853376function TIteratorBlock <T>.MoveNextOrdered: Boolean;
32863377begin
32873378 if Index < Count then
0 commit comments