class FFI::StructLayoutBuilder
def add(name, type, offset = nil)
def add(name, type, offset = nil) if offset.nil? || offset == -1 offset = @union ? 0 : align(@size, @packed ? [ @packed, type.alignment ].min : [ @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 = type.is_a?(StructLayout::Field) ? type : field_for_type(name, offset, type) @fields << field @alignment = [ @alignment, field.alignment ].max unless @packed @size = [ @size, field.size + (@union ? 0 : field.offset) ].max return self end
def add_array(name, type, count, offset = nil)
def add_array(name, type, count, offset = nil) add(name, Type::Array.new(type, count), offset) end
def add_field(name, type, offset = nil)
def add_field(name, type, offset = nil) add(name, type, offset) end
def add_struct(name, type, offset = nil)
def add_struct(name, type, offset = nil) add(name, Type::Struct.new(type), offset) end
def align(offset, align)
def align(offset, align) align + ((offset - 1) & ~(align - 1)); end
def alignment=(align)
def alignment=(align) @alignment = align if align > @alignment @min_alignment = align end
def build
def build # Add tail padding if the struct is not packed size = @packed ? @size : align(@size, @alignment) StructLayout.new(@fields, size, @alignment) end
def field_for_type(name, offset, type)
def field_for_type(name, offset, type) field_class = case when type.is_a?(Type::Function) StructLayout::Function when type.is_a?(Type::Struct) StructLayout::InnerStruct when type.is_a?(Type::Array) StructLayout::Array when type.is_a?(FFI::Enum) StructLayout::Enum when NUMBER_TYPES.include?(type) StructLayout::Number when type == Type::POINTER StructLayout::Pointer when type == Type::STRING StructLayout::String when type.is_a?(Class) && type < StructLayout::Field type when type.is_a?(DataConverter) return StructLayout::Mapped.new(name, offset, Type::Mapped.new(type), field_for_type(name, offset, type.native_type)) when type.is_a?(Type::Mapped) return StructLayout::Mapped.new(name, offset, type, field_for_type(name, offset, type.native_type)) else raise TypeError, "invalid struct field type #{type.inspect}" end field_class.new(name, offset, type) end
def initialize
def initialize @size = 0 @alignment = 1 @min_alignment = 1 @packed = false @union = false @fields = Array.new end
def packed=(packed)
def packed=(packed) if packed.is_a?(Fixnum) @alignment = packed @packed = packed else @packed = packed ? 1 : 0 end end
def size=(size)
def size=(size) @size = size if size > @size end
def union=(is_union)
def union=(is_union) @union = is_union end
def union?
def union? @union end