/*
* Copyright (c) 2008, 2009, Wayne Meissner
*
* All rights reserved.
*
* This file is part of ruby-ffi.
*
* This code is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License version 3 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* version 3 for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with this work. If not, see .
*/
#include
#include
#include
#include
#include "rbffi.h"
#include "AbstractMemory.h"
#include "Pointer.h"
typedef struct Pointer {
AbstractMemory memory;
VALUE parent;
} Pointer;
#define POINTER(obj) rbffi_AbstractMemory_Cast((obj), rbffi_PointerClass)
VALUE rbffi_PointerClass = Qnil;
VALUE rbffi_NullPointerSingleton = Qnil;
static void ptr_mark(Pointer* ptr);
VALUE
rbffi_Pointer_NewInstance(void* addr)
{
Pointer* p;
VALUE obj;
if (addr == NULL) {
return rbffi_NullPointerSingleton;
}
obj = Data_Make_Struct(rbffi_PointerClass, Pointer, NULL, -1, p);
p->memory.address = addr;
p->memory.size = LONG_MAX;
p->memory.flags = (addr == NULL) ? 0 : (MEM_RD | MEM_WR);
p->memory.typeSize = 1;
p->parent = Qnil;
return obj;
}
static VALUE
ptr_allocate(VALUE klass)
{
Pointer* p;
VALUE obj;
obj = Data_Make_Struct(klass, Pointer, ptr_mark, -1, p);
p->parent = Qnil;
p->memory.flags = MEM_RD | MEM_WR;
return obj;
}
static VALUE
ptr_initialize(int argc, VALUE* argv, VALUE self)
{
Pointer* p;
VALUE rbType = Qnil, rbAddress = Qnil;
int typeSize = 1;
Data_Get_Struct(self, Pointer, p);
switch (rb_scan_args(argc, argv, "11", &rbType, &rbAddress)) {
case 1:
rbAddress = rbType;
typeSize = 1;
break;
case 2:
typeSize = rbffi_type_size(rbType);
break;
default:
rb_raise(rb_eArgError, "Invalid arguments");
}
switch (TYPE(rbAddress)) {
case T_FIXNUM:
case T_BIGNUM:
p->memory.address = (void*) (uintptr_t) NUM2LL(rbAddress);
p->memory.size = LONG_MAX;
if (p->memory.address == NULL) {
p->memory.flags = 0;
}
break;
default:
if (rb_obj_is_kind_of(rbAddress, rbffi_PointerClass)) {
Pointer* orig;
p->parent = rbAddress;
Data_Get_Struct(rbAddress, Pointer, orig);
p->memory = orig->memory;
} else {
rb_raise(rb_eTypeError, "wrong argument type, expected Integer or FFI::Pointer");
}
break;
}
p->memory.typeSize = typeSize;
return self;
}
static VALUE
slice(VALUE self, long offset, long size)
{
AbstractMemory* ptr;
Pointer* p;
VALUE retval;
Data_Get_Struct(self, AbstractMemory, ptr);
checkBounds(ptr, offset, 1);
retval = Data_Make_Struct(rbffi_PointerClass, Pointer, ptr_mark, -1, p);
p->memory.address = ptr->address + offset;
p->memory.size = size;
p->memory.flags = ptr->flags;
p->memory.typeSize = ptr->typeSize;
p->parent = self;
return retval;
}
static VALUE
ptr_plus(VALUE self, VALUE offset)
{
AbstractMemory* ptr;
long off = NUM2LONG(offset);
Data_Get_Struct(self, AbstractMemory, ptr);
return slice(self, off, ptr->size == LONG_MAX ? LONG_MAX : ptr->size - off);
}
static VALUE
ptr_slice(VALUE self, VALUE rbOffset, VALUE rbLength)
{
return slice(self, NUM2LONG(rbOffset), NUM2LONG(rbLength));
}
static VALUE
ptr_inspect(VALUE self)
{
char buf[100];
Pointer* ptr;
Data_Get_Struct(self, Pointer, ptr);
if (ptr->memory.size != LONG_MAX) {
snprintf(buf, sizeof(buf), "#<%s address=%p size=%lu>",
rb_obj_classname(self), ptr->memory.address, ptr->memory.size);
} else {
snprintf(buf, sizeof(buf), "#<%s address=%p>", rb_obj_classname(self), ptr->memory.address);
}
return rb_str_new2(buf);
}
static VALUE
ptr_null_p(VALUE self)
{
Pointer* ptr;
Data_Get_Struct(self, Pointer, ptr);
return ptr->memory.address == NULL ? Qtrue : Qfalse;
}
static VALUE
ptr_equals(VALUE self, VALUE other)
{
Pointer* ptr;
Data_Get_Struct(self, Pointer, ptr);
return ptr->memory.address == POINTER(other)->address ? Qtrue : Qfalse;
}
static VALUE
ptr_address(VALUE self)
{
Pointer* ptr;
Data_Get_Struct(self, Pointer, ptr);
return ULL2NUM((uintptr_t) ptr->memory.address);
}
#if BYTE_ORDER == LITTLE_ENDIAN
# define SWAPPED_ORDER BIG_ENDIAN
#else
# define SWAPPED_ORDER LITTLE_ENDIAN
#endif
static VALUE
ptr_order(int argc, VALUE* argv, VALUE self)
{
Pointer* ptr;
Data_Get_Struct(self, Pointer, ptr);
if (argc == 0) {
int order = (ptr->memory.flags & MEM_SWAP) == 0 ? BYTE_ORDER : SWAPPED_ORDER;
return order == BIG_ENDIAN ? ID2SYM(rb_intern("big")) : ID2SYM(rb_intern("little"));
} else {
VALUE rbOrder = Qnil;
int order = BYTE_ORDER;
if (rb_scan_args(argc, argv, "1", &rbOrder) < 1) {
rb_raise(rb_eArgError, "need byte order");
}
if (SYMBOL_P(rbOrder)) {
ID id = SYM2ID(rbOrder);
if (id == rb_intern("little")) {
order = LITTLE_ENDIAN;
} else if (id == rb_intern("big") || id == rb_intern("network")) {
order = BIG_ENDIAN;
}
}
if (order != BYTE_ORDER) {
Pointer* p2;
VALUE retval = slice(self, 0, ptr->memory.size);
Data_Get_Struct(retval, Pointer, p2);
p2->memory.flags |= MEM_SWAP;
return retval;
}
return self;
}
}
static void
ptr_mark(Pointer* ptr)
{
rb_gc_mark(ptr->parent);
}
void
rbffi_Pointer_Init(VALUE moduleFFI)
{
VALUE rbNullAddress = ULL2NUM(0);
rbffi_PointerClass = rb_define_class_under(moduleFFI, "Pointer", rbffi_AbstractMemoryClass);
rb_global_variable(&rbffi_PointerClass);
rb_define_alloc_func(rbffi_PointerClass, ptr_allocate);
rb_define_method(rbffi_PointerClass, "initialize", ptr_initialize, -1);
rb_define_method(rbffi_PointerClass, "inspect", ptr_inspect, 0);
rb_define_method(rbffi_PointerClass, "to_s", ptr_inspect, 0);
rb_define_method(rbffi_PointerClass, "+", ptr_plus, 1);
rb_define_method(rbffi_PointerClass, "slice", ptr_slice, 2);
rb_define_method(rbffi_PointerClass, "null?", ptr_null_p, 0);
rb_define_method(rbffi_PointerClass, "address", ptr_address, 0);
rb_define_alias(rbffi_PointerClass, "to_i", "address");
rb_define_method(rbffi_PointerClass, "==", ptr_equals, 1);
rb_define_method(rbffi_PointerClass, "order", ptr_order, -1);
rbffi_NullPointerSingleton = rb_class_new_instance(1, &rbNullAddress, rbffi_PointerClass);
rb_define_const(rbffi_PointerClass, "NULL", rbffi_NullPointerSingleton);
}