Class: Ruckus::Number

Inherits:
Parsel show all
Defined in:
lib/ruckus/number.rb

Overview

A Ruckus::Number is a type of Parsel that wraps an Integer.

Direct Known Subclasses

Be16, Be32, Be64, Bit, Byte, Decimal, Enum, Le16, Le32, Le64, Len, Nibble, TimeT

Constant Summary collapse

BASERX =
{
    2 => /([01]+)/,
    8 => /([0-7]+)/,
    10 => /([0-9]+)/,
    16 => /([0-9a-fA-F]+)/
}

Constants inherited from Parsel

Parsel::VERBOTEN

Instance Attribute Summary collapse

Attributes inherited from Parsel

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

Instance Method Summary collapse

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?, #method_missing, #native?, native?, #next, #out, #parent_structure, #permute, #prev, #respond_to?, #root, #visit, #where_am_i?

Constructor Details

#initialize(opts = {}) ⇒ Number

Options:

width

(Default: 32) Width in bits — can be odd!

endian

(Default: :little) Endianness — not honored for odd widths

value

Usually a fixnum, but can be a method to call on sibling Parsel, e.g. :size for “Length” fields.

Note: odd-width fields must be parented (see Blob or Structure) to render or capture their values



20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/ruckus/number.rb', line 20

def initialize(opts={})
    opts[:width] ||= 32
    opts[:endian] ||= :little
    # opts[:value] ||= 0xABADCAFE
    opts[:value] ||= 0
    opts[:radix] ||= 0

    if opts[:endian] == :native
        opts[:endian] = Parsel.endian?
    end
    super(opts)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Ruckus::Parsel

Instance Attribute Details

#asciiObject

Returns the value of attribute ascii.



6
7
8
# File 'lib/ruckus/number.rb', line 6

def ascii
  @ascii
end

#endianObject

Returns the value of attribute endian.



6
7
8
# File 'lib/ruckus/number.rb', line 6

def endian
  @endian
end

#padObject

Returns the value of attribute pad.



6
7
8
# File 'lib/ruckus/number.rb', line 6

def pad
  @pad
end

#radixObject

Returns the value of attribute radix.



6
7
8
# File 'lib/ruckus/number.rb', line 6

def radix
  @radix
end

#valueObject

Returns the value of attribute value.



7
8
9
# File 'lib/ruckus/number.rb', line 7

def value
  @value
end

#widthObject

Returns the value of attribute width.



6
7
8
# File 'lib/ruckus/number.rb', line 6

def width
  @width
end

Instance Method Details

#ascii_capture(str) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/ruckus/number.rb', line 117

def ascii_capture(str)
    incomplete! if str.empty?

    # weak
    if (rad = resolve(@radix)) == 0
        if str.starts_with? "0x"
            rad = 16
            str.shift 2
        else
            rad = 10
        end
    end

    if(str =~ BASERX[rad])
        @value = $1.to_i(rad)
        str.shift($1.size)
    else
        @value = 0
    end

    return str
end

#ascii_to_sObject



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

def ascii_to_s
    if((rad = resolve(@radix)) == 0)
        rad = 10
    end

    pad = resolve(@pad) || 0
    @value.to_s(rad).rjust(pad, "0")
end

#capture(str) ⇒ Object

Given: a string, return: the remainder of the string after parsing the number, side-effect: populate @value



99
100
101
102
103
104
105
106
107
108
# File 'lib/ruckus/number.rb', line 99

def capture(str)
    return ascii_capture(str) if @ascii
    return odd_width_capture(str) if not (code = widthcode)

    incomplete! if str.size < size
    cap = str.shift(size)
    cap = cap.reverse if not Parsel.native?(@endian)
    @value = cap.unpack(code).first
    return str
end

#odd_width?Boolean

Is this an odd-width (single bit, nibble, etc) number?

Returns:

  • (Boolean)


52
# File 'lib/ruckus/number.rb', line 52

def odd_width? ; not widthcode ; end

#odd_width_capture(str) ⇒ Object

