@@ -353,12 +353,114 @@ byteswriter_highlevel(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
353353}
354354
355355
356+ static size_t
357+ pybyteswriter_small_buffer_size (void )
358+ {
359+ return offsetof(PyBytesWriter , obj );
360+ }
361+
362+
363+ // Test the "Pointer" API of PyBytesWriter
364+ static PyObject *
365+ test_byteswriter_ptr (PyObject * Py_UNUSED (module ), PyObject * Py_UNUSED (args ))
366+ {
367+ // Test PyBytesWriter_FinishWithPointer(): create the string "abc"
368+ PyBytesWriter * writer = PyBytesWriter_Create (3 );
369+ if (writer == NULL ) {
370+ return NULL ;
371+ }
372+ char * str = PyBytesWriter_GetData (writer );
373+ memcpy (str , "abc" , 3 );
374+ str += 3 ;
375+ PyObject * result = PyBytesWriter_FinishWithPointer (writer , str );
376+ if (result == NULL ) {
377+ return NULL ;
378+ }
379+ assert (PyBytes_GET_SIZE (result ) == 3 );
380+ assert (memcmp (PyBytes_AS_STRING (result ), "abc" , 3 ) == 0 );
381+ Py_DECREF (result );
382+
383+ // Test PyBytesWriter_GrowAndUpdatePointer().
384+ // Start by using the small buffer, and then resize to use a bytes object.
385+ writer = PyBytesWriter_Create (0 );
386+ if (writer == NULL ) {
387+ return NULL ;
388+ }
389+ str = PyBytesWriter_GetData (writer );
390+
391+ str = PyBytesWriter_GrowAndUpdatePointer (writer , 100 , str );
392+ if (str == NULL ) {
393+ PyBytesWriter_Discard (writer );
394+ return NULL ;
395+ }
396+ memset (str , 'x' , 100 );
397+ str += 100 ;
398+
399+ // make sure that the test switchs to a bytes object
400+ assert ((100 + 200 ) > pybyteswriter_small_buffer_size ());
401+ char * old_str = str ;
402+ str = PyBytesWriter_GrowAndUpdatePointer (writer , 200 , str );
403+ if (str == NULL ) {
404+ PyBytesWriter_Discard (writer );
405+ return NULL ;
406+ }
407+ // make sure that we moved from the small buffer to a bytes object
408+ assert (str != old_str );
409+ memset (str , 'y' , 200 );
410+ str += 200 ;
411+
412+ result = PyBytesWriter_FinishWithPointer (writer , str );
413+ if (result == NULL ) {
414+ return NULL ;
415+ }
416+ assert (PyBytes_GET_SIZE (result ) == 300 );
417+ str = PyBytes_AS_STRING (result );
418+ for (Py_ssize_t i = 0 ; i < 100 ; i ++ ) {
419+ assert (str [i ] == 'x' );
420+ }
421+ for (Py_ssize_t i = 0 ; i < 200 ; i ++ ) {
422+ assert (str [100 + i ] == 'y' );
423+ }
424+ Py_DECREF (result );
425+
426+ // Check that PyBytesWriter_FinishWithPointer() rejects pointer
427+ // after the buffer end (create a string larger than the allocated size)
428+ writer = PyBytesWriter_Create (3 );
429+ if (writer == NULL ) {
430+ return NULL ;
431+ }
432+ str = PyBytesWriter_GetData (writer );
433+ memcpy (str , "abc" , 3 );
434+ str += 4 ; // off-by-one bug on purpose
435+ result = PyBytesWriter_FinishWithPointer (writer , str );
436+ assert (result == NULL );
437+ assert (PyErr_ExceptionMatches (PyExc_ValueError ));
438+ PyErr_Clear ();
439+
440+ // Check that PyBytesWriter_FinishWithPointer() rejects pointer
441+ // before the buffer start (negative size)
442+ writer = PyBytesWriter_Create (3 );
443+ if (writer == NULL ) {
444+ return NULL ;
445+ }
446+ str = PyBytesWriter_GetData (writer );
447+ str -- ; // bug on purpose: go before the buffer start
448+ result = PyBytesWriter_FinishWithPointer (writer , str );
449+ assert (result == NULL );
450+ assert (PyErr_ExceptionMatches (PyExc_ValueError ));
451+ PyErr_Clear ();
452+
453+ Py_RETURN_NONE ;
454+ }
455+
456+
356457static PyMethodDef test_methods [] = {
357458 {"bytes_resize" , bytes_resize , METH_VARARGS },
358459 {"bytes_join" , bytes_join , METH_VARARGS },
359460 {"byteswriter_abc" , byteswriter_abc , METH_NOARGS },
360461 {"byteswriter_resize" , byteswriter_resize , METH_NOARGS },
361462 {"byteswriter_highlevel" , byteswriter_highlevel , METH_NOARGS },
463+ {"test_byteswriter_ptr" , test_byteswriter_ptr , METH_NOARGS },
362464 {NULL },
363465};
364466
@@ -380,7 +482,7 @@ _PyTestCapi_Init_Bytes(PyObject *m)
380482 Py_DECREF (writer_type );
381483
382484 // PyBytesWriter.obj is the second member, small_buffer is the first member
383- long size = (long )offsetof( PyBytesWriter , obj );
485+ long size = (long )pybyteswriter_small_buffer_size ( );
384486 if (PyModule_AddIntConstant (m , "PyBytesWriter_small_buffer" , size ) < 0 ) {
385487 Py_DECREF (writer_type );
386488 return -1 ;
0 commit comments