Memory hole in Data-Join fixed

This commit is contained in:
Michael Schimmel
2025-07-24 13:07:29 +02:00
parent 4859c49738
commit ff5b379fde
5 changed files with 141 additions and 137 deletions
+5 -14
View File
@@ -315,15 +315,6 @@ begin
// next stage // next stage
var ATREndPoint := ATR.CreateEndpoint(5);
var ATRSeries: TSeries<Double>;
var HullEndPoint := Hull.CreateEndpoint(5);
var HullSeries: TSeries<Double>;
var SmaEndPoint := Sma.CreateEndpoint(5);
var SmaSeries: TSeries<Double>;
var curr: TSignal; var curr: TSignal;
curr.SL := Double.NaN; curr.SL := Double.NaN;
curr.Entry := Double.NaN; curr.Entry := Double.NaN;
@@ -333,7 +324,7 @@ begin
var conv := TConverter.Join<Double>([Ohlc.Field<Double>('Low'), Ohlc.Field<Double>('High'), Closes, ATR, Hull, Sma]); var conv := TConverter.Join<Double>([Ohlc.Field<Double>('Low'), Ohlc.Field<Double>('High'), Closes, ATR, Hull, Sma]);
var Signal := var Signal :=
TConverter<TArray<Double>, TSignal>.CreateGeneric( TConverter<TArray<Double>, TSignal>.CreateConverter(
function(const Values: TArray<Double>): TSignal function(const Values: TArray<Double>): TSignal
begin begin
var low := Values[0]; var low := Values[0];
@@ -473,7 +464,7 @@ begin
panel.AddDoubleSeries(Signal.Producer.Field<Double>('Entry'), TAlphaColors.Green, 1); panel.AddDoubleSeries(Signal.Producer.Field<Double>('Entry'), TAlphaColors.Green, 1);
panel.AddDoubleSeries(Signal.Producer.Field<Double>('SL'), TAlphaColors.Red, 2); panel.AddDoubleSeries(Signal.Producer.Field<Double>('SL'), TAlphaColors.Red, 2);
var mean := TConverter<TArray<Double>, Double>.CreateGeneric(TIndicators.CreateMean()); var mean := TConverter<TArray<Double>, Double>.CreateConverter(TIndicators.CreateMean());
TConverter.Join<Double>([Hull, Sma]).Chain<Double>(mean); TConverter.Join<Double>([Hull, Sma]).Chain<Double>(mean);
@@ -494,14 +485,14 @@ begin
var indi := EMAFActory.CreateIndicator(Params); var indi := EMAFActory.CreateIndicator(Params);
var EMAConv := TConverter<TDataRecord, TDataRecord>.CreateGeneric(indi); var EMAConv := TConverter<TDataRecord, TDataRecord>.CreateConverter(indi);
var equityEMA := var equityEMA :=
equity equity
.Producer .Producer
.Chain<TDataRecord>(TConverter.FieldToRecord<Double>(EMAFactory.Input, 'Price')) .Chain<TDataRecord>(EMAFactory.Input.FieldAsRecord<Double>('Price'))
.Chain<TDataRecord>(EMAConv) .Chain<TDataRecord>(EMAConv)
.Chain<Double>(TConverter.FieldOfRecord<Double>(EMAFactory.Output, 'MA')); .Chain<Double>(EMAFactory.Output.FieldOfRecord<Double>('MA'));
////////////// //////////////
+1 -1
View File
@@ -50,7 +50,7 @@ begin
var signalGenerator := var signalGenerator :=
conv.Chain<TSignalEvent>( conv.Chain<TSignalEvent>(
TConverter<TArray<Double>, TSignalEvent>.CreateGeneric( TConverter<TArray<Double>, TSignalEvent>.CreateConverter(
function(const Values: TArray<Double>): TSignalEvent function(const Values: TArray<Double>): TSignalEvent
begin begin
Result.Low := Values[0]; Result.Low := Values[0];
+19 -8
View File
@@ -18,8 +18,8 @@ type
{ TChartCustomLayer } { TChartCustomLayer }
TChartCustomLayer<T> = class abstract(TMycChart.TDataLayer) TChartCustomLayer<T> = class abstract(TMycChart.TDataLayer)
private private
FProducer: TProducer<T>; FProducer: TProducer<T>.TSubscription;
FReceiver: TLazy<TSeries<T>>; FSeries: TLazy<TSeries<T>>;
FData: TSeries<T>; FData: TSeries<T>;
protected protected
function GetCount: Int64; override; function GetCount: Int64; override;
@@ -34,6 +34,7 @@ type
); override; abstract; ); override; abstract;
public public
constructor Create(AParent: TMycChart.TPanel; const AProducer: TProducer<T>); constructor Create(AParent: TMycChart.TPanel; const AProducer: TProducer<T>);
destructor Destroy; override;
property Data: TSeries<T> read FData; property Data: TSeries<T> read FData;
end; end;
@@ -83,7 +84,7 @@ type
{ TChartXAxisLayer } { TChartXAxisLayer }
TChartXAxisLayer<T> = class(TMycChart.TXAxisLayer) TChartXAxisLayer<T> = class(TMycChart.TXAxisLayer)
private private
FProducer: TProducer<T>; FProducer: TProducer<T>.TSubscription;
FReceiver: TLazy<TSeries<T>>; FReceiver: TLazy<TSeries<T>>;
FData: TSeries<T>; FData: TSeries<T>;
protected protected
@@ -93,6 +94,7 @@ type
function GetTotalCount: Int64; override; function GetTotalCount: Int64; override;
public public
constructor Create(AOwner: TMycChart; const AProducer: TProducer<T>); constructor Create(AOwner: TMycChart; const AProducer: TProducer<T>);
destructor Destroy; override;
property Data: TSeries<T> read FData; property Data: TSeries<T> read FData;
end; end;
@@ -270,9 +272,13 @@ end;
constructor TChartCustomLayer<T>.Create(AParent: TMycChart.TPanel; const AProducer: TProducer<T>); constructor TChartCustomLayer<T>.Create(AParent: TMycChart.TPanel; const AProducer: TProducer<T>);
begin begin
inherited Create(AParent); inherited Create(AParent);
FProducer := AProducer; FProducer := AProducer.CreateEndpoint(Owner.Lookback.Value, FSeries);
end;
FReceiver := FProducer.CreateEndpoint(Owner.Lookback.Value); destructor TChartCustomLayer<T>.Destroy;
begin
FProducer.Unlink;
inherited;
end; end;
function TChartCustomLayer<T>.GetCount: Int64; function TChartCustomLayer<T>.GetCount: Int64;
@@ -287,7 +293,7 @@ end;
function TChartCustomLayer<T>.Update: Boolean; function TChartCustomLayer<T>.Update: Boolean;
begin begin
Result := FReceiver.Update(FData); Result := FSeries.Update(FData);
end; end;
{ TChartXAxisLayer } { TChartXAxisLayer }
@@ -295,8 +301,13 @@ end;
constructor TChartXAxisLayer<T>.Create(AOwner: TMycChart; const AProducer: TProducer<T>); constructor TChartXAxisLayer<T>.Create(AOwner: TMycChart; const AProducer: TProducer<T>);
begin begin
inherited Create(AOwner); inherited Create(AOwner);
FProducer := AProducer; FProducer := AProducer.CreateEndpoint(Owner.Lookback.Value, FReceiver);
FReceiver := FProducer.CreateEndpoint(Owner.Lookback.Value); end;
destructor TChartXAxisLayer<T>.Destroy;
begin
FProducer.Unlink;
inherited;
end; end;
function TChartXAxisLayer<T>.GetCaption(Idx: Int64): String; function TChartXAxisLayer<T>.GetCaption(Idx: Int64): String;
+36 -51
View File
@@ -31,6 +31,16 @@ type
class property Null: IConsumer<T> read FNull; class property Null: IConsumer<T> read FNull;
end; end;
// A consumer implementation that is owned by a controller.
TMycGenericConsumer<T> = class(TMycConsumer<T>)
private
FProc: TConstFunc<T, TState>;
protected
function Consume(const Value: T): TState; override; final;
public
constructor Create(const Controller: IInterface; const AProc: TConstFunc<T, TState>);
end;
TMycProducer<T> = class(TInterfacedObject, IProducer<T>) TMycProducer<T> = class(TInterfacedObject, IProducer<T>)
strict private strict private
type type
@@ -79,18 +89,7 @@ type
FNull: IConverter<S, T>; FNull: IConverter<S, T>;
class constructor CreateClass; class constructor CreateClass;
private private
type FConsumer: TMycGenericConsumer<S>;
TConsumer = class(TMycConsumer<S>)
private
FOwner: TMycConverter<S, T>;
protected
function Consume(const Value: S): TState; override; final;
public
constructor Create(AOwner: TMycConverter<S, T>);
end;
private
FConsumer: TConsumer;
function GetConsumer: IConsumer<S>; function GetConsumer: IConsumer<S>;
protected protected
// To be implemented by descendants to perform the actual conversion. // To be implemented by descendants to perform the actual conversion.
@@ -162,16 +161,6 @@ type
constructor Create(const AFieldName: String); constructor Create(const AFieldName: String);
end; end;
// A consumer implementation that is owned by a controller.
TMycGenericConsumer<T> = class(TMycConsumer<T>)
private
FProc: TConstFunc<T, TState>;
protected
function Consume(const Value: T): TState; override;
public
constructor Create(const Controller: IInterface; const AProc: TConstFunc<T, TState>);
end;
// Endpoint that collects data into a series. // Endpoint that collects data into a series.
TMycDataEndpoint<T> = class(TInterfacedObject, TLazy<TSeries<T>>.ILazy) TMycDataEndpoint<T> = class(TInterfacedObject, TLazy<TSeries<T>>.ILazy)
private private
@@ -183,9 +172,7 @@ type
end; end;
private private
FConsumer: TMycGenericConsumer<T>; FConsumer: TMycGenericConsumer<T>;
FTag: TTag; FLookback: Integer;
FProducer: IProducer<T>;
FLookback: Int64;
FChanged: TFlag; FChanged: TFlag;
FLock: TLightweightMREW; FLock: TLightweightMREW;
FFirst: PItem; FFirst: PItem;
@@ -193,9 +180,11 @@ type
function GetChanged: TState; function GetChanged: TState;
function Consume(const Value: T): TState; function Consume(const Value: T): TState;
public public
constructor Create(const AProducer: TProducer<T>; ALookback: Int64); constructor Create(ALookback: Integer);
destructor Destroy; override; destructor Destroy; override;
function Update(var Value: TSeries<T>): Boolean; function Update(var Value: TSeries<T>): Boolean;
class function CreateDataEndpoint(Lookback: Integer; out Series: TLazy<TSeries<T>>): IConsumer<T>;
end; end;
TTickAggregation = class(TMycConverter<TDataPoint<Double>, TDataPoint<TOhlcItem>>) TTickAggregation = class(TMycConverter<TDataPoint<Double>, TDataPoint<TOhlcItem>>)
@@ -239,9 +228,7 @@ type
TMycDataJoin<T> = class(TInterfacedObject, IProducer<TArray<T>>) TMycDataJoin<T> = class(TInterfacedObject, IProducer<TArray<T>>)
private private
FConsumers: array of record FConsumers: array of record
Producer: IProducer<T>;
Consumer: TMycGenericConsumer<T>; Consumer: TMycGenericConsumer<T>;
Tag: TTag;
Queue: TQueue<T>; Queue: TQueue<T>;
end; end;
@@ -249,11 +236,13 @@ type
FLock: TSpinLock; FLock: TSpinLock;
FCount: Integer; FCount: Integer;
function Consume(Idx: Integer; const Value: T): TState; function Consume(Idx: Integer; const Value: T): TState;
function GetConsumers(Idx: Integer): IConsumer<T>;
function Link(const Consumer: IConsumer<TArray<T>>): TTag; function Link(const Consumer: IConsumer<TArray<T>>): TTag;
procedure Unlink(Tag: TTag); procedure Unlink(Tag: TTag);
public public
constructor Create(const AProducers: TArray<TProducer<T>>); constructor Create(ACount: Integer);
destructor Destroy; override; destructor Destroy; override;
property Consumers[Idx: Integer]: IConsumer<T> read GetConsumers;
end; end;
TMycComposedConverter<S, T> = class(TInterfacedObject, IConverter<S, T>) TMycComposedConverter<S, T> = class(TInterfacedObject, IConverter<S, T>)
@@ -363,7 +352,7 @@ end;
constructor TMycConverter<S, T>.Create; constructor TMycConverter<S, T>.Create;
begin begin
inherited Create; inherited Create;
FConsumer := TConsumer.Create(Self); FConsumer := TMycGenericConsumer<S>.Create(Self, Consume);
end; end;
{ TMycConverter<S, T> } { TMycConverter<S, T> }
@@ -486,19 +475,15 @@ end;
{ TMycDataEndpoint<T> } { TMycDataEndpoint<T> }
constructor TMycDataEndpoint<T>.Create(const AProducer: TProducer<T>; ALookback: Int64); constructor TMycDataEndpoint<T>.Create(ALookback: Integer);
begin begin
inherited Create; inherited Create;
FProducer := AProducer;
FLookback := ALookback; FLookback := ALookback;
FConsumer := TMycGenericConsumer<T>.Create(Self, Consume); FConsumer := TMycGenericConsumer<T>.Create(Self, Consume);
FTag := FProducer.Link(FConsumer);
end; end;
destructor TMycDataEndpoint<T>.Destroy; destructor TMycDataEndpoint<T>.Destroy;
begin begin
FProducer.Unlink(FTag);
FConsumer.Free; FConsumer.Free;
inherited; inherited;
end; end;
@@ -524,6 +509,15 @@ begin
end; end;
end; end;
class function TMycDataEndpoint<T>.CreateDataEndpoint(Lookback: Integer; out Series: TLazy<TSeries<T>>): IConsumer<T>;
var
endPoint: TMycDataEndpoint<T>;
begin
endPoint := TMycDataEndpoint<T>.Create(Lookback);
Result := endPoint.FConsumer;
Series := endPoint;
end;
function TMycDataEndpoint<T>.Update(var Value: TSeries<T>): Boolean; function TMycDataEndpoint<T>.Update(var Value: TSeries<T>): Boolean;
begin begin
FLock.BeginWrite; FLock.BeginWrite;
@@ -801,11 +795,11 @@ end;
{ TMycDataJoin<T> } { TMycDataJoin<T> }
constructor TMycDataJoin<T>.Create(const AProducers: TArray<TProducer<T>>); constructor TMycDataJoin<T>.Create(ACount: Integer);
begin begin
inherited Create; inherited Create;
SetLength(FConsumers, Length(AProducers)); SetLength(FConsumers, ACount);
FLock := TSpinLock.Create(false); FLock := TSpinLock.Create(false);
FCount := Length(FConsumers); FCount := Length(FConsumers);
@@ -822,9 +816,7 @@ begin
with FConsumers[i] do with FConsumers[i] do
begin begin
Queue := TQueue<T>.Create; Queue := TQueue<T>.Create;
Producer := AProducers[i];
Consumer := TMycGenericConsumer<T>.Create(Self, cFunc(i)); Consumer := TMycGenericConsumer<T>.Create(Self, cFunc(i));
Tag := Producer.Link(Consumer);
end; end;
end; end;
@@ -833,7 +825,6 @@ begin
for var i := High(FConsumers) downto 0 do for var i := High(FConsumers) downto 0 do
with FConsumers[i] do with FConsumers[i] do
begin begin
Producer.Unlink(FConsumers[i].Tag);
Consumer.Free; Consumer.Free;
Queue.Free; Queue.Free;
end; end;
@@ -884,6 +875,11 @@ begin
FContainedProvider.Broadcast(Arr); FContainedProvider.Broadcast(Arr);
end; end;
function TMycDataJoin<T>.GetConsumers(Idx: Integer): IConsumer<T>;
begin
Result := FConsumers[Idx].Consumer;
end;
{ TMycGenericAggregator<S, T> } { TMycGenericAggregator<S, T> }
constructor TMycGenericAggregator<S, T>.Create(const AFunc: TConstFunc<S, TConverter<S, T>.TBroadcastProc, TState>); constructor TMycGenericAggregator<S, T>.Create(const AFunc: TConstFunc<S, TConverter<S, T>.TBroadcastProc, TState>);
@@ -1005,15 +1001,4 @@ begin
end; end;
constructor TMycConverter<S, T>.TConsumer.Create(AOwner: TMycConverter<S, T>);
begin
inherited Create(AOwner);
FOwner := AOwner;
end;
function TMycConverter<S, T>.TConsumer.Consume(const Value: S): TState;
begin
Result := FOwner.Consume(Value);
end;
end. end.
+80 -63
View File
@@ -17,6 +17,7 @@ type
function Consume(const Value: T): TState; function Consume(const Value: T): TState;
end; end;
// A producer generates data and distributes it to linked consumers.
IProducer<T> = interface IProducer<T> = interface
function Link(const Consumer: IConsumer<T>): TTag; function Link(const Consumer: IConsumer<T>): TTag;
procedure Unlink(Tag: TTag); procedure Unlink(Tag: TTag);
@@ -30,17 +31,18 @@ type
property Consumer: IConsumer<S> read GetConsumer; property Consumer: IConsumer<S> read GetConsumer;
end; end;
// Interface helper for IProducer providing the null object pattern. // Interface helper for IProducer providing the null object pattern nad subscriptions for linked consumers.
TProducer<T> = record TProducer<T> = record
public public
type type
TSubscription = record TSubscription = record
private private
FProducer: IProducer<T>; FOwner: IProducer<T>;
FTag: TTag; FTag: TTag;
public public
procedure Unlink; procedure Unlink;
property Producer: IProducer<T> read FProducer; class operator Implicit(A: TSubscription): TProducer<T>; overload;
property Owner: IProducer<T> read FOwner;
end; end;
private private
FProducer: IProducer<T>; FProducer: IProducer<T>;
@@ -54,17 +56,14 @@ type
class operator Implicit(const A: IProducer<T>): TProducer<T>; overload; class operator Implicit(const A: IProducer<T>): TProducer<T>; overload;
class operator Implicit(const A: TProducer<T>): IProducer<T>; overload; class operator Implicit(const A: TProducer<T>): IProducer<T>; overload;
function Link(const Consumer: IConsumer<T>): TSubscription; inline; function CreateLink(const Consumer: IConsumer<T>): TSubscription; inline;
function CreateEndpoint(Lookback: Int64; out Series: TLazy<TSeries<T>>): TSubscription;
function CreateSequence(Count: Integer; out Seq: TArray<IProducer<T>>): TSubscription; overload; experimental;
// Chain consumers
function Chain(const Next: IConsumer<T>): IConsumer<T>; overload; inline; function Chain(const Next: IConsumer<T>): IConsumer<T>; overload; inline;
function Chain<R>(const Next: IConverter<T, R>): TProducer<R>; overload; inline; function Chain<R>(const Next: IConverter<T, R>): TProducer<R>; overload; inline;
function Chain<R>(const Func: TConstFunc<T, R>): TProducer<R>; overload; inline; function Chain<R>(const Func: TConstFunc<T, R>): TProducer<R>; overload; inline;
function CreateEndpoint(Lookback: Int64): TLazy<TSeries<T>>;
function CreateSequence(Count: Integer): TArray<IProducer<T>>; overload; experimental;
// Extracts the field of a record by it's name (using RTTI). // Extracts the field of a record by it's name (using RTTI).
function Field<R>(const FieldName: String): TProducer<R>; inline; function Field<R>(const FieldName: String): TProducer<R>; inline;
@@ -93,7 +92,8 @@ type
class operator Implicit(const A: TConverter<S, T>): IConverter<S, T>; overload; class operator Implicit(const A: TConverter<S, T>): IConverter<S, T>; overload;
class function Construct(const Consumer: IConsumer<S>; const Producer: TProducer<T>): TConverter<S, T>; static; class function Construct(const Consumer: IConsumer<S>; const Producer: TProducer<T>): TConverter<S, T>; static;
class function CreateGeneric(const Func: TConstFunc<S, T>): TConverter<S, T>; static;
class function CreateConverter(const Func: TConstFunc<S, T>): TConverter<S, T>; static;
class function CreateAggregation(const Func: TConstFunc<S, TBroadcastProc, TState>): TConverter<S, T>; static; class function CreateAggregation(const Func: TConstFunc<S, TBroadcastProc, TState>): TConverter<S, T>; static;
property Consumer: IConsumer<S> read GetConsumer; property Consumer: IConsumer<S> read GetConsumer;
@@ -102,20 +102,21 @@ type
class property Null: IConverter<S, T> read GetNull; class property Null: IConverter<S, T> read GetNull;
end; end;
TDataRecordLayoutHelper = record helper for TDataRecord.TLayout
function FieldAsRecord<T>(const Name: String): TConverter<T, TDataRecord>;
function FieldOfRecord<T>(const Name: String): TConverter<TDataRecord, T>;
end;
// Factory for creating specific converter instances. // Factory for creating specific converter instances.
TConverter = record TConverter = record
class function CreateCounter<T>: TConverter<T, Int64>; static; class function CreateCounter<T>: TConverter<T, Int64>; static;
class function CreateTicker<T>: TConverter<TArray<T>, T>; static; class function CreateTicker<T>: TConverter<TArray<T>, T>; static;
class function CreateRecordField<S, T>(const FieldName: String): TConverter<S, T>; static;
class function CreateIdentity<T>: TConverter<T, T>; static; class function CreateIdentity<T>: TConverter<T, T>; static;
class function CreateTickAggregation(Timeframe: TTimeframe): TConverter<TDataPoint<Double>, TDataPoint<TOhlcItem>>; static; class function CreateTickAggregation(Timeframe: TTimeframe): TConverter<TDataPoint<Double>, TDataPoint<TOhlcItem>>; static;
class function CreateOhlcAggregation(Timeframe: TTimeframe): TConverter<TDataPoint<TOhlcItem>, TDataPoint<TOhlcItem>>; static; class function CreateOhlcAggregation(Timeframe: TTimeframe): TConverter<TDataPoint<TOhlcItem>, TDataPoint<TOhlcItem>>; static;
class function CreateDataPointConverter<S, T>(const Func: TConstFunc<S, T>): TConverter<TDataPoint<S>, TDataPoint<T>>; static; class function CreateEndpoint<T>(Lookback: Int64; out Series: TLazy<TSeries<T>>): IConsumer<T>; static;
class function FieldToRecord<T>(const Layout: TDataRecord.TLayout; const Name: String): TConverter<T, TDataRecord>; static;
class function FieldOfRecord<T>(const Layout: TDataRecord.TLayout; const Name: String): TConverter<TDataRecord, T>; static;
class function Join<T>(const Producers: TArray<TProducer<T>>): TProducer<TArray<T>>; static; class function Join<T>(const Producers: TArray<TProducer<T>>): TProducer<TArray<T>>; static;
@@ -139,6 +140,13 @@ type
): TConverter<TArray<TDataRecord>, TDataRecord>; static; ): TConverter<TArray<TDataRecord>, TDataRecord>; static;
end; end;
TDataRecordHelper = record helper for TArray<TProducer<TDataRecord>>
function JoinRecords(
const TargetLayout: TDataRecord.TLayout;
const Mapping: TArray<TConverter.TRecordMapping>
): TProducer<TDataRecord>; experimental;
end;
implementation implementation
uses uses
@@ -150,11 +158,16 @@ procedure TProducer<T>.TSubscription.Unlink;
begin begin
if FTag <> nil then if FTag <> nil then
begin begin
FProducer.Unlink(FTag); FOwner.Unlink(FTag);
FTag := nil; FTag := nil;
end; end;
end; end;
class operator TProducer<T>.TSubscription.Implicit(A: TSubscription): TProducer<T>;
begin
Result := A.FOwner;
end;
constructor TProducer<T>.Create(const AProducer: IProducer<T>); constructor TProducer<T>.Create(const AProducer: IProducer<T>);
begin begin
FProducer := AProducer; FProducer := AProducer;
@@ -170,7 +183,6 @@ end;
function TProducer<T>.Chain<R>(const Next: IConverter<T, R>): TProducer<R>; function TProducer<T>.Chain<R>(const Next: IConverter<T, R>): TProducer<R>;
begin begin
// REFACTOR: Link to the consumer part, return the converter as the new producer
FProducer.Link(Next.Consumer); FProducer.Link(Next.Consumer);
Result := Next; Result := Next;
end; end;
@@ -180,14 +192,14 @@ begin
Result := Chain<R>(TMycGenericConverter<T, R>.Create(Func)); Result := Chain<R>(TMycGenericConverter<T, R>.Create(Func));
end; end;
function TProducer<T>.CreateEndpoint(Lookback: Int64): TLazy<TSeries<T>>; function TProducer<T>.CreateEndpoint(Lookback: Int64; out Series: TLazy<TSeries<T>>): TSubscription;
begin begin
Result := TMycDataEndpoint<T>.Create(FProducer, Lookback); Result := CreateLink(TMycDataEndpoint<T>.CreateDataEndpoint(Lookback, Series));
end; end;
function TProducer<T>.CreateSequence(Count: Integer): TArray<IProducer<T>>; function TProducer<T>.CreateSequence(Count: Integer; out Seq: TArray<IProducer<T>>): TSubscription;
begin begin
FProducer.Link(TMycSequence<T>.CreateSequence(Count, Result)); Result := CreateLink(TMycSequence<T>.CreateSequence(Count, Seq));
end; end;
function TProducer<T>.Field<R>(const FieldName: String): TProducer<R>; function TProducer<T>.Field<R>(const FieldName: String): TProducer<R>;
@@ -215,9 +227,9 @@ begin
Result := A.FProducer; Result := A.FProducer;
end; end;
function TProducer<T>.Link(const Consumer: IConsumer<T>): TSubscription; function TProducer<T>.CreateLink(const Consumer: IConsumer<T>): TSubscription;
begin begin
Result.FProducer := FProducer; Result.FOwner := FProducer;
Result.FTag := FProducer.Link(Consumer); Result.FTag := FProducer.Link(Consumer);
end; end;
@@ -243,7 +255,7 @@ begin
Result := TMycGenericAggregator<S, T>.Create(Func); Result := TMycGenericAggregator<S, T>.Create(Func);
end; end;
class function TConverter<S, T>.CreateGeneric(const Func: TConstFunc<S, T>): TConverter<S, T>; class function TConverter<S, T>.CreateConverter(const Func: TConstFunc<S, T>): TConverter<S, T>;
begin begin
Result := TMycGenericConverter<S, T>.Create(Func); Result := TMycGenericConverter<S, T>.Create(Func);
end; end;
@@ -290,17 +302,9 @@ begin
Result := TMycDataCounter<T>.Create; Result := TMycDataCounter<T>.Create;
end; end;
class function TConverter.CreateDataPointConverter<S, T>(const Func: TConstFunc<S, T>): TConverter<TDataPoint<S>, TDataPoint<T>>; class function TConverter.CreateEndpoint<T>(Lookback: Int64; out Series: TLazy<TSeries<T>>): IConsumer<T>;
begin begin
var cFunc: TConstFunc<S, T> := Func; Result := TMycDataEndpoint<T>.CreateDataEndpoint(Lookback, Series);
Result :=
TMycGenericConverter<TDataPoint<S>, TDataPoint<T>>.Create(
function(const Value: TDataPoint<S>): TDataPoint<T>
begin
Result.Time := Value.Time;
Result.Data := cFunc(Value.Data);
end
);
end; end;
class function TConverter.CreateIdentity<T>: TConverter<T, T>; class function TConverter.CreateIdentity<T>: TConverter<T, T>;
@@ -308,11 +312,6 @@ begin
Result := TMycIdentityConverter<T>.Create; Result := TMycIdentityConverter<T>.Create;
end; end;
class function TConverter.CreateRecordField<S, T>(const FieldName: String): TConverter<S, T>;
begin
Result := TMycRecordFieldReader<S, T>.Create(FieldName);
end;
class function TConverter.CreateTicker<T>: TConverter<TArray<T>, T>; class function TConverter.CreateTicker<T>: TConverter<TArray<T>, T>;
begin begin
Result := TMycTicker<T>.Create; Result := TMycTicker<T>.Create;
@@ -344,7 +343,7 @@ begin
end; end;
Result := Result :=
TConverter<TArray<TDataRecord>, TDataRecord>.CreateGeneric( TConverter<TArray<TDataRecord>, TDataRecord>.CreateConverter(
function(const Inputs: TArray<TDataRecord>): TDataRecord function(const Inputs: TArray<TDataRecord>): TDataRecord
begin begin
Result := TDataRecord.Create(Output); Result := TDataRecord.Create(Output);
@@ -355,28 +354,15 @@ begin
); );
end; end;
class function TConverter.FieldOfRecord<T>(const Layout: TDataRecord.TLayout; const Name: String): TConverter<TDataRecord, T>;
begin
var idx := Layout.IndexOf(Name);
Result := TConverter<TDataRecord, T>.CreateGeneric(function(const Value: TDataRecord): T begin Value.GetValue(idx, Result); end);
end;
class function TConverter.FieldToRecord<T>(const Layout: TDataRecord.TLayout; const Name: String): TConverter<T, TDataRecord>;
begin
var idx := Layout.IndexOf(Name);
Result :=
TConverter<T, TDataRecord>.CreateGeneric(
function(const Value: T): TDataRecord
begin
Result := TDataRecord.Create(Layout);
Result.SetValue(idx, Value);
end
);
end;
class function TConverter.Join<T>(const Producers: TArray<TProducer<T>>): TProducer<TArray<T>>; class function TConverter.Join<T>(const Producers: TArray<TProducer<T>>): TProducer<TArray<T>>;
var
joiner: TMycDataJoin<T>;
begin begin
Result := TMycDataJoin<T>.Create(Producers); joiner := TMycDataJoin<T>.Create(Length(Producers));
Result := joiner;
for var i := 0 to High(Producers) do
Producers[i].Chain(joiner.Consumers[i]);
end; end;
class function TConverter.JoinRecords( class function TConverter.JoinRecords(
@@ -389,8 +375,39 @@ begin
Result := DataMapping(Mapping, TargetLayout); Result := DataMapping(Mapping, TargetLayout);
// REFACTOR: Link to the consumer part of the mapping converter. RecProvider.Chain(Result.Consumer);
RecProvider.Link(Result.Consumer); end;
function TDataRecordLayoutHelper.FieldAsRecord<T>(const Name: String): TConverter<T, TDataRecord>;
begin
var layout := Self;
var idx := IndexOf(Name);
Result :=
TConverter<T, TDataRecord>.CreateConverter(
function(const Value: T): TDataRecord
begin
Result := TDataRecord.Create(layout);
Result.SetValue(idx, Value);
end
);
end;
function TDataRecordLayoutHelper.FieldOfRecord<T>(const Name: String): TConverter<TDataRecord, T>;
begin
var idx := IndexOf(Name);
Result := TConverter<TDataRecord, T>.CreateConverter(function(const Value: TDataRecord): T begin Value.GetValue(idx, Result); end);
end;
function TDataRecordHelper.JoinRecords(
const TargetLayout: TDataRecord.TLayout;
const Mapping: TArray<TConverter.TRecordMapping>
): TProducer<TDataRecord>;
begin
var map := TConverter.DataMapping(Mapping, TargetLayout);
TConverter.Join<TDataRecord>(Self).Chain(map.Consumer);
Result := map.Producer;
end; end;
end. end.