Files
MycLib/Src/Data/Myc.Data.Types.Arrays.pas
Michael Schimmel 54d470b2f8 New type system
2025-08-26 15:49:28 +02:00

272 lines
7.4 KiB
ObjectPascal

unit Myc.Data.Types.Arrays;
interface
uses
System.Generics.Collections,
System.SysUtils,
Myc.Data.Types;
type
// Implements the array data type.
TImplDataArrayType = class(TInterfacedObject, IDataType, IDataArrayType)
strict private
// Class-level registry to cache and reuse array type definitions.
class var
FArrayTypeRegistry: TDictionary<IDataType, IDataArrayType>;
private
FElementType: IDataType;
FEmptyValue: IDataArrayValue; // Cached empty array value for this type
function GetElementType: IDataType;
function GetName: String;
function GetKind: TDataKind;
public
constructor Create(const AElementType: IDataType);
class constructor CreateClass;
class destructor DestroyClass;
// Factory method to get a cached or new array type instance.
class function GetInstance(const AElementType: IDataType): IDataArrayType; static;
function CreateValue(const AItems: array of IDataValue): IDataArrayValue;
end;
implementation
uses
System.SyncObjs;
type
// Implements the array data value.
TImplDataArrayValue = class(TInterfacedObject, IDataValue, IDataArrayValue)
private
FArrayType: IDataArrayType;
FItems: TArray<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;
// Optimized implementation for an array with 0 elements.
TImplDataArrayValue0 = 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;
// Optimized implementation for an array with 1 element.
TImplDataArrayValue1 = class(TInterfacedObject, IDataValue, IDataArrayValue)
private
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 (generic implementation for N > 1 elements) }
constructor TImplDataArrayValue.Create(const AArrayType: IDataArrayType; const AItems: array of IDataValue);
var
i: Integer;
begin
inherited Create;
FArrayType := AArrayType;
SetLength(FItems, Length(AItems));
for i := 0 to High(AItems) do
FItems[i] := AItems[i];
end;
function TImplDataArrayValue.GetDataType: IDataType;
begin
Result := FArrayType;
end;
function TImplDataArrayValue.GetAsString: string;
var
sb: TStringBuilder;
i: Integer;
begin
sb := TStringBuilder.Create;
try
sb.Append('[');
for i := 0 to High(FItems) do
begin
sb.Append(FItems[i].AsString);
if (i < High(FItems)) then
sb.Append(', ');
end;
sb.Append(']');
Result := sb.ToString;
finally
sb.Free;
end;
end;
function TImplDataArrayValue.GetElementCount: Integer;
begin
Result := Length(FItems);
end;
function TImplDataArrayValue.GetItem(Idx: Integer): IDataValue;
begin
Result := FItems[Idx];
end;
{ TImplDataArrayValue0 }
constructor TImplDataArrayValue0.Create(const AArrayType: IDataArrayType);
begin
inherited Create;
FArrayType := AArrayType;
end;
function TImplDataArrayValue0.GetAsString: string;
begin
Result := '[]';
end;
function TImplDataArrayValue0.GetDataType: IDataType;
begin
Result := FArrayType;
end;
function TImplDataArrayValue0.GetElementCount: Integer;
begin
Result := 0;
end;
function TImplDataArrayValue0.GetItem(Idx: Integer): IDataValue;
begin
raise EArgumentException.Create('Index out of bounds');
end;
{ TImplDataArrayValue1 }
constructor TImplDataArrayValue1.Create(const AArrayType: IDataArrayType; const AItems: array of IDataValue);
begin
inherited Create;
FArrayType := AArrayType;
FItem0 := AItems[0];
end;
function TImplDataArrayValue1.GetAsString: string;
begin
Result := '[' + FItem0.AsString + ']';
end;
function TImplDataArrayValue1.GetDataType: IDataType;
begin
Result := FArrayType;
end;
function TImplDataArrayValue1.GetElementCount: Integer;
begin
Result := 1;
end;
function TImplDataArrayValue1.GetItem(Idx: Integer): IDataValue;
begin
case Idx of
0: Result := FItem0;
else
raise EArgumentException.Create('Index out of bounds');
end;
end;
{ TImplDataArrayType }
class constructor TImplDataArrayType.CreateClass;
begin
// Initialize the global registry for array types.
FArrayTypeRegistry := TDictionary<IDataType, IDataArrayType>.Create;
end;
class destructor TImplDataArrayType.DestroyClass;
begin
FArrayTypeRegistry.Free;
FArrayTypeRegistry := nil;
end;
class function TImplDataArrayType.GetInstance(const AElementType: IDataType): IDataArrayType;
var
res: IDataArrayType;
begin
if not Assigned(AElementType) then
raise EArgumentException.Create('Cannot create an array type with a nil element type.');
TMonitor.Enter(FArrayTypeRegistry);
try
if not FArrayTypeRegistry.TryGetValue(AElementType, res) then
begin
res := TImplDataArrayType.Create(AElementType);
FArrayTypeRegistry.Add(AElementType, res);
end;
finally
TMonitor.Exit(FArrayTypeRegistry);
end;
Result := res;
end;
constructor TImplDataArrayType.Create(const AElementType: IDataType);
begin
inherited Create;
FElementType := AElementType;
FEmptyValue := TImplDataArrayValue0.Create(Self);
end;
function TImplDataArrayType.CreateValue(const AItems: array of IDataValue): IDataArrayValue;
var
item: IDataValue;
i: Integer;
begin
// Validate that all items match the element type of this array type.
i := 0;
for item in AItems do
begin
if (item.DataType <> FElementType) then
raise EArgumentException.CreateFmt(
'Invalid data type for item at index %d. Expected ''%s'', but got ''%s''.',
[i, FElementType.Name, item.DataType.Name]);
inc(i);
end;
// Use optimized implementations for arrays with 0 or 1 elements.
case Length(AItems) of
0: Result := FEmptyValue;
1: Result := TImplDataArrayValue1.Create(Self, AItems);
else
// Use the generic implementation for arrays with more than 1 element.
Result := TImplDataArrayValue.Create(Self, AItems);
end;
end;
function TImplDataArrayType.GetElementType: IDataType;
begin
Result := FElementType;
end;
function TImplDataArrayType.GetName: String;
begin
Result := Format('Array<%s>', [FElementType.Name]);
end;
function TImplDataArrayType.GetKind: TDataKind;
begin
Result := dkArray;
end;
end.