Class: FFI::StructLayout

Inherits:
Type
  • Object
show all
Defined in:
ext/ffi_c/StructLayout.c,
lib/ffi/struct_layout.rb,
ext/ffi_c/StructLayout.c

Overview

This class aims at defining a struct layout.

Defined Under Namespace

Classes: Array, CharArray, Enum, Field, Function, InnerStruct, Mapped, Number, Pointer, String

Constant Summary

Constants inherited from Type

Type::Struct

Instance Method Summary collapse

Methods inherited from Type

#alignment, #inspect, #size

Constructor Details

#initialize(fields, size, align) ⇒ self

A new StructLayout instance.

Parameters:



482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
# 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) ⇒ StructLayout::Field

Get a field from the layout.

Parameters:

  • field (Symbol)

Returns:



709
710
711
712
713
714
715
716
717
# 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!Object



632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
# 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 

#fieldsArray<StructLayout::Field>

Get fields list.



724
725
726
727
728
729
730
731
732
# 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);
}

#membersArray<Symbol>

Get list of field names.

Returns:



739
740
741
742
743
744
745
746
747
# 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.

Returns:

  • (Integer)


46
47
48
# 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)>



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

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

#to_aArray<StructLayout::Field>

Get an array of fields.



754
755
756
757
758
759
760
761
762
# 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);
}