Skip to content

Commit 63a2dc8

Browse files
authored
PEP 782: Address Steve's review (#4335)
1 parent c887b29 commit 63a2dc8

File tree

1 file changed

+103
-23
lines changed

1 file changed

+103
-23
lines changed

peps/pep-0782.rst

Lines changed: 103 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,9 @@ Create, Finish, Discard
8080
8181
Create a :c:type:`PyBytesWriter` to write *size* bytes.
8282
83-
If *size* is greater than zero, allocate *size* bytes for the
84-
returned buffer.
83+
If *size* is greater than zero, allocate *size* bytes, and set the
84+
writer size to *size*. The caller is responsible to write *size*
85+
bytes using :c:func:`PyBytesWriter_GetData`.
8586
8687
On error, set an exception and return NULL.
8788
@@ -107,14 +108,14 @@ Create, Finish, Discard
107108
Similar to :c:func:`PyBytesWriter_Finish`, but resize the writer
108109
using *buf* pointer before creating the :class:`bytes` object.
109110
110-
Pseudo-code::
111+
Set an exception and return ``NULL`` if *buf* pointer is outside the
112+
internal buffer bounds.
113+
114+
Function pseudo-code::
111115
112116
Py_ssize_t size = (char*)buf - (char*)PyBytesWriter_GetData(writer);
113117
return PyBytesWriter_FinishWithSize(writer, size);
114118
115-
Set an exception and return ``NULL`` if *buf* pointer is outside the
116-
internal buffer bounds.
117-
118119
.. c:function:: void PyBytesWriter_Discard(PyBytesWriter *writer)
119120
120121
Discard a :c:type:`PyBytesWriter` created by :c:func:`PyBytesWriter_Create`.
@@ -128,7 +129,9 @@ High-level API
128129
129130
.. c:function:: int PyBytesWriter_WriteBytes(PyBytesWriter *writer, const void *bytes, Py_ssize_t size)
130131
131-
Write *size* bytes of *bytes* into the *writer*.
132+
Grow the *writer* internal buffer by *size* bytes,
133+
write *size* bytes of *bytes* at the *writer* end,
134+
and add *size* to the *writer* size.
132135
133136
If *size* is equal to ``-1``, call ``strlen(bytes)`` to get the
134137
string length.
@@ -138,8 +141,9 @@ High-level API
138141
139142
.. c:function:: int PyBytesWriter_Format(PyBytesWriter *writer, const char *format, ...)
140143
141-
Similar to ``PyBytes_FromFormat()``, but write the output directly
142-
into the writer.
144+
Similar to ``PyBytes_FromFormat()``, but write the output directly at
145+
the writer end. Grow the writer internal buffer on demand.
146+
Then add the written size to the writer size.
143147
144148
On success, return ``0``.
145149
On error, set an exception and return ``-1``.
@@ -153,7 +157,7 @@ Getters
153157
154158
.. c:function:: void* PyBytesWriter_GetData(PyBytesWriter *writer)
155159
156-
Get the writer data.
160+
Get the writer data: start of the internal buffer.
157161
158162
The pointer is valid until :c:func:`PyBytesWriter_Finish` or
159163
:c:func:`PyBytesWriter_Discard` is called on *writer*.
@@ -182,16 +186,22 @@ Low-level API
182186
On success, return ``0``.
183187
On error, set an exception and return ``-1``.
184188
185-
*size* must be positive or zero.
189+
*size* can be negative to shrink the writer.
186190
187191
.. c:function:: void* PyBytesWriter_GrowAndUpdatePointer(PyBytesWriter *writer, Py_ssize_t size, void *buf)
188192
189193
Similar to :c:func:`PyBytesWriter_Grow`, but update also the *buf*
190194
pointer.
191195
196+
The *buf* pointer is moved if the internal buffer is moved in memory.
197+
The *buf* relative position within the internal buffer is left
198+
unchanged.
199+
192200
On error, set an exception and return ``NULL``.
193201
194-
Pseudo-code::
202+
*buf* must not be ``NULL``.
203+
204+
Function pseudo-code::
195205
196206
Py_ssize_t pos = (char*)buf - (char*)PyBytesWriter_GetData(writer);
197207
if (PyBytesWriter_Grow(writer, size) < 0) {
@@ -207,6 +217,10 @@ Overallocation
207217
overallocate the internal buffer to reduce the number of ``realloc()``
208218
calls and so reduce memory copies.
209219
220+
:c:func:`PyBytesWriter_Finish` trims overallocations: it shrinks the
221+
internal buffer to the exact size when creating the final :class:`bytes`
222+
object.
223+
210224
211225
Thread safety
212226
-------------
@@ -273,8 +287,8 @@ Example creating the bytes string ``b"abc"``, with a fixed size of 3 bytes::
273287
return PyBytesWriter_Finish(writer);
274288
}
275289
276-
GrowAndUpdatePointer() example
277-
------------------------------
290+
``GrowAndUpdatePointer()`` example
291+
----------------------------------
278292
279293
Example using a pointer to write bytes and to track the written size.
280294
@@ -310,22 +324,84 @@ Create the bytes string ``b"Hello World"``::
310324
}
311325
312326
327+
Update ``PyBytes_FromStringAndSize()`` code
328+
-------------------------------------------
329+
330+
Example of code using the soft deprecated
331+
``PyBytes_FromStringAndSize(NULL, size)`` API::
332+
333+
PyObject *result = PyBytes_FromStringAndSize(NULL, num_bytes);
334+
if (result == NULL) {
335+
return NULL;
336+
}
337+
if (copy_bytes(PyBytes_AS_STRING(result), start, num_bytes) < 0) {
338+
Py_CLEAR(result);
339+
}
340+
return result;
341+
342+
It can now be updated to::
343+
344+
PyBytesWriter *writer = PyBytesWriter_Create(num_bytes);
345+
if (writer == NULL) {
346+
return NULL;
347+
}
348+
if (copy_bytes(PyBytesWriter_GetData(writer), start, num_bytes) < 0) {
349+
PyBytesWriter_Discard(writer);
350+
return NULL;
351+
}
352+
return PyBytesWriter_Finish(writer);
353+
354+
355+
Update ``_PyBytes_Resize()`` code
356+
---------------------------------
357+
358+
Example of code using the soft deprecated ``_PyBytes_Resize()`` API::
359+
360+
PyObject *v = PyBytes_FromStringAndSize(NULL, size);
361+
if (v == NULL) {
362+
return NULL;
363+
}
364+
char *p = PyBytes_AS_STRING(v);
365+
366+
// ... fill bytes into 'p' ...
367+
368+
if (_PyBytes_Resize(&v, (p - PyBytes_AS_STRING(v)))) {
369+
return NULL;
370+
}
371+
return v;
372+
373+
It can now be updated to::
374+
375+
PyBytesWriter *writer = PyBytesWriter_Create(size);
376+
if (writer == NULL) {
377+
return NULL;
378+
}
379+
char *p = PyBytesWriter_GetData(writer);
380+
381+
// ... fill bytes into 'p' ...
382+
383+
return PyBytesWriter_FinishWithPointer(writer, p);
384+
385+
313386
Reference Implementation
314387
========================
315388
316389
`Pull request gh-131681 <https://github.com/python/cpython/pull/131681>`__.
317390
318-
The implementation allocates internally a :class:`bytes` object, so
319-
:c:func:`PyBytesWriter_Finish` just returns the object without having
320-
to copy memory.
391+
Notes on the CPython reference implementation which are not part of the
392+
Specification:
321393
322-
For strings up to 256 bytes, a small internal raw buffer of bytes is
323-
used. It avoids having to resize a :class:`bytes` object which is
324-
inefficient. At the end, :c:func:`PyBytesWriter_Finish` creates the
325-
:class:`bytes` object from this small buffer.
394+
* The implementation allocates internally a :class:`bytes` object, so
395+
:c:func:`PyBytesWriter_Finish` just returns the object without having
396+
to copy memory.
326397
327-
A free list is used to reduce the cost of allocating a
328-
:c:type:`PyBytesWriter` on the heap memory.
398+
* For strings up to 256 bytes, a small internal raw buffer of bytes is
399+
used. It avoids having to resize a :class:`bytes` object which is
400+
inefficient. At the end, :c:func:`PyBytesWriter_Finish` creates the
401+
:class:`bytes` object from this small buffer.
402+
403+
* A free list is used to reduce the cost of allocating a
404+
:c:type:`PyBytesWriter` on the heap memory.
329405
330406
331407
Backwards Compatibility
@@ -334,6 +410,10 @@ Backwards Compatibility
334410
There is no impact on the backward compatibility, only new APIs are
335411
added.
336412
413+
``PyBytes_FromStringAndSize(NULL, size)`` and ``_PyBytes_Resize()`` APIs
414+
are soft deprecated. No new warnings is emitted when these functions are
415+
used and they are not planned for removal.
416+
337417
338418
Prior Discussions
339419
=================

0 commit comments

Comments
 (0)