Class: Ruckus::Structure

Inherits:
Parsel show all
Includes:
StructureAllowFieldReplacement, StructureAtCreate, StructureBeforeCallbacks, StructureDefaultValues, StructureInitializers, StructureProxies, StructureSearchModules
Defined in:
lib/ruckus/structure.rb,
lib/ruckus/enum.rb,
lib/ruckus/dictionary.rb,
lib/ruckus/structure_shortcuts.rb

Overview

class Dictionary

Direct Known Subclasses

Padding

Constant Summary

Constants inherited from Parsel

Parsel::VERBOTEN

Instance Attribute Summary

Attributes inherited from Parsel

#name, #parent, #rendered_offset, #rendering, #tag, #value

Class Method Summary collapse

Instance Method Summary collapse

Methods included from StructureSearchModules

included

Methods included from StructureProxies

#n, #v

Methods included from StructureBeforeCallbacks

included

Methods included from StructureAtCreate

included

Methods included from StructureDefaultValues

included

Methods included from StructureInitializers

included

Methods inherited from Parsel

bytes_for_bits, coerce, #each_matching_selector, endian?, factory?, #find_containing, #find_tag, #find_tag_struct, #fixup, #in, #incomplete!, #index_for_selectors, #inspect, #matches_selector?, #native?, native?, #next, #out, #parent_structure, #permute, #prev, #resolve, #root, #size, #visit, #where_am_i?

Constructor Details

#initialize(opts = {}) ⇒ Structure

No special options yet. A structure is just a parsel; pass options through to the parent class. f



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/ruckus/structure.rb', line 121

def initialize(opts={})

    # A Structure is really just a Blob with extra goop
    @value = Blob.new
    @value.parent = self

    # Most of the goop is for figuring out what fields to
    # add, with what arguments, given where we are in the
    # inheritance hierarchy.

    template = self.class.templates

    # If this is the first time we're seeing this definition,
    # we also need to convert field names into blob offsets.
    pop = false
    if not self.class.structure_field_names
        self.class.write_inheritable_hash :structure_field_names, {}
        pop = true
    end

    template.each do |t|
        # do some rewriting to support an old style of declaring
        # fields that we supported like 6 months ago.
        name = template_decoder_ring(t)

        # index the field name if this is the first time we've
        # ever instantiated this kind of structure, and the field
        # is valid
        self.class.structure_field_names[name] = @value.count if (name and pop)

        begin
            # create the structure field object, parent it
            klass, args = [t[0], t[1]]
            obj = klass.new(*args)
            obj.parent = @value

            template_entry_added_hook(obj) || @value << obj
        rescue
            pp t
            raise
        end
    end

    opts.each do |k, v|
        if self.class.structure_field_names.has_key? k
            raise "attempting to assign field name as option; use with_{field_name} instead"
        end
    end

    super(opts)

    final_initialization_hook
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args) ⇒ Object

A la openstruct/struct — method calls can be references to field names.



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/ruckus/structure.rb', line 220

def method_missing(meth, *args)
    return if not method_missing_hook(meth, args)

    d = self.class.structure_field_names
    m = meth.to_s

    setter = (m[-1].chr == "=") ? true : false
    m = m[0..-2] if setter

    puts "WARNING: assignment to @value as struct field" if setter and m == "value"

    if (i = d[m.intern])
        if setter
            self[m.intern] = args[0]
        else
            self[m.intern]
        end
    else
        super(meth, *args)
    end
end

Class Method Details

.add(*args) ⇒ Object

If you have an actual class reference, you can just pass it to add, as in:

add(HeaderStructure, :name => :header)


90
91
92
93
94
# File 'lib/ruckus/structure.rb', line 90

def self.add(*args)
    raise "no class" if not args[0]

    write_inheritable_array :templates, [[args[0], args[1..-1]]]
end

.base_pad(name, tag = nil) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/ruckus/structure_shortcuts.rb', line 64

