Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Speicherausrichtung (Align) berechnen (https://www.delphipraxis.net/143800-speicherausrichtung-align-berechnen.html)

himitsu 23. Nov 2009 21:58


Speicherausrichtung (Align) berechnen
 
Ich dreh bald noch durch ... wie um Himmels Willen macht Delphi das nur? :wall:

Also wie entscheidet Delphi wann es wie ausrichtet?

Hier mal ein kleiner Test:
TY und TZ sind gleich groß, aber dennnoch ist es anders.
Delphi-Quellcode:
Type
{$ALIGN 1}
  TYb1 = Record x: byte; y: integer; End;
{$ALIGN 4}
  TYb = Record x: byte; y: integer; End;
  TZb = Record x: array[0..7] of Byte; End;
  TX = Record
    a: Byte;
    b: Int64;
  End;
  TY1 = Record
    a: Byte;
    b: TYb1;
  End;
  TY = Record
    a: Byte;
    b: TYb;
  End;
  TZ = Record
    a: Byte;
    b: TZb;
  End;

Var X: TX;
  Y: TY;
  Y1: TY1;
  Z: TZ;
  i, j, k,l: Integer;

Begin
  i := Integer(@X.b) - Integer(@X.a);
  j := Integer(@Y.b) - Integer(@Y.a);
  l := Integer(@Y1.b) - Integer(@Y1.a);
  k := Integer(@Z.b) - Integer(@Z.a);
  ShowMessage(Format('X.a-Xb: %d  X: %d'#10
    + 'Y.a-Yb: %d  Y: %d  TY: %d'#10
    + 'Z.a-Zb: %d  Z: %d  TZ: %d'#10
    + 'Y1.a-Y1b: %d  Y1: %d  TY1: %d'#10,
    [i, SizeOf(TX),
     j, SizeOf(TY), SizeOf(TYb),
     k, SizeOf(TZ), SizeOf(TZb),
     l, SizeOf(TY1), SizeOf(TYb1)]));

Code:
---------------------------
Test
---------------------------
X.a-Xb: 4   X: 12
Y.a-Yb: 4   Y: 12   TY: 8
Z.a-Zb: 1   Z: 9   TZ: 8
Y1.a-Y1b: 1   Y1: 6   TY1: 5

---------------------------
OK  
---------------------------
Im Packed-Modus läuft meine Record-Serialisierung, aber mit der automatischen Ausrichtung will es einfach nicht klappen. :cry:

Uwe Raabe 24. Nov 2009 10:03

Re: Speicherausrichtung (Align) berechnen
 
So ganz bin ich noch nicht dahintergekommen. Kannst du das Problem etwas genauer schildern?

Ich sehe zwar was du tust, aber ich weiß nicht was du erwartest und was stattdessen herauskommt.

himitsu 24. Nov 2009 10:21

Re: Speicherausrichtung (Align) berechnen
 
Ich hab in meinem himXML einige Datenserialisierungen implementiert,
womit man praktisch Record-Inhalte in eine entsprechende XML-Stucktur umwandeln kann.

Und nun suche ich, da sich meine bisherige Rechnung als "etwas falsch" herausstellte,
eine Möglichkeit das Alignment zu berechnen.
Also so, daß mein Code selbstständig die Ausrichtung bestimmen/berechnen kann.

gegeben seien z.B. diese Typen:
Delphi-Quellcode:
type
  TFileName = type String;
  THandle = LongWord;
  TWin32FindData = record
    dwFileAttributes:  DWORD;
    ftCreationTime:    TFileTime;
    ftLastAccessTime:  TFileTime;
    ftLastWriteTime:   TFileTime;
    nFileSizeHigh:     DWORD;
    nFileSizeLow:      DWORD;
    dwReserved0:       DWORD;
    dwReserved1:       DWORD;
    cFileName:         array[0..259] of Char;
    cAlternateFileName: array[0..13] of Char;
  end;

  TSearchRec = record
    Time:       Integer;
    Size:       Int64;
    Attr:       Integer;
    Name:       TFileName;
    ExcludeAttr: Integer;
    FindHandle: THandle;
    FindData:   TWin32FindData;
  end;
Diese sind nicht gepackt und werden entsprechend ab Delphi 2009 mit einem {$ALIGN 8} ausgerichtet.

Nun würde man meinem Serialisierer den Aufbau der Structur mitteilen und dieser würde (theoretisch) im vorliegenden Fall die übergebenen Daten-Adressen noch ausrichten und dann die Inhalte in der Datei speichern.
Delphi-Quellcode:
Var Data: TSearchRec;
  XML:  TXMLFile;
  RI, RIx: TXMLSerializeRecordInfo;

// einfach nur den Record mit irgendetwas befüllen
FindFirst(Application.ExeName, faAnyFile, Data);
FindClose(Data);

XML := TXMLFile.Create;
Try

  RI := TXMLSerializeRecordInfo.Create;
  Try
    //RI.Align(4);
    RI.Add('Time',   rtInteger);
    RI.Add('Size',   rtInt64);
    RI.Add('Attr',   rtInteger);
    RI.Add('Name',   rtString);
    RI.Add('Exclude', rtInteger);
    RI.Add('Handle', rtLongWord);
    RIx := RI.Add('Data', rtRecord);
    //RIx.Align(4);
    RIx.Add('Attributes', rtLongWord);
    RIx.Add('Creation',  rtWord64);
    RIx.Add('LastAccess', rtWord64);
    RIx.Add('LastWrite', rtWord64);
    RIx.Add('FileSize',  rtInt64);
    RIx.Add('Reserved',  rtInt64);
    RIx.Add('FileName',  rtCharArray, 260);
    RIx.Add('Alternate', rtCharArray, 14);
    XML.AddNode('via_Add').Serialize(Data, RI);
  Finally
    RI.Free;
  End;

  RI := TXMLSerializeRecordInfo.Create;
  Try
    //RI.Parse('I I8 I S I W4 R ( W4 W8 W8 W8 I8 I8 C260 C14 )');
    RI.Parse('ii8isiw4r(w4w8w8w8i8i8c260c14)');
    XML.AddNode('short').Serialize(Data, RI);
    RI.Clear;

    RI.Parse('I"Time" I8"Size" I"Attr" S"Name" I"Exclude" W4"Handle" R"Data" ('
      + 'W4"Attributes" W8"Creation" W8"LastAccess" W8"LastWrite" I8"FileSize"'
      + 'I8"Reserved" C260"FileName" C14"Alternate" )');
    XML.AddNode('long').Serialize(Data, RI);
  Finally
    RI.Free;
  End;

  XML.SaveToFile('Test.xml');
Finally
  XML.Free;
End;
Entstehen würde jetzt sowas:
XML-Code:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<xml>
  <via_Add>
    <Time himxml:type="LongInt">997743401</Time>
    <Size himxml:type="Int64">939520</Size>
    <Attr himxml:type="LongInt">8224</Attr>
    <Name himxml:type="WideString"/>
    <Exclude himxml:type="LongInt">8224</Exclude>
    <Handle himxml:type="LongWord">30042434</Handle>
    <Data himxml:type="Record">
      <Attributes himxml:type="LongWord">30043376</Attributes>
      <Creation himxml:type="Word64">30043376</Creation>
      <LastAccess himxml:type="Word64">939520</LastAccess>
      <LastWrite himxml:type="Word64">28429333425029120</LastWrite>
      <FileSize himxml:type="Int64">28429170223874163</FileSize>
      <Reserved himxml:type="Int64">6619256</Reserved>
      <FileName himxml:type="WideCharArray"/>
      <Alternate himxml:type="WideCharArray"/>
    </Data>
  </via_Add>
  <short>
    <rec:r0 himxml:type="LongInt">997743401</rec:r0>
    <rec:r1 himxml:type="Int64">939520</rec:r1>
    <rec:r2 himxml:type="LongInt">8224</rec:r2>
    <rec:r3 himxml:type="WideString"/>
    <rec:r4 himxml:type="LongInt">8224</rec:r4>
    <rec:r5 himxml:type="LongWord">30042434</rec:r5>
    <rec:r6 himxml:type="Record">
      <rec:r0 himxml:type="LongWord">30043376</rec:r0>
      <rec:r1 himxml:type="Word64">30043376</rec:r1>
      <rec:r2 himxml:type="Word64">939520</rec:r2>
      <rec:r3 himxml:type="Word64">28429333425029120</rec:r3>
      <rec:r4 himxml:type="Int64">28429170223874163</rec:r4>
      <rec:r5 himxml:type="Int64">6619256</rec:r5>
      <rec:r6 himxml:type="WideCharArray"/>
      <rec:r7 himxml:type="WideCharArray"/>
    </rec:r6>
  </short>
  <long>
    <Time himxml:type="LongInt">997743401</Time>
    <Size himxml:type="Int64">939520</Size>
    <Attr himxml:type="LongInt">8224</Attr>
    <Name himxml:type="WideString"/>
    <Exclude himxml:type="LongInt">8224</Exclude>
    <Handle himxml:type="LongWord">30042434</Handle>
    <Data himxml:type="Record">
      <Attributes himxml:type="LongWord">30043376</Attributes>
      <Creation himxml:type="Word64">30043376</Creation>
      <LastAccess himxml:type="Word64">939520</LastAccess>
      <LastWrite himxml:type="Word64">28429333425029120</LastWrite>
      <FileSize himxml:type="Int64">28429170223874163</FileSize>
      <Reserved himxml:type="Int64">6619256</Reserved>
      <FileName himxml:type="WideCharArray"/>
      <Alternate himxml:type="WideCharArray"/>
    </Data>
  </long>
</xml>
nur leider sind meine errechneten Adressen falsch, weswegen die Inhalte natürlich nicht stimmen :cry:


Tja und nun suche ich nach einer korrekten Berechnung, welches für einfache Daten theoretisch garnicht soschwer ist, aber schon im vorliegenden Fall taucht da ein Problem auf, wenn die untergeordneten Records (siehe Post #1) werden unterschiedlich ausgerichtet und gefür fehlt mir die zündende Idee, also nach welchen Regeln dieses nun alles abläuft.

Uwe Raabe 24. Nov 2009 14:51

Re: Speicherausrichtung (Align) berechnen
 
Ah ja, hätte mich bei dir auch gewundert, wenn die Frage so leicht zu beantworten wäre.

Ich denke mal, das Problem liegt darin, daß nur der Compiler weiß, wie die Records wirklich aligned sind - und das kann sich ja im Source an jeder Stelle ändern.

Spontan fällt mir folgende Lösung ein: Du übergibst bei TXMLSerializeRecordInfo.Create eine Record-Instanz als Parameter und bei jedem Add einen Pointer auf das jeweilige Feld. Dann kanst du die Offsets direkt berechnen. Nebenbei kann dann auch die Reihenfolge beliebig sein und es können Felder weggelassen werden.

Delphi-Quellcode:
 
RI := TXMLSerializeRecordInfo.Create(Data);
Try
  RI.Add('Time',   rtInteger, Data.Time);
  RI.Add('Size',   rtInt64, Data.Size);
  RI.Add('Attr',   rtInteger, Data.Attr);
  RI.Add('Name',   rtString, Data.Name);
...
Oder man steigt gleich auf D2010 um und benutzt RTTI :zwinker:

himitsu 24. Nov 2009 15:03

Re: Speicherausrichtung (Align) berechnen
 
Bei einer Typendefinition geht der Compiler erstmal davon aus, daß der Anfang des Records ausgerichtet ist und alles andere wird dann nach gewissen Regeln und Anhand von {$ALIGN *} und {$A*} ausgerichtet ... tja und genau diese Regeln bräuchte ich.

Hab jetzt erstmal 'nen ganzen Browser voller Tabs und versuch die ganzen Informationen irgendwie zusammenzubekommen, bzw. mir daraus 'nen Satz (Rechen)Regeln zu erstellen, aber für das Beispiel aus Post #1 hab ich noch nichts gefunden :?


Joar, was die neue 2010er RTTI betrifft, da muß ich irgendwann mal sehn, ob sich damit was machen läßt, aber vorallem für ältere Delphis ist das eh nicht möglich.

Nja, und mit den Pointern wollte ich nicht unbedingt rumspielen, kommt mir auch etwas umständlich vor.
(abgesehn davon kann man so auch "virtuelle" Records füllen :angel: )

brechi 24. Nov 2009 17:47

Re: Speicherausrichtung (Align) berechnen
 
Ich hätte Code da, kann dir den aber leider so net rausgeben.
Problem ist auch dass z.B. die Daten verschachtelt sein können (z.b. packed record mit weiterem aligned 4 record usw.)
Ich hab das genau für einen virtuellen Record gemacht.

Vielleicht kann ich dir morgen bisl Code dazugeben, aber eigentlich solltest das leicht selbst rausbekommen:

z.B. 4 Bytes groß:
Delphi-Quellcode:
{$A4}
type x = record
  a: byte; //@0
  b: byte; //@1
  c: word; //@2
end;
z.B: 6 Bytes groß
Delphi-Quellcode:
{$A4}
type x = record
  a: byte; //@0
  c: word; //@2
  b: byte; //@4
end;
(bytes können z.b. bei 0,1,2,3 startet, words nur bei 0,2,4 dwords bei 0,4,8)

dann ist der größte Datentyp wichtig (also ob byte, word oder dword) verwendet wird, denn der größte verwendete Datentyp bestimmt die absolute Größe des Records falls, dieser kleiner als das im Delphi eingestellte alignment ist.

heißt:

Delphi-Quellcode:
{$A4}
type x = record
  a: byte; //@0
  c: word; //@2
  b: byte; //@4
end;
größte = word = 2 (obwohl alignment 4) d.h. record Größe muss mod 2 = 0 sein -> 6 bytes Gesamt statt 5


Delphi-Quellcode:
{$A4}
type x = record
  a: byte; //@0
  c: dword; //@4
  b: byte; //@8
end;
größte = dword = 4, align = 4 -> 12 bytes Gesamt


Hoffe das hilft erstmal, ein string = pointer, ein string[xx] ist wie nen array of char (d.h. einzelne Bytes)
records in records sind wiedrum an ihrem alignment ausgerichtet (bzw. dem größten Datentyp falls dieser kleiner ist)

himitsu 24. Nov 2009 18:27

Re: Speicherausrichtung (Align) berechnen
 
Also zumindestens weiß ich jetzt, daß ich die untergeordneten Arrays/Records zuerst berechnen muß
und dann scheint es so, als wenn je nach Aufbau dieses untergeordnete Array anders im übergeordneten plaziert wird.

Delphi-Quellcode:
{$align 4}

1: record
  a: Byte;
  {3x Byte align}
  b: LongWord;
end;

2: record
  a: Byte;
  {kein align}
  b: array[0..3] of Byte;
end;

3: record
  a: Byte;
  {nun ratet mal}
  b: trec;
end;

trec: record
  case byte of
    0: (c: LongWord);
    1: (d: array[0..3] of Byte);
end;
Zitat:

Problem ist auch dass z.B. die Daten verschachtelt sein können
Joar, deswegen ist in der Struktur auch für jeden Bereich/Record eine eigene Align-Definition vorgesen (von 1=packed über 2, 4 bis 8)

thkerkmann 24. Nov 2009 19:23

Re: Speicherausrichtung (Align) berechnen
 
Hi,

schiesst Du nicht mit dieser Art der Serialisierung über das Ziel hinaus ?
Du analysierst ja bereits "händisch" den Record, um ihn dann wieder gepackt in die Struktur zu bringen.

Dann kann ich doch gleich einzeln die Elemente des Records serialisieren.

Oder hab ich da was falsch verstanden :?:

himitsu 24. Nov 2009 20:01

Re: Speicherausrichtung (Align) berechnen
 
nja, theoretisch kannst du den Record auch selber Element für Element abspeichern.

so bräuchtest du aber nur einmal den Aufbau definieren
(mal sehn, ab Delphi 2010 kannst dir eventuell auch von der neuen RTTI den Aufbau geben lassen)
und kannst diesen für die Serialisierung UND auch gleich für die Deserialisierung und auch an mehreren Stellen verwenden.

oder gleich die kurze Variante über'n Parser
Delphi-Quellcode:
RI.Parse('ii8isiw4r(w4w8w8w8i8i8c260c14)');
XML.AddNode('data').Serialize(Data, RI);
und schon hast du mit nur 2 Zeilen den Demo-Record mit 16 Werten gespeichert.

Delphi-Quellcode:
TXMLSerializeRDataType = (
  rtBoolean, rtBOOL, rtByte, rtWord, rtLongWord, rtWord64, {rtCardinal,}
  rtShortInt, rtSmallInt, rtLongInt, rtInt64, {rtInteger,}
  rtSingle, rtDouble, rtExtended, {rtReal,} rtCurrency, rtDateTime,
  rtAnsiCharArray, rtWideCharArray, {rtCharArray,}
  rtShortString, rtAnsiString, rtWideString, rtUnicodeString, {rtString,}
  rtBinary, rtVariant, rtObject, rtRecord, rtArray, rtDynArray,
  rtDummy, rtAlign, rtResetAlign);
von den Serialisierungen lassen sich ganze Variants, Objekte und Records/Arrays speichern
und vorallen bei den Records und Variants sind die ganzen Binär<>Text-Konvertierungen alle schon enthalten.

Vor 'ner Weile hatte ich dieses auch mal mißbraucht
Delphi-Quellcode:
Uses Dialogs, himXML__DataConv;

Var Src: packed Record
           B: Byte;
           i: Integer;
           S: String;
         End;
  Dest: packed Record
           S: String;
           i: Integer;
           W: Word;
         End;

Begin
  Src.B := 123;
  Src.i := 987654321;
  Src.S := 'Test';
  DataConverter(Src, Dest, 'p1 w1>B i>I s>S', 'p1 s>S i>I w2>W');
  ShowMessage(Format('"%s"  %d', [Dest.S, Dest.i]));
End;
Und mir 'nen Record-Converter gebastelt, da ich 'ne Binärdatei umstellen mußte.

himitsu 25. Nov 2009 21:24

Re: Speicherausrichtung (Align) berechnen
 
Die bißher "besten" Informationen hab ich nun erstmal hier gefunden:
http://en.wikipedia.org/wiki/Data_structure_alignment
http://msdn.microsoft.com/en-us/libr...8VS.71%29.aspx

Schlimm wird es aber bei sowas:
Delphi-Quellcode:
// align = 2, 4 oder 8

Type
  T1 = Record
    A: Array[0..1] of Byte;
    B: Byte;
  End;
  T2 = Record
    A: Word;
    B: Byte;
  End;
Die beiden Records sind (packed) 3 Byte groß, werden aber anders behandelt.

SizeOf(T1) = 3
SizeOf(T2) = 4

Und als SubRecords oder in Arrays wird T2 an 2er-Grenzen ausgerichter, aber T1 natürlich nicht.


Ich bekomme zwar langsam so Einiges zusammen, aber auch nur durch ausprobieren

hatte sogar mal vor lauter Wahnsinn 'ne größere Testreihe gemacht ... nur um ganz ganz ganz sicher zu gehn :wall:
Delphi-Quellcode:
{$ALIGN x}
Type TA = Record
    B: Array[1..y] of Byte;
    C: z;
  End;
Type TD = Record
    B: Array[1..Y] of Byte;
    C: z;
    D: Byte;
  End;
Code:
x = align
y = 1..7
z = 1:Byte 2:Word 4:LongWord 8:Int64 10:Extended

// > size  (C)
// V offset (B)
//
// Integer(@A.C) - Integer(@A.B)
//     align 1           align 2           align 4           align 8
//     1  2  4  8 10     1  2  4  8 10     1  2  4  8 10     1  2  4  8 10
// 1   1  1  1  1  1     1  2  2  2  2     1  2  4  4  4     1  2  4  8  8
// 2   2  2  2  2  2     2  2  2  2  2     2  2  4  4  4     2  2  4  8  8
// 3   3  3  3  3  3     3  4  4  4  4     3  4  4  4  4     3  4  4  8  8
// 4   4  4  4  4  4     4  4  4  4  4     4  4  4  4  4     4  4  4  8  8
// 5   5  5  5  5  5     5  6  6  6  6     5  6  8  8  8     5  6  8  8  8
// 6   6  6  6  6  6     6  6  6  6  6     6  6  8  8  8     6  6  8  8  8
// 7   7  7  7  7  7     7  8  8  8  8     7  8  8  8  8     7  8  8  8  8
//
// SizeOf(TA)
//     align 1           align 2           align 4           align 8
//     1  2  4  8 10     1  2  4  8 10     1  2  4  8 10     1  2  4  8 10
// 1   2  3  5  9 11     2  4  6 10 12     2  4  8 12 16     2  4  8 16 24
// 2   3  4  6 10 12     3  4  6 10 12     3  4  8 12 16     3  4  8 16 24
// 3   4  5  7 11 13     4  6  8 12 14     4  6  8 12 16     4  6  8 16 24
// 4   5  6  8 12 14     5  6  8 12 14     5  6  8 12 16     5  6  8 16 24
// 5   6  7  9 13 15     6  8 10 14 16     6  8 12 16 20     6  8 12 16 24
// 6   7  8 10 14 16     7  8 10 14 16     7  8 12 16 20     7  8 12 16 24
// 7   8  9 11 15 17     8 10 12 16 18     8 10 12 16 20     8 10 12 16 24
//
// SizeOf(TD)
//     align 1           align 2           align 4           align 8
//     1  2  4  8 10     1  2  4  8 10     1  2  4  8 10     1  2  4  8 10
// 1   3  4  6 10 12     3  6  8 12 14     3  6 12 16 16     3  6 12 24 24
// 2   4  5  7 11 13     4  6  8 12 14     4  6 12 16 16     4  6 12 24 24
// 3   5  6  8 12 14     5  8 10 14 16     5  8 12 16 16     5  8 12 24 24
// 4   6  7  9 13 15     6  8 10 14 16     6  8 12 16 16     6  8 12 24 24
// 5   7  8 10 14 16     7 10 12 16 18     7 10 16 20 20     7 10 16 24 24
// 6   8  9 11 15 17     8 10 12 16 18     8 10 16 20 20     8 10 16 24 24
// 7   9 10 12 16 18     9 12 14 18 20     9 12 16 20 20     9 12 16 24 24
und nun probiere ich verschiedene Record/Array-Kombinationen :?


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:54 Uhr.
Seite 1 von 2  1 2      

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