123456789_123456789_123456789_123456789_123456789_

Class: FFI::StructLayout

Relationships & Source Files
Namespace Children
Classes:
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, Type
Instance Chain:
self, Type
Inherits: FFI::Type
Defined in: ext/ffi_c/StructLayout.c,
lib/ffi/struct_layout.rb

Overview

This class aims at defining a struct layout.

Constant Summary

Type - Inherited

Struct

Class Method Summary

Type - Inherited

Instance Method Summary

Type - Inherited

Constructor Details

.new(fields, size, align) .new(StructLayout)

[ GitHub ]

  
# File 'ext/ffi_c/StructLayout.c', line 482

static VALUE
struct_layout_initialize(VALUE self, VALUE fields, VALUE size, VALUE align)
{
    StructLayout* layout;
    ffi_type* ltype;
    int i;

    TypedData_Get_Struct(self, StructLayout, &rbffi_struct_layout_data_type, layout);
    layout->fieldCount = RARRAY_LENINT(fields);
    RB_OBJ_WRITE(self, &layout->rbFieldMap, rb_hash_new());
    RB_OBJ_WRITE(self, &layout->rbFieldNames, rb_ary_new2(layout->fieldCount));
    layout->size = (int) FFI_ALIGN(NUM2INT(size),  NUM2INT(align));
    layout->align = NUM2INT(align);
    layout->fields = xcalloc(layout->fieldCount, sizeof(StructField *));
    layout->ffiTypes = xcalloc(layout->fieldCount + 1, sizeof(ffi_type *));
    RB_OBJ_WRITE(self, &layout->rbFields, rb_ary_new2(layout->fieldCount));
    layout->referenceFieldCount = 0;
    layout->base.ffiType->elements = layout->ffiTypes;
    layout->base.ffiType->size = layout->size;
    layout->base.ffiType->alignment = layout->align;

    ltype = layout->base.ffiType;
    for (i = 0; i < (int) layout->fieldCount; ++i) {
        VALUE rbField = rb_ary_entry(fields, i);
        VALUE rbName;
        StructField* field;
        ffi_type* ftype;


        if (!rb_obj_is_kind_of(rbField, rbffi_StructLayoutFieldClass)) {
            rb_raise(rb_eTypeError, "wrong type for field %d.", i);
        }
        rbName = rb_funcall2(rbField, rb_intern("name"), 0, NULL);

        TypedData_Get_Struct(rbField, StructField, &rbffi_struct_field_data_type, field);
        layout->fields[i] = field;

        if (field->type == NULL || field->type->ffiType == NULL) {
            rb_raise(rb_eRuntimeError, "type of field %d not supported", i);
        }

        ftype = field->type->ffiType;
        if (ftype->size == 0 && i < ((int) layout->fieldCount - 1)) {
            rb_raise(rb_eTypeError, "type of field %d has zero size", i);
        }

        if (field->referenceRequired) {
            field->referenceIndex = layout->referenceFieldCount++;
        }


        layout->ffiTypes[i] = ftype->size > 0 ? ftype : NULL;
        rb_hash_aset(layout->rbFieldMap, rbName, rbField);
        rb_ary_push(layout->rbFields, rbField);
        rb_ary_push(layout->rbFieldNames, rbName);
    }

    if (ltype->size == 0) {
        rb_raise(rb_eRuntimeError, "Struct size is zero");
    }

    rb_obj_freeze(layout->rbFieldMap);
    rb_obj_freeze(layout->rbFields);
    rb_obj_freeze(layout->rbFieldNames);
    rb_obj_freeze(self);

    return self;
}

Instance Method Details

#[](field) #a(field)

[ GitHub ]

  
# File 'ext/ffi_c/StructLayout.c', line 709

static VALUE
struct_layout_aref(VALUE self, VALUE field)
{
    StructLayout* layout;

    TypedData_Get_Struct(self, StructLayout, &rbffi_struct_layout_data_type, layout);

    return rb_hash_aref(layout->rbFieldMap, field);
}

#__union!

[ GitHub ]

  
# File 'ext/ffi_c/StructLayout.c', line 632

