Coverage Report

Created: 2026-08-28 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython3/Modules/_io/bytesio.c
Line
Count
Source
1
#include "Python.h"
2
#include "pycore_critical_section.h"  // Py_BEGIN_CRITICAL_SECTION()
3
#include "pycore_object.h"
4
#include "pycore_pyatomic_ft_wrappers.h"
5
#include "pycore_sysmodule.h"         // _PySys_GetSizeOf()
6
#include "pycore_weakref.h"           // FT_CLEAR_WEAKREFS()
7
8
#include <stddef.h>                   // offsetof()
9
#include "_iomodule.h"
10
11
/*[clinic input]
12
module _io
13
class _io.BytesIO "bytesio *" "clinic_state()->PyBytesIO_Type"
14
[clinic start generated code]*/
15
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=48ede2f330f847c3]*/
16
17
typedef struct {
18
    PyObject_HEAD
19
    PyObject *buf;
20
    Py_ssize_t pos;
21
    Py_ssize_t string_size;
22
    PyObject *dict;
23
    PyObject *weakreflist;
24
    Py_ssize_t exports;
25
#ifdef Py_GIL_DISABLED
26
    int buf_shared;
27
#endif
28
} bytesio;
29
30
0
#define bytesio_CAST(op)    ((bytesio *)(op))
31
32
typedef struct {
33
    PyObject_HEAD
34
    bytesio *source;
35
} bytesiobuf;
36
37
0
#define bytesiobuf_CAST(op) ((bytesiobuf *)(op))
38
39
/* The bytesio object can be in three states:
40
  * Py_REFCNT(buf) == 1, exports == 0.
41
  * Py_REFCNT(buf) > 1.  exports == 0,
42
    first modification or export causes the internal buffer copying.
43
  * exports > 0.  Any modifications are forbidden.  Every exported buffer
44
    keeps a reference to buf, so it outlives closing of the bytesio object.
45
*/
46
47
static int
48
check_closed(bytesio *self)
49
0
{
50
0
    if (self->buf == NULL) {
51
0
        PyErr_SetString(PyExc_ValueError, "I/O operation on closed file.");
52
0
        return 1;
53
0
    }
54
0
    return 0;
55
0
}
56
57
static int
58
check_exports(bytesio *self)
59
0
{
60
0
    if (FT_ATOMIC_LOAD_SSIZE_RELAXED(self->exports) > 0) {
61
0
        PyErr_SetString(PyExc_BufferError,
62
0
                        "Existing exports of data: object cannot be re-sized");
63
0
        return 1;
64
0
    }
65
0
    return 0;
66
0
}
67
68
#define CHECK_CLOSED(self)                                  \
69
0
    if (check_closed(self)) {                               \
70
0
        return NULL;                                        \
71
0
    }
72
73
#define CHECK_EXPORTS(self) \
74
0
    if (check_exports(self)) { \
75
0
        return NULL; \
76
0
    }
77
78
#ifdef Py_GIL_DISABLED
79
#define SHARED_BUF(self) ((self)->buf_shared || !_PyObject_IsUniquelyReferenced((self)->buf))
80
#else
81
0
#define SHARED_BUF(self) (!_PyObject_IsUniquelyReferenced((self)->buf))
82
#endif
83
84
static inline void
85
set_shared_buf(bytesio *self)
86
0
{
87
#ifdef Py_GIL_DISABLED
88
    self->buf_shared = 1;
89
#endif
90
0
}
91
92
static inline void
93
clear_shared_buf(bytesio *self)
94
0
{
95
#ifdef Py_GIL_DISABLED
96
    self->buf_shared = 0;
97
#endif
98
0
}
99
100
static int
101
resize_unshared_buffer_lock_held(bytesio *self, Py_ssize_t size)
102
0
{
103
0
    _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
104
105
#ifdef Py_GIL_DISABLED
106
    /* If the internal bytes object escaped via a zero-copy getvalue(), read(),
107
       or peek(), resizing it would mutate an object visible to Python code.
108
       Callers must detach first. */
109
    assert(!self->buf_shared);
110
#endif
111
0
    int ret = _PyBytes_Resize(&self->buf, size);
112
0
    if (ret == 0) {
113
0
        clear_shared_buf(self);
114
0
    }
115
0
    return ret;
116
0
}
117
118
119
/* Internal routine to get a line from the buffer of a BytesIO
120
   object. Returns the length between the current position to the
121
   next newline character. */
122
static Py_ssize_t
123
scan_eol_lock_held(bytesio *self, Py_ssize_t len)
124
0
{
125
0
    _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
126
127
0
    const char *start, *n;
128
0
    Py_ssize_t maxlen;
129
130
0
    assert(self->buf != NULL);
131
0
    assert(self->pos >= 0);
132
133
0
    if (self->pos >= self->string_size)
134
0
        return 0;
135
136
    /* Move to the end of the line, up to the end of the string, s. */
137
0
    maxlen = self->string_size - self->pos;
138
0
    if (len < 0 || len > maxlen)
139
0
        len = maxlen;
140
141
0
    if (len) {
142
0
        start = PyBytes_AS_STRING(self->buf) + self->pos;
143
0
        n = memchr(start, '\n', len);
144
0
        if (n)
145
            /* Get the length from the current position to the end of
146
               the line. */
147
0
            len = n - start + 1;
148
0
    }
149
0
    assert(len >= 0);
150
0
    assert(self->pos < PY_SSIZE_T_MAX - len);
151
152
0
    return len;
153
0
}
154
155
/* Internal routine for detaching the shared buffer of BytesIO objects.
156
   The caller should ensure that the 'size' argument is non-negative and
157
   not lesser than self->string_size.  Returns 0 on success, -1 otherwise. */
158
static int
159
unshare_buffer_lock_held(bytesio *self, size_t size)
160
0
{
161
0
    _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
162
163
0
    PyObject *new_buf;
164
0
    assert(SHARED_BUF(self));
165
0
    assert(FT_ATOMIC_LOAD_SSIZE_RELAXED(self->exports) == 0);
166
0
    assert(size >= (size_t)self->string_size);
167
0
    new_buf = PyBytes_FromStringAndSize(NULL, size);
168
0
    if (new_buf == NULL)
169
0
        return -1;
170
0
    memcpy(PyBytes_AS_STRING(new_buf), PyBytes_AS_STRING(self->buf),
171
0
           self->string_size);
172
0
    Py_SETREF(self->buf, new_buf);
173
0
    clear_shared_buf(self);
174
0
    return 0;
175
0
}
176
177
/* Internal routine for changing the size of the buffer of BytesIO objects.
178
   The caller should ensure that the 'size' argument is non-negative.  Returns
179
   0 on success, -1 otherwise. */
