Navigation:  Programming Cookbook > External Interfacing >

Structure Packing

Previous pageReturn to chapter overviewNext page

Structures are not just the sum of their parts, at least when it comes to their size and how they should be aligned when embedded in other structures or arrays. The actual size of a structure, the alignment of the fields within it, and how it is itself aligned depends on the packing algorithm. The inputs to the packaging algorithm are:

Field size
Structure packing constant
Field alignment

In the case of a simple scalar field, a field will be aligned to the minimum of its size and the structure packing constant. In other words if packing is 4, a WORDField will align to the nearest 2-byte boundary, a DWORDField will be aligned to the nearest 4-byte boundary, and so will a DOUBLEField.

On Win32 platforms the standard packing constant is 8 (this corresponds to a C program compiled with the /Zp8 compiler option). That is the maximum amount of padding between two fields is 7 bytes, e.g. the size of the structure defined by the following definition would actually be 8-bytes:

defineFields

  self

      defineField: #aByte type: BYTEField new;

      defineField: #aDouble type: DOUBLEField new.

 

In the case of a structure, its alignment when embedded in another structure is the minimum of the structure packing constant and the alignment of its largest member (which might be another embedded structure). For example the structure RECT, when embedded in other structures such as DRAWITEMSTRUCT as defined above, will align to a 4-byte boundary because its largest member fields are 32-bit integers.

The total size of a structure is rounded up to its alignment. For example the Active-X Automation IDLDESC structure, defined as follows, would appear to require 6-bytes, but is actually an 8 byte structure because its largest member has 4-byte alignment.

defineFields

  self

      defineField: #dwReserved type: DWORDField filler;

      defineField: #wIDLFlags type: WORDField new

 

The packing algorithm described above is standard on Win32 platforms, and is sometimes referred to as "natural" packing, or 2-4 packing. As of version 3.0, Dolphin defaults to this standard Windows structure-packing algorithm.

Although structure packing may seem complicated, it is not normally something that it is necessary to worry about. Most external function libraries that use structures will follow the standard Windows packing, and designers often make some effort to arrange the fields so that they align correctly without requiring any packing. On the rare occasions where an alternative packing algorithm is required (the commonest being no packing), then it might be necessary to override one or more of the following methods:

ExternalStructure class>>packing
ExternalStructure class>>alignment
ExternalStructure class>>offsetFor:base:

Normally it will be sufficient to override just the first of these. For example the PRINTDLG structure overrides #packing as follows:

packing

  "Answer the default packing for instances of the receiver.

  This is the maximum alignment to which members of the structure

  after the first are padded. Each field type has a natural alignment

  (e.g. ints align to 32-bit boundaries), and it is the smaller of the

  packing and the natural alignment which is used to decide the

  padding necessary for the field in the structure.

  PRINTDLG is an old Win16 structure with byte packing."

  ^1

 

By overriding #packing can be used to accommodate different maximum packing, and has much the same effect as changing the argument to the C compilers /Zp flag. Specifying one as the packing indicates no packing, which is sometimes used for older Windows structures inherited from 16-bit Windows, or where space is at a premium.

An alternative to modifying the packing algorithm (overkill for a single case) is to specify the exact offsets of all the fields in the structure so that Dolphin doesn't have to work them out. For example:

defineFields

  "Define the fields of the RECT structure.

      typedef struct tagRECT {

          long left;

          long top;

          long right;

          long bottom;

      } RECT;

  "

  self

      defineField: #left type: SDWORDField new offset: 0;

      defineField: #top type: SDWORDField new offset: 4;

      defineField: #right type: SDWORDField new offset: 8;

      defineField: #bottom type: SDWORDField new offset: 12.

  self byteSize: 16

 

RECT is defined like this because the Active-X Component Wizard generated it, but manual definitions may follow this form too. Note that the byte size can also be manually specified.