Class: PackedStruct::Directive

Inherits:
Object
  • Object
show all
Defined in:
lib/packed_struct/directive.rb

Constant Summary collapse

BYTES_IN_STRING =

The number of bytes a type takes up in the string.

{
  :char  => [0].pack("c").bytesize,
  :short => [0].pack("s").bytesize,
  :int   => [0].pack("i").bytesize,
  :long  => [0].pack("l").bytesize,
  :float_single => [0].pack("f").bytesize,
  :float_double => [0].pack("D").bytesize,
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name) ⇒ Directive

Initialize the directive.

Parameters:

  • name (Symbol)

    the name of the directive.

  • package (Package)

    the package this directive is a part of.



28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/packed_struct/directive.rb', line 28

def initialize(name)
  @name = name
  @modifiers = []
  @finalized = true

  @tags = {
    :endian     => :native,
    :signedness => :signed,
    :size       => nil,
    :precision  => :single,
    :size_mod   => 0
  }
end

Instance Attribute Details

#modifiersSet<Modifier> (readonly)

The modifiers for this directive.

Returns:



15
16
17
# File 'lib/packed_struct/directive.rb', line 15

def modifiers
  @modifiers
end

#nameSymbol (readonly)

The name of the directive. This is passed as the first value of the directive; from Package, it is the name of the method call.

Returns:

  • (Symbol)


10
11
12
# File 'lib/packed_struct/directive.rb', line 10

def name
  @name
end

#tagsHash<Symbol, Object> (readonly)

Metadata about the directive. Created when #finalize! is called.

Returns:

  • (Hash<Symbol, Object>)


21
22
23
# File 'lib/packed_struct/directive.rb', line 21

def tags
  @tags
end

Instance Method Details

#+(other) ⇒ self, Object

