diff --git a/Src/Myc.DataRecord.pas b/Src/Myc.DataRecord.pas index b04ff8f..b75f142 100644 --- a/Src/Myc.DataRecord.pas +++ b/Src/Myc.DataRecord.pas @@ -10,26 +10,25 @@ uses System.Generics.Defaults; type - // Provides generic access to a value of type T. - IAccess = interface - ['{1D422E3A-25C8-459B-A480-2E246942CF56}'] - function GetValue: T; - procedure SetValue(const Value: T); - property Value: T read GetValue write SetValue; - end; - - // Describes the memory layout of a field in the data buffer. - TFieldLayout = record - Key: string; - Offset: Integer; - Size: Integer; - TypeInfo: PTypeInfo; - end; - // A record that provides field-level access to data, stored in a packed byte buffer. TDataRecord = record public type + // Provides generic access to a value of type T. + IField = interface + function GetValue: T; + procedure SetValue(const Value: T); + property Value: T read GetValue write SetValue; + end; + + // Describes the field and its memory layout in the data buffer. + TFieldLayout = record + Name: string; + Offset: Integer; + Size: Integer; + TypeInfo: PTypeInfo; + end; + // Builder for dynamically creating a TDataRecord. TBuilder = record private @@ -44,37 +43,40 @@ type private FLayout: TArray; FBuffer: TBytes; + public class operator Initialize(out Dest: TDataRecord); class operator Finalize(var Dest: TDataRecord); - public - function GetAccess(const Name: String): IAccess; + class operator Assign(var Dest: TDataRecord; const [ref] Src: TDataRecord); class function CreateFromRec(const Rec: T): TDataRecord; static; class function CreateBuilder: TDataRecord.TBuilder; static; - class operator Assign(var Dest: TDataRecord; const [ref] Src: TDataRecord); + + function GetField(const Name: String): IField; + + property Layout: TArray read FLayout; end; // Accessor for reading/writing data directly from/to the TBytes buffer. - TByteAccessor = class(TInterfacedObject, IAccess) + TByteAccessor = class(TInterfacedObject, TDataRecord.IField) private FBuffer: TBytes; - FLayout: TFieldLayout; + FField: TDataRecord.TFieldLayout; public - constructor Create(ABuffer: TBytes; const ALayout: TFieldLayout); + constructor Create(ABuffer: TBytes; const AField: TDataRecord.TFieldLayout); function GetValue: T; procedure SetValue(const Value: T); end; // Comparer for sorting and searching field layouts by their string key (Singleton). - TFieldLayoutComparer = class(TComparer) + TFieldLayoutComparer = class(TComparer) strict private class var - FDefault: IComparer; + FDefault: IComparer; class constructor CreateClass; class destructor DestroyClass; public - function Compare(const Left, Right: TFieldLayout): Integer; override; - class property Default: IComparer read FDefault; + function Compare(const Left, Right: TDataRecord.TFieldLayout): Integer; override; + class property Default: IComparer read FDefault; end; procedure Testfunc; @@ -96,43 +98,43 @@ begin FDefault := nil; end; -function TFieldLayoutComparer.Compare(const Left, Right: TFieldLayout): Integer; +function TFieldLayoutComparer.Compare(const Left, Right: TDataRecord.TFieldLayout): Integer; begin - Result := TComparer.Default.Compare(Left.Key, Right.Key); + Result := TComparer.Default.Compare(Left.Name, Right.Name); end; {$endregion} {$region 'TByteAccessor'} -constructor TByteAccessor.Create(ABuffer: TBytes; const ALayout: TFieldLayout); +constructor TByteAccessor.Create(ABuffer: TBytes; const AField: TDataRecord.TFieldLayout); begin inherited Create; FBuffer := ABuffer; - FLayout := ALayout; + FField := AField; end; function TByteAccessor.GetValue: T; type PT = ^T; begin - Result := PT(@FBuffer[FLayout.Offset])^; + Result := PT(@FBuffer[FField.Offset])^; end; procedure TByteAccessor.SetValue(const Value: T); type PT = ^T; begin - PT(@FBuffer[FLayout.Offset])^ := Value; + PT(@FBuffer[FField.Offset])^ := Value; end; {$endregion} {$region 'TDataRecord'} -function TDataRecord.GetAccess(const Name: String): IAccess; +function TDataRecord.GetField(const Name: String): IField; var dummyLayout: TFieldLayout; index: Integer; begin Result := nil; - dummyLayout.Key := Name; + dummyLayout.Name := Name; if TArray.BinarySearch(FLayout, dummyLayout, index, TFieldLayoutComparer.Default) then begin @@ -236,7 +238,7 @@ begin var ofs: NativeUInt := 0; for pair in FStagedValues do begin - layout.Key := pair.Key; + layout.Name := pair.Key; layout.Size := pair.Value.DataSize; ofs := (ofs + 15) and not 15; @@ -287,8 +289,8 @@ procedure Testfunc; var testRec: TTestRec; R: TDataRecord; - nameAccess: IAccess; - bAccess: IAccess; + nameAccess: TDataRecord.IField; + bAccess: TDataRecord.IField; const JSON_DATA = '{"IntValue": 123, "StringValue": "Hello JSON", "FloatValue": 99.9, "BoolValue": true}'; begin @@ -301,12 +303,12 @@ begin R := TDataRecord.CreateFromRec(testRec); // Test reading a string value - nameAccess := R.GetAccess('Name'); + nameAccess := R.GetField('Name'); Assert(Assigned(nameAccess)); Assert(nameAccess.Value = 'Hi'); // Test reading an integer value - bAccess := R.GetAccess('B'); + bAccess := R.GetField('B'); Assert(Assigned(bAccess)); Assert(bAccess.Value = 5); @@ -314,11 +316,11 @@ begin bAccess.Value := 99; Assert(bAccess.Value = 99); // Verify it was written back to the buffer - var bAccess2 := R.GetAccess('B'); + var bAccess2 := R.GetField('B'); Assert(bAccess2.Value = 99); // Verify that a request for a wrong type returns nil - var aAccess := R.GetAccess('A'); + var aAccess := R.GetField('A'); Assert(not Assigned(aAccess)); // Test the builder pattern @@ -329,29 +331,29 @@ begin var T := S; - var dblAccess := T.GetAccess('ZValue'); + var dblAccess := T.GetField('ZValue'); Assert(Assigned(dblAccess)); Assert(dblAccess.Value = 222.0); - var strAccess := T.GetAccess('AValue'); + var strAccess := T.GetField('AValue'); Assert(Assigned(strAccess)); Assert(strAccess.Value = 'Builder Test'); { // Test JSON parsing var jsonRec := TDataRecord.CreateFromJSON(JSON_DATA); - var intAccess := jsonRec.GetAccess('IntValue'); + var intAccess := jsonRec.GetField('IntValue'); Assert(Assigned(intAccess)); Assert(intAccess.Value = 123); - strAccess := jsonRec.GetAccess('StringValue'); + strAccess := jsonRec.GetField('StringValue'); Assert(Assigned(strAccess)); Assert(strAccess.Value = 'Hello JSON'); - dblAccess := jsonRec.GetAccess('FloatValue'); + dblAccess := jsonRec.GetField('FloatValue'); Assert(Assigned(dblAccess)); Assert(dblAccess.Value = 99.9); - var boolAccess := jsonRec.GetAccess('BoolValue'); + var boolAccess := jsonRec.GetField('BoolValue'); Assert(Assigned(boolAccess)); Assert(boolAccess.Value = true); }