Class: Kaitai::Struct::Stream
- Inherits:
-
Object
- Object
- Kaitai::Struct::Stream
- Defined in:
- lib/kaitai/struct/struct.rb
Overview
Kaitai::Struct::Stream is an implementation of Kaitai Struct stream API for Ruby. It’s implemented as a wrapper for generic IO objects.
It provides a wide variety of simple methods to read (parse) binary representations of primitive types, such as integer and floating point numbers, byte arrays and strings, and also provides stream positioning / navigation methods with unified cross-language and cross-toolkit semantics.
Typically, end users won’t access Kaitai Stream class manually, but would describe a binary structure format using .ksy language and then would use Kaitai Struct compiler to generate source code in desired target language. That code, in turn, would use this class and API to do the actual parsing job.
Defined Under Namespace
Classes: UnexpectedDataError
Constant Summary collapse
- @@big_endian =
Test endianness of the platform
[0x0102].pack('s') == [0x0102].pack('n')
Stream positioning collapse
-
#eof? ⇒ true, false
Check if stream pointer is at the end of stream.
-
#pos ⇒ Fixnum
Get current position of a stream pointer.
-
#seek(x) ⇒ Object
Set stream pointer to designated position.
-
#size ⇒ Fixnum
Get total size of the stream in bytes.
Integer numbers collapse
-
#read_s1 ⇒ Object
———————————————————————— Signed ————————————————————————.
-
#read_s2be ⇒ Object
.….….….….….….….….….….….….….….….….….…
-
#read_s2le ⇒ Object
.….….….….….….….….….….….….….….….….….…
- #read_s4be ⇒ Object
- #read_s4le ⇒ Object
- #read_s8be ⇒ Object
- #read_s8le ⇒ Object
-
#read_u1 ⇒ Object
———————————————————————— Unsigned ————————————————————————.
-
#read_u2be ⇒ Object
.….….….….….….….….….….….….….….….….….…
-
#read_u2le ⇒ Object
.….….….….….….….….….….….….….….….….….…
- #read_u4be ⇒ Object
- #read_u4le ⇒ Object
- #read_u8be ⇒ Object
- #read_u8le ⇒ Object
Floating point numbers collapse
-
#read_f4be ⇒ Object
———————————————————————— Big-endian ————————————————————————.
-
#read_f4le ⇒ Object
———————————————————————— Little-endian ————————————————————————.
- #read_f8be ⇒ Object
- #read_f8le ⇒ Object
Unaligned bit values collapse
- #align_to_byte ⇒ Object
-
#read_bits_int(n) ⇒ Object
Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions.
- #read_bits_int_be(n) ⇒ Object
- #read_bits_int_le(n) ⇒ Object
Byte arrays collapse
- .bytes_strip_right(bytes, pad_byte) ⇒ Object
- .bytes_terminate(bytes, term, include_term) ⇒ Object
-
#ensure_fixed_contents(expected) ⇒ String
Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions.
-
#read_bytes(n) ⇒ String
Reads designated number of bytes from the stream.
-
#read_bytes_full ⇒ String
Reads all the remaining bytes in a stream as byte array.
- #read_bytes_term(term, include_term, consume_term, eos_error) ⇒ Object
Byte array processing collapse
-
.process_rotate_left(data, amount, group_size) ⇒ String
Performs a circular left rotation shift for a given buffer by a given amount of bits, using groups of groupSize bytes each time.
-
.process_xor_many(data, key) ⇒ String
Performs a XOR processing with given data, XORing every byte of input with a key array, repeating key array many times, if necessary (i.e. if data array is longer than key array).
-
.process_xor_one(data, key) ⇒ String
Performs a XOR processing with given data, XORing every byte of input with a single given value.
Class Method Summary collapse
-
.open(filename) ⇒ Object
Convenience method to create a Kaitai Stream object, opening a local file with a given filename.
-
.resolve_enum(enum_map, value) ⇒ Object
Resolves value using enum: if the value is not found in the map, we’ll just use literal value per se.
Instance Method Summary collapse
-
#close ⇒ Object
Closes underlying IO object.
-
#initialize(arg) ⇒ Stream
constructor
Constructs new Kaitai Stream object.
Constructor Details
#initialize(arg) ⇒ Stream
Constructs new Kaitai Stream object.
98 99 100 101 102 103 104 105 106 107 |
# File 'lib/kaitai/struct/struct.rb', line 98 def initialize(arg) if arg.is_a?(String) @_io = StringIO.new(arg) elsif arg.is_a?(IO) @_io = arg else raise TypeError.new('can be initialized with IO or String only') end align_to_byte end |
Class Method Details
.bytes_strip_right(bytes, pad_byte) ⇒ Object
420 421 422 423 424 425 426 427 |
# File 'lib/kaitai/struct/struct.rb', line 420 def self.bytes_strip_right(bytes, pad_byte) new_len = bytes.length while new_len > 0 and bytes.getbyte(new_len - 1) == pad_byte new_len -= 1 end bytes[0, new_len] end |
.bytes_terminate(bytes, term, include_term) ⇒ Object
429 430 431 432 433 434 435 436 437 |
# File 'lib/kaitai/struct/struct.rb', line 429 def self.bytes_terminate(bytes, term, include_term) new_len = 0 max_len = bytes.length while bytes.getbyte(new_len) != term and new_len < max_len new_len += 1 end new_len += 1 if include_term and new_len < max_len bytes[0, new_len] end |
.open(filename) ⇒ Object
Convenience method to create a Kaitai Stream object, opening a local file with a given filename.
113 114 115 |
# File 'lib/kaitai/struct/struct.rb', line 113 def self.open(filename) self.new(File.open(filename, 'rb:ASCII-8BIT')) end |
.process_rotate_left(data, amount, group_size) ⇒ String
Performs a circular left rotation shift for a given buffer by a given amount of bits, using groups of groupSize bytes each time. Right circular rotation should be performed using this procedure with corrected amount.
496 497 498 499 500 501 502 503 504 505 506 507 508 509 |
# File 'lib/kaitai/struct/struct.rb', line 496 def self.process_rotate_left(data, amount, group_size) raise NotImplementedError.new("unable to rotate group #{group_size} bytes yet") unless group_size == 1 mask = group_size * 8 - 1 anti_amount = -amount & mask # NB: actually, left bit shift (<<) in Ruby would have required # truncation to type_bits size (i.e. something like "& 0xff" for # group_size == 8), but we can skip this one, because later these # number would be packed with Array#pack, which will do truncation # anyway data.bytes.map { |x| (x << amount) | (x >> anti_amount) }.pack('C*') end |
.process_xor_many(data, key) ⇒ String
Performs a XOR processing with given data, XORing every byte of input with a key array, repeating key array many times, if necessary (i.e. if data array is longer than key array). Uses pure Ruby implementation suggested by [Thomas Leitner](github.com/gettalong), borrowed from github.com/fny/xorcist/blob/master/bin/benchmark
472 473 474 475 476 477 478 479 480 481 482 483 484 485 |
# File 'lib/kaitai/struct/struct.rb', line 472 def self.process_xor_many(data, key) out = data.dup kl = key.length ki = 0 i = 0 max = data.length while i < max out.setbyte(i, data.getbyte(i) ^ key.getbyte(ki)) ki += 1 ki = 0 if ki >= kl i += 1 end out end |
.process_xor_one(data, key) ⇒ String
Performs a XOR processing with given data, XORing every byte of input with a single given value. Uses pure Ruby implementation suggested by [Thomas Leitner](github.com/gettalong), borrowed from github.com/fny/xorcist/blob/master/bin/benchmark
451 452 453 454 455 456 457 458 459 460 |
# File 'lib/kaitai/struct/struct.rb', line 451 def self.process_xor_one(data, key) out = data.dup i = 0 max = data.length while i < max out.setbyte(i, data.getbyte(i) ^ key) i += 1 end out end |
.resolve_enum(enum_map, value) ⇒ Object
Resolves value using enum: if the value is not found in the map, we’ll just use literal value per se.
516 517 518 |
# File 'lib/kaitai/struct/struct.rb', line 516 def self.resolve_enum(enum_map, value) enum_map[value] || value end |
Instance Method Details
#align_to_byte ⇒ Object
290 291 292 293 |
# File 'lib/kaitai/struct/struct.rb', line 290 def align_to_byte @bits_left = 0 @bits = 0 end |
#close ⇒ Object
Closes underlying IO object.
119 120 121 |
# File 'lib/kaitai/struct/struct.rb', line 119 def close @_io.close end |
#ensure_fixed_contents(expected) ⇒ String
Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions.
Reads next len bytes from the stream and ensures that they match expected fixed byte array. If they differ, throws a UnexpectedDataError runtime exception.
413 414 415 416 417 418 |
# File 'lib/kaitai/struct/struct.rb', line 413 def ensure_fixed_contents(expected) len = expected.bytesize actual = @_io.read(len) raise UnexpectedDataError.new(actual, expected) if actual != expected actual end |
#eof? ⇒ true, false
Check if stream pointer is at the end of stream.
131 |
# File 'lib/kaitai/struct/struct.rb', line 131 def eof?; @_io.eof? and @bits_left == 0; end |
#pos ⇒ Fixnum
Get current position of a stream pointer.
141 |
# File 'lib/kaitai/struct/struct.rb', line 141 def pos; @_io.pos; end |
#read_bits_int(n) ⇒ Object
Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions.
325 326 327 |
# File 'lib/kaitai/struct/struct.rb', line 325 def read_bits_int(n) read_bits_int_be(n) end |
#read_bits_int_be(n) ⇒ Object
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/kaitai/struct/struct.rb', line 295 def read_bits_int_be(n) bits_needed = n - @bits_left if bits_needed > 0 # 1 bit => 1 byte # 8 bits => 1 byte # 9 bits => 2 bytes bytes_needed = ((bits_needed - 1) / 8) + 1 buf = read_bytes(bytes_needed) buf.each_byte { |byte| @bits <<= 8 @bits |= byte @bits_left += 8 } end # raw mask with required number of 1s, starting from lowest bit mask = (1 << n) - 1 # shift @bits to align the highest bits with the mask & derive reading result shift_bits = @bits_left - n res = (@bits >> shift_bits) & mask # clear top bits that we've just read => AND with 1s @bits_left -= n mask = (1 << @bits_left) - 1 @bits &= mask res end |
#read_bits_int_le(n) ⇒ Object
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 |
# File 'lib/kaitai/struct/struct.rb', line 329 def read_bits_int_le(n) bits_needed = n - @bits_left if bits_needed > 0 # 1 bit => 1 byte # 8 bits => 1 byte # 9 bits => 2 bytes bytes_needed = ((bits_needed - 1) / 8) + 1 buf = read_bytes(bytes_needed) buf.each_byte { |byte| @bits |= (byte << @bits_left) @bits_left += 8 } end # raw mask with required number of 1s, starting from lowest bit mask = (1 << n) - 1 # derive reading result res = @bits & mask # remove bottom bits that we've just read by shifting @bits >>= n @bits_left -= n res end |
#read_bytes(n) ⇒ String
Reads designated number of bytes from the stream.
364 365 366 367 368 369 370 371 372 373 |
# File 'lib/kaitai/struct/struct.rb', line 364 def read_bytes(n) r = @_io.read(n) if r rl = r.bytesize else rl = 0 end raise EOFError.new("attempted to read #{n} bytes, got only #{rl}") if rl < n r end |
#read_bytes_full ⇒ String
Reads all the remaining bytes in a stream as byte array.
378 379 380 |
# File 'lib/kaitai/struct/struct.rb', line 378 def read_bytes_full @_io.read end |
#read_bytes_term(term, include_term, consume_term, eos_error) ⇒ Object
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 |
# File 'lib/kaitai/struct/struct.rb', line 382 def read_bytes_term(term, include_term, consume_term, eos_error) r = '' loop { if @_io.eof? if eos_error raise EOFError.new("end of stream reached, but no terminator #{term} found") else return r end end c = @_io.getc if c.ord == term r << c if include_term @_io.seek(@_io.pos - 1) unless consume_term return r end r << c } end |
#read_f4be ⇒ Object
Big-endian
266 267 268 |
# File 'lib/kaitai/struct/struct.rb', line 266 def read_f4be read_bytes(4).unpack('g')[0] end |
#read_f4le ⇒ Object
Little-endian
278 279 280 |
# File 'lib/kaitai/struct/struct.rb', line 278 def read_f4le read_bytes(4).unpack('e')[0] end |
#read_f8be ⇒ Object
270 271 272 |
# File 'lib/kaitai/struct/struct.rb', line 270 def read_f8be read_bytes(8).unpack('G')[0] end |
#read_f8le ⇒ Object
282 283 284 |
# File 'lib/kaitai/struct/struct.rb', line 282 def read_f8le read_bytes(8).unpack('E')[0] end |
#read_s1 ⇒ Object
Signed
156 157 158 |
# File 'lib/kaitai/struct/struct.rb', line 156 def read_s1 read_bytes(1).unpack('c')[0] end |
#read_s2be ⇒ Object
.….….….….….….….….….….….….….….….….….… Big-endian .….….….….….….….….….….….….….….….….….…
164 165 166 |
# File 'lib/kaitai/struct/struct.rb', line 164 def read_s2be to_signed(read_u2be, SIGN_MASK_16) end |
#read_s2le ⇒ Object
.….….….….….….….….….….….….….….….….….… Little-endian .….….….….….….….….….….….….….….….….….…
186 187 188 |
# File 'lib/kaitai/struct/struct.rb', line 186 def read_s2le to_signed(read_u2le, SIGN_MASK_16) end |
#read_s4be ⇒ Object
168 169 170 |
# File 'lib/kaitai/struct/struct.rb', line 168 def read_s4be to_signed(read_u4be, SIGN_MASK_32) end |
#read_s4le ⇒ Object
190 191 192 |
# File 'lib/kaitai/struct/struct.rb', line 190 def read_s4le to_signed(read_u4le, SIGN_MASK_32) end |
#read_s8be ⇒ Object
173 174 175 |
# File 'lib/kaitai/struct/struct.rb', line 173 def read_s8be read_bytes(8).unpack('q')[0] end |
#read_s8le ⇒ Object
195 196 197 |
# File 'lib/kaitai/struct/struct.rb', line 195 def read_s8le read_bytes(8).unpack('q')[0] end |
#read_u1 ⇒ Object
Unsigned
208 209 210 |
# File 'lib/kaitai/struct/struct.rb', line 208 def read_u1 read_bytes(1).unpack('C')[0] end |
#read_u2be ⇒ Object
.….….….….….….….….….….….….….….….….….… Big-endian .….….….….….….….….….….….….….….….….….…
216 217 218 |
# File 'lib/kaitai/struct/struct.rb', line 216 def read_u2be read_bytes(2).unpack('n')[0] end |
#read_u2le ⇒ Object
.….….….….….….….….….….….….….….….….….… Little-endian .….….….….….….….….….….….….….….….….….…
239 240 241 |
# File 'lib/kaitai/struct/struct.rb', line 239 def read_u2le read_bytes(2).unpack('v')[0] end |
#read_u4be ⇒ Object
220 221 222 |
# File 'lib/kaitai/struct/struct.rb', line 220 def read_u4be read_bytes(4).unpack('N')[0] end |
#read_u4le ⇒ Object
243 244 245 |
# File 'lib/kaitai/struct/struct.rb', line 243 def read_u4le read_bytes(4).unpack('V')[0] end |
#read_u8be ⇒ Object
225 226 227 |
# File 'lib/kaitai/struct/struct.rb', line 225 def read_u8be read_bytes(8).unpack('Q')[0] end |
#read_u8le ⇒ Object
248 249 250 |
# File 'lib/kaitai/struct/struct.rb', line 248 def read_u8le read_bytes(8).unpack('Q')[0] end |
#seek(x) ⇒ Object
Set stream pointer to designated position.
136 |
# File 'lib/kaitai/struct/struct.rb', line 136 def seek(x); @_io.seek(x); end |
#size ⇒ Fixnum
Get total size of the stream in bytes.
146 |
# File 'lib/kaitai/struct/struct.rb', line 146 def size; @_io.size; end |