@@ -41,7 +41,7 @@ _grow_buffer(BytesWriterObject *data, Py_ssize_t n) {
4141 do {
4242 size *= 2 ;
4343 } while (target >= size );
44- if (old_size == WRITER_EMBEDDED_BUF_LEN ) {
44+ if (data -> buf == data -> data ) {
4545 // Move from embedded buffer to heap-allocated buffer
4646 data -> buf = PyMem_Malloc (size );
4747 if (data -> buf != NULL ) {
@@ -155,8 +155,65 @@ BytesWriter_getvalue(BytesWriterObject *self, PyObject *Py_UNUSED(ignored))
155155 return PyBytes_FromStringAndSize (self -> buf , self -> len );
156156}
157157
158+ static Py_ssize_t
159+ BytesWriter_length (BytesWriterObject * self )
160+ {
161+ return self -> len ;
162+ }
163+
164+ static PyObject *
165+ BytesWriter_item (BytesWriterObject * self , Py_ssize_t index )
166+ {
167+ Py_ssize_t length = self -> len ;
168+
169+ // Check bounds
170+ if (index < 0 || index >= length ) {
171+ PyErr_SetString (PyExc_IndexError , "BytesWriter index out of range" );
172+ return NULL ;
173+ }
174+
175+ // Return the byte at the given index as a Python int
176+ return PyLong_FromLong ((unsigned char )self -> buf [index ]);
177+ }
178+
179+ static int
180+ BytesWriter_ass_item (BytesWriterObject * self , Py_ssize_t index , PyObject * value )
181+ {
182+ Py_ssize_t length = self -> len ;
183+
184+ // Check bounds
185+ if (index < 0 || index >= length ) {
186+ PyErr_SetString (PyExc_IndexError , "BytesWriter index out of range" );
187+ return -1 ;
188+ }
189+
190+ // Check that value is not NULL (deletion not supported)
191+ if (value == NULL ) {
192+ PyErr_SetString (PyExc_TypeError , "BytesWriter does not support item deletion" );
193+ return -1 ;
194+ }
195+
196+ // Convert value to uint8
197+ uint8_t byte_value = CPyLong_AsUInt8 (value );
198+ if (unlikely (byte_value == CPY_LL_UINT_ERROR && PyErr_Occurred ())) {
199+ CPy_TypeError ("u8" , value );
200+ return -1 ;
201+ }
202+
203+ // Assign the byte
204+ self -> buf [index ] = (char )byte_value ;
205+ return 0 ;
206+ }
207+
208+ static PySequenceMethods BytesWriter_as_sequence = {
209+ .sq_length = (lenfunc )BytesWriter_length ,
210+ .sq_item = (ssizeargfunc )BytesWriter_item ,
211+ .sq_ass_item = (ssizeobjargproc )BytesWriter_ass_item ,
212+ };
213+
158214static PyObject * BytesWriter_append (PyObject * self , PyObject * const * args , size_t nargs , PyObject * kwnames );
159215static PyObject * BytesWriter_write (PyObject * self , PyObject * const * args , size_t nargs , PyObject * kwnames );
216+ static PyObject * BytesWriter_truncate (PyObject * self , PyObject * const * args , size_t nargs );
160217
161218static PyMethodDef BytesWriter_methods [] = {
162219 {"append" , (PyCFunction ) BytesWriter_append , METH_FASTCALL | METH_KEYWORDS ,
@@ -168,6 +225,9 @@ static PyMethodDef BytesWriter_methods[] = {
168225 {"getvalue" , (PyCFunction ) BytesWriter_getvalue , METH_NOARGS ,
169226 "Return the buffer content as bytes object"
170227 },
228+ {"truncate" , (PyCFunction ) BytesWriter_truncate , METH_FASTCALL ,
229+ PyDoc_STR ("Truncate the buffer to the specified size" )
230+ },
171231 {NULL } /* Sentinel */
172232};
173233
@@ -182,6 +242,7 @@ static PyTypeObject BytesWriterType = {
182242 .tp_init = (initproc ) BytesWriter_init ,
183243 .tp_dealloc = (destructor ) BytesWriter_dealloc ,
184244 .tp_methods = BytesWriter_methods ,
245+ .tp_as_sequence = & BytesWriter_as_sequence ,
185246 .tp_repr = (reprfunc )BytesWriter_repr ,
186247};
187248
@@ -268,11 +329,68 @@ BytesWriter_append(PyObject *self, PyObject *const *args, size_t nargs, PyObject
268329 return Py_None ;
269330}
270331
332+ static char
333+ BytesWriter_truncate_internal (PyObject * self , int64_t size ) {
334+ BytesWriterObject * writer = (BytesWriterObject * )self ;
335+ Py_ssize_t current_size = writer -> len ;
336+
337+ // Validate size is non-negative
338+ if (size < 0 ) {
339+ PyErr_SetString (PyExc_ValueError , "size must be non-negative" );
340+ return CPY_NONE_ERROR ;
341+ }
342+
343+ // Validate size doesn't exceed current size
344+ if (size > current_size ) {
345+ PyErr_SetString (PyExc_ValueError , "size cannot be larger than current buffer size" );
346+ return CPY_NONE_ERROR ;
347+ }
348+
349+ writer -> len = size ;
350+ return CPY_NONE ;
351+ }
352+
353+ static PyObject *
354+ BytesWriter_truncate (PyObject * self , PyObject * const * args , size_t nargs ) {
355+ if (unlikely (nargs != 1 )) {
356+ PyErr_Format (PyExc_TypeError ,
357+ "truncate() takes exactly 1 argument (%zu given)" , nargs );
358+ return NULL ;
359+ }
360+ if (!check_bytes_writer (self )) {
361+ return NULL ;
362+ }
363+
364+ PyObject * size_obj = args [0 ];
365+ int overflow ;
366+ long long size = PyLong_AsLongLongAndOverflow (size_obj , & overflow );
367+
368+ if (size == -1 && PyErr_Occurred ()) {
369+ return NULL ;
370+ }
371+ if (overflow != 0 ) {
372+ PyErr_SetString (PyExc_ValueError , "integer out of range" );
373+ return NULL ;
374+ }
375+
376+ if (unlikely (BytesWriter_truncate_internal (self , size ) == CPY_NONE_ERROR )) {
377+ return NULL ;
378+ }
379+ Py_INCREF (Py_None );
380+ return Py_None ;
381+ }
382+
271383static PyTypeObject *
272384BytesWriter_type_internal (void ) {
273385 return & BytesWriterType ; // Return borrowed reference
274386};
275387
388+ static CPyTagged
389+ BytesWriter_len_internal (PyObject * self ) {
390+ BytesWriterObject * writer = (BytesWriterObject * )self ;
391+ return writer -> len << 1 ;
392+ }
393+
276394static PyMethodDef librt_strings_module_methods [] = {
277395 {NULL , NULL , 0 , NULL }
278396};
@@ -311,6 +429,8 @@ librt_strings_module_exec(PyObject *m)
311429 (void * )BytesWriter_append_internal ,
312430 (void * )BytesWriter_write_internal ,
313431 (void * )BytesWriter_type_internal ,
432+ (void * )BytesWriter_len_internal ,
433+ (void * )BytesWriter_truncate_internal ,
314434 };
315435 PyObject * c_api_object = PyCapsule_New ((void * )librt_strings_api , "librt.strings._C_API" , NULL );
316436 if (PyModule_Add (m , "_C_API" , c_api_object ) < 0 ) {
0 commit comments