blob: 0d91c35b7c872dcd2378903b02086ffcd9782bfd [file] [log] [blame]
/*
* Copyright (c) 2008, 2009, Wayne Meissner
* Copyright (C) 2009 Luc Heinrich <luc@honk-honk.com>
*
* Copyright (c) 2008-2013, Ruby FFI project contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Ruby FFI project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _MSC_VER
# include <stdbool.h>
# include <stdint.h>
#else
# include "win32/stdbool.h"
# include "win32/stdint.h"
#endif
#include <limits.h>
#include <ruby.h>
#include "rbffi.h"
#include "AbstractMemory.h"
#include "Pointer.h"
#include "MemoryPointer.h"
static VALUE memptr_allocate(VALUE klass);
static void memptr_release(Pointer* ptr);
static VALUE memptr_malloc(VALUE self, long size, long count, bool clear);
static VALUE memptr_free(VALUE self);
VALUE rbffi_MemoryPointerClass;
#define MEMPTR(obj) ((MemoryPointer *) rbffi_AbstractMemory_Cast(obj, rbffi_MemoryPointerClass))
VALUE
rbffi_MemoryPointer_NewInstance(long size, long count, bool clear)
{
return memptr_malloc(memptr_allocate(rbffi_MemoryPointerClass), size, count, clear);
}
static VALUE
memptr_allocate(VALUE klass)
{
Pointer* p;
VALUE obj = Data_Make_Struct(klass, Pointer, NULL, memptr_release, p);
p->rbParent = Qnil;
p->memory.flags = MEM_RD | MEM_WR;
return obj;
}
/*
* call-seq: initialize(size, count=1, clear=true)
* @param [Fixnum, Bignum, Symbol, FFI::Type] size size of a memory cell (in bytes, or type whom size will be used)
* @param [Numeric] count number of cells in memory
* @param [Boolean] clear set memory to all-zero if +true+
* @return [self]
* A new instance of FFI::MemoryPointer.
*/
static VALUE
memptr_initialize(int argc, VALUE* argv, VALUE self)
{
VALUE size = Qnil, count = Qnil, clear = Qnil;
int nargs = rb_scan_args(argc, argv, "12", &size, &count, &clear);
memptr_malloc(self, rbffi_type_size(size), nargs > 1 ? NUM2LONG(count) : 1,
RTEST(clear) || clear == Qnil);
if (rb_block_given_p()) {
return rb_ensure(rb_yield, self, memptr_free, self);
}
return self;
}
static VALUE
memptr_malloc(VALUE self, long size, long count, bool clear)
{
Pointer* p;
unsigned long msize;
Data_Get_Struct(self, Pointer, p);
msize = size * count;
p->storage = xmalloc(msize + 7);
if (p->storage == NULL) {
rb_raise(rb_eNoMemError, "Failed to allocate memory size=%ld bytes", msize);
return Qnil;
}
p->autorelease = true;
p->memory.typeSize = (int) size;
p->memory.size = msize;
/* ensure the memory is aligned on at least a 8 byte boundary */
p->memory.address = (char *) (((uintptr_t) p->storage + 0x7) & (uintptr_t) ~0x7UL);;
p->allocated = true;
if (clear && p->memory.size > 0) {
memset(p->memory.address, 0, p->memory.size);
}
return self;
}
static VALUE
memptr_free(VALUE self)
{
Pointer* ptr;
Data_Get_Struct(self, Pointer, ptr);
if (ptr->allocated) {
if (ptr->storage != NULL) {
xfree(ptr->storage);
ptr->storage = NULL;
}
ptr->allocated = false;
}
return self;
}
static void
memptr_release(Pointer* ptr)
{
if (ptr->autorelease && ptr->allocated && ptr->storage != NULL) {
xfree(ptr->storage);
ptr->storage = NULL;
}
xfree(ptr);
}
/*
* call-seq: from_string(s)
* @param [String] s string
* @return [MemoryPointer]
* Create a {MemoryPointer} with +s+ inside.
*/
static VALUE
memptr_s_from_string(VALUE klass, VALUE to_str)
{
VALUE s = StringValue(to_str);
VALUE args[] = { INT2FIX(1), LONG2NUM(RSTRING_LEN(s) + 1), Qfalse };
VALUE obj = rb_class_new_instance(3, args, klass);
rb_funcall(obj, rb_intern("put_string"), 2, INT2FIX(0), s);
return obj;
}
void
rbffi_MemoryPointer_Init(VALUE moduleFFI)
{
VALUE ffi_Pointer;
ffi_Pointer = rbffi_PointerClass;
/*
* Document-class: FFI::MemoryPointer < FFI::Pointer
* A MemoryPointer is a specific {Pointer}. It points to a memory composed of cells. All cells have the
* same size.
*
* @example Create a new MemoryPointer
* mp = FFI::MemoryPointer.new(:long, 16) # Create a pointer on a memory of 16 long ints.
* @example Create a new MemoryPointer from a String
* mp1 = FFI::MemoryPointer.from_string("this is a string")
* # same as:
* mp2 = FFI::MemoryPointer.new(:char,16)
* mp2.put_string("this is a string")
*/
rbffi_MemoryPointerClass = rb_define_class_under(moduleFFI, "MemoryPointer", ffi_Pointer);
rb_global_variable(&rbffi_MemoryPointerClass);
rb_define_alloc_func(rbffi_MemoryPointerClass, memptr_allocate);
rb_define_method(rbffi_MemoryPointerClass, "initialize", memptr_initialize, -1);
rb_define_singleton_method(rbffi_MemoryPointerClass, "from_string", memptr_s_from_string, 1);
}