summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWayne Meissner <wmeissner@gmail.com>2010-01-24 11:12:18 +1000
committerWayne Meissner <wmeissner@gmail.com>2010-01-24 11:12:18 +1000
commit5c06940e40fd1ffd0bf62b7e58d38be45dc39e09 (patch)
treeeeed9b11f9b8fa500cf3202f3b1426b623efa934
parent7e74b06016d6d909dd7f2a663670aa6b277f73ed (diff)
downloadffi-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.c14
-rw-r--r--ext/ffi_c/Struct.h3
-rw-r--r--ext/ffi_c/StructLayout.c81
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);