1require 'dl' 2require 'dl/value' 3require 'dl/pack.rb' 4 5module DL 6 # C struct shell 7 class CStruct 8 # accessor to DL::CStructEntity 9 def CStruct.entity_class() 10 CStructEntity 11 end 12 end 13 14 # C union shell 15 class CUnion 16 # accessor to DL::CUnionEntity 17 def CUnion.entity_class() 18 CUnionEntity 19 end 20 end 21 22 # Used to construct C classes (CUnion, CStruct, etc) 23 # 24 # DL::Importer#struct and DL::Importer#union wrap this functionality in an 25 # easy-to-use manner. 26 module CStructBuilder 27 # Construct a new class given a C: 28 # * class +klass+ (CUnion, CStruct, or other that provide an 29 # #entity_class) 30 # * +types+ (DL:TYPE_INT, DL::TYPE_SIZE_T, etc., see the C types 31 # constants) 32 # * corresponding +members+ 33 # 34 # DL::Importer#struct and DL::Importer#union wrap this functionality in an 35 # easy-to-use manner. 36 # 37 # Example: 38 # 39 # require 'dl/struct' 40 # require 'dl/cparser' 41 # 42 # include DL::CParser 43 # 44 # types, members = parse_struct_signature(['int i','char c']) 45 # 46 # MyStruct = DL::CStructBuilder.create(CUnion, types, members) 47 # 48 # obj = MyStruct.allocate 49 # 50 def create(klass, types, members) 51 new_class = Class.new(klass){ 52 define_method(:initialize){|addr| 53 @entity = klass.entity_class.new(addr, types) 54 @entity.assign_names(members) 55 } 56 define_method(:to_ptr){ @entity } 57 define_method(:to_i){ @entity.to_i } 58 members.each{|name| 59 define_method(name){ @entity[name] } 60 define_method(name + "="){|val| @entity[name] = val } 61 } 62 } 63 size = klass.entity_class.size(types) 64 new_class.module_eval(<<-EOS, __FILE__, __LINE__+1) 65 def new_class.size() 66 #{size} 67 end 68 def new_class.malloc() 69 addr = DL.malloc(#{size}) 70 new(addr) 71 end 72 EOS 73 return new_class 74 end 75 module_function :create 76 end 77 78 # A C struct wrapper 79 class CStructEntity < (DL.fiddle? ? Fiddle::Pointer : CPtr) 80 include PackInfo 81 include ValueUtil 82 83 # Allocates a C struct the +types+ provided. The C function +func+ is 84 # called when the instance is garbage collected. 85 def CStructEntity.malloc(types, func = nil) 86 addr = DL.malloc(CStructEntity.size(types)) 87 CStructEntity.new(addr, types, func) 88 end 89 90 # Given +types+, returns the offset for the packed sizes of those types 91 # 92 # DL::CStructEntity.size([DL::TYPE_DOUBLE, DL::TYPE_INT, DL::TYPE_CHAR, 93 # DL::TYPE_VOIDP]) 94 # => 24 95 def CStructEntity.size(types) 96 offset = 0 97 98 max_align = types.map { |type, count = 1| 99 last_offset = offset 100 101 align = PackInfo::ALIGN_MAP[type] 102 offset = PackInfo.align(last_offset, align) + 103 (PackInfo::SIZE_MAP[type] * count) 104 105 align 106 }.max 107 108 PackInfo.align(offset, max_align) 109 end 110 111 # Wraps the C pointer +addr+ as a C struct with the given +types+. The C 112 # function +func+ is called when the instance is garbage collected. 113 # 114 # See also DL::CPtr.new 115 def initialize(addr, types, func = nil) 116 set_ctypes(types) 117 super(addr, @size, func) 118 end 119 120 # Set the names of the +members+ in this C struct 121 def assign_names(members) 122 @members = members 123 end 124 125 # Given +types+, calculate the offsets and sizes for the types in the 126 # struct. 127 def set_ctypes(types) 128 @ctypes = types 129 @offset = [] 130 offset = 0 131 132 max_align = types.map { |type, count = 1| 133 orig_offset = offset 134 align = ALIGN_MAP[type] 135 offset = PackInfo.align(orig_offset, align) 136 137 @offset << offset 138 139 offset += (SIZE_MAP[type] * count) 140 141 align 142 }.max 143 144 @size = PackInfo.align(offset, max_align) 145 end 146 147 # Fetch struct member +name+ 148 def [](name) 149 idx = @members.index(name) 150 if( idx.nil? ) 151 raise(ArgumentError, "no such member: #{name}") 152 end 153 ty = @ctypes[idx] 154 if( ty.is_a?(Array) ) 155 r = super(@offset[idx], SIZE_MAP[ty[0]] * ty[1]) 156 else 157 r = super(@offset[idx], SIZE_MAP[ty.abs]) 158 end 159 packer = Packer.new([ty]) 160 val = packer.unpack([r]) 161 case ty 162 when Array 163 case ty[0] 164 when TYPE_VOIDP 165 val = val.collect{|v| CPtr.new(v)} 166 end 167 when TYPE_VOIDP 168 val = CPtr.new(val[0]) 169 else 170 val = val[0] 171 end 172 if( ty.is_a?(Integer) && (ty < 0) ) 173 return unsigned_value(val, ty) 174 elsif( ty.is_a?(Array) && (ty[0] < 0) ) 175 return val.collect{|v| unsigned_value(v,ty[0])} 176 else 177 return val 178 end 179 end 180 181 # Set struct member +name+, to value +val+ 182 def []=(name, val) 183 idx = @members.index(name) 184 if( idx.nil? ) 185 raise(ArgumentError, "no such member: #{name}") 186 end 187 ty = @ctypes[idx] 188 packer = Packer.new([ty]) 189 val = wrap_arg(val, ty, []) 190 buff = packer.pack([val].flatten()) 191 super(@offset[idx], buff.size, buff) 192 if( ty.is_a?(Integer) && (ty < 0) ) 193 return unsigned_value(val, ty) 194 elsif( ty.is_a?(Array) && (ty[0] < 0) ) 195 return val.collect{|v| unsigned_value(v,ty[0])} 196 else 197 return val 198 end 199 end 200 201 def to_s() # :nodoc: 202 super(@size) 203 end 204 end 205 206 # A C union wrapper 207 class CUnionEntity < CStructEntity 208 include PackInfo 209 210 # Allocates a C union the +types+ provided. The C function +func+ is 211 # called when the instance is garbage collected. 212 def CUnionEntity.malloc(types, func=nil) 213 addr = DL.malloc(CUnionEntity.size(types)) 214 CUnionEntity.new(addr, types, func) 215 end 216 217 # Given +types+, returns the size needed for the union. 218 # 219 # DL::CUnionEntity.size([DL::TYPE_DOUBLE, DL::TYPE_INT, DL::TYPE_CHAR, 220 # DL::TYPE_VOIDP]) 221 # => 8 222 def CUnionEntity.size(types) 223 types.map { |type, count = 1| 224 PackInfo::SIZE_MAP[type] * count 225 }.max 226 end 227 228 # Given +types+, calculate the necessary offset and for each union member 229 def set_ctypes(types) 230 @ctypes = types 231 @offset = Array.new(types.length, 0) 232 @size = self.class.size types 233 end 234 end 235end 236 237