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