Class: Fields::Field

Inherits:
Object show all
Defined in:
lib/fields.rb

Overview

Subclasses of Fields::Field should probably at least overload input_to_bitstring and get_value and set @length_type. Check the source code for the base subclasses for ideas. Remember that for the purposes of fuzzing you can probably get away with inheriting from one of the base classes a lot of the time - an EmailField can probably just inherit StringField unless you want stringent checking in your parser.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(bitstring, name, length, desc, default, endian) ⇒ Field

Returns a new instance of Field.



26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/fields.rb', line 26

def initialize(bitstring, name, length, desc, default, endian)
    @name=name
    @length=length
    @desc=desc
    default&&=self.input_to_bitstring(default)
    @default_value=default # can still be nil
    @bitstring=self.parse_buffer(bitstring)
    @type=self.class.to_s[/\w+(?=Field)/].downcase #fricking ugly
    @endianness=endian
    unless @endianness.downcase==:big or @endianness.downcase==:little
        raise ArgumentError, "#{self.class.to_s[/\w+$/]} (#{@name}): Unknown endianness #{endianness}, use :little or :big (default)."
    end
end

Instance Attribute Details

#bitstringObject (readonly)

Returns the value of attribute bitstring.



24
25
26
# File 'lib/fields.rb', line 24

def bitstring
  @bitstring
end

#default_valueObject (readonly)

Returns the value of attribute default_value.



24
25
26
# File 'lib/fields.rb', line 24

def default_value
  @default_value
end

#descObject (readonly)

Returns the value of attribute desc.



24
25
26
# File 'lib/fields.rb', line 24

def desc
  @desc
end

#endiannessObject (readonly)

Returns the value of attribute endianness.



24
25
26
# File 'lib/fields.rb', line 24

def endianness
  @endianness
end

#lengthObject (readonly)

Returns the value of attribute length.



24
25
26
# File 'lib/fields.rb', line 24

def length
  @length
end

#length_typeObject (readonly)

Returns the value of attribute length_type.



24
25
26
# File 'lib/fields.rb', line 24

def length_type
  @length_type
end

#nameObject (readonly)

Returns the value of attribute name.



24
25
26
# File 'lib/fields.rb', line 24

def name
  @name
end

#typeObject (readonly)

Returns the value of attribute type.



24
25
26
# File 'lib/fields.rb', line 24

def type
  @type
end

Instance Method Details

#get_valueObject

Subclasses should override this. This default returns the raw bitstring.



106
107
108
# File 'lib/fields.rb', line 106

def get_value
    @bitstring
end

#input_to_bitstring(value) ⇒ Object

Placeholder, classes should override as neccessary. Parse a value in whatever format is determined by the class and return a bitstring. This default does zero checking, so expects a bitstring as input



85
86
87
# File 'lib/fields.rb', line 85

def input_to_bitstring( value )
    value
end

#parse_buffer(bitstring) ⇒ Object

Placeholder, subclasses should probably redefine for clarity. Defaults to calling parse_buffer_strict, but uses parse_buffer_lazy for variable length fields.



70
71
72
73
# File 'lib/fields.rb', line 70

def parse_buffer( bitstring )
    return self.parse_buffer_lazy( bitstring ) if self.length_type=="variable"
    self.parse_buffer_strict( bitstring ) # default to this for subclasses that forget to define @length_type
end

#parse_buffer_lazy(bitstring) ⇒ Object

Provided for classes that don’t care about length matching when assigning the contents. Called by parse_buffer internally.



59
60
61
62
63
64
65
66
# File 'lib/fields.rb', line 59

def parse_buffer_lazy( bitstring )
    return "" unless bitstring
    bitstring||=""
    unless bitstring.is_a? String and bitstring=~/^[10]*$/
        raise ArgumentError, "#{self.class.to_s[/\w+$/]} (#{@name}): <Internal> bitstring buffer borked??"
    end
    bitstring
end

#parse_buffer_strict(bitstring) ⇒ Object

Provided for classes that require strict lengths. Left pads with 0 to @length if the input buffer is incomplete. Truncates at @length if too long. Called by parse_buffer internally.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/fields.rb', line 42

def parse_buffer_strict( bitstring )
    return "" unless bitstring
    bitstring||=""
    unless bitstring.is_a? String and bitstring=~/^[10]*$/
        raise ArgumentError, "Field: <Internal> bitstring buffer borked?? #{bitstring.inspect}"
    end
    if bitstring.length <= @length
"0"*(@length-bitstring.length)+bitstring
    elsif bitstring.length > @length
        bitstring.slice(0,@length)
    else
        raise RuntimeError, "Universe Broken Error: value neither less/equal nor greater"
    end
end

#randomize!Object

Randomize the bitstring for this field, bit by bit



90
91
92
93
94
95
96
97
98
# File 'lib/fields.rb', line 90

def randomize!
    random_bitstring=Array.new(self.length).map {|e| e=rand(2).to_s}.join
    if self.length_type=="variable"
        slice=random_bitstring[0,(rand((self.length/8)+1)*8)]
        set_raw(slice)
    else
        set_raw(random_bitstring)
    end
end

#set_raw(bitstring) ⇒ Object

Sets the raw field contents. Can be useful if the set_value method does inconvenient checking when you want to set crazy values.



77
78
79
# File 'lib/fields.rb', line 77

def set_raw( bitstring )
    @bitstring=self.parse_buffer( bitstring )
end

#set_value(new_val) ⇒ Object

Set the field value. Calls self.input_to_bitstring which is expected to return a binary string.



101
102
103
# File 'lib/fields.rb', line 101

def set_value(new_val)
    @bitstring=self.input_to_bitstring(new_val)
end

#to_sObject

This really only makes sense for fields that are byte aligned, but what the hey. For the rest it effectively packs them as left padded with zeroes to a byte boundary (so a 3 bit field “110” will pack as 006)



112
113
114
# File 'lib/fields.rb', line 112

def to_s
    @bitstring.reverse.scan(/.{1,8}/).map {|s| s.reverse}.reverse.map {|bin| "" << bin.to_i(2)}.join
end