Modifies self such that it sizes itself (on #to_sing), and keeping this size modification in mind. In other words, this is meant to be used in another directive’s #[] method for telling it what size it should be, and this method modifies that size by the given amount.

Examples:

some_directive[another_directive - 5]

Parameters:

Returns:

  • (self, Object)


111
112
113
114
115
116
117
118
119
# File 'lib/packed_struct/directive.rb', line 111

def +(other)
  if other.is_a? Numeric
    tags[:size_mod] = +other
    self
  else
    self_equiv, arg_equiv = other.coerce(self)
    self_equiv + arg_equiv
  end
end

#-(other) ⇒ self, Object

Modifies self such that it sizes itself (on #to_sing), and keeping this size modification in mind. In other words, this is meant to be used in another directive’s #[] method for telling it what size it should be, and this method modifies that size by the given amount.

Examples:

some_directive[another_directive - 5]

Parameters:

Returns:

  • (self, Object)


100
101
102
103
104
105
106
107
108
# File 'lib/packed_struct/directive.rb', line 100

def -(other)
  if other.is_a? Numeric
    tags[:size_mod] = -other
    self
  else
    self_equiv, arg_equiv = other.coerce(self)
    self_equiv - arg_equiv
  end
end

#[](new_size) ⇒ self

Changes the size of the directive to the given size. It is possible for the given value to the a directive; if it is, it just uses the name of the directive.

Parameters:

Returns:

  • (self)


69
70
71
72
73
74
75
76
77
# File 'lib/packed_struct/directive.rb', line 69

def [](new_size)
  if new_size.is_a? Directive
    tags.merge! new_size.tags_for_sized_directive
  else
    tags[:size] = new_size
  end

  self
end

#add_modifier(mod) ⇒ self

Add a modifier to this directive.

Parameters:

Returns:

  • (self)


57
58
59
60
61
# File 'lib/packed_struct/directive.rb', line 57

def add_modifier(mod)
  @finalized = false
  modifiers << mod
  self
end

#bytesize(data = {}) ⇒ Numeric

The number of bytes this takes up in the resulting packed string.

Parameters:

  • data (Hash<Symbol, Object>) (defaults to: {})

    the data that may be used for the length.

Returns:

  • (Numeric)


223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/packed_struct/directive.rb', line 223

def bytesize(data = {})
  case tags[:type]
  when nil
    (size(data) || 8) / 8
  when :short, :int, :long
    BYTES_IN_STRING.fetch tags[:type]
  when :float
    if tags[:precision] == :double
      BYTES_IN_STRING[:float_double]
    else
      BYTES_IN_STRING[:float_single]
    end
  when :null
    size(data) || 1
  when :string
    size(data)
  else
    0
  end
end

#coerce(other) ⇒ Array<(self, Object)>

Coerces self into a format that can be used with Numeric to add or subtract from this class.

Examples:

some_directive[1 + another_directive]

Parameters:

  • other (Object)

Returns:

  • (Array<(self, Object)>)


128
129
130
# File 'lib/packed_struct/directive.rb', line 128

def coerce(other)
  [self, other]
end

#empty?Boolean

Determines whether or not this directive is empty. It is considered empty when its tags has all of the default values, it has no modifiers, and its name is not :null.

Returns:

  • (Boolean)


47
48
49
50
51
# File 'lib/packed_struct/directive.rb', line 47

def empty?
  tags == { :endian => :native, :signedness => :signed,
    :size => nil, :precision => :single, :size_mod => 0
  } && modifiers.length == 0 && name != :null
end

#finalize!void

This method returns an undefined value.

Finalizes the directive.



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/packed_struct/directive.rb', line 144

def finalize!
  return if finalized?

  modifiers.each do |modifier|
    modifier.type.each_with_index do |type, i|
      case type
      when :endian, :signedness, :precision, :type, :string_type
        tags[type] = modifier.value[i]
      when :size
        tags[:size] = modifier.value[i] unless tags[:size]
      else
        raise UnknownModifierError,
          "Unknown modifier: #{type}"
      end
    end
  end

  @finalized = true
  cache_string
end

#finalized?Boolean

Whether or not this directive has finalized. It is finalized until a modifier is added, and then #finalize! is required to finalize the directive.

Returns:

  • (Boolean)


137
138
139
# File 'lib/packed_struct/directive.rb', line 137

def finalized?
  @finalized
end

#size(data = {}) ⇒ nil, Numeric

The size of this directive.

Parameters:

  • data (Hash<Symbol, Object>) (defaults to: {})

    the data that may be used for the length.

Returns:

  • (nil, Numeric)


248
249
250
251
252
253
254
# File 'lib/packed_struct/directive.rb', line 248

def size(data = {})
  if tags[:size].is_a? Symbol
    data.fetch(tags[:size])
  else
    tags[:size]
  end
end

#tags_for_sized_directiveHash<Symbol, Object>

Returns a hash to be merged into the tags of a directive that recieved this directive for a size.

Returns:

  • (Hash<Symbol, Object>)


83
84
85
86
87
88
# File 'lib/packed_struct/directive.rb', line 83

def tags_for_sized_directive
  {
    :size => name,
    :size_mod => tags[:size_mod]
  }
end

#to_s(data = {}) ⇒ String

Turn the directive into a string, with the given data. It shouldn’t need the data unless tags[:size] is a Symbol.

Parameters:

  • data (Hash<Symbol, Object>) (defaults to: {})

    the data that may be used for the length.

Returns:

  • (String)


179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/packed_struct/directive.rb', line 179

def to_s(data = {})
  return @_cache unless undefined_size? || !@_cache
  return "x" * (tags[:size] || 1) if name == :null

  out = case tags[:type]
  when :short
    modify_if_needed "S"
  when :int
    modify_if_needed "I"
  when :long
    modify_if_needed "L"
  when :string
    handle_string_type
  when :float
    handle_float_type
  when nil
    handle_empty_type
  else
    nil
  end

  if tags[:size].is_a? Symbol
    out << data.fetch(tags[:size]).to_s
  elsif tags[:size] && ![:null, nil].include?(tags[:type])
    out << tags[:size].to_s
  end

  out
end

#undefined_size?Boolean

Returns whether or not this directive depends on another directive for its value.

Returns:

  • (Boolean)


169
170
171
# File 'lib/packed_struct/directive.rb', line 169

def undefined_size?
  tags[:size].is_a?(Symbol)
end