@@ -46,11 +46,13 @@ extern "C" {
4646
4747typedef struct {
4848 // List of bytes objects
49- PyObject * list ;
49+ PyBytesWriter * writer ;
5050 // Number of whole allocated size
5151 Py_ssize_t allocated ;
52- // Max length of the buffer, negative number means unlimited length.
52+ // Max length of the buffer, negative number means unlimited length
5353 Py_ssize_t max_length ;
54+ // Number of blocks of bytes. Used to calculate next allocation size
55+ Py_ssize_t num_blocks ;
5456} _BlocksOutputBuffer ;
5557
5658static const char unable_allocate_msg [] = "Unable to allocate output buffer." ;
@@ -107,11 +109,10 @@ _BlocksOutputBuffer_InitAndGrow(_BlocksOutputBuffer *buffer,
107109 const Py_ssize_t max_length ,
108110 void * * next_out )
109111{
110- PyObject * b ;
111112 Py_ssize_t block_size ;
112113
113- // ensure .list was set to NULL
114- assert (buffer -> list == NULL );
114+ // ensure .writer was set to NULL
115+ assert (buffer -> writer == NULL );
115116
116117 // get block size
117118 if (0 <= max_length && max_length < BUFFER_BLOCK_SIZE [0 ]) {
@@ -120,25 +121,17 @@ _BlocksOutputBuffer_InitAndGrow(_BlocksOutputBuffer *buffer,
120121 block_size = BUFFER_BLOCK_SIZE [0 ];
121122 }
122123
123- // the first block
124- b = PyBytes_FromStringAndSize (NULL , block_size );
125- if (b == NULL ) {
124+ buffer -> writer = PyBytesWriter_Create (block_size );
125+ if (buffer -> writer == NULL ) {
126126 return -1 ;
127127 }
128128
129- // create the list
130- buffer -> list = PyList_New (1 );
131- if (buffer -> list == NULL ) {
132- Py_DECREF (b );
133- return -1 ;
134- }
135- PyList_SET_ITEM (buffer -> list , 0 , b );
136-
137129 // set variables
138130 buffer -> allocated = block_size ;
139131 buffer -> max_length = max_length ;
132+ buffer -> num_blocks = 1 ;
140133
141- * next_out = PyBytes_AS_STRING ( b );
134+ * next_out = PyBytesWriter_GetData ( buffer -> writer );
142135 return block_size ;
143136}
144137
@@ -155,31 +148,22 @@ _BlocksOutputBuffer_InitWithSize(_BlocksOutputBuffer *buffer,
155148 const Py_ssize_t init_size ,
156149 void * * next_out )
157150{
158- PyObject * b ;
159151
160- // ensure .list was set to NULL
161- assert (buffer -> list == NULL );
152+ // ensure .writer was set to NULL
153+ assert (buffer -> writer == NULL );
162154
163- // the first block
164- b = PyBytes_FromStringAndSize (NULL , init_size );
165- if (b == NULL ) {
155+ buffer -> writer = PyBytesWriter_Create (init_size );
156+ if (buffer -> writer == NULL ) {
166157 PyErr_SetString (PyExc_MemoryError , unable_allocate_msg );
167158 return -1 ;
168159 }
169160
170- // create the list
171- buffer -> list = PyList_New (1 );
172- if (buffer -> list == NULL ) {
173- Py_DECREF (b );
174- return -1 ;
175- }
176- PyList_SET_ITEM (buffer -> list , 0 , b );
177-
178161 // set variables
179162 buffer -> allocated = init_size ;
180163 buffer -> max_length = -1 ;
164+ buffer -> num_blocks = 1 ;
181165
182- * next_out = PyBytes_AS_STRING ( b );
166+ * next_out = PyBytesWriter_GetData ( buffer -> writer );
183167 return init_size ;
184168}
185169
@@ -193,8 +177,6 @@ _BlocksOutputBuffer_Grow(_BlocksOutputBuffer *buffer,
193177 void * * next_out ,
194178 const Py_ssize_t avail_out )
195179{
196- PyObject * b ;
197- const Py_ssize_t list_len = Py_SIZE (buffer -> list );
198180 Py_ssize_t block_size ;
199181
200182 // ensure no gaps in the data
@@ -205,8 +187,8 @@ _BlocksOutputBuffer_Grow(_BlocksOutputBuffer *buffer,
205187 }
206188
207189 // get block size
208- if (list_len < (Py_ssize_t ) Py_ARRAY_LENGTH (BUFFER_BLOCK_SIZE )) {
209- block_size = BUFFER_BLOCK_SIZE [list_len ];
190+ if (buffer -> num_blocks < (Py_ssize_t ) Py_ARRAY_LENGTH (BUFFER_BLOCK_SIZE )) {
191+ block_size = BUFFER_BLOCK_SIZE [buffer -> num_blocks ];
210192 } else {
211193 block_size = BUFFER_BLOCK_SIZE [Py_ARRAY_LENGTH (BUFFER_BLOCK_SIZE ) - 1 ];
212194 }
@@ -229,22 +211,18 @@ _BlocksOutputBuffer_Grow(_BlocksOutputBuffer *buffer,
229211 return -1 ;
230212 }
231213
232- // create the block
233- b = PyBytes_FromStringAndSize (NULL , block_size );
234- if (b == NULL ) {
214+ if (PyBytesWriter_Grow (buffer -> writer , block_size )) {
235215 PyErr_SetString (PyExc_MemoryError , unable_allocate_msg );
236216 return -1 ;
237217 }
238- if (PyList_Append (buffer -> list , b ) < 0 ) {
239- Py_DECREF (b );
240- return -1 ;
241- }
242- Py_DECREF (b );
218+
219+ Py_ssize_t current_size = buffer -> allocated ;
243220
244221 // set variables
245222 buffer -> allocated += block_size ;
223+ buffer -> num_blocks += 1 ;
246224
247- * next_out = PyBytes_AS_STRING ( b ) ;
225+ * next_out = PyBytesWriter_GetData ( buffer -> writer ) + current_size ;
248226 return block_size ;
249227}
250228
@@ -265,54 +243,15 @@ static inline PyObject *
265243_BlocksOutputBuffer_Finish (_BlocksOutputBuffer * buffer ,
266244 const Py_ssize_t avail_out )
267245{
268- PyObject * result , * block ;
269- const Py_ssize_t list_len = Py_SIZE (buffer -> list );
270-
271- // fast path for single block
272- if ((list_len == 1 && avail_out == 0 ) ||
273- (list_len == 2 && Py_SIZE (PyList_GET_ITEM (buffer -> list , 1 )) == avail_out ))
274- {
275- block = PyList_GET_ITEM (buffer -> list , 0 );
276- Py_INCREF (block );
277-
278- Py_CLEAR (buffer -> list );
279- return block ;
280- }
281-
282- // final bytes object
283- result = PyBytes_FromStringAndSize (NULL , buffer -> allocated - avail_out );
284- if (result == NULL ) {
285- PyErr_SetString (PyExc_MemoryError , unable_allocate_msg );
286- return NULL ;
287- }
288-
289- // memory copy
290- if (list_len > 0 ) {
291- char * posi = PyBytes_AS_STRING (result );
292-
293- // blocks except the last one
294- Py_ssize_t i = 0 ;
295- for (; i < list_len - 1 ; i ++ ) {
296- block = PyList_GET_ITEM (buffer -> list , i );
297- memcpy (posi , PyBytes_AS_STRING (block ), Py_SIZE (block ));
298- posi += Py_SIZE (block );
299- }
300- // the last block
301- block = PyList_GET_ITEM (buffer -> list , i );
302- memcpy (posi , PyBytes_AS_STRING (block ), Py_SIZE (block ) - avail_out );
303- } else {
304- assert (Py_SIZE (result ) == 0 );
305- }
306-
307- Py_CLEAR (buffer -> list );
308- return result ;
246+ return PyBytesWriter_FinishWithSize (buffer -> writer ,
247+ buffer -> allocated - avail_out );
309248}
310249
311250/* Clean up the buffer when an error occurred. */
312251static inline void
313252_BlocksOutputBuffer_OnError (_BlocksOutputBuffer * buffer )
314253{
315- Py_CLEAR (buffer -> list );
254+ PyBytesWriter_Discard (buffer -> writer );
316255}
317256
318257#ifdef __cplusplus
0 commit comments