@@ -45,12 +45,14 @@ extern "C" {
4545#endif
4646
4747typedef struct {
48- // List of bytes objects
49- PyObject * list ;
48+ // Bytes writer managing output buffer
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+ size_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,21 @@ _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 ) {
166- PyErr_SetString (PyExc_MemoryError , unable_allocate_msg );
155+ buffer -> writer = PyBytesWriter_Create (init_size );
156+ if (buffer -> writer == NULL ) {
167157 return -1 ;
168158 }
169159
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-
178160 // set variables
179161 buffer -> allocated = init_size ;
180162 buffer -> max_length = -1 ;
163+ buffer -> num_blocks = 1 ;
181164
182- * next_out = PyBytes_AS_STRING ( b );
165+ * next_out = PyBytesWriter_GetData ( buffer -> writer );
183166 return init_size ;
184167}
185168
@@ -193,8 +176,6 @@ _BlocksOutputBuffer_Grow(_BlocksOutputBuffer *buffer,
193176 void * * next_out ,
194177 const Py_ssize_t avail_out )
195178{
196- PyObject * b ;
197- const Py_ssize_t list_len = Py_SIZE (buffer -> list );
198179 Py_ssize_t block_size ;
199180
200181 // ensure no gaps in the data
@@ -205,11 +186,10 @@ _BlocksOutputBuffer_Grow(_BlocksOutputBuffer *buffer,
205186 }
206187
207188 // get block size
208- if (list_len < (Py_ssize_t ) Py_ARRAY_LENGTH (BUFFER_BLOCK_SIZE )) {
209- block_size = BUFFER_BLOCK_SIZE [list_len ];
210- } else {
211- block_size = BUFFER_BLOCK_SIZE [Py_ARRAY_LENGTH (BUFFER_BLOCK_SIZE ) - 1 ];
212- }
189+ size_t maxblock = Py_ARRAY_LENGTH (BUFFER_BLOCK_SIZE );
190+ assert (maxblock >= 1 );
191+ size_t block_index = Py_MIN (buffer -> num_blocks , maxblock - 1 );
192+ block_size = BUFFER_BLOCK_SIZE [block_index ];
213193
214194 // check max_length
215195 if (buffer -> max_length >= 0 ) {
@@ -229,22 +209,19 @@ _BlocksOutputBuffer_Grow(_BlocksOutputBuffer *buffer,
229209 return -1 ;
230210 }
231211
232- // create the block
233- b = PyBytes_FromStringAndSize (NULL , block_size );
234- if (b == NULL ) {
212+ if (PyBytesWriter_Grow (buffer -> writer , block_size )) {
235213 PyErr_SetString (PyExc_MemoryError , unable_allocate_msg );
236214 return -1 ;
237215 }
238- if (PyList_Append (buffer -> list , b ) < 0 ) {
239- Py_DECREF (b );
240- return -1 ;
241- }
242- Py_DECREF (b );
216+
217+ Py_ssize_t current_size = buffer -> allocated ;
243218
244219 // set variables
245220 buffer -> allocated += block_size ;
221+ buffer -> num_blocks += 1 ;
246222
247- * next_out = PyBytes_AS_STRING (b );
223+ char * data = PyBytesWriter_GetData (buffer -> writer );
224+ * next_out = data + current_size ;
248225 return block_size ;
249226}
250227
@@ -265,54 +242,17 @@ static inline PyObject *
265242_BlocksOutputBuffer_Finish (_BlocksOutputBuffer * buffer ,
266243 const Py_ssize_t avail_out )
267244{
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 ;
245+ assert (buffer -> writer != NULL );
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 );
255+ buffer -> writer = NULL ;
316256}
317257
318258#ifdef __cplusplus
0 commit comments