Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 9f4d9a498be1
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 47bc672df484
Choose a head ref
  • 3 commits
  • 10 files changed
  • 1 contributor

Commits on Jan 14, 2015

  1. Copy the full SHA
    dc30c92 View commit details
  2. Copy the full SHA
    697b06a View commit details
  3. Copy the full SHA
    47bc672 View commit details
43 changes: 25 additions & 18 deletions core/src/main/java/org/jruby/RubyThread.java
Original file line number Diff line number Diff line change
@@ -140,6 +140,9 @@ public class RubyThread extends RubyObject implements ExecutionContext {
/** Stack of interrupt masks active for this thread */
private final List<RubyHash> interruptMaskStack = new ArrayList<RubyHash>();

/** Thread-local tuple used for sleeping (semaphore, millis, nanos) */
private final SleepTask2 sleepTask = new SleepTask2();

private static final boolean DEBUG = false;
private int RUBY_MIN_THREAD_PRIORITY = -3;
private int RUBY_MAX_THREAD_PRIORITY = 3;
@@ -1172,12 +1175,16 @@ public synchronized IRubyObject run() {
*/
public boolean sleep(long millis) throws InterruptedException {
assert this == getRuntime().getCurrentContext().getThread();
Semaphore sem = new Semaphore(1);
sem.acquire();
if (executeTask(getContext(), new Object[]{sem, millis, 0}, SLEEP_TASK2) >= millis) {
return true;
} else {
return false;
sleepTask.millis = millis;
try {
if (executeTask(getContext(), null, sleepTask) >= millis) {
return true;
} else {
return false;
}
} finally {
// ensure we've re-acquired the semaphore, or a subsequent sleep may return immediately
sleepTask.semaphore.drainPermits();
}
}

@@ -1240,30 +1247,30 @@ public void wakeup() {
}
}

private static final class SleepTask2 implements Task<Object[], Long> {
@Override
public Long run(ThreadContext context, Object[] data) throws InterruptedException {
long millis = (Long)data[1];
int nanos = (Integer)data[2];
private class SleepTask2 implements Task<Object, Long> {
final Semaphore semaphore = new Semaphore(1);
long millis;
{ try {semaphore.acquire();} catch (InterruptedException ie){} }

@Override
public Long run(ThreadContext context, Object data) throws InterruptedException {
long start = System.currentTimeMillis();
// TODO: nano handling?

if (millis == 0) {
((Semaphore) data[0]).acquire();
semaphore.acquire();
} else {
((Semaphore) data[0]).tryAcquire(millis, TimeUnit.MILLISECONDS);
semaphore.tryAcquire(millis, TimeUnit.MILLISECONDS);
}

return System.currentTimeMillis() - start;
}

@Override
public void wakeup(RubyThread thread, Object[] data) {
((Semaphore)data[0]).release();
public void wakeup(RubyThread thread, Object data) {
semaphore.release();
}
}

private static final Task<Object[], Long> SLEEP_TASK2 = new SleepTask2();

@Deprecated
public void executeBlockingTask(BlockingTask task) throws InterruptedException {
try {
31 changes: 26 additions & 5 deletions lib/ruby/stdlib/fiddle.rb
Original file line number Diff line number Diff line change
@@ -2,13 +2,8 @@
require 'fiddle/jruby' if RUBY_ENGINE == 'jruby'
require 'fiddle/function'
require 'fiddle/closure'
require 'dl' unless Object.const_defined?(:DL)

module Fiddle

# A reference to DL::CPtr
Pointer = DL::CPtr unless RUBY_ENGINE == 'jruby'

if WINDOWS
# Returns the last win32 +Error+ of the current executing +Thread+ or nil
# if none
@@ -51,4 +46,30 @@ def self.last_error= error
Thread.current[:__FIDDLE_LAST_ERROR__] = error
end
end

# call-seq: dlopen(library) => Fiddle::Handle
#
# Creates a new handler that opens +library+, and returns an instance of
# Fiddle::Handle.
#
# If +nil+ is given for the +library+, Fiddle::Handle::DEFAULT is used, which
# is the equivalent to RTLD_DEFAULT. See <code>man 3 dlopen</code> for more.
#
# lib = Fiddle.dlopen(nil)
#
# The default is dependent on OS, and provide a handle for all libraries
# already loaded. For example, in most cases you can use this to access
# +libc+ functions, or ruby functions like +rb_str_new+.
#
# See Fiddle::Handle.new for more.
def dlopen library
Fiddle::Handle.new library
end
module_function :dlopen

# Add constants for backwards compat

RTLD_GLOBAL = Handle::RTLD_GLOBAL # :nodoc:
RTLD_LAZY = Handle::RTLD_LAZY # :nodoc:
RTLD_NOW = Handle::RTLD_NOW # :nodoc:
end
176 changes: 176 additions & 0 deletions lib/ruby/stdlib/fiddle/cparser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
module Fiddle
# A mixin that provides methods for parsing C struct and prototype signatures.
#
# == Example
# require 'fiddle/import'
#
# include Fiddle::CParser
# #=> Object
#
# parse_ctype('int increment(int)')
# #=> ["increment", Fiddle::TYPE_INT, [Fiddle::TYPE_INT]]
#
module CParser
# Parses a C struct's members
#
# Example:
#
# include Fiddle::CParser
# #=> Object
#
# parse_struct_signature(['int i', 'char c'])
# #=> [[Fiddle::TYPE_INT, Fiddle::TYPE_CHAR], ["i", "c"]]
#
def parse_struct_signature(signature, tymap=nil)
if( signature.is_a?(String) )
signature = signature.split(/\s*,\s*/)
end
mems = []
tys = []
signature.each{|msig|
tks = msig.split(/\s+(\*)?/)
ty = tks[0..-2].join(" ")
member = tks[-1]

case ty
when /\[(\d+)\]/
n = $1.to_i
ty.gsub!(/\s*\[\d+\]/,"")
ty = [ty, n]
when /\[\]/
ty.gsub!(/\s*\[\]/, "*")
end

case member
when /\[(\d+)\]/
ty = [ty, $1.to_i]
member.gsub!(/\s*\[\d+\]/,"")
when /\[\]/
ty = ty + "*"
member.gsub!(/\s*\[\]/, "")
end

mems.push(member)
tys.push(parse_ctype(ty,tymap))
}
return tys, mems
end

# Parses a C prototype signature
#
# If Hash +tymap+ is provided, the return value and the arguments from the
# +signature+ are expected to be keys, and the value will be the C type to
# be looked up.
#
# Example:
#
# include Fiddle::CParser
# #=> Object
#
# parse_signature('double sum(double, double)')
# #=> ["sum", Fiddle::TYPE_DOUBLE, [Fiddle::TYPE_DOUBLE, Fiddle::TYPE_DOUBLE]]
#
def parse_signature(signature, tymap=nil)
tymap ||= {}
signature = signature.gsub(/\s+/, " ").strip
case signature
when /^([\w@\*\s]+)\(([\w\*\s\,\[\]]*)\)$/
ret = $1
(args = $2).strip!
ret = ret.split(/\s+/)
args = args.split(/\s*,\s*/)
func = ret.pop
if( func =~ /^\*/ )
func.gsub!(/^\*+/,"")
ret.push("*")
end
ret = ret.join(" ")
return [func, parse_ctype(ret, tymap), args.collect{|arg| parse_ctype(arg, tymap)}]
else
raise(RuntimeError,"can't parse the function prototype: #{signature}")
end
end

# Given a String of C type +ty+, returns the corresponding Fiddle constant.
#
# +ty+ can also accept an Array of C type Strings, and will be returned in
# a corresponding Array.
#
# If Hash +tymap+ is provided, +ty+ is expected to be the key, and the
# value will be the C type to be looked up.
#
# Example:
#
# include Fiddle::CParser
# #=> Object
#
# parse_ctype('int')
# #=> Fiddle::TYPE_INT
#
# parse_ctype('double')
# #=> Fiddle::TYPE_DOUBLE
#
# parse_ctype('unsigned char')
# #=> -Fiddle::TYPE_CHAR
#
def parse_ctype(ty, tymap=nil)
tymap ||= {}
case ty
when Array
return [parse_ctype(ty[0], tymap), ty[1]]
when "void"
return TYPE_VOID
when "char"
return TYPE_CHAR
when "unsigned char"
return -TYPE_CHAR
when "short"
return TYPE_SHORT
when "unsigned short"
return -TYPE_SHORT
when "int"
return TYPE_INT
when "unsigned int", 'uint'
return -TYPE_INT
when "long"
return TYPE_LONG
when "unsigned long"
return -TYPE_LONG
when "long long"
if( defined?(TYPE_LONG_LONG) )
return TYPE_LONG_LONG
else
raise(RuntimeError, "unsupported type: #{ty}")
end
when "unsigned long long"
if( defined?(TYPE_LONG_LONG) )
return -TYPE_LONG_LONG
else
raise(RuntimeError, "unsupported type: #{ty}")
end
when "float"
return TYPE_FLOAT
when "double"
return TYPE_DOUBLE
when "size_t"
return TYPE_SIZE_T
when "ssize_t"
return TYPE_SSIZE_T
when "ptrdiff_t"
return TYPE_PTRDIFF_T
when "intptr_t"
return TYPE_INTPTR_T
when "uintptr_t"
return TYPE_UINTPTR_T
when /\*/, /\[\s*\]/
return TYPE_VOIDP
else
if( tymap[ty] )
return parse_ctype(tymap[ty], tymap)
else
raise(DLError, "unknown type: #{ty}")
end
end
end
end
end
11 changes: 11 additions & 0 deletions lib/ruby/stdlib/fiddle/function.rb
Original file line number Diff line number Diff line change
@@ -2,5 +2,16 @@ module Fiddle
class Function
# The ABI of the Function.
attr_reader :abi

# The address of this function
attr_reader :ptr

# The name of this function
attr_reader :name

# The integer memory location of this function
def to_i
ptr.to_i
end
end
end
314 changes: 314 additions & 0 deletions lib/ruby/stdlib/fiddle/import.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,314 @@
require 'fiddle'
require 'fiddle/struct'
require 'fiddle/cparser'

module Fiddle

# Used internally by Fiddle::Importer
class CompositeHandler
# Create a new handler with the open +handlers+
#
# Used internally by Fiddle::Importer.dlload
def initialize(handlers)
@handlers = handlers
end

# Array of the currently loaded libraries.
def handlers()
@handlers
end

# Returns the address as an Integer from any handlers with the function
# named +symbol+.
#
# Raises a DLError if the handle is closed.
def sym(symbol)
@handlers.each{|handle|
if( handle )
begin
addr = handle.sym(symbol)
return addr
rescue DLError
end
end
}
return nil
end

# See Fiddle::CompositeHandler.sym
def [](symbol)
sym(symbol)
end
end

# A DSL that provides the means to dynamically load libraries and build
# modules around them including calling extern functions within the C
# library that has been loaded.
#
# == Example
#
# require 'fiddle'
# require 'fiddle/import'
#
# module LibSum
# extend Fiddle::Importer
# dlload './libsum.so'
# extern 'double sum(double*, int)'
# extern 'double split(double)'
# end
#
module Importer
include Fiddle
include CParser
extend Importer

# Creates an array of handlers for the given +libs+, can be an instance of
# Fiddle::Handle, Fiddle::Importer, or will create a new instance of
# Fiddle::Handle using Fiddle.dlopen
#
# Raises a DLError if the library cannot be loaded.
#
# See Fiddle.dlopen
def dlload(*libs)
handles = libs.collect{|lib|
case lib
when nil
nil
when Handle
lib
when Importer
lib.handlers
else
begin
Fiddle.dlopen(lib)
rescue DLError
raise(DLError, "can't load #{lib}")
end
end
}.flatten()
@handler = CompositeHandler.new(handles)
@func_map = {}
@type_alias = {}
end

# Sets the type alias for +alias_type+ as +orig_type+
def typealias(alias_type, orig_type)
@type_alias[alias_type] = orig_type
end

# Returns the sizeof +ty+, using Fiddle::Importer.parse_ctype to determine
# the C type and the appropriate Fiddle constant.
def sizeof(ty)
case ty
when String
ty = parse_ctype(ty, @type_alias).abs()
case ty
when TYPE_CHAR
return SIZEOF_CHAR
when TYPE_SHORT
return SIZEOF_SHORT
when TYPE_INT
return SIZEOF_INT
when TYPE_LONG
return SIZEOF_LONG
when TYPE_LONG_LONG
return SIZEOF_LONG_LONG
when TYPE_FLOAT
return SIZEOF_FLOAT
when TYPE_DOUBLE
return SIZEOF_DOUBLE
when TYPE_VOIDP
return SIZEOF_VOIDP
else
raise(DLError, "unknown type: #{ty}")
end
when Class
if( ty.instance_methods().include?(:to_ptr) )
return ty.size()
end
end
return Pointer[ty].size()
end

def parse_bind_options(opts)
h = {}
while( opt = opts.shift() )
case opt
when :stdcall, :cdecl
h[:call_type] = opt
when :carried, :temp, :temporal, :bind
h[:callback_type] = opt
h[:carrier] = opts.shift()
else
h[opt] = true
end
end
h
end
private :parse_bind_options

# :stopdoc:
CALL_TYPE_TO_ABI = Hash.new { |h, k|
raise RuntimeError, "unsupported call type: #{k}"
}.merge({ :stdcall => (Function::STDCALL rescue Function::DEFAULT),
:cdecl => Function::DEFAULT,
nil => Function::DEFAULT
}).freeze
private_constant :CALL_TYPE_TO_ABI
# :startdoc:

# Creates a global method from the given C +signature+.
def extern(signature, *opts)
symname, ctype, argtype = parse_signature(signature, @type_alias)
opt = parse_bind_options(opts)
f = import_function(symname, ctype, argtype, opt[:call_type])
name = symname.gsub(/@.+/,'')
@func_map[name] = f
# define_method(name){|*args,&block| f.call(*args,&block)}
begin
/^(.+?):(\d+)/ =~ caller.first
file, line = $1, $2.to_i
rescue
file, line = __FILE__, __LINE__+3
end
module_eval(<<-EOS, file, line)
def #{name}(*args, &block)
@func_map['#{name}'].call(*args,&block)
end
EOS
module_function(name)
f
end

# Creates a global method from the given C +signature+ using the given
# +opts+ as bind parameters with the given block.
def bind(signature, *opts, &blk)
name, ctype, argtype = parse_signature(signature, @type_alias)
h = parse_bind_options(opts)
case h[:callback_type]
when :bind, nil
f = bind_function(name, ctype, argtype, h[:call_type], &blk)
else
raise(RuntimeError, "unknown callback type: #{h[:callback_type]}")
end
@func_map[name] = f
#define_method(name){|*args,&block| f.call(*args,&block)}
begin
/^(.+?):(\d+)/ =~ caller.first
file, line = $1, $2.to_i
rescue
file, line = __FILE__, __LINE__+3
end
module_eval(<<-EOS, file, line)
def #{name}(*args,&block)
@func_map['#{name}'].call(*args,&block)
end
EOS
module_function(name)
f
end

# Creates a class to wrap the C struct described by +signature+.
#
# MyStruct = struct ['int i', 'char c']
def struct(signature)
tys, mems = parse_struct_signature(signature, @type_alias)
Fiddle::CStructBuilder.create(CStruct, tys, mems)
end

# Creates a class to wrap the C union described by +signature+.
#
# MyUnion = union ['int i', 'char c']
def union(signature)
tys, mems = parse_struct_signature(signature, @type_alias)
Fiddle::CStructBuilder.create(CUnion, tys, mems)
end

# Returns the function mapped to +name+, that was created by either
# Fiddle::Importer.extern or Fiddle::Importer.bind
def [](name)
@func_map[name]
end

# Creates a class to wrap the C struct with the value +ty+
#
# See also Fiddle::Importer.struct
def create_value(ty, val=nil)
s = struct([ty + " value"])
ptr = s.malloc()
if( val )
ptr.value = val
end
return ptr
end
alias value create_value

# Returns a new instance of the C struct with the value +ty+ at the +addr+
# address.
def import_value(ty, addr)
s = struct([ty + " value"])
ptr = s.new(addr)
return ptr
end


# The Fiddle::CompositeHandler instance
#
# Will raise an error if no handlers are open.
def handler
@handler or raise "call dlload before importing symbols and functions"
end

# Returns a new Fiddle::Pointer instance at the memory address of the given
# +name+ symbol.
#
# Raises a DLError if the +name+ doesn't exist.
#
# See Fiddle::CompositeHandler.sym and Fiddle::Handle.sym
def import_symbol(name)
addr = handler.sym(name)
if( !addr )
raise(DLError, "cannot find the symbol: #{name}")
end
Pointer.new(addr)
end

# Returns a new Fiddle::Function instance at the memory address of the given
# +name+ function.
#
# Raises a DLError if the +name+ doesn't exist.
#
# * +argtype+ is an Array of arguments, passed to the +name+ function.
# * +ctype+ is the return type of the function
# * +call_type+ is the ABI of the function
#
# See also Fiddle:Function.new
#
# See Fiddle::CompositeHandler.sym and Fiddle::Handler.sym
def import_function(name, ctype, argtype, call_type = nil)
addr = handler.sym(name)
if( !addr )
raise(DLError, "cannot find the function: #{name}()")
end
Function.new(addr, argtype, ctype, CALL_TYPE_TO_ABI[call_type],
name: name)
end

# Returns a new closure wrapper for the +name+ function.
#
# * +ctype+ is the return type of the function
# * +argtype+ is an Array of arguments, passed to the callback function
# * +call_type+ is the abi of the closure
# * +block+ is passed to the callback
#
# See Fiddle::Closure
def bind_function(name, ctype, argtype, call_type = nil, &block)
abi = CALL_TYPE_TO_ABI[call_type]
closure = Class.new(Fiddle::Closure) {
define_method(:call, block)
}.new(ctype, argtype, abi)

Function.new(closure, argtype, ctype, abi, name: name)
end
end
end
128 changes: 128 additions & 0 deletions lib/ruby/stdlib/fiddle/pack.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
require 'fiddle'

module Fiddle
module PackInfo # :nodoc: all
ALIGN_MAP = {
TYPE_VOIDP => ALIGN_VOIDP,
TYPE_CHAR => ALIGN_CHAR,
TYPE_SHORT => ALIGN_SHORT,
TYPE_INT => ALIGN_INT,
TYPE_LONG => ALIGN_LONG,
TYPE_FLOAT => ALIGN_FLOAT,
TYPE_DOUBLE => ALIGN_DOUBLE,
-TYPE_CHAR => ALIGN_CHAR,
-TYPE_SHORT => ALIGN_SHORT,
-TYPE_INT => ALIGN_INT,
-TYPE_LONG => ALIGN_LONG,
}

PACK_MAP = {
TYPE_VOIDP => ((SIZEOF_VOIDP == SIZEOF_LONG_LONG) ? "q" : "l!"),
TYPE_CHAR => "c",
TYPE_SHORT => "s!",
TYPE_INT => "i!",
TYPE_LONG => "l!",
TYPE_FLOAT => "f",
TYPE_DOUBLE => "d",
-TYPE_CHAR => "c",
-TYPE_SHORT => "s!",
-TYPE_INT => "i!",
-TYPE_LONG => "l!",
}

SIZE_MAP = {
TYPE_VOIDP => SIZEOF_VOIDP,
TYPE_CHAR => SIZEOF_CHAR,
TYPE_SHORT => SIZEOF_SHORT,
TYPE_INT => SIZEOF_INT,
TYPE_LONG => SIZEOF_LONG,
TYPE_FLOAT => SIZEOF_FLOAT,
TYPE_DOUBLE => SIZEOF_DOUBLE,
-TYPE_CHAR => SIZEOF_CHAR,
-TYPE_SHORT => SIZEOF_SHORT,
-TYPE_INT => SIZEOF_INT,
-TYPE_LONG => SIZEOF_LONG,
}
if defined?(TYPE_LONG_LONG)
ALIGN_MAP[TYPE_LONG_LONG] = ALIGN_MAP[-TYPE_LONG_LONG] = ALIGN_LONG_LONG
PACK_MAP[TYPE_LONG_LONG] = PACK_MAP[-TYPE_LONG_LONG] = "q"
SIZE_MAP[TYPE_LONG_LONG] = SIZE_MAP[-TYPE_LONG_LONG] = SIZEOF_LONG_LONG
end

def align(addr, align)
d = addr % align
if( d == 0 )
addr
else
addr + (align - d)
end
end
module_function :align
end

class Packer # :nodoc: all
include PackInfo

def self.[](*types)
new(types)
end

def initialize(types)
parse_types(types)
end

def size()
@size
end

def pack(ary)
case SIZEOF_VOIDP
when SIZEOF_LONG
ary.pack(@template)
when SIZEOF_LONG_LONG
ary.pack(@template)
else
raise(RuntimeError, "sizeof(void*)?")
end
end

def unpack(ary)
case SIZEOF_VOIDP
when SIZEOF_LONG
ary.join().unpack(@template)
when SIZEOF_LONG_LONG
ary.join().unpack(@template)
else
raise(RuntimeError, "sizeof(void*)?")
end
end

private

def parse_types(types)
@template = ""
addr = 0
types.each{|t|
orig_addr = addr
if( t.is_a?(Array) )
addr = align(orig_addr, ALIGN_MAP[TYPE_VOIDP])
else
addr = align(orig_addr, ALIGN_MAP[t])
end
d = addr - orig_addr
if( d > 0 )
@template << "x#{d}"
end
if( t.is_a?(Array) )
@template << (PACK_MAP[t[0]] * t[1])
addr += (SIZE_MAP[t[0]] * t[1])
else
@template << PACK_MAP[t]
addr += SIZE_MAP[t]
end
}
addr = align(addr, ALIGN_MAP[TYPE_VOIDP])
@size = addr
end
end
end
243 changes: 243 additions & 0 deletions lib/ruby/stdlib/fiddle/struct.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
require 'fiddle'
require 'fiddle/value'
require 'fiddle/pack'

module Fiddle
# C struct shell
class CStruct
# accessor to Fiddle::CStructEntity
def CStruct.entity_class
CStructEntity
end
end

# C union shell
class CUnion
# accessor to Fiddle::CUnionEntity
def CUnion.entity_class
CUnionEntity
end
end

# Used to construct C classes (CUnion, CStruct, etc)
#
# Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an
# easy-to-use manner.
module CStructBuilder
# Construct a new class given a C:
# * class +klass+ (CUnion, CStruct, or other that provide an
# #entity_class)
# * +types+ (Fiddle::TYPE_INT, Fiddle::TYPE_SIZE_T, etc., see the C types
# constants)
# * corresponding +members+
#
# Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an
# easy-to-use manner.
#
# Example:
#
# require 'fiddle/struct'
# require 'fiddle/cparser'
#
# include Fiddle::CParser
#
# types, members = parse_struct_signature(['int i','char c'])
#
# MyStruct = Fiddle::CStructBuilder.create(Fiddle::CUnion, types, members)
#
# obj = MyStruct.allocate
#
def create(klass, types, members)
new_class = Class.new(klass){
define_method(:initialize){|addr|
@entity = klass.entity_class.new(addr, types)
@entity.assign_names(members)
}
define_method(:to_ptr){ @entity }
define_method(:to_i){ @entity.to_i }
members.each{|name|
define_method(name){ @entity[name] }
define_method(name + "="){|val| @entity[name] = val }
}
}
size = klass.entity_class.size(types)
new_class.module_eval(<<-EOS, __FILE__, __LINE__+1)
def new_class.size()
#{size}
end
def new_class.malloc()
addr = Fiddle.malloc(#{size})
new(addr)
end
EOS
return new_class
end
module_function :create
end

# A C struct wrapper
class CStructEntity < Fiddle::Pointer
include PackInfo
include ValueUtil

# Allocates a C struct with the +types+ provided.
#
# When the instance is garbage collected, the C function +func+ is called.
def CStructEntity.malloc(types, func = nil)
addr = Fiddle.malloc(CStructEntity.size(types))
CStructEntity.new(addr, types, func)
end

# Returns the offset for the packed sizes for the given +types+.
#
# Fiddle::CStructEntity.size(
# [ Fiddle::TYPE_DOUBLE,
# Fiddle::TYPE_INT,
# Fiddle::TYPE_CHAR,
# Fiddle::TYPE_VOIDP ]) #=> 24
def CStructEntity.size(types)
offset = 0

max_align = types.map { |type, count = 1|
last_offset = offset

align = PackInfo::ALIGN_MAP[type]
offset = PackInfo.align(last_offset, align) +
(PackInfo::SIZE_MAP[type] * count)

align
}.max

PackInfo.align(offset, max_align)
end

# Wraps the C pointer +addr+ as a C struct with the given +types+.
#
# When the instance is garbage collected, the C function +func+ is called.
#
# See also Fiddle::Pointer.new
def initialize(addr, types, func = nil)
set_ctypes(types)
super(addr, @size, func)
end

# Set the names of the +members+ in this C struct
def assign_names(members)
@members = members
end

# Calculates the offsets and sizes for the given +types+ in the struct.
def set_ctypes(types)
@ctypes = types
@offset = []
offset = 0

max_align = types.map { |type, count = 1|
orig_offset = offset
align = ALIGN_MAP[type]
offset = PackInfo.align(orig_offset, align)

@offset << offset

offset += (SIZE_MAP[type] * count)

align
}.max

@size = PackInfo.align(offset, max_align)
end

# Fetch struct member +name+
def [](name)
idx = @members.index(name)
if( idx.nil? )
raise(ArgumentError, "no such member: #{name}")
end
ty = @ctypes[idx]
if( ty.is_a?(Array) )
r = super(@offset[idx], SIZE_MAP[ty[0]] * ty[1])
else
r = super(@offset[idx], SIZE_MAP[ty.abs])
end
packer = Packer.new([ty])
val = packer.unpack([r])
case ty
when Array
case ty[0]
when TYPE_VOIDP
val = val.collect{|v| Pointer.new(v)}
end
when TYPE_VOIDP
val = Pointer.new(val[0])
else
val = val[0]
end
if( ty.is_a?(Integer) && (ty < 0) )
return unsigned_value(val, ty)
elsif( ty.is_a?(Array) && (ty[0] < 0) )
return val.collect{|v| unsigned_value(v,ty[0])}
else
return val
end
end

# Set struct member +name+, to value +val+
def []=(name, val)
idx = @members.index(name)
if( idx.nil? )
raise(ArgumentError, "no such member: #{name}")
end
ty = @ctypes[idx]
packer = Packer.new([ty])
val = wrap_arg(val, ty, [])
buff = packer.pack([val].flatten())
super(@offset[idx], buff.size, buff)
if( ty.is_a?(Integer) && (ty < 0) )
return unsigned_value(val, ty)
elsif( ty.is_a?(Array) && (ty[0] < 0) )
return val.collect{|v| unsigned_value(v,ty[0])}
else
return val
end
end

def to_s() # :nodoc:
super(@size)
end
end

# A C union wrapper
class CUnionEntity < CStructEntity
include PackInfo

# Allocates a C union the +types+ provided.
#
# When the instance is garbage collected, the C function +func+ is called.
def CUnionEntity.malloc(types, func=nil)
addr = Fiddle.malloc(CUnionEntity.size(types))
CUnionEntity.new(addr, types, func)
end

# Returns the size needed for the union with the given +types+.
#
# Fiddle::CUnionEntity.size(
# [ Fiddle::TYPE_DOUBLE,
# Fiddle::TYPE_INT,
# Fiddle::TYPE_CHAR,
# Fiddle::TYPE_VOIDP ]) #=> 8
def CUnionEntity.size(types)
types.map { |type, count = 1|
PackInfo::SIZE_MAP[type] * count
}.max
end

# Calculate the necessary offset and for each union member with the given
# +types+
def set_ctypes(types)
@ctypes = types
@offset = Array.new(types.length, 0)
@size = self.class.size types
end
end
end

71 changes: 71 additions & 0 deletions lib/ruby/stdlib/fiddle/types.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
module Fiddle
# Adds Windows type aliases to the including class for use with
# Fiddle::Importer.
#
# The aliases added are:
# * ATOM
# * BOOL
# * BYTE
# * DWORD
# * DWORD32
# * DWORD64
# * HANDLE
# * HDC
# * HINSTANCE
# * HWND
# * LPCSTR
# * LPSTR
# * PBYTE
# * PDWORD
# * PHANDLE
# * PVOID
# * PWORD
# * UCHAR
# * UINT
# * ULONG
# * WORD
module Win32Types
def included(m) # :nodoc:
m.module_eval{
typealias "DWORD", "unsigned long"
typealias "PDWORD", "unsigned long *"
typealias "DWORD32", "unsigned long"
typealias "DWORD64", "unsigned long long"
typealias "WORD", "unsigned short"
typealias "PWORD", "unsigned short *"
typealias "BOOL", "int"
typealias "ATOM", "int"
typealias "BYTE", "unsigned char"
typealias "PBYTE", "unsigned char *"
typealias "UINT", "unsigned int"
typealias "ULONG", "unsigned long"
typealias "UCHAR", "unsigned char"
typealias "HANDLE", "uintptr_t"
typealias "PHANDLE", "void*"
typealias "PVOID", "void*"
typealias "LPCSTR", "char*"
typealias "LPSTR", "char*"
typealias "HINSTANCE", "unsigned int"
typealias "HDC", "unsigned int"
typealias "HWND", "unsigned int"
}
end
module_function :included
end

# Adds basic type aliases to the including class for use with Fiddle::Importer.
#
# The aliases added are +uint+ and +u_int+ (<tt>unsigned int</tt>) and
# +ulong+ and +u_long+ (<tt>unsigned long</tt>)
module BasicTypes
def included(m) # :nodoc:
m.module_eval{
typealias "uint", "unsigned int"
typealias "u_int", "unsigned int"
typealias "ulong", "unsigned long"
typealias "u_long", "unsigned long"
}
end
module_function :included
end
end
112 changes: 112 additions & 0 deletions lib/ruby/stdlib/fiddle/value.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
require 'fiddle'

module Fiddle
module ValueUtil #:nodoc: all
def unsigned_value(val, ty)
case ty.abs
when TYPE_CHAR
[val].pack("c").unpack("C")[0]
when TYPE_SHORT
[val].pack("s!").unpack("S!")[0]
when TYPE_INT
[val].pack("i!").unpack("I!")[0]
when TYPE_LONG
[val].pack("l!").unpack("L!")[0]
when TYPE_LONG_LONG
[val].pack("q").unpack("Q")[0]
else
val
end
end

def signed_value(val, ty)
case ty.abs
when TYPE_CHAR
[val].pack("C").unpack("c")[0]
when TYPE_SHORT
[val].pack("S!").unpack("s!")[0]
when TYPE_INT
[val].pack("I!").unpack("i!")[0]
when TYPE_LONG
[val].pack("L!").unpack("l!")[0]
when TYPE_LONG_LONG
[val].pack("Q").unpack("q")[0]
else
val
end
end

def wrap_args(args, tys, funcs, &block)
result = []
tys ||= []
args.each_with_index{|arg, idx|
result.push(wrap_arg(arg, tys[idx], funcs, &block))
}
result
end

def wrap_arg(arg, ty, funcs = [], &block)
funcs ||= []
case arg
when nil
return 0
when Pointer
return arg.to_i
when IO
case ty
when TYPE_VOIDP
return Pointer[arg].to_i
else
return arg.to_i
end
when Function
if( block )
arg.bind_at_call(&block)
funcs.push(arg)
elsif !arg.bound?
raise(RuntimeError, "block must be given.")
end
return arg.to_i
when String
if( ty.is_a?(Array) )
return arg.unpack('C*')
else
case SIZEOF_VOIDP
when SIZEOF_LONG
return [arg].pack("p").unpack("l!")[0]
when SIZEOF_LONG_LONG
return [arg].pack("p").unpack("q")[0]
else
raise(RuntimeError, "sizeof(void*)?")
end
end
when Float, Integer
return arg
when Array
if( ty.is_a?(Array) ) # used only by struct
case ty[0]
when TYPE_VOIDP
return arg.collect{|v| Integer(v)}
when TYPE_CHAR
if( arg.is_a?(String) )
return val.unpack('C*')
end
end
return arg
else
return arg
end
else
if( arg.respond_to?(:to_ptr) )
return arg.to_ptr.to_i
else
begin
return Integer(arg)
rescue
raise(ArgumentError, "unknown argument type: #{arg.class}")
end
end
end
end
end
end
4 changes: 3 additions & 1 deletion tool/globals_2_2_0.rb
Original file line number Diff line number Diff line change
@@ -86,5 +86,7 @@
'ext/ripper/lib/ripper' => 'ripper',
'ext/ripper/lib/ripper.rb' => 'ripper.rb',
'ext/socket/lib/socket.rb' => 'socket.rb',
'ext/win32/lib/win32' => 'win32'
'ext/win32/lib/win32' => 'win32',
'ext/fiddle/lib/fiddle.rb' => 'fiddle.rb',
'ext/fiddle/lib/fiddle' => 'fiddle'
}