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: UndecidedEndiannessError, 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.
101 102 103 104 105 106 107 108 109 110 |
# File 'lib/kaitai/struct/struct.rb', line 101 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
385 386 387 388 389 390 391 392 |
# File 'lib/kaitai/struct/struct.rb', line 385 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
394 395 396 397 398 399 400 401 402 |
# File 'lib/kaitai/struct/struct.rb', line 394 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.
116 117 118 |
# File 'lib/kaitai/struct/struct.rb', line 116 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.
461 462 463 464 465 466 467 468 469 470 471 472 473 474 |
# File 'lib/kaitai/struct/struct.rb', line 461 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
437 438 439 440 441 442 443 444 445 446 447 448 449 450 |
# File 'lib/kaitai/struct/struct.rb', line 437 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
416 417 418 419 420 421 422 423 424 425 |
# File 'lib/kaitai/struct/struct.rb', line 416 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.
481 482 483 |
# File 'lib/kaitai/struct/struct.rb', line 481 def self.resolve_enum(enum_map, value) enum_map[value] || value end |
Instance Method Details
#align_to_byte ⇒ Object
287 288 289 290 |
# File 'lib/kaitai/struct/struct.rb', line 287 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.
378 379 380 381 382 383 |
# File 'lib/kaitai/struct/struct.rb', line 378 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.
128 |
# File 'lib/kaitai/struct/struct.rb', line 128 def eof?; @_io.eof?; end |
#pos ⇒ Fixnum
Get current position of a stream pointer.
138 |
# File 'lib/kaitai/struct/struct.rb', line 138 def pos; @_io.pos; end |
#read_bits_int(n) ⇒ Object
292 293 294 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 |
# File 'lib/kaitai/struct/struct.rb', line 292 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.
332 333 334 335 336 337 338 339 340 341 |
# File 'lib/kaitai/struct/struct.rb', line 332 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.
346 347 348 |
# File 'lib/kaitai/struct/struct.rb', line 346 def read_bytes_full @_io.read end |
#read_bytes_term(term, include_term, consume_term, eos_error) ⇒ Object
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 |
# File 'lib/kaitai/struct/struct.rb', line 350 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
263 264 265 |
# File 'lib/kaitai/struct/struct.rb', line 263 def read_f4be read_bytes(4).unpack('g')[0] end |
#read_f4le ⇒ Object
Little-endian
275 276 277 |
# File 'lib/kaitai/struct/struct.rb', line 275 def read_f4le read_bytes(4).unpack('e')[0] end |
#read_f8be ⇒ Object
267 268 269 |
# File 'lib/kaitai/struct/struct.rb', line 267 def read_f8be read_bytes(8).unpack('G')[0] end |
#read_f8le ⇒ Object
279 280 281 |
# File 'lib/kaitai/struct/struct.rb', line 279 def read_f8le read_bytes(8).unpack('E')[0] end |
#read_s1 ⇒ Object
Signed
153 154 155 |
# File 'lib/kaitai/struct/struct.rb', line 153 def read_s1 read_bytes(1).unpack('c')[0] end |
#read_s2be ⇒ Object
.….….….….….….….….….….….….….….….….….… Big-endian .….….….….….….….….….….….….….….….….….…
161 162 163 |
# File 'lib/kaitai/struct/struct.rb', line 161 def read_s2be to_signed(read_u2be, SIGN_MASK_16) end |
#read_s2le ⇒ Object
.….….….….….….….….….….….….….….….….….… Little-endian .….….….….….….….….….….….….….….….….….…
183 184 185 |
# File 'lib/kaitai/struct/struct.rb', line 183 def read_s2le to_signed(read_u2le, SIGN_MASK_16) end |
#read_s4be ⇒ Object
165 166 167 |
# File 'lib/kaitai/struct/struct.rb', line 165 def read_s4be to_signed(read_u4be, SIGN_MASK_32) end |
#read_s4le ⇒ Object
187 188 189 |
# File 'lib/kaitai/struct/struct.rb', line 187 def read_s4le to_signed(read_u4le, SIGN_MASK_32) end |
#read_s8be ⇒ Object
170 171 172 |
# File 'lib/kaitai/struct/struct.rb', line 170 def read_s8be read_bytes(8).unpack('q')[0] end |
#read_s8le ⇒ Object
192 193 194 |
# File 'lib/kaitai/struct/struct.rb', line 192 def read_s8le read_bytes(8).unpack('q')[0] end |
#read_u1 ⇒ Object
Unsigned
205 206 207 |
# File 'lib/kaitai/struct/struct.rb', line 205 def read_u1 read_bytes(1).unpack('C')[0] end |
#read_u2be ⇒ Object
.….….….….….….….….….….….….….….….….….… Big-endian .….….….….….….….….….….….….….….….….….…
213 214 215 |
# File 'lib/kaitai/struct/struct.rb', line 213 def read_u2be read_bytes(2).unpack('n')[0] end |
#read_u2le ⇒ Object
.….….….….….….….….….….….….….….….….….… Little-endian .….….….….….….….….….….….….….….….….….…
236 237 238 |
# File 'lib/kaitai/struct/struct.rb', line 236 def read_u2le read_bytes(2).unpack('v')[0] end |
#read_u4be ⇒ Object
217 218 219 |
# File 'lib/kaitai/struct/struct.rb', line 217 def read_u4be read_bytes(4).unpack('N')[0] end |
#read_u4le ⇒ Object
240 241 242 |
# File 'lib/kaitai/struct/struct.rb', line 240 def read_u4le read_bytes(4).unpack('V')[0] end |
#read_u8be ⇒ Object
222 223 224 |
# File 'lib/kaitai/struct/struct.rb', line 222 def read_u8be read_bytes(8).unpack('Q')[0] end |
#read_u8le ⇒ Object
245 246 247 |
# File 'lib/kaitai/struct/struct.rb', line 245 def read_u8le read_bytes(8).unpack('Q')[0] end |
#seek(x) ⇒ Object
Set stream pointer to designated position.
133 |
# File 'lib/kaitai/struct/struct.rb', line 133 def seek(x); @_io.seek(x); end |
#size ⇒ Fixnum
Get total size of the stream in bytes.
143 |
# File 'lib/kaitai/struct/struct.rb', line 143 def size; @_io.size; end |