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; 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; 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.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.