/*
* Copyright (c) 2009, Wayne Meissner
*
* 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 .
*/
#ifndef _MSC_VER
#include
#endif
#include
#include
#include
#include "rbffi.h"
#include "compat.h"
#include "Types.h"
#include "Type.h"
typedef struct BuiltinType_ {
Type type;
char* name;
} BuiltinType;
static void builtin_type_free(BuiltinType *);
VALUE rbffi_TypeClass = Qnil;
static VALUE classBuiltinType = Qnil;
static VALUE typeMap = Qnil, sizeMap = Qnil;
static ID id_find_type = 0, id_type_size = 0, id_size = 0;
static VALUE
type_allocate(VALUE klass)
{
Type* type;
VALUE obj = Data_Make_Struct(klass, Type, NULL, -1, type);
type->nativeType = -1;
type->ffiType = &ffi_type_void;
return obj;
}
/*
* Document-method: initialize
* call-seq: initialize(value)
* @param [Fixnum,Type] value
* @return [self]
*/
static VALUE
type_initialize(VALUE self, VALUE value)
{
Type* type;
Type* other;
Data_Get_Struct(self, Type, type);
if (FIXNUM_P(value)) {
type->nativeType = FIX2INT(value);
} else if (rb_obj_is_kind_of(value, rbffi_TypeClass)) {
Data_Get_Struct(value, Type, other);
type->nativeType = other->nativeType;
type->ffiType = other->ffiType;
} else {
rb_raise(rb_eArgError, "wrong type");
}
return self;
}
/*
* call-seq: type.size
* @return [Fixnum]
* Return type's size, in bytes.
*/
static VALUE
type_size(VALUE self)
{
Type *type;
Data_Get_Struct(self, Type, type);
return INT2FIX(type->ffiType->size);
}
/*
* call-seq: type.alignment
* @return [Fixnum]
*/
static VALUE
type_alignment(VALUE self)
{
Type *type;
Data_Get_Struct(self, Type, type);
return INT2FIX(type->ffiType->alignment);
}
/*
* call-seq: type.inspect
* @return [String]
* Inspect {Type} object.
*/
static VALUE
type_inspect(VALUE self)
{
char buf[100];
Type *type;
Data_Get_Struct(self, Type, type);
snprintf(buf, sizeof(buf), "#<%s:%p size=%d alignment=%d>",
rb_obj_classname(self), type, (int) type->ffiType->size, (int) type->ffiType->alignment);
return rb_str_new2(buf);
}
static VALUE
builtin_type_new(VALUE klass, int nativeType, ffi_type* ffiType, const char* name)
{
BuiltinType* type;
VALUE obj = Qnil;
obj = Data_Make_Struct(klass, BuiltinType, NULL, builtin_type_free, type);
type->name = strdup(name);
type->type.nativeType = nativeType;
type->type.ffiType = ffiType;
return obj;
}
static void
builtin_type_free(BuiltinType *type)
{
free(type->name);
xfree(type);
}
/*
* call-seq: type.inspect
* @return [String]
* Inspect {Type::Builtin} object.
*/
static VALUE
builtin_type_inspect(VALUE self)
{
char buf[100];
BuiltinType *type;
Data_Get_Struct(self, BuiltinType, type);
snprintf(buf, sizeof(buf), "#<%s:%s size=%d alignment=%d>",
rb_obj_classname(self), type->name, (int) type->type.ffiType->size, type->type.ffiType->alignment);
return rb_str_new2(buf);
}
int
rbffi_type_size(VALUE type)
{
int t = TYPE(type);
if (t == T_FIXNUM || t == T_BIGNUM) {
return NUM2INT(type);
} else if (t == T_SYMBOL) {
/*
* Try looking up directly in the type and size maps
*/
VALUE nType;
if ((nType = rb_hash_lookup(typeMap, type)) != Qnil) {
if (rb_obj_is_kind_of(nType, rbffi_TypeClass)) {
Type* type;
Data_Get_Struct(nType, Type, type);
return (int) type->ffiType->size;
} else if (rb_respond_to(nType, id_size)) {
return NUM2INT(rb_funcall2(nType, id_size, 0, NULL));
}
}
/* Not found - call up to the ruby version to resolve */
return NUM2INT(rb_funcall2(rbffi_FFIModule, id_type_size, 1, &type));
} else {
return NUM2INT(rb_funcall2(type, id_size, 0, NULL));
}
}
VALUE
rbffi_Type_Lookup(VALUE name)
{
int t = TYPE(name);
if (t == T_SYMBOL || t == T_STRING) {
/*
* Try looking up directly in the type Map
*/
VALUE nType;
if ((nType = rb_hash_lookup(typeMap, name)) != Qnil && rb_obj_is_kind_of(nType, rbffi_TypeClass)) {
return nType;
}
} else if (rb_obj_is_kind_of(name, rbffi_TypeClass)) {
return name;
}
/* Nothing found - let caller handle raising exceptions */
return Qnil;
}
/**
* rbffi_Type_Find() is like rbffi_Type_Lookup, but an error is raised if the
* type is not found.
*/
VALUE
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);
}
return rbType;
}
void
rbffi_Type_Init(VALUE moduleFFI)
{
VALUE moduleNativeType;
/*
* Document-class: FFI::Type
* This class manages C types.
*
* It embbed {FFI::Type::Builtin} objects as constants (for names,
* see {FFI::NativeType}).
*/
rbffi_TypeClass = rb_define_class_under(moduleFFI, "Type", rb_cObject);
/*
* Document-constant: FFI::TypeDefs
*/
rb_define_const(moduleFFI, "TypeDefs", typeMap = rb_hash_new());
rb_define_const(moduleFFI, "SizeTypes", sizeMap = rb_hash_new());
rb_global_variable(&typeMap);
rb_global_variable(&sizeMap);
id_find_type = rb_intern("find_type");
id_type_size = rb_intern("type_size");
id_size = rb_intern("size");
/*
* Document-class: FFI::Type::Builtin
* Class for Built-in types.
*/
classBuiltinType = rb_define_class_under(rbffi_TypeClass, "Builtin", rbffi_TypeClass);
/*
* Document-module: FFI::NativeType
* This module defines constants for native (C) types.
*
* ==Native type constants
* Native types are defined by constants :
* * INT8, SCHAR, CHAR
* * UINT8, UCHAR
* * INT16, SHORT, SSHORT
* * UINT16, USHORT
* * INT32,, INT, SINT
* * UINT32, UINT
* * INT64, LONG_LONG, SLONG_LONG
* * UINT64, ULONG_LONG
* * LONG, SLONG
* * ULONG
* * FLOAT32, FLOAT
* * FLOAT64, DOUBLE
* * POINTER
* * CALLBACK
* * FUNCTION
* * CHAR_ARRAY
* * BOOL
* * STRING (immutable string, nul terminated)
* * STRUCT (struct-b-value param or result)
* * ARRAY (array type definition)
* * MAPPED (custom native type)
* For function return type only :
* * VOID
* For function argument type only :
* * BUFFER_IN
* * BUFFER_OUT
* * VARARGS (function takes a variable number of arguments)
*
* All these constants are exported to {FFI} module prefixed with "TYPE_".
* They are objets from {FFI::Type::Builtin} class.
*/
moduleNativeType = rb_define_module_under(moduleFFI, "NativeType");
/*
* Document-global: FFI::Type
*/
rb_global_variable(&rbffi_TypeClass);
rb_global_variable(&classBuiltinType);
rb_global_variable(&moduleNativeType);
rb_define_alloc_func(rbffi_TypeClass, type_allocate);
rb_define_method(rbffi_TypeClass, "initialize", type_initialize, 1);
rb_define_method(rbffi_TypeClass, "size", type_size, 0);
rb_define_method(rbffi_TypeClass, "alignment", type_alignment, 0);
rb_define_method(rbffi_TypeClass, "inspect", type_inspect, 0);
/* Make Type::Builtin non-allocatable */
rb_undef_method(CLASS_OF(classBuiltinType), "new");
rb_define_method(classBuiltinType, "inspect", builtin_type_inspect, 0);
rb_global_variable(&rbffi_TypeClass);
rb_global_variable(&classBuiltinType);
/* Define all the builtin types */
#define T(x, ffiType) do { \
VALUE t = Qnil; \
rb_define_const(rbffi_TypeClass, #x, t = builtin_type_new(classBuiltinType, NATIVE_##x, ffiType, #x)); \
rb_define_const(moduleNativeType, #x, t); \
rb_define_const(moduleFFI, "TYPE_" #x, t); \
} while(0)
#define A(old_type, new_type) do { \
VALUE t = rb_const_get(rbffi_TypeClass, rb_intern(#old_type)); \
rb_const_set(rbffi_TypeClass, rb_intern(#new_type), t); \
} while(0)
/*
* Document-constant: FFI::Type::Builtin::VOID
*/
T(VOID, &ffi_type_void);
T(INT8, &ffi_type_sint8);
A(INT8, SCHAR);
A(INT8, CHAR);
T(UINT8, &ffi_type_uint8);
A(UINT8, UCHAR);
T(INT16, &ffi_type_sint16);
A(INT16, SHORT);
A(INT16, SSHORT);
T(UINT16, &ffi_type_uint16);
A(UINT16, USHORT);
T(INT32, &ffi_type_sint32);
A(INT32, INT);
A(INT32, SINT);
T(UINT32, &ffi_type_uint32);
A(UINT32, UINT);
T(INT64, &ffi_type_sint64);
A(INT64, LONG_LONG);
A(INT64, SLONG_LONG);
T(UINT64, &ffi_type_uint64);
A(UINT64, ULONG_LONG);
T(LONG, &ffi_type_slong);
A(LONG, SLONG);
T(ULONG, &ffi_type_ulong);
T(FLOAT32, &ffi_type_float);
A(FLOAT32, FLOAT);
T(FLOAT64, &ffi_type_double);
A(FLOAT64, DOUBLE);
T(LONGDOUBLE, &ffi_type_longdouble);
T(POINTER, &ffi_type_pointer);
T(STRING, &ffi_type_pointer);
T(BUFFER_IN, &ffi_type_pointer);
T(BUFFER_OUT, &ffi_type_pointer);
T(BUFFER_INOUT, &ffi_type_pointer);
T(BOOL, &ffi_type_uchar);
T(VARARGS, &ffi_type_void);
}