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
Byte arrays collapse
- .bytes_strip_right(bytes, pad_byte) ⇒ Object
- .bytes_terminate(bytes, term, include_term) ⇒ Object
-
#ensure_fixed_contents(expected) ⇒ String
Reads next len bytes from the stream and ensures that they match expected fixed byte array.
-
#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
-
#initialize(arg) ⇒ Stream
constructor
Constructs new Kaitai Stream object.
Constructor Details
#initialize(arg) ⇒ Stream
Constructs new Kaitai Stream object.
94 95 96 97 98 99 100 101 102 103 |
# File 'lib/kaitai/struct/struct.rb', line 94 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
378 379 380 381 382 383 384 |
# File 'lib/kaitai/struct/struct.rb', line 378 def self.bytes_strip_right(bytes, pad_byte) new_len = bytes.length while bytes.getbyte(new_len - 1) == pad_byte new_len -= 1 end bytes[0, new_len] end |
.bytes_terminate(bytes, term, include_term) ⇒ Object
386 387 388 389 390 391 392 393 394 |
# File 'lib/kaitai/struct/struct.rb', line 386 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.
109 110 111 |
# File 'lib/kaitai/struct/struct.rb', line 109 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.
453 454 455 456 457 458 459 460 461 462 463 464 465 466 |
# File 'lib/kaitai/struct/struct.rb', line 453 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
429 430 431 432 433 434 435 436 437 438 439 440 441 442 |
# File 'lib/kaitai/struct/struct.rb', line 429 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
408 409 410 411 412 413 414 415 416 417 |
# File 'lib/kaitai/struct/struct.rb', line 408 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.
473 474 475 |
# File 'lib/kaitai/struct/struct.rb', line 473 def self.resolve_enum(enum_map, value) enum_map[value] || value end |
Instance Method Details
#align_to_byte ⇒ Object
280 281 282 283 |
# File 'lib/kaitai/struct/struct.rb', line 280 def align_to_byte @bits_left = 0 @bits = 0 end |
#ensure_fixed_contents(expected) ⇒ String
Reads next len bytes from the stream and ensures that they match expected fixed byte array. If they differ, throws a UnexpectedDataError runtime exception.
371 372 373 374 375 376 |
# File 'lib/kaitai/struct/struct.rb', line 371 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.
121 |
# File 'lib/kaitai/struct/struct.rb', line 121 def eof?; @_io.eof?; end |
#pos ⇒ Fixnum
Get current position of a stream pointer.
131 |
# File 'lib/kaitai/struct/struct.rb', line 131 def pos; @_io.pos; end |
#read_bits_int(n) ⇒ Object
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/kaitai/struct/struct.rb', line 285 def read_bits_int(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 mask to align with highest bits available in @bits shift_bits = @bits_left - n mask <<= shift_bits # derive reading result res = (@bits & mask) >> shift_bits # clear top bits that we've just read => AND with 1s @bits_left -= n mask = (1 << @bits_left) - 1 @bits &= mask res end |
#read_bytes(n) ⇒ String
Reads designated number of bytes from the stream.
325 326 327 328 329 330 331 332 333 334 |
# File 'lib/kaitai/struct/struct.rb', line 325 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.
339 340 341 |
# File 'lib/kaitai/struct/struct.rb', line 339 def read_bytes_full @_io.read end |
#read_bytes_term(term, include_term, consume_term, eos_error) ⇒ Object
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 |
# File 'lib/kaitai/struct/struct.rb', line 343 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
256 257 258 |
# File 'lib/kaitai/struct/struct.rb', line 256 def read_f4be read_bytes(4).unpack('g')[0] end |
#read_f4le ⇒ Object
Little-endian
268 269 270 |
# File 'lib/kaitai/struct/struct.rb', line 268 def read_f4le read_bytes(4).unpack('e')[0] end |
#read_f8be ⇒ Object
260 261 262 |
# File 'lib/kaitai/struct/struct.rb', line 260 def read_f8be read_bytes(8).unpack('G')[0] end |
#read_f8le ⇒ Object
272 273 274 |
# File 'lib/kaitai/struct/struct.rb', line 272 def read_f8le read_bytes(8).unpack('E')[0] end |
#read_s1 ⇒ Object
Signed
146 147 148 |
# File 'lib/kaitai/struct/struct.rb', line 146 def read_s1 read_bytes(1).unpack('c')[0] end |
#read_s2be ⇒ Object
.….….….….….….….….….….….….….….….….….… Big-endian .….….….….….….….….….….….….….….….….….…
154 155 156 |
# File 'lib/kaitai/struct/struct.rb', line 154 def read_s2be to_signed(read_u2be, SIGN_MASK_16) end |
#read_s2le ⇒ Object
.….….….….….….….….….….….….….….….….….… Little-endian .….….….….….….….….….….….….….….….….….…
176 177 178 |
# File 'lib/kaitai/struct/struct.rb', line 176 def read_s2le to_signed(read_u2le, SIGN_MASK_16) end |
#read_s4be ⇒ Object
158 159 160 |
# File 'lib/kaitai/struct/struct.rb', line 158 def read_s4be to_signed(read_u4be, SIGN_MASK_32) end |
#read_s4le ⇒ Object
180 181 182 |
# File 'lib/kaitai/struct/struct.rb', line 180 def read_s4le to_signed(read_u4le, SIGN_MASK_32) end |
#read_s8be ⇒ Object
163 164 165 |
# File 'lib/kaitai/struct/struct.rb', line 163 def read_s8be read_bytes(8).unpack('q')[0] end |
#read_s8le ⇒ Object
185 186 187 |
# File 'lib/kaitai/struct/struct.rb', line 185 def read_s8le read_bytes(8).unpack('q')[0] end |
#read_u1 ⇒ Object
Unsigned
198 199 200 |
# File 'lib/kaitai/struct/struct.rb', line 198 def read_u1 read_bytes(1).unpack('C')[0] end |
#read_u2be ⇒ Object
.….….….….….….….….….….….….….….….….….… Big-endian .….….….….….….….….….….….….….….….….….…
206 207 208 |
# File 'lib/kaitai/struct/struct.rb', line 206 def read_u2be read_bytes(2).unpack('n')[0] end |
#read_u2le ⇒ Object
.….….….….….….….….….….….….….….….….….… Little-endian .….….….….….….….….….….….….….….….….….…
229 230 231 |
# File 'lib/kaitai/struct/struct.rb', line 229 def read_u2le read_bytes(2).unpack('v')[0] end |
#read_u4be ⇒ Object
210 211 212 |
# File 'lib/kaitai/struct/struct.rb', line 210 def read_u4be read_bytes(4).unpack('N')[0] end |
#read_u4le ⇒ Object
233 234 235 |
# File 'lib/kaitai/struct/struct.rb', line 233 def read_u4le read_bytes(4).unpack('V')[0] end |
#read_u8be ⇒ Object
215 216 217 |
# File 'lib/kaitai/struct/struct.rb', line 215 def read_u8be read_bytes(8).unpack('Q')[0] end |
#read_u8le ⇒ Object
238 239 240 |
# File 'lib/kaitai/struct/struct.rb', line 238 def read_u8le read_bytes(8).unpack('Q')[0] end |
#seek(x) ⇒ Object
Set stream pointer to designated position.
126 |
# File 'lib/kaitai/struct/struct.rb', line 126 def seek(x); @_io.seek(x); end |
#size ⇒ Fixnum
Get total size of the stream in bytes.
136 |
# File 'lib/kaitai/struct/struct.rb', line 136 def size; @_io.size; end |