Types added to Tuples
This commit is contained in:
+226
-138
@@ -3,121 +3,283 @@ unit Myc.Data.Types.Tuple;
|
||||
interface
|
||||
|
||||
uses
|
||||
System.Generics.Collections,
|
||||
System.SysUtils,
|
||||
System.Generics.Defaults,
|
||||
System.Hash,
|
||||
Myc.Data.Types;
|
||||
|
||||
type
|
||||
// Implements the singleton tuple data type.
|
||||
// Custom comparer for TArray<IDataType> to be used as a dictionary key.
|
||||
TDataTupleTypeComparer = class(TInterfacedObject, IEqualityComparer<TArray<IDataType>>)
|
||||
public
|
||||
function Equals(const Left, Right: TArray<IDataType>): Boolean; reintroduce;
|
||||
function GetHashCode(const Value: TArray<IDataType>): Integer; reintroduce;
|
||||
end;
|
||||
|
||||
// Implements the tuple data type.
|
||||
TImplDataTupleType = class(TInterfacedObject, IDataType, IDataTupleType)
|
||||
strict private
|
||||
// Class-level registry to cache and reuse tuple type definitions.
|
||||
class var
|
||||
FSingleton: IDataTupleType;
|
||||
class constructor CreateClass;
|
||||
FTupleTypeRegistry: TDictionary<TArray<IDataType>, IDataTupleType>;
|
||||
private
|
||||
FItemTypes: TArray<IDataType>;
|
||||
FEmptyValue: IDataTupleValue; // Cached instance for zero-field tuples
|
||||
function GetName: String;
|
||||
function GetKind: TDataKind;
|
||||
public
|
||||
function CreateValue(const AItems: array of IDataValue): IDataTupleValue;
|
||||
class property Singleton: IDataTupleType read FSingleton;
|
||||
function GetItemCount: Integer;
|
||||
function GetItemType(Idx: Integer): IDataType;
|
||||
public
|
||||
constructor Create(const AItemTypes: TArray<IDataType>);
|
||||
destructor Destroy; override;
|
||||
|
||||
class constructor CreateClass;
|
||||
class destructor DestroyClass;
|
||||
|
||||
class function GetInstance(const AItemTypes: array of IDataType): IDataTupleType; static;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.SyncObjs,
|
||||
System.Rtti;
|
||||
|
||||
type
|
||||
// Implements the simple tuple data value.
|
||||
// Implements the tuple data value.
|
||||
TImplDataTupleValue = class(TInterfacedObject, IDataValue, IDataTupleValue)
|
||||
private
|
||||
FDataType: IDataTupleType;
|
||||
FItems: TArray<IDataValue>;
|
||||
function GetDataType: IDataType;
|
||||
function GetAsString: string;
|
||||
function GetItemCount: Integer;
|
||||
function GetItem(Idx: Integer): IDataValue;
|
||||
public
|
||||
constructor Create(const AItems: array of IDataValue);
|
||||
constructor Create(const ADataType: IDataTupleType; const AItems: array of IDataValue);
|
||||
end;
|
||||
|
||||
// Optimized singleton implementation for a tuple with 0 elements.
|
||||
// Optimized implementation for a tuple with 0 fields.
|
||||
TImplDataTupleValue0 = class(TInterfacedObject, IDataValue, IDataTupleValue)
|
||||
strict private
|
||||
class var
|
||||
FSingleton: IDataTupleValue;
|
||||
class constructor CreateClass;
|
||||
private
|
||||
FDataType: IDataTupleType;
|
||||
function GetDataType: IDataType;
|
||||
function GetAsString: string;
|
||||
function GetItemCount: Integer;
|
||||
function GetItem(Idx: Integer): IDataValue;
|
||||
public
|
||||
class property Singleton: IDataTupleValue read FSingleton;
|
||||
constructor Create(const ADataType: IDataTupleType);
|
||||
end;
|
||||
|
||||
// Optimized implementation for a tuple with 1 element.
|
||||
// Optimized implementation for a tuple with 1 field.
|
||||
TImplDataTupleValue1 = class(TInterfacedObject, IDataValue, IDataTupleValue)
|
||||
private
|
||||
FDataType: IDataTupleType;
|
||||
FItem0: IDataValue;
|
||||
function GetDataType: IDataType;
|
||||
function GetAsString: string;
|
||||
function GetItemCount: Integer;
|
||||
function GetItem(Idx: Integer): IDataValue;
|
||||
public
|
||||
constructor Create(const AItems: array of IDataValue);
|
||||
constructor Create(const ADataType: IDataTupleType; const AItems: array of IDataValue);
|
||||
end;
|
||||
|
||||
// Optimized implementation for a tuple with 2 elements.
|
||||
// Optimized implementation for a tuple with 2 fields.
|
||||
TImplDataTupleValue2 = class(TInterfacedObject, IDataValue, IDataTupleValue)
|
||||
private
|
||||
FDataType: IDataTupleType;
|
||||
FItem0, FItem1: IDataValue;
|
||||
function GetDataType: IDataType;
|
||||
function GetAsString: string;
|
||||
function GetItemCount: Integer;
|
||||
function GetItem(Idx: Integer): IDataValue;
|
||||
public
|
||||
constructor Create(const AItems: array of IDataValue);
|
||||
constructor Create(const ADataType: IDataTupleType; const AItems: array of IDataValue);
|
||||
end;
|
||||
|
||||
// Optimized implementation for a tuple with 3 elements.
|
||||
// Optimized implementation for a tuple with 3 fields.
|
||||
TImplDataTupleValue3 = class(TInterfacedObject, IDataValue, IDataTupleValue)
|
||||
private
|
||||
FDataType: IDataTupleType;
|
||||
FItem0, FItem1, FItem2: IDataValue;
|
||||
function GetDataType: IDataType;
|
||||
function GetAsString: string;
|
||||
function GetItemCount: Integer;
|
||||
function GetItem(Idx: Integer): IDataValue;
|
||||
public
|
||||
constructor Create(const AItems: array of IDataValue);
|
||||
constructor Create(const ADataType: IDataTupleType; const AItems: array of IDataValue);
|
||||
end;
|
||||
|
||||
// Optimized implementation for a tuple with 4 elements.
|
||||
// Optimized implementation for a tuple with 4 fields.
|
||||
TImplDataTupleValue4 = class(TInterfacedObject, IDataValue, IDataTupleValue)
|
||||
private
|
||||
FDataType: IDataTupleType;
|
||||
FItem0, FItem1, FItem2, FItem3: IDataValue;
|
||||
function GetDataType: IDataType;
|
||||
function GetAsString: string;
|
||||
function GetItemCount: Integer;
|
||||
function GetItem(Idx: Integer): IDataValue;
|
||||
public
|
||||
constructor Create(const AItems: array of IDataValue);
|
||||
constructor Create(const ADataType: IDataTupleType; const AItems: array of IDataValue);
|
||||
end;
|
||||
|
||||
// Optimized implementation for a tuple with 5 elements.
|
||||
TImplDataTupleValue5 = class(TInterfacedObject, IDataValue, IDataTupleValue)
|
||||
private
|
||||
FItem0, FItem1, FItem2, FItem3, FItem4: IDataValue;
|
||||
function GetDataType: IDataType;
|
||||
function GetAsString: string;
|
||||
function GetItemCount: Integer;
|
||||
function GetItem(Idx: Integer): IDataValue;
|
||||
public
|
||||
constructor Create(const AItems: array of IDataValue);
|
||||
{ TDataTupleTypeComparer }
|
||||
|
||||
function TDataTupleTypeComparer.Equals(const Left, Right: TArray<IDataType>): Boolean;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
if (Length(Left) <> Length(Right)) then
|
||||
exit(False);
|
||||
|
||||
for i := 0 to High(Left) do
|
||||
begin
|
||||
// Compare by interface reference.
|
||||
// It's safe because all IDataType instances are managed by GetInstance singletons.
|
||||
if (Left[i] <> Right[i]) then
|
||||
exit(False);
|
||||
end;
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
{ TImplDataTupleValue (generic implementation for N > 5 elements) }
|
||||
function TDataTupleTypeComparer.GetHashCode(const Value: TArray<IDataType>): Integer;
|
||||
var
|
||||
dataType: IDataType;
|
||||
begin
|
||||
Result := 0;
|
||||
for dataType in Value do
|
||||
begin
|
||||
// Use the hash of the interface's memory address.
|
||||
// This is safe and fast due to the singleton pattern for types.
|
||||
Result := Integer(PPointer(@dataType)^) xor Result;
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TImplDataTupleValue.Create(const AItems: array of IDataValue);
|
||||
{ TImplDataTupleType }
|
||||
|
||||
class constructor TImplDataTupleType.CreateClass;
|
||||
begin
|
||||
// Initialize the global registry for tuple types.
|
||||
FTupleTypeRegistry := TDictionary<TArray<IDataType>, IDataTupleType>.Create(TDataTupleTypeComparer.Create);
|
||||
end;
|
||||
|
||||
class destructor TImplDataTupleType.DestroyClass;
|
||||
begin
|
||||
FTupleTypeRegistry.Free;
|
||||
FTupleTypeRegistry := nil;
|
||||
end;
|
||||
|
||||
constructor TImplDataTupleType.Create(const AItemTypes: TArray<IDataType>);
|
||||
begin
|
||||
inherited Create;
|
||||
FItemTypes := AItemTypes;
|
||||
|
||||
// Pre-create the singleton value instance if this is a zero-field tuple.
|
||||
if (Length(FItemTypes) = 0) then
|
||||
FEmptyValue := TImplDataTupleValue0.Create(Self);
|
||||
end;
|
||||
|
||||
destructor TImplDataTupleType.Destroy;
|
||||
begin
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TImplDataTupleType.CreateValue(const AItems: array of IDataValue): IDataTupleValue;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
if (Length(AItems) <> Length(FItemTypes)) then
|
||||
raise EArgumentException
|
||||
.CreateFmt('Invalid number of items for this tuple type. Expected %d, but got %d.', [Length(FItemTypes), Length(AItems)]);
|
||||
|
||||
for i := 0 to High(AItems) do
|
||||
if not Assigned(AItems[i]) or (AItems[i].DataType <> FItemTypes[i]) then
|
||||
raise EArgumentException.CreateFmt(
|
||||
'Invalid data type for item at index %d. Expected %s, but got %s.',
|
||||
[i, FItemTypes[i].Name, AItems[i].DataType.Name]);
|
||||
|
||||
// Use optimized implementations for tuples with 0, 1, 2, 3, or 4 fields.
|
||||
case Length(FItemTypes) of
|
||||
0: Result := FEmptyValue; // Return the pre-cached instance.
|
||||
1: Result := TImplDataTupleValue1.Create(Self, AItems);
|
||||
2: Result := TImplDataTupleValue2.Create(Self, AItems);
|
||||
3: Result := TImplDataTupleValue3.Create(Self, AItems);
|
||||
4: Result := TImplDataTupleValue4.Create(Self, AItems);
|
||||
else
|
||||
// Use the generic implementation for tuples with more than 4 fields.
|
||||
Result := TImplDataTupleValue.Create(Self, AItems);
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TImplDataTupleType.GetInstance(const AItemTypes: array of IDataType): IDataTupleType;
|
||||
var
|
||||
tItems: TArray<IDataType>;
|
||||
i: Integer;
|
||||
res: IDataTupleType;
|
||||
begin
|
||||
SetLength(tItems, Length(AItemTypes));
|
||||
for i := 0 to High(AItemTypes) do
|
||||
tItems[i] := AItemTypes[i];
|
||||
|
||||
TMonitor.Enter(FTupleTypeRegistry);
|
||||
try
|
||||
if not FTupleTypeRegistry.TryGetValue(tItems, res) then
|
||||
begin
|
||||
res := TImplDataTupleType.Create(tItems);
|
||||
FTupleTypeRegistry.Add(tItems, res);
|
||||
end;
|
||||
finally
|
||||
TMonitor.Exit(FTupleTypeRegistry);
|
||||
end;
|
||||
Result := res;
|
||||
end;
|
||||
|
||||
function TImplDataTupleType.GetItemCount: Integer;
|
||||
begin
|
||||
Result := Length(FItemTypes);
|
||||
end;
|
||||
|
||||
function TImplDataTupleType.GetItemType(Idx: Integer): IDataType;
|
||||
begin
|
||||
Result := FItemTypes[Idx];
|
||||
end;
|
||||
|
||||
function TImplDataTupleType.GetName: String;
|
||||
var
|
||||
i: Integer;
|
||||
sb: TStringBuilder;
|
||||
begin
|
||||
sb := TStringBuilder.Create;
|
||||
try
|
||||
sb.Append('Tuple<');
|
||||
for i := 0 to High(FItemTypes) do
|
||||
begin
|
||||
sb.Append(FItemTypes[i].Name);
|
||||
if (i < High(FItemTypes)) then
|
||||
sb.Append(', ');
|
||||
end;
|
||||
sb.Append('>');
|
||||
Result := sb.ToString;
|
||||
finally
|
||||
sb.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TImplDataTupleType.GetKind: TDataKind;
|
||||
begin
|
||||
Result := dkTuple;
|
||||
end;
|
||||
|
||||
{ TImplDataTupleValue (generic implementation for N > 4 fields) }
|
||||
|
||||
constructor TImplDataTupleValue.Create(const ADataType: IDataTupleType; const AItems: array of IDataValue);
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
inherited Create;
|
||||
FDataType := ADataType;
|
||||
SetLength(FItems, Length(AItems));
|
||||
for i := 0 to High(AItems) do
|
||||
FItems[i] := AItems[i];
|
||||
@@ -125,7 +287,7 @@ end;
|
||||
|
||||
function TImplDataTupleValue.GetDataType: IDataType;
|
||||
begin
|
||||
Result := TImplDataTupleType.Singleton;
|
||||
Result := FDataType;
|
||||
end;
|
||||
|
||||
function TImplDataTupleValue.GetAsString: string;
|
||||
@@ -161,9 +323,10 @@ end;
|
||||
|
||||
{ TImplDataTupleValue0 }
|
||||
|
||||
class constructor TImplDataTupleValue0.CreateClass;
|
||||
constructor TImplDataTupleValue0.Create(const ADataType: IDataTupleType);
|
||||
begin
|
||||
FSingleton := TImplDataTupleValue0.Create;
|
||||
inherited Create;
|
||||
FDataType := ADataType;
|
||||
end;
|
||||
|
||||
function TImplDataTupleValue0.GetAsString: string;
|
||||
@@ -173,7 +336,7 @@ end;
|
||||
|
||||
function TImplDataTupleValue0.GetDataType: IDataType;
|
||||
begin
|
||||
Result := TImplDataTupleType.Singleton;
|
||||
Result := FDataType;
|
||||
end;
|
||||
|
||||
function TImplDataTupleValue0.GetItem(Idx: Integer): IDataValue;
|
||||
@@ -188,20 +351,21 @@ end;
|
||||
|
||||
{ TImplDataTupleValue1 }
|
||||
|
||||
constructor TImplDataTupleValue1.Create(const AItems: array of IDataValue);
|
||||
constructor TImplDataTupleValue1.Create(const ADataType: IDataTupleType; const AItems: array of IDataValue);
|
||||
begin
|
||||
inherited Create;
|
||||
FDataType := ADataType;
|
||||
FItem0 := AItems[0];
|
||||
end;
|
||||
|
||||
function TImplDataTupleValue1.GetAsString: string;
|
||||
begin
|
||||
Result := '(' + FItem0.AsString + ')';
|
||||
Result := Format('(%s)', [FItem0.AsString]);
|
||||
end;
|
||||
|
||||
function TImplDataTupleValue1.GetDataType: IDataType;
|
||||
begin
|
||||
Result := TImplDataTupleType.Singleton;
|
||||
Result := FDataType;
|
||||
end;
|
||||
|
||||
function TImplDataTupleValue1.GetItem(Idx: Integer): IDataValue;
|
||||
@@ -220,21 +384,34 @@ end;
|
||||
|
||||
{ TImplDataTupleValue2 }
|
||||
|
||||
constructor TImplDataTupleValue2.Create(const AItems: array of IDataValue);
|
||||
constructor TImplDataTupleValue2.Create(const ADataType: IDataTupleType; const AItems: array of IDataValue);
|
||||
begin
|
||||
inherited Create;
|
||||
FDataType := ADataType;
|
||||
FItem0 := AItems[0];
|
||||
FItem1 := AItems[1];
|
||||
end;
|
||||
|
||||
function TImplDataTupleValue2.GetAsString: string;
|
||||
var
|
||||
sb: TStringBuilder;
|
||||
begin
|
||||
Result := '(' + FItem0.AsString + ', ' + FItem1.AsString + ')';
|
||||
sb := TStringBuilder.Create;
|
||||
try
|
||||
sb.Append('(');
|
||||
sb.Append(FItem0.AsString);
|
||||
sb.Append(', ');
|
||||
sb.Append(FItem1.AsString);
|
||||
sb.Append(')');
|
||||
Result := sb.ToString;
|
||||
finally
|
||||
sb.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TImplDataTupleValue2.GetDataType: IDataType;
|
||||
begin
|
||||
Result := TImplDataTupleType.Singleton;
|
||||
Result := FDataType;
|
||||
end;
|
||||
|
||||
function TImplDataTupleValue2.GetItem(Idx: Integer): IDataValue;
|
||||
@@ -254,9 +431,10 @@ end;
|
||||
|
||||
{ TImplDataTupleValue3 }
|
||||
|
||||
constructor TImplDataTupleValue3.Create(const AItems: array of IDataValue);
|
||||
constructor TImplDataTupleValue3.Create(const ADataType: IDataTupleType; const AItems: array of IDataValue);
|
||||
begin
|
||||
inherited Create;
|
||||
FDataType := ADataType;
|
||||
FItem0 := AItems[0];
|
||||
FItem1 := AItems[1];
|
||||
FItem2 := AItems[2];
|
||||
@@ -283,7 +461,7 @@ end;
|
||||
|
||||
function TImplDataTupleValue3.GetDataType: IDataType;
|
||||
begin
|
||||
Result := TImplDataTupleType.Singleton;
|
||||
Result := FDataType;
|
||||
end;
|
||||
|
||||
function TImplDataTupleValue3.GetItem(Idx: Integer): IDataValue;
|
||||
@@ -304,9 +482,10 @@ end;
|
||||
|
||||
{ TImplDataTupleValue4 }
|
||||
|
||||
constructor TImplDataTupleValue4.Create(const AItems: array of IDataValue);
|
||||
constructor TImplDataTupleValue4.Create(const ADataType: IDataTupleType; const AItems: array of IDataValue);
|
||||
begin
|
||||
inherited Create;
|
||||
FDataType := ADataType;
|
||||
FItem0 := AItems[0];
|
||||
FItem1 := AItems[1];
|
||||
FItem2 := AItems[2];
|
||||
@@ -336,7 +515,7 @@ end;
|
||||
|
||||
function TImplDataTupleValue4.GetDataType: IDataType;
|
||||
begin
|
||||
Result := TImplDataTupleType.Singleton;
|
||||
Result := FDataType;
|
||||
end;
|
||||
|
||||
function TImplDataTupleValue4.GetItem(Idx: Integer): IDataValue;
|
||||
@@ -356,95 +535,4 @@ begin
|
||||
Result := 4;
|
||||
end;
|
||||
|
||||
{ TImplDataTupleValue5 }
|
||||
|
||||
constructor TImplDataTupleValue5.Create(const AItems: array of IDataValue);
|
||||
begin
|
||||
inherited Create;
|
||||
FItem0 := AItems[0];
|
||||
FItem1 := AItems[1];
|
||||
FItem2 := AItems[2];
|
||||
FItem3 := AItems[3];
|
||||
FItem4 := AItems[4];
|
||||
end;
|
||||
|
||||
function TImplDataTupleValue5.GetAsString: string;
|
||||
var
|
||||
sb: TStringBuilder;
|
||||
begin
|
||||
sb := TStringBuilder.Create;
|
||||
try
|
||||
sb.Append('(');
|
||||
sb.Append(FItem0.AsString);
|
||||
sb.Append(', ');
|
||||
sb.Append(FItem1.AsString);
|
||||
sb.Append(', ');
|
||||
sb.Append(FItem2.AsString);
|
||||
sb.Append(', ');
|
||||
sb.Append(FItem3.AsString);
|
||||
sb.Append(', ');
|
||||
sb.Append(FItem4.AsString);
|
||||
sb.Append(')');
|
||||
Result := sb.ToString;
|
||||
finally
|
||||
sb.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TImplDataTupleValue5.GetDataType: IDataType;
|
||||
begin
|
||||
Result := TImplDataTupleType.Singleton;
|
||||
end;
|
||||
|
||||
function TImplDataTupleValue5.GetItem(Idx: Integer): IDataValue;
|
||||
begin
|
||||
case Idx of
|
||||
0: Result := FItem0;
|
||||
1: Result := FItem1;
|
||||
2: Result := FItem2;
|
||||
3: Result := FItem3;
|
||||
4: Result := FItem4;
|
||||
else
|
||||
raise EArgumentException.Create('Index out of bounds');
|
||||
end;
|
||||
end;
|
||||
|
||||
function TImplDataTupleValue5.GetItemCount: Integer;
|
||||
begin
|
||||
Result := 5;
|
||||
end;
|
||||
|
||||
{ TImplDataTupleType }
|
||||
|
||||
class constructor TImplDataTupleType.CreateClass;
|
||||
begin
|
||||
FSingleton := TImplDataTupleType.Create;
|
||||
end;
|
||||
|
||||
function TImplDataTupleType.CreateValue(const AItems: array of IDataValue): IDataTupleValue;
|
||||
begin
|
||||
// Use optimized implementations for tuples with 0 to 5 elements.
|
||||
case Length(AItems) of
|
||||
0: Result := TImplDataTupleValue0.Singleton;
|
||||
1: Result := TImplDataTupleValue1.Create(AItems);
|
||||
2: Result := TImplDataTupleValue2.Create(AItems);
|
||||
3: Result := TImplDataTupleValue3.Create(AItems);
|
||||
4: Result := TImplDataTupleValue4.Create(AItems);
|
||||
5: Result := TImplDataTupleValue5.Create(AItems);
|
||||
else
|
||||
// Use the generic implementation for tuples with more than 5 elements.
|
||||
Result := TImplDataTupleValue.Create(AItems);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TImplDataTupleType.GetName: String;
|
||||
begin
|
||||
Result := 'Tuple';
|
||||
end;
|
||||
|
||||
function TImplDataTupleType.GetKind: TDataKind;
|
||||
begin
|
||||
Result := dkTuple;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user