static VALUE
struct_layout_union_bang(VALUE self)
{
    StructLayout* layout;
    ffi_type *t = NULL;
    int count, i;
    bool float_only = true;
    const ffi_type *homogeneous = NULL;

    TypedData_Get_Struct(self, StructLayout, &rbffi_struct_layout_data_type, layout);

    /*
     * __union! replaces the real field types with a repeated filler type.
     * The filler must be chosen so that libffi uses the same calling convention
     * as the C compiler would for the real union.
     *
     * The rules vary by architecture:
     *
     * ARM64: A "Homogeneous Floating-point Aggregate" (HFA) is
     * passed in floating-point registers (d0-d3). An HFA requires all members
     * to be the *same* float type. {double, double} is an HFA; {float, double}
     * is NOT. Non-HFAs use integer registers.
     *
     * x86_64 targets using the System V ABI (Linux/macOS/BSD, not Windows):
     * an eightbyte containing only float/double fields is classified as SSE
     * and passed in XMM registers, regardless of whether the float types are
     * mixed. {float, double} is SSE; {int, double} is INTEGER.
     *
     * Strategy:
     * 1. If all fields are the same float type, use that type directly.
     *    Correct on all platforms.
     * 2. If fields are mixed float types (e.g. {float, double}):
     *    - On ARM64: not an HFA, use integer filler.
     *    - On x86_64 System V targets: still SSE class, use float filler.
     * 3. Otherwise: use integer filler.
     */
    homogeneous = find_homogeneous_leaf_type(layout->base.ffiType, &float_only);

    if (homogeneous != NULL && float_only) {
        /* Case 1: all fields are the same float type. */
        t = (ffi_type *) homogeneous;
    }
#if defined(__x86_64__) && !defined(_WIN32)
    else if (float_only) {
        /* Case 2: mixed float types use float filler on SysV x86_64. */
        t = (ffi_type *) find_type_by_alignment(get_float_types(), layout->align);
    }
#endif

    if (t == NULL) {
        /* Case 3: integer or mixed int/float — use integer filler */
        t = (ffi_type *) find_type_by_alignment(get_int_types(), layout->align);
    }

    if (t == NULL) {
        rb_raise(rb_eRuntimeError, "cannot create libffi union representation for alignment %d", layout->align);
        return Qnil;
    }

    count = (int) layout->size / (int) t->size;
    xfree(layout->ffiTypes);
    layout->ffiTypes = xcalloc(count + 1, sizeof(ffi_type *));
    layout->base.ffiType->elements = layout->ffiTypes;

    for (i = 0; i < count; ++i) {
        layout->ffiTypes[i] = t;
    }

    return self;
}

#fields #fields(list)

[ GitHub ]

  
# File 'ext/ffi_c/StructLayout.c', line 724

static VALUE
struct_layout_fields(VALUE self)
{
    StructLayout* layout;

    TypedData_Get_Struct(self, StructLayout, &rbffi_struct_layout_data_type, layout);

    return rb_ary_dup(layout->rbFields);
}

#members #list(of)

[ GitHub ]

  
# File 'ext/ffi_c/StructLayout.c', line 739

static VALUE
struct_layout_members(VALUE self)
{
    StructLayout* layout;

    TypedData_Get_Struct(self, StructLayout, &rbffi_struct_layout_data_type, layout);

    return rb_ary_dup(layout->rbFieldNames);
}

#offset_of(field_name) ⇒ Integer

Get the offset of a field.

[ GitHub ]

  
# File 'lib/ffi/struct_layout.rb', line 46

def offset_of(field_name)
  self[field_name].offset
end

#offsetsArray<Array(Symbol, Integer)>

Get an array of tuples (field name, offset of the field).

Returns:

  • (Array<Array(Symbol, Integer)>)

    Array<Array(Symbol, Integer)>

[ GitHub ]

  
# File 'lib/ffi/struct_layout.rb', line 40

def offsets
  members.map { |m| [ m, self[m].offset ] }
end

#to_a ⇒ ? #an(array)

[ GitHub ]

  
# File 'ext/ffi_c/StructLayout.c', line 754

static VALUE
struct_layout_to_a(VALUE self)
{
    StructLayout* layout;

    TypedData_Get_Struct(self, StructLayout, &rbffi_struct_layout_data_type, layout);

    return rb_ary_dup(layout->rbFields);
}