def self.base_pad(name, tag=nil)
    string name, :value => {:offset => :this, :block => lambda do |this|
            r = this.root
            r = r.find_tag_struct(tag) if tag
            if ((k = this.rendered_offset - r.rendered_offset) % 4) != 0
                pad = 4 - ((this.rendered_offset - r.rendered_offset) % 4)
            else
                pad = 0
            end
            "\x00" * pad
        end
    }
end

.bounded(*args) ⇒ Object

A bounded string, takes its size from the preceding element



31
32
33
34
35
36
37
# File 'lib/ruckus/structure_shortcuts.rb', line 31

def self.bounded(*args)
    with_args(*args) do |name, opts|
        opts[:size] ||= :value
        opts[:offset] ||= -1
        str name, opts
    end
end

.char(*args) ⇒ Object



12
# File 'lib/ruckus/structure_shortcuts.rb', line 12

def self.char(*args); with_args(*args) {|name, opts| str opts.merge(:name => name, :size => 1)}; end

.choose(name, tag = nil, &block) ⇒ Object



60
61
62
# File 'lib/ruckus/structure_shortcuts.rb', line 60

def self.choose(name, tag=nil, &block)
    add(Ruckus::Choice, :name => name, :block => block)
end

.decimal(*args) ⇒ Object



14
# File 'lib/ruckus/structure_shortcuts.rb', line 14

def self.decimal(*args); with_args(*args) {|name, opts| num name, opts.merge(:ascii => true, :radix => 10)}; end

.dict(*args) ⇒ Object

Convenience alias for ‘dictionary’. First arg is “name”, all others are passed directly into ‘new’



114
115
116
# File 'lib/ruckus/dictionary.rb', line 114

def self.dict(*args)
  with_args(*args) {|name, opts| dictionary opts.merge(:name => name)}
end

.enum16be(*args) ⇒ Object



30
31
32
# File 'lib/ruckus/enum.rb', line 30

def self.enum16be(*args)
  with_args(*args) {|name, opts| enum name, opts.merge(:width => 16, :endian => :big)}
end

.enum16le(*args) ⇒ Object



22
23
24
# File 'lib/ruckus/enum.rb', line 22

def self.enum16le(*args)
  with_args(*args) {|name, opts| enum name, opts.merge(:width => 16, :endian => :little)}
end

.enum32be(*args) ⇒ Object



34
35
36
# File 'lib/ruckus/enum.rb', line 34

def self.enum32be(*args)
  with_args(*args) {|name, opts| enum name, opts.merge(:width => 32, :endian => :big)}
end

.enum32le(*args) ⇒ Object



26
27
28
# File 'lib/ruckus/enum.rb', line 26

def self.enum32le(*args)
  with_args(*args) {|name, opts| enum name, opts.merge(:width => 32, :endian => :little)}
end

.hex_number(*args) ⇒ Object



15
# File 'lib/ruckus/structure_shortcuts.rb', line 15

def self.hex_number(*args); with_args(*args) {|name, opts| num name, opts.merge(:ascii => true, :radix => 16)}; end

.ipv4(*args) ⇒ Object

4-byte IP address (IPv4)



54
55
56
57
58
# File 'lib/ruckus/structure_shortcuts.rb', line 54

def self.ipv4(*args)
    with_args(*args) do |name, opts|
        add(Ruckus::IP, opts.merge(:name => name))
    end
end

.mark(*args) ⇒ Object

A Null byte



47
48
49
50
51
# File 'lib/ruckus/structure_shortcuts.rb', line 47

def self.mark(*args)
    with_args(*args) do |name, opts|
        null opts.merge(:name => name)
    end
end

.msg_len(*args) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/ruckus/structure_shortcuts.rb', line 86

def self.msg_len(*args); with_args(*args) do |name, opts|
        opts[:value] = { :block => lambda do |n|
                if not n.rendering
                    begin
                        n.rendering = true
                        n.parent_struct.size
                    ensure
                        n.rendering = false
                    end
                else
                    4
                end
            end
        }
        opts[:width] ||= 32
        num name, opts
    end
end

