add ffi_get_struct_offsets
authorTom Tromey <tom@tromey.com>
Wed, 18 Nov 2015 04:18:20 +0000 (21:18 -0700)
committerTom Tromey <tom@tromey.com>
Mon, 22 Feb 2016 23:07:55 +0000 (16:07 -0700)
doc/libffi.texi
include/ffi.h.in
src/prep_cif.c
testsuite/libffi.call/offsets.c [new file with mode: 0644]

index 5c9fddd..94b7a9e 100644 (file)
@@ -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
 
index cd3358f..9e65277 100644 (file)
@@ -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)
 
index 5881ceb..43f0487 100644 (file)
@@ -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 (file)
index 0000000..23d88b3
--- /dev/null
@@ -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;
+}