Unit refactoring
Fixed massive heap corruption bug in TDataRecord
This commit is contained in:
+75
-40
@@ -4,8 +4,6 @@ interface
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
System.Generics.Collections,
|
||||
System.Rtti,
|
||||
System.TypInfo;
|
||||
|
||||
type
|
||||
@@ -23,8 +21,11 @@ type
|
||||
procedure FromType(const [ref] Buffer: TBytes; SrcType: PTypeInfo; const Src);
|
||||
procedure ToType(const [ref] Buffer: TBytes; DstType: PTypeInfo; var Dst);
|
||||
|
||||
procedure Assign(const [ref] Buffer: TBytes; const Src); overload;
|
||||
procedure Finalize(const [ref] Buffer: TBytes);
|
||||
procedure InitField(const [ref] Buffer: TBytes);
|
||||
procedure AssignField(const [ref] Dest: TBytes; const [ref] Source: TBytes);
|
||||
procedure FinalizeField(const [ref] Buffer: TBytes);
|
||||
|
||||
procedure CopyField(Src, Dst: Pointer);
|
||||
|
||||
property Offset: Integer read FOffset;
|
||||
|
||||
@@ -51,6 +52,7 @@ type
|
||||
FFields: TArray<TField>;
|
||||
|
||||
constructor Create(const AFields: TArray<TField>);
|
||||
|
||||
public
|
||||
class function FromRecord<T>: TLayout; static;
|
||||
class function Construct(const Def: TArray<TFieldDef>): TLayout; static;
|
||||
@@ -60,15 +62,17 @@ type
|
||||
end;
|
||||
|
||||
const
|
||||
Align = 8;
|
||||
Align = sizeof(Pointer);
|
||||
|
||||
private
|
||||
FLayout: TLayout;
|
||||
FBuffer: TBytes;
|
||||
|
||||
public
|
||||
constructor Create(const ALayout: TLayout; const ABuffer: TBytes = nil);
|
||||
constructor Create(const ALayout: TLayout);
|
||||
|
||||
class operator Finalize(var Dest: TDataRecord);
|
||||
class operator Assign(var Dest: TDataRecord; const [ref] Src: TDataRecord);
|
||||
|
||||
class function FromRecord<T>: TDataRecord; overload; static;
|
||||
class function FromRecord<T>(const Src: T): TDataRecord; overload; static;
|
||||
@@ -79,52 +83,54 @@ type
|
||||
function GetValue<T>(const Name: String): T; overload;
|
||||
procedure GetValue(Idx: Integer; out Value); overload;
|
||||
|
||||
procedure CopyField(Idx: Integer; Dst: TDataRecord; DstIdx: Integer);
|
||||
procedure CopyValue(const SrcRec: TDataRecord; SrcIdx, DstIdx: Integer);
|
||||
|
||||
property Layout: TLayout read FLayout;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.Rtti;
|
||||
|
||||
const
|
||||
DataSize: array[TDataRecord.TFieldType] of Integer =
|
||||
(sizeof(Double), sizeof(Int64), sizeof(String), sizeof(TDateTime), sizeof(TDataRecord));
|
||||
|
||||
implementation
|
||||
{ TDataRecord }
|
||||
|
||||
uses
|
||||
System.Classes;
|
||||
|
||||
constructor TDataRecord.Create(const ALayout: TLayout; const ABuffer: TBytes = nil);
|
||||
constructor TDataRecord.Create(const ALayout: TLayout);
|
||||
begin
|
||||
FLayout := ALayout;
|
||||
FBuffer := ABuffer;
|
||||
|
||||
var bufSize := 0;
|
||||
if Length(FLayout.Fields) > 0 then
|
||||
with FLayout.Fields[High(FLayout.Fields)] do
|
||||
bufSize := Offset + AlignedSize;
|
||||
|
||||
if FBuffer = nil then
|
||||
SetLength(FBuffer, bufSize)
|
||||
else
|
||||
Assert(Length(FBuffer) >= bufSize);
|
||||
SetLength(FBuffer, bufSize);
|
||||
for var i := 0 to High(FLayout.Fields) do
|
||||
FLayout.Fields[i].InitField(FBuffer);
|
||||
end;
|
||||
|
||||
procedure TDataRecord.CopyField(Idx: Integer; Dst: TDataRecord; DstIdx: Integer);
|
||||
procedure TDataRecord.CopyValue(const SrcRec: TDataRecord; SrcIdx, DstIdx: Integer);
|
||||
begin
|
||||
Assert(Dst.Layout.Fields[DstIdx].FieldType = FLayout.Fields[Idx].FieldType);
|
||||
Dst.Layout.Fields[DstIdx].Assign(Dst.FBuffer, FBuffer[FLayout.Fields[DstIdx].Offset])
|
||||
end;
|
||||
Assert(SrcRec.Layout.Fields[SrcIdx].FieldType = FLayout.Fields[SrcIdx].FieldType);
|
||||
|
||||
{ TDataRecord }
|
||||
var Src := @SrcRec.FBuffer[SrcRec.Layout.Fields[SrcIdx].Offset];
|
||||
var Dst := @FBuffer[FLayout.Fields[DstIdx].Offset];
|
||||
|
||||
FLayout.Fields[SrcIdx].CopyField(Src, Dst);
|
||||
end;
|
||||
|
||||
class function TDataRecord.FromRecord<T>: TDataRecord;
|
||||
begin
|
||||
Result := TDataRecord.Create(TLayout.FromRecord<T>);
|
||||
Result.Create(TLayout.FromRecord<T>);
|
||||
end;
|
||||
|
||||
class function TDataRecord.FromRecord<T>(const Src: T): TDataRecord;
|
||||
begin
|
||||
Result := FromRecord<T>;
|
||||
Result.Create(TLayout.FromRecord<T>);
|
||||
|
||||
var ctx := TRttiContext.Create;
|
||||
var rttiType := ctx.GetType(TypeInfo(T));
|
||||
@@ -187,10 +193,22 @@ begin
|
||||
FLayout.Fields[idx].FromType(FBuffer, TypeInfo(T), Value);
|
||||
end;
|
||||
|
||||
class operator TDataRecord.Assign(var Dest: TDataRecord; const [ref] Src: TDataRecord);
|
||||
begin
|
||||
if Dest.FLayout.FFields <> Src.FLayout.FFields then
|
||||
begin
|
||||
Finalize(Dest);
|
||||
Dest.Create(Src.Layout);
|
||||
end;
|
||||
|
||||
for var i := 0 to High(Dest.Layout.Fields) do
|
||||
Dest.Layout.Fields[i].AssignField(Dest.FBuffer, Src.FBuffer);
|
||||
end;
|
||||
|
||||
class operator TDataRecord.Finalize(var Dest: TDataRecord);
|
||||
begin
|
||||
for var i := 0 to High(Dest.FLayout.Fields) do
|
||||
Dest.FLayout.Fields[i].Finalize(Dest.FBuffer);
|
||||
for var i := High(Dest.FLayout.Fields) downto 0 do
|
||||
Dest.Layout.Fields[i].FinalizeField(Dest.FBuffer);
|
||||
end;
|
||||
|
||||
constructor TDataRecord.TField.Create(const AName: string; AFieldType: TFieldType; AOffset: Integer);
|
||||
@@ -200,7 +218,7 @@ begin
|
||||
FOffset := AOffset;
|
||||
end;
|
||||
|
||||
procedure TDataRecord.TField.Finalize(const [ref] Buffer: TBytes);
|
||||
procedure TDataRecord.TField.InitField(const [ref] Buffer: TBytes);
|
||||
begin
|
||||
if not (FFieldType in [dfString, dfRecord]) then
|
||||
exit;
|
||||
@@ -209,8 +227,22 @@ begin
|
||||
|
||||
var P := @Buffer[FOffset];
|
||||
case FFieldType of
|
||||
dfString: PString(P)^ := '';
|
||||
dfRecord: TDataRecord(P^) := Default(TDataRecord);
|
||||
dfString: Initialize(PString(P)^);
|
||||
dfRecord: Initialize(TDataRecord(P^));
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TDataRecord.TField.FinalizeField(const [ref] Buffer: TBytes);
|
||||
begin
|
||||
if not (FFieldType in [dfString, dfRecord]) then
|
||||
exit;
|
||||
|
||||
Assert(FOffset + Size <= Length(Buffer));
|
||||
|
||||
var P := @Buffer[FOffset];
|
||||
case FFieldType of
|
||||
dfString: Finalize(PString(P)^);
|
||||
dfRecord: Finalize(TDataRecord(P^));
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -218,8 +250,6 @@ procedure TDataRecord.TField.FromType(const [ref] Buffer: TBytes; SrcType: PType
|
||||
begin
|
||||
Assert(FOffset + Size <= Length(Buffer));
|
||||
|
||||
Finalize(Buffer);
|
||||
|
||||
var Dst := @Buffer[FOffset];
|
||||
case FFieldType of
|
||||
dfFloat:
|
||||
@@ -317,19 +347,24 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TDataRecord.TField.Assign(const [ref] Buffer: TBytes; const Src);
|
||||
procedure TDataRecord.TField.AssignField(const [ref] Dest: TBytes; const [ref] Source: TBytes);
|
||||
begin
|
||||
Assert(FOffset + Size <= Length(Buffer));
|
||||
Assert(FOffset + Size <= Length(Dest));
|
||||
|
||||
Finalize(Buffer);
|
||||
var Dst := @Dest[FOffset];
|
||||
var Src := @Source[FOffset];
|
||||
|
||||
var Dst := @Buffer[FOffset];
|
||||
CopyField(Src, Dst);
|
||||
end;
|
||||
|
||||
procedure TDataRecord.TField.CopyField(Src, Dst: Pointer);
|
||||
begin
|
||||
case FFieldType of
|
||||
dfFloat: PDouble(Dst)^ := PDouble(@Src)^;
|
||||
dfInteger: PInt64(Dst)^ := PInt64(@Src)^;
|
||||
dfString: PString(Dst)^ := PString(@Src)^;
|
||||
dfTimestamp: PDateTime(Dst)^ := PDateTime(@Src)^;
|
||||
dfRecord: TDataRecord(Dst^) := TDataRecord(Src);
|
||||
dfFloat: PDouble(Dst)^ := PDouble(Src)^;
|
||||
dfInteger: PInt64(Dst)^ := PInt64(Src)^;
|
||||
dfString: PString(Dst)^ := PString(Src)^;
|
||||
dfTimestamp: PDateTime(Dst)^ := PDateTime(Src)^;
|
||||
dfRecord: TDataRecord(Dst^) := TDataRecord(Src^);
|
||||
else
|
||||
Assert(false);
|
||||
end;
|
||||
|
||||
Reference in New Issue
Block a user