diff options
author | Wayne Meissner <wmeissner@gmail.com> | 2010-04-16 22:59:25 +1000 |
---|---|---|
committer | Wayne Meissner <wmeissner@gmail.com> | 2010-04-16 22:59:25 +1000 |
commit | 41cd6343ceb19640c7f619bd1e1a145ef6ddc517 (patch) | |
tree | 105e8e895e76667db6fae837f5d4070bf385decd /lib/ffi/struct_layout_builder.rb | |
parent | 82a403052040459d0c3923aacfa8fe6e2cc116bd (diff) | |
download | ffi-41cd6343ceb19640c7f619bd1e1a145ef6ddc517.tar.gz |
Convert StructLayoutBuilder to ruby, and allow packing to take an optional packing boundary
Diffstat (limited to 'lib/ffi/struct_layout_builder.rb')
-rw-r--r-- | lib/ffi/struct_layout_builder.rb | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/lib/ffi/struct_layout_builder.rb b/lib/ffi/struct_layout_builder.rb new file mode 100644 index 0000000..f6e7b14 --- /dev/null +++ b/lib/ffi/struct_layout_builder.rb @@ -0,0 +1,113 @@ + +module FFI + class StructLayoutBuilder + attr_reader :size, :alignment + + def initialize + @size = 0 + @alignment = 1 + @min_alignment = 1 + @packed = false + @union = false + @field_names = Array.new + @field_map = Hash.new + end + + def size=(size) + @size = size if size > @size + end + + def alignment=(align) + @alignment = align if align > @alignment + @min_alignment = align + end + + def union=(is_union) + @union = is_union + end + + def union? + @union + end + + def packed=(packed) + if packed.is_a?(Fixnum) + @alignment = packed + @packed = packed + else + @packed = packed ? 1 : 0 + end + end + + + def add_field(name, type, offset = nil) + + if offset.nil? || offset == -1 + offset = @union ? 0 : align(@size, @packed ? @packed : [ @min_alignment, type.alignment ].max) + end + + # + # If a FFI::Type type was passed in as the field arg, try and convert to a StructLayout::Field instance + # + field = if !type.is_a?(StructLayout::Field) + + field_class = if type.is_a?(FFI::Type::Function) + StructLayout::Function + + elsif type.is_a?(FFI::Type::Struct) + StructLayout::InlineStruct + + elsif type.is_a?(FFI::Type::Array) + StructLayout::Array + + elsif type.is_a?(FFI::Enum) + StructLayout::Enum + + elsif type.is_a?(FFI::Type) + StructLayout::Field + else + raise TypeError, "invalid struct field type #{type.inspect}" unless field_class + end + + field_class.new(name, offset, type) + + else + type + end + + store_field(name, field, type) + + end + + def add_struct(name, type, offset = nil) + add_field(name, FFI::Type::Struct.new(type), offset) + end + + def add_array(name, type, count, offset = nil) + add_field(name, FFI::Type::Array.new(type, count), offset) + end + + def build + # Add tail padding if the struct is not packed + size = @packed ? @size : align(@size, @alignment) + + FFI::StructLayout.new(@field_names, @field_map, size, @alignment) + end + + private + + def align(offset, align) + align + ((offset - 1) & ~(align - 1)); + end + + + def store_field(name, field, type) + @field_names << name + @field_map[name] = field + @alignment = @packed ? @packed : [ @alignment, type.alignment ].max + @size = [ @size, type.size + (@union ? 0 : field.offset) ].max + end + + end + +end
\ No newline at end of file |