Skip to content

Commit 01b1e02

Browse files
authored
Merge pull request #126 from pycompression/review
Small performance and style refactorings
2 parents 9d0d217 + 9084c98 commit 01b1e02

File tree

5 files changed

+203
-153
lines changed

5 files changed

+203
-153
lines changed

CHANGELOG.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ Changelog
77
.. This document is user facing. Please word the changes in such a way
88
.. that users understand how the changes affect the new version.
99
10+
version 1.1.0-dev
11+
-----------------
12+
+ Small performance-oriented refactorings: argument parsing formats and
13+
keywords are now static, some very small functions are now declared inline
14+
and struct ordering was changed.
15+
1016
version 1.0.1
1117
------------------
1218
+ Fixed failing tests and wheel builds for PyPy.

src/isal/_isalmodule.c

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
// Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
2-
// 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022
3-
// Python Software Foundation; All Rights Reserved
1+
/*
2+
Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
3+
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022
4+
Python Software Foundation; All Rights Reserved
45
5-
// This file is part of python-isal which is distributed under the
6-
// PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2.
6+
This file is part of python-isal which is distributed under the
7+
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2.
78
8-
// This file is not originally from the CPython distribution. But it does contain mostly example code
9-
// from the Python docs. Also dual licensing just for this one file seemed silly.
9+
This file is not originally from the CPython distribution. But it does
10+
contain mostly example code from the Python docs. Also dual licensing just
11+
for this one file seemed silly.
12+
*/
1013

1114
#define PY_SSIZE_T_CLEAN
1215
#include <Python.h>
@@ -27,17 +30,18 @@ PyInit__isal(void)
2730
PyObject *m;
2831

2932
m = PyModule_Create(&_isal_module);
30-
if (m == NULL)
33+
if (m == NULL) {
3134
return NULL;
32-
35+
}
3336
PyModule_AddIntMacro(m, ISAL_MAJOR_VERSION);
3437
PyModule_AddIntMacro(m, ISAL_MINOR_VERSION);
3538
PyModule_AddIntMacro(m, ISAL_PATCH_VERSION);
3639

3740
PyObject *isal_version = PyUnicode_FromFormat(
3841
"%d.%d.%d", ISAL_MAJOR_VERSION, ISAL_MINOR_VERSION, ISAL_PATCH_VERSION);
39-
if (isal_version == NULL)
42+
if (isal_version == NULL) {
4043
return NULL;
44+
}
4145
PyModule_AddObject(m, "ISAL_VERSION", isal_version);
4246
return m;
4347
}

src/isal/igzip_libmodule.c

Lines changed: 63 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,47 @@
1-
// Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
2-
// 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022
3-
// Python Software Foundation; All Rights Reserved
4-
5-
// This file is part of python-isal which is distributed under the
6-
// PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2.
7-
8-
// This file was modified from Cpython Modules/bz2module.c file from the 3.9
9-
// branch.
10-
11-
// Changes compared to CPython:
12-
// - The BZ2Decompressor has been used as a basis for IgzipDecompressor.
13-
// Functionality is almost the same. IgzipDecompressor does have a more
14-
// elaborate __init__ to set settings. It also implements decompress_buf more
15-
// akin to how decompression is implemented in isal_shared.h
16-
// - Constants were added that are particular to igzip_lib.
17-
// - Argument parsers were written using th CPython API rather than argument
18-
// clinic.
1+
/*
2+
Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
3+
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022
4+
Python Software Foundation; All Rights Reserved
5+
6+
This file is part of python-isal which is distributed under the
7+
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2.
8+
9+
This file was modified from Cpython Modules/bz2module.c file from the 3.9
10+
branch.
11+
12+
Changes compared to CPython:
13+
- The BZ2Decompressor has been used as a basis for IgzipDecompressor.
14+
Functionality is almost the same. IgzipDecompressor does have a more
15+
elaborate __init__ to set settings. It also implements decompress_buf more
16+
akin to how decompression is implemented in isal_shared.h
17+
- Constants were added that are particular to igzip_lib.
18+
- Argument parsers were written using th CPython API rather than argument
19+
clinic.
20+
*/
1921

