## Copyright (C) 2008-2010 Wayne Meissner# Copyright (C) 2008, 2009 Andrea Fazzi# Copyright (C) 2008, 2009 Luc Heinrich## All rights reserved.## 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 <http://www.gnu.org/licenses/>.#require'ffi/platform'require'ffi/struct_layout_builder'moduleFFIclassStructLayoutdefoffsetsmembers.map{|m|[m,self[m].offset]}enddefoffset_of(field_name)self[field_name].offsetendclassEnum<Fielddefget(ptr)type.find(ptr.get_int(offset))enddefput(ptr,value)ptr.put_int(offset,type.find(value))endendclassInnerStruct<Fielddefget(ptr)type.struct_class.new(ptr.slice(self.offset,self.size))end# def put(ptr, value)# raise TypeError, "wrong value type (expected #{type.struct_class}" unless value.is_a(type.struct_class)# endendclassMapped<Fielddefinitialize(name,offset,type,orig_field)super(name,offset,type)@orig_field=orig_fieldenddefget(ptr)type.from_native(@orig_field.get(ptr),nil)enddefput(ptr,value)@orig_field.put(ptr,type.to_native(value,nil))endendendclassStructdefsizeself.class.sizeenddefalignmentself.class.alignmentendalias_method:align,:alignmentdefoffset_of(name)self.class.offset_of(name)enddefmembersself.class.membersenddefvaluesmembers.map{|m|self[m]}enddefoffsetsself.class.offsetsenddefclearpointer.clearselfenddefto_ptrpointerenddefself.sizedefined?(@layout)?@layout.size:defined?(@size)?@size:0enddefself.size=(size)raiseArgumentError,"Size already set"ifdefined?(@size)||defined?(@layout)@size=sizeenddefself.alignment@layout.alignmentenddefself.members@layout.membersenddefself.offsets@layout.offsetsenddefself.offset_of(name)@layout.offset_of(name)enddefself.inptr(:in)enddefself.outptr(:out)enddefself.ptr(flags=:inout)@ref_data_type||=Type::Mapped.new(StructByReference.new(self))enddefself.val@val_data_type||=StructByValue.new(self)enddefself.by_valueself.valenddefself.by_ref(flags=:inout)self.ptr(flags)endclassManagedStructConverter<StructByReferencedefinitialize(struct_class)super(struct_class)raiseNoMethodError,"release() not implemented for class #{struct_class}"unlessstruct_class.respond_to?:release@method=struct_class.method(:release)enddeffrom_native(ptr,ctx)struct_class.new(AutoPointer.new(ptr,@method))endenddefself.auto_ptr@managed_type||=Type::Mapped.new(ManagedStructConverter.new(self))endclass<<selfpublicdeflayout(*spec)# raise RuntimeError, "struct layout already defined for #{self.inspect}" if defined?(@layout)return@layoutifspec.size==0builder=StructLayoutBuilder.newbuilder.union=self<Unionbuilder.packed=@packedifdefined?(@packed)builder.alignment=@min_alignmentifdefined?(@min_alignment)ifspec[0].kind_of?(Hash)hash_layout(builder,spec)elsearray_layout(builder,spec)endbuilder.size=@sizeifdefined?(@size)&&@size>builder.sizecspec=builder.build@layout=cspecunlessself==Struct@size=cspec.sizereturncspecendprotecteddefcallback(params,ret)mod=enclosing_moduleFFI::CallbackInfo.new(find_type(ret,mod),params.map{|e|find_type(e,mod)})enddefpacked(packed=1)@packed=packedendalias:pack:packeddefaligned(alignment=1)@min_alignment=alignmentendalias:align:aligneddefenclosing_modulebeginmod=self.name.split("::")[0..-2].inject(Object){|obj,c|obj.const_get(c)}mod.respond_to?(:find_type)?mod:nilrescueExceptionnilendenddeffind_field_type(type,mod=enclosing_module)iftype.kind_of?(Class)&&type<StructFFI::Type::Struct.new(type)elsiftype.kind_of?(Class)&&type<FFI::StructLayout::Fieldtypeelsiftype.kind_of?(::Array)FFI::Type::Array.new(find_field_type(type[0]),type[1])elsefind_type(type,mod)endenddeffind_type(type,mod=enclosing_module)ifmodmod.find_type(type)end||FFI.find_type(type)endprivatedefhash_layout(builder,spec)raise"Ruby version not supported"ifRUBY_VERSION=~/1.8.*/spec[0].eachdo|name,type|builder.addname,find_field_type(type),nilendenddefarray_layout(builder,spec)i=0whilei<spec.sizename,type=spec[i,2]i+=2# If the next param is a Integer, it specifies the offsetifspec[i].kind_of?(Integer)offset=spec[i]i+=1elseoffset=nilendbuilder.addname,find_field_type(type),offsetendendendendend