Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 50 additions & 29 deletions ext/ffi_c/Struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,23 @@ typedef struct InlineArray_ {

static void struct_mark(void *data);
static void struct_free(void *data);
static size_t struct_memsize(const void *);
static VALUE struct_class_layout(VALUE klass);
static void struct_malloc(Struct* s);
static void struct_malloc(VALUE self, Struct* s);
static void inline_array_mark(void *);
static void store_reference_value(StructField* f, Struct* s, VALUE value);
static size_t inline_array_memsize(const void *);
static void store_reference_value(VALUE self, StructField* f, Struct* s, VALUE value);

const rb_data_type_t rbffi_struct_data_type = { /* extern */
.wrap_struct_name = "FFI::Struct",
.function = {
.dmark = struct_mark,
.dfree = struct_free,
.dsize = NULL,
.dsize = struct_memsize,
},
.flags = RUBY_TYPED_FREE_IMMEDIATELY
// IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
// macro to update VALUE references, as to trigger write barriers.
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
};
VALUE rbffi_StructClass = Qnil;

Expand All @@ -99,8 +103,8 @@ struct_allocate(VALUE klass)
Struct* s;
VALUE obj = TypedData_Make_Struct(klass, Struct, &rbffi_struct_data_type, s);

s->rbPointer = Qnil;
s->rbLayout = Qnil;
RB_OBJ_WRITE(obj, &s->rbPointer, Qnil);
RB_OBJ_WRITE(obj, &s->rbLayout, Qnil);

return obj;
}
Expand All @@ -125,9 +129,10 @@ struct_initialize(int argc, VALUE* argv, VALUE self)

/* Call up into ruby code to adjust the layout */
if (nargs > 1) {
s->rbLayout = rb_funcall2(CLASS_OF(self), id_layout, (int) RARRAY_LEN(rest), RARRAY_PTR(rest));
VALUE rbLayout = rb_funcall2(CLASS_OF(self), id_layout, (int) RARRAY_LEN(rest), RARRAY_PTR(rest));
RB_OBJ_WRITE(self, &s->rbLayout, rbLayout);
} else {
s->rbLayout = struct_class_layout(klass);
RB_OBJ_WRITE(self, &s->rbLayout, struct_class_layout(klass));
}

if (!rb_obj_is_kind_of(s->rbLayout, rbffi_StructLayoutClass)) {
Expand All @@ -138,9 +143,9 @@ struct_initialize(int argc, VALUE* argv, VALUE self)

if (rbPointer != Qnil) {
s->pointer = MEMORY(rbPointer);
s->rbPointer = rbPointer;
RB_OBJ_WRITE(self, &s->rbPointer, rbPointer);
} else {
struct_malloc(s);
struct_malloc(self, s);
}

return self;
Expand All @@ -163,7 +168,7 @@ struct_initialize_copy(VALUE self, VALUE other)
return self;
}

dst->rbLayout = src->rbLayout;
RB_OBJ_WRITE(self, &dst->rbLayout, src->rbLayout);
dst->layout = src->layout;

/*
Expand All @@ -172,17 +177,20 @@ struct_initialize_copy(VALUE self, VALUE other)
* be longer than just this struct.
*/
if (src->pointer->address != NULL) {
dst->rbPointer = rbffi_MemoryPointer_NewInstance(1, src->layout->size, false);
RB_OBJ_WRITE(self, &dst->rbPointer, rbffi_MemoryPointer_NewInstance(1, src->layout->size, false));
dst->pointer = MEMORY(dst->rbPointer);
memcpy(dst->pointer->address, src->pointer->address, src->layout->size);
} else {
dst->rbPointer = src->rbPointer;
RB_OBJ_WRITE(self, &dst->rbPointer, src->rbPointer);
dst->pointer = src->pointer;
}

if (src->layout->referenceFieldCount > 0) {
dst->rbReferences = ALLOC_N(VALUE, dst->layout->referenceFieldCount);
memcpy(dst->rbReferences, src->rbReferences, dst->layout->referenceFieldCount * sizeof(VALUE));
for (size_t index = 0; index < dst->layout->referenceFieldCount; index++) {
RB_OBJ_WRITTEN(self, Qundef, &dst->rbReferences[index]);
}
}

return self;
Expand Down Expand Up @@ -214,7 +222,7 @@ struct_layout(VALUE self)
}

if (s->layout == NULL) {
s->rbLayout = struct_class_layout(CLASS_OF(self));
RB_OBJ_WRITE(self, &s->rbLayout, struct_class_layout(CLASS_OF(self)));
TypedData_Get_Struct(s->rbLayout, StructLayout, &rbffi_struct_layout_data_type, s->layout);
}

Expand All @@ -232,18 +240,17 @@ struct_validate(VALUE self)
}

if (s->pointer == NULL) {
struct_malloc(s);
struct_malloc(self, s);
}

return s;
}

