.. Mars Documentation
Copyright (C) 2013 Matt Giuca
.. This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
.. This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.. You should have received a copy of the GNU General Public License
along with this program. If not, see .
:mod:`struct` --- Native struct and union helpers
=================================================
.. sectionauthor:: Matt Giuca
.. moduleauthor:: Matt Giuca
.. module:: struct
:synopsis: Provides functions to help read and write fields of structs and
unions when interfacing with native code.
This module, designed for use in conjunction with the :mod:`native` module,
allows C structs and unions to be declared dynamically, to calculate the
offset of struct fields and the size of structs and unions. This is useful for
reading and writing struct fields, as well as indexing into arrays of structs
and unions.
The major advantage to using this module is that it attempts to get field
alignment right, inserting padding bytes where necessary to match the C
compiler.
.. note::
We say "attempts to get alignment right" because `alignment is hard
`_. It varies across
different architectures, platforms and compilers, and there are a lot of
corner cases. If you find that the alignment is broken in a particular
case, please `file a bug report `_.
.. highlight:: c
For example, consider the following C struct definition::
struct person
{
char gender; // Male or female.
short country; // Country ID number.
double age; // Years.
int height; // Centimetres.
};
.. highlight:: mars
Accessing the fields of this struct from Mars is non-trivial, due to padding.
On 64-bit Linux, ``double`` has a size and alignment of 8, and ``int`` has a
size and alignment of 4, so the following constraints apply:
* *gender* will always be at offset 0, since it is first.
* *country* will be at offset 2, since it must be 2-byte aligned. So there
will be a padding byte after *gender*.
* *age* will be at offset 8, since it must be 8-byte aligned. So there will be
4 padding bytes after *country*.
* *height* will be at offset 16, right after *age*.
* There will be 4 padding bytes after *height*, to round the struct up to a
multiple of 8 (due to the presence of the ``double`` earlier on). So the
whole struct will have a size of 24 bytes.
However, on 32-bit Linux, ``double`` has 4-byte alignment, so you cannot
assume these offsets. This module helps you calculate the correct offsets of
struct fields with the correct alignment. To do so, simply call
:func:`build_struct_prims` with a string that denotes the primitive types of
the struct fields, and store it in a global constant::
def struct_person :: StructDef = build_struct_prims("bhdi")
.. highlight:: marscon
Now, the :func:`sizeof_struct` and :func:`struct_fieldoffset` functions can
provide information about how to access struct fields::
?> struct_fieldoffset(struct_person, 0)
0
?> struct_fieldoffset(struct_person, 1)
2
?> struct_fieldoffset(struct_person, 2)
8
?> struct_fieldoffset(struct_person, 3)
16
?> sizeof_struct(struct_person)
24
.. highlight:: mars
If you have a pointer to such a struct, you can access a field using
:func:`cptr_add`, and adding one of the field offsets. Or, you can use the
convenience function :func:`struct_fieldptr` to do just that. If you are
accessing an array of structs, you should use :func:`sizeof_struct` to find an
individual struct element of the array.
The same concept applies for unions, but they are much simpler. Because all
elements of a union are found at offset 0, there is no
:func:`union_fieldoffset` or :func:`union_fieldptr` function. However, you can
still use this to compute the size of a union (simply the maximum size of all
of the union's fields).
.. type:: AlignInfo
::
AlignInfo(size :: Num, alignment :: Num)
Stores information about the size and alignment of a native type (either a
primitive type, or a struct or union).
* *size*: The size of the type in bytes. Must be a multiple of *alignment*.
* *alignment*: Elements of this type must be stored at an address which is
a multiple of this number. Must be >0.
.. constant:: aligninfo_cchar :: AlignInfo
The alignment info for :type:`CChar`, for use with :func:`build_struct`.
.. constant:: aligninfo_cshort :: AlignInfo
The alignment info for :type:`CShort`, for use with :func:`build_struct`.
.. constant:: aligninfo_cint :: AlignInfo
The alignment info for :type:`CInt`, for use with :func:`build_struct`.
.. constant:: aligninfo_cfloat :: AlignInfo
The alignment info for :type:`CFloat`, for use with :func:`build_struct`.
.. constant:: aligninfo_cdouble :: AlignInfo
The alignment info for :type:`CDouble`, for use with :func:`build_struct`.
.. constant:: aligninfo_cptr :: AlignInfo
The alignment info for :type:`CPtr`, for use with :func:`build_struct`.
.. type:: StructDef
Stores the computed field offsets and size of a struct.
.. function:: build_struct_prims(format :: Array(Num)) :: StructDef
Builds a :type:`StructDef` for a struct with only primitive fields from a
format string describing the fields of the native struct.
.. note::
This function does all the expensive work of computing the size and
offsets, storing the result in the :type:`StructDef`. The subsequent
operations are cheap lookups. Therefore, it is recommended that you call
:func:`build_struct_prims` once for each C struct and store the result
in a global constant for quick recall.
.. warning::
Do not use this to describe structs with structs inside them (flattening
out the fields); this may produce incorrect alignment. Instead, use
:func:`build_struct`.
*format* is a string containing a character for each struct field. The
character indicates the primitive type of the field, as shown in this
table.
+------------------+------------+-----------------+
| Format character | C type | Mars type |
+==================+============+=================+
| ``b`` | ``char`` | :type:`CChar` |
+------------------+------------+-----------------+
| ``h`` | ``short`` | :type:`CShort` |
+------------------+------------+-----------------+
| ``i`` | ``int`` | :type:`CInt` |
+------------------+------------+-----------------+
| ``f`` | ``float`` | :type:`CFloat` |
+------------------+------------+-----------------+
| ``d`` | ``double`` | :type:`CDouble` |
+------------------+------------+-----------------+
| ``p`` | ``void*`` | :type:`CPtr` |
+------------------+------------+-----------------+
.. function:: build_struct(fields :: Array(AlignInfo)) :: StructDef
Builds a :type:`StructDef` for a struct with arbitrary fields. This allows
structs to be specified with fields of struct or union types.
*fields* is a list of alignment infos, each describing a field of
primitive, struct, or union type. For a primitive type, pass the
``aligninfo_*`` constant for that type. For example,
``build_struct([aligninfo_int, aligninfo_char])`` is equivalent to
``build_struct_prims("ib")``. For a struct type, pass the result of
:func:`aligninfo_struct`. For a union type, pass the result of
:func:`aligninfo_union`.
.. highlight:: c
For example, consider the following C struct definitions::
struct inner
{
char a;
int b;
};
struct outer
{
char x;
char y;
struct inner z;
};
.. highlight:: mars
This can be described in Mars as follows::
def struct_inner :: StructDef = build_struct_prims("bi")
def struct_outer :: StructDef = build_struct([aligninfo_cchar, aligninfo_cchar, aligninfo_struct(struct_inner)])
Note that you cannot simply write ``build_struct_prims("bbbi")`` because
the inner struct must be aligned to a 4-byte boundary, even though it
starts with a ``char``.
.. function:: sizeof_struct(struct :: StructDef) :: Num
Returns the size of a native struct, including any padding bytes. This is
equivalent to the C expression applying ``sizeof`` to the struct type.
.. function:: aligninfo_struct(struct :: StructDef) :: AlignInfo
Returns the alignment info for a native struct, for use with
:func:`build_struct`.
.. function:: struct_fieldoffset(struct :: StructDef, fieldnum :: Num) :: Num
Returns the offset of struct field number *fieldnum* within a native
struct, in bytes. If *p* is a pointer to a value of the struct type, this
is the number of bytes after *p* where the given field may be found.
.. function:: struct_fieldptr(struct :: StructDef, pointer :: CPtr, fieldnum :: Num) :: CPtr
Helper method for finding the pointer to a struct field *fieldnum*. If
*pointer* is a pointer to a value of the struct type, this returns a
pointer to the given field.
This is equivalent to ``cptr_add(pointer, struct_fieldoffset(struct,
fieldnum))``.
.. type:: UnionDef
Stores the computed size of a union.
.. function:: build_union_prims(format :: Array(Num)) :: UnionDef
Builds a :type:`UnionDef` for a union with only primitive fields from a
format string describing the fields of the native union.
.. note::
This function does all the expensive work of computing the size, storing
the result in the :type:`UnionDef`. The subsequent operations are cheap
lookups. Therefore, it is recommended that you call
:func:`build_union_prims` once for each C union and store the result in
a global constant for quick recall.
*format* is a string containing a character for each union field. See
:func:`build_struct_prims` for an explanation of this string's syntax.
.. function:: build_union(fields :: Array(AlignInfo)) :: UnionDef
Builds a :type:`UnionDef` for a union with arbitrary fields. This allows
unions to be specified with fields of struct or union types.
*fields* is a list of alignment infos, each describing a field of
primitive, struct, or union type. See :func:`build_struct` for more
information.
.. function:: sizeof_union(union :: UnionDef) :: Num
Returns the size of a native union. This is equivalent to the C expression
applying ``sizeof`` to the union type.
.. function:: aligninfo_union(union :: UnionDef) :: AlignInfo
Returns the alignment info for a union, for use with :func:`build_struct`.