Class: Depix::Field

Inherits:
Object
  • Object
show all
Defined in:
lib/depix/dict.rb

Overview

A basic C structs library (only works by value).

Here's the basic mode of operation:
 1) You define a struct, with a number of fields in it
 3) Each field knows how big it is and how to produce a pattern to get it's value from the byte stream
    by using Ruby's "pack/unpack". Each field thus provides an unpack pattern, and patterns are ordered
    into a stack, starting with the first unpack pattern
 4) When you parse some bytes using the struct, heres what will happen:
    - An unpack pattern will be compiled from all of the fields composing the struct,
     and it will be a single string. The string gets applied to the bytes passed to parse()
    - An array of unpacked values returned by unpack is then passed to the struct's consumption engine,
      which lets each field take as many items off the stack as it needs. A field might happily produce
      4 items for unpacking and then take the same 4 items off the stack of parsed values. Or not.
    - A new structure gets created and for every named field it defines an attr_accessor. When consuming,
      the values returned by Field objects get set using the accessors (so accessors can be overridden too!)
 5) When you save out the struct roughly the same happens but in reverse (readers are called per field,
    then it's checked whether the data can be packed and fits into the alloted number of bytes, and then
    one big array of values is composed and passed on to Array#pack)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Field

Hash init



35
36
37
# File 'lib/depix/dict.rb', line 35

def initialize(opts = {})
  opts.each_pair {|k, v| send(k.to_s + '=', v) }
end

Instance Attribute Details

#descObject

Field name



26
27
28
# File 'lib/depix/dict.rb', line 26

def desc
  @desc
end

#lengthObject

Field name



26
27
28
# File 'lib/depix/dict.rb', line 26

def length
  @length
end

#nameObject

Field name



26
27
28
# File 'lib/depix/dict.rb', line 26

def name
  @name
end

#patternObject

Field name



26
27
28
# File 'lib/depix/dict.rb', line 26

def pattern
  @pattern
end

#reqObject Also known as: req?

Field name



26
27
28
# File 'lib/depix/dict.rb', line 26

def req
  @req
end

#rtypeObject

Field name



26
27
28
# File 'lib/depix/dict.rb', line 26

def rtype
  @rtype
end

Class Method Details

.emit_char(o = {}) ⇒ Object

Emit a char field



55
56
57
58
# File 'lib/depix/dict.rb', line 55

def self.emit_char(o = {})
  opts = {:length => 1}.merge(o)
  CharField.new(opts)
end

.emit_r32(o = {}) ⇒ Object

Emit a float field



61
62
63
# File 'lib/depix/dict.rb', line 61

def self.emit_r32(o = {})
  R32Field.new(o)
end

.emit_u16(o = {}) ⇒ Object

Emit a double int field



50
51
52
# File 'lib/depix/dict.rb', line 50

def self.emit_u16(o = {})
  U16Field.new(o)
end

.emit_u32(o = {}) ⇒ Object

Emit an unsigned int field



40
41
42
# File 'lib/depix/dict.rb', line 40

def self.emit_u32(o = {})
  U32Field.new(o)
end

.emit_u8(o = {}) ⇒ Object

Emit a short int field



45
46
47
# File 'lib/depix/dict.rb', line 45

def self.emit_u8(o = {})
  U8Field.new(o)
end

Instance Method Details

#clean(v) ⇒ Object

Return a cleaned value (like a null-terminated string truncated up to null)



66
67
68
# File 'lib/depix/dict.rb', line 66

def clean(v)
  v
end

#consume!(stack) ⇒ Object

Return the actual values from the stack. The stack will begin on the element we need, so the default consumption is shift. Normally all fields shift the stack as they go, and if they contain nested substructs they will pop the stack as well



78
79
80
# File 'lib/depix/dict.rb', line 78

def consume!(stack)
  clean(stack.shift)
end

#explainObject

Show a nice textual explanation of the field



71
72
73
# File 'lib/depix/dict.rb', line 71

def explain
  [rtype ? ("(%s)" % rtype) : nil, desc, (req? ? "- required" : nil)].compact.join(' ')
end

#pack(value) ⇒ Object

Pack a value passed into a string



93
94
95
96
97
98
99
100
# File 'lib/depix/dict.rb', line 93

def pack(value)
  raise "No pattern defined for #{self}" unless pattern
  if value.nil?
    [self.class.const_get(:BLANK)].pack(pattern)
  else
    [value].pack(pattern)
  end
end

#validate!(value) ⇒ Object

Check that the passed value: a) Matches the Ruby type expected b) Fits into the slot c) Does not overflow When the validation fails should raise



87
88
89
90
# File 'lib/depix/dict.rb', line 87

def validate!(value)
  raise "#{name} value required, but got nil in #{name}".strip if value.nil? && req?
  raise "Value expected to be #{rtype} but was #{value.class}" if !value.nil? && rtype && !value.is_a?(rtype)
end