diff options
author | Wayne Meissner <wmeissner@gmail.com> | 2010-01-24 11:12:18 +1000 |
---|---|---|
committer | Wayne Meissner <wmeissner@gmail.com> | 2010-01-24 11:12:18 +1000 |
commit | 5c06940e40fd1ffd0bf62b7e58d38be45dc39e09 (patch) | |
tree | eeed9b11f9b8fa500cf3202f3b1426b623efa934 | |
parent | 7e74b06016d6d909dd7f2a663670aa6b277f73ed (diff) | |
download | ffi-5c06940e40fd1ffd0bf62b7e58d38be45dc39e09.tar.gz |
Add support for assigning to inline arrays in Struct.
e.g.
class S < FFI::Struct
layout :s, [ :char, 20 ]
end
s = S.new
s[:s] = "Test"
-rw-r--r-- | ext/ffi_c/Struct.c | 14 | ||||
-rw-r--r-- | ext/ffi_c/Struct.h | 3 | ||||
-rw-r--r-- | ext/ffi_c/StructLayout.c | 81 |
3 files changed, 92 insertions, 6 deletions
diff --git a/ext/ffi_c/Struct.c b/ext/ffi_c/Struct.c index 710f642..6516dd1 100644 --- a/ext/ffi_c/Struct.c +++ b/ext/ffi_c/Struct.c @@ -79,6 +79,8 @@ VALUE rbffi_StructClass = Qnil; static VALUE StructLayoutBuilderClass = Qnil; VALUE rbffi_StructInlineArrayClass = Qnil; +VALUE rbffi_StructLayoutCharArrayClass = Qnil; + static ID id_pointer_ivar = 0, id_layout_ivar = 0; static ID id_get = 0, id_put = 0, id_to_ptr = 0, id_to_s = 0, id_layout = 0; @@ -727,8 +729,8 @@ inline_array_to_s(VALUE self) Data_Get_Struct(array->field->rbType, ArrayType, arrayType); if (arrayType->componentType->nativeType != NATIVE_INT8 && arrayType->componentType->nativeType != NATIVE_UINT8) { - rb_raise(rb_eNoMethodError, "to_s not defined for this array type"); - return Qnil; + VALUE dummy = Qnil; + return rb_call_super(0, &dummy); } argv[0] = UINT2NUM(array->field->offset); @@ -775,6 +777,9 @@ rbffi_Struct_Init(VALUE moduleFFI) rbffi_StructInlineArrayClass = rb_define_class_under(rbffi_StructClass, "InlineArray", rb_cObject); rb_global_variable(&rbffi_StructInlineArrayClass); + rbffi_StructLayoutCharArrayClass = rb_define_class_under(rbffi_StructLayoutClass, + "CharArray", rbffi_StructInlineArrayClass); + rb_global_variable(&rbffi_StructLayoutCharArrayClass); rb_define_alloc_func(StructClass, struct_allocate); @@ -820,10 +825,11 @@ rbffi_Struct_Init(VALUE moduleFFI) rb_define_method(rbffi_StructInlineArrayClass, "each", inline_array_each, 0); rb_define_method(rbffi_StructInlineArrayClass, "size", inline_array_size, 0); rb_define_method(rbffi_StructInlineArrayClass, "to_a", inline_array_to_a, 0); - rb_define_method(rbffi_StructInlineArrayClass, "to_s", inline_array_to_s, 0); - rb_define_alias(rbffi_StructInlineArrayClass, "to_str", "to_s"); rb_define_method(rbffi_StructInlineArrayClass, "to_ptr", inline_array_to_ptr, 0); + rb_define_method(rbffi_StructLayoutCharArrayClass, "to_s", inline_array_to_s, 0); + rb_define_alias(rbffi_StructLayoutCharArrayClass, "to_str", "to_s"); + id_pointer_ivar = rb_intern("@pointer"); id_layout_ivar = rb_intern("@layout"); id_layout = rb_intern("layout"); diff --git a/ext/ffi_c/Struct.h b/ext/ffi_c/Struct.h index 862345c..1ea9b29 100644 --- a/ext/ffi_c/Struct.h +++ b/ext/ffi_c/Struct.h @@ -78,7 +78,8 @@ extern "C" { extern VALUE rbffi_StructClass, rbffi_StructLayoutClass; extern VALUE rbffi_StructLayoutFieldClass, rbffi_StructLayoutFunctionFieldClass; extern VALUE rbffi_StructLayoutArrayFieldClass, rbffi_StructLayoutStructFieldClass; - extern VALUE rbffi_StructInlineArrayClass, rbffi_StructLayoutEnumFieldClass;; + extern VALUE rbffi_StructInlineArrayClass, rbffi_StructLayoutEnumFieldClass; + extern VALUE rbffi_StructLayoutCharArrayClass; #ifdef __cplusplus } diff --git a/ext/ffi_c/StructLayout.c b/ext/ffi_c/StructLayout.c index 85b444f..2119041 100644 --- a/ext/ffi_c/StructLayout.c +++ b/ext/ffi_c/StructLayout.c @@ -213,6 +213,13 @@ function_field_put(VALUE self, VALUE pointer, VALUE proc) return self; } +static inline bool +isCharArray(ArrayType* arrayType) +{ + return arrayType->componentType->nativeType == NATIVE_INT8 + || arrayType->componentType->nativeType == NATIVE_UINT8; +} + static VALUE array_field_get(VALUE self, VALUE pointer) { @@ -226,7 +233,78 @@ array_field_get(VALUE self, VALUE pointer) argv[0] = pointer; argv[1] = self; - return rb_class_new_instance(2, argv, rbffi_StructInlineArrayClass); + return rb_class_new_instance(2, argv, isCharArray(array) + ? rbffi_StructLayoutCharArrayClass : rbffi_StructInlineArrayClass); +} + +static VALUE +array_field_put(VALUE self, VALUE pointer, VALUE value) +{ + StructField* f; + ArrayType* array; + MemoryOp* op; + + Data_Get_Struct(self, StructField, f); + Data_Get_Struct(f->rbType, ArrayType, array); + + + if (isCharArray(array) && rb_obj_is_instance_of(value, rb_cString)) { + VALUE argv[2]; + + argv[0] = INT2FIX(f->offset); + argv[1] = value; + + rb_funcall2(pointer, rb_intern("put_string"), 2, argv); + + } else { + int count = RARRAY_LEN(value); + int i; + AbstractMemory* memory = MEMORY(pointer); + + if (count > array->length) { + rb_raise(rb_eIndexError, "array too large"); + } + + // clear the contents in case of a short write + checkWrite(memory); + checkBounds(memory, f->offset, f->type->ffiType->size); + if (count < array->length) { + memset(memory->address + f->offset + (count * array->componentType->ffiType->size), + 0, (array->length - count) * array->componentType->ffiType->size); + } + + // now copy each element in + if ((op = get_memory_op(array->componentType)) != NULL) { + + for (i = 0; i < count; ++i) { + (*op->put)(memory, f->offset + (i * array->componentType->ffiType->size), rb_ary_entry(value, i)); + } + + } else if (array->componentType->nativeType == NATIVE_STRUCT) { + + for (i = 0; i < count; ++i) { + VALUE entry = rb_ary_entry(value, i); + Struct* s; + + if (!rb_obj_is_kind_of(entry, rbffi_StructClass)) { + rb_raise(rb_eTypeError, "array element not an instance of FFI::Struct"); + break; + } + + Data_Get_Struct(entry, Struct, s); + checkRead(s->pointer); + checkBounds(s->pointer, 0, array->componentType->ffiType->size); + + memcpy(memory->address + f->offset + (i * array->componentType->ffiType->size), + s->pointer->address, array->componentType->ffiType->size); + } + + } else { + rb_raise(rb_eArgError, "put not supported for arrays of type %s", rb_obj_classname(array->rbComponentType)); + } + } + + return self; } static VALUE @@ -469,6 +547,7 @@ rbffi_StructLayout_Init(VALUE moduleFFI) rb_define_method(rbffi_StructLayoutFunctionFieldClass, "get", function_field_get, 1); rb_define_method(rbffi_StructLayoutArrayFieldClass, "get", array_field_get, 1); + rb_define_method(rbffi_StructLayoutArrayFieldClass, "put", array_field_put, 2); rb_define_method(rbffi_StructLayoutStructFieldClass, "get", inline_struct_field_get, 1); rb_define_alloc_func(rbffi_StructLayoutEnumFieldClass, enum_field_allocate); |