Este script cria a possibilidade de conexão com a internet via TCPSocket pelo RPGMaker VXAce.
Não devo mais precisar desde script, por tanto não devo mais atualiza-lo, então resolvi postar.
Ela possui dependências não supridas pelo RGSS3, ele inclui a extensão dl, mas não os seus arquivos .rb(com exceção do 'Win32API.rb'), então será necessário repor esses scripts:
Nessa grande código há 10 scripts diferentes, o thread.rb foi criado por Yukihiro Matsumoto (criador do Ruby e é licenciado pela Ruby License (Compatível com GPL))
Os outros não possuem header com os autores, apenas tem algumas RCS IDs com nomes que prefiro não citar aqui, mas de qualquer mode estão sob a Ruby License do mesmo modo.
E por fim, as funções de socket:
Pergunta: Por que não usar Win32API ao invés de dl?o código ficaria 36.5KB mais leve!
Resposta: Prefiro usar dl, Win32API não deve mais ser usada, esta em ativa apenas por compatibilidade, será removida do Ruby em breve.
Não devo mais precisar desde script, por tanto não devo mais atualiza-lo, então resolvi postar.
Ela possui dependências não supridas pelo RGSS3, ele inclui a extensão dl, mas não os seus arquivos .rb(com exceção do 'Win32API.rb'), então será necessário repor esses scripts:
- Código:
#
# thread.rb - thread support classes
# by Yukihiro Matsumoto <matz@netlab.co.jp>
#
# Copyright (C) 2001 Yukihiro Matsumoto
# Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
# Copyright (C) 2000 Information-technology Promotion Agency, Japan
#
unless defined? Thread
raise "Thread not available for this ruby interpreter"
end
unless defined? ThreadError
class ThreadError < StandardError
end
end
if $DEBUG
Thread.abort_on_exception = true
end
#
# ConditionVariable objects augment class Mutex. Using condition variables,
# it is possible to suspend while in the middle of a critical section until a
# resource becomes available.
#
# Example:
#
# require 'thread'
#
# mutex = Mutex.new
# resource = ConditionVariable.new
#
# a = Thread.new {
# mutex.synchronize {
# # Thread 'a' now needs the resource
# resource.wait(mutex)
# # 'a' can now have the resource
# }
# }
#
# b = Thread.new {
# mutex.synchronize {
# # Thread 'b' has finished using the resource
# resource.signal
# }
# }
#
class ConditionVariable
#
# Creates a new ConditionVariable
#
def initialize
@waiters = []
@waiters_mutex = Mutex.new
end
#
# Releases the lock held in +mutex+ and waits; reacquires the lock on wakeup.
#
def wait(mutex)
begin
# TODO: mutex should not be used
@waiters_mutex.synchronize do
@waiters.push(Thread.current)
end
mutex.sleep
end
end
#
# Wakes up the first thread in line waiting for this lock.
#
def signal
begin
t = @waiters_mutex.synchronize { @waiters.shift }
t.run if t
rescue ThreadError
retry
end
end
#
# Wakes up all threads waiting for this lock.
#
def broadcast
# TODO: imcomplete
waiters0 = nil
@waiters_mutex.synchronize do
waiters0 = @waiters.dup
@waiters.clear
end
for t in waiters0
begin
t.run
rescue ThreadError
end
end
end
end
#
# This class provides a way to synchronize communication between threads.
#
# Example:
#
# require 'thread'
#
# queue = Queue.new
#
# producer = Thread.new do
# 5.times do |i|
# sleep rand(i) # simulate expense
# queue << i
# puts "#{i} produced"
# end
# end
#
# consumer = Thread.new do
# 5.times do |i|
# value = queue.pop
# sleep rand(i/2) # simulate expense
# puts "consumed #{value}"
# end
# end
#
# consumer.join
#
class Queue
#
# Creates a new queue.
#
def initialize
@que = []
@waiting = []
@que.taint # enable tainted comunication
@waiting.taint
self.taint
@mutex = Mutex.new
end
#
# Pushes +obj+ to the queue.
#
def push(obj)
t = nil
@mutex.synchronize{
@que.push obj
begin
t = @waiting.shift
t.wakeup if t
rescue ThreadError
retry
end
}
begin
t.run if t
rescue ThreadError
end
end
#
# Alias of push
#
alias << push
#
# Alias of push
#
alias enq push
#
# Retrieves data from the queue. If the queue is empty, the calling thread is
# suspended until data is pushed onto the queue. If +non_block+ is true, the
# thread isn't suspended, and an exception is raised.
#
def pop(non_block=false)
while true
@mutex.synchronize{
if @que.empty?
raise ThreadError, "queue empty" if non_block
@waiting.push Thread.current
@mutex.sleep
else
return @que.shift
end
}
end
end
#
# Alias of pop
#
alias shift pop
#
# Alias of pop
#
alias deq pop
#
# Returns +true+ if the queue is empty.
#
def empty?
@que.empty?
end
#
# Removes all objects from the queue.
#
def clear
@que.clear
end
#
# Returns the length of the queue.
#
def length
@que.length
end
#
# Alias of length.
#
alias size length
#
# Returns the number of threads waiting on the queue.
#
def num_waiting
@waiting.size
end
end
#
# This class represents queues of specified size capacity. The push operation
# may be blocked if the capacity is full.
#
# See Queue for an example of how a SizedQueue works.
#
class SizedQueue < Queue
#
# Creates a fixed-length queue with a maximum size of +max+.
#
def initialize(max)
raise ArgumentError, "queue size must be positive" unless max > 0
@max = max
@queue_wait = []
@queue_wait.taint # enable tainted comunication
super()
end
#
# Returns the maximum size of the queue.
#
def max
@max
end
#
# Sets the maximum size of the queue.
#
def max=(max)
diff = nil
@mutex.synchronize {
if max <= @max
@max = max
else
diff = max - @max
@max = max
end
}
if diff
diff.times do
begin
t = @queue_wait.shift
t.run if t
rescue ThreadError
retry
end
end
end
max
end
#
# Pushes +obj+ to the queue. If there is no space left in the queue, waits
# until space becomes available.
#
def push(obj)
t = nil
@mutex.synchronize{
while true
break if @que.length < @max
@queue_wait.push Thread.current
@mutex.sleep
end
@que.push obj
begin
t = @waiting.shift
t.wakeup if t
rescue ThreadError
retry
end
}
begin
t.run if t
rescue ThreadError
end
end
#
# Alias of push
#
alias << push
#
# Alias of push
#
alias enq push
#
# Retrieves data from the queue and runs a waiting thread, if any.
#
def pop(*args)
retval = super
t = nil
@mutex.synchronize {
if @que.length < @max
begin
t = @queue_wait.shift
t.wakeup if t
rescue ThreadError
retry
end
end
}
begin
t.run if t
rescue ThreadError
end
retval
end
#
# Alias of pop
#
alias shift pop
#
# Alias of pop
#
alias deq pop
#
# Returns the number of threads waiting on the queue.
#
def num_waiting
@waiting.size + @queue_wait.size
end
end
# Documentation comments:
# - How do you make RDoc inherit documentation from superclass?
#require 'dl'
#require 'thread'
module DL
SEM = Mutex.new
def set_callback_internal(proc_entry, addr_entry, argc, ty, &cbp)
if( argc < 0 )
raise(ArgumentError, "arity should not be less than 0.")
end
addr = nil
SEM.synchronize{
ary = proc_entry[ty]
(0...MAX_CALLBACK).each{|n|
idx = (n * DLSTACK_SIZE) + argc
if( ary[idx].nil? )
ary[idx] = cbp
addr = addr_entry[ty][idx]
break
end
}
}
addr
end
def set_cdecl_callback(ty, argc, &cbp)
set_callback_internal(CdeclCallbackProcs, CdeclCallbackAddrs, argc, ty, &cbp)
end
def set_stdcall_callback(ty, argc, &cbp)
set_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, argc, ty, &cbp)
end
def remove_callback_internal(proc_entry, addr_entry, addr, ctype = nil)
index = nil
if( ctype )
addr_entry[ctype].each_with_index{|xaddr, idx|
if( xaddr == addr )
index = idx
end
}
else
addr_entry.each{|ty,entry|
entry.each_with_index{|xaddr, idx|
if( xaddr == addr )
index = idx
end
}
}
end
if( index and proc_entry[ctype][index] )
proc_entry[ctype][index] = nil
return true
else
return false
end
end
def remove_cdecl_callback(addr, ctype = nil)
remove_callback_internal(CdeclCallbackProcs, CdeclCallbackAddrs, addr, ctype)
end
def remove_stdcall_callback(addr, ctype = nil)
remove_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, addr, ctype)
end
alias set_callback set_cdecl_callback
alias remove_callback remove_cdecl_callback
end
module DL
module CParser
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
def parse_signature(signature, tymap=nil)
tymap ||= {}
signature = signature.gsub(/\s+/, " ").strip
case signature
when /^([\d\w@\*_\s]+)\(([\d\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: #{proto}")
end
end
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"
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 /\*/, /\[\s*\]/
return TYPE_VOIDP
else
if( tymap[ty] )
return parse_ctype(tymap[ty], tymap)
else
raise(DLError, "unknown type: #{ty}", caller(1))
end
end
end
end
end
#require 'dl'
module DL
module PackInfo
if( defined?(TYPE_LONG_LONG) )
ALIGN_MAP = {
TYPE_VOIDP => ALIGN_VOIDP,
TYPE_CHAR => ALIGN_CHAR,
TYPE_SHORT => ALIGN_SHORT,
TYPE_INT => ALIGN_INT,
TYPE_LONG => ALIGN_LONG,
TYPE_LONG_LONG => ALIGN_LONG_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,
-TYPE_LONG_LONG => ALIGN_LONG_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_LONG_LONG => "q",
TYPE_FLOAT => "f",
TYPE_DOUBLE => "d",
-TYPE_CHAR => "c",
-TYPE_SHORT => "s!",
-TYPE_INT => "i!",
-TYPE_LONG => "l!",
-TYPE_LONG_LONG => "q",
}
SIZE_MAP = {
TYPE_VOIDP => SIZEOF_VOIDP,
TYPE_CHAR => SIZEOF_CHAR,
TYPE_SHORT => SIZEOF_SHORT,
TYPE_INT => SIZEOF_INT,
TYPE_LONG => SIZEOF_LONG,
TYPE_LONG_LONG => SIZEOF_LONG_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,
-TYPE_LONG_LONG => SIZEOF_LONG_LONG,
}
else
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,
}
end
def align(addr, align)
d = addr % align
if( d == 0 )
addr
else
addr + (align - d)
end
end
module_function :align
end
class Packer
include PackInfo
def Packer.[](*types)
Packer.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
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
#require 'dl'
module DL
class Stack
def Stack.[](*types)
Stack.new(types)
end
def initialize(types)
parse_types(types)
end
def size()
@size
end
def types()
@types
end
def pack(ary)
case SIZEOF_VOIDP
when SIZEOF_LONG
ary.pack(@template).unpack('l!*')
when SIZEOF_LONG_LONG
ary.pack(@template).unpack('q*')
else
raise(RuntimeError, "sizeof(void*)?")
end
end
def unpack(ary)
case SIZEOF_VOIDP
when SIZEOF_LONG
ary.pack('l!*').unpack(@template)
when SIZEOF_LONG_LONG
ary.pack('q*').unpack(@template)
else
raise(RuntimeError, "sizeof(void*)?")
end
end
private
def align(addr, align)
d = addr % align
if( d == 0 )
addr
else
addr + (align - d)
end
end
if( defined?(TYPE_LONG_LONG) )
ALIGN_MAP = {
TYPE_VOIDP => ALIGN_VOIDP,
TYPE_CHAR => ALIGN_VOIDP,
TYPE_SHORT => ALIGN_VOIDP,
TYPE_INT => ALIGN_VOIDP,
TYPE_LONG => ALIGN_VOIDP,
TYPE_LONG_LONG => ALIGN_LONG_LONG,
TYPE_FLOAT => ALIGN_FLOAT,
TYPE_DOUBLE => ALIGN_DOUBLE,
}
PACK_MAP = {
TYPE_VOIDP => ((SIZEOF_VOIDP == SIZEOF_LONG_LONG)? "q" : "l!"),
TYPE_CHAR => "c",
TYPE_SHORT => "s!",
TYPE_INT => "i!",
TYPE_LONG => "l!",
TYPE_LONG_LONG => "q",
TYPE_FLOAT => "f",
TYPE_DOUBLE => "d",
}
SIZE_MAP = {
TYPE_VOIDP => SIZEOF_VOIDP,
TYPE_CHAR => SIZEOF_CHAR,
TYPE_SHORT => SIZEOF_SHORT,
TYPE_INT => SIZEOF_INT,
TYPE_LONG => SIZEOF_LONG,
TYPE_LONG_LONG => SIZEOF_LONG_LONG,
TYPE_FLOAT => SIZEOF_FLOAT,
TYPE_DOUBLE => SIZEOF_DOUBLE,
}
else
ALIGN_MAP = {
TYPE_VOIDP => ALIGN_VOIDP,
TYPE_CHAR => ALIGN_VOIDP,
TYPE_SHORT => ALIGN_VOIDP,
TYPE_INT => ALIGN_VOIDP,
TYPE_LONG => ALIGN_VOIDP,
TYPE_FLOAT => ALIGN_FLOAT,
TYPE_DOUBLE => ALIGN_DOUBLE,
}
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",
}
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,
}
end
def parse_types(types)
@types = types
@template = ""
addr = 0
types.each{|t|
addr = add_padding(addr, ALIGN_MAP[t])
@template << PACK_MAP[t]
addr += SIZE_MAP[t]
}
addr = add_padding(addr, ALIGN_MAP[SIZEOF_VOIDP])
if( addr % SIZEOF_VOIDP == 0 )
@size = addr / SIZEOF_VOIDP
else
@size = (addr / SIZEOF_VOIDP) + 1
end
end
def add_padding(addr, align)
orig_addr = addr
addr = align(orig_addr, align)
d = addr - orig_addr
if( d > 0 )
@template << "x#{d}"
end
addr
end
end
end
#require 'dl'
module DL
module ValueUtil
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 CPtr
return arg.to_i
when IO
case ty
when TYPE_VOIDP
return CPtr[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
#require 'dl'
#require 'dl/pack.rb'
module DL
class CStruct
def CStruct.entity_class()
CStructEntity
end
end
class CUnion
def CUnion.entity_class()
CUnionEntity
end
end
module CStructBuilder
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)
def new_class.size()
#{size}
end
def new_class.malloc()
addr = DL.malloc(#{size})
new(addr)
end
EOS
return new_class
end
module_function :create
end
class CStructEntity < CPtr
include PackInfo
include ValueUtil
def CStructEntity.malloc(types, func = nil)
addr = DL.malloc(CStructEntity.size(types))
CStructEntity.new(addr, types, func)
end
def CStructEntity.size(types)
offset = 0
max_align = 0
types.each_with_index{|t,i|
orig_offset = offset
if( t.is_a?(Array) )
align = PackInfo::ALIGN_MAP[t[0]]
offset = PackInfo.align(orig_offset, align)
size = offset - orig_offset
offset += (PackInfo::SIZE_MAP[t[0]] * t[1])
else
align = PackInfo::ALIGN_MAP[t]
offset = PackInfo.align(orig_offset, align)
size = offset - orig_offset
offset += PackInfo::SIZE_MAP[t]
end
if (max_align < align)
max_align = align
end
}
offset = PackInfo.align(offset, max_align)
offset
end
def initialize(addr, types, func = nil)
set_ctypes(types)
super(addr, @size, func)
end
def assign_names(members)
@members = members
end
def set_ctypes(types)
@ctypes = types
@offset = []
offset = 0
max_align = 0
types.each_with_index{|t,i|
orig_offset = offset
if( t.is_a?(Array) )
align = ALIGN_MAP[t[0]]
else
align = ALIGN_MAP[t]
end
offset = PackInfo.align(orig_offset, align)
size = offset - orig_offset
@offset[i] = offset
if( t.is_a?(Array) )
offset += (SIZE_MAP[t[0]] * t[1])
else
offset += SIZE_MAP[t]
end
if (max_align < align)
max_align = align
end
}
offset = PackInfo.align(offset, max_align)
@size = offset
end
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| CPtr.new(v)}
end
when TYPE_VOIDP
val = CPtr.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
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()
super(@size)
end
end
class CUnionEntity < CStructEntity
include PackInfo
def CUnionEntity.malloc(types, func=nil)
addr = DL.malloc(CUnionEntity.size(types))
CUnionEntity.new(addr, types, func)
end
def CUnionEntity.size(types)
size = 0
types.each_with_index{|t,i|
if( t.is_a?(Array) )
tsize = PackInfo::SIZE_MAP[t[0]] * t[1]
else
tsize = PackInfo::SIZE_MAP[t]
end
if( tsize > size )
size = tsize
end
}
end
def set_ctypes(types)
@ctypes = types
@offset = []
@size = 0
types.each_with_index{|t,i|
@offset[i] = 0
if( t.is_a?(Array) )
size = SIZE_MAP[t[0]] * t[1]
else
size = SIZE_MAP[t]
end
if( size > @size )
@size = size
end
}
end
end
end
module DL
module Win32Types
def included(m)
m.module_eval{
typealias "DWORD", "unsigned long"
typealias "PDWORD", "unsigned 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", "unsigned long"
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
module BasicTypes
def included(m)
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
#require 'dl'
#require 'dl/callback'
#require 'dl/stack'
#require 'dl/value'
#require 'thread'
module DL
class Function
include DL
include ValueUtil
def initialize(cfunc, argtypes, &proc)
@cfunc = cfunc
@stack = Stack.new(argtypes.collect{|ty| ty.abs})
if( @cfunc.ctype < 0 )
@cfunc.ctype = @cfunc.ctype.abs
@unsigned = true
end
if( proc )
bind(&proc)
end
end
def to_i()
@cfunc.to_i
end
def check_safe_obj(val)
if $SAFE > 0 and val.tainted?
raise SecurityError, 'Insecure operation'
end
end
def call(*args, &block)
funcs = []
args.each{|e| check_safe_obj(e) }
check_safe_obj(block)
args = wrap_args(args, @stack.types, funcs, &block)
r = @cfunc.call(@stack.pack(args))
funcs.each{|f| f.unbind_at_call()}
return wrap_result(r)
end
def wrap_result(r)
case @cfunc.ctype
when TYPE_VOIDP
r = CPtr.new(r)
else
if( @unsigned )
r = unsigned_value(r, @cfunc.ctype)
end
end
r
end
def bind(&block)
if( !block )
raise(RuntimeError, "block must be given.")
end
if( @cfunc.ptr == 0 )
cb = Proc.new{|*args|
ary = @stack.unpack(args)
@stack.types.each_with_index{|ty, idx|
case ty
when TYPE_VOIDP
ary[idx] = CPtr.new(ary[idx])
end
}
r = block.call(*ary)
wrap_arg(r, @cfunc.ctype, [])
}
case @cfunc.calltype
when :cdecl
@cfunc.ptr = set_cdecl_callback(@cfunc.ctype, @stack.size, &cb)
when :stdcall
@cfunc.ptr = set_stdcall_callback(@cfunc.ctype, @stack.size, &cb)
else
raise(RuntimeError, "unsupported calltype: #{@cfunc.calltype}")
end
if( @cfunc.ptr == 0 )
raise(RuntimeException, "can't bind C function.")
end
end
end
def unbind()
if( @cfunc.ptr != 0 )
case @cfunc.calltype
when :cdecl
remove_cdecl_callback(@cfunc.ptr, @cfunc.ctype)
when :stdcall
remove_stdcall_callback(@cfunc.ptr, @cfunc.ctype)
else
raise(RuntimeError, "unsupported calltype: #{@cfunc.calltype}")
end
@cfunc.ptr = 0
end
end
def bound?()
@cfunc.ptr != 0
end
def bind_at_call(&block)
bind(&block)
end
def unbind_at_call()
end
end
class TempFunction < Function
def bind_at_call(&block)
bind(&block)
end
def unbind_at_call()
unbind()
end
end
class CarriedFunction < Function
def initialize(cfunc, argtypes, n)
super(cfunc, argtypes)
@carrier = []
@index = n
@mutex = Mutex.new
end
def create_carrier(data)
ary = []
userdata = [ary, data]
@mutex.lock()
@carrier.push(userdata)
return dlwrap(userdata)
end
def bind_at_call(&block)
userdata = @carrier[-1]
userdata[0].push(block)
bind{|*args|
ptr = args[@index]
if( !ptr )
raise(RuntimeError, "The index of userdata should be lower than #{args.size}.")
end
userdata = dlunwrap(Integer(ptr))
args[@index] = userdata[1]
userdata[0][0].call(*args)
}
@mutex.unlock()
end
end
end
#require 'dl'
#require 'dl/func.rb'
#require 'dl/struct.rb'
#require 'dl/cparser.rb'
module DL
class CompositeHandler
def initialize(handlers)
@handlers = handlers
end
def handlers()
@handlers
end
def sym(symbol)
@handlers.each{|handle|
if( handle )
begin
addr = handle.sym(symbol)
return addr
rescue DLError
end
end
}
return nil
end
def [](symbol)
sym(symbol)
end
end
module Importer
include DL
include CParser
extend Importer
def dlload(*libs)
handles = libs.collect{|lib|
case lib
when nil
nil
when Handle
lib
when Importer
lib.handlers
else
begin
DL.dlopen(lib)
rescue DLError
raise(DLError, "can't load #{lib}")
end
end
}.flatten()
@handler = CompositeHandler.new(handles)
@func_map = {}
@type_alias = {}
end
def typealias(alias_type, orig_type)
@type_alias[alias_type] = orig_type
end
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_LON
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 CPtr[ty].size()
end
def parse_bind_options(opts)
h = {}
prekey = nil
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
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)}
module_eval(<<-EOS)
def #{name}(*args, &block)
@func_map['#{name}'].call(*args,&block)
end
EOS
module_function(name)
f
end
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)
when :temp, :temporal
f = create_temp_function(name, ctype, argtype, h[:call_type])
when :carried
f = create_carried_function(name, ctype, argtype, h[:call_type], h[:carrier])
else
raise(RuntimeError, "unknown callback type: #{h[:callback_type]}")
end
@func_map[name] = f
#define_method(name){|*args,&block| f.call(*args,&block)}
module_eval(<<-EOS)
def #{name}(*args,&block)
@func_map['#{name}'].call(*args,&block)
end
EOS
module_function(name)
f
end
def struct(signature)
tys, mems = parse_struct_signature(signature, @type_alias)
DL::CStructBuilder.create(CStruct, tys, mems)
end
def union(signature)
tys, mems = parse_struct_signature(signature, @type_alias)
DL::CStructBuilder.create(CUnion, tys, mems)
end
def [](name)
@func_map[name]
end
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
def import_value(ty, addr)
s = struct([ty + " value"])
ptr = s.new(addr)
return ptr
end
def import_symbol(name)
addr = @handler.sym(name)
if( !addr )
raise(DLError, "cannot find the symbol: #{name}")
end
CPtr.new(addr)
end
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(CFunc.new(addr, ctype, name, call_type || :cdecl), argtype)
end
def bind_function(name, ctype, argtype, call_type = nil, &block)
f = Function.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype)
f.bind(&block)
f
end
def create_temp_function(name, ctype, argtype, call_type = nil)
TempFunction.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype)
end
def create_carried_function(name, ctype, argtype, call_type = nil, n = 0)
CarriedFunction.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype, n)
end
end
end
Nessa grande código há 10 scripts diferentes, o thread.rb foi criado por Yukihiro Matsumoto (criador do Ruby e é licenciado pela Ruby License (Compatível com GPL))
Os outros não possuem header com os autores, apenas tem algumas RCS IDs com nomes que prefiro não citar aqui, mas de qualquer mode estão sob a Ruby License do mesmo modo.
E por fim, as funções de socket:
- Código:
#--------------------
#require "dl"
#require "dl/import"
#require "dl/struct"
#--------------------
# Created by Cidiomar
#--------------------
module DL::Importer
#----------
def typedef(string)
args = string.split(' ')
alias_type = args.pop
typealias(alias_type, args.join(' '))
end
#-----
module_function :typedef
#----------
end
#---------------
module CIDI_DK
#---------------
Timeval = DL::Importer.struct [
"long tv_sec",
"long tv_usec",
]
#---------------
module Winsock2
#----------
extend DL::Importer
#----------
dlload 'ws2_32.dll'
#----------
SOCK_STREAM = 1
AF_INET = 2
IPPROTO_TCP = 6
INADDR_NONE = 0xffffffff
#----------
typedef 'unsigned char u_char'
typedef 'unsigned short u_short'
typedef 'unsigned int u_int'
typedef 'unsigned long u_long'
#-----
typedef 'u_int SOCKET'
#----------
Fd_set = struct [
'u_int fd_count',
'SOCKET fd_array[2]'
]
#----------
Fd_set1 = struct [
'u_int fd_count',
'SOCKET fd_array'
]
#----------
Sockaddr = struct [
'u_short sa_family',
'char sa_data[14]'
]
#----------
Sockaddr2 = struct [
'u_short sa_family',
'u_short s_port',
'u_long s_host',
'char sa_data[8]'
]
#----------
Hostent = struct [
'char * h_name',
'char ** h_aliases',
'short h_addrtype',
'short h_length',
'char ** h_addr_list',
]
#----------
extern 'int closesocket(SOCKET)'
extern 'int connect(SOCKET,const struct sockaddr*,int)'
extern 'hostent * gethostbyname(const char*)'
extern 'int recv(SOCKET,char*,int,int)'
extern 'int send(SOCKET,const char*,int,int)'
extern 'SOCKET socket(int,int,int)'
extern 'int select(int,fd_set*,fd_set*,fd_set*,const struct timeval*)'
extern 'int WSAGetLastError()'
#----------
end
#---------------
class TCPSocket
#----------
attr_reader :host, :port
#----------
def initialize(host, port)
@host, @port = host, port
@recv_back_buffer = ""
connect
end
#----------
def connect
#-----
@sock_io_id = Winsock2.socket(Winsock2::AF_INET, Winsock2::SOCK_STREAM, Winsock2::IPPROTO_TCP)
#-----
unless (_host = Winsock2.gethostbyname(@host)).null?
hostent = Winsock2::Hostent.new(Winsock2.gethostbyname(@host))
else
SocketError.raise_no_assoc_host
end
#-----
zeros = i = 0
ip_addr = []
loop do
now = hostent.h_addr_list[i]
if zeros == 5
now += 256 if now < 0
ip_addr << now
break if ip_addr.size == 4
elsif now == 0
zeros += 1
else
zeros = 0
end
i += 1
end
#-----
sock_addr = Winsock2::Sockaddr2.malloc
sock_addr.sa_family = Winsock2::AF_INET
sock_addr.s_port = [@port].pack("n").unpack("S")[0]
sock_addr.s_host = ip_addr.pack("C4").unpack("L")[0]
#-----
if Winsock2.connect(@sock_io_id, sock_addr, Winsock2::Sockaddr2.size) == -1 or sock_addr.s_host == 0
SocketError.raise
end
#-----
nil
end
#----------
def close
SocketError.raise if Winsock2.closesocket(@sock_io_id) == -1
nil
end
#----------
def recv(len)
if @recv_back_buffer.size != 0
if @recv_back_buffer.size > len
buffer = @recv_back_buffer[0 ... len]
@recv_back_buffer[0 ... len] = ''
elsif @recv_back_buffer.size == len
buffer = @recv_back_buffer
@recv_back_buffer = ""
else
len -= @recv_back_buffer.size
buffer = "\0" * len
SocketError.raise if (recv_size = Winsock2.recv(@sock_io_id, buffer, len, 0)) == -1
buffer = @recv_back_buffer + buffer[0 ... recv_size]
@recv_back_buffer = ""
end
else
buffer = "\0" * len
SocketError.raise if (recv_size = Winsock2.recv(@sock_io_id, buffer, len, 0)) == -1
buffer = buffer[0 ... recv_size]
end
return buffer
end
#----------
def gets(end_sequence = "\n")
end_sequence_size = end_sequence.size
return nil if end_sequence_size == 0
ret_buffer = ""
buffer = "\0"
result = -1
#-----
while not ret_buffer[ret_buffer.size - end_sequence_size ... ret_buffer.size] == end_sequence
if @recv_back_buffer.size != 0
buffer = @recv_back_buffer[0]
@recv_back_buffer[0] = ''
else
SocketError.raise if (result = Winsock2.recv(@sock_io_id, buffer, 1, 0)) == -1
end
ret_buffer << buffer unless result == 0
end
#-----
return ret_buffer
end
#----------
def send(*args)
string = args.join
SocketError.raise if Winsock2.send(@sock_io_id, string, string.size, 0) == -1
string
end
#----------
def eof?
#-----
timeval = Timeval.malloc
timeval.tv_sec = 0
timeval.tv_usec = 0
#-----
readfds = Winsock2::Fd_set1.malloc
readfds.fd_count = 1
readfds.fd_array = @sock_io_id
#-----
result = Winsock2.select(2, readfds, 0, 0, timeval)
#-----
if result == 1
buffer = "\0"
if Winsock2.recv(@sock_io_id, buffer, 1, 0) == 0
result = 0
else
@recv_back_buffer << buffer
end
end
#-----
SocketError.raise if result == -1
return !(result > 0)
end
#----------
protected :connect
#----------
end
#---------------
class SocketError < StandardError
#----------
ENOASSOCHOST = 'getaddrinfo: no address associated with hostname.'
#----------
def self.raise(errno = Winsock2.WSAGetLastError)
_caller = caller()
_caller.pop
Kernel.raise Errno.const_get(Errno.constants.detect { |c| Errno.const_get(c).new.errno == errno }), '', _caller
end
#----------
def self.raise_no_assoc_host
_caller = caller()
_caller.pop
Kernel.raise 'getaddrinfo: no address associated with hostname.', '', _caller
end
#----------
end
end
#--------------------
TCPSocket = CIDI_DK::TCPSocket
Pergunta: Por que não usar Win32API ao invés de dl?o código ficaria 36.5KB mais leve!
Resposta: Prefiro usar dl, Win32API não deve mais ser usada, esta em ativa apenas por compatibilidade, será removida do Ruby em breve.