2022
#include "isal_shared.h"
2123

2224
typedef struct {
2325
PyObject_HEAD
24-
struct inflate_state state;
25-
char eof; /* T_BOOL expects a char */
2626
PyObject *unused_data;
2727
PyObject *zdict;
28-
char needs_input;
2928
uint8_t *input_buffer;
3029
Py_ssize_t input_buffer_size;
31-
3230
/* inflate_state>avail_in is only 32 bit, so we store the true length
3331
separately. Conversion and looping is encapsulated in
3432
decompress_buf() */
3533
Py_ssize_t avail_in_real;
34+
char eof; /* T_BOOL expects a char */
35+
char needs_input;
36+
/* Struct inflate state contains a massive buffer at the end. Put it at
37+
the end of the IgzipDecompressor so members can be accessed easily. */
38+
struct inflate_state state;
3639
} IgzipDecompressor;
3740

3841
static void
3942
IgzipDecompressor_dealloc(IgzipDecompressor *self)
4043
{
41-
if(self->input_buffer != NULL)
42-
PyMem_Free(self->input_buffer);
44+
PyMem_Free(self->input_buffer);
4345
Py_CLEAR(self->unused_data);
4446
Py_CLEAR(self->zdict);
4547
Py_TYPE(self)->tp_free((PyObject *)self);
@@ -62,16 +64,16 @@ decompress_buf(IgzipDecompressor *self, Py_ssize_t max_length)
6264

6365
int err;
6466

65-
// In Python 3.10 sometimes sys.maxsize is passed by default. In those cases
66-
// we do want to use DEF_BUF_SIZE as start buffer.
67+
/* In Python 3.10 sometimes sys.maxsize is passed by default. In those cases
68+
we do want to use DEF_BUF_SIZE as start buffer. */
6769
if ((max_length < 0) || max_length == PY_SSIZE_T_MAX) {
6870
hard_limit = PY_SSIZE_T_MAX;
6971
obuflen = DEF_BUF_SIZE;
7072
} else {
71-
// Assume that decompressor is used in file decompression with a fixed
72-
// block size of max_length. In that case we will reach max_length almost
73-
// always (except at the end of the file). So it makes sense to allocate
74-
// max_length.
73+
/* Assume that decompressor is used in file decompression with a fixed
74+
block size of max_length. In that case we will reach max_length almost
75+
always (except at the end of the file). So it makes sense to allocate
76+
max_length. */
7577
hard_limit = max_length;
7678
obuflen = max_length;
7779
if (obuflen > DEF_MAX_INITIAL_BUF_SIZE){
@@ -102,8 +104,10 @@ decompress_buf(IgzipDecompressor *self, Py_ssize_t max_length)
102104
isal_inflate_error(err);
103105
goto error;
104106
}
105-
} while (self->state.avail_out == 0 && self->state.block_state != ISAL_BLOCK_FINISH);
106-
} while(self->avail_in_real != 0 && self->state.block_state != ISAL_BLOCK_FINISH);
107+
} while (self->state.avail_out == 0 &&
108+
self->state.block_state != ISAL_BLOCK_FINISH);
109+
} while(self->avail_in_real != 0 &&
110+
self->state.block_state != ISAL_BLOCK_FINISH);
107111

108112
if (self->state.block_state == ISAL_BLOCK_FINISH)
109113
self->eof = 1;
@@ -189,7 +193,8 @@ decompress(IgzipDecompressor *self, uint8_t *data, size_t len, Py_ssize_t max_le
189193
goto error;
190194
char * new_data_ptr = PyBytes_AS_STRING(new_data);
191195
bitbuffer_copy(&(self->state), new_data_ptr, bytes_in_bitbuffer);
192-
memcpy(new_data_ptr + bytes_in_bitbuffer, self->state.next_in, self->avail_in_real);
196+
memcpy(new_data_ptr + bytes_in_bitbuffer, self->state.next_in,
197+
self->avail_in_real);
193198
Py_XSETREF(self->unused_data, new_data);
194199
}
195200
}
@@ -257,13 +262,14 @@ PyDoc_STRVAR(igzip_lib_compress__doc__,
257262
" the header and trailer are controlled by the flag parameter.");
258263

