module Rubinius module FFI # We need to define this so we can include it. Since the file is # auto-generated, we can't pull it in here but we don't want to break the # alphabetized load order for random cases. module PointerAccessors; end ## # Pointer is Rubinius's "fat" pointer class. It represents an actual # pointer, in C language terms, to an address in memory. They're called # fat pointers because the Pointer object is an wrapper around # the actual pointer, the Rubinius runtime doesn't have direct access # to the raw address. # # This class is used extensively in FFI usage to interface with various # parts of the underlying system. It provides a number of operations # for operating on the memory that is pointed to. These operations effectively # give Rubinius the cast/read capabilities available in C, but using # high level methods. # # MemoryPointer objects can be put in autorelease mode. In this mode, # when the GC cleans up a MemoryPointer object, the memory it points # to is passed to free(3), releasing the memory back to the OS. # # NOTE: MemoryPointer exposes direct, unmanaged operations on any # memory. It therefore MUST be used carefully. Reading or writing to # invalid address will cause bus errors and segmentation faults. # class Pointer include PointerAccessors def initialize(a1, a2=undefined) if undefined.equal? a2 self.address = a1 else @type = a1 self.address = a2 end end private :initialize def inspect # Don't have this print the data at the location. It can crash everything. addr = address() if addr < 0 sign = "-" addr = -addr else sign = "" end "#<#{self.class.name} address=#{sign}0x#{addr.to_s(16)}>" end # Return the address pointed to as an Integer def address Rubinius.primitive :pointer_address raise PrimitiveFailure, "FFI::Pointer#address primitive failed" end alias_method :to_i, :address # Set the address pointed to from an Integer def address=(address) Rubinius.primitive :pointer_set_address raise PrimitiveFailure, "FFI::Pointer#address= primitive failed" end def null? address == 0x0 end # Add +value+ to the address pointed to and return a new Pointer def +(value) Rubinius.primitive :pointer_add raise PrimitiveFailure, "FFI::Pointer#+ primitive failed" end # Indicates if +self+ and +other+ point to the same address def ==(other) return false unless other.kind_of? Pointer return address == other.address end def network_order(start, size) Rubinius.primitive :pointer_network_order raise PrimitiveFailure, "FFI::Pointer#network_order primitive failed" end # Read +len+ bytes from the memory pointed to and return them as # a String def read_string_length(len) Rubinius.primitive :pointer_read_string raise PrimitiveFailure, "FFI::Pointer#read_string_length primitive failed" end # Read bytes from the memory pointed to until a NULL is seen, return # the bytes as a String def read_string_to_null Rubinius.primitive :pointer_read_string_to_null raise PrimitiveFailure, "FFI::Pointer#read_string_to_null primitive failed" end # Read bytes as a String from the memory pointed to def read_string(len=nil) if len read_string_length(len) else read_string_to_null end end # FFI compat methods def get_bytes(offset, length) (self + offset).read_string_length(length) end # Write String +str+ as bytes into the memory pointed to. Only # write up to +len+ bytes. def write_string_length(str, len) Rubinius.primitive :pointer_write_string raise PrimitiveFailure, "FFI::Pointer#write_string_length primitive failed" end # Write a String +str+ as bytes to the memory pointed to. def write_string(str, len=nil) len = str.bytesize unless len write_string_length(str, len); end # Read a sequence of types +type+, length +length+, using method +reader+ def read_array_of_type(type, reader, length, signed=nil) # If signed is not nil and is actually a boolean, # then use that as an argument to the reader, which # is then assumed to support signed reading. args = [] args = [signed] if !signed.nil? # Build up the array ary = [] size = FFI.type_size(FFI.find_type type) tmp = self length.times { ary << tmp.send(reader, *args) tmp += size } ary end # Write a sequence of types +type+ using method +reader+ from +ary+ def write_array_of_type(type, writer, ary) size = FFI.type_size(FFI.find_type type) tmp = self ary.each do |i| tmp.send(writer, i) tmp += size end self end # Read bytes from +offset+ from the memory pointed to as type +type+ def get_at_offset(offset, type) Rubinius.primitive :pointer_get_at_offset raise PrimitiveFailure, "FFI::Pointer#get_at_offset primitive failed" end # Write +val+ as type +type+ to bytes from +offset+ def set_at_offset(offset, type, val) Rubinius.primitive :pointer_set_at_offset raise PrimitiveFailure, "FFI::Pointer#set_at_offset primitive failed" end # Number of bytes taken up by a pointer. def self.size Rubinius::WORDSIZE / 8 end # Primitive methods def primitive_read_char(signed) Rubinius.primitive :pointer_read_char raise PrimitiveFailure, "FFI::Pointer#primitive_read_char primitive failed" end def primitive_write_char(obj) Rubinius.primitive :pointer_write_char raise PrimitiveFailure, "FFI::Pointer#primitive_write_char primitive failed" end def primitive_read_short(signed) Rubinius.primitive :pointer_read_short raise PrimitiveFailure, "FFI::Pointer#primitive_read_short primitive failed" end def primitive_write_short(obj) Rubinius.primitive :pointer_write_short raise PrimitiveFailure, "FFI::Pointer#primitive_write_short primitive failed" end def primitive_read_int(signed) Rubinius.primitive :pointer_read_int raise PrimitiveFailure, "FFI::Pointer#primitive_read_int primitive failed" end def primitive_write_int(obj) Rubinius.primitive :pointer_write_int raise PrimitiveFailure, "FFI::Pointer#primitive_write_int primitive failed" end def primitive_read_long(signed) Rubinius.primitive :pointer_read_long raise PrimitiveFailure, "FFI::Pointer#primitive_read_long primitive failed" end def primitive_write_long(obj) Rubinius.primitive :pointer_write_long raise PrimitiveFailure, "FFI::Pointer#primitive_write_long primitive failed" end def primitive_read_long_long(signed) Rubinius.primitive :pointer_read_long_long raise PrimitiveFailure, "FFI::Pointer#primitive_read_long_long primitive failed" end def primitive_write_long_long(obj) Rubinius.primitive :pointer_write_long_long raise PrimitiveFailure, "FFI::Pointer#primitive_write_long_long primitive failed" end def primitive_read_float Rubinius.primitive :pointer_read_float raise PrimitiveFailure, "FFI::Pointer#primitive_read_float primitive failed" end def primitive_write_float(obj) Rubinius.primitive :pointer_write_float raise PrimitiveFailure, "FFI::Pointer#primitive_write_float primitive failed" end def primitive_read_double Rubinius.primitive :pointer_read_double raise PrimitiveFailure, "FFI::Pointer#primitive_read_double primitive failed" end def primitive_write_double(obj) Rubinius.primitive :pointer_write_double raise PrimitiveFailure, "FFI::Pointer#primitive_write_double primitive failed" end def primitive_read_pointer Rubinius.primitive :pointer_read_pointer raise PrimitiveFailure, "FFI::Pointer#primitive_read_pointer primitive failed" end def primitive_write_pointer(obj) Rubinius.primitive :pointer_write_pointer raise PrimitiveFailure, "FFI::Pointer#primitive_write_pointer primitive failed" end ## # If +val+ is true, this Pointer object will call # free() on it's address when it is garbage collected. def autorelease=(val) Rubinius.primitive :pointer_set_autorelease raise PrimitiveFailure, "FFI::Pointer#autorelease= primitive failed" end ## # Returns true if autorelease is enabled, otherwise false. def autorelease? Rubinius.primitive :pointer_autorelease_p raise PrimitiveFailure, "FFI::Pointer#pointer_autorelease_p primitive failed" end NULL = Pointer.new(0x0) end class MemoryPointer < Pointer # call-seq: # MemoryPointer.new(num) => MemoryPointer instance of num bytes # MemoryPointer.new(sym) => MemoryPointer instance with number # of bytes need by FFI type sym # MemoryPointer.new(obj) => MemoryPointer instance with number # of obj.size bytes # MemoryPointer.new(sym, count) => MemoryPointer instance with number # of bytes need by length-count array # of FFI type sym # MemoryPointer.new(obj, count) => MemoryPointer instance with number # of bytes need by length-count array # of obj.size bytes # MemoryPointer.new(arg) { |p| ... } # # Both forms create a MemoryPointer instance. The number of bytes to # allocate is either specified directly or by passing an FFI type, which # specifies the number of bytes needed for that type. # # The form without a block returns the MemoryPointer instance. The form # with a block yields the MemoryPointer instance and frees the memory # when the block returns. The value returned is the value of the block. # def self.new(type, count=nil, clear=true) if type.kind_of? Fixnum size = type elsif type.kind_of? Symbol type = FFI.find_type type size = FFI.type_size(type) else size = type.size end if count total = size * count else total = size end return NULL if total < 0 ptr = malloc total ptr.total = total ptr.type_size = size FFI::Platform::POSIX.memset ptr, 0, total if clear if block_given? begin value = yield ptr ensure ptr.free end return value else ptr.autorelease = true ptr end end def self.malloc(total) Rubinius.primitive :pointer_malloc raise PrimitiveFailure, "FFI::MemoryPointer.malloc primitive failed" end def self.from_string(str) ptr = new str.bytesize + 1 ptr.write_string str + "\0" ptr end def copy other = malloc total other.total = total other.type_size = type_size FFI::Platform::POSIX.memcpy other, self, total Rubinius.privately do other.initialize_copy self end other end # Indicates how many bytes the chunk of memory that is pointed to takes up. attr_accessor :total # Indicates how many bytes the type that the pointer is cast as uses. attr_accessor :type_size # Access the MemoryPointer like a C array, accessing the +which+ number # element in memory. The position of the element is calculate from # +@type_size+ and +which+. A new MemoryPointer object is returned, which # points to the address of the element. # # Example: # ptr = MemoryPointer.new(:int, 20) # new_ptr = ptr[9] # # c-equiv: # int *ptr = (int*)malloc(sizeof(int) * 20); # int *new_ptr; # new_ptr = &ptr[9]; # def [](which) raise ArgumentError, "unknown type size" unless @type_size self + (which * @type_size) end # Release the memory pointed to back to the OS. def free Rubinius.primitive :pointer_free raise PrimitiveFailure, "FFI::MemoryPointer#free primitive failed" end end class DynamicLibrary class Symbol < Pointer def initialize(library, ptr, name) @library = library @name = name self.address = ptr.address end private :initialize def inspect "#" end end end class Function < Pointer def initialize(ret_type, arg_types, val=nil, options=nil, &block) if block if val or options raise ArgumentError, "specify a block or a proc/address, not both" end val = block end args = arg_types.map { |x| FFI.find_type(x) } ret = FFI.find_type(ret_type) if val.kind_of? Pointer @function = FFI.generate_function(val, :func, args, ret) self.address = val.address elsif val.respond_to? :call @function, ptr = FFI.generate_trampoline(val, :func, args, ret) self.address = ptr.address else raise ArgumentError, "value wasn't a FFI::Pointer and didn't respond to call" end # Hook the created function into the method_table so that #call goes # straight to it. sc = Rubinius::Type.object_singleton_class(self) Rubinius::VM.reset_method_cache sc, :call sc.method_table.store :call, nil, @function, nil, 0, :public end private :initialize attr_reader :function # Hook this Function up to be an instance/class method +name+ on +mod+ def attach(mod, name) unless mod.kind_of?(Module) raise TypeError, "mod must be a Module" end name = name.to_sym # Make it available as a method callable directly.. sc = Rubinius::Type.object_singleton_class(mod) Rubinius::VM.reset_method_cache sc, name sc.method_table.store name, nil, @function, nil, 0, :public # and expose it as a private method for people who # want to include this module. Rubinius::VM.reset_method_cache mod, name mod.method_table.store name, nil, @function, nil, 0, :public end end end end