This commit is contained in:
Michael Schimmel
2025-10-31 18:12:53 +01:00
parent 0526ec8a24
commit 8abec8e98f
8 changed files with 199 additions and 106 deletions
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -769,7 +769,7 @@ var
ast, callAst: IAstNode; ast, callAst: IAstNode;
resultValue: TDataValue; resultValue: TDataValue;
series: TScalarRecordSeries; series: TScalarRecordSeries;
recordDef: TScalarRecordDefinition; recordDef: IScalarRecordDefinition;
i: Integer; i: Integer;
scope: IExecutionScope; scope: IExecutionScope;
values: TArray<TScalar.TValue>; values: TArray<TScalar.TValue>;
+7 -7
View File
@@ -155,10 +155,10 @@ type
TBoundRecordLiteralNode = class(TRecordLiteralNode) TBoundRecordLiteralNode = class(TRecordLiteralNode)
private private
FDefinition: TScalarRecordDefinition; FDefinition: IScalarRecordDefinition;
public public
constructor Create(const AFields: TArray<TRecordFieldLiteral>; const ADef: TScalarRecordDefinition); constructor Create(const AFields: TArray<TRecordFieldLiteral>; const ADef: IScalarRecordDefinition);
property Definition: TScalarRecordDefinition read FDefinition; property Definition: IScalarRecordDefinition read FDefinition;
end; end;
implementation implementation
@@ -345,7 +345,7 @@ begin
end; end;
{ TBoundRecordLiteralNode } { TBoundRecordLiteralNode }
constructor TBoundRecordLiteralNode.Create(const AFields: TArray<TRecordFieldLiteral>; const ADef: TScalarRecordDefinition); constructor TBoundRecordLiteralNode.Create(const AFields: TArray<TRecordFieldLiteral>; const ADef: IScalarRecordDefinition);
begin begin
inherited Create(AFields); inherited Create(AFields);
FDefinition := ADef; FDefinition := ADef;
@@ -960,8 +960,8 @@ function TAstBinder.VisitRecordLiteral(const Node: IRecordLiteralNode): TDataVal
var var
i: Integer; i: Integer;
boundFields: TArray<TRecordFieldLiteral>; boundFields: TArray<TRecordFieldLiteral>;
defFields: TArray<TScalarRecordDefinition.TField>; defFields: TArray<TScalarRecordField>;
def: TScalarRecordDefinition; def: IScalarRecordDefinition;
staticType: IStaticType; staticType: IStaticType;
boundNode: IRecordLiteralNode; boundNode: IRecordLiteralNode;
valNode: IAstNode; valNode: IAstNode;
@@ -997,7 +997,7 @@ begin
defFields[i] := TScalarRecordField.Create(Node.Fields[i].Key.Value, scalarKind); defFields[i] := TScalarRecordField.Create(Node.Fields[i].Key.Value, scalarKind);
end; end;
def := TScalarRecordDefinition.Create(defFields); def := TScalarRecordRegistry.Intern(defFields);
staticType := TTypes.CreateRecord(def); staticType := TTypes.CreateRecord(def);
boundNode := TBoundRecordLiteralNode.Create(boundFields, def); boundNode := TBoundRecordLiteralNode.Create(boundFields, def);
+2 -10
View File
@@ -91,7 +91,7 @@ end;
function NativeCreateRecordSeries(const Args: TArray<TDataValue>): TDataValue; function NativeCreateRecordSeries(const Args: TArray<TDataValue>): TDataValue;
var var
jsonDef: string; jsonDef: string;
recordDef: TScalarRecordDefinition; recordDef: IScalarRecordDefinition;
series: IRecordSeries; series: IRecordSeries;
begin begin
if (Length(Args) <> 1) or (Args[0].Kind <> vkText) then if (Length(Args) <> 1) or (Args[0].Kind <> vkText) then
@@ -421,14 +421,6 @@ begin
Result := Items[Integer(index)]; Result := Items[Integer(index)];
end; end;
end; end;
vkRecordSeries:
begin
var series := baseValue.AsRecordSeries;
if (index < 0) or (index >= series.TotalCount) then
raise EArgumentException.CreateFmt('Index %d is out of bounds for series with %d elements.', [index, series.TotalCount]);
var recordValue := series.Items[Integer(index)];
Result := TDataValue.FromRecord(recordValue);
end;
else else
raise EArgumentException.Create('Indexer `[]` is not supported for this value type.'); raise EArgumentException.Create('Indexer `[]` is not supported for this value type.');
end; end;
@@ -441,7 +433,7 @@ begin
baseValue := Node.Base.Accept(Self); baseValue := Node.Base.Accept(Self);
case baseValue.Kind of case baseValue.Kind of
vkRecordSeries: Result := TDataValue.FromSeries(baseValue.AsRecordSeries.CreateMemberSeries(Node.Member.Value)); vkRecordSeries: Result := TDataValue.FromSeries(baseValue.AsRecordSeries[Node.Member.Value]);
vkRecord: Result := baseValue.AsRecord[Node.Member.Value]; vkRecord: Result := baseValue.AsRecord[Node.Member.Value];
else else
raise EArgumentException.Create('Member access operator `.` is not supported for this value type.'); raise EArgumentException.Create('Member access operator `.` is not supported for this value type.');
+14 -14
View File
@@ -46,7 +46,7 @@ type
function GetKind: TStaticTypeKind; function GetKind: TStaticTypeKind;
function GetElementType: IStaticType; function GetElementType: IStaticType;
function GetSignature: IMethodSignature; function GetSignature: IMethodSignature;
function GetDefinition: TScalarRecordDefinition; function GetDefinition: IScalarRecordDefinition;
{$endregion} {$endregion}
// The kind of type (e.g., Ordinal, Series, etc.) // The kind of type (e.g., Ordinal, Series, etc.)
@@ -56,7 +56,7 @@ type
// The signature (if Kind = stMethod) // The signature (if Kind = stMethod)
property Signature: IMethodSignature read GetSignature; property Signature: IMethodSignature read GetSignature;
// The definition (if Kind = stRecord or stRecordSeries) // The definition (if Kind = stRecord or stRecordSeries)
property Definition: TScalarRecordDefinition read GetDefinition; property Definition: IScalarRecordDefinition read GetDefinition;
// Checks for type equality // Checks for type equality
function IsEqual(const Other: IStaticType): Boolean; function IsEqual(const Other: IStaticType): Boolean;
@@ -94,8 +94,8 @@ type
// Factory functions for complex types // Factory functions for complex types
class function CreateSeries(const AElementType: IStaticType): IStaticType; static; class function CreateSeries(const AElementType: IStaticType): IStaticType; static;
class function CreateMethod(const AParamTypes: TArray<IStaticType>; const AReturnType: IStaticType): IStaticType; static; class function CreateMethod(const AParamTypes: TArray<IStaticType>; const AReturnType: IStaticType): IStaticType; static;
class function CreateRecord(const ADef: TScalarRecordDefinition): IStaticType; static; class function CreateRecord(const ADef: IScalarRecordDefinition): IStaticType; static;
class function CreateRecordSeries(const ADef: TScalarRecordDefinition): IStaticType; static; class function CreateRecordSeries(const ADef: IScalarRecordDefinition): IStaticType; static;
class function FromScalarKind(AKind: TScalar.TKind): IStaticType; static; class function FromScalarKind(AKind: TScalar.TKind): IStaticType; static;
end; end;
@@ -148,7 +148,7 @@ type
function GetKind: TStaticTypeKind; virtual; abstract; function GetKind: TStaticTypeKind; virtual; abstract;
function GetElementType: IStaticType; virtual; function GetElementType: IStaticType; virtual;
function GetSignature: IMethodSignature; virtual; function GetSignature: IMethodSignature; virtual;
function GetDefinition: TScalarRecordDefinition; virtual; function GetDefinition: IScalarRecordDefinition; virtual;
function IsEqual(const Other: IStaticType): Boolean; virtual; function IsEqual(const Other: IStaticType): Boolean; virtual;
function ToString: string; override; function ToString: string; override;
end; end;
@@ -163,10 +163,10 @@ begin
Result := nil; Result := nil;
end; end;
function TAbstractStaticType.GetDefinition: TScalarRecordDefinition; function TAbstractStaticType.GetDefinition: IScalarRecordDefinition;
begin begin
// Return an empty/invalid definition // Return an empty/invalid definition
Result := Default(TScalarRecordDefinition); Result := Default(IScalarRecordDefinition);
end; end;
function TAbstractStaticType.IsEqual(const Other: IStaticType): Boolean; function TAbstractStaticType.IsEqual(const Other: IStaticType): Boolean;
@@ -346,16 +346,16 @@ type
TRecordType = class(TAbstractStaticType) TRecordType = class(TAbstractStaticType)
private private
FKind: TStaticTypeKind; FKind: TStaticTypeKind;
FDefinition: TScalarRecordDefinition; FDefinition: IScalarRecordDefinition;
public public
constructor Create(AKind: TStaticTypeKind; ADef: TScalarRecordDefinition); constructor Create(AKind: TStaticTypeKind; ADef: IScalarRecordDefinition);
function GetKind: TStaticTypeKind; override; function GetKind: TStaticTypeKind; override;
function GetDefinition: TScalarRecordDefinition; override; function GetDefinition: IScalarRecordDefinition; override;
function IsEqual(const Other: IStaticType): Boolean; override; function IsEqual(const Other: IStaticType): Boolean; override;
function ToString: string; override; function ToString: string; override;
end; end;
constructor TRecordType.Create(AKind: TStaticTypeKind; ADef: TScalarRecordDefinition); constructor TRecordType.Create(AKind: TStaticTypeKind; ADef: IScalarRecordDefinition);
begin begin
inherited Create; inherited Create;
Assert(AKind in [stRecord, stRecordSeries]); Assert(AKind in [stRecord, stRecordSeries]);
@@ -368,7 +368,7 @@ begin
Result := FKind; Result := FKind;
end; end;
function TRecordType.GetDefinition: TScalarRecordDefinition; function TRecordType.GetDefinition: IScalarRecordDefinition;
begin begin
Result := FDefinition; Result := FDefinition;
end; end;
@@ -426,12 +426,12 @@ begin
Result := TMethodType.Create(AParamTypes, AReturnType); Result := TMethodType.Create(AParamTypes, AReturnType);
end; end;
class function TTypes.CreateRecord(const ADef: TScalarRecordDefinition): IStaticType; class function TTypes.CreateRecord(const ADef: IScalarRecordDefinition): IStaticType;
begin begin
Result := TRecordType.Create(stRecord, ADef); Result := TRecordType.Create(stRecord, ADef);
end; end;
class function TTypes.CreateRecordSeries(const ADef: TScalarRecordDefinition): IStaticType; class function TTypes.CreateRecordSeries(const ADef: IScalarRecordDefinition): IStaticType;
begin begin
Result := TRecordType.Create(stRecordSeries, ADef); Result := TRecordType.Create(stRecordSeries, ADef);
end; end;
+105 -23
View File
@@ -10,7 +10,7 @@ uses
type type
// The runtime representation of an interned keyword // The runtime representation of an interned keyword
IKeyword = interface(IInterface) IKeyword = interface
function GetIdx: Integer; function GetIdx: Integer;
function GetName: string; function GetName: string;
property Idx: Integer read GetIdx; property Idx: Integer read GetIdx;
@@ -42,26 +42,52 @@ type
class function Intern(const AName: string): IKeyword; static; class function Intern(const AName: string): IKeyword; static;
end; end;
TKeywordMapping<T> = record // Defines a mapping from Keywords to a generic value T
type IKeywordMapping<T> = interface
TField = record {$region 'private'}
Key: IKeyword; function GetFields: TArray<TPair<IKeyword, T>>;
Value: T; {$endregion}
public // Finds the 0-based index for a given keyword. Returns -1 if not found.
constructor Create(const AKey: IKeyword; const AValue: T); function IndexOf(const Key: IKeyword): Integer;
// Gets all fields in the mapping.
property Fields: TArray<TPair<IKeyword, T>> read GetFields;
end; end;
// Factory for creating IKeywordMapping instances
TKeywordMappingRegistry<T> = record
strict private
// Implementation class for IKeywordMapping
type
TKeywordMapping = class(TInterfacedObject, IKeywordMapping<T>)
private private
FMap: TArray<Integer>; FMap: TArray<Integer>;
FFields: TArray<TField>; FFields: TArray<TPair<IKeyword, T>>;
FFirst, FLast: Integer; FFirst, FLast: Integer;
function GetFields: TArray<TPair<IKeyword, T>>;
public public
constructor Create(const AFields: TArray<TField>); // Expects AFields to be in the desired (canonical) order
constructor Create(const AFields: TArray<TPair<IKeyword, T>>);
function IndexOf(const Key: IKeyword): Integer; function IndexOf(const Key: IKeyword): Integer;
property Fields: TArray<TField> read FFields; end;
strict private
// Cache implementation
class var
FLock: TSpinLock;
class var
FRegistry: TDictionary<TArray<Integer>, IKeywordMapping<T>>;
class constructor Create;
class destructor Destroy;
public
// Creates or gets the cached keyword mapping.
class function Intern(const AFields: TArray<TPair<IKeyword, T>>): IKeywordMapping<T>; static;
end; end;
implementation implementation
uses
System.Generics.Defaults; // For TComparer<Integer>
{ TKeywordRegistry.TKeyword } { TKeywordRegistry.TKeyword }
constructor TKeywordRegistry.TKeyword.Create(const AName: string; AIdx: Integer); constructor TKeywordRegistry.TKeyword.Create(const AName: string; AIdx: Integer);
@@ -108,21 +134,25 @@ begin
end; end;
end; end;
{ TKeywordMapping } { TKeywordMappingRegistry<T>.TKeywordMapping<T> }
constructor TKeywordMapping<T>.Create(const AFields: TArray<TField>); constructor TKeywordMappingRegistry<T>.TKeywordMapping.Create(const AFields: TArray<TPair<IKeyword, T>>);
var
i, idx: Integer;
begin begin
inherited Create;
FFields := AFields; FFields := AFields;
FFirst := 0; FFirst := 0;
FLast := -1; FLast := -1;
if Length(FFields) = 0 then if Length(FFields) = 0 then
exit; exit;
// Find min/max Idx for the map. Fields are *not* assumed to be sorted.
FFirst := FFields[0].Key.Idx; FFirst := FFields[0].Key.Idx;
FLast := FFirst; FLast := FFirst;
for var i := 1 to High(FFields) do for i := 1 to High(FFields) do
begin begin
var idx := FFields[i].Key.Idx; idx := FFields[i].Key.Idx;
if FFirst > idx then if FFirst > idx then
FFirst := idx; FFirst := idx;
if FLast < idx then if FLast < idx then
@@ -130,25 +160,77 @@ begin
end; end;
SetLength(FMap, FLast - FFirst + 1); SetLength(FMap, FLast - FFirst + 1);
for var i := 0 to High(FMap) do for i := 0 to High(FMap) do
FMap[i] := -1; FMap[i] := -1;
for var i := 0 to High(FFields) do
FMap[FFirst + FFields[i].Key.Idx] := i; // Populate map based on the *original field order*
for i := 0 to High(FFields) do
FMap[FFields[i].Key.Idx - FFirst] := i;
end; end;
function TKeywordMapping<T>.IndexOf(const Key: IKeyword): Integer; function TKeywordMappingRegistry<T>.TKeywordMapping.GetFields: TArray<TPair<IKeyword, T>>;
begin begin
var idx := Key.Idx - FFirst; Result := FFields;
end;
function TKeywordMappingRegistry<T>.TKeywordMapping.IndexOf(const Key: IKeyword): Integer;
var
idx: Integer;
begin
idx := Key.Idx - FFirst;
if (idx < 0) or (Idx > High(FMap)) then if (idx < 0) or (Idx > High(FMap)) then
exit(-1); exit(-1);
Result := FMap[idx]; Result := FMap[idx];
end; end;
constructor TKeywordMapping<T>.TField.Create(const AKey: IKeyword; const AValue: T); { TKeywordMappingRegistry<T> }
class constructor TKeywordMappingRegistry<T>.Create;
begin begin
Key := AKey; FLock := TSpinLock.Create(false);
Value := AValue; FRegistry := TDictionary<TArray<Integer>, IKeywordMapping<T>>.Create;
end;
class destructor TKeywordMappingRegistry<T>.Destroy;
begin
FRegistry.Free;
end;
class function TKeywordMappingRegistry<T>.Intern(const AFields: TArray<TPair<IKeyword, T>>): IKeywordMapping<T>;
var
key: TArray<Integer>;
begin
SetLength(key, Length(AFields));
for var i := 0 to High(key) do
key[i] := AFields[i].Key.Idx;
// 2. Lock
FLock.Enter;
try
// 3. Check cache
if FRegistry.TryGetValue(key, Result) then
exit; // Found it, exit
finally
FLock.Exit;
end;
// 4. Not in cache. Create (O(N+M)) - OUTSIDE lock
var newMapping := TKeywordMapping.Create(AFields) as IKeywordMapping<T>;
// 5. Lock again to add
FLock.Enter;
try
// 6. Check again (double-check)
if FRegistry.TryGetValue(key, Result) then
exit; // Another thread won the race
// 7. Add our new mapping.
Result := newMapping;
FRegistry.Add(key, Result);
finally
FLock.Exit;
end;
end; end;
end. end.
+3 -3
View File
@@ -18,7 +18,7 @@ type
// Creates a JSON definition string from a record type info. // Creates a JSON definition string from a record type info.
class function RecordDefinitionToJson<T: record>: string; static; class function RecordDefinitionToJson<T: record>: string; static;
// Creates a record definition from a JSON string. // Creates a record definition from a JSON string.
class function JsonToRecordDefinition(const AJson: string): TScalarRecordDefinition; static; class function JsonToRecordDefinition(const AJson: string): IScalarRecordDefinition; static;
end; end;
implementation implementation
@@ -30,7 +30,7 @@ uses
{ TRttiAstHelper } { TRttiAstHelper }
class function TRttiAstHelper.JsonToRecordDefinition(const AJson: string): TScalarRecordDefinition; class function TRttiAstHelper.JsonToRecordDefinition(const AJson: string): IScalarRecordDefinition;
var var
jsonValue: TJSONValue; jsonValue: TJSONValue;
fieldsArray: TJSONArray; fieldsArray: TJSONArray;
@@ -63,7 +63,7 @@ begin
fields[i] := TScalarRecordField.Create(fieldKey, TScalar.TKind(GetEnumValue(TypeInfo(TScalar.TKind), kindStr))); fields[i] := TScalarRecordField.Create(fieldKey, TScalar.TKind(GetEnumValue(TypeInfo(TScalar.TKind), kindStr)));
end; end;
Result := TScalarRecordDefinition.Create(fields); Result := TScalarRecordRegistry.Intern(fields);
finally finally
jsonValue.Free; jsonValue.Free;
end; end;
+61 -42
View File
@@ -4,6 +4,8 @@ interface
uses uses
System.SysUtils, System.SysUtils,
System.Generics.Collections,
System.Generics.Defaults,
Myc.Data.Decimal, Myc.Data.Decimal,
Myc.Data.Series, Myc.Data.Series,
Myc.Data.Keyword; Myc.Data.Keyword;
@@ -92,21 +94,22 @@ type
TScalarTuple = TArray<TScalar>; TScalarTuple = TArray<TScalar>;
// A field definition for a scalar record. // A field definition for a scalar record.
TScalarRecordField = TKeywordMapping<TScalar.TKind>.TField; TScalarRecordField = TPair<IKeyword, TScalar.TKind>;
TScalarRecordDefinition = TKeywordMapping<TScalar.TKind>; IScalarRecordDefinition = IKeywordMapping<TScalar.TKind>;
TScalarRecordRegistry = TKeywordMappingRegistry<TScalar.TKind>;
// A record of scalar values, based on a definition. // A record of scalar values, based on a definition.
TScalarRecord = record TScalarRecord = record
public public
constructor Create(const ADef: TScalarRecordDefinition; const AFields: TArray<TScalar.TValue>); constructor Create(const ADef: IScalarRecordDefinition; const AFields: TArray<TScalar.TValue>);
function GetDef: TScalarRecordDefinition; inline; function GetDef: IScalarRecordDefinition; inline;
function GetFields: TArray<TScalar.TValue>; inline; function GetFields: TArray<TScalar.TValue>; inline;
function GetKeys(const Key: IKeyword): TScalar; function GetKeys(const Key: IKeyword): TScalar;
property Def: TScalarRecordDefinition read GetDef; property Def: IScalarRecordDefinition read GetDef;
property Fields: TArray<TScalar.TValue> read GetFields; property Fields: TArray<TScalar.TValue> read GetFields;
property Keys[const Key: IKeyword]: TScalar read GetKeys; default; property Keys[const Key: IKeyword]: TScalar read GetKeys; default;
strict private strict private
FDef: TScalarRecordDefinition; FDef: IScalarRecordDefinition;
FFields: TArray<TScalar.TValue>; FFields: TArray<TScalar.TValue>;
end; end;
@@ -142,22 +145,21 @@ type
IRecordSeries = interface IRecordSeries = interface
{$region 'private'} {$region 'private'}
function GetCount: Int64; function GetCount: Int64;
function GetDef: TScalarRecordDefinition; function GetDef: IScalarRecordDefinition;
function GetItems(Idx: Integer): TScalarRecord;
function GetTotalCount: Int64; function GetTotalCount: Int64;
function GetFields(const Key: IKeyword): ISeries;
{$endregion} {$endregion}
procedure Add(const Item: TScalarRecord; Lookback: Int64 = -1); procedure Add(const Item: TScalarRecord; Lookback: Int64 = -1);
function CreateMemberSeries(const Key: IKeyword): ISeries;
property Count: Int64 read GetCount; property Count: Int64 read GetCount;
property Def: TScalarRecordDefinition read GetDef; property Def: IScalarRecordDefinition read GetDef;
property Items[Idx: Integer]: TScalarRecord read GetItems; default; property Fields[const Key: IKeyword]: ISeries read GetFields; default;
property TotalCount: Int64 read GetTotalCount; property TotalCount: Int64 read GetTotalCount;
end; end;
// A time series of scalar records, optimized for memory and access speed. // A time series of scalar records, optimized for memory and access speed.
TScalarRecordSeries = class(TInterfacedObject, IRecordSeries) TScalarRecordSeries = class(TInterfacedObject, IRecordSeries)
type type
TMemberSeries = class(TInterfacedObject, ISeries) TMemberSeries = class(TObject, ISeries)
private private
FRecordSeries: TScalarRecordSeries; FRecordSeries: TScalarRecordSeries;
FKind: TScalar.TKind; FKind: TScalar.TKind;
@@ -165,26 +167,29 @@ type
function GetCount: Int64; function GetCount: Int64;
function GetItems(Idx: Integer): TScalar; function GetItems(Idx: Integer): TScalar;
function GetTotalCount: Int64; function GetTotalCount: Int64;
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
public public
constructor Create(ARecordSeries: TScalarRecordSeries; AElementIdx: Integer); constructor Create(ARecordSeries: TScalarRecordSeries; AElementIdx: Integer);
destructor Destroy; override;
end; end;
private private
FDef: TScalarRecordDefinition; FDef: IScalarRecordDefinition;
FArray: TChunkArray<TScalar.TValue>; FArray: TChunkArray<TScalar.TValue>;
FFields: TArray<TMemberSeries>;
FTotalCount: Int64; FTotalCount: Int64;
function GetCount: Int64; inline; function GetCount: Int64; inline;
function GetDef: TScalarRecordDefinition; inline; function GetFields(const Key: IKeyword): ISeries;
function GetItems(Idx: Integer): TScalarRecord; inline; function GetDef: IScalarRecordDefinition; inline;
function GetTotalCount: Int64; inline; function GetTotalCount: Int64; inline;
function GetItemRef(Idx: Integer): TChunkArray<TScalar.TValue>.PT; inline; function GetItemRef(Idx: Integer): TChunkArray<TScalar.TValue>.PT; inline;
public public
constructor Create(const ADef: TScalarRecordDefinition); constructor Create(const ADef: IScalarRecordDefinition);
destructor Destroy; override;
procedure Add(const Item: TScalarRecord; Lookback: Int64 = -1); procedure Add(const Item: TScalarRecord; Lookback: Int64 = -1);
function CreateMemberSeries(const Key: IKeyword): ISeries;
property Count: Int64 read GetCount; property Count: Int64 read GetCount;
property Def: TScalarRecordDefinition read GetDef; property Fields[const Key: IKeyword]: ISeries read GetFields; default;
property Items[Idx: Integer]: TScalarRecord read GetItems; default; property Def: IScalarRecordDefinition read GetDef;
property TotalCount: Int64 read GetTotalCount; property TotalCount: Int64 read GetTotalCount;
end; end;
@@ -519,14 +524,14 @@ end;
{ TScalarRecord } { TScalarRecord }
constructor TScalarRecord.Create(const ADef: TScalarRecordDefinition; const AFields: TArray<TScalar.TValue>); constructor TScalarRecord.Create(const ADef: IScalarRecordDefinition; const AFields: TArray<TScalar.TValue>);
begin begin
Assert(Length(ADef.Fields) = Length(AFields), 'Field definition and value count must match.'); Assert(Length(ADef.Fields) = Length(AFields), 'Field definition and value count must match.');
FDef := ADef; FDef := ADef;
FFields := AFields; FFields := AFields;
end; end;
function TScalarRecord.GetDef: TScalarRecordDefinition; function TScalarRecord.GetDef: IScalarRecordDefinition;
begin begin
Result := FDef; Result := FDef;
end; end;
@@ -549,11 +554,21 @@ end;
{ TScalarRecordSeries } { TScalarRecordSeries }
constructor TScalarRecordSeries.Create(const ADef: TScalarRecordDefinition); constructor TScalarRecordSeries.Create(const ADef: IScalarRecordDefinition);
begin begin
Assert(Length(ADef.Fields) > 0); Assert(Length(ADef.Fields) > 0);
FDef := ADef; FDef := ADef;
FTotalCount := 0; FTotalCount := 0;
SetLength(FFields, Length(FDef.Fields));
for var i := 0 to High(FFields) do
FFields[i] := TMemberSeries.Create(Self, i);
end;
destructor TScalarRecordSeries.Destroy;
begin
for var i := High(FFields) downto 0 do
FFields[i].Free;
inherited;
end; end;
procedure TScalarRecordSeries.Add(const Item: TScalarRecord; Lookback: Int64 = -1); procedure TScalarRecordSeries.Add(const Item: TScalarRecord; Lookback: Int64 = -1);
@@ -562,13 +577,13 @@ begin
inc(FTotalCount); inc(FTotalCount);
end; end;
function TScalarRecordSeries.CreateMemberSeries(const Key: IKeyword): ISeries; function TScalarRecordSeries.GetFields(const Key: IKeyword): ISeries;
begin begin
var elem := FDef.IndexOf(Key); var elem := FDef.IndexOf(Key);
if elem < 0 then if elem < 0 then
raise EArgumentException.CreateFmt('Field ":%s" not found in record definition.', [Key.Name]); raise EArgumentException.CreateFmt('Field ":%s" not found in record definition.', [Key.Name]);
Result := TMemberSeries.Create(Self, elem); Result := FFields[elem];
end; end;
function TScalarRecordSeries.GetCount: Int64; function TScalarRecordSeries.GetCount: Int64;
@@ -576,7 +591,7 @@ begin
Result := FArray.Count div Length(FDef.Fields); Result := FArray.Count div Length(FDef.Fields);
end; end;
function TScalarRecordSeries.GetDef: TScalarRecordDefinition; function TScalarRecordSeries.GetDef: IScalarRecordDefinition;
begin begin
Result := FDef; Result := FDef;
end; end;
@@ -587,15 +602,6 @@ begin
Result := FArray.ItemRef[(FArray.Count - len) - (Idx * len)]; Result := FArray.ItemRef[(FArray.Count - len) - (Idx * len)];
end; end;
function TScalarRecordSeries.GetItems(Idx: Integer): TScalarRecord;
var
values: TArray<TScalar.TValue>;
begin
SetLength(values, Length(FDef.Fields));
Move(GetItemRef(Idx)^, values[0], sizeof(TScalar.TValue) * Length(values));
Result.Create(FDef, values);
end;
function TScalarRecordSeries.GetTotalCount: Int64; function TScalarRecordSeries.GetTotalCount: Int64;
begin begin
Result := FTotalCount; Result := FTotalCount;
@@ -643,21 +649,16 @@ begin
end; end;
end; end;
{ TScalarRecordSeries.TMemberSeries }
constructor TScalarRecordSeries.TMemberSeries.Create(ARecordSeries: TScalarRecordSeries; AElementIdx: Integer); constructor TScalarRecordSeries.TMemberSeries.Create(ARecordSeries: TScalarRecordSeries; AElementIdx: Integer);
begin begin
inherited Create; inherited Create;
FRecordSeries := ARecordSeries; FRecordSeries := ARecordSeries;
FRecordSeries._AddRef;
FKind := FRecordSeries.FDef.Fields[AElementIdx].Value; FKind := FRecordSeries.FDef.Fields[AElementIdx].Value;
FOffset := sizeof(TScalar.TValue) * AElementIdx; FOffset := sizeof(TScalar.TValue) * AElementIdx;
end; end;
destructor TScalarRecordSeries.TMemberSeries.Destroy;
begin
FRecordSeries._Release;
inherited;
end;
function TScalarRecordSeries.TMemberSeries.GetCount: Int64; function TScalarRecordSeries.TMemberSeries.GetCount: Int64;
begin begin
Result := FRecordSeries.Count; Result := FRecordSeries.Count;
@@ -677,6 +678,24 @@ begin
Result := FRecordSeries.TotalCount; Result := FRecordSeries.TotalCount;
end; end;
function TScalarRecordSeries.TMemberSeries.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if GetInterface(IID, Obj) then
Result := S_OK
else
Result := E_NOINTERFACE;
end;
function TScalarRecordSeries.TMemberSeries._AddRef: Integer;
begin
Result := FRecordSeries._AddRef;
end;
function TScalarRecordSeries.TMemberSeries._Release: Integer;
begin
Result := FRecordSeries._Release;
end;
{ TScalarSeries } { TScalarSeries }
constructor TScalarSeries.Create(AKind: TScalar.TKind); constructor TScalarSeries.Create(AKind: TScalar.TKind);