3
3
import struct
4
4
from io import open , BytesIO , SEEK_CUR , SEEK_END # noqa
5
5
6
- PY2 = sys .version_info [0 ] == 2
6
+ PY_OLD = sys .version_info [0 ] == 2 or ( sys . version_info [ 0 ] == 3 and sys . version_info [ 1 ] < 2 )
7
7
8
- # Kaitai Struct runtime streaming API version, defined as per PEP-0396
9
- # standard. Used for two purposes:
8
+ # Kaitai Struct runtime streaming API version, defined as per PEP-0396 standard.
9
+ # Used for two purposes:
10
10
#
11
- # * .py files generated by ksc from .ksy check that they import proper
12
- # KS runtime library by this version number;
11
+ # * .py files generated by ksc from .ksy check that they import proper KS runtime
12
+ # library by this version number;
13
13
# * distribution utils (setup.py) use this when packaging for PyPI
14
14
#
15
15
__version__ = '0.9'
@@ -38,14 +38,32 @@ def from_file(cls, filename):
38
38
f .close ()
39
39
raise
40
40
41
+ @classmethod
42
+ def to_file (cls , filename , data = None ):
43
+ f = open (filename , 'rb' )
44
+ try :
45
+ return cls (KaitaiStream (f ), _mode = 'w' , _data = data )
46
+ except Exception :
47
+ # close file descriptor, then reraise the exception
48
+ f .close ()
49
+ raise
50
+
41
51
@classmethod
42
52
def from_bytes (cls , buf ):
43
53
return cls (KaitaiStream (BytesIO (buf )))
44
54
55
+ @classmethod
56
+ def to_bytes (cls , buf , data = None ):
57
+ return cls (KaitaiStream (BytesIO (buf )), _mode = 'w' , _data = data )
58
+
45
59
@classmethod
46
60
def from_io (cls , io ):
47
61
return cls (KaitaiStream (io ))
48
62
63
+ @classmethod
64
+ def to_io (cls , io , data = None ):
65
+ return cls (KaitaiStream (io ), _mode = 'w' , _data = data )
66
+
49
67
50
68
class KaitaiStream (object ):
51
69
def __init__ (self , io ):
@@ -125,6 +143,9 @@ def size(self):
125
143
def read_s1 (self ):
126
144
return KaitaiStream .packer_s1 .unpack (self .read_bytes (1 ))[0 ]
127
145
146
+ def write_s1 (self , data ):
147
+ return self .write_bytes (KaitaiStream .packer_s1 .pack (data ))
148
+
128
149
# ........................................................................
129
150
# Big-endian
130
151
# ........................................................................
@@ -138,6 +159,15 @@ def read_s4be(self):
138
159
def read_s8be (self ):
139
160
return KaitaiStream .packer_s8be .unpack (self .read_bytes (8 ))[0 ]
140
161
162
+ def write_s2be (self , data ):
163
+ return self .write_bytes (KaitaiStream .packer_s2be .pack (data ))
164
+
165
+ def write_s4be (self , data ):
166
+ return self .write_bytes (KaitaiStream .packer_s4be .pack (data ))
167
+
168
+ def write_s8be (self , data ):
169
+ return self .write_bytes (KaitaiStream .packer_s8be .pack (data ))
170
+
141
171
# ........................................................................
142
172
# Little-endian
143
173
# ........................................................................
@@ -151,13 +181,25 @@ def read_s4le(self):
151
181
def read_s8le (self ):
152
182
return KaitaiStream .packer_s8le .unpack (self .read_bytes (8 ))[0 ]
153
183
184
+ def write_s2le (self , data ):
185
+ return self .write_bytes (KaitaiStream .packer_s2le .pack (data ))
186
+
187
+ def write_s4le (self , data ):
188
+ return self .write_bytes (KaitaiStream .packer_s4le .pack (data ))
189
+
190
+ def write_s8le (self , data ):
191
+ return self .write_bytes (KaitaiStream .packer_s8le .pack (data ))
192
+
154
193
# ------------------------------------------------------------------------
155
194
# Unsigned
156
195
# ------------------------------------------------------------------------
157
196
158
197
def read_u1 (self ):
159
198
return KaitaiStream .packer_u1 .unpack (self .read_bytes (1 ))[0 ]
160
199
200
+ def write_u1 (self , data ):
201
+ return self .write_bytes (KaitaiStream .packer_u1 .pack (data ))
202
+
161
203
# ........................................................................
162
204
# Big-endian
163
205
# ........................................................................
@@ -171,6 +213,15 @@ def read_u4be(self):
171
213
def read_u8be (self ):
172
214
return KaitaiStream .packer_u8be .unpack (self .read_bytes (8 ))[0 ]
173
215
216
+ def write_u2be (self , data ):
217
+ return self .write_bytes (KaitaiStream .packer_u2be .pack (data ))
218
+
219
+ def write_u4be (self , data ):
220
+ return self .write_bytes (KaitaiStream .packer_u4be .pack (data ))
221
+
222
+ def write_u8be (self , data ):
223
+ return self .write_bytes (KaitaiStream .packer_u8be .pack (data ))
224
+
174
225
# ........................................................................
175
226
# Little-endian
176
227
# ........................................................................
@@ -184,6 +235,15 @@ def read_u4le(self):
184
235
def read_u8le (self ):
185
236
return KaitaiStream .packer_u8le .unpack (self .read_bytes (8 ))[0 ]
186
237
238
+ def write_u2le (self , data ):
239
+ return self .write_bytes (KaitaiStream .packer_u2le .pack (data ))
240
+
241
+ def write_u4le (self , data ):
242
+ return self .write_bytes (KaitaiStream .packer_u4le .pack (data ))
243
+
244
+ def write_u8le (self , data ):
245
+ return self .write_bytes (KaitaiStream .packer_u8le .pack (data ))
246
+
187
247
# ========================================================================
188
248
# Floating point numbers
189
249
# ========================================================================
@@ -203,6 +263,12 @@ def read_f4be(self):
203
263
def read_f8be (self ):
204
264
return KaitaiStream .packer_f8be .unpack (self .read_bytes (8 ))[0 ]
205
265
266
+ def write_f4be (self , data ):
267
+ return self .write_bytes (KaitaiStream .packer_f4be .pack (data ))
268
+
269
+ def write_f8be (self , data ):
270
+ return self .write_bytes (KaitaiStream .packer_f8be .pack (data ))
271
+
206
272
# ........................................................................
207
273
# Little-endian
208
274
# ........................................................................
@@ -213,6 +279,12 @@ def read_f4le(self):
213
279
def read_f8le (self ):
214
280
return KaitaiStream .packer_f8le .unpack (self .read_bytes (8 ))[0 ]
215
281
282
+ def write_f4le (self , data ):
283
+ return self .write_bytes (KaitaiStream .packer_f4le .pack (data ))
284
+
285
+ def write_f8le (self , data ):
286
+ return self .write_bytes (KaitaiStream .packer_f8le .pack (data ))
287
+
216
288
# ========================================================================
217
289
# Unaligned bit values
218
290
# ========================================================================
@@ -279,57 +351,67 @@ def read_bits_int_le(self, n):
279
351
# Byte arrays
280
352
# ========================================================================
281
353
282
- def read_bytes (self , n ):
354
+ def alignment (self , a ):
355
+ return (a - self .pos ()) % a
356
+
357
+ def read_bytes (self , n , align = 0 ):
283
358
if n < 0 :
284
- raise ValueError (
285
- "requested invalid %d amount of bytes" %
286
- (n ,)
287
- )
359
+ raise ValueError ("%d is invalid amount of bytes" % n )
288
360
r = self ._io .read (n )
289
361
if len (r ) < n :
290
- raise EOFError (
291
- "requested %d bytes, but got only %d bytes" %
292
- (n , len (r ))
293
- )
362
+ raise EOFError ("got only %d bytes out of %d requested" % (n , len (r )))
363
+ if align > 1 :
364
+ self ._io .seek (self .alignment (align ), 1 )
294
365
return r
295
366
367
+ def write_bytes (self , data , align = 0 , pad = 0 , padding = b'\0 ' ):
368
+ if data is None :
369
+ return
370
+ nb = len (data )
371
+ if nb == 0 and align < 2 and pad < 1 :
372
+ return
373
+ if self ._io .write (data ) != nb :
374
+ raise Exception ("not all bytes written" )
375
+ if pad > 0 :
376
+ self ._io .write (padding * pad )
377
+ if align > 1 :
378
+ self ._io .write (padding * self .alignment (align ))
379
+ return
380
+
296
381
def read_bytes_full (self ):
297
382
return self ._io .read ()
298
383
299
- def read_bytes_term (self , term , include_term , consume_term , eos_error ):
384
+ def read_bytes_term (self , term , include_term = False , consume_term = True , eos_error = True , elem_size = 1 ):
300
385
r = b''
301
386
while True :
302
- c = self ._io .read (1 )
387
+ c = self ._io .read (elem_size )
303
388
if c == b'' :
304
389
if eos_error :
305
- raise Exception (
306
- "end of stream reached, but no terminator %d found" %
307
- (term ,)
308
- )
390
+ raise Exception ("end of stream reached, but no terminator (%d) found" % term )
309
391
else :
310
392
return r
311
393
elif ord (c ) == term :
312
394
if include_term :
313
395
r += c
314
396
if not consume_term :
315
- self ._io .seek (- 1 , SEEK_CUR )
397
+ self ._io .seek (- elem_size , SEEK_CUR )
316
398
return r
317
399
else :
318
400
r += c
319
401
402
+ def write_bytes_term (self , data , term = b'\0 ' , align = 0 ):
403
+ self .write_bytes (data , align = align , pad = 1 , padding = term )
404
+
320
405
def ensure_fixed_contents (self , expected ):
321
406
actual = self ._io .read (len (expected ))
322
407
if actual != expected :
323
- raise Exception (
324
- "unexpected fixed contents: got %r, was waiting for %r" %
325
- (actual , expected )
326
- )
408
+ raise Exception ("unexpected fixed contents: got %r, was waiting for %r" % (actual , expected ))
327
409
return actual
328
410
329
411
@staticmethod
330
- def bytes_strip_right (data , pad_byte ):
412
+ def bytes_strip_right (data , pad_byte = b' \0 ' ):
331
413
new_len = len (data )
332
- if PY2 :
414
+ if PY_OLD :
333
415
# data[...] must yield an integer, to compare with integer pad_byte
334
416
data = bytearray (data )
335
417
@@ -339,18 +421,18 @@ def bytes_strip_right(data, pad_byte):
339
421
return data [:new_len ]
340
422
341
423
@staticmethod
342
- def bytes_terminate (data , term , include_term ):
424
+ def bytes_terminate (data , term , include_term = True , elem_size = 1 ):
343
425
new_len = 0
344
426
max_len = len (data )
345
- if PY2 :
427
+ if PY_OLD :
346
428
# data[...] must yield an integer, to compare with integer term
347
429
data = bytearray (data )
348
430
349
431
while new_len < max_len and data [new_len ] != term :
350
- new_len += 1
432
+ new_len += elem_size
351
433
352
434
if include_term and new_len < max_len :
353
- new_len += 1
435
+ new_len += elem_size
354
436
355
437
return data [:new_len ]
356
438
@@ -360,14 +442,14 @@ def bytes_terminate(data, term, include_term):
360
442
361
443
@staticmethod
362
444
def process_xor_one (data , key ):
363
- if PY2 :
445
+ if PY_OLD :
364
446
return bytes (bytearray (v ^ key for v in bytearray (data )))
365
447
else :
366
448
return bytes (v ^ key for v in data )
367
449
368
450
@staticmethod
369
451
def process_xor_many (data , key ):
370
- if PY2 :
452
+ if PY_OLD :
371
453
return bytes (bytearray (a ^ b for a , b in zip (bytearray (data ), itertools .cycle (bytearray (key )))))
372
454
else :
373
455
return bytes (a ^ b for a , b in zip (data , itertools .cycle (key )))
@@ -393,10 +475,10 @@ def process_rotate_left(data, amount, group_size):
393
475
# ========================================================================
394
476
395
477
@staticmethod
396
- def int_from_byte (v ):
397
- if PY2 :
478
+ def int_from_byte (v , signed = False ):
479
+ if PY_OLD :
398
480
return ord (v )
399
- return v
481
+ return int . from_bytes ( v , signed = signed )
400
482
401
483
@staticmethod
402
484
def byte_array_index (data , i ):
0 commit comments