272 lines
7.4 KiB
ObjectPascal
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.
|