180
static int
181
resize_buffer_lock_held(bytesio *self, size_t size)
182
0
{
183
0
    _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
184
185
0
    assert(self->buf != NULL);
186
0
    assert(FT_ATOMIC_LOAD_SSIZE_RELAXED(self->exports) == 0);
187
188
    /* Here, unsigned types are used to avoid dealing with signed integer
189
       overflow, which is undefined in C. */
190
0
    size_t alloc = PyBytes_GET_SIZE(self->buf);
191
192
    /* For simplicity, stay in the range of the signed type. Anyway, Python
193
       doesn't allow strings to be longer than this. */
194
0
    if (size > PY_SSIZE_T_MAX)
195
0
        goto overflow;
196
197
0
    if (size < alloc / 2) {
198
        /* Major downsize; resize down to exact size. */
199
0
        alloc = size + 1;
200
0
    }
201
0
    else if (size < alloc) {
202
        /* Within allocated size; quick exit */
203
0
        return 0;
204
0
    }
205
0
    else if (size <= alloc * 1.125) {
206
        /* Moderate upsize; overallocate similar to list_resize() */
207
0
        alloc = size + (size >> 3) + (size < 9 ? 3 : 6);
208
0
    }
209
0
    else {
210
        /* Major upsize; resize up to exact size */
211
0
        alloc = size + 1;
212
0
    }
213
214
0
    if (SHARED_BUF(self)) {
215
0
        if (unshare_buffer_lock_held(self, alloc) < 0)
216
0
            return -1;
217
0
    }
218
0
    else {
219
0
        if (resize_unshared_buffer_lock_held(self, alloc) < 0)
220
0
            return -1;
221
0
    }
222
223
0
    return 0;
224
225
0
  overflow:
226
0
    PyErr_SetString(PyExc_OverflowError,
227
0
                    "new buffer size too large");
228
0
    return -1;
229
0
}
230
231
/* Internal routine for writing a string of bytes to the buffer of a BytesIO
232
   object. Returns the number of bytes written, or -1 on error.
233
   Inlining is disabled because it's significantly decreases performance
234
   of writelines() in PGO build. */