static void
struct_malloc(Struct* s)
struct_malloc(VALUE self, Struct* s)
{
if (s->rbPointer == Qnil) {
s->rbPointer = rbffi_MemoryPointer_NewInstance(s->layout->size, 1, true);

RB_OBJ_WRITE(self, &s->rbPointer, rbffi_MemoryPointer_NewInstance(s->layout->size, 1, true));
} else if (!rb_obj_is_kind_of(s->rbPointer, rbffi_AbstractMemoryClass)) {
rb_raise(rb_eRuntimeError, "invalid pointer in struct");
}
Expand All @@ -270,9 +277,15 @@ struct_free(void *data)
xfree(s);
}

static size_t
struct_memsize(const void *data)
{
const Struct *s = (const Struct *)data;
return sizeof(Struct) + (s->layout->referenceFieldCount * sizeof(VALUE));
}

static void
store_reference_value(StructField* f, Struct* s, VALUE value)
store_reference_value(VALUE self, StructField* f, Struct* s, VALUE value)
{
if (unlikely(f->referenceIndex == -1)) {
rb_raise(rb_eRuntimeError, "put_reference_value called for non-reference type");
Expand All @@ -282,11 +295,11 @@ store_reference_value(StructField* f, Struct* s, VALUE value)
int i;
s->rbReferences = ALLOC_N(VALUE, s->layout->referenceFieldCount);
for (i = 0; i < s->layout->referenceFieldCount; ++i) {
s->rbReferences[i] = Qnil;
RB_OBJ_WRITE(self, &s->rbReferences[i], Qnil);
}
}

s->rbReferences[f->referenceIndex] = value;
RB_OBJ_WRITE(self, &s->rbReferences[f->referenceIndex], value);
}


Expand Down Expand Up @@ -371,7 +384,7 @@ struct_aset(VALUE self, VALUE fieldName, VALUE value)
}

if (f->referenceRequired) {
store_reference_value(f, s, value);
store_reference_value(self, f, s, value);
}

return value;
Expand Down Expand Up @@ -407,7 +420,7 @@ struct_set_pointer(VALUE self, VALUE pointer)
}

s->pointer = MEMORY(pointer);
s->rbPointer = pointer;
RB_OBJ_WRITE(self, &s->rbPointer, pointer);
rb_ivar_set(self, id_pointer_ivar, pointer);

return self;
Expand Down Expand Up @@ -508,9 +521,11 @@ static const rb_data_type_t inline_array_data_type = {
.function = {
.dmark = inline_array_mark,
.dfree = RUBY_TYPED_DEFAULT_FREE,
.dsize = NULL,
.dsize = inline_array_memsize,
},
.flags = RUBY_TYPED_FREE_IMMEDIATELY
// IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
// macro to update VALUE references, as to trigger write barriers.
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
};

static VALUE
Expand All @@ -520,8 +535,8 @@ inline_array_allocate(VALUE klass)
VALUE obj;

obj = TypedData_Make_Struct(klass, InlineArray, &inline_array_data_type, array);
array->rbField = Qnil;
array->rbMemory = Qnil;
RB_OBJ_WRITE(obj, &array->rbMemory, Qnil);
RB_OBJ_WRITE(obj, &array->rbField, Qnil);

return obj;
}
Expand All @@ -534,6 +549,12 @@ inline_array_mark(void *data)
rb_gc_mark(array->rbMemory);
}

static size_t
inline_array_memsize(const void *data)
{
return sizeof(InlineArray);
}

/*
* Document-method: FFI::Struct::InlineArray#initialize
* call-seq: initialize(memory, field)
Expand All @@ -547,8 +568,8 @@ inline_array_initialize(VALUE self, VALUE rbMemory, VALUE rbField)
InlineArray* array;

TypedData_Get_Struct(self, InlineArray, &inline_array_data_type, array);
array->rbMemory = rbMemory;
array->rbField = rbField;
RB_OBJ_WRITE(self, &array->rbMemory, rbMemory);
RB_OBJ_WRITE(self, &array->rbField, rbField);

TypedData_Get_Struct(rbMemory, AbstractMemory, &rbffi_abstract_memory_data_type, array->memory);
TypedData_Get_Struct(rbField, StructField, &rbffi_struct_field_data_type, array->field);
Expand Down
13 changes: 13 additions & 0 deletions spec/ffi/struct_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1027,6 +1027,19 @@ class BuggedStruct < FFI::Struct
end
end

describe "Struct memsize", skip: RUBY_ENGINE != "ruby" do
it "has a memsize function" do
base_size = ObjectSpace.memsize_of(Object.new)

c = Class.new(FFI::Struct) do
layout :b, :bool
end
struct = c.new
size = ObjectSpace.memsize_of(struct)
expect(size).to be > base_size
end
end

describe "Struct order" do
before :all do
@struct = Class.new(FFI::Struct) do
Expand Down