.num(name, opts) ⇒ Object

Any number, ie

num :len, :width => 32, :endianness => :little


8
9
10
# File 'lib/ruckus/structure_shortcuts.rb', line 8

def self.num(name, opts)
    add(Number, opts.merge(:name => name))
end

.string(*args) ⇒ Object

A string.



40
41
42
43
44
# File 'lib/ruckus/structure_shortcuts.rb', line 40

def self.string(*args)
    with_args(*args) do |name, opts|
        str opts.merge(:name => name)
    end
end

.tag_bit(*args) ⇒ Object



17
# File 'lib/ruckus/structure_shortcuts.rb', line 17

def self.tag_bit(*args); with_args(*args) {|name, opts| num name, opts.merge(:width => 1, :tag => name)}; end

.with_args(*args, &block) ⇒ Object



242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/ruckus/structure.rb', line 242

def self.with_args(*args, &block)
    if args[0].kind_of? Hash
        name = nil
        opts = args[0]
    else
        name = args[0]
        opts = args[1]
    end

    opts ||= {}
    block.call(name, opts)
end

.word_len(*args) ⇒ Object



78
79
80
81
82
83
84
# File 'lib/ruckus/structure_shortcuts.rb', line 78

def self.word_len(*args); with_args(*args) do |name, opts|
        opts[:value] ||= :size
        opts[:width] ||= 16
        opts[:modifier] = lambda {|o, s| s/=2}
        num name, opts
    end
end

.zero_pad(*args) ⇒ Object

A string (ie, multiple of 8 bits wide) containing all zeroes. You could also just use

num :width => whatever, :value => 0


23
24
25
26
27
# File 'lib/ruckus/structure_shortcuts.rb', line 23

def self.zero_pad(*args)
    with_args(*args) do |name, opts|
        str opts.merge(:name => name, :padding => "\x00")
    end
end

Instance Method Details

#[](k) ⇒ Object

Return a field (the Parsel object) by offset into the Structure or by name lookup



178
179
180
181
182
183
184
185
# File 'lib/ruckus/structure.rb', line 178

def [](k)
    k = k.intern if k.kind_of? String
    if k.kind_of? Symbol
        @value[self.class.structure_field_names[k]]
    else
        @value[k]
    end
end

#[]=(k, v) ⇒ Object

Assign to a field. You can pass scalar types and they’ll be converted, so struct.name = “foo” works.



190
191
192
193
194
195
196
197
# File 'lib/ruckus/structure.rb', line 190

def []=(k, v)
    k = k.intern if k.kind_of? String
    if k.kind_of? Symbol
        @value[self.class.structure_field_names[k]] = v
    else
        @value[k] = v
    end
end

#before_render_hook(*args) ⇒ Object



205
# File 'lib/ruckus/structure.rb', line 205

def before_render_hook(*args); super(*args); end

#capture(str) ⇒ Object

Easy — delegate to blob



201
202
203
# File 'lib/ruckus/structure.rb', line 201

def capture(str)
    @value.capture(str)
end

#each_fieldObject



255
256
257
# File 'lib/ruckus/structure.rb', line 255

def each_field
    @value.each {|f| yield f.name, f}
end

#method_missing_hook(meth, *args) ⇒ Object



215
# File 'lib/ruckus/structure.rb', line 215

def method_missing_hook(meth, *args); super; end

#respond_to?(symbol, include_priv = false) ⇒ Boolean

Returns:

  • (Boolean)


259
260
261
# File 'lib/ruckus/structure.rb', line 259

def respond_to?(symbol, include_priv = false)
  !!(super(symbol, include_priv) || @value.respond_to?(symbol, include_priv) || self.class.structure_field_names.include?(symbol) || self.find_tag(symbol))
end

#to_s(off = nil) ⇒ Object

Easy — delegate to blob



209
210
211
212
213
# File 'lib/ruckus/structure.rb', line 209

def to_s(off=nil)
    before_render_hook
    @rendered_offset = off || 0
    @value.to_s(off)
end