235
Py_NO_INLINE static Py_ssize_t
236
write_bytes_lock_held(bytesio *self, PyObject *b)
237
0
{
238
0
    _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
239
240
0
    Py_buffer buf;
241
0
    Py_ssize_t len;
242
0
    if (PyObject_GetBuffer(b, &buf, PyBUF_CONTIG_RO) < 0) {
243
0
        return -1;
244
0
    }
245
246
0
    if (check_closed(self) || check_exports(self)) {
247
0
        len = -1;
248
0
        goto done;
249
0
    }
250
251
0
    len = buf.len;
252
0
    if (len == 0) {
253
0
        goto done;
254
0
    }
255
256
0
    assert(self->pos >= 0);
257
0
    size_t endpos = (size_t)self->pos + len;
258
0
    if (endpos > (size_t)PyBytes_GET_SIZE(self->buf)) {
259
0
        if (resize_buffer_lock_held(self, endpos) < 0) {
260
0
            len = -1;
261
0
            goto done;
262
0
        }
263
0
    }
264
0
    else if (SHARED_BUF(self)) {
265
0
        if (unshare_buffer_lock_held(self, Py_MAX(endpos, (size_t)self->string_size)) < 0) {
266
0
            len = -1;
267
0
            goto done;
268
0
        }
269
0
    }
270
271
0
    if (self->pos > self->string_size) {
272
        /* In case of overseek, pad with null bytes the buffer region between
273
           the end of stream and the current position.
274
275
          0   lo      string_size                           hi
276
          |   |<---used--->|<----------available----------->|
277
          |   |            <--to pad-->|<---to write--->    |
278
          0   buf                   position
279
        */
280
0
        memset(PyBytes_AS_STRING(self->buf) + self->string_size, '\0',
281
0
               (self->pos - self->string_size) * sizeof(char));
282
0
    }
283
284
    /* Copy the data to the internal buffer, overwriting some of the existing
285
       data if self->pos < self->string_size. */
286
0
    memcpy(PyBytes_AS_STRING(self->buf) + self->pos, buf.buf, len);
287
0
    self->pos = endpos;
288
289
    /* Set the new length of the internal string if it has changed. */
290
0
    if ((size_t)self->string_size < endpos) {
291
0
        self->string_size = endpos;
292
0
    }
293
294
0
  done:
295
0
    PyBuffer_Release(&buf);
296
0
    return len;
297
0
}
298
299
static PyObject *
300
bytesio_get_closed(PyObject *op, void *Py_UNUSED(closure))
301
0
{
302
0
    PyObject *ret;
303
0
    bytesio *self = bytesio_CAST(op);
304
0
    Py_BEGIN_CRITICAL_SECTION(self);
305
0
    if (self->buf == NULL) {
306
0
        ret = Py_True;
307
0
    }
308
0
    else {
309
0
        ret = Py_False;
310
0
    }
311
0
    Py_END_CRITICAL_SECTION();
312
0
    return ret;
313
0
}
314
315
/*[clinic input]
316
@critical_section
317
_io.BytesIO.readable
318
319
Returns True if the IO object can be read.
320
[clinic start generated code]*/
321
322
static PyObject *
323
_io_BytesIO_readable_impl(bytesio *self)
324
/*[clinic end generated code: output=4e93822ad5b62263 input=ab7816facef48bfd]*/
325
0
{
326
0
    CHECK_CLOSED(self);
327
0
    Py_RETURN_TRUE;
328
0
}
329
330
/*[clinic input]
331
@critical_section
332
_io.BytesIO.writable
333
334
Returns True if the IO object can be written.
335
[clinic start generated code]*/
336
337
static PyObject *
338
_io_BytesIO_writable_impl(bytesio *self)
339
/*[clinic end generated code: output=64ff6a254b1150b8 input=4f35d49d26dab024]*/
340
0
{
341
0
    CHECK_CLOSED(self);
342
0
    Py_RETURN_TRUE;
343
0
}
344
345
/*[clinic input]
346
@critical_section
347
_io.BytesIO.seekable
348
349
Returns True if the IO object can be seeked.
350
[clinic start generated code]*/
351
352
static PyObject *
353
_io_BytesIO_seekable_impl(bytesio *self)
354
/*[clinic end generated code: output=6b417f46dcc09b56 input=9cc78d15aa1deaa3]*/
355
0
{
356
0
    CHECK_CLOSED(self);
357
0
    Py_RETURN_TRUE;
358
0
}
359
360
/*[clinic input]
361
@critical_section
362
_io.BytesIO.flush
363
364
Does nothing.
365
[clinic start generated code]*/
366
367
static PyObject *
368
_io_BytesIO_flush_impl(bytesio *self)
369
/*[clinic end generated code: output=187e3d781ca134a0 input=c60842743910b381]*/
370
0
{
371
0
    CHECK_CLOSED(self);
372
0
    Py_RETURN_NONE;
373
0
}
374
375
/*[clinic input]
376
@critical_section
377
_io.BytesIO.getbuffer
378
379
    cls: defining_class
380
    /
381
382
Get a read-write view over the contents of the BytesIO object.
383
[clinic start generated code]*/
384
385
static PyObject *
386
_io_BytesIO_getbuffer_impl(bytesio *self, PyTypeObject *cls)
387
/*[clinic end generated code: output=045091d7ce87fe4e input=8295764061be77fd]*/
388
0
{
389
0
    _PyIO_State *state = get_io_state_by_cls(cls);
390
0
    PyTypeObject *type = state->PyBytesIOBuffer_Type;
391
0
    bytesiobuf *buf;
392
0
    PyObject *view;
393
394
0
    CHECK_CLOSED(self);
395
396
0
    buf = (bytesiobuf *) type->tp_alloc(type, 0);
397
0
    if (buf == NULL)
398
0
        return NULL;
399
0
    buf->source = (bytesio*)Py_NewRef(self);
400
0
    view = PyMemoryView_FromObject((PyObject *) buf);
401
0
    Py_DECREF(buf);
402
0
    return view;
403
0
}
404
405
/*[clinic input]
406
@critical_section
407
_io.BytesIO.getvalue
408
409
Retrieve the entire contents of the BytesIO object.
410
[clinic start generated code]*/
411
412
static PyObject *
413
_io_BytesIO_getvalue_impl(bytesio *self)
414
/*[clinic end generated code: output=b3f6a3233c8fd628 input=c91bff398df0c352]*/
415
0
{
416
0
    CHECK_CLOSED(self);
417
0
    if (self->string_size <= 1 || FT_ATOMIC_LOAD_SSIZE_RELAXED(self->exports) > 0)
418
0
        return PyBytes_FromStringAndSize(PyBytes_AS_STRING(self->buf),
419
0
                                         self->string_size);
420
421
0
    if (self->string_size != PyBytes_GET_SIZE(self->buf)) {
422
0
        if (SHARED_BUF(self)) {
423
0
            if (unshare_buffer_lock_held(self, self->string_size) < 0)
424
0
                return NULL;
425
0
        }
426
0
        else {
427
0
            if (resize_unshared_buffer_lock_held(self, self->string_size) < 0)
428
0
                return NULL;
429
0
        }
430
0
    }
431
0
    set_shared_buf(self);
432
0
    return Py_NewRef(self->buf);
433
0
}
434
435
/*[clinic input]
436
@critical_section
437
_io.BytesIO.isatty
438
439
Always returns False.
440
441
BytesIO objects are not connected to a TTY-like device.
442
[clinic start generated code]*/
443
444
static PyObject *
445
_io_BytesIO_isatty_impl(bytesio *self)
446
/*[clinic end generated code: output=df67712e669f6c8f input=50487b74dc5ae8a9]*/
447
0
{
448
0
    CHECK_CLOSED(self);
449
0
    Py_RETURN_FALSE;
450
0
}
451
452
/*[clinic input]
453
@critical_section
454
_io.BytesIO.tell
455
456
Current file position, an integer.
457
[clinic start generated code]*/
458
459
static PyObject *
460
_io_BytesIO_tell_impl(bytesio *self)
461
/*[clinic end generated code: output=b54b0f93cd0e5e1d input=2c7b0e8f82e05c4d]*/
462
0
{
463
0
    CHECK_CLOSED(self);
464
0
    return PyLong_FromSsize_t(self->pos);
465
0
}
466
467
/* Read without advancing position. */
468
static PyObject *
469
peek_bytes_lock_held(bytesio *self, Py_ssize_t size)
470
0
{
471
0
    _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
472
473
0
    const char *output;
474
475
0
    assert(self->buf != NULL);
476
0
    assert(size <= self->string_size);
477
0
    if (size > 1 &&
478
0
        self->pos == 0 && size == PyBytes_GET_SIZE(self->buf) &&
479
0
        FT_ATOMIC_LOAD_SSIZE_RELAXED(self->exports) == 0) {
480
0
        set_shared_buf(self);
481
0
        return Py_NewRef(self->buf);
482
0
    }
483
484
    /* gh-141311: Avoid undefined behavior when self->pos (limit PY_SSIZE_T_MAX)
485
       is beyond the size of self->buf. Assert above validates size is always in
486
       bounds. When self->pos is out of bounds calling code sets size to 0. */
487
0
    if (size == 0) {
488
0
        return PyBytes_FromStringAndSize(NULL, 0);
489
0
    }
490
491
0
    output = PyBytes_AS_STRING(self->buf) + self->pos;
492
0
    return PyBytes_FromStringAndSize(output, size);
493
0
}
494
495
static PyObject *
496
read_bytes_lock_held(bytesio *self, Py_ssize_t size)
497
0
{
498
0
    PyObject *bytes = peek_bytes_lock_held(self, size);
499
0
    if (bytes != NULL) {
500
0
        assert(PyBytes_GET_SIZE(bytes) == size);
501
0
        self->pos += size;
502
0
    }
503
0
    return bytes;
504
0
}
505
506
/*[clinic input]
507
@critical_section
508
_io.BytesIO.read
509
    size: Py_ssize_t(accept={int, NoneType}) = -1
510
    /
511
512
Read at most size bytes, returned as a bytes object.
513
514
If the size argument is negative, read until EOF is reached.
515
Return an empty bytes object at EOF.
516
[clinic start generated code]*/
517
518
static PyObject *
519
_io_BytesIO_read_impl(bytesio *self, Py_ssize_t size)
520
/*[clinic end generated code: output=9cc025f21c75bdd2 input=9e2f7ff3075fdd39]*/
521
0
{
522
0
    Py_ssize_t n;
523
524
0
    CHECK_CLOSED(self);
525
526
    /* adjust invalid sizes */
527
0
    n = self->string_size - self->pos;
528
0
    if (size < 0 || size > n) {
529
0
        size = n;
530
0
        if (size < 0)
531
0
            size = 0;
532
0
    }
533
534
0
    return read_bytes_lock_held(self, size);
535
0
}
536
537
538
/*[clinic input]
539
@critical_section
540
_io.BytesIO.read1
541
    size: Py_ssize_t(accept={int, NoneType}) = -1
542
    /
543
544
Read at most size bytes, returned as a bytes object.
545
546
If the size argument is negative or omitted, read until EOF is
547
reached.  Return an empty bytes object at EOF.
548
[clinic start generated code]*/
549
550
static PyObject *
551
_io_BytesIO_read1_impl(bytesio *self, Py_ssize_t size)
552
/*[clinic end generated code: output=d0f843285aa95f1c input=796ff4e0efccc4d9]*/
553
0
{
554
0
    return _io_BytesIO_read_impl(self, size);
555
0
}
556
557
558
/*[clinic input]
559
@critical_section
560
_io.BytesIO.peek
561
    size: Py_ssize_t = 0
562
    /
563
564
Return bytes from the stream without advancing the position.
565
566
Return an empty bytes object at EOF.
567
[clinic start generated code]*/
568
569
static PyObject *
570
_io_BytesIO_peek_impl(bytesio *self, Py_ssize_t size)
571
/*[clinic end generated code: output=fa4d8ce28b35db9b input=2ce74234b10aec3e]*/
572
0
{
573
0
    CHECK_CLOSED(self);
574
575
0
    if (size < 1) {
576
0
        size = DEFAULT_BUFFER_SIZE;
577
0
    }
578
579
    /* adjust invalid sizes */
580
0
    Py_ssize_t n = self->string_size - self->pos;
581
0
    if (size > n) {
582
0
        size = n;
583
        /* n can be negative after truncate() or seek() */
584
0
        if (size < 0) {
585
0
            size = 0;
586
0
        }
587
0
    }
588
0
    return peek_bytes_lock_held(self, size);
589
0
}
590
591
592
/*[clinic input]
593
@critical_section
594
_io.BytesIO.readline
595
    size: Py_ssize_t(accept={int, NoneType}) = -1
596
    /
597
598
Next line from the file, as a bytes object.
599
600
Retain newline.  A non-negative size argument limits the maximum
601
number of bytes to return (an incomplete line may be returned then).
602
Return an empty bytes object at EOF.
603
[clinic start generated code]*/
604
605
static PyObject *
606
_io_BytesIO_readline_impl(bytesio *self, Py_ssize_t size)
607
/*[clinic end generated code: output=4bff3c251df8ffcd input=db09d47e23cf2c9e]*/
608
0
{
609
0
    Py_ssize_t n;
610
611
0
    CHECK_CLOSED(self);
612
613
0
    n = scan_eol_lock_held(self, size);
614
615
0
    return read_bytes_lock_held(self, n);
616
0
}
617
618
/*[clinic input]
619
@critical_section
620
_io.BytesIO.readlines
621
    size as arg: object = None
622
    /
623
624
List of bytes objects, each a line from the file.
625
626
Call readline() repeatedly and return a list of the lines so read.
627
The optional size argument, if given, is an approximate bound on the
628
total number of bytes in the lines returned.
629
[clinic start generated code]*/
630
631
static PyObject *
632
_io_BytesIO_readlines_impl(bytesio *self, PyObject *arg)
633
/*[clinic end generated code: output=09b8e34c880808ff input=5c57d7d78e409985]*/
634
0
{
635
0
    Py_ssize_t maxsize, size, n;
636
0
    PyObject *result, *line;
637
0
    const char *output;
638
639
0
    CHECK_CLOSED(self);
640
641
0
    if (PyLong_Check(arg)) {
642
0
        maxsize = PyLong_AsSsize_t(arg);
643
0
        if (maxsize == -1 && PyErr_Occurred())
644
0
            return NULL;
645
0
    }
646
0
    else if (arg == Py_None) {
647
        /* No size limit, by default. */
648
0
        maxsize = -1;
649
0
    }
650
0
    else {
651
0
        PyErr_Format(PyExc_TypeError, "integer argument expected, got '%s'",
652
0
                     Py_TYPE(arg)->tp_name);
653
0
        return NULL;
654
0
    }
655
656
0
    size = 0;
657
0
    result = PyList_New(0);
658
0
    if (!result)
659
0
        return NULL;
660
661
0
    output = PyBytes_AS_STRING(self->buf) + self->pos;
662
0
    while ((n = scan_eol_lock_held(self, -1)) != 0) {
663
0
        self->pos += n;
664
0
        line = PyBytes_FromStringAndSize(output, n);
665
0
        if (!line)
666
0
            goto on_error;
667
0
        if (PyList_Append(result, line) == -1) {
668
0
            Py_DECREF(line);
669
0
            goto on_error;
670
0
        }
671
0
        Py_DECREF(line);
672
0
        size += n;
673
0
        if (maxsize > 0 && size >= maxsize)
674
0
            break;
675
0
        output += n;
676
0
    }
677
0
    return result;
678
679
0
  on_error:
680
0
    Py_DECREF(result);
681
0
    return NULL;
682
0
}
683
684
/*[clinic input]
685
@critical_section
686
_io.BytesIO.readinto
687
    buffer: Py_buffer(accept={rwbuffer})
688
    /
689
690
Read bytes into buffer.
691
692
Returns number of bytes read (0 for EOF), or None if the object
693
is set not to block and has no data to read.
694
[clinic start generated code]*/
695
696
static PyObject *
697
_io_BytesIO_readinto_impl(bytesio *self, Py_buffer *buffer)
698
/*[clinic end generated code: output=a5d407217dcf0639 input=093a8d330de3fcd1]*/
699
0
{
700
0
    Py_ssize_t len, n;
701
702
0
    CHECK_CLOSED(self);
703
704
    /* adjust invalid sizes */
705
0
    len = buffer->len;
706
0
    n = self->string_size - self->pos;
707
0
    if (len > n) {
708
0
        len = n;
709
0
        if (len < 0) {
710
            /* gh-141311: Avoid undefined behavior when self->pos (limit
711
               PY_SSIZE_T_MAX) points beyond the size of self->buf. */
712
0
            return PyLong_FromSsize_t(0);
713
0
        }
714
0
    }
715
716
0
    assert(self->pos + len <= PY_SSIZE_T_MAX);
717
0
    assert(len >= 0);
718
0
    memcpy(buffer->buf, PyBytes_AS_STRING(self->buf) + self->pos, len);
719
0
    self->pos += len;
720
721
0
    return PyLong_FromSsize_t(len);
722
0
}
723
724
/*[clinic input]
725
@critical_section
726
_io.BytesIO.truncate
727
    size: object = None
728
    /
729
730
Truncate the file to at most size bytes.
731
732
Size defaults to the current file position, as returned by tell().
733
The current file position is unchanged.  Returns the new size.
734
[clinic start generated code]*/
735
736
static PyObject *
737
_io_BytesIO_truncate_impl(bytesio *self, PyObject *size)
738
/*[clinic end generated code: output=ab42491b4824f384 input=b4acb5f80481c053]*/
739
0
{
740
0
    CHECK_CLOSED(self);
741
0
    CHECK_EXPORTS(self);
742
743
0
    Py_ssize_t new_size;
744
745
0
    if (size == Py_None) {
746
0
        new_size = self->pos;
747
0
    }
748
0
    else {
749
0
        new_size = PyLong_AsLong(size);
750
0
        if (new_size == -1 && PyErr_Occurred()) {
751
0
            return NULL;
752
0
        }
753
0
        if (new_size < 0) {
754
0
            PyErr_Format(PyExc_ValueError,
755
0
                         "negative size value %zd", new_size);
756
0
            return NULL;
757
0
        }
758
0
    }
759
760
0
    if (new_size < self->string_size) {
761
0
        self->string_size = new_size;
762
0
        if (resize_buffer_lock_held(self, new_size) < 0)
763
0
            return NULL;
764
0
    }
765
766
0
    return PyLong_FromSsize_t(new_size);
767
0
}
768
769
static PyObject *
770
bytesio_iternext_lock_held(PyObject *op)
771
0
{
772
0
    _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
773
774
0
    Py_ssize_t n;
775
0
    bytesio *self = bytesio_CAST(op);
776
777
0
    CHECK_CLOSED(self);
778
779
0
    n = scan_eol_lock_held(self, -1);
780
781
0
    if (n == 0)
782
0
        return NULL;
783
784
0
    return read_bytes_lock_held(self, n);
785
0
}
786
787
static PyObject *
788
bytesio_iternext(PyObject *op)
789
0
{
790
0
    PyObject *ret;
791
0
    Py_BEGIN_CRITICAL_SECTION(op);
792
0
    ret = bytesio_iternext_lock_held(op);
793
0
    Py_END_CRITICAL_SECTION();
794
0
    return ret;
795
0
}
796
797
/*[clinic input]
798
@critical_section
799
_io.BytesIO.seek
800
    pos: Py_ssize_t
801
    whence: int = 0
802
    /
803
804
Change stream position.
805
806
Seek to byte offset pos relative to position indicated by whence:
807
     0  Start of stream (the default).  pos should be >= 0;
808
     1  Current position - pos may be negative;
809
     2  End of stream - pos usually negative.
810
Returns the new absolute position.
811
[clinic start generated code]*/
812
813
static PyObject *
814
_io_BytesIO_seek_impl(bytesio *self, Py_ssize_t pos, int whence)
815
/*[clinic end generated code: output=c26204a68e9190e4 input=20f05ddf659255df]*/
816
0
{
817
0
    CHECK_CLOSED(self);
818
819
0
    if (pos < 0 && whence == 0) {
820
0
        PyErr_Format(PyExc_ValueError,
821
0
                     "negative seek value %zd", pos);
822
0
        return NULL;
823
0
    }
824
825
    /* whence = 0: offset relative to beginning of the string.
826
       whence = 1: offset relative to current position.
827
       whence = 2: offset relative the end of the string. */
828
0
    if (whence == 1) {
829
0
        if (pos > PY_SSIZE_T_MAX - self->pos) {
830
0
            PyErr_SetString(PyExc_OverflowError,
831
0
                            "new position too large");
832
0
            return NULL;
833
0
        }
834
0
        pos += self->pos;
835
0
    }
836
0
    else if (whence == 2) {
837
0
        if (pos > PY_SSIZE_T_MAX - self->string_size) {
838
0
            PyErr_SetString(PyExc_OverflowError,
839
0
                            "new position too large");
840
0
            return NULL;
841
0
        }
842
0
        pos += self->string_size;
843
0
    }
844
0
    else if (whence != 0) {
845
0
        PyErr_Format(PyExc_ValueError,
846
0
                     "invalid whence (%i, should be 0, 1 or 2)", whence);
847
0
        return NULL;
848
0
    }
849
850
0
    if (pos < 0)
851
0
        pos = 0;
852
0
    self->pos = pos;
853
854
0
    return PyLong_FromSsize_t(self->pos);
855
0
}
856
857
/*[clinic input]
858
@critical_section
859
_io.BytesIO.write
860
    b: object
861
    /
862
863
Write bytes to file.
864
865
Return the number of bytes written.
866
[clinic start generated code]*/
867
868
static PyObject *
869
_io_BytesIO_write_impl(bytesio *self, PyObject *b)
870
/*[clinic end generated code: output=d3e46bcec8d9e21c input=46c0c17eac7474a4]*/
871
0
{
872
0
    Py_ssize_t n = write_bytes_lock_held(self, b);
873
0
    return n >= 0 ? PyLong_FromSsize_t(n) : NULL;
874
0
}
875
876
/*[clinic input]
877
@critical_section
878
_io.BytesIO.writelines
879
    lines: object
880
    /
881
882
Write lines to the file.
883
884
Note that newlines are not added.  lines can be any iterable object
885
producing bytes-like objects.  This is equivalent to calling write()
886
for each element.
887
[clinic start generated code]*/
888
889
static PyObject *
890
_io_BytesIO_writelines_impl(bytesio *self, PyObject *lines)
891
/*[clinic end generated code: output=03a43a75773bc397 input=d265f76533b058e7]*/
892
0
{
893
0
    PyObject *it, *item;
894
895
0
    CHECK_CLOSED(self);
896
897
0
    it = PyObject_GetIter(lines);
898
0
    if (it == NULL)
899
0
        return NULL;
900
901
0
    while ((item = PyIter_Next(it)) != NULL) {
902
0
        Py_ssize_t ret = write_bytes_lock_held(self, item);
903
0
        Py_DECREF(item);
904
0
        if (ret < 0) {
905
0
            Py_DECREF(it);
906
0
            return NULL;
907
0
        }
908
0
    }
909
0
    Py_DECREF(it);
910
911
    /* See if PyIter_Next failed */
912
0
    if (PyErr_Occurred())
913
0
        return NULL;
914
915
0
    Py_RETURN_NONE;
916
0
}
917
918
/*[clinic input]
919
@critical_section
920
_io.BytesIO.close
921
922
Disable all I/O operations.
923
[clinic start generated code]*/
924
925
static PyObject *
926
_io_BytesIO_close_impl(bytesio *self)
927
/*[clinic end generated code: output=1471bb9411af84a0 input=34ce76d8bd17a23b]*/
928
0
{
929
    /* The exported buffers keep the internal buffer alive. */
930
0
    Py_CLEAR(self->buf);
931
0
    Py_RETURN_NONE;
932
0
}
933
934
/* Pickling support.
935
936
   Note that only pickle protocol 2 and onward are supported since we use
937
   extended __reduce__ API of PEP 307 to make BytesIO instances picklable.
938
939
   Providing support for protocol < 2 would require the __reduce_ex__ method
940
   which is notably long-winded when defined properly.
941
942
   For BytesIO, the implementation would similar to one coded for
943
   object.__reduce_ex__, but slightly less general. To be more specific, we
944
   could call bytesio_getstate directly and avoid checking for the presence of
945
   a fallback __reduce__ method. However, we would still need a __newobj__
946
   function to use the efficient instance representation of PEP 307.
947
 */