Capture a whole span of odd-width numbers, see capture



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/ruckus/number.rb', line 233

def odd_width_capture(str)
    return str if not odd_width_first?

    incomplete! if str.size < (bytes_for_bits = Parsel.bytes_for_bits(span_bits))

    cap = str.shift(bytes_for_bits)

    acc = 0
    cap.each_byte do |b|
        acc <<= 8
        acc |= b
    end
    tbits = cap.size * 8
    tot = 0
    where_am_i?.upto(parent.count - 1) do |i|
        break if not parent[i].respond_to? :odd_width?
        break if not parent[i].odd_width?
        tot += parent[i].width
        parent[i].value = (acc >> (tbits - tot)) & Numeric.mask_for(parent[i].width)
    end

    return str
end

#odd_width_first?Boolean

Are we the first odd-width number in the span?

Returns:

  • (Boolean)


222
223
224
225
226
227
228
229
# File 'lib/ruckus/number.rb', line 222

def odd_width_first?
    return true if odd_width? and where_am_i? == 0
    begin
        not parent[where_am_i? - 1].odd_width?
    rescue
        true
    end
end

#odd_width_to_sObject

Render a span of odd-width numbers as a byte string



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/ruckus/number.rb', line 188

def odd_width_to_s
    return "" if not odd_width_first?

    acc = 0
    tot = 0
    bits = span_bits

    # Cheat horribly: Ruby has bignums, so just treat the whole
    # span as one giant bignum and math it into place.

    where_am_i?.upto(parent.count - 1) do |i|
        break if not parent[i].respond_to? :odd_width?
        break if not parent[i].odd_width?
        acc <<= parent[i].width
        tot += parent[i].width
        acc |= parent[i].resolve(parent[i].value) & Numeric.mask_for(parent[i].width)
    end
    acc <<= 8 - (tot % 8) if (tot % 8) != 0

    # Dump the bignum to a binary string

    ret = ""
    while bits > 0
        ret << (acc & 0xff).chr
        acc >>= 8
        bits -= 8
    end

    ret.reverse! if @endian == :big
    return ret
end

#resolve(x) ⇒ Object

Quick hack: true is 1, false is 0, nil is 0.



142
143
144
145
146
147
148
# File 'lib/ruckus/number.rb', line 142

def resolve(x)
    r = super
    r = 1 if r == true
    r = 0 if r == false
    r = 0 if r == nil
    return r
end

#sizeObject

How big is the encoded number?



259
260
261
262
263
264
265
266
267
268
# File 'lib/ruckus/number.rb', line 259

def size
    if not odd_width?
        s = Parsel.bytes_for_bits(@width);
    elsif odd_width_first?
        s = span_bits * 8
    else
        s = 0
    end
    s
end

#span_bitsObject

For odd-width fields — find how big the span of neighboring odd-width fields is, in bits.



75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/ruckus/number.rb', line 75

def span_bits
    last = where_am_i?
    span_start.upto(parent.count - 1) do |i|
        break if not parent[i].respond_to? :odd_width?
        break if not parent[i].odd_width?
        last = i
    end

    tot = 0
    span_start.upto(last) { |i| tot += parent[i].width }
    tot
end

#span_offsetObject

Where are we in the span of neighboring odd-width fields?



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

def span_offset
    tot = 0
    span_start.upto(where_am_i? - 1) {|i| tot += parent[i].width}
    tot
end

#to_s(off = nil) ⇒ Object

Render this number (or, if odd-width, this span of odd-width values, if we’re the first element of the span) as a bit string.



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/ruckus/number.rb', line 153

def to_s(off=nil)
    @rendered_offset = off || 0

    begin
        if @ascii
            r = ascii_to_s
        elsif not (code = widthcode)
            r = odd_width_to_s
        else
            val = resolve(@value).to_i
            r = [val].pack(code)
            r = r.reverse if @endian != Parsel.endian?
        end

        if off
            return r, off+r.size
        else
            return r
        end
    rescue
        raise
    end
end