Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Pointer#read_array_of and Pointer#write_array_of #526

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
185 changes: 97 additions & 88 deletions ext/ffi_c/AbstractMemory.c
Expand Up @@ -76,7 +76,6 @@ memory_allocate(VALUE klass)
#define VAL(x, swap) (unlikely(((memory->flags & MEM_SWAP) != 0)) ? swap((x)) : (x))

#define NUM_OP(name, type, toNative, fromNative, swap) \
static void memory_op_put_##name(AbstractMemory* memory, long off, VALUE value); \
static void \
memory_op_put_##name(AbstractMemory* memory, long off, VALUE value) \
{ \
Expand All @@ -85,7 +84,43 @@ memory_op_put_##name(AbstractMemory* memory, long off, VALUE value) \
checkBounds(memory, off, sizeof(type)); \
memcpy(memory->address + off, &tmp, sizeof(tmp)); \
} \
static VALUE memory_put_##name(VALUE self, VALUE offset, VALUE value); \
static VALUE \
memory_op_get_##name(AbstractMemory* memory, long off) \
{ \
type tmp; \
checkRead(memory); \
checkBounds(memory, off, sizeof(type)); \
memcpy(&tmp, memory->address + off, sizeof(tmp)); \
return fromNative(VAL(tmp, swap)); \
} \
static void \
memory_op_put_array_of_##name(AbstractMemory *memory, long off, VALUE ary) \
{ \
long count = RARRAY_LEN(ary); \
long i; \
checkWrite(memory); \
checkBounds(memory, off, count * sizeof(type)); \
for (i = 0; i < count; i++) { \
type tmp = (type) VAL(toNative(RARRAY_PTR(ary)[i]), swap); \
memcpy(memory->address + off + (i * sizeof(type)), &tmp, sizeof(tmp)); \
} \
} \
static VALUE \
memory_op_get_array_of_##name(AbstractMemory *memory, long off, long count) \
{ \
VALUE retVal = rb_ary_new2(count); \
long i; \
checkRead(memory); \
checkBounds(memory, off, count * sizeof(type)); \
for (i = 0; i < count; ++i) { \
type tmp; \
memcpy(&tmp, memory->address + off + (i * sizeof(type)), sizeof(tmp)); \
rb_ary_push(retVal, fromNative(VAL(tmp, swap))); \
} \
return retVal; \
} \
static MemoryOp memory_op_##name = { memory_op_get_##name, memory_op_put_##name, memory_op_get_array_of_##name, memory_op_put_array_of_##name }; \
\
static VALUE \
memory_put_##name(VALUE self, VALUE offset, VALUE value) \
{ \
Expand All @@ -94,7 +129,6 @@ memory_put_##name(VALUE self, VALUE offset, VALUE value) \
memory_op_put_##name(memory, NUM2LONG(offset), value); \
return self; \
} \
static VALUE memory_write_##name(VALUE self, VALUE value); \
static VALUE \
memory_write_##name(VALUE self, VALUE value) \
{ \
Expand All @@ -103,79 +137,49 @@ memory_write_##name(VALUE self, VALUE value) \
memory_op_put_##name(memory, 0, value); \
return self; \
} \
static VALUE memory_op_get_##name(AbstractMemory* memory, long off); \
static VALUE \
memory_op_get_##name(AbstractMemory* memory, long off) \
{ \
type tmp; \
checkRead(memory); \
checkBounds(memory, off, sizeof(type)); \
memcpy(&tmp, memory->address + off, sizeof(tmp)); \
return fromNative(VAL(tmp, swap)); \
} \
static VALUE memory_get_##name(VALUE self, VALUE offset); \
static VALUE \
memory_get_##name(VALUE self, VALUE offset) \
{ \
AbstractMemory* memory; \
Data_Get_Struct(self, AbstractMemory, memory); \
return memory_op_get_##name(memory, NUM2LONG(offset)); \
} \
static VALUE memory_read_##name(VALUE self); \
static VALUE \
memory_read_##name(VALUE self) \
{ \
AbstractMemory* memory; \
Data_Get_Struct(self, AbstractMemory, memory); \
return memory_op_get_##name(memory, 0); \
} \
static MemoryOp memory_op_##name = { memory_op_get_##name, memory_op_put_##name }; \
\
static VALUE memory_put_array_of_##name(VALUE self, VALUE offset, VALUE ary); \
static VALUE \
memory_put_array_of_##name(VALUE self, VALUE offset, VALUE ary) \
{ \
long count = RARRAY_LEN(ary); \
long off = NUM2LONG(offset); \
AbstractMemory* memory = MEMORY(self); \
long i; \
checkWrite(memory); \
checkBounds(memory, off, count * sizeof(type)); \
for (i = 0; i < count; i++) { \
type tmp = (type) VAL(toNative(RARRAY_PTR(ary)[i]), swap); \
memcpy(memory->address + off + (i * sizeof(type)), &tmp, sizeof(tmp)); \
} \
memory_op_put_array_of_##name(memory, off, ary); \
return self; \
} \
static VALUE memory_write_array_of_##name(VALUE self, VALUE ary); \
static VALUE \
memory_write_array_of_##name(VALUE self, VALUE ary) \
{ \
return memory_put_array_of_##name(self, INT2FIX(0), ary); \
AbstractMemory* memory = MEMORY(self); \
memory_op_put_array_of_##name(memory, 0, ary); \
return self; \
} \
static VALUE memory_get_array_of_##name(VALUE self, VALUE offset, VALUE length); \
static VALUE \
memory_get_array_of_##name(VALUE self, VALUE offset, VALUE length) \
{ \
long count = NUM2LONG(length); \
long off = NUM2LONG(offset); \
AbstractMemory* memory = MEMORY(self); \
VALUE retVal = rb_ary_new2(count); \
long i; \
checkRead(memory); \
checkBounds(memory, off, count * sizeof(type)); \
for (i = 0; i < count; ++i) { \
type tmp; \
memcpy(&tmp, memory->address + off + (i * sizeof(type)), sizeof(tmp)); \
rb_ary_push(retVal, fromNative(VAL(tmp, swap))); \
} \
return retVal; \
return memory_op_get_array_of_##name(memory, off, count); \
} \
static VALUE memory_read_array_of_##name(VALUE self, VALUE length); \
static VALUE \
memory_read_array_of_##name(VALUE self, VALUE length) \
{ \
return memory_get_array_of_##name(self, INT2FIX(0), length); \
long count = NUM2LONG(length); \
AbstractMemory* memory = MEMORY(self); \
return memory_op_get_array_of_##name(memory, 0, count); \
}

