Merge pull request #212 from tromey/struct-layout add ffi_get_struct_offsets
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
diff --git a/doc/libffi.texi b/doc/libffi.texi
index 5c9fddd..94b7a9e 100644
--- a/doc/libffi.texi
+++ b/doc/libffi.texi
@@ -440,7 +440,8 @@ on the chosen ABI.
@item
The size and alignment of a new structure type will not be set by
-@code{libffi} until it has been passed to @code{ffi_prep_cif}.
+@code{libffi} until it has been passed to @code{ffi_prep_cif} or
+@code{ffi_get_struct_offsets}.
@item
A structure type cannot be shared across ABIs. Instead each ABI needs
@@ -448,8 +449,9 @@ its own copy of the structure type.
@end itemize
So, before examining these fields, it is safest to pass the
-@code{ffi_type} object to @code{ffi_prep_cif} first. This function
-will do all the needed setup.
+@code{ffi_type} object to @code{ffi_prep_cif} or
+@code{ffi_get_struct_offsets} first. This function will do all the
+needed setup.
@example
ffi_type *desired_type;
@@ -463,6 +465,28 @@ if (ffi_prep_cif (&cif, desired_abi, 0, desired_type, NULL) == FFI_OK)
@}
@end example
+@code{libffi} also provides a way to get the offsets of the members of
+a structure.
+
+@findex ffi_get_struct_offsets
+@defun ffi_status ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type, size_t *offsets)
+Compute the offset of each element of the given structure type.
+@var{abi} is the ABI to use; this is needed because in some cases the
+layout depends on the ABI.
+
+@var{sizes} is an out parameter. The caller is responsible for
+providing enough space for all the results to be written -- one
+element per element type in @var{struct_type}. If @var{sizes} is
+@code{NULL}, then the type will be laid out but not otherwise
+modified. This can be useful for accessing the type's size or layout,
+as mentioned above.
+
+This function returns @code{FFI_OK} on success; @code{FFI_BAD_ABI} if
+@var{abi} is invalid; or @code{FFI_BAD_TYPEDEF} if @var{struct_type}
+is invalid in some way. Note that only @code{FFI_STRUCT} types are
+valid here.
+@end defun
+
@node Arrays Unions Enums
@subsection Arrays, Unions, and Enumerations
diff --git a/include/ffi.h.in b/include/ffi.h.in
index cd3358f..9e65277 100644
--- a/include/ffi.h.in
+++ b/include/ffi.h.in
@@ -458,6 +458,9 @@ void ffi_call(ffi_cif *cif,
void *rvalue,
void **avalue);
+ffi_status ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type,
+ size_t *offsets);
+
/* Useful for eliminating compiler warnings */
#define FFI_FN(f) ((void (*)(void))f)
diff --git a/src/prep_cif.c b/src/prep_cif.c
index 5881ceb..43f0487 100644
--- a/src/prep_cif.c
+++ b/src/prep_cif.c
@@ -34,7 +34,7 @@
/* Perform machine independent initialization of aggregate type
specifications. */
-static ffi_status initialize_aggregate(ffi_type *arg)
+static ffi_status initialize_aggregate(ffi_type *arg, size_t *offsets)
{
ffi_type **ptr;
@@ -52,13 +52,15 @@ static ffi_status initialize_aggregate(ffi_type *arg)
while ((*ptr) != NULL)
{
if (UNLIKELY(((*ptr)->size == 0)
- && (initialize_aggregate((*ptr)) != FFI_OK)))
+ && (initialize_aggregate((*ptr), NULL) != FFI_OK)))
return FFI_BAD_TYPEDEF;
/* Perform a sanity check on the argument type */
FFI_ASSERT_VALID_TYPE(*ptr);
arg->size = ALIGN(arg->size, (*ptr)->alignment);
+ if (offsets)
+ *offsets++ = arg->size;
arg->size += (*ptr)->size;
arg->alignment = (arg->alignment > (*ptr)->alignment) ?
@@ -133,7 +135,8 @@ ffi_status FFI_HIDDEN ffi_prep_cif_core(ffi_cif *cif, ffi_abi abi,
#endif
/* Initialize the return type if necessary */
- if ((cif->rtype->size == 0) && (initialize_aggregate(cif->rtype) != FFI_OK))
+ if ((cif->rtype->size == 0)
+ && (initialize_aggregate(cif->rtype, NULL) != FFI_OK))
return FFI_BAD_TYPEDEF;
#ifndef FFI_TARGET_HAS_COMPLEX_TYPE
@@ -164,7 +167,8 @@ ffi_status FFI_HIDDEN ffi_prep_cif_core(ffi_cif *cif, ffi_abi abi,
{
/* Initialize any uninitialized aggregate type definitions */
- if (((*ptr)->size == 0) && (initialize_aggregate((*ptr)) != FFI_OK))
+ if (((*ptr)->size == 0)
+ && (initialize_aggregate((*ptr), NULL) != FFI_OK))
return FFI_BAD_TYPEDEF;
#ifndef FFI_TARGET_HAS_COMPLEX_TYPE
@@ -240,3 +244,18 @@ ffi_prep_closure (ffi_closure* closure,
}
#endif
+
+ffi_status
+ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type, size_t *offsets)
+{
+ if (! (abi > FFI_FIRST_ABI && abi < FFI_LAST_ABI))
+ return FFI_BAD_ABI;
+ if (struct_type->type != FFI_TYPE_STRUCT)
+ return FFI_BAD_TYPEDEF;
+
+#if HAVE_LONG_DOUBLE_VARIANT
+ ffi_prep_types (abi);
+#endif
+
+ return initialize_aggregate(struct_type, offsets);
+}
diff --git a/testsuite/libffi.call/offsets.c b/testsuite/libffi.call/offsets.c
new file mode 100644
index 0000000..23d88b3
--- /dev/null
+++ b/testsuite/libffi.call/offsets.c
@@ -0,0 +1,46 @@
+/* Area: Struct layout
+ Purpose: Test ffi_get_struct_offsets
+ Limitations: none.
+ PR: none.
+ Originator: Tom Tromey. */
+
+/* { dg-do run } */
+#include "ffitest.h"
+#include <stddef.h>
+
+struct test_1
+{
+ char c;
+ float f;
+ char c2;
+ int i;
+};
+
+int
+main (void)
+{
+ ffi_type test_1_type;
+ ffi_type *test_1_elements[5];
+ size_t test_1_offsets[4];
+
+ test_1_elements[0] = &ffi_type_schar;
+ test_1_elements[1] = &ffi_type_float;
+ test_1_elements[2] = &ffi_type_schar;
+ test_1_elements[3] = &ffi_type_sint;
+ test_1_elements[4] = NULL;
+
+ test_1_type.size = 0;
+ test_1_type.alignment = 0;
+ test_1_type.type = FFI_TYPE_STRUCT;
+ test_1_type.elements = test_1_elements;
+
+ CHECK (ffi_get_struct_offsets (FFI_DEFAULT_ABI, &test_1_type, test_1_offsets)
+ == FFI_OK);
+ CHECK (test_1_type.size == sizeof (struct test_1));
+ CHECK (offsetof (struct test_1, c) == test_1_offsets[0]);
+ CHECK (offsetof (struct test_1, f) == test_1_offsets[1]);
+ CHECK (offsetof (struct test_1, c2) == test_1_offsets[2]);
+ CHECK (offsetof (struct test_1, i) == test_1_offsets[3]);
+
+ return 0;
+}