As the caller you’re expected to manage both a data pointer and a writer pointer:
char *buf;
PyBytesWriter *writer;
buf = PyBytesWriter_Create(&writer, 6);
/* error handling omitted */
Then, for each function (except _Discard), you pass both of those in, and (except for _Finish) you assign the result back to the data pointer:
buf = PyBytesWriter_WriteBytes(writer, buf, "abc", 3);
You can also fill the buffer manually, if there’s enough space reserved. That’s what you do for performance:
memcpy(buf, "xyz", 3);
buf += 3;
Which seems a bit silly, and I find it hard to explain for the docs.
It should be possible for Python to define a public struct with a public member:
typedef struct {void *buf, _PyBytesWriter_InternalObject *_writer} PyBytesWriter;
so the usage would be easier to explain:
PyBytesWriter writer;
int result = PyBytesWriter_Init(&writer, 6)
PyBytesWriter_WriteBytes(&writer, "abc", 3);
memcpy(writer.buf, "xyz", 3);
writer.buf += 3;
(We tend to avoid structs in the API because it’s hard to change them later, but making every function take two related arguments has exactly the same issue.)