#define NOSWAP(x) (x)
Expand Down Expand Up @@ -327,71 +331,74 @@ memory_size(VALUE self)
return LONG2NUM(ptr->size);
}


#define MEM_OP_GET_PUT(func, ret, ...) \
AbstractMemory* ptr; \
Type *type; \
VALUE nType = rbffi_Type_Find(typeName); \
Data_Get_Struct(self, AbstractMemory, ptr); \
Data_Get_Struct(nType, Type, type); \
MemoryOp *op = get_memory_op(type); \
if(op == NULL || op->func == NULL) rbffi_type_raise_invalid(typeName); \
ret op->func(ptr, __VA_ARGS__);

/*
* call-seq: memory.get(type, offset)
* Return data of given type contained in memory.
* @param [Symbol, Type] type_name type of data to get
* @param [Symbol, Type] typeName type of data to get
* @param [Numeric] offset point in buffer to start from
* @return [Object]
* @raise {ArgumentError} if type is not supported
* @raise {TypeError} if type is not supported
*/
static VALUE
memory_get(VALUE self, VALUE type_name, VALUE offset)
memory_get(VALUE self, VALUE typeName, VALUE offset)
{
AbstractMemory* ptr;
VALUE nType;
Type *type;

nType = rbffi_Type_Lookup(type_name);
if(NIL_P(nType)) goto undefined_type;

Data_Get_Struct(self, AbstractMemory, ptr);
Data_Get_Struct(nType, Type, type);

MemoryOp *op = get_memory_op(type);
if(op == NULL) goto undefined_type;

return op->get(ptr, NUM2LONG(offset));
MEM_OP_GET_PUT(get, return, NUM2LONG(offset));
}