259264
#define IGZIP_LIB_COMPRESS_METHODDEF \
260-
{"compress", (PyCFunction)(void(*)(void))igzip_lib_compress, METH_VARARGS|METH_KEYWORDS, igzip_lib_compress__doc__}
265+
{"compress", (PyCFunction)(void(*)(void))igzip_lib_compress, \
266+
METH_VARARGS|METH_KEYWORDS, igzip_lib_compress__doc__}
261267

262268
static PyObject *
263269
igzip_lib_compress(PyObject *module, PyObject *args, PyObject *kwargs)
264270
{
265-
char *keywords[] = {"", "level", "flag", "mem_level", "hist_bits", NULL};
266-
char *format ="y*|iiii:compress";
271+
static char *keywords[] = {"", "level", "flag", "mem_level", "hist_bits", NULL};
272+
static char *format ="y*|iiii:compress";
267273
Py_buffer data = {NULL, NULL};
268274
int level = ISAL_DEFAULT_COMPRESSION;
269275
int flag = COMP_DEFLATE;
@@ -298,13 +304,14 @@ PyDoc_STRVAR(igzip_lib_decompress__doc__,
298304
" The initial output buffer size.");
299305

300306
#define IGZIP_LIB_DECOMPRESS_METHODDEF \
301-
{"decompress", (PyCFunction)(void(*)(void))igzip_lib_decompress, METH_VARARGS|METH_KEYWORDS, igzip_lib_decompress__doc__}
307+
{"decompress", (PyCFunction)(void(*)(void))igzip_lib_decompress, \
308+
METH_VARARGS|METH_KEYWORDS, igzip_lib_decompress__doc__}
302309

303310
static PyObject *
304311
igzip_lib_decompress(PyObject *module, PyObject *args, PyObject *kwargs)
305312
{
306-
char *keywords[] = {"", "flag", "hist_bits", "bufsize", NULL};
307-
char *format ="y*|iin:decompress";
313+
static char *keywords[] = {"", "flag", "hist_bits", "bufsize", NULL};
314+
static char *format ="y*|iin:decompress";
308315
Py_buffer data = {NULL, NULL};
309316
int flag = DECOMP_DEFLATE;
310317
int hist_bits = ISAL_DEF_MAX_HIST_BITS;
@@ -315,7 +322,7 @@ igzip_lib_decompress(PyObject *module, PyObject *args, PyObject *kwargs)
315322
&data, &flag, &hist_bits, &bufsize)) {
316323
return NULL;
317324
}
318-
PyObject * return_value = igzip_lib_decompress_impl(&data, flag, hist_bits, bufsize);
325+
PyObject *return_value = igzip_lib_decompress_impl(&data, flag, hist_bits, bufsize);
319326
PyBuffer_Release(&data);
320327
return return_value;
321328
}
@@ -340,13 +347,16 @@ PyDoc_STRVAR(igzip_lib_IgzipDecompressor_decompress__doc__,
340347
"the unused_data attribute.");
341348

342349
#define IGZIP_LIB_IGZIPDECOMPRESSOR_DECOMPRESS_METHODDEF \
343-
{"decompress", (PyCFunction)(void(*)(void))igzip_lib_IgzipDecompressor_decompress, METH_VARARGS|METH_KEYWORDS, igzip_lib_IgzipDecompressor_decompress__doc__}
350+
{"decompress", (PyCFunction)(void(*)(void))igzip_lib_IgzipDecompressor_decompress, \
351+
METH_VARARGS|METH_KEYWORDS, igzip_lib_IgzipDecompressor_decompress__doc__}
344352

