![]() |
Padding Bytes herausfinden
Angenommen folgender Record:
Delphi-Quellcode:
Der Record hat
TMyRecord = record
a: Byte; // ( 3 Padding Bytes) b: Integer; end;
Delphi-Quellcode:
denn zwischen
SizeOf() = 8
Delphi-Quellcode:
und
a
Delphi-Quellcode:
sind noch drei Byte Luft (zumindest unter Win32).
b
Kann ich gezielt herausfinden dass von diesen 8 Bytes an Position [1], [2] und [3] Padding Bytes sind? Ziel: Ich möchte für den Unit-Test einer Hash-Funktion für Records gezielt Padding-Bytes setzen. Ich kann natürlich mit Methoden wie von
Delphi-Quellcode:
oder
TBitConverter
Delphi-Quellcode:
direkt in den Speicher schreiben, allerdings bekomme ich dann arge Probleme wenn der Record Referenztypen wie Strings oder Interfaces enthält denn dort steht dann arger Müll. Einzige Ausnahme wären Nullen, aber ich möchte die Padding-Bytes in zwei Records ja unterschiedlich haben.
FillChar(..)
|
AW: Padding Bytes herausfinden
Zitat:
Delphi-Quellcode:
var
rec: TMyRecord; begin if Sizeof(rec.a) < (UIntPtr(@rec.b) - UIntPtr(@rec.a)) then begin Writeln('Padding'); end; end; |
AW: Padding Bytes herausfinden
Also "Messen", siehe Uwe Raabe,
oder selber berechnen. So schwer ist die Logic nicht. Bei krumen Sondertypen, wie dem Extended, oder geheimen Typen ala Variant, die nicht gleich als Record erkennbar sind, muß man nur erstmal genauer hinschauen und notfalls ein bissl rumprobieren. Erstmal kommt es drauf an, wie der Compiler eingestellt ist. ![]()
Delphi-Quellcode:
enspricht
TMyRecord = packed record
Delphi-Quellcode:
.
{$A1}
Früher war es mal
Delphi-Quellcode:
und aktuell (seit knapp 10 Jahren) ist es meisten
{$A4}
Delphi-Quellcode:
.
{$A8}
Und dann kann man das alles problemlos ausrechnen.
Delphi-Quellcode:
{$ALIGN 4}
TMySubRecord = record x: Word; y: Word; end; // oder TMySubRecord = array[0..1] of Word; TMyRecord = record a: Byte; // ( 3 Padding Bytes) b: Integer; c: Int64; d: TMySubRecord; end;
Code:
"a" ist 1 Byte, also wird es auf ganz 1-Byte ausgerichtet.
0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3
a . . . b b b b c c c c c c c c d d d d "b" ist 4 Byte, also wird es auf ganz 4-Byte ausgerichtet. "c" ist 8 Byte, also würde es auf ganz 8-Byte ausgerichtet, aber da ALIGN=4, wird es gekürzt und somit auf ganz 4-Byte ausgerichtet. "d" ist 4 Byte, aber laut seine "internen" Ausrichtung auf maximal 2 Byte ausgerichtet, also wird es auf ganz 2-Byte ausgerichtet. Wäre "b" ein Word, würde es auf 2 ausgerichtet und somit 2 Felder nach links rutschen.
Code:
Am Ende wird der RECORD nochmal mit dem aufgefüllt, was dem größten enthalten Typen entspricht, abgerundet auf das ALIGN.
0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 - ALIGN 4
a . b b c c c c c c c c d d d d 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 - ALIGN 8 a . b b . . . . c c c c c c c c d d d d . . . . Das $ALIGN ist sowas wie eine MAXimale Begrenzung für die Ausrichtung. Und Arrays "quasi" wie PACKED ... sie nehmen das ALIGN ihrer Felder an, welche keinen Freiraum zwischen sich lassen, abgesehn von ihrem eigenen Padding. |
AW: Padding Bytes herausfinden
Vielen Dank euch beiden für die Antworten erst einmal.
Ich hätte gedacht man kann etwas allgemeingültiges finden. Es hängt ja schon von der Reihenfolge der Felder ab, wie der Record deklariert ist. Compiler-Direktiven tun ihr übriges. Von Hand wollte ich da so wenig wie möglich machen. Ich glaube mein Ziel "Beschreibe alle Padding-Bytes mit X" erreiche ich folgendermaßen:
Delphi-Quellcode:
Das Zauberwort war
type
TMyRecord = record a: Byte; // ( 3 Padding Bytes) b: String; end; procedure p(); var x, y: TMyRecord; bytes_before: TBytes; bytes_after: TBytes; begin FillChar(x, SizeOf(x), $FF); System.Initialize(x); FillChar(y, SizeOf(y), $A5); System.Initialize(y); // Records sind nun semantisch, aber nicht bitweise gleich end; ![]() Zitat:
Wenn ich jetzt nichts übersehen habe ist das perfekt und genau was ich haben wollte! |
AW: Padding Bytes herausfinden
Kurz mal die RTTI bemüht:
Delphi-Quellcode:
procedure PrintPaddingBytes(typeInfo: PTypeInfo);
var ctx: TRttiContext; t: TRttiType; f: TRttiField; offset: Integer; begin t := ctx.GetType(typeInfo); offset := 0; for f in t.GetFields do begin case f.Offset - offset of 0:; 1: Writeln('padding at byte ', offset); else Writeln('padding at bytes ', offset, ' - ', f.Offset - 1); end; offset := f.Offset + f.FieldType.TypeSize; end; end; |
AW: Padding Bytes herausfinden
Super,
Delphi-Quellcode:
war mich nicht bewusst.
TRttiField.Offset
Jetzt muss ich mich entscheiden. Beides sieht gut aus. Hm... :? |
AW: Padding Bytes herausfinden
Meine Records haben häufig eine Clear-Methode, um den Speicher zu Nullen.
Delphi-Quellcode:
Finalize wird nur benötigt, wenn String, dynamische Array oder Interface-Member vorhanden sind.
type
TMyRecord = record Value1: Byte; Value2: string; procedure Clear; end; implementation procedure TMyRecord.Clear; begin Finalize(Self); FillChar(Self, SizeOf(Self), #0); end; Andernfalls meldet der Compiler das. |
AW: Padding Bytes herausfinden
Zitat:
Delphi-Quellcode:
-Schleife muss man noch einmal den Wert von offset mit
for
Delphi-Quellcode:
vergleichen. Die Differenz davon sind die letzten Padding-Bytes.
t.TypeSize
Mein "fillPaddingBytes" sähe dann so aus:
Delphi-Quellcode:
procedure TMyStructHelper.fillPaddingBytes(const pattern: Byte);
var ctx: TRttiContext; rttiType: TRttiType; field: TRttiField; offset: Integer; rawMemory: PByte; index: NativeInt; begin rttiType := ctx.GetType( TypeInfo(TMyStruct) ); offset := 0; rawMemory := @self; for field in rttiType.GetFields() do begin case field.offset - offset of 0: ; 1: rawMemory[offset] := pattern; else for index := offset to Pred(field.Offset) do rawMemory[index] := pattern; end; offset := field.offset + field.FieldType.TypeSize; end; for index := offset to Pred(rttiType.TypeSize) do rawMemory[index] := pattern; end; |
AW: Padding Bytes herausfinden
Delphi-Quellcode:
Man kann nun direkt die Offsets der Felder ermitteln, auch ohne extended RTTI zu bemühen:
type
TMyRecord = record a: Byte; // ( 3 Padding Bytes) b: String; end; PMyRecord = ^TMyRecord; NativeUInt(@PMyRecord(nil)^.a) // offset von a NativeUInt(@PMyRecord(nil)^.b) // offset von b etc. |
AW: Padding Bytes herausfinden
Aber der Code gilt nur für
Delphi-Quellcode:
und man muss ihn anpassen wenn man an
TMyRecord
Delphi-Quellcode:
etwas ändert. Eine Methode die auf alle Records passt hat schon was für sich 😎
TMyRecord
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:53 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz