@@ -62,6 +62,8 @@ typedef unsigned int uint32_t;
62
62
#define inline
63
63
#endif
64
64
65
+ static const char * capsule_name = "_frame.LZ4F_cctx" ;
66
+ static void destruct_compression_context (PyObject * py_context );
65
67
struct compression_context
66
68
{
67
69
LZ4F_compressionContext_t compression_context ;
@@ -114,57 +116,26 @@ create_compression_context (PyObject * Py_UNUSED (self),
114
116
return NULL ;
115
117
}
116
118
117
- return PyCapsule_New (context , NULL , NULL );
119
+ return PyCapsule_New (context , capsule_name , destruct_compression_context );
118
120
}
119
121
120
- /****************************
121
- * free_compression_context *
122
- ****************************/
123
- PyDoc_STRVAR (free_compression_context__doc ,
124
- "free_compression_context(context)\n\n" \
125
- "Releases the resources held by a compression context previously\n" \
126
- "created with create_compression_context.\n\n" \
127
- "Args:\n" \
128
- " context (cCtx): Compression context.\n"
129
- );
130
-
131
- static PyObject *
132
- free_compression_context (PyObject * Py_UNUSED (self ), PyObject * args ,
133
- PyObject * keywds )
122
+ static void
123
+ destruct_compression_context (PyObject * py_context )
134
124
{
135
- PyObject * py_context = NULL ;
136
- static char * kwlist [] = { "context" , NULL };
137
- struct compression_context * context ;
138
- LZ4F_errorCode_t result ;
139
-
140
- if (!PyArg_ParseTupleAndKeywords (args , keywds , "O" , kwlist , & py_context ))
141
- {
142
- return NULL ;
143
- }
144
-
145
- context =
146
- (struct compression_context * ) PyCapsule_GetPointer (py_context , NULL );
147
- if (!context )
148
- {
149
- PyErr_Format (PyExc_ValueError , "No compression context supplied" );
150
- return NULL ;
151
- }
125
+ struct compression_context * context =
126
+ #ifndef PyCapsule_Type
127
+ PyCapsule_GetPointer (py_context , capsule_name );
128
+ // That's always true as there is no PyCapsule_SetPointer calls.
129
+ #else
130
+ py_context ; // compatibility with 2.6 via capsulethunk
131
+ #endif
152
132
153
133
Py_BEGIN_ALLOW_THREADS
154
- result =
155
- LZ4F_freeCompressionContext ( context -> compression_context );
134
+ LZ4F_freeCompressionContext ( context -> compression_context );
135
+ // That's always LZ4F_OK_NoError as free() is `void free()` and it's just a wrapper.
156
136
Py_END_ALLOW_THREADS
157
137
158
- if (LZ4F_isError (result ))
159
- {
160
- PyErr_Format (PyExc_RuntimeError ,
161
- "LZ4F_freeCompressionContext failed with code: %s" ,
162
- LZ4F_getErrorName (result ));
163
- return NULL ;
164
- }
165
138
PyMem_Free (context );
166
-
167
- Py_RETURN_NONE ;
168
139
}
169
140
170
141
/******************
@@ -185,9 +156,10 @@ free_compression_context (PyObject * Py_UNUSED (self), PyObject * args,
185
156
" - BLOCKMODE_LINKED or 1: linked mode\n\n" \
186
157
" The default is BLOCKMODE_INDEPENDENT.\n" \
187
158
" compression_level (int): Specifies the level of compression used.\n" \
188
- " Values between 0-16 are valid, with 0 (default) being the\n" \
189
- " lowest compression, and 16 the highest. Values above 16 will\n" \
190
- " be treated as 16. Values betwee 3-6 are recommended.\n" \
159
+ " Values between 0-16 are valid, with 0 (default) being the\n" \
160
+ " lowest compression (0-2 are the same value), and 16 the highest.\n" \
161
+ " Values above 16 will be treated as 16.\n" \
162
+ " Values between 4-9 are recommended.\n" \
191
163
" The following module constants are provided as a convenience:\n\n" \
192
164
" - COMPRESSIONLEVEL_MIN: Minimum compression (0, the default)\n" \
193
165
" - COMPRESSIONLEVEL_MINHC: Minimum high-compression mode (3)\n" \
@@ -381,7 +353,7 @@ compress_begin (PyObject * Py_UNUSED (self), PyObject * args,
381
353
preferences .frameInfo .contentSize = source_size ;
382
354
383
355
context =
384
- (struct compression_context * ) PyCapsule_GetPointer (py_context , NULL );
356
+ (struct compression_context * ) PyCapsule_GetPointer (py_context , capsule_name );
385
357
386
358
if (!context || !context -> compression_context )
387
359
{
@@ -448,7 +420,7 @@ compress_update (PyObject * Py_UNUSED (self), PyObject * args,
448
420
}
449
421
450
422
context =
451
- (struct compression_context * ) PyCapsule_GetPointer (py_context , NULL );
423
+ (struct compression_context * ) PyCapsule_GetPointer (py_context , capsule_name );
452
424
if (!context || !context -> compression_context )
453
425
{
454
426
PyErr_Format (PyExc_ValueError , "No compression context supplied" );
@@ -542,7 +514,7 @@ compress_end (PyObject * Py_UNUSED (self), PyObject * args, PyObject * keywds)
542
514
}
543
515
544
516
context =
545
- (struct compression_context * ) PyCapsule_GetPointer (py_context , NULL );
517
+ (struct compression_context * ) PyCapsule_GetPointer (py_context , capsule_name );
546
518
if (!context || !context -> compression_context )
547
519
{
548
520
PyErr_SetString (PyExc_ValueError , "No compression context supplied" );
@@ -805,6 +777,7 @@ decompress (PyObject * Py_UNUSED (self), PyObject * args, PyObject * keywds)
805
777
}
806
778
807
779
destination_written += destination_write ;
780
+ source_cursor += source_read ;
808
781
809
782
if (result == 0 )
810
783
{
@@ -813,11 +786,10 @@ decompress (PyObject * Py_UNUSED (self), PyObject * args, PyObject * keywds)
813
786
814
787
if (destination_written == destination_size )
815
788
{
816
- /* Destination_buffer is full, so need to expand it. We'll expand
817
- it by the approximate size needed from the return value - see
818
- LZ4 docs. */
819
- destination_size += result ;
820
- if (!PyMem_Realloc (destination_buffer , destination_size ))
789
+ /* Destination_buffer is full, so need to expand it. */
790
+ destination_size *= 2 ;
791
+ char * nextgen = PyMem_Realloc (destination_buffer , destination_size );
792
+ if (!nextgen )
821
793
{
822
794
LZ4F_freeDecompressionContext (context );
823
795
Py_BLOCK_THREADS
@@ -826,14 +798,14 @@ decompress (PyObject * Py_UNUSED (self), PyObject * args, PyObject * keywds)
826
798
PyMem_Free (destination_buffer );
827
799
return NULL ;
828
800
}
801
+ destination_buffer = nextgen ;
829
802
}
830
803
/* Data still remaining to be decompressed, so increment the source and
831
804
destination cursor locations, and reset source_read and
832
805
destination_write ready for the next iteration. Important to
833
806
re-initialize destination_cursor here (as opposed to simply
834
807
incrementing it) so we're pointing to the realloc'd memory location. */
835
808
destination_cursor = destination_buffer + destination_written ;
836
- source_cursor += source_read ;
837
809
destination_write = destination_size - destination_written ;
838
810
source_read = source_end - source_cursor ;
839
811
}
@@ -850,6 +822,13 @@ decompress (PyObject * Py_UNUSED (self), PyObject * args, PyObject * keywds)
850
822
LZ4F_getErrorName (result ));
851
823
return NULL ;
852
824
}
825
+ else if (source_cursor != source_end )
826
+ {
827
+ PyMem_Free (destination_buffer );
828
+ PyErr_Format (PyExc_ValueError ,
829
+ "Extra data: %zd trailing bytes" , source_end - source_cursor );
830
+ return NULL ;
831
+ }
853
832
854
833
py_dest = PyBytes_FromStringAndSize (destination_buffer , destination_written );
855
834
@@ -869,10 +848,6 @@ static PyMethodDef module_methods[] =
869
848
"create_compression_context" , (PyCFunction ) create_compression_context ,
870
849
METH_VARARGS | METH_KEYWORDS , create_compression_context__doc
871
850
},
872
- {
873
- "free_compression_context" , (PyCFunction ) free_compression_context ,
874
- METH_VARARGS | METH_KEYWORDS , free_compression_context__doc
875
- },
876
851
{
877
852
"compress" , (PyCFunction ) compress ,
878
853
METH_VARARGS | METH_KEYWORDS , compress__doc
0 commit comments