948
949
 static PyObject *
950
 bytesio_getstate_lock_held(PyObject *op)
951
0
 {
952
0
     _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
953
954
0
     bytesio *self = bytesio_CAST(op);
955
0
     PyObject *initvalue = _io_BytesIO_getvalue_impl(self);
956
0
     PyObject *dict;
957
0
     PyObject *state;
958
959
0
     if (initvalue == NULL)
960
0
         return NULL;
961
0
     if (self->dict == NULL) {
962
0
         dict = Py_NewRef(Py_None);
963
0
     }
964
0
     else {
965
0
         dict = PyDict_Copy(self->dict);
966
0
         if (dict == NULL) {
967
0
             Py_DECREF(initvalue);
968
0
             return NULL;
969
0
         }
970
0
     }
971
972
0
     state = Py_BuildValue("(OnN)", initvalue, self->pos, dict);
973
0
     Py_DECREF(initvalue);
974
0
     return state;
975
0
}
976
977
static PyObject *
978
bytesio_getstate(PyObject *op, PyObject *Py_UNUSED(dummy))
979
0
{
980
0
    PyObject *ret;
981
0
    Py_BEGIN_CRITICAL_SECTION(op);
982
0
    ret = bytesio_getstate_lock_held(op);
983
0
    Py_END_CRITICAL_SECTION();
984
0
    return ret;
985
0
}
986
987
static PyObject *
988
bytesio_setstate_lock_held(PyObject *op, PyObject *state)
989
0
{
990
0
    _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
991
992
0
    PyObject *result;
993
0
    PyObject *position_obj;
994
0
    PyObject *dict;
995
0
    Py_ssize_t pos;
996
0
    bytesio *self = bytesio_CAST(op);
997
998
0
    assert(state != NULL);
999
1000
    /* We allow the state tuple to be longer than 3, because we may need
1001
       someday to extend the object's state without breaking
1002
       backward-compatibility. */
1003
0
    if (!PyTuple_Check(state) || PyTuple_GET_SIZE(state) < 3) {
1004
0
        PyErr_Format(PyExc_TypeError,
1005
0
                     "%.200s.__setstate__ argument should be 3-tuple, got %.200s",
1006
0
                     Py_TYPE(self)->tp_name, Py_TYPE(state)->tp_name);
1007
0
        return NULL;
1008
0
    }
1009
0
    CHECK_EXPORTS(self);
1010
    /* Reset the object to its default state. This is only needed to handle
1011
       the case of repeated calls to __setstate__. */
1012
0
    self->string_size = 0;
1013
0
    self->pos = 0;
1014
1015
    /* Set the value of the internal buffer. If state[0] does not support the
1016
       buffer protocol, bytesio_write will raise the appropriate TypeError. */
1017
0
    result = _io_BytesIO_write_impl(self, PyTuple_GET_ITEM(state, 0));
1018
0
    if (result == NULL)
1019
0
        return NULL;
1020
0
    Py_DECREF(result);
1021
1022
    /* Set carefully the position value. Alternatively, we could use the seek
1023
       method instead of modifying self->pos directly to better protect the
1024
       object internal state against erroneous (or malicious) inputs. */
1025
0
    position_obj = PyTuple_GET_ITEM(state, 1);
1026
0
    if (!PyLong_Check(position_obj)) {
1027
0
        PyErr_Format(PyExc_TypeError,
1028
0
                     "second item of state must be an integer, not %.200s",
1029
0
                     Py_TYPE(position_obj)->tp_name);
1030
0
        return NULL;
1031
0
    }
1032
0
    pos = PyLong_AsSsize_t(position_obj);
1033
0
    if (pos == -1 && PyErr_Occurred())
1034
0
        return NULL;
1035
0
    if (pos < 0) {
1036
0
        PyErr_SetString(PyExc_ValueError,
1037
0
                        "position value cannot be negative");
1038
0
        return NULL;
1039
0
    }
1040
0
    self->pos = pos;
1041
1042
    /* Set the dictionary of the instance variables. */
1043
0
    dict = PyTuple_GET_ITEM(state, 2);
1044
0
    if (dict != Py_None) {
1045
0
        if (!PyDict_Check(dict)) {
1046
0
            PyErr_Format(PyExc_TypeError,
1047
0
                         "third item of state should be a dict, got a %.200s",
1048
0
                         Py_TYPE(dict)->tp_name);
1049
0
            return NULL;
1050
0
        }
1051
0
        if (self->dict) {
1052
            /* Alternatively, we could replace the internal dictionary
1053
               completely. However, it seems more practical to just update it. */
1054
0
            if (PyDict_Update(self->dict, dict) < 0)
1055
0
                return NULL;
1056
0
        }
1057
0
        else {
1058
            /* The LOAD_ATTR specializations read the dict slot lock-free
1059
               with an acquire load, so pair it with a release store. */
1060
0
            FT_ATOMIC_STORE_PTR_RELEASE(self->dict, Py_NewRef(dict));
1061
0
        }
1062
0
    }
1063
1064
0
    Py_RETURN_NONE;
1065
0
}
1066
1067
static PyObject *
1068
bytesio_setstate(PyObject *op, PyObject *state)
1069
0
{
1070
0
    PyObject *ret;
1071
0
    Py_BEGIN_CRITICAL_SECTION(op);
1072
0
    ret = bytesio_setstate_lock_held(op, state);
1073
0
    Py_END_CRITICAL_SECTION();
1074
0
    return ret;
1075
0
}
1076
1077
static void
1078
bytesio_dealloc(PyObject *op)
1079
0
{
1080
0
    bytesio *self = bytesio_CAST(op);
1081
0
    PyTypeObject *tp = Py_TYPE(self);
1082
0
    _PyObject_GC_UNTRACK(self);
1083
0
    if (FT_ATOMIC_LOAD_SSIZE_RELAXED(self->exports) > 0) {
1084
0
        PyErr_SetString(PyExc_SystemError,
1085
0
                        "deallocated BytesIO object has exported buffers");
1086
0
        PyErr_Print();
1087
0
    }
1088
0
    Py_CLEAR(self->buf);
1089
0
    Py_CLEAR(self->dict);
1090
0
    FT_CLEAR_WEAKREFS(op, self->weakreflist);
1091
0
    tp->tp_free(self);
1092
0
    Py_DECREF(tp);
1093
0
}
1094
1095
static PyObject *
1096
bytesio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
1097
0
{
1098
0
    bytesio *self;
1099
1100
0
    assert(type != NULL && type->tp_alloc != NULL);
1101
0
    self = (bytesio *)type->tp_alloc(type, 0);
1102
0
    if (self == NULL)
1103
0
        return NULL;
1104
1105
    /* tp_alloc initializes all the fields to zero. So we don't have to
1106
       initialize them here. */
1107
1108
0
    self->buf = PyBytes_FromStringAndSize(NULL, 0);
1109
0
    if (self->buf == NULL) {
1110
0
        Py_DECREF(self);
1111
0
        return PyErr_NoMemory();
1112
0
    }
1113
1114
0
    return (PyObject *)self;
1115
0
}
1116
1117
/*[clinic input]
1118
@critical_section
1119
_io.BytesIO.__init__
1120
    initial_bytes as initvalue: object(c_default="NULL") = b''
1121
1122
Buffered I/O implementation using an in-memory bytes buffer.
1123
[clinic start generated code]*/
1124
1125
static int
1126
_io_BytesIO___init___impl(bytesio *self, PyObject *initvalue)
1127
/*[clinic end generated code: output=65c0c51e24c5b621 input=3da5a74ee4c4f1ac]*/
1128
0
{
1129
    /* In case, __init__ is called multiple times. */
1130
0
    self->string_size = 0;
1131
0
    self->pos = 0;
1132
1133
0
    if (FT_ATOMIC_LOAD_SSIZE_RELAXED(self->exports) > 0) {
1134
0
        PyErr_SetString(PyExc_BufferError,
1135
0
                        "Existing exports of data: object cannot be re-sized");
1136
0
        return -1;
1137
0
    }
1138
0
    if (initvalue && initvalue != Py_None) {
1139
0
        if (PyBytes_CheckExact(initvalue)) {
1140
0
            Py_XSETREF(self->buf, Py_NewRef(initvalue));
1141
0
            clear_shared_buf(self);
1142
0
            self->string_size = PyBytes_GET_SIZE(initvalue);
1143
0
        }
1144
0
        else {
1145
0
            PyObject *res;
1146
0
            res = _io_BytesIO_write_impl(self, initvalue);
1147
0
            if (res == NULL)
1148
0
                return -1;
1149
0
            Py_DECREF(res);
1150
0
            self->pos = 0;
1151
0
        }
1152
0
    }
1153
1154
0
    return 0;
1155
0
}
1156
1157
static PyObject *
1158
bytesio_sizeof_lock_held(PyObject *op)
1159
0
{
1160
0
    _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
1161
1162
0
    bytesio *self = bytesio_CAST(op);
1163
0
    size_t res = _PyObject_SIZE(Py_TYPE(self));
1164
0
    if (self->buf && !SHARED_BUF(self)) {
1165
0
        size_t s = _PySys_GetSizeOf(self->buf);
1166
0
        if (s == (size_t)-1) {
1167
0
            return NULL;
1168
0
        }
1169
0
        res += s;
1170
0
    }
1171
0
    return PyLong_FromSize_t(res);
1172
0
}
1173
1174
static PyObject *
1175
bytesio_sizeof(PyObject *op, PyObject *Py_UNUSED(dummy))
1176
0
{
1177
0
    PyObject *ret;
1178
0
    Py_BEGIN_CRITICAL_SECTION(op);
1179
0
    ret = bytesio_sizeof_lock_held(op);
1180
0
    Py_END_CRITICAL_SECTION();
1181
0
    return ret;
1182
0
}
1183
1184
static int
1185
bytesio_traverse(PyObject *op, visitproc visit, void *arg)
1186
0
{
1187
0
    bytesio *self = bytesio_CAST(op);
1188
0
    Py_VISIT(Py_TYPE(self));
1189
0
    Py_VISIT(self->dict);
1190
0
    Py_VISIT(self->buf);
1191
0
    return 0;
1192
0
}
1193
1194
static int
1195
bytesio_clear(PyObject *op)
1196
0
{
1197
0
    bytesio *self = bytesio_CAST(op);
1198
0
    Py_CLEAR(self->dict);
1199
0
    if (FT_ATOMIC_LOAD_SSIZE_RELAXED(self->exports) == 0) {
1200
0
        Py_CLEAR(self->buf);
1201
0
    }
1202
0
    return 0;
1203
0
}
1204
1205
1206
#define clinic_state() (find_io_state_by_def(Py_TYPE(self)))
1207
#include "clinic/bytesio.c.h"
1208
#undef clinic_state
1209
1210
static PyGetSetDef bytesio_getsetlist[] = {
1211
    {"closed",  bytesio_get_closed, NULL,
1212
     "True if the file is closed."},
1213
    {NULL},            /* sentinel */
1214
};
1215
1216
static struct PyMethodDef bytesio_methods[] = {
1217
    _IO_BYTESIO_READABLE_METHODDEF
1218
    _IO_BYTESIO_SEEKABLE_METHODDEF
1219
    _IO_BYTESIO_WRITABLE_METHODDEF
1220
    _IO_BYTESIO_CLOSE_METHODDEF
1221
    _IO_BYTESIO_FLUSH_METHODDEF
1222
    _IO_BYTESIO_ISATTY_METHODDEF
1223
    _IO_BYTESIO_TELL_METHODDEF
1224
    _IO_BYTESIO_WRITE_METHODDEF
1225
    _IO_BYTESIO_WRITELINES_METHODDEF
1226
    _IO_BYTESIO_READ1_METHODDEF
1227
    _IO_BYTESIO_READINTO_METHODDEF
1228
    _IO_BYTESIO_READLINE_METHODDEF
1229
    _IO_BYTESIO_READLINES_METHODDEF
1230
    _IO_BYTESIO_READ_METHODDEF
1231
    _IO_BYTESIO_PEEK_METHODDEF
1232
    _IO_BYTESIO_GETBUFFER_METHODDEF
1233
    _IO_BYTESIO_GETVALUE_METHODDEF
1234
    _IO_BYTESIO_SEEK_METHODDEF
1235
    _IO_BYTESIO_TRUNCATE_METHODDEF
1236
    {"__getstate__",  bytesio_getstate,  METH_NOARGS, NULL},
1237
    {"__setstate__",  bytesio_setstate,  METH_O, NULL},
1238
    {"__sizeof__", bytesio_sizeof,     METH_NOARGS, NULL},
1239
    {NULL, NULL}        /* sentinel */
1240
};
1241
1242
static PyMemberDef bytesio_members[] = {
1243
    {"__weaklistoffset__", Py_T_PYSSIZET, offsetof(bytesio, weakreflist), Py_READONLY},
1244
    {"__dictoffset__", Py_T_PYSSIZET, offsetof(bytesio, dict), Py_READONLY},
1245
    {NULL}
1246
};
1247
1248
static PyType_Slot bytesio_slots[] = {
1249
    {Py_tp_dealloc, bytesio_dealloc},
1250
    {Py_tp_doc, (void *)_io_BytesIO___init____doc__},
1251
    {Py_tp_traverse, bytesio_traverse},
1252
    {Py_tp_clear, bytesio_clear},
1253
    {Py_tp_iter, PyObject_SelfIter},
1254
    {Py_tp_iternext, bytesio_iternext},
1255
    {Py_tp_methods, bytesio_methods},
1256
    {Py_tp_members, bytesio_members},
1257
    {Py_tp_getset, bytesio_getsetlist},
1258
    {Py_tp_init, _io_BytesIO___init__},
1259
    {Py_tp_new, bytesio_new},
1260
    {0, NULL},
1261
};
1262
1263
PyType_Spec _Py_bytesio_spec = {
1264
    .name = "_io.BytesIO",
1265
    .basicsize = sizeof(bytesio),
1266
    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
1267
              Py_TPFLAGS_IMMUTABLETYPE),