undefined_type: {
VALUE msg = rb_sprintf("undefined type '%" PRIsVALUE "'", type_name);
rb_exc_raise(rb_exc_new3(rb_eArgError, msg));
return Qnil;
}
/*
* call-seq: memory.get_array_of(type, offset)
* Return array of given type contained in memory.
* @param [Symbol, Type] typeName type of data to get
* @param [Numeric] offset point in buffer to start from
* @return [Object]
* @raise {TypeError} if type is not supported
*/
static VALUE
memory_get_array_of(VALUE self, VALUE typeName, VALUE offset, VALUE length)
{
MEM_OP_GET_PUT(getArray, return, NUM2LONG(offset), NUM2LONG(length));
}

/*
* call-seq: memory.put(type, offset, value)
* @param [Symbol, Type] type_name type of data to put
* @param [Symbol, Type] typeName type of data to put
* @param [Numeric] offset point in buffer to start from
* @return [nil]
* @raise {ArgumentError} if type is not supported
* @return [self]
* @raise {TypeError} if type is not supported
*/
static VALUE
memory_put(VALUE self, VALUE type_name, VALUE offset, VALUE value)
memory_put(VALUE self, VALUE typeName, VALUE offset, VALUE value)
{
AbstractMemory* ptr;
VALUE nType;
Type *type;

nType = rbffi_Type_Lookup(type_name);
if(NIL_P(nType)) goto undefined_type;
MEM_OP_GET_PUT(put, (void), NUM2LONG(offset), value);
}

Data_Get_Struct(self, AbstractMemory, ptr);
Data_Get_Struct(nType, Type, type);

MemoryOp *op = get_memory_op(type);
if(op == NULL) goto undefined_type;
/*
* call-seq: memory.put_array_of(type, offset, ary)
* @param [Symbol, Type] type type of data to write to pointer's contents
* @param [Numeric] offset point in buffer to start from
* @param [Array] ary array of values
* @return [self]
* @raise {TypeError} if type is not supported
*/
static VALUE
memory_put_array_of(VALUE self, VALUE typeName, VALUE offset, VALUE ary)
{
MEM_OP_GET_PUT(putArray, (void), NUM2LONG(offset), ary);
}

op->put(ptr, NUM2LONG(offset), value);
return Qnil;

undefined_type: {
VALUE msg = rb_sprintf("unsupported type '%" PRIsVALUE "'", type_name);
rb_exc_raise(rb_exc_new3(rb_eArgError, msg));
return Qnil;
}
}

/*
* call-seq: memory.get_string(offset, length=nil)
Expand Down Expand Up @@ -421,6 +428,7 @@ memory_get_string(int argc, VALUE* argv, VALUE self)
(end != NULL ? end - ptr->address - off : len));
}


/*
* call-seq: memory.get_array_of_string(offset, count=nil)
* Return an array of strings contained in memory.
Expand Down Expand Up @@ -450,7 +458,7 @@ memory_get_array_of_string(int argc, VALUE* argv, VALUE self)
int i;

checkBounds(ptr, off, count * sizeof (char*));

for (i = 0; i < count; ++i) {
const char* strptr = *((const char**) (ptr->address + off) + i);
rb_ary_push(retVal, (strptr == NULL ? Qnil : rb_tainted_str_new2(strptr)));
Expand Down Expand Up @@ -727,8 +735,7 @@ memory_op_put_strptr(AbstractMemory* ptr, long offset, VALUE value)
rb_raise(rb_eArgError, "Cannot set :string fields");
}

static MemoryOp memory_op_strptr = { memory_op_get_strptr, memory_op_put_strptr };

static MemoryOp memory_op_strptr = { memory_op_get_strptr, memory_op_put_strptr, NULL, NULL};

MemoryOps rbffi_AbstractMemoryOps = {
&memory_op_int8, /*.int8 */
Expand Down Expand Up @@ -1094,6 +1101,8 @@ rbffi_AbstractMemory_Init(VALUE moduleFFI)

rb_define_method(classMemory, "get", memory_get, 2);
rb_define_method(classMemory, "put", memory_put, 3);
rb_define_method(classMemory, "get_array_of", memory_get_array_of, 3);
rb_define_method(classMemory, "put_array_of", memory_put_array_of, 3);

rb_define_method(classMemory, "clear", memory_clear, 0);
rb_define_method(classMemory, "total", memory_size, 0);
Expand Down
11 changes: 9 additions & 2 deletions ext/ffi_c/AbstractMemory.h
Expand Up @@ -54,9 +54,16 @@ extern "C" {

typedef struct AbstractMemory_ AbstractMemory;

typedef VALUE (*AbstractMemoryGetFunc)(AbstractMemory* ptr, long offset);
typedef void (*AbstractMemoryPutFunc)(AbstractMemory* ptr, long offset, VALUE value);
typedef VALUE (*AbstractMemoryGetArrayFunc)(AbstractMemory* ptr, long offset, long count);
typedef void (*AbstractMemoryPutArrayFunc)(AbstractMemory* ptr, long offset, VALUE ary);

typedef struct {
VALUE (*get)(AbstractMemory* ptr, long offset);
void (*put)(AbstractMemory* ptr, long offset, VALUE value);
AbstractMemoryGetFunc get;
AbstractMemoryPutFunc put;
AbstractMemoryGetArrayFunc getArray;
AbstractMemoryPutArrayFunc putArray;
} MemoryOp;

typedef struct {
Expand Down
12 changes: 9 additions & 3 deletions ext/ffi_c/Type.c
Expand Up @@ -214,6 +214,14 @@ rbffi_type_size(VALUE type)
}
}

void
rbffi_type_raise_invalid(VALUE typeName)
{
VALUE s = rb_inspect(typeName);
rb_raise(rb_eTypeError, "invalid type, %s", RSTRING_PTR(s));
RB_GC_GUARD(s);
}

VALUE
rbffi_Type_Lookup(VALUE name)
{
Expand Down Expand Up @@ -245,9 +253,7 @@ rbffi_Type_Find(VALUE name)
VALUE rbType = rbffi_Type_Lookup(name);

if (!RTEST(rbType)) {
VALUE s = rb_inspect(name);
rb_raise(rb_eTypeError, "invalid type, %s", RSTRING_PTR(s));
RB_GC_GUARD(s);
rbffi_type_raise_invalid(name);
}

return rbType;
Expand Down
1 change: 1 addition & 0 deletions ext/ffi_c/Type.h
Expand Up @@ -51,6 +51,7 @@ struct Type_ {
};

extern VALUE rbffi_TypeClass;
extern void rbffi_type_raise_invalid(VALUE typeName);
extern VALUE rbffi_Type_Lookup(VALUE type);
extern VALUE rbffi_Type_Find(VALUE type);

Expand Down
22 changes: 22 additions & 0 deletions lib/ffi/pointer.rb
Expand Up @@ -103,6 +103,7 @@ def write_string(str, len=nil)
# Read an array of +type+ of length +length+.
# @example
# ptr.read_array_of_type(TYPE_UINT8, :get_uint8, 4) # -> [1, 2, 3, 4]
# @deprecated Use {#read_array_of} instead
def read_array_of_type(type, reader, length)
ary = []
size = FFI.type_size(type)
Expand All @@ -121,6 +122,7 @@ def read_array_of_type(type, reader, length)
# Write +ary+ in pointer's contents as +type+.
# @example
# ptr.write_array_of_type(TYPE_UINT8, :put_uint8, [1, 2, 3 ,4])
# @deprecated Use {#write_array_of} instead
def write_array_of_type(type, writer, ary)
size = FFI.type_size(type)
tmp = self
Expand All @@ -131,6 +133,26 @@ def write_array_of_type(type, writer, ary)
self
end

# @param [Type] type type of data to write to pointer's contents
# @param [Array] ary array of values
# @return [self]
# Write +ary+ in pointer's contents as +type+.
# @example
# ptr.write_array_of(:uint8, [1, 2, 3 ,4])
def write_array_of(type, ary)
put_array_of(type, 0, ary)
end

# @param [Type] type type of data to read from pointer's contents
# @param [Numeric] length
# @return [Array]
# Read an array of +type+ of length +length+.
# @example
# ptr.read_array_of_type(:uint8, 4) # -> [1, 2, 3, 4]
def read_array_of(type, length)
get_array_of(type, 0, length)
end

# @return [self]
def to_ptr
self
Expand Down