@@ -51,6 +51,199 @@ bytes_join(PyObject *Py_UNUSED(module), PyObject *args)
5151}
5252
5353
54+ // --- PyBytesWriter type ---------------------------------------------------
55+
56+ typedef struct {
57+ PyObject_HEAD
58+ PyBytesWriter * writer ;
59+ } WriterObject ;
60+
61+
62+ static PyObject *
63+ writer_new (PyTypeObject * type , PyObject * args , PyObject * kwargs )
64+ {
65+ WriterObject * self = (WriterObject * )type -> tp_alloc (type , 0 );
66+ if (!self ) {
67+ return NULL ;
68+ }
69+ self -> writer = NULL ;
70+ return (PyObject * )self ;
71+ }
72+
73+
74+ static int
75+ writer_init (PyObject * self_raw , PyObject * args , PyObject * kwargs )
76+ {
77+ WriterObject * self = (WriterObject * )self_raw ;
78+ if (self -> writer ) {
79+ PyBytesWriter_Discard (self -> writer );
80+ }
81+
82+ if (kwargs && PyDict_GET_SIZE (kwargs )) {
83+ PyErr_Format (PyExc_TypeError ,
84+ "PyBytesWriter() takes exactly no keyword arguments" );
85+ return -1 ;
86+ }
87+
88+ Py_ssize_t alloc ;
89+ char * str ;
90+ Py_ssize_t str_size ;
91+ if (!PyArg_ParseTuple (args , "ny#" , & alloc , & str , & str_size )) {
92+ return -1 ;
93+ }
94+
95+ self -> writer = PyBytesWriter_Create (alloc );
96+ if (self -> writer == NULL ) {
97+ return -1 ;
98+ }
99+
100+ if (str_size ) {
101+ char * buf = PyBytesWriter_GetData (self -> writer );
102+ memcpy (buf , str , str_size );
103+ }
104+
105+ return 0 ;
106+ }
107+
108+
109+ static void
110+ writer_dealloc (PyObject * self_raw )
111+ {
112+ WriterObject * self = (WriterObject * )self_raw ;
113+ PyTypeObject * tp = Py_TYPE (self );
114+ if (self -> writer ) {
115+ PyBytesWriter_Discard (self -> writer );
116+ }
117+ tp -> tp_free (self );
118+ Py_DECREF (tp );
119+ }
120+
121+
122+ static inline int
123+ writer_check (WriterObject * self )
124+ {
125+ if (self -> writer == NULL ) {
126+ PyErr_SetString (PyExc_ValueError , "operation on finished writer" );
127+ return -1 ;
128+ }
129+ return 0 ;
130+ }
131+
132+
133+ static PyObject *
134+ writer_resize (PyObject * self_raw , PyObject * args )
135+ {
136+ WriterObject * self = (WriterObject * )self_raw ;
137+ if (writer_check (self ) < 0 ) {
138+ return NULL ;
139+ }
140+
141+ Py_ssize_t size ;
142+ char * str ;
143+ Py_ssize_t str_size ;
144+ if (!PyArg_ParseTuple (args ,
145+ "ny#" ,
146+ & size , & str , & str_size )) {
147+ return NULL ;
148+ }
149+ assert (size >= str_size );
150+
151+ Py_ssize_t pos = PyBytesWriter_GetSize (self -> writer );
152+ if (PyBytesWriter_Resize (self -> writer , size ) < 0 ) {
153+ return NULL ;
154+ }
155+
156+ char * buf = PyBytesWriter_GetData (self -> writer );
157+ memcpy (buf + pos , str , str_size );
158+
159+ Py_RETURN_NONE ;
160+ }
161+
162+
163+ static PyObject *
164+ writer_get_size (PyObject * self_raw , PyObject * Py_UNUSED (args ))
165+ {
166+ WriterObject * self = (WriterObject * )self_raw ;
167+ if (writer_check (self ) < 0 ) {
168+ return NULL ;
169+ }
170+
171+ Py_ssize_t alloc = PyBytesWriter_GetSize (self -> writer );
172+ return PyLong_FromSsize_t (alloc );
173+ }
174+
175+
176+ static PyObject *
177+ writer_get_allocated (PyObject * self_raw , PyObject * Py_UNUSED (args ))
178+ {
179+ WriterObject * self = (WriterObject * )self_raw ;
180+ if (writer_check (self ) < 0 ) {
181+ return NULL ;
182+ }
183+
184+ Py_ssize_t alloc = PyBytesWriter_GetAllocated (self -> writer );
185+ return PyLong_FromSsize_t (alloc );
186+ }
187+
188+
189+ static PyObject *
190+ writer_finish (PyObject * self_raw , PyObject * Py_UNUSED (args ))
191+ {
192+ WriterObject * self = (WriterObject * )self_raw ;
193+ if (writer_check (self ) < 0 ) {
194+ return NULL ;
195+ }
196+
197+ PyObject * str = PyBytesWriter_Finish (self -> writer );
198+ self -> writer = NULL ;
199+ return str ;
200+ }
201+
202+
203+ static PyObject *
204+ writer_finish_with_size (PyObject * self_raw , PyObject * args )
205+ {
206+ WriterObject * self = (WriterObject * )self_raw ;
207+ if (writer_check (self ) < 0 ) {
208+ return NULL ;
209+ }
210+
211+ Py_ssize_t size ;
212+ if (!PyArg_ParseTuple (args , "n" , & size )) {
213+ return NULL ;
214+ }
215+
216+ PyObject * str = PyBytesWriter_FinishWithSize (self -> writer , size );
217+ self -> writer = NULL ;
218+ return str ;
219+ }
220+
221+
222+ static PyMethodDef writer_methods [] = {
223+ {"resize" , _PyCFunction_CAST (writer_resize ), METH_VARARGS },
224+ {"get_size" , _PyCFunction_CAST (writer_get_size ), METH_NOARGS },
225+ {"get_allocated" , _PyCFunction_CAST (writer_get_allocated ), METH_NOARGS },
226+ {"finish" , _PyCFunction_CAST (writer_finish ), METH_NOARGS },
227+ {"finish_with_size" , _PyCFunction_CAST (writer_finish_with_size ), METH_VARARGS },
228+ {NULL , NULL } /* sentinel */
229+ };
230+
231+ static PyType_Slot Writer_Type_slots [] = {
232+ {Py_tp_new , writer_new },
233+ {Py_tp_init , writer_init },
234+ {Py_tp_dealloc , writer_dealloc },
235+ {Py_tp_methods , writer_methods },
236+ {0 , 0 }, /* sentinel */
237+ };
238+
239+ static PyType_Spec Writer_spec = {
240+ .name = "_testcapi.PyBytesWriter" ,
241+ .basicsize = sizeof (WriterObject ),
242+ .flags = Py_TPFLAGS_DEFAULT ,
243+ .slots = Writer_Type_slots ,
244+ };
245+
246+
54247static PyMethodDef test_methods [] = {
55248 {"bytes_resize" , bytes_resize , METH_VARARGS },
56249 {"bytes_join" , bytes_join , METH_VARARGS },
@@ -64,5 +257,15 @@ _PyTestCapi_Init_Bytes(PyObject *m)
64257 return -1 ;
65258 }
66259
260+ PyTypeObject * writer_type = (PyTypeObject * )PyType_FromSpec (& Writer_spec );
261+ if (writer_type == NULL ) {
262+ return -1 ;
263+ }
264+ if (PyModule_AddType (m , writer_type ) < 0 ) {
265+ Py_DECREF (writer_type );
266+ return -1 ;
267+ }
268+ Py_DECREF (writer_type );
269+
67270 return 0 ;
68271}
0 commit comments