Data Types

This commit is contained in:
Michael Schimmel
2025-08-25 15:31:15 +02:00
parent c9e28a946d
commit 42110e8471
10 changed files with 980 additions and 193 deletions
+126 -20
View File
@@ -8,12 +8,11 @@ uses
Myc.Data.Types;
type
TImplDataArrayValue = class; // fwd
// Implements the array data type.
TImplDataArrayType = class(TInterfacedObject, IDataType, IDataArrayType)
TDataArrayType = class(TInterfacedObject, IDataType, IDataArrayType)
private
FElementType: IDataType;
FEmptyValue: IDataArrayValue; // Cached empty array value for this type
function GetElementType: IDataType;
function GetName: String;
function GetKind: TDataKind;
@@ -22,8 +21,14 @@ type
function CreateValue(const AItems: array of IDataValue): IDataArrayValue;
end;
implementation
uses
System.SyncObjs;
type
// Implements the array data value.
TImplDataArrayValue = class(TInterfacedObject, IDataValue, IDataArrayValue)
TDataArrayValue = class(TInterfacedObject, IDataValue, IDataArrayValue)
private
FArrayType: IDataArrayType;
FItems: TArray<IDataValue>;
@@ -35,14 +40,34 @@ type
constructor Create(const AArrayType: IDataArrayType; const AItems: array of IDataValue);
end;
implementation
// Optimized implementation for an array with 0 elements.
TDataArrayValue0 = class(TInterfacedObject, IDataValue, IDataArrayValue)
private
FArrayType: IDataArrayType;
function GetDataType: IDataType;
function GetAsString: string;
function GetElementCount: Integer;
function GetItem(Idx: Integer): IDataValue;
public
constructor Create(const AArrayType: IDataArrayType);
end;
uses
System.SyncObjs;
// Optimized implementation for an array with 1 element.
TDataArrayValue1 = class(TInterfacedObject, IDataValue, IDataArrayValue)
private
FArrayType: IDataArrayType;
FItem0: IDataValue;
function GetDataType: IDataType;
function GetAsString: string;
function GetElementCount: Integer;
function GetItem(Idx: Integer): IDataValue;
public
constructor Create(const AArrayType: IDataArrayType; const AItems: array of IDataValue);
end;
{ TImplDataArrayValue }
{ TDataArrayValue (generic implementation for N > 1 elements) }
constructor TImplDataArrayValue.Create(const AArrayType: IDataArrayType; const AItems: array of IDataValue);
constructor TDataArrayValue.Create(const AArrayType: IDataArrayType; const AItems: array of IDataValue);
var
i: Integer;
begin
@@ -53,12 +78,12 @@ begin
FItems[i] := AItems[i];
end;
function TImplDataArrayValue.GetDataType: IDataType;
function TDataArrayValue.GetDataType: IDataType;
begin
Result := FArrayType;
end;
function TImplDataArrayValue.GetAsString: string;
function TDataArrayValue.GetAsString: string;
var
sb: TStringBuilder;
i: Integer;
@@ -79,28 +104,91 @@ begin
end;
end;
function TImplDataArrayValue.GetElementCount: Integer;
function TDataArrayValue.GetElementCount: Integer;
begin
Result := Length(FItems);
end;
function TImplDataArrayValue.GetItem(Idx: Integer): IDataValue;
function TDataArrayValue.GetItem(Idx: Integer): IDataValue;
begin
Result := FItems[Idx];
end;
{ TImplDataArrayType }
{ TDataArrayValue0 }
constructor TImplDataArrayType.Create(const AElementType: IDataType);
constructor TDataArrayValue0.Create(const AArrayType: IDataArrayType);
begin
inherited Create;
FArrayType := AArrayType;
end;
function TDataArrayValue0.GetAsString: string;
begin
Result := '[]';
end;
function TDataArrayValue0.GetDataType: IDataType;
begin
Result := FArrayType;
end;
function TDataArrayValue0.GetElementCount: Integer;
begin
Result := 0;
end;
function TDataArrayValue0.GetItem(Idx: Integer): IDataValue;
begin
raise EArgumentException.Create('Index out of bounds');
end;
{ TDataArrayValue1 }
constructor TDataArrayValue1.Create(const AArrayType: IDataArrayType; const AItems: array of IDataValue);
begin
inherited Create;
FArrayType := AArrayType;
FItem0 := AItems[0];
end;
function TDataArrayValue1.GetAsString: string;
begin
Result := '[' + FItem0.AsString + ']';
end;
function TDataArrayValue1.GetDataType: IDataType;
begin
Result := FArrayType;
end;
function TDataArrayValue1.GetElementCount: Integer;
begin
Result := 1;
end;
function TDataArrayValue1.GetItem(Idx: Integer): IDataValue;
begin
case Idx of
0: Result := FItem0;
else
raise EArgumentException.Create('Index out of bounds');
end;
end;
{ TDataArrayType }
constructor TDataArrayType.Create(const AElementType: IDataType);
begin
inherited Create;
FElementType := AElementType;
FEmptyValue := nil;
end;
function TImplDataArrayType.CreateValue(const AItems: array of IDataValue): IDataArrayValue;
function TDataArrayType.CreateValue(const AItems: array of IDataValue): IDataArrayValue;
var
item: IDataValue;
i: Integer;
newValue: IDataArrayValue;
begin
// Validate that all items match the element type of this array type.
i := 0;
@@ -112,20 +200,38 @@ begin
[i, FElementType.Name, item.DataType.Name]);
inc(i);
end;
Result := TImplDataArrayValue.Create(Self, AItems);
// Use optimized implementations for arrays with 0 or 1 elements.
case Length(AItems) of
0:
begin
// Thread-safe lazy initialization for the cached empty array value.
if FEmptyValue = nil then
begin
newValue := TDataArrayValue0.Create(Self);
if TInterlocked.CompareExchange(Pointer(FEmptyValue), Pointer(newValue), nil) <> nil then
newValue := nil; // Another thread won, discard our instance.
end;
Result := FEmptyValue;
end;
1: Result := TDataArrayValue1.Create(Self, AItems);
else
// Use the generic implementation for arrays with more than 1 element.
Result := TDataArrayValue.Create(Self, AItems);
end;
end;
function TImplDataArrayType.GetElementType: IDataType;
function TDataArrayType.GetElementType: IDataType;
begin
Result := FElementType;
end;
function TImplDataArrayType.GetName: String;
function TDataArrayType.GetName: String;
begin
Result := Format('Array<%s>', [FElementType.Name]);
end;
function TImplDataArrayType.GetKind: TDataKind;
function TDataArrayType.GetKind: TDataKind;
begin
Result := dkArray;
end;