1268
    .slots = bytesio_slots,
1269
};
1270
1271
/*
1272
 * Implementation of the small intermediate object used by getbuffer().
1273
 * getbuffer() returns a memoryview over this object, which should make it
1274
 * invisible from Python code.
1275
 */
1276
1277
static int
1278
bytesiobuf_getbuffer_lock_held(PyObject *op, Py_buffer *view, int flags)
1279
0
{
1280
0
    bytesiobuf *obj = bytesiobuf_CAST(op);
1281
0
    bytesio *b = bytesio_CAST(obj->source);
1282
1283
0
    _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(b);
1284
1285
0
    if (check_closed(b)) {
1286
0
        return -1;
1287
0
    }
1288
0
    if (FT_ATOMIC_LOAD_SSIZE_RELAXED(b->exports) == 0 && SHARED_BUF(b)) {
1289
0
        if (unshare_buffer_lock_held(b, b->string_size) < 0)
1290
0
            return -1;
1291
0
    }
1292
1293
    /* cannot fail if view != NULL and readonly == 0 */
1294
0
    (void)PyBuffer_FillInfo(view, op,
1295
0
                            PyBytes_AS_STRING(b->buf), b->string_size,
1296
0
                            0, flags);
1297
    /* Keep the internal buffer alive: the bytesio object can be closed
1298
       while the buffer is exported. */
1299
0
    view->internal = Py_NewRef(b->buf);
1300
0
    FT_ATOMIC_ADD_SSIZE(b->exports, 1);
1301
0
    return 0;
1302
0
}
1303
1304
static int
1305
bytesiobuf_getbuffer(PyObject *op, Py_buffer *view, int flags)
1306
0
{
1307
0
    if (view == NULL) {
1308
0
        PyErr_SetString(PyExc_BufferError,
1309
0
            "bytesiobuf_getbuffer: view==NULL argument is obsolete");
1310
0
        return -1;
1311
0
    }
1312
1313
0
    int ret;
1314
0
    Py_BEGIN_CRITICAL_SECTION(bytesiobuf_CAST(op)->source);
1315
0
    ret = bytesiobuf_getbuffer_lock_held(op, view, flags);
1316
0
    Py_END_CRITICAL_SECTION();
1317
0
    return ret;
1318
0
}
1319
1320
static void
1321
bytesiobuf_releasebuffer(PyObject *op, Py_buffer *view)
1322
0
{
1323
0
    bytesiobuf *obj = bytesiobuf_CAST(op);
1324
0
    bytesio *b = bytesio_CAST(obj->source);
1325
0
    FT_ATOMIC_ADD_SSIZE(b->exports, -1);
1326
0
    Py_CLEAR(view->internal);
1327
0
}
1328
1329
static int
1330
bytesiobuf_traverse(PyObject *op, visitproc visit, void *arg)
1331
0
{
1332
0
    bytesiobuf *self = bytesiobuf_CAST(op);
1333
0
    Py_VISIT(Py_TYPE(self));
1334
0
    Py_VISIT(self->source);
1335
0
    return 0;
1336
0
}
1337
1338
static void
1339
bytesiobuf_dealloc(PyObject *op)
1340
0
{
1341
0
    bytesiobuf *self = bytesiobuf_CAST(op);
1342
0
    PyTypeObject *tp = Py_TYPE(self);
1343
    /* bpo-31095: UnTrack is needed before calling any callbacks */
1344
0
    PyObject_GC_UnTrack(op);
1345
0
    Py_CLEAR(self->source);
1346
0
    tp->tp_free(self);
1347
0
    Py_DECREF(tp);
1348
0
}
1349
1350
static PyType_Slot bytesiobuf_slots[] = {
1351
    {Py_tp_dealloc, bytesiobuf_dealloc},
1352
    {Py_tp_traverse, bytesiobuf_traverse},
1353
1354
    // Buffer protocol
1355
    {Py_bf_getbuffer, bytesiobuf_getbuffer},
1356
    {Py_bf_releasebuffer, bytesiobuf_releasebuffer},
1357
    {0, NULL},
1358
};
1359
1360
PyType_Spec _Py_bytesiobuf_spec = {
1361
    .name = "_io._BytesIOBuffer",
1362
    .basicsize = sizeof(bytesiobuf),
1363
    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
1364
              Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION),
1365
    .slots = bytesiobuf_slots,
1366
};