@@ -65,6 +65,7 @@ This module comes with the following constants:
65
65
"""
66
66
67
67
from libc.stdint cimport UINT64_MAX, UINT32_MAX
68
+ from libc.string cimport memmove, memcpy
68
69
from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free
69
70
from cpython.buffer cimport PyBUF_C_CONTIGUOUS, PyObject_GetBuffer, PyBuffer_Release
70
71
from cpython.bytes cimport PyBytes_FromStringAndSize
@@ -327,6 +328,170 @@ cdef bytes view_bitbuffer(inflate_state * stream):
327
328
return (read_in >> remainder).to_bytes(8 , " little" )[:read_in_length]
328
329
329
330
331
+ cdef class IgzipDecompressor:
332
+ """ Decompress object for handling streaming decompression."""
333
+ cdef public bytes unused_data
334
+ cdef public bint eof
335
+ cdef public bint needs_input
336
+ cdef inflate_state stream
337
+ cdef unsigned char * input_buffer
338
+ cdef size_t input_buffer_size
339
+ cdef Py_ssize_t avail_in_real
340
+
341
+ def __dealloc__ (self ):
342
+ if self .input_buffer != NULL :
343
+ PyMem_Free(self .input_buffer)
344
+
345
+ def __cinit__ (self ,
346
+ flag = ISAL_DEFLATE,
347
+ hist_bits = ISAL_DEF_MAX_HIST_BITS,
348
+ zdict = None ):
349
+ isal_inflate_init(& self .stream)
350
+
351
+ self .stream.hist_bits = hist_bits
352
+ self .stream.crc_flag = flag
353
+ cdef Py_ssize_t zdict_length
354
+ if zdict:
355
+ zdict_length = len (zdict)
356
+ if zdict_length > UINT32_MAX:
357
+ raise OverflowError (" zdict length does not fit in an unsigned int" )
358
+ err = isal_inflate_set_dict(& self .stream, zdict, zdict_length)
359
+ if err != COMP_OK:
360
+ check_isal_deflate_rc(err)
361
+ self .unused_data = b" "
362
+ self .eof = False
363
+ self .input_buffer = NULL
364
+ self .input_buffer_size = 0
365
+ self .avail_in_real = 0
366
+ self .needs_input = True
367
+
368
+ def _view_bitbuffer (self ):
369
+ """ Shows the 64-bitbuffer of the internal inflate_state. It contains
370
+ a maximum of 8 bytes. This data is already read-in so is not part
371
+ of the unconsumed tail."""
372
+ return view_bitbuffer(& self .stream)
373
+
374
+ cdef decompress_buf(self , Py_ssize_t max_length, unsigned char ** obuf):
375
+ obuf[0 ] = NULL
376
+ cdef Py_ssize_t obuflen = DEF_BUF_SIZE_I
377
+ cdef int err
378
+ if obuflen > max_length:
379
+ obuflen = max_length
380
+ while True :
381
+ obuflen = arrange_output_buffer_with_maximum(& self .stream, obuf, obuflen, max_length)
382
+ if obuflen == - 1 :
383
+ raise MemoryError (" Unsufficient memory for buffer allocation" )
384
+ elif obuflen == - 2 :
385
+ break
386
+ arrange_input_buffer(& self .stream, & self .avail_in_real)
387
+ err = isal_inflate(& self .stream)
388
+ self .avail_in_real += self .stream.avail_in
389
+ if err != ISAL_DECOMP_OK:
390
+ check_isal_inflate_rc(err)
391
+ if self .stream.block_state == ISAL_BLOCK_FINISH:
392
+ self .eof = 1
393
+ break
394
+ elif self .avail_in_real == 0 :
395
+ break
396
+ return
397
+
398
+ def decompress (self , data , Py_ssize_t max_length = - 1 ):
399
+ """
400
+ Decompress data, returning a bytes object containing the uncompressed
401
+ data corresponding to at least part of the data in string.
402
+ :param max_length: if non-zero then the return value will be no longer
403
+ than max_length. Unprocessed data will be in the
404
+ unconsumed_tail attribute.
405
+ """
406
+ if self .eof:
407
+ raise EOFError (" End of stream already reached" )
408
+ cdef bint input_buffer_in_use
409
+
410
+ cdef Py_ssize_t hard_limit
411
+ if max_length < 0 :
412
+ hard_limit = PY_SSIZE_T_MAX
413
+ else :
414
+ hard_limit = max_length
415
+
416
+ cdef unsigned int avail_now
417
+ cdef unsigned int avail_total
418
+ # Cython makes sure error is handled when acquiring buffer fails.
419
+ cdef Py_buffer buffer_data
420
+ cdef Py_buffer* buffer = & buffer_data
421
+ PyObject_GetBuffer(data, buffer , PyBUF_C_CONTIGUOUS)
422
+ cdef Py_ssize_t ibuflen = buffer .len
423
+ cdef unsigned char * data_ptr = < unsigned char * > buffer .buf
424
+
425
+
426
+ cdef bint max_length_reached = False
427
+ cdef unsigned char * tmp
428
+ cdef size_t offset
429
+ # Initialise output buffer
430
+ cdef unsigned char * obuf = NULL
431
+
432
+ try :
433
+ if self .stream.next_in != NULL :
434
+ avail_now = (self .input_buffer + self .input_buffer_size) - \
435
+ (self .stream.next_in + self .avail_in_real)
436
+ avail_total = self .input_buffer_size - self .avail_in_real
437
+ if avail_total < ibuflen:
438
+ offset = self .stream.next_in - self .input_buffer
439
+ new_size = self .input_buffer_size + ibuflen - avail_now
440
+ tmp = < unsigned char * > PyMem_Realloc(self .input_buffer, new_size)
441
+ if tmp == NULL :
442
+ raise MemoryError ()
443
+ self .input_buffer = tmp
444
+ self .input_buffer_size = new_size
445
+ self .stream.next_in = self .input_buffer + offset
446
+ elif avail_now < ibuflen:
447
+ memmove(self .input_buffer, self .stream.next_in,
448
+ self .avail_in_real)
449
+ self .stream.next_in = self .input_buffer
450
+ memcpy(< void * > (self .stream.next_in + self .avail_in_real), data_ptr, buffer .len)
451
+ self .avail_in_real += ibuflen
452
+ input_buffer_in_use = 1
453
+ else :
454
+ self .stream.next_in = data_ptr
455
+ self .avail_in_real = ibuflen
456
+ input_buffer_in_use = 0
457
+
458
+ self .decompress_buf(hard_limit, & obuf)
459
+ if obuf == NULL :
460
+ self .stream.next_in = NULL
461
+ return b" "
462
+ if self .eof:
463
+ self .needs_input = False
464
+ if self .avail_in_real > 0 :
465
+ new_data = PyBytes_FromStringAndSize(< char * > self .stream.next_in, self .avail_in_real)
466
+ self .unused_data = self ._view_bitbuffer() + new_data
467
+ elif self .avail_in_real == 0 :
468
+ self .stream.next_in = NULL
469
+ self .needs_input = True
470
+ else :
471
+ self .needs_input = False
472
+ if not input_buffer_in_use:
473
+ # Discard buffer if to small.
474
+ # Resizing may needlessly copy the current contents.
475
+ if self .input_buffer != NULL and self .input_buffer_size < self .avail_in_real:
476
+ PyMem_Free(self .input_buffer)
477
+ self .input_buffer = NULL
478
+
479
+ # Allocate of necessary
480
+ if self .input_buffer == NULL :
481
+ self .input_buffer = < unsigned char * > PyMem_Malloc(self .avail_in_real)
482
+ if self .input_buffer == NULL :
483
+ raise MemoryError ()
484
+ self .input_buffer_size = self .avail_in_real
485
+
486
+ # Copy tail
487
+ memcpy(self .input_buffer, self .stream.next_in, self .avail_in_real)
488
+ self .stream.next_in = self .input_buffer
489
+ return PyBytes_FromStringAndSize(< char * > obuf, self .stream.next_out - obuf)
490
+ finally :
491
+ PyBuffer_Release(buffer )
492
+ PyMem_Free(obuf)
493
+
494
+
330
495
cdef int mem_level_to_bufsize(int compression_level, int mem_level, unsigned int * bufsize):
331
496
"""
332
497
Convert zlib memory levels to isal equivalents
0 commit comments