Coverage Report

Created: 2026-10-31 06:21

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