unit Myc.Data.POD; interface uses System.SysUtils, Myc.Data.Decimal, Myc.Data.Series; type // POD: Plain old data (...that fits into 128 bits) TScalarKind = ( skInteger, skInt64, skUInt64, skSingle, skDouble, skDateTime, skTimestamp, skBoolean, skChar, skPChar, skString, skBytes, skDecimal ); TScalarBytes = array[0..15] of Byte; TScalarPChar = array[0..7] of Char; TScalarString = String[15]; TScalarValue = record public // Factory methods for creating a TScalarValue without a kind. class function FromInteger(AValue: Integer): TScalarValue; static; inline; class function FromInt64(AValue: Int64): TScalarValue; static; inline; class function FromUInt64(AValue: UInt64): TScalarValue; static; inline; class function FromSingle(AValue: Single): TScalarValue; static; inline; class function FromDouble(AValue: Double): TScalarValue; static; inline; class function FromDateTime(AValue: TDateTime): TScalarValue; static; inline; class function FromTimestamp(AValue: TTimestamp): TScalarValue; static; inline; class function FromBoolean(AValue: Boolean): TScalarValue; static; inline; class function FromChar(AValue: Char): TScalarValue; static; inline; class function FromPChar(const AValue: String): TScalarValue; overload; static; inline; class function FromPChar(const AValue: TScalarPChar): TScalarValue; overload; static; inline; class function FromString(const AValue: String): TScalarValue; overload; static; inline; class function FromString(const AValue: TScalarString): TScalarValue; overload; static; inline; class function FromBytes(const AValue: TScalarBytes): TScalarValue; static; inline; class function FromDecimal(AValue: TDecimal): TScalarValue; static; inline; function GetAsInteger: Integer; inline; function GetAsInt64: Int64; inline; function GetAsUInt64: UInt64; inline; function GetAsSingle: Single; inline; function GetAsDouble: Double; inline; function GetAsDateTime: TDateTime; inline; function GetAsTimestamp: TTimestamp; inline; function GetAsBoolean: Boolean; inline; function GetAsChar: Char; inline; function GetAsPChar: PChar; inline; function GetAsString: ShortString; inline; function GetAsBytes: TScalarBytes; inline; function GetAsDecimal: TDecimal; inline; property AsInteger: Integer read GetAsInteger; property AsInt64: Int64 read GetAsInt64; property AsUInt64: UInt64 read GetAsUInt64; property AsSingle: Single read GetAsSingle; property AsDouble: Double read GetAsDouble; property AsDateTime: TDateTime read GetAsDateTime; property AsTimestamp: TTimestamp read GetAsTimestamp; property AsBoolean: Boolean read GetAsBoolean; property AsChar: Char read GetAsChar; property AsPChar: PChar read GetAsPChar; property AsString: ShortString read GetAsString; property AsBytes: TScalarBytes read GetAsBytes; property AsDecimal: TDecimal read GetAsDecimal; strict private case TScalarKind of skInteger: (FInteger: Integer); skInt64: (FInt64: Int64); skUInt64: (FUInt64: UInt64); skSingle: (FSingle: Single); skDouble: (FDouble: Double); skDateTime: (FDateTime: TDateTime); skTimestamp: (FTimestamp: TTimestamp); skBoolean: (FBoolean: Boolean); skChar: (FChar: Char); skPChar: (FPChar: TScalarPChar); skString: (FString: String[15]); skBytes: (FBytes: TScalarBytes); skDecimal: (FDecimal: TDecimal); end; // A scalar value with a type identifier. TScalar = record public // Creates a new scalar from a kind and a value. constructor Create(AKind: TScalarKind; const AValue: TScalarValue); // Factory methods for specific scalar types. class function FromInteger(AValue: Integer): TScalar; static; inline; class function FromInt64(AValue: Int64): TScalar; static; inline; class function FromUInt64(AValue: UInt64): TScalar; static; inline; class function FromSingle(AValue: Single): TScalar; static; inline; class function FromDouble(AValue: Double): TScalar; static; inline; class function FromDateTime(AValue: TDateTime): TScalar; static; inline; class function FromTimestamp(AValue: TTimestamp): TScalar; static; inline; class function FromBoolean(AValue: Boolean): TScalar; static; inline; class function FromChar(AValue: Char): TScalar; static; inline; class function FromPChar(const AValue: TScalarPChar): TScalar; static; inline; class function FromString(const AValue: String): TScalar; static; inline; class function FromBytes(const AValue: TScalarBytes): TScalar; static; inline; class function FromDecimal(AValue: TDecimal): TScalar; static; inline; function GetKind: TScalarKind; inline; function GetValue: TScalarValue; inline; function ToString: String; property Kind: TScalarKind read GetKind; property Value: TScalarValue read GetValue; strict private FKind: TScalarKind; FValue: TScalarValue; end; // Basic data structures using the scalar type (these are not POD of course) // An array of scalar values of the same kind. TScalarArray = record public // Creates a new scalar array. constructor Create(AKind: TScalarKind; const AItems: TArray); function GetKind: TScalarKind; inline; function GetItems: TArray; inline; property Kind: TScalarKind read GetKind; property Items: TArray read GetItems; strict private FKind: TScalarKind; FItems: TArray; end; TScalarTuple = TArray; // A field definition for a scalar record. TScalarRecordField = record public // Creates a new record field definition. constructor Create(const AName: String; AKind: TScalarKind); function GetName: String; inline; function GetKind: TScalarKind; inline; property Name: String read GetName; property Kind: TScalarKind read GetKind; strict private FName: String; FKind: TScalarKind; end; // A collection of field definitions that describe a scalar record. TScalarRecordDefinition = record public // Creates a new record definition. constructor Create(const AFields: TArray); function GetFields: TArray; inline; property Fields: TArray read GetFields; strict private FFields: TArray; end; // A record of scalar values, based on a definition. TScalarRecord = record public // Creates a new scalar record. constructor Create(const ADef: TScalarRecordDefinition; const AFields: TArray); function GetDef: TScalarRecordDefinition; inline; function GetFields: TArray; inline; function GetItems(const Name: String): TScalar; property Def: TScalarRecordDefinition read GetDef; property Fields: TArray read GetFields; property Items[const Name: String]: TScalar read GetItems; default; strict private FDef: TScalarRecordDefinition; FFields: TArray; end; // A time series of scalar values of the same kind. TScalarSeries = record public // Creates a new scalar series. constructor Create(AKind: TScalarKind; const AItems: TSeries); function GetKind: TScalarKind; inline; function GetItems: TSeries; inline; property Kind: TScalarKind read GetKind; property Items: TSeries read GetItems; strict private FKind: TScalarKind; FItems: TSeries; end; implementation { TScalarValue } class function TScalarValue.FromBoolean(AValue: Boolean): TScalarValue; begin Result.FBoolean := AValue; end; class function TScalarValue.FromBytes(const AValue: TScalarBytes): TScalarValue; begin Result.FBytes := AValue; end; class function TScalarValue.FromChar(AValue: Char): TScalarValue; begin Result.FChar := AValue; end; class function TScalarValue.FromDateTime(AValue: TDateTime): TScalarValue; begin Result.FDateTime := AValue; end; class function TScalarValue.FromDecimal(AValue: TDecimal): TScalarValue; begin Result.FDecimal := AValue; end; class function TScalarValue.FromDouble(AValue: Double): TScalarValue; begin Result.FDouble := AValue; end; class function TScalarValue.FromInt64(AValue: Int64): TScalarValue; begin Result.FInt64 := AValue; end; class function TScalarValue.FromInteger(AValue: Integer): TScalarValue; begin Result.FInteger := AValue; end; class function TScalarValue.FromPChar(const AValue: String): TScalarValue; var len, i: Integer; begin FillChar(Result.FPChar, SizeOf(Result.FPChar), #0); len := Length(AValue); if (len > 0) then begin if (len > Length(Result.FPChar)) then len := Length(Result.FPChar); for i := 0 to len - 1 do Result.FPChar[i] := AValue[i + 1]; end; end; class function TScalarValue.FromPChar(const AValue: TScalarPChar): TScalarValue; begin Result.FPChar := AValue; end; class function TScalarValue.FromSingle(AValue: Single): TScalarValue; begin Result.FSingle := AValue; end; class function TScalarValue.FromString(const AValue: String): TScalarValue; begin Result.FString := ShortString(AValue); end; class function TScalarValue.FromString(const AValue: TScalarString): TScalarValue; begin Result.FString := AValue; end; class function TScalarValue.FromTimestamp(AValue: TTimestamp): TScalarValue; begin Result.FTimestamp := AValue; end; class function TScalarValue.FromUInt64(AValue: UInt64): TScalarValue; begin Result.FUInt64 := AValue; end; function TScalarValue.GetAsBoolean: Boolean; begin Result := FBoolean; end; function TScalarValue.GetAsBytes: TScalarBytes; begin Result := FBytes; end; function TScalarValue.GetAsChar: Char; begin Result := FChar; end; function TScalarValue.GetAsDateTime: TDateTime; begin Result := FDateTime; end; function TScalarValue.GetAsDecimal: TDecimal; begin Result := FDecimal; end; function TScalarValue.GetAsDouble: Double; begin Result := FDouble; end; function TScalarValue.GetAsInt64: Int64; begin Result := FInt64; end; function TScalarValue.GetAsInteger: Integer; begin Result := FInteger; end; function TScalarValue.GetAsPChar: PChar; begin Result := @FPChar; end; function TScalarValue.GetAsSingle: Single; begin Result := FSingle; end; function TScalarValue.GetAsString: ShortString; begin Result := FString; end; function TScalarValue.GetAsTimestamp: TTimestamp; begin Result := FTimestamp; end; function TScalarValue.GetAsUInt64: UInt64; begin Result := FUInt64; end; { TScalar } constructor TScalar.Create(AKind: TScalarKind; const AValue: TScalarValue); begin FKind := AKind; FValue := AValue; end; class function TScalar.FromBoolean(AValue: Boolean): TScalar; begin Result.FKind := skBoolean; Result.FValue := TScalarValue.FromBoolean(AValue); end; class function TScalar.FromBytes(const AValue: TScalarBytes): TScalar; begin Result.FKind := skBytes; Result.FValue := TScalarValue.FromBytes(AValue); end; class function TScalar.FromChar(AValue: Char): TScalar; begin Result.FKind := skChar; Result.FValue := TScalarValue.FromChar(AValue); end; class function TScalar.FromDateTime(AValue: TDateTime): TScalar; begin Result.FKind := skDateTime; Result.FValue := TScalarValue.FromDateTime(AValue); end; class function TScalar.FromDecimal(AValue: TDecimal): TScalar; begin Result.FKind := skDecimal; Result.FValue := TScalarValue.FromDecimal(AValue); end; class function TScalar.FromDouble(AValue: Double): TScalar; begin Result.FKind := skDouble; Result.FValue := TScalarValue.FromDouble(AValue); end; class function TScalar.FromInt64(AValue: Int64): TScalar; begin Result.FKind := skInt64; Result.FValue := TScalarValue.FromInt64(AValue); end; class function TScalar.FromInteger(AValue: Integer): TScalar; begin Result.FKind := skInteger; Result.FValue := TScalarValue.FromInteger(AValue); end; class function TScalar.FromPChar(const AValue: TScalarPChar): TScalar; begin Result.FKind := skPChar; Result.FValue := TScalarValue.FromPChar(AValue); end; class function TScalar.FromSingle(AValue: Single): TScalar; begin Result.FKind := skSingle; Result.FValue := TScalarValue.FromSingle(AValue); end; class function TScalar.FromString(const AValue: String): TScalar; begin Result.FKind := skString; Result.FValue := TScalarValue.FromString(AValue); end; class function TScalar.FromTimestamp(AValue: TTimestamp): TScalar; begin Result.FKind := skTimestamp; Result.FValue := TScalarValue.FromTimestamp(AValue); end; class function TScalar.FromUInt64(AValue: UInt64): TScalar; begin Result.FKind := skUInt64; Result.FValue := TScalarValue.FromUInt64(AValue); end; function TScalar.GetKind: TScalarKind; begin Result := FKind; end; function TScalar.GetValue: TScalarValue; begin Result := FValue; end; function TScalar.ToString: String; begin case FKind of skInteger: Result := IntToStr(FValue.AsInteger); skInt64: Result := IntToStr(FValue.AsInt64); skUInt64: Result := UIntToStr(FValue.AsUInt64); skSingle: Result := FloatToStr(FValue.AsSingle); skDouble: Result := FloatToStr(FValue.AsDouble); skDateTime: Result := DateTimeToStr(FValue.AsDateTime); skTimestamp: Result := FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz', TimeStampToDateTime(FValue.AsTimestamp)); skBoolean: Result := BoolToStr(FValue.AsBoolean, True); skChar: Result := '''' + FValue.AsChar + ''''; skPChar: Result := FValue.AsPChar; // Already null-terminated skString: Result := '''' + string(FValue.AsString) + ''''; skBytes: Result := '(Bytes)'; skDecimal: Result := FloatToStr(Double(FValue.AsDecimal)); else Result := '[Unknown Scalar]'; end; end; { TScalarArray } constructor TScalarArray.Create(AKind: TScalarKind; const AItems: TArray); begin FKind := AKind; FItems := AItems; end; function TScalarArray.GetKind: TScalarKind; begin Result := FKind; end; function TScalarArray.GetItems: TArray; begin Result := FItems; end; { TScalarRecordField } constructor TScalarRecordField.Create(const AName: String; AKind: TScalarKind); begin FName := AName; FKind := AKind; end; function TScalarRecordField.GetName: String; begin Result := FName; end; function TScalarRecordField.GetKind: TScalarKind; begin Result := FKind; end; { TScalarRecordDefinition } constructor TScalarRecordDefinition.Create(const AFields: TArray); begin FFields := AFields; end; function TScalarRecordDefinition.GetFields: TArray; begin Result := FFields; end; { TScalarRecord } constructor TScalarRecord.Create(const ADef: TScalarRecordDefinition; const AFields: TArray); begin Assert(Length(ADef.Fields) = Length(AFields), 'Field definition and value count must match.'); FDef := ADef; FFields := AFields; end; function TScalarRecord.GetDef: TScalarRecordDefinition; begin Result := FDef; end; function TScalarRecord.GetFields: TArray; begin Result := FFields; end; function TScalarRecord.GetItems(const Name: String): TScalar; var i: Integer; fieldDef: TScalarRecordField; begin // Find the index of the field by name (case-insensitive) for i := 0 to Length(FDef.Fields) - 1 do begin fieldDef := FDef.Fields[i]; if (CompareText(fieldDef.Name, Name) = 0) then begin // Found it. Construct the TScalar result from the kind and value. Result.Create(fieldDef.Kind, FFields[i]); exit; end; end; // If we get here, the field was not found. raise EArgumentException.CreateFmt('Field "%s" not found in record definition.', [Name]); end; { TScalarSeries } constructor TScalarSeries.Create(AKind: TScalarKind; const AItems: TSeries); begin FKind := AKind; FItems := AItems; end; function TScalarSeries.GetKind: TScalarKind; begin Result := FKind; end; function TScalarSeries.GetItems: TSeries; begin Result := FItems; end; initialization Assert(sizeof(TScalarValue) = 16); end.