Class: Snow::CStruct::Builder
- Inherits:
-
Object
- Object
- Snow::CStruct::Builder
- Defined in:
- lib/snow-data/c_struct/builder.rb
Overview
Utility classes used by CStruct to create member info structs. Exposed to the user via instance_exec blocks for declaring struct/union members.
See CStruct::new, CStruct::struct, or CStruct::union.
Defined Under Namespace
Classes: MemberStackLevel
Constant Summary collapse
- @@defined_types =
A Set of symbols for all types whose declaration methods have already been defined by ::flush_type_methods!.
Set.new
Class Method Summary collapse
-
.adjust_level(level, start_at: 0) ⇒ Object
Iterates over a level’s members, including sub-levels, and adjusts their offsets and the level’s size accordingly.
-
.flatten_level(level, info_buffer = []) ⇒ Object
Flattens a MemberStackLevel’s members into a single array and returns an array of StructMemberInfo objects.
-
.flush_type_methods! ⇒ Object
call-seq: flush_type_methods! => self.
Instance Method Summary collapse
-
#__do_level__(align: nil, is_union: false, &block) ⇒ Object
Creates a new member level for the builder and instance_exec-s the block, then passes its alignment onto its ancestor level.
-
#initialize(is_union: false, &block) ⇒ Builder
constructor
call-seq: new => Builder new { … } => Builder.
-
#member_info ⇒ Object
call-seq: member_info => [StructMemberInfo].
-
#struct(align: nil, &block) ⇒ Object
call-seq: struct { … } struct(align: nil) { … }.
-
#union(align: nil, &block) ⇒ Object
call-seq: union { … } union(align: nil) { … }.
Constructor Details
#initialize(is_union: false, &block) ⇒ Builder
call-seq:
new => Builder
new { ... } => Builder
In either form, a new Builder is allocated and returned. If a block is given, then it will be instance_exec’d, allowing you to easily call any declaration methods on the builder instance.
42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/snow-data/c_struct/builder.rb', line 42 def initialize(is_union: false, &block) @member_names = Set.new @level = MemberStackLevel[!!is_union, 0, 1, 0, []] instance_exec(&block) if block_given? @members = [] self.class.adjust_level(@level) @level.members.each { |member| member.freeze } @members = self.class.flatten_level(@level) @members.each(&:freeze) @members.freeze end |
Class Method Details
.adjust_level(level, start_at: 0) ⇒ Object
Iterates over a level’s members, including sub-levels, and adjusts their offsets and the level’s size accordingly. This is essentially the processing phase done to give members their offsets and levels their sizes.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/snow-data/c_struct/builder.rb', line 76 def self.adjust_level(level, start_at: 0) base_offset = offset = ::Snow::Memory.align_size(start_at, level.alignment) level.size = 0 level.members.each do |m| m.offset = offset = ::Snow::Memory.align_size(offset, m.alignment) if m.kind_of?(MemberStackLevel) adjust_level(m, start_at: offset) end if level.is_union level.size = [level.size, (m.offset + m.size) - base_offset].max else level.size = (m.offset + m.size) - base_offset offset += m.size end end end |
.flatten_level(level, info_buffer = []) ⇒ Object
Flattens a MemberStackLevel’s members into a single array and returns an array of StructMemberInfo objects.
59 60 61 62 63 64 65 66 67 68 |
# File 'lib/snow-data/c_struct/builder.rb', line 59 def self.flatten_level(level, info_buffer = []) level.members.each { |m| if m.kind_of?(MemberStackLevel) flatten_level(m, info_buffer) else info_buffer.push(m) end } info_buffer end |
.flush_type_methods! ⇒ Object
call-seq:
flush_type_methods! => self
Defines methods for declaring members of any recognized CStruct type, including aliases.
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/snow-data/c_struct/builder.rb', line 112 def self.flush_type_methods! ::Snow::CStruct::SIZES.each do |type_name, type_size| next if @@defined_types.include?(type_name) method_name = type_name.to_s first_char = method_name[0] case when first_char >= ?A && first_char <= ?Z method_name[0] = first_char.downcase when first_char >= ?0 && first_char <= ?9 method_name[0] = "_#{first_char}" end method_name = method_name.to_sym __send__(:define_method, method_name) do | name, lengths = 1, align: nil | level = instance_variable_get(:@level) member_names = instance_variable_get(:@member_names) name = name.to_sym raise ArgumentError, "#{name} redefined in struct" if member_names.include?(name) member_names.add(name) length = case lengths when Integer, Fixnum then lengths when Array then lengths.reduce(:*) else lengths.to_i end raise "Invalid length for member #{name}: must be >= 1" if length < 1 size = length * type_size align = (align || ::Snow::CStruct::ALIGNMENTS[type_name]).to_i raise "Nil alignment for type #{type_name}" if align.nil? base_offset = level.offset offset = ::Snow::Memory.align_size(base_offset, align) level.offset = offset + size unless level.is_union member_info = ::Snow::CStruct::StructMemberInfo[ name, type_name, size, length, align, offset] level.alignment = [level.alignment, align].max if level.is_union level.size = [level.size, size].max else level.size += (offset - base_offset) + size end level.members.push(member_info) end # define_method(type_name) @@defined_types.add(type_name) end # SIZES.each ::Snow::CStruct::TYPE_ALIASES.each { |short, long| __send__(:alias_method, short, long) } self end |
Instance Method Details
#__do_level__(align: nil, is_union: false, &block) ⇒ Object
Creates a new member level for the builder and instance_exec-s the block, then passes its alignment onto its ancestor level.
180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/snow-data/c_struct/builder.rb', line 180 def __do_level__(align: nil, is_union: false, &block) parent = @level next_level = MemberStackLevel[!!is_union, 0, 1, 0, []] @level = next_level self.instance_exec(&block) next_level.alignment = align || next_level.alignment parent.alignment = [parent.alignment, next_level.alignment].max @level = parent parent.members.push(next_level) end |
#member_info ⇒ Object
100 101 102 |
# File 'lib/snow-data/c_struct/builder.rb', line 100 def member_info @members end |
#struct(align: nil, &block) ⇒ Object
call-seq:
struct { ... }
struct(align: nil) { ... }
For the scope of the block, any members declared are considered struct members, as opposed to union members.
Each member of a struct occupies its own space inside the struct, unlike a union (where each member occupies either the same or adjacent space in the union).
Unless an alignment is specified, the default alignment of a struct is that of the largest alignment of all its members. If specifying an alignment, keep in mind that the members of the struct may need to also be manually aligned, otherwise the first member may be preceeded by padding bytes regardless of the start of the struct. See #union for more information on alignment oddities.
240 241 242 |
# File 'lib/snow-data/c_struct/builder.rb', line 240 def struct(align: nil, &block) __do_level__(align: align, is_union: false, &block) end |
#union(align: nil, &block) ⇒ Object
call-seq:
union { ... }
union(align: nil) { ... }
For the scope of the block, any members declared are considered union members, as opposed to struct members. Each member of a union occupies the same space as other members of the union, though their offsets may differ if their alignments differ as well.
If no alignment is specified for the union, its base offset will be aligned to that of the member with the largest alignment. Otherwise, if an alignment is specified, members may not occupy the same offsets relative to the beginning of the union.
For example, if a union with an alignment of 4 has uint32_t and uint64_t members with default alignments with a starting offset of 4, the uint32_t member will be located at offset 4, while the uint64_t member will be at offset 8. As such, it’s best to leave union alignments at their default unless absolutely necessary.
216 217 218 |
# File 'lib/snow-data/c_struct/builder.rb', line 216 def union(align: nil, &block) __do_level__(align: align, is_union: true, &block) end |