Class: Ronin::Binary::Struct
Overview
Generic Binary Struct class.
Example
class Packet < Binary::Struct
endian :network
layout :length, :uint32,
:data, [:uchar, 48]
end
pkt = Packet.new
pkt.length = 5
pkt.data = 'hello'
buffer = pkt.pack
# => "\x00\x00\x00\x05hello\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
new_pkt = Packet.unpack(buffer)
# => #<Packet: length: 5, data: "hello">
Class Method Summary collapse
-
.default(type) ⇒ Integer, ...
protected
private
Default value for a field.
-
.each_field {|struct, name, type| ... } ⇒ Enumerator
protected
private
Enumerates the fields of the structure, and all nested structures.
-
.endian(type = nil) ⇒ :little, ...
protected
Sets or gets the endianness of the structure.
-
.field?(name) ⇒ Boolean
Determines if a field exists in the structure.
-
.fields ⇒ Hash{Symbol => type, (type, length)}
private
The fields in the structure.
-
.layout(*fields) ⇒ Array<Symbol>
protected
The layout of the structure.
-
.template(fields, options = {}) ⇒ Template
protected
Creates a new template for the structure.
-
.templates ⇒ Hash{Hash => Template}
protected
The templates for the structure.
-
.typedef(type, type_alias) ⇒ Object
protected
Defines a typedef.
-
.typedefs ⇒ Hash{Symbol => Symbol}
protected
private
Typedefs.
-
.unpack(data, options = {}) ⇒ Struct
Unpacks data into the structure.
Instance Method Summary collapse
-
#[](name) ⇒ Integer, ...
Reads a value from the structure.
-
#[]=(name, value) ⇒ Integer, ...
Writes a value to the structure.
-
#clear ⇒ Struct
Clears the fields of the structure.
-
#each_field {|struct, name, type| ... } ⇒ Enumerator
protected
private
Enumerates the fields of the structure, and all nested structure.
-
#field?(name) ⇒ Boolean
Determines if a field exists in the structure.
-
#initialize ⇒ Struct
constructor
Initializes the structure.
-
#inspect ⇒ String
Inspects the structure.
-
#pack(options = {}) ⇒ String
Packs the structure.
-
#to_s ⇒ Object
-
#to_str ⇒ Object
-
#unpack(data, options = {}) ⇒ Struct
Unpacks data into the structure.
-
#values ⇒ Array<Integer, Float, String, Struct>
The values within the structure.
Constructor Details
#initialize ⇒ Struct
Initializes the structure.
57 58 59 60 61 62 |
# File 'lib/ronin/binary/struct.rb', line 57 def initialize # initialize the fields in order self.class.layout.each do |name| self[name] = self.class.default(self.class.fields[name]) end end |
Class Method Details
.default(type) ⇒ Integer, ... (protected)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Default value for a field.
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 |
# File 'lib/ronin/binary/struct.rb', line 464 def self.default(type) type, length = type if length if Template::STRING_TYPES.include?(type) # arrays of chars should be Strings String.new else # create an array of values Array.new(length) { |index| default(type) } end else if type.kind_of?(Symbol) if Template::INT_TYPES.include?(type) then 0 elsif Template::FLOAT_TYPES.include?(type) then 0.0 elsif Template::CHAR_TYPES.include?(type) then "\0" elsif Template::STRING_TYPES.include?(type) then '' end elsif type < Struct type.new end end end |
.each_field {|struct, name, type| ... } ⇒ Enumerator (protected)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Enumerates the fields of the structure, and all nested structures.
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 |
# File 'lib/ronin/binary/struct.rb', line 509 def self.each_field(&block) return enum_for(__method__) unless block layout.each do |name| type, length = field = fields[name] if type.kind_of?(Symbol) yield self, name, field elsif type < Struct if length length.times { type.each_field(&block) } else type.each_field(&block) end end end end |
.endian(type = nil) ⇒ :little, ... (protected)
Sets or gets the endianness of the structure.
377 378 379 380 381 |
# File 'lib/ronin/binary/struct.rb', line 377 def self.endian(type=nil) if type then @endian = type.to_sym else @endian end end |
.field?(name) ⇒ Boolean
Determines if a field exists in the structure.
85 86 87 |
# File 'lib/ronin/binary/struct.rb', line 85 def self.field?(name) fields.has_key?(name.to_sym) end |
.fields ⇒ Hash{Symbol => type, (type, length)}
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The fields in the structure.
72 73 74 |
# File 'lib/ronin/binary/struct.rb', line 72 def self.fields @fields ||= {} end |
.layout(*fields) ⇒ Array<Symbol> (protected)
The layout of the structure.
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 |
# File 'lib/ronin/binary/struct.rb', line 396 def self.layout(*fields) unless fields.empty? @layout = [] @fields = {} fields.each_slice(2) do |name,(type,length)| type = typedefs.fetch(type,type) unless (type.kind_of?(Symbol) || type < Struct) raise(TypeError,"#{type.inspect} is not a Symbol or #{Struct}") end @layout << name @fields[name] = [type, length] attr_accessor name end end return (@layout ||= []) end |
.template(fields, options = {}) ⇒ Template (protected)
Creates a new template for the structure.
449 450 451 |
# File 'lib/ronin/binary/struct.rb', line 449 def self.template(fields,={}) Template.new(fields,) end |
.templates ⇒ Hash{Hash => Template} (protected)
The templates for the structure.
426 427 428 429 430 431 432 433 |
# File 'lib/ronin/binary/struct.rb', line 426 def self.templates @templates ||= Hash.new do |hash,| fields = each_field.map { |struct,name,field| field } = {:endian => self.endian}.merge() hash[] = template(fields,) end end |
.typedef(type, type_alias) ⇒ Object (protected)
Defines a typedef.
296 297 298 299 300 301 302 303 304 |
# File 'lib/ronin/binary/struct.rb', line 296 def self.typedef(type,type_alias) type = typedefs.fetch(type,type) unless (type.kind_of?(Symbol) || type < Struct) raise(TypeError,"#{type.inspect} is not a Symbol or #{Struct}") end typedefs[type_alias] = typedefs.fetch(type,type) end |
.typedefs ⇒ Hash{Symbol => Symbol} (protected)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Typedefs.
283 284 285 |
# File 'lib/ronin/binary/struct.rb', line 283 def self.typedefs @@typedefs ||= {} end |
.unpack(data, options = {}) ⇒ Struct
Unpacks data into the structure.
104 105 106 |
# File 'lib/ronin/binary/struct.rb', line 104 def self.unpack(data,={}) new().unpack(data,) end |
Instance Method Details
#[](name) ⇒ Integer, ...
Reads a value from the structure.
133 134 135 136 137 |
# File 'lib/ronin/binary/struct.rb', line 133 def [](name) if field?(name) then send(name) else raise(ArgumentError,"no such field '#{name}'") end end |
#[]=(name, value) ⇒ Integer, ...
Writes a value to the structure.
154 155 156 157 158 |
# File 'lib/ronin/binary/struct.rb', line 154 def []=(name,value) if field?(name) then send("#{name}=",value) else raise(ArgumentError,"no such field '#{name}'") end end |
#clear ⇒ Struct
Clears the fields of the structure.
188 189 190 191 192 193 194 |
# File 'lib/ronin/binary/struct.rb', line 188 def clear each_field do |struct,name,field| struct[name] = self.class.default(field) end return self end |
#each_field {|struct, name, type| ... } ⇒ Enumerator (protected)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Enumerates the fields of the structure, and all nested structure.
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 |
# File 'lib/ronin/binary/struct.rb', line 547 def each_field(&block) return enum_for(__method__) unless block self.class.layout.each do |name| type, length = field = self.class.fields[name] if type.kind_of?(Symbol) yield self, name, field elsif type < Struct if length self[name].each { |struct| struct.each_field(&block) } else self[name].each_field(&block) end end end end |
#field?(name) ⇒ Boolean
Determines if a field exists in the structure.
117 118 119 |
# File 'lib/ronin/binary/struct.rb', line 117 def field?(name) self.class.field?(name) end |
#inspect ⇒ String
Inspects the structure.
267 268 269 270 271 |
# File 'lib/ronin/binary/struct.rb', line 267 def inspect "#<#{self.class}: " << self.class.layout.map { |name| "#{name}: " << self[name].inspect }.join(', ') << '>' end |
#pack(options = {}) ⇒ String
Packs the structure.
208 209 210 |
# File 'lib/ronin/binary/struct.rb', line 208 def pack(={}) self.class.templates[].pack(*values.flatten) end |
#unpack(data, options = {}) ⇒ Struct
Unpacks data into the structure.
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/ronin/binary/struct.rb', line 227 def unpack(data,={}) values = self.class.templates[].unpack(data) each_field do |struct,name,(type,length)| struct[name] = if length if Template::STRING_TYPES.include?(type) # string types are combined into a single String values.shift else # shift off an Array of elements values.shift(length) end else values.shift end end return self end |
#values ⇒ Array<Integer, Float, String, Struct>
The values within the structure.
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/ronin/binary/struct.rb', line 166 def values normalize = lambda { |value| case value when Struct then value.values else value end } self.class.layout.map do |name| case (value = self[name]) when Array then value.map(&normalize) else normalize[value] end end end |