Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Padding Bytes herausfinden (https://www.delphipraxis.net/195327-padding-bytes-herausfinden.html)

Der schöne Günther 20. Feb 2018 12:50

Padding Bytes herausfinden
 
Angenommen folgender Record:

Delphi-Quellcode:
   TMyRecord = record
      a:   Byte;
      // ( 3 Padding Bytes)
      b:   Integer;
   end;
Der Record hat
Delphi-Quellcode:
SizeOf() = 8
denn zwischen
Delphi-Quellcode:
a
und
Delphi-Quellcode:
b
sind noch drei Byte Luft (zumindest unter Win32).

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:
TBitConverter
oder
Delphi-Quellcode:
FillChar(..)
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.

Uwe Raabe 20. Feb 2018 13:06

AW: Padding Bytes herausfinden
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1394253)
Kann ich gezielt herausfinden dass von diesen 8 Bytes an Position [1], [2] und [3] Padding Bytes sind?

Delphi-Quellcode:
var
  rec: TMyRecord;
begin
  if Sizeof(rec.a) < (UIntPtr(@rec.b) - UIntPtr(@rec.a)) then begin
    Writeln('Padding');
  end;
end;

himitsu 20. Feb 2018 13:12

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.
http://docwiki.embarcadero.com/RADSt...ichten_(Delphi)
Delphi-Quellcode:
TMyRecord = packed record
enspricht
Delphi-Quellcode:
{$A1}
.

Früher war es mal
Delphi-Quellcode:
{$A4}
und aktuell (seit knapp 10 Jahren) ist es meisten
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:
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
"a" ist 1 Byte, also wird es auf ganz 1-Byte ausgerichtet.
"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:
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 . . . .
Am Ende wird der RECORD nochmal mit dem aufgefüllt, was dem größten enthalten Typen entspricht, abgerundet auf das ALIGN.



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.

Der schöne Günther 20. Feb 2018 15:59

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:
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;
Das Zauberwort war System.Initialize(..), das kannte ich bislang nicht. Wenn der Record für b beispielsweise einen Integer hätte gibt der Compiler sogar völlig berechtigt eine Warnung
Zitat:

H2243 Der Ausdruck benötigt kein Initialize/Finalize
aus.

Wenn ich jetzt nichts übersehen habe ist das perfekt und genau was ich haben wollte!

Stevie 20. Feb 2018 16:05

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;

Der schöne Günther 20. Feb 2018 16:35

AW: Padding Bytes herausfinden
 
Super,
Delphi-Quellcode:
TRttiField.Offset
war mich nicht bewusst.

Jetzt muss ich mich entscheiden. Beides sieht gut aus. Hm... :?

Blup 21. Feb 2018 09:26

AW: Padding Bytes herausfinden
 
Meine Records haben häufig eine Clear-Methode, um den Speicher zu Nullen.
Delphi-Quellcode:
type
  TMyRecord = record
    Value1: Byte;
    Value2: string;
    procedure Clear;
  end;

implementation

procedure TMyRecord.Clear;
begin
  Finalize(Self);
  FillChar(Self, SizeOf(Self), #0);
end;
Finalize wird nur benötigt, wenn String, dynamische Array oder Interface-Member vorhanden sind.
Andernfalls meldet der Compiler das.

Der schöne Günther 6. Feb 2019 10:46

AW: Padding Bytes herausfinden
 
Zitat:

Zitat von Stevie (Beitrag 1394282)
Kurz mal die RTTI bemüht:
(…)

Kleiner Nachtrag: Hierbei wird der Fall vergessen wenn das letzte Feld im Record noch Padding hat. Nach der
Delphi-Quellcode:
for
-Schleife muss man noch einmal den Wert von offset mit
Delphi-Quellcode:
t.TypeSize
vergleichen. Die Differenz davon sind die letzten Padding-Bytes.


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;

peterbelow 7. Feb 2019 11:32

AW: Padding Bytes herausfinden
 
Delphi-Quellcode:
type
   TMyRecord = record
      a:  Byte;
      // ( 3 Padding Bytes)
      b:  String;
   end;
   PMyRecord = ^TMyRecord;
Man kann nun direkt die Offsets der Felder ermitteln, auch ohne extended RTTI zu bemühen:

NativeUInt(@PMyRecord(nil)^.a) // offset von a
NativeUInt(@PMyRecord(nil)^.b) // offset von b
etc.

Der schöne Günther 7. Feb 2019 11:42

AW: Padding Bytes herausfinden
 
Aber der Code gilt nur für
Delphi-Quellcode:
TMyRecord
und man muss ihn anpassen wenn man an
Delphi-Quellcode:
TMyRecord
etwas ändert. Eine Methode die auf alle Records passt hat schon was für sich 😎


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:21 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz