@@ -28,6 +28,10 @@ from .crc cimport crc32_gzip_refl
28
28
from .igzip_lib cimport *
29
29
from libc.stdint cimport UINT64_MAX, UINT32_MAX, uint32_t
30
30
from cpython.mem cimport PyMem_Malloc, PyMem_Free
31
+ from cpython.buffer cimport PyBUF_READ, PyBUF_C_CONTIGUOUS, PyObject_GetBuffer, \
32
+ PyBuffer_Release
33
+
34
+
31
35
32
36
cdef extern from " <Python.h>" :
33
37
const Py_ssize_t PY_SSIZE_T_MAX
@@ -80,16 +84,28 @@ if ISAL_DEF_MAX_HIST_BITS > zlib.MAX_WBITS:
80
84
81
85
82
86
cpdef adler32(data, unsigned long value = 1 ):
83
- cdef Py_ssize_t length = len (data)
84
- if length > UINT64_MAX:
85
- raise ValueError (" Data too big for adler32" )
86
- return isal_adler32(value, data, length)
87
+ cdef Py_buffer buffer_data
88
+ cdef Py_buffer* buffer = & buffer_data
89
+ if PyObject_GetBuffer(data, buffer , PyBUF_READ & PyBUF_C_CONTIGUOUS) != 0 :
90
+ raise TypeError (" Failed to get buffer" )
91
+ try :
92
+ if buffer .len > UINT64_MAX:
93
+ raise ValueError (" Data too big for adler32" )
94
+ return isal_adler32(value, < unsigned char * > buffer .buf, buffer .len)
95
+ finally :
96
+ PyBuffer_Release(buffer )
87
97
88
98
cpdef crc32(data, unsigned long value = 0 ):
89
- cdef Py_ssize_t length = len (data)
90
- if length > UINT64_MAX:
91
- raise ValueError (" Data too big for crc32" )
92
- return crc32_gzip_refl(value, data, length)
99
+ cdef Py_buffer buffer_data
100
+ cdef Py_buffer* buffer = & buffer_data
101
+ if PyObject_GetBuffer(data, buffer , PyBUF_READ & PyBUF_C_CONTIGUOUS) != 0 :
102
+ raise TypeError (" Failed to get buffer" )
103
+ try :
104
+ if buffer .len > UINT64_MAX:
105
+ raise ValueError (" Data too big for adler32" )
106
+ return crc32_gzip_refl(value, < unsigned char * > buffer .buf, buffer .len)
107
+ finally :
108
+ PyBuffer_Release(buffer )
93
109
94
110
cdef Py_ssize_t Py_ssize_t_min(Py_ssize_t a, Py_ssize_t b):
95
111
if a <= b:
@@ -135,8 +151,12 @@ def compress(data,
135
151
out = []
136
152
137
153
# initialise input
138
- cdef Py_ssize_t ibuflen = len (data)
139
- cdef unsigned char * ibuf = data
154
+ cdef Py_buffer buffer_data
155
+ cdef Py_buffer* buffer = & buffer_data
156
+ if PyObject_GetBuffer(data, buffer , PyBUF_READ & PyBUF_C_CONTIGUOUS) != 0 :
157
+ raise TypeError (" Failed to get buffer" )
158
+ cdef Py_ssize_t ibuflen = buffer .len
159
+ cdef unsigned char * ibuf = < unsigned char * > buffer .buf
140
160
stream.next_in = ibuf
141
161
142
162
# initialise helper variables
@@ -175,6 +195,7 @@ def compress(data,
175
195
break
176
196
return b" " .join(out)
177
197
finally :
198
+ PyBuffer_Release(buffer )
178
199
PyMem_Free(level_buf)
179
200
PyMem_Free(obuf)
180
201
@@ -193,8 +214,12 @@ cpdef decompress(data,
193
214
& stream.crc_flag)
194
215
195
216
# initialise input
196
- cdef Py_ssize_t ibuflen = len (data)
197
- cdef unsigned char * ibuf = data
217
+ cdef Py_buffer buffer_data
218
+ cdef Py_buffer* buffer = & buffer_data
219
+ if PyObject_GetBuffer(data, buffer , PyBUF_READ & PyBUF_C_CONTIGUOUS) != 0 :
220
+ raise TypeError (" Failed to get buffer" )
221
+ cdef Py_ssize_t ibuflen = buffer .len
222
+ cdef unsigned char * ibuf = < unsigned char * > buffer .buf
198
223
stream.next_in = ibuf
199
224
200
225
# Initialise output buffer
@@ -234,6 +259,7 @@ cpdef decompress(data,
234
259
raise IsalError(" incomplete or truncated stream" )
235
260
return b" " .join(out)
236
261
finally :
262
+ PyBuffer_Release(buffer )
237
263
PyMem_Free(obuf)
238
264
239
265
@@ -302,36 +328,38 @@ cdef class Compress:
302
328
out = []
303
329
304
330
# initialise input
305
- cdef Py_ssize_t total_length = len (data)
306
- if total_length > UINT32_MAX:
307
- # Zlib allows a maximum of 64 KB (16-bit length) and python has
308
- # integrated workarounds in order to compress up to 64 bits
309
- # lengths. This comes at a cost however. Considering 4 GB should
310
- # be ample for streaming applications, the workaround is not
311
- # implemented here. (It is in the stand-alone compress function).
312
- raise OverflowError (" A maximum of 4 GB is allowed." )
313
- self .stream.next_in = data
314
- self .stream.avail_in = total_length
331
+ cdef Py_buffer buffer_data
332
+ cdef Py_buffer* buffer = & buffer_data
333
+ if PyObject_GetBuffer(data, buffer , PyBUF_READ & PyBUF_C_CONTIGUOUS) != 0 :
334
+ raise TypeError (" Failed to get buffer" )
335
+ cdef Py_ssize_t ibuflen = buffer .len
336
+ cdef unsigned char * ibuf = < unsigned char * > buffer .buf
337
+ self .stream.next_in = ibuf
315
338
316
339
# initialise helper variables
317
340
cdef int err
318
-
319
- # This loop reads all the input bytes. If there are no input bytes
320
- # anymore the output is written.
321
- while self .stream.avail_in != 0 :
322
- self .stream.next_out = self .obuf # Reset output buffer.
323
- self .stream.avail_out = self .obuflen
324
- err = isal_deflate(& self .stream)
325
- if err != COMP_OK:
326
- # There is some python interacting when possible exceptions
327
- # Are raised. So we remain in pure C code if we check for
328
- # COMP_OK first.
329
- check_isal_deflate_rc(err)
330
- # Instead of output buffer resizing as the zlibmodule.c example
331
- # the data is appended to a list.
332
- # TODO: Improve this with the buffer protocol.
333
- out.append(self .obuf[:self .obuflen - self .stream.avail_out])
334
- return b" " .join(out)
341
+ try :
342
+ while ibuflen != 0 :
343
+ # This loop runs n times (at least twice). n-1 times to fill the input
344
+ # buffer with data. The nth time the input is empty. In that case
345
+ # stream.flush is set to FULL_FLUSH and the end_of_stream is activated.
346
+ arrange_input_buffer(& self .stream, & ibuflen)
347
+ while self .stream.avail_in != 0 :
348
+ self .stream.next_out = self .obuf # Reset output buffer.
349
+ self .stream.avail_out = self .obuflen
350
+ err = isal_deflate(& self .stream)
351
+ if err != COMP_OK:
352
+ # There is some python interacting when possible exceptions
353
+ # Are raised. So we remain in pure C code if we check for
354
+ # COMP_OK first.
355
+ check_isal_deflate_rc(err)
356
+ # Instead of output buffer resizing as the zlibmodule.c example
357
+ # the data is appended to a list.
358
+ # TODO: Improve this with the buffer protocol.
359
+ out.append(self .obuf[:self .obuflen - self .stream.avail_out])
360
+ return b" " .join(out)
361
+ finally :
362
+ PyBuffer_Release(buffer )
335
363
336
364
def flush (self , int mode = FULL_FLUSH):
337
365
# Initialise stream
@@ -419,16 +447,14 @@ cdef class Decompress:
419
447
elif max_length < 0 :
420
448
raise ValueError (" max_length can not be smaller than 0" )
421
449
422
- cdef Py_ssize_t total_length = len (data)
423
- if total_length > UINT32_MAX:
424
- # Zlib allows a maximum of 64 KB (16-bit length) and python has
425
- # integrated workarounds in order to compress up to 64 bits
426
- # lengths. This comes at a cost however. Considering 4 GB should
427
- # be ample for streaming applications, the workaround is not
428
- # implemented here. (It is in the stand-alone compress function).
429
- raise OverflowError (" A maximum of 4 GB is allowed." )
430
- self .stream.next_in = data
431
- self .stream.avail_in = total_length
450
+ # initialise input
451
+ cdef Py_buffer buffer_data
452
+ cdef Py_buffer* buffer = & buffer_data
453
+ if PyObject_GetBuffer(data, buffer , PyBUF_READ & PyBUF_C_CONTIGUOUS) != 0 :
454
+ raise TypeError (" Failed to get buffer" )
455
+ cdef Py_ssize_t ibuflen = buffer .len
456
+ cdef unsigned char * ibuf = < unsigned char * > buffer .buf
457
+ self .stream.next_in = ibuf
432
458
self .stream.avail_out = 0
433
459
cdef unsigned long prev_avail_out
434
460
cdef unsigned long bytes_written
@@ -443,34 +469,36 @@ cdef class Decompress:
443
469
try :
444
470
# This loop reads all the input bytes. If there are no input bytes
445
471
# anymore the output is written.
446
- while (self .stream.avail_out == 0
447
- or self .stream.avail_in != 0 ):
448
- self .stream.next_out = obuf # Reset output buffer.
449
- if total_bytes >= max_length:
450
- break
451
- elif total_bytes + self .obuflen >= max_length:
452
- self .stream.avail_out = max_length - total_bytes
453
- # The inflate process may not fill all available bytes so
454
- # we make sure this is the last round.
455
- last_round = 1
456
- else :
457
- self .stream.avail_out = self .obuflen
458
- prev_avail_out = self .stream.avail_out
459
- err = isal_inflate(& self .stream)
460
- if err != ISAL_DECOMP_OK:
461
- # There is some python interacting when possible exceptions
462
- # Are raised. So we remain in pure C code if we check for
463
- # COMP_OK first.
464
- check_isal_inflate_rc(err)
465
- bytes_written = prev_avail_out - self .stream.avail_out
466
- total_bytes += bytes_written
467
- out.append(obuf[:bytes_written])
468
- if self .stream.block_state == ISAL_BLOCK_FINISH or last_round:
469
- break
472
+ while self .stream.block_state != ISAL_BLOCK_FINISH and ibuflen != 0 and not last_round:
473
+ arrange_input_buffer(& self .stream, & ibuflen)
474
+ while (self .stream.avail_out == 0 or self .stream.avail_in != 0 ):
475
+ self .stream.next_out = obuf # Reset output buffer.
476
+ if total_bytes >= max_length:
477
+ break
478
+ elif total_bytes + self .obuflen >= max_length:
479
+ self .stream.avail_out = max_length - total_bytes
480
+ # The inflate process may not fill all available bytes so
481
+ # we make sure this is the last round.
482
+ last_round = 1
483
+ else :
484
+ self .stream.avail_out = self .obuflen
485
+ prev_avail_out = self .stream.avail_out
486
+ err = isal_inflate(& self .stream)
487
+ if err != ISAL_DECOMP_OK:
488
+ # There is some python interacting when possible exceptions
489
+ # Are raised. So we remain in pure C code if we check for
490
+ # COMP_OK first.
491
+ check_isal_inflate_rc(err)
492
+ bytes_written = prev_avail_out - self .stream.avail_out
493
+ total_bytes += bytes_written
494
+ out.append(obuf[:bytes_written])
495
+ if self .stream.block_state == ISAL_BLOCK_FINISH or last_round:
496
+ break
470
497
self .save_unconsumed_input(data)
471
498
return b" " .join(out)
472
499
finally :
473
500
PyMem_Free(obuf)
501
+ PyBuffer_Release(buffer )
474
502
475
503
def flush (self , Py_ssize_t length = DEF_BUF_SIZE):
476
504
if length <= 0 :
0 commit comments