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;
+29 -28
View File
@@ -8,18 +8,7 @@ uses
Myc.Data.Types;
type
TImplDataEnumValue = class(TInterfacedObject, IDataEnumValue)
private
FDataType: IDataEnumType;
FValue: Integer;
function GetDataType: IDataType;
function GetAsString: string;
function GetValue: Integer;
public
constructor Create(const ADataType: IDataEnumType; const AValue: Integer);
end;
TImplDataEnumType = class(TInterfacedObject, IDataEnumType)
TDataEnumType = class(TInterfacedObject, IDataEnumType)
private
FName: string;
FIdentifiers: TArray<string>;
@@ -38,9 +27,21 @@ type
implementation
{ TImplDataEnumType }
type
TDataEnumValue = class(TInterfacedObject, IDataEnumValue)
private
FDataType: IDataEnumType;
FValue: Integer;
function GetDataType: IDataType;
function GetAsString: string;
function GetValue: Integer;
public
constructor Create(const ADataType: IDataEnumType; const AValue: Integer);
end;
constructor TImplDataEnumType.Create(const AName: string; const AIdentifiers: array of string);
{ TDataEnumType }
constructor TDataEnumType.Create(const AName: string; const AIdentifiers: array of string);
var
i: Integer;
begin
@@ -57,46 +58,46 @@ begin
end;
end;
destructor TImplDataEnumType.Destroy;
destructor TDataEnumType.Destroy;
begin
FIdentifierMap.Free;
inherited;
end;
function TImplDataEnumType.GetName: String;
function TDataEnumType.GetName: String;
begin
Result := FName;
end;
function TImplDataEnumType.GetKind: TDataKind;
function TDataEnumType.GetKind: TDataKind;
begin
Result := dkEnum;
end;
function TImplDataEnumType.GetIdentifier(Idx: Integer): string;
function TDataEnumType.GetIdentifier(Idx: Integer): string;
begin
Result := FIdentifiers[Idx];
end;
function TImplDataEnumType.GetIdentifierCount: Integer;
function TDataEnumType.GetIdentifierCount: Integer;
begin
Result := Length(FIdentifiers);
end;
function TImplDataEnumType.IndexOf(const AIdentifier: string): Integer;
function TDataEnumType.IndexOf(const AIdentifier: string): Integer;
begin
if not FIdentifierMap.TryGetValue(AIdentifier, Result) then
Result := -1;
end;
function TImplDataEnumType.CreateValue(const AValue: Integer): IDataEnumValue;
function TDataEnumType.CreateValue(const AValue: Integer): IDataEnumValue;
begin
if (AValue < 0) or (AValue >= Length(FIdentifiers)) then
raise EArgumentException.Create('Invalid enum value');
Result := TImplDataEnumValue.Create(Self, AValue);
Result := TDataEnumValue.Create(Self, AValue);
end;
function TImplDataEnumType.CreateValue(const AIdentifier: string): IDataEnumValue;
function TDataEnumType.CreateValue(const AIdentifier: string): IDataEnumValue;
var
idx: Integer;
begin
@@ -106,25 +107,25 @@ begin
Result := CreateValue(idx);
end;
{ TImplDataEnumValue }
{ TDataEnumValue }
constructor TImplDataEnumValue.Create(const ADataType: IDataEnumType; const AValue: Integer);
constructor TDataEnumValue.Create(const ADataType: IDataEnumType; const AValue: Integer);
begin
FDataType := ADataType;
FValue := AValue;
end;
function TImplDataEnumValue.GetDataType: IDataType;
function TDataEnumValue.GetDataType: IDataType;
begin
Result := FDataType;
end;
function TImplDataEnumValue.GetAsString: string;
function TDataEnumValue.GetAsString: string;
begin
Result := FDataType.Identifiers[FValue];
end;
function TImplDataEnumValue.GetValue: Integer;
function TDataEnumValue.GetValue: Integer;
begin
Result := FValue;
end;
+89 -25
View File
@@ -6,9 +6,30 @@ uses
System.SysUtils,
Myc.Data.Types;
type
// Implements the float data type.
TDataFloatType = class(TInterfacedObject, IDataType, IDataFloatType)
strict private
class var
FSingleton: IDataFloatType;
FZero: IDataFloatValue;
FNaN: IDataFloatValue;
class constructor CreateClass;
public
function GetName: String;
function GetKind: TDataKind;
function CreateValue(Init: Double): IDataFloatValue;
class property Singleton: IDataFloatType read FSingleton;
end;
implementation
uses
System.Math;
type
// Implements the float data value.
TImplDataFloatValue = class(TInterfacedObject, IDataValue, IDataFloatValue)
TDataFloatValue = class(TInterfacedObject, IDataValue, IDataFloatValue)
private
FValue: Double;
public
@@ -22,64 +43,107 @@ type
function GetValue: Double;
end;
// Implements the float data type.
TImplDataFloatType = class(TInterfacedObject, IDataType, IDataFloatType)
strict private
class var
FSingleton: IDataFloatType;
class constructor CreateClass;
// Special implementation for the value 0.0 (Singleton)
TDataFloatValueZero = class(TInterfacedObject, IDataValue, IDataFloatValue)
public
function GetName: String;
function GetKind: TDataKind;
function CreateValue(Init: Double): IDataFloatValue;
class property Singleton: IDataFloatType read FSingleton;
function GetDataType: IDataType;
function GetAsString: string;
function GetValue: Double;
end;
implementation
// Special implementation for NaN (Not a Number) (Singleton)
TDataFloatValueNaN = class(TInterfacedObject, IDataValue, IDataFloatValue)
public
function GetDataType: IDataType;
function GetAsString: string;
function GetValue: Double;
end;
{ TImplDataFloatValue }
{ TDataFloatValue }
constructor TImplDataFloatValue.Create(const AValue: Double);
constructor TDataFloatValue.Create(const AValue: Double);
begin
inherited Create;
FValue := AValue;
end;
function TImplDataFloatValue.GetDataType: IDataType;
function TDataFloatValue.GetDataType: IDataType;
begin
Result := TImplDataFloatType.Singleton;
Result := TDataFloatType.Singleton;
end;
function TImplDataFloatValue.GetAsString: string;
function TDataFloatValue.GetAsString: string;
begin
Result := FloatToStr(FValue);
end;
function TImplDataFloatValue.GetValue: Double;
function TDataFloatValue.GetValue: Double;
begin
Result := FValue;
end;
{ TImplDataFloatType }
{ TDataFloatValueZero }
class constructor TImplDataFloatType.CreateClass;
function TDataFloatValueZero.GetAsString: string;
begin
FSingleton := TImplDataFloatType.Create;
Result := '0';
end;
function TImplDataFloatType.GetName: String;
function TDataFloatValueZero.GetDataType: IDataType;
begin
Result := TDataFloatType.Singleton;
end;
function TDataFloatValueZero.GetValue: Double;
begin
Result := 0.0;
end;
{ TDataFloatValueNaN }
function TDataFloatValueNaN.GetAsString: string;
begin
Result := 'NaN';
end;
function TDataFloatValueNaN.GetDataType: IDataType;
begin
Result := TDataFloatType.Singleton;
end;
function TDataFloatValueNaN.GetValue: Double;
begin
Result := NaN;
end;
{ TDataFloatType }
class constructor TDataFloatType.CreateClass;
begin
FSingleton := TDataFloatType.Create;
FZero := TDataFloatValueZero.Create;
FNaN := TDataFloatValueNaN.Create;
end;
function TDataFloatType.GetName: String;
begin
Result := 'Float';
end;
function TImplDataFloatType.GetKind: TDataKind;
function TDataFloatType.GetKind: TDataKind;
begin
Result := dkFloat;
end;
function TImplDataFloatType.CreateValue(Init: Double): IDataFloatValue;
function TDataFloatType.CreateValue(Init: Double): IDataFloatValue;
begin
Result := TImplDataFloatValue.Create(Init);
// Return singleton instances for 0.0 and NaN to avoid unnecessary allocations.
if (Init = 0.0) then
Result := FZero
else if IsNaN(Init) then
Result := FNaN
else
Result := TDataFloatValue.Create(Init);
end;
end.
+112 -25
View File
@@ -6,9 +6,26 @@ uses
Myc.Data.Types,
System.SysUtils;
type
// Implements the ordinal data type.
TDataOrdinalType = class(TInterfacedObject, IDataType, IDataOrdinalType)
strict private
class var
FSingleton: IDataOrdinalType;
FMinusOne, FZero, FOne: IDataOrdinalValue; // Singletons for -1, 0, 1
class constructor CreateClass;
public
function GetName: String;
function GetKind: TDataKind;
function CreateValue(Init: Int64): IDataOrdinalValue;
class property Singleton: IDataOrdinalType read FSingleton;
end;
implementation
type
// Implements the ordinal data value.
TImplDataOrdinalValue = class(TInterfacedObject, IDataValue, IDataOrdinalValue)
TDataOrdinalValue = class(TInterfacedObject, IDataValue, IDataOrdinalValue)
private
FValue: Int64;
public
@@ -22,64 +39,134 @@ type
function GetValue: Int64;
end;
// Implements the ordinal data type.
TImplDataOrdinalType = class(TInterfacedObject, IDataType, IDataOrdinalType)
strict private
class var
FSingleton: IDataOrdinalType;
class constructor CreateClass;
// Specialized implementation for the value -1.
TDataOrdinalValueMinusOne = class(TInterfacedObject, IDataValue, IDataOrdinalValue)
public
function GetName: String;
function GetKind: TDataKind;
function CreateValue(Init: Int64): IDataOrdinalValue;
class property Singleton: IDataOrdinalType read FSingleton;
function GetDataType: IDataType;
function GetAsString: string;
function GetValue: Int64;
end;
implementation
// Specialized implementation for the value 0.
TDataOrdinalValueZero = class(TInterfacedObject, IDataValue, IDataOrdinalValue)
public
function GetDataType: IDataType;
function GetAsString: string;
function GetValue: Int64;
end;
{ TImplDataOrdinalValue }
// Specialized implementation for the value 1.
TDataOrdinalValueOne = class(TInterfacedObject, IDataValue, IDataOrdinalValue)
public
function GetDataType: IDataType;
function GetAsString: string;
function GetValue: Int64;
end;
constructor TImplDataOrdinalValue.Create(const AValue: Int64);
{ TDataOrdinalValue }
constructor TDataOrdinalValue.Create(const AValue: Int64);
begin
inherited Create;
FValue := AValue;
end;
function TImplDataOrdinalValue.GetDataType: IDataType;
function TDataOrdinalValue.GetDataType: IDataType;
begin
Result := TImplDataOrdinalType.Singleton;
Result := TDataOrdinalType.Singleton;
end;
function TImplDataOrdinalValue.GetAsString: string;
function TDataOrdinalValue.GetAsString: string;
begin
Result := FValue.ToString;
end;
function TImplDataOrdinalValue.GetValue: Int64;
function TDataOrdinalValue.GetValue: Int64;
begin
Result := FValue;
end;
{ TImplDataOrdinalType }
{ TDataOrdinalValueMinusOne }
class constructor TImplDataOrdinalType.CreateClass;
function TDataOrdinalValueMinusOne.GetDataType: IDataType;
begin
FSingleton := TImplDataOrdinalType.Create;
Result := TDataOrdinalType.Singleton;
end;
function TImplDataOrdinalType.GetName: String;
function TDataOrdinalValueMinusOne.GetAsString: string;
begin
Result := '-1';
end;
function TDataOrdinalValueMinusOne.GetValue: Int64;
begin
Result := -1;
end;
{ TDataOrdinalValueZero }
function TDataOrdinalValueZero.GetDataType: IDataType;
begin
Result := TDataOrdinalType.Singleton;
end;
function TDataOrdinalValueZero.GetAsString: string;
begin
Result := '0';
end;
function TDataOrdinalValueZero.GetValue: Int64;
begin
Result := 0;
end;
{ TDataOrdinalValueOne }
function TDataOrdinalValueOne.GetDataType: IDataType;
begin
Result := TDataOrdinalType.Singleton;
end;
function TDataOrdinalValueOne.GetAsString: string;
begin
Result := '1';
end;
function TDataOrdinalValueOne.GetValue: Int64;
begin
Result := 1;
end;
{ TDataOrdinalType }
class constructor TDataOrdinalType.CreateClass;
begin
FSingleton := TDataOrdinalType.Create;
FMinusOne := TDataOrdinalValueMinusOne.Create;
FZero := TDataOrdinalValueZero.Create;
FOne := TDataOrdinalValueOne.Create;
end;
function TDataOrdinalType.GetName: String;
begin
Result := 'Integer';
end;
function TImplDataOrdinalType.GetKind: TDataKind;
function TDataOrdinalType.GetKind: TDataKind;
begin
Result := dkOrdinal;
end;
function TImplDataOrdinalType.CreateValue(Init: Int64): IDataOrdinalValue;
function TDataOrdinalType.CreateValue(Init: Int64): IDataOrdinalValue;
begin
Result := TImplDataOrdinalValue.Create(Init);
// Use specialized singletons for common values.
case Init of
-1: Result := FMinusOne;
0: Result := FZero;
1: Result := FOne;
else
Result := TDataOrdinalValue.Create(Init);
end;
end;
end.
+166 -22
View File
@@ -17,10 +17,8 @@ type
function GetHashCode(const Value: TArray<TRecordField>): Integer; reintroduce;
end;
TImplDataRecordValue = class; // fwd
// Implements the record data type.
TImplDataRecordType = class(TInterfacedObject, IDataType, IDataRecordType)
TDataRecordType = class(TInterfacedObject, IDataType, IDataRecordType)
private
FFields: TArray<TRecordField>;
FFieldMap: TDictionary<string, Integer>; // For fast lookups by name
@@ -35,8 +33,14 @@ type
function CreateValue(const AItems: array of IDataValue): IDataRecordValue;
end;
implementation
uses
System.SyncObjs;
type
// Implements the record data value.
TImplDataRecordValue = class(TInterfacedObject, IDataValue, IDataRecordValue)
TDataRecordValue = class(TInterfacedObject, IDataValue, IDataRecordValue)
private
FDataType: IDataRecordType;
FItems: TArray<IDataValue>;
@@ -47,10 +51,40 @@ type
constructor Create(const ADataType: IDataRecordType; const AItems: array of IDataValue);
end;
implementation
// Optimized implementation for a record with 0 fields.
TDataRecordValue0 = class(TInterfacedObject, IDataValue, IDataRecordValue)
private
FDataType: IDataRecordType;
function GetDataType: IDataType;
function GetAsString: string;
function GetItem(Idx: Integer): IDataValue;
public
constructor Create(const ADataType: IDataRecordType);
end;
uses
System.SyncObjs;
// Optimized implementation for a record with 1 field.
TDataRecordValue1 = class(TInterfacedObject, IDataValue, IDataRecordValue)
private
FDataType: IDataRecordType;
FItem0: IDataValue;
function GetDataType: IDataType;
function GetAsString: string;
function GetItem(Idx: Integer): IDataValue;
public
constructor Create(const ADataType: IDataRecordType; const AItems: array of IDataValue);
end;
// Optimized implementation for a record with 2 fields.
TDataRecordValue2 = class(TInterfacedObject, IDataValue, IDataRecordValue)
private
FDataType: IDataRecordType;
FItem0, FItem1: IDataValue;
function GetDataType: IDataType;
function GetAsString: string;
function GetItem(Idx: Integer): IDataValue;
public
constructor Create(const ADataType: IDataRecordType; const AItems: array of IDataValue);
end;
{ TRecordFieldComparer }
@@ -80,9 +114,9 @@ begin
end;
end;
{ TImplDataRecordValue }
{ TDataRecordValue (generic implementation for N > 2 fields) }
constructor TImplDataRecordValue.Create(const ADataType: IDataRecordType; const AItems: array of IDataValue);
constructor TDataRecordValue.Create(const ADataType: IDataRecordType; const AItems: array of IDataValue);
var
i: Integer;
begin
@@ -93,12 +127,12 @@ begin
FItems[i] := AItems[i];
end;
function TImplDataRecordValue.GetDataType: IDataType;
function TDataRecordValue.GetDataType: IDataType;
begin
Result := FDataType;
end;
function TImplDataRecordValue.GetAsString: string;
function TDataRecordValue.GetAsString: string;
var
sb: TStringBuilder;
i: Integer;
@@ -123,14 +157,116 @@ begin
end;
end;
function TImplDataRecordValue.GetItem(Idx: Integer): IDataValue;
function TDataRecordValue.GetItem(Idx: Integer): IDataValue;
begin
Result := FItems[Idx];
end;
{ TImplDataRecordType }
{ TDataRecordValue0 }
constructor TImplDataRecordType.Create(const AFields: array of TRecordField);
constructor TDataRecordValue0.Create(const ADataType: IDataRecordType);
begin
inherited Create;
FDataType := ADataType;
end;
function TDataRecordValue0.GetAsString: string;
begin
Result := '<>';
end;
function TDataRecordValue0.GetDataType: IDataType;
begin
Result := FDataType;
end;
function TDataRecordValue0.GetItem(Idx: Integer): IDataValue;
begin
raise EArgumentException.Create('Index out of bounds');
end;
{ TDataRecordValue1 }
constructor TDataRecordValue1.Create(const ADataType: IDataRecordType; const AItems: array of IDataValue);
begin
inherited Create;
FDataType := ADataType;
FItem0 := AItems[0];
end;
function TDataRecordValue1.GetAsString: string;
var
recordType: IDataRecordType;
begin
recordType := FDataType;
Result := Format('<%s: %s>', [recordType.Fields[0].Name, FItem0.AsString]);
end;
function TDataRecordValue1.GetDataType: IDataType;
begin
Result := FDataType;
end;
function TDataRecordValue1.GetItem(Idx: Integer): IDataValue;
begin
case Idx of
0: Result := FItem0;
else
raise EArgumentException.Create('Index out of bounds');
end;
end;
{ TDataRecordValue2 }
constructor TDataRecordValue2.Create(const ADataType: IDataRecordType; const AItems: array of IDataValue);
begin
inherited Create;
FDataType := ADataType;
FItem0 := AItems[0];
FItem1 := AItems[1];
end;
function TDataRecordValue2.GetAsString: string;
var
recordType: IDataRecordType;
sb: TStringBuilder;
begin
recordType := FDataType;
sb := TStringBuilder.Create;
try
sb.Append('<');
sb.Append(recordType.Fields[0].Name);
sb.Append(': ');
sb.Append(FItem0.AsString);
sb.Append(', ');
sb.Append(recordType.Fields[1].Name);
sb.Append(': ');
sb.Append(FItem1.AsString);
sb.Append('>');
Result := sb.ToString;
finally
sb.Free;
end;
end;
function TDataRecordValue2.GetDataType: IDataType;
begin
Result := FDataType;
end;
function TDataRecordValue2.GetItem(Idx: Integer): IDataValue;
begin
case Idx of
0: Result := FItem0;
1: Result := FItem1;
else
raise EArgumentException.Create('Index out of bounds');
end;
end;
{ TDataRecordType }
constructor TDataRecordType.Create(const AFields: array of TRecordField);
var
i: Integer;
field: TRecordField;
@@ -148,13 +284,13 @@ begin
end;
end;
destructor TImplDataRecordType.Destroy;
destructor TDataRecordType.Destroy;
begin
FFieldMap.Free;
inherited;
end;
function TImplDataRecordType.CreateValue(const AItems: array of IDataValue): IDataRecordValue;
function TDataRecordType.CreateValue(const AItems: array of IDataValue): IDataRecordValue;
var
i: Integer;
begin
@@ -168,20 +304,28 @@ begin
'Invalid data type for field ''%s'' at index %d. Expected ''%s'', but got ''%s''.',
[FFields[i].Name, i, FFields[i].DataType.Name, AItems[i].DataType.Name]);
Result := TImplDataRecordValue.Create(Self, AItems);
// Use optimized implementations for records with 0, 1, or 2 fields.
case Length(FFields) of
0: Result := TDataRecordValue0.Create(Self);
1: Result := TDataRecordValue1.Create(Self, AItems);
2: Result := TDataRecordValue2.Create(Self, AItems);
else
// Use the generic implementation for records with more than 2 fields.
Result := TDataRecordValue.Create(Self, AItems);
end;
end;
function TImplDataRecordType.GetField(Idx: Integer): TRecordField;
function TDataRecordType.GetField(Idx: Integer): TRecordField;
begin
Result := FFields[Idx];
end;
function TImplDataRecordType.GetFieldCount: Integer;
function TDataRecordType.GetFieldCount: Integer;
begin
Result := Length(FFields);
end;
function TImplDataRecordType.GetName: String;
function TDataRecordType.GetName: String;
var
i: Integer;
sb: TStringBuilder;
@@ -204,12 +348,12 @@ begin
end;
end;
function TImplDataRecordType.GetKind: TDataKind;
function TDataRecordType.GetKind: TDataKind;
begin
Result := dkRecord;
end;
function TImplDataRecordType.IndexOf(const AName: string): Integer;
function TDataRecordType.IndexOf(const AName: string): Integer;
begin
if not FFieldMap.TryGetValue(AName, Result) then
Result := -1;
+57 -16
View File
@@ -7,7 +7,7 @@ uses
Myc.Data.Types;
type
TImplDataTextType = class(TInterfacedObject, IDataType, IDataTextType)
TDataTextType = class(TInterfacedObject, IDataType, IDataTextType)
strict private
class var
FSingleton: IDataTextType;
@@ -19,7 +19,10 @@ type
class property Singleton: IDataTextType read FSingleton;
end;
TImplDataTextValue = class(TInterfacedObject, IDataValue, IDataTextValue)
implementation
type
TDataTextValue = class(TInterfacedObject, IDataValue, IDataTextValue)
private
FValue: string;
function GetDataType: IDataType;
@@ -29,51 +32,89 @@ type
constructor Create(const AValue: string);
end;
implementation
// Singleton implementation for the empty string value.
TDataTextValueEmpty = class(TInterfacedObject, IDataValue, IDataTextValue)
strict private
class var
FSingleton: IDataTextValue;
class constructor CreateClass;
private
function GetDataType: IDataType;
function GetAsString: string;
function GetValue: string;
public
class property Singleton: IDataTextValue read FSingleton;
end;
{ TImplDataTextType }
{ TDataTextType }
class constructor TImplDataTextType.CreateClass;
class constructor TDataTextType.CreateClass;
begin
FSingleton := TImplDataTextType.Create;
FSingleton := TDataTextType.Create;
end;
function TImplDataTextType.CreateValue(const AValue: string): IDataTextValue;
function TDataTextType.CreateValue(const AValue: string): IDataTextValue;
begin
Result := TImplDataTextValue.Create(AValue);
// Use the singleton for empty strings.
if (AValue = '') then
Result := TDataTextValueEmpty.Singleton
else
Result := TDataTextValue.Create(AValue);
end;
function TImplDataTextType.GetName: String;
function TDataTextType.GetName: String;
begin
Result := 'Text';
end;
function TImplDataTextType.GetKind: TDataKind;
function TDataTextType.GetKind: TDataKind;
begin
Result := dkText;
end;
{ TImplDataTextValue }
{ TDataTextValue }
constructor TImplDataTextValue.Create(const AValue: string);
constructor TDataTextValue.Create(const AValue: string);
begin
inherited Create;
FValue := AValue;
end;
function TImplDataTextValue.GetDataType: IDataType;
function TDataTextValue.GetDataType: IDataType;
begin
Result := TImplDataTextType.Singleton;
Result := TDataTextType.Singleton;
end;
function TImplDataTextValue.GetAsString: string;
function TDataTextValue.GetAsString: string;
begin
Result := FValue;
end;
function TImplDataTextValue.GetValue: string;
function TDataTextValue.GetValue: string;
begin
Result := FValue;
end;
{ TDataTextValueEmpty }
class constructor TDataTextValueEmpty.CreateClass;
begin
FSingleton := TDataTextValueEmpty.Create;
end;
function TDataTextValueEmpty.GetAsString: string;
begin
Result := '';
end;
function TDataTextValueEmpty.GetDataType: IDataType;
begin
Result := TDataTextType.Singleton;
end;
function TDataTextValueEmpty.GetValue: string;
begin
Result := '';
end;
end.
+18 -17
View File
@@ -7,7 +7,7 @@ uses
Myc.Data.Types;
type
TImplDataTimestampType = class(TInterfacedObject, IDataType, IDataTimestampType)
TDataTimestampType = class(TInterfacedObject, IDataType, IDataTimestampType)
strict private
class var
FSingleton: IDataTimestampType;
@@ -19,7 +19,10 @@ type
class property Singleton: IDataTimestampType read FSingleton;
end;
TImplDataTimestampValue = class(TInterfacedObject, IDataValue, IDataTimestampValue)
implementation
type
TDataTimestampValue = class(TInterfacedObject, IDataValue, IDataTimestampValue)
private
FValue: TDateTime;
function GetDataType: IDataType;
@@ -29,49 +32,47 @@ type
constructor Create(const AValue: TDateTime);
end;
implementation
{ TDataTimestampType }
{ TImplDataTimestampType }
class constructor TImplDataTimestampType.CreateClass;
class constructor TDataTimestampType.CreateClass;
begin
FSingleton := TImplDataTimestampType.Create;
FSingleton := TDataTimestampType.Create;
end;
function TImplDataTimestampType.CreateValue(const AValue: TDateTime): IDataTimestampValue;
function TDataTimestampType.CreateValue(const AValue: TDateTime): IDataTimestampValue;
begin
Result := TImplDataTimestampValue.Create(AValue);
Result := TDataTimestampValue.Create(AValue);
end;
function TImplDataTimestampType.GetName: String;
function TDataTimestampType.GetName: String;
begin
Result := 'Timestamp';
end;
function TImplDataTimestampType.GetKind: TDataKind;
function TDataTimestampType.GetKind: TDataKind;
begin
Result := dkTimestamp;
end;
{ TImplDataTimestampValue }
{ TDataTimestampValue }
constructor TImplDataTimestampValue.Create(const AValue: TDateTime);
constructor TDataTimestampValue.Create(const AValue: TDateTime);
begin
inherited Create;
FValue := AValue;
end;
function TImplDataTimestampValue.GetDataType: IDataType;
function TDataTimestampValue.GetDataType: IDataType;
begin
Result := TImplDataTimestampType.Singleton;
Result := TDataTimestampType.Singleton;
end;
function TImplDataTimestampValue.GetAsString: string;
function TDataTimestampValue.GetAsString: string;
begin
Result := DateTimeToStr(FValue);
end;
function TImplDataTimestampValue.GetValue: TDateTime;
function TDataTimestampValue.GetValue: TDateTime;
begin
Result := FValue;
end;
+369 -27
View File
@@ -7,20 +7,8 @@ uses
Myc.Data.Types;
type
// Implements the simple tuple data value.
TImplDataTupleValue = class(TInterfacedObject, IDataValue, IDataTupleValue)
private
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);
end;
// Implements the singleton tuple data type.
TImplDataTupleType = class(TInterfacedObject, IDataType, IDataTupleType)
TDataTupleType = class(TInterfacedObject, IDataType, IDataTupleType)
strict private
class var
FSingleton: IDataTupleType;
@@ -35,9 +23,97 @@ type
implementation
{ TImplDataTupleValue }
type
// Implements the simple tuple data value.
TDataTupleValue = class(TInterfacedObject, IDataValue, IDataTupleValue)
private
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);
end;
constructor TImplDataTupleValue.Create(const AItems: array of IDataValue);
// Optimized singleton implementation for a tuple with 0 elements.
TDataTupleValue0 = class(TInterfacedObject, IDataValue, IDataTupleValue)
strict private
class var
FSingleton: IDataTupleValue;
class constructor CreateClass;
private
function GetDataType: IDataType;
function GetAsString: string;
function GetItemCount: Integer;
function GetItem(Idx: Integer): IDataValue;
public
class property Singleton: IDataTupleValue read FSingleton;
end;
// Optimized implementation for a tuple with 1 element.
TDataTupleValue1 = class(TInterfacedObject, IDataValue, IDataTupleValue)
private
FItem0: IDataValue;
function GetDataType: IDataType;
function GetAsString: string;
function GetItemCount: Integer;
function GetItem(Idx: Integer): IDataValue;
public
constructor Create(const AItems: array of IDataValue);
end;
// Optimized implementation for a tuple with 2 elements.
TDataTupleValue2 = class(TInterfacedObject, IDataValue, IDataTupleValue)
private
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);
end;
// Optimized implementation for a tuple with 3 elements.
TDataTupleValue3 = class(TInterfacedObject, IDataValue, IDataTupleValue)
private
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);
end;
// Optimized implementation for a tuple with 4 elements.
TDataTupleValue4 = class(TInterfacedObject, IDataValue, IDataTupleValue)
private
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);
end;
// Optimized implementation for a tuple with 5 elements.
TDataTupleValue5 = 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);
end;
{ TDataTupleValue (generic implementation for N > 5 elements) }
constructor TDataTupleValue.Create(const AItems: array of IDataValue);
var
i: Integer;
begin
@@ -47,12 +123,12 @@ begin
FItems[i] := AItems[i];
end;
function TImplDataTupleValue.GetDataType: IDataType;
function TDataTupleValue.GetDataType: IDataType;
begin
Result := TImplDataTupleType.Singleton;
Result := TDataTupleType.Singleton;
end;
function TImplDataTupleValue.GetAsString: string;
function TDataTupleValue.GetAsString: string;
var
sb: TStringBuilder;
i: Integer;
@@ -73,34 +149,300 @@ begin
end;
end;
function TImplDataTupleValue.GetItem(Idx: Integer): IDataValue;
function TDataTupleValue.GetItem(Idx: Integer): IDataValue;
begin
Result := FItems[Idx];
end;
function TImplDataTupleValue.GetItemCount: Integer;
function TDataTupleValue.GetItemCount: Integer;
begin
Result := Length(FItems);
end;
{ TImplDataTupleType }
{ TDataTupleValue0 }
class constructor TImplDataTupleType.CreateClass;
class constructor TDataTupleValue0.CreateClass;
begin
FSingleton := TImplDataTupleType.Create;
FSingleton := TDataTupleValue0.Create;
end;
function TImplDataTupleType.CreateValue(const AItems: array of IDataValue): IDataTupleValue;
function TDataTupleValue0.GetAsString: string;
begin
Result := TImplDataTupleValue.Create(AItems);
Result := '()';
end;
function TImplDataTupleType.GetName: String;
function TDataTupleValue0.GetDataType: IDataType;
begin
Result := TDataTupleType.Singleton;
end;
function TDataTupleValue0.GetItem(Idx: Integer): IDataValue;
begin
raise EArgumentException.Create('Index out of bounds');
end;
function TDataTupleValue0.GetItemCount: Integer;
begin
Result := 0;
end;
{ TDataTupleValue1 }
constructor TDataTupleValue1.Create(const AItems: array of IDataValue);
begin
inherited Create;
FItem0 := AItems[0];
end;
function TDataTupleValue1.GetAsString: string;
begin
Result := '(' + FItem0.AsString + ')';
end;
function TDataTupleValue1.GetDataType: IDataType;
begin
Result := TDataTupleType.Singleton;
end;
function TDataTupleValue1.GetItem(Idx: Integer): IDataValue;
begin
case Idx of
0: Result := FItem0;
else
raise EArgumentException.Create('Index out of bounds');
end;
end;
function TDataTupleValue1.GetItemCount: Integer;
begin
Result := 1;
end;
{ TDataTupleValue2 }
constructor TDataTupleValue2.Create(const AItems: array of IDataValue);
begin
inherited Create;
FItem0 := AItems[0];
FItem1 := AItems[1];
end;
function TDataTupleValue2.GetAsString: string;
begin
Result := '(' + FItem0.AsString + ', ' + FItem1.AsString + ')';
end;
function TDataTupleValue2.GetDataType: IDataType;
begin
Result := TDataTupleType.Singleton;
end;
function TDataTupleValue2.GetItem(Idx: Integer): IDataValue;
begin
case Idx of
0: Result := FItem0;
1: Result := FItem1;
else
raise EArgumentException.Create('Index out of bounds');
end;
end;
function TDataTupleValue2.GetItemCount: Integer;
begin
Result := 2;
end;
{ TDataTupleValue3 }
constructor TDataTupleValue3.Create(const AItems: array of IDataValue);
begin
inherited Create;
FItem0 := AItems[0];
FItem1 := AItems[1];
FItem2 := AItems[2];
end;
function TDataTupleValue3.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(')');
Result := sb.ToString;
finally
sb.Free;
end;
end;
function TDataTupleValue3.GetDataType: IDataType;
begin
Result := TDataTupleType.Singleton;
end;
function TDataTupleValue3.GetItem(Idx: Integer): IDataValue;
begin
case Idx of
0: Result := FItem0;
1: Result := FItem1;
2: Result := FItem2;
else
raise EArgumentException.Create('Index out of bounds');
end;
end;
function TDataTupleValue3.GetItemCount: Integer;
begin
Result := 3;
end;
{ TDataTupleValue4 }
constructor TDataTupleValue4.Create(const AItems: array of IDataValue);
begin
inherited Create;
FItem0 := AItems[0];
FItem1 := AItems[1];
FItem2 := AItems[2];
FItem3 := AItems[3];
end;
function TDataTupleValue4.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(')');
Result := sb.ToString;
finally
sb.Free;
end;
end;
function TDataTupleValue4.GetDataType: IDataType;
begin
Result := TDataTupleType.Singleton;
end;
function TDataTupleValue4.GetItem(Idx: Integer): IDataValue;
begin
case Idx of
0: Result := FItem0;
1: Result := FItem1;
2: Result := FItem2;
3: Result := FItem3;
else
raise EArgumentException.Create('Index out of bounds');
end;
end;
function TDataTupleValue4.GetItemCount: Integer;
begin
Result := 4;
end;
{ TDataTupleValue5 }
constructor TDataTupleValue5.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 TDataTupleValue5.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 TDataTupleValue5.GetDataType: IDataType;
begin
Result := TDataTupleType.Singleton;
end;
function TDataTupleValue5.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 TDataTupleValue5.GetItemCount: Integer;
begin
Result := 5;
end;
{ TDataTupleType }
class constructor TDataTupleType.CreateClass;
begin
FSingleton := TDataTupleType.Create;
end;
function TDataTupleType.CreateValue(const AItems: array of IDataValue): IDataTupleValue;
begin
// Use optimized implementations for tuples with 0 to 5 elements.
case Length(AItems) of
0: Result := TDataTupleValue0.Singleton;
1: Result := TDataTupleValue1.Create(AItems);
2: Result := TDataTupleValue2.Create(AItems);
3: Result := TDataTupleValue3.Create(AItems);
4: Result := TDataTupleValue4.Create(AItems);
5: Result := TDataTupleValue5.Create(AItems);
else
// Use the generic implementation for tuples with more than 5 elements.
Result := TDataTupleValue.Create(AItems);
end;
end;
function TDataTupleType.GetName: String;
begin
Result := 'Tuple';
end;
function TImplDataTupleType.GetKind: TDataKind;
function TDataTupleType.GetKind: TDataKind;
begin
Result := dkTuple;
end;
+13 -13
View File
@@ -231,7 +231,7 @@ begin
try
if not FArrayTypeRegistry.TryGetValue(AElementType, Result) then
begin
Result := TImplDataArrayType.Create(AElementType);
Result := TDataArrayType.Create(AElementType);
FArrayTypeRegistry.Add(AElementType, Result);
end;
finally
@@ -241,7 +241,7 @@ end;
class function TDataType.Float: IDataFloatType;
begin
Result := TImplDataFloatType.Singleton;
Result := TDataFloatType.Singleton;
end;
function TDataType.GetName: String;
@@ -254,7 +254,7 @@ end;
class function TDataType.Ordinal: IDataOrdinalType;
begin
Result := TImplDataOrdinalType.Singleton;
Result := TDataOrdinalType.Singleton;
end;
class function TDataType.RecordOf(const Fields: TArray<TRecordField>): IDataRecordType;
@@ -263,7 +263,7 @@ begin
try
if not FRecordTypeRegistry.TryGetValue(Fields, Result) then
begin
Result := TImplDataRecordType.Create(Fields);
Result := TDataRecordType.Create(Fields);
FRecordTypeRegistry.Add(Fields, Result);
end;
finally
@@ -284,22 +284,22 @@ end;
class function TDataType.EnumOf(const AName: string; const AIdentifiers: array of string): IDataEnumType;
begin
Result := TImplDataEnumType.Create(AName, AIdentifiers);
Result := TDataEnumType.Create(AName, AIdentifiers);
end;
class function TDataType.Text: IDataTextType;
begin
Result := TImplDataTextType.Singleton;
Result := TDataTextType.Singleton;
end;
class function TDataType.Timestamp: IDataTimestampType;
begin
Result := TImplDataTimestampType.Singleton;
Result := TDataTimestampType.Singleton;
end;
class function TDataType.Tuple: IDataTupleType;
begin
Result := TImplDataTupleType.Singleton;
Result := TDataTupleType.Singleton;
end;
class operator TDataType.Implicit(const A: TDataType): IDataType;
@@ -377,27 +377,27 @@ end;
class function TDataValue.FromOrdinal(const AValue: Int64): IDataOrdinalValue;
begin
Result := TImplDataOrdinalType.Singleton.CreateValue(AValue);
Result := TDataOrdinalType.Singleton.CreateValue(AValue);
end;
class function TDataValue.FromFloat(const AValue: Double): IDataFloatValue;
begin
Result := TImplDataFloatType.Singleton.CreateValue(AValue);
Result := TDataFloatType.Singleton.CreateValue(AValue);
end;
class function TDataValue.FromText(const AValue: string): IDataTextValue;
begin
Result := TImplDataTextType.Singleton.CreateValue(AValue);
Result := TDataTextType.Singleton.CreateValue(AValue);
end;
class function TDataValue.FromTimestamp(const AValue: TDateTime): IDataTimestampValue;
begin
Result := TImplDataTimestampType.Singleton.CreateValue(AValue);
Result := TDataTimestampType.Singleton.CreateValue(AValue);
end;
class function TDataValue.FromTuple(const AItems: array of IDataValue): IDataTupleValue;
begin
Result := TImplDataTupleType.Singleton.CreateValue(AItems);
Result := TDataTupleType.Singleton.CreateValue(AItems);
end;
function TDataValue.GetDataType: IDataType;
+1
View File
@@ -1,3 +1,4 @@
T:\Myc\Src
T:\Myc\Src\Data
T:\Myc\Test
T:\Myc\AuraTrader