@@ -288,8 +288,75 @@ isal_zlib_crc32_combine(PyObject *module, PyObject *args) {
288
288
crc32_comb (crc1 , crc2 , crc2_length ) & 0xFFFFFFFF );
289
289
}
290
290
291
- PyDoc_STRVAR (isal_zlib_parallel_deflate_and_crc__doc__ ,
292
- "parallel_deflate_and_crc($module, data, zdict, /, level)\n"
291
+
292
+ typedef struct {
293
+ PyObject_HEAD
294
+ uint8_t * buffer ;
295
+ uint32_t buffer_size ;
296
+ struct isal_zstream zst ;
297
+ } ParallelCompress ;
298
+
299
+ static void
300
+ ParallelCompress_dealloc (ParallelCompress * self )
301
+ {
302
+ PyMem_Free (self -> buffer );
303
+ PyMem_Free (self -> zst .level_buf );
304
+ Py_TYPE (self )-> tp_free ((PyObject * )self );
305
+ }
306
+
307
+ static PyObject *
308
+ ParallelCompress__new__ (PyTypeObject * type , PyObject * args , PyObject * kwargs )
309
+ {
310
+ Py_ssize_t buffer_size = 0 ;
311
+ int level = ISAL_DEFAULT_COMPRESSION ;
312
+ static char * format = "n|i:ParallelCompress__new__" ;
313
+ static char * kwarg_names [] = {"buffersize" , "level" , NULL };
314
+ if (PyArg_ParseTupleAndKeywords (args , kwargs , format , kwarg_names ,
315
+ & buffer_size , & level ) < 0 ) {
316
+ return NULL ;
317
+ }
318
+ uint32_t level_buf_size ;
319
+ if (mem_level_to_bufsize (level , MEM_LEVEL_DEFAULT , & level_buf_size ) < 0 ) {
320
+ PyErr_Format (PyExc_ValueError , "Invalid compression level %d" , level );
321
+ return NULL ;
322
+ }
323
+ if (buffer_size > UINT32_MAX ) {
324
+ PyErr_Format (PyExc_ValueError ,
325
+ "buffersize must be at most %zd, got %zd" ,
326
+ (Py_ssize_t )UINT32_MAX , buffer_size );
327
+ }
328
+ ParallelCompress * self = PyObject_New (ParallelCompress , type );
329
+ if (self == NULL ) {
330
+ return PyErr_NoMemory ();
331
+ }
332
+ self -> buffer = NULL ;
333
+ self -> zst .level_buf = NULL ;
334
+ isal_deflate_init (& self -> zst );
335
+ uint8_t * level_buf = PyMem_Malloc (level_buf_size );
336
+ if (level_buf == NULL ) {
337
+ Py_DECREF (self );
338
+ return PyErr_NoMemory ();
339
+ }
340
+ uint8_t * buffer = PyMem_Malloc (buffer_size );
341
+ if (buffer == NULL ) {
342
+ Py_DECREF (self );
343
+ PyMem_Free (level_buf );
344
+ return PyErr_NoMemory ();
345
+ }
346
+ self -> buffer = buffer ;
347
+ self -> buffer_size = buffer_size ;
348
+ self -> zst .level_buf = level_buf ;
349
+ self -> zst .level_buf_size = level_buf_size ;
350
+ self -> zst .gzip_flag = IGZIP_DEFLATE ;
351
+ self -> zst .hist_bits = ISAL_DEF_MAX_HIST_BITS ;
352
+ self -> zst .level = (uint32_t )level ;
353
+ self -> zst .flush = SYNC_FLUSH ;
354
+ return (PyObject * )self ;
355
+ }
356
+
357
+
358
+ PyDoc_STRVAR (ParallelCompress_compress_and_crc__doc__ ,
359
+ "compress_and_crc($self, data, zdict, /)\n"
293
360
"--\n"
294
361
"\n"
295
362
"Function specifically designed for use in parallel compression. Data is \n"
@@ -301,28 +368,20 @@ PyDoc_STRVAR(isal_zlib_parallel_deflate_and_crc__doc__,
301
368
" bytes-like object containing the to be compressed data\n"
302
369
" zdict\n"
303
370
" last 32 bytes of the previous block\n"
304
- " level\n"
305
- " the compression level to use.\n"
306
371
);
307
- #define ISAL_ZLIB_PARALLEL_DEFLATE_AND_CRC_METHODDEF \
308
- { \
309
- "_parallel_deflate_and_crc", (PyCFunction)(void (*)(void))isal_zlib_parallel_deflate_and_crc, \
310
- METH_VARARGS | METH_KEYWORDS, isal_zlib_parallel_deflate_and_crc__doc__ \
311
- }
372
+ #define PARALLELCOMPRESS_COMPRESS_AND_CRC_METHODDEF \
373
+ { \
374
+ "compress_and_crc", (PyCFunction)ParallelCompress_compress_and_crc, \
375
+ METH_VARARGS, ParallelCompress_compress_and_crc__doc__}
312
376
313
377
static PyObject *
314
- isal_zlib_parallel_deflate_and_crc ( PyObject * module , PyObject * args , PyObject * kwargs )
378
+ ParallelCompress_compress_and_crc ( ParallelCompress * self , PyObject * args )
315
379
{
316
380
Py_buffer data ;
317
381
Py_buffer zdict ;
318
- int level = ISAL_DEFAULT_COMPRESSION ;
319
- static char * keywords [] = {"" , "" , "level" };
320
- static char * format = "y*y*|i:isal_zlib.parallel_deflate_and_crc" ;
321
- PyObject * out_bytes = NULL ;
322
- uint8_t * level_buf = NULL ;
382
+ static char * format = "y*y*:compress_and_crc" ;
323
383
324
- if (PyArg_ParseTupleAndKeywords (
325
- args , kwargs , format , keywords , & data , & zdict , & level ) < 0 ) {
384
+ if (PyArg_ParseTuple (args , format , & data , & zdict ) < 0 ) {
326
385
return NULL ;
327
386
}
328
387
@@ -331,84 +390,69 @@ isal_zlib_parallel_deflate_and_crc(PyObject *module, PyObject *args, PyObject *k
331
390
"Can only compress %d bytes of data" , UINT32_MAX );
332
391
goto error ;
333
392
}
334
-
335
- uint32_t level_buf_size ;
336
- if (mem_level_to_bufsize (level , MEM_LEVEL_DEFAULT , & level_buf_size ) != 0 ) {
337
- PyErr_SetString (IsalError , "Invalid compression level" );
338
- goto error ;
339
- }
340
-
341
- level_buf = (uint8_t * )PyMem_Malloc (level_buf_size );
342
- if (level_buf == NULL ) {
343
- PyErr_NoMemory ();
344
- goto error ;
345
- }
346
- // Assume output size < input_size. But just to be sure add 350 safety
347
- // bytes per 64K of input.
348
- Py_ssize_t output_size = data .len + (((data .len >> 16 ) + 1 ) * 350 );
349
- if (output_size > UINT32_MAX ) {
350
- PyErr_SetNone (PyExc_OverflowError );
351
- goto error ;
352
- }
353
- out_bytes = PyBytes_FromStringAndSize (NULL , output_size );
354
- if (out_bytes == NULL ) {
355
- PyErr_NoMemory ();
356
- goto error ;
357
- }
358
- uint8_t * out_ptr = (uint8_t * )PyBytes_AS_STRING (out_bytes );
359
- int err ;
360
- struct isal_zstream zst ;
361
- isal_deflate_init (& zst );
362
- zst .level = (uint32_t )level ;
363
- zst .level_buf = level_buf ;
364
- zst .level_buf_size = level_buf_size ;
365
- zst .hist_bits = ISAL_DEF_MAX_HIST_BITS ;
366
- zst .gzip_flag = IGZIP_DEFLATE ;
367
- zst .avail_in = data .len ;
368
- zst .next_in = data .buf ;
369
- zst .next_out = out_ptr ;
370
- zst .avail_out = output_size ;
371
- zst .flush = SYNC_FLUSH ;
372
- err = isal_deflate_set_dict (& zst , zdict .buf , zdict .len );
393
+ isal_deflate_reset (& self -> zst );
394
+ self -> zst .avail_in = data .len ;
395
+ self -> zst .next_in = data .buf ;
396
+ self -> zst .next_out = self -> buffer ;
397
+ self -> zst .avail_out = self -> buffer_size ;
398
+ PyThreadState * _save ;
399
+ Py_UNBLOCK_THREADS
400
+ int err = isal_deflate_set_dict (& self -> zst , zdict .buf , zdict .len );
373
401
if (err != 0 ){
402
+ Py_BLOCK_THREADS ;
374
403
isal_deflate_error (err );
375
- return NULL ;
404
+ goto error ;
376
405
}
377
- uint32_t crc ;
378
- Py_BEGIN_ALLOW_THREADS
379
- err = isal_deflate (& zst );
380
- crc = crc32_gzip_refl (0 , data .buf , data .len );
381
- Py_END_ALLOW_THREADS
406
+ err = isal_deflate (& self -> zst );
407
+ uint32_t crc = crc32_gzip_refl (0 , data .buf , data .len );
408
+ Py_BLOCK_THREADS ;
382
409
383
410
if (err != COMP_OK ) {
384
411
isal_deflate_error (err );
385
412
goto error ;
386
413
}
387
- if (zst .avail_in != 0 ) {
414
+ if (self -> zst .avail_out == 0 ) {
415
+ PyErr_Format (
416
+ PyExc_OverflowError ,
417
+ "Compressed output exceeds buffer size of %u" , self -> buffer_size
418
+ );
419
+ goto error ;
420
+ }
421
+ if (self -> zst .avail_in != 0 ) {
388
422
PyErr_Format (
389
423
PyExc_RuntimeError ,
390
424
"Developer error input bytes are still available: %u. "
391
425
"Please contact the developers by creating an issue at "
392
426
"https://github.com/pycompression/python-isal/issues" ,
393
- zst .avail_in );
427
+ self -> zst .avail_in );
394
428
goto error ;
395
429
}
396
-
397
- if (_PyBytes_Resize (& out_bytes , zst .next_out - out_ptr ) < 0 ) {
430
+ PyObject * out_bytes = PyBytes_FromStringAndSize (
431
+ (char * )self -> buffer , self -> zst .next_out - self -> buffer );
432
+ if (out_bytes == NULL ) {
398
433
goto error ;
399
434
}
400
- PyBuffer_Release (& data );
401
- PyBuffer_Release (& zdict );
402
- PyMem_Free (level_buf );
403
435
return Py_BuildValue ("(OI)" , out_bytes , crc );
404
436
error :
405
- PyMem_Free (level_buf );
406
- Py_XDECREF (out_bytes );
407
437
PyBuffer_Release (& data );
408
438
PyBuffer_Release (& zdict );
409
439
return NULL ;
410
440
}
411
441
442
+ static PyMethodDef ParallelCompress_methods [] = {
443
+ PARALLELCOMPRESS_COMPRESS_AND_CRC_METHODDEF ,
444
+ {NULL },
445
+ };
446
+
447
+ static PyTypeObject ParallelCompress_Type = {
448
+ .tp_name = "isal_zlib._ParallelCompress" ,
449
+ .tp_basicsize = sizeof (ParallelCompress ),
450
+ .tp_doc = PyDoc_STR (
451
+ "A reusable zstream and buffer fast parallel compression." ),
452
+ .tp_dealloc = (destructor )ParallelCompress_dealloc ,
453
+ .tp_new = ParallelCompress__new__ ,
454
+ .tp_methods = ParallelCompress_methods ,
455
+ };
412
456
413
457
PyDoc_STRVAR (zlib_compress__doc__ ,
414
458
"compress($module, data, /, level=ISAL_DEFAULT_COMPRESSION, wbits=MAX_WBITS)\n"
@@ -2094,7 +2138,6 @@ static PyMethodDef IsalZlibMethods[] = {
2094
2138
ISAL_ZLIB_ADLER32_METHODDEF ,
2095
2139
ISAL_ZLIB_CRC32_METHODDEF ,
2096
2140
ISAL_ZLIB_CRC32_COMBINE_METHODDEF ,
2097
- ISAL_ZLIB_PARALLEL_DEFLATE_AND_CRC_METHODDEF ,
2098
2141
ISAL_ZLIB_COMPRESS_METHODDEF ,
2099
2142
ISAL_ZLIB_DECOMPRESS_METHODDEF ,
2100
2143
ISAL_ZLIB_COMPRESSOBJ_METHODDEF ,
@@ -2178,6 +2221,15 @@ PyInit_isal_zlib(void)
2178
2221
return NULL ;
2179
2222
}
2180
2223
2224
+ if (PyType_Ready (& ParallelCompress_Type ) != 0 ) {
2225
+ return NULL ;
2226
+ }
2227
+ Py_INCREF (& ParallelCompress_Type );
2228
+ if (PyModule_AddObject (m , "_ParallelCompress" ,
2229
+ (PyObject * )& ParallelCompress_Type ) < 0 ) {
2230
+ return NULL ;
2231
+ }
2232
+
2181
2233
PyModule_AddIntConstant (m , "MAX_WBITS" , ISAL_DEF_MAX_HIST_BITS );
2182
2234
PyModule_AddIntConstant (m , "DEFLATED" , Z_DEFLATED );
2183
2235
PyModule_AddIntMacro (m , DEF_MEM_LEVEL );
0 commit comments