345353
static PyObject *
346-
igzip_lib_IgzipDecompressor_decompress(IgzipDecompressor *self, PyObject *args, PyObject *kwargs)
354+
igzip_lib_IgzipDecompressor_decompress(IgzipDecompressor *self,
355+
PyObject *args,
356+
PyObject *kwargs)
347357
{
348-
char *keywords[] = {"", "max_length", NULL};
349-
char *format = "y*|n:decompress";
358+
static char *keywords[] = {"", "max_length", NULL};
359+
static char *format = "y*|n:decompress";
350360
Py_buffer data = {NULL, NULL};
351361
Py_ssize_t max_length = -1;
352362

@@ -383,8 +393,8 @@ igzip_lib_IgzipDecompressor__new__(PyTypeObject *type,
383393
PyObject *args,
384394
PyObject *kwargs)
385395
{
386-
char *keywords[] = {"flag", "hist_bits", "zdict", NULL};
387-
char *format = "|iiO:IgzipDecompressor";
396+
static char *keywords[] = {"flag", "hist_bits", "zdict", NULL};
397+
static char *format = "|iiO:IgzipDecompressor";
388398
int flag = ISAL_DEFLATE;
389399
int hist_bits = ISAL_DEF_MAX_HIST_BITS;
390400
PyObject *zdict = NULL;
@@ -458,8 +468,9 @@ static PyMemberDef IgzipDecompressor_members[] = {
458468
READONLY, IgzipDecompressor_unused_data__doc__},
459469
{"needs_input", T_BOOL, offsetof(IgzipDecompressor, needs_input), READONLY,
460470
IgzipDecompressor_needs_input_doc},
461-
{"crc", T_UINT, offsetof(IgzipDecompressor, state) + offsetof(struct inflate_state, crc), READONLY,
462-
IgzipDecompressor_crc_doc},
471+
{"crc", T_UINT,
472+
offsetof(IgzipDecompressor, state) + offsetof(struct inflate_state, crc),
473+
READONLY, IgzipDecompressor_crc_doc},
463474
{NULL}
464475
};
465476

@@ -580,10 +591,12 @@ PyInit_igzip_lib(void)
580591
return NULL;
581592
}
582593

583-
if (PyType_Ready(&IgzipDecompressor_Type) != 0)
594+
if (PyType_Ready(&IgzipDecompressor_Type) != 0) {
584595
return NULL;
596+
}
585597
Py_INCREF(&IgzipDecompressor_Type);
586-
if (PyModule_AddObject(m, "IgzipDecompressor", (PyObject *)&IgzipDecompressor_Type) < 0) {
598+
if (PyModule_AddObject(m, "IgzipDecompressor",
599+
(PyObject *)&IgzipDecompressor_Type) < 0) {
587600
return NULL;
588601
}
589602

src/isal/isal_shared.h

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
1-
// Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
2-
// 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022
3-
// Python Software Foundation; All Rights Reserved
4-
5-
// This file is part of python-isal which is distributed under the
6-
// PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2.
7-
8-
// This file was modified from Cpython Modules/zlibmodule.c file from the 3.9
9-
// branch. This is because the BlocksBuffer used in Python 3.10 and higher is
10-
// not available in python 3.7-3.9 which this project supports.
11-
12-
// Changes compared to CPython:
13-
// - igzip_lib.compress and igzip_lib.decompress are equivalent to
14-
// zlib.compress and zlib.decompress except that these use a 'flag' and
15-
// 'hist_bits' argument to set compression headers and trailers and window
16-
// size respectively. The igzip_lib functions also offer more control by
17-
// allowing to set no header, but include the trailer.
18-
// - This file also includes some utility functions to set parameters on ISA-L
19-
// structs.
20-
1+
/*
2+
Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
3+
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022
4+
Python Software Foundation; All Rights Reserved
5+
6+
This file is part of python-isal which is distributed under the
7+
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2.
8+
9+
This file was modified from Cpython Modules/zlibmodule.c file from the 3.9
10+
branch. This is because the BlocksBuffer used in Python 3.10 and higher is
11+
not available in python 3.7-3.9 which this project supports.
12+
13+
Changes compared to CPython:
14+
- igzip_lib.compress and igzip_lib.decompress are equivalent to
15+
zlib.compress and zlib.decompress except that these use a 'flag' and
16+
'hist_bits' argument to set compression headers and trailers and window
17+
size respectively. The igzip_lib functions also offer more control by
18+
allowing to set no header, but include the trailer.
19+
- This file also includes some utility functions to set parameters on ISA-L
20+
structs.
21+
*/
2122
#define PY_SSIZE_T_CLEAN
2223
#include <Python.h>
2324
#include "structmember.h" // PyMemberDef
@@ -82,10 +83,11 @@ static const uint32_t LEVEL_BUF_SIZES[24] = {
8283
ISAL_DEF_LVL3_EXTRA_LARGE
8384
};
8485

85-
static int mem_level_to_bufsize(int compression_level, int mem_level,
86+
static inline int mem_level_to_bufsize(int compression_level, int mem_level,
8687
uint32_t * bufsize)
8788
{
88-
if (compression_level < 0 || compression_level > 3 || mem_level < MEM_LEVEL_DEFAULT || mem_level > MEM_LEVEL_EXTRA_LARGE) {
89+
if (compression_level < 0 || compression_level > 3 ||
90+
mem_level < MEM_LEVEL_DEFAULT || mem_level > MEM_LEVEL_EXTRA_LARGE) {
8991
*bufsize = 0; return -1;
9092
}
9193
*bufsize = LEVEL_BUF_SIZES[compression_level * 6 + mem_level];
@@ -121,7 +123,8 @@ static void isal_inflate_error(int err){
121123
else if (err == ISAL_INVALID_SYMBOL) msg = "Invalid deflate symbol found";
122124
else if (err == ISAL_INVALID_LOOKBACK) msg = "Invalid lookback distance found";
123125
else if (err == ISAL_INVALID_WRAPPER) msg = "Invalid gzip/zlib wrapper found";
124-
else if (err == ISAL_UNSUPPORTED_METHOD) msg = "Gzip/zlib wrapper specifies unsupported compress method";
126+
else if (err == ISAL_UNSUPPORTED_METHOD) msg = "Gzip/zlib wrapper specifies"
127+
"unsupported compress method";
125128
else if (err == ISAL_INCORRECT_CHECKSUM) msg = "Incorrect checksum found";
126129
else msg = "Unknown error";
127130

@@ -135,7 +138,7 @@ static void isal_inflate_error(int err){
135138
* @param state An inflate_state
136139
* @return size_t
137140
*/
138-
static size_t bitbuffer_size(struct inflate_state *state){
141+
static inline size_t bitbuffer_size(struct inflate_state *state){
139142
return state->read_in_length / 8;
140143
}
141144

@@ -162,7 +165,7 @@ static int bitbuffer_copy(struct inflate_state *state, char *to, size_t n){
162165
return 0;
163166
}
164167

165-
static void
168+
static inline void
166169
arrange_input_buffer(uint32_t *avail_in, Py_ssize_t *remains)
167170
{
168171
*avail_in = (uint32_t)Py_MIN((size_t)*remains, UINT32_MAX);
@@ -208,7 +211,7 @@ arrange_output_buffer_with_maximum(uint32_t *avail_out,
208211
return length;
209212
}
210213

211-
static Py_ssize_t
214+
static inline Py_ssize_t
212215
arrange_output_buffer(uint32_t *avail_out,
213216
uint8_t **next_out,
214217
PyObject **buffer,
@@ -268,7 +271,8 @@ igzip_lib_compress_impl(Py_buffer *data,
268271
else zst.flush = NO_FLUSH;
269272

270273
do {
271-
obuflen = arrange_output_buffer(&(zst.avail_out), &(zst.next_out), &RetVal, obuflen);
274+
obuflen = arrange_output_buffer(&(zst.avail_out), &(zst.next_out),
275+
&RetVal, obuflen);
272276
if (obuflen < 0) {
273277
PyErr_SetString(PyExc_MemoryError,
274278
"Unsufficient memory for buffer allocation");

0 commit comments

Comments
 (0)