Skip to content

Commit a9aa5e2

Browse files
committed
Support writing/reading bytes in librt.internal
1 parent 94d0d7e commit a9aa5e2

File tree

6 files changed

+165
-28
lines changed

6 files changed

+165
-28
lines changed

mypy/typeshed/stubs/librt/librt/internal.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ def write_bool(data: Buffer, value: bool) -> None: ...
88
def read_bool(data: Buffer) -> bool: ...
99
def write_str(data: Buffer, value: str) -> None: ...
1010
def read_str(data: Buffer) -> str: ...
11+
def write_bytes(data: Buffer, value: bytes) -> None: ...
12+
def read_bytes(data: Buffer) -> bytes: ...
1113
def write_float(data: Buffer, value: float) -> None: ...
1214
def read_float(data: Buffer) -> float: ...
1315
def write_int(data: Buffer, value: int) -> None: ...

mypyc/lib-rt/librt_internal.c

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,100 @@ write_str(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames
346346
return Py_None;
347347
}
348348

349+
/*
350+
bytes format: size followed by bytes
351+
short bytes (len <= 127): single byte for size as `(uint8_t)size << 1`
352+
long bytes: \x01 followed by size as Py_ssize_t
353+
*/
354+
355+
static PyObject*
356+
read_bytes_internal(PyObject *data) {
357+
_CHECK_BUFFER(data, NULL)
358+
359+
// Read length.
360+
Py_ssize_t size;
361+
_CHECK_READ(data, 1, NULL)
362+
uint8_t first = _READ(data, uint8_t)
363+
if (likely(first != LONG_STR_TAG)) {
364+
// Common case: short bytes (len <= 127).
365+
size = (Py_ssize_t)(first >> 1);
366+
} else {
367+
_CHECK_READ(data, sizeof(CPyTagged), NULL)
368+
size = _READ(data, Py_ssize_t)
369+
}
370+
// Read byes content.
371+
char *buf = ((BufferObject *)data)->buf;
372+
_CHECK_READ(data, size, NULL)
373+
PyObject *res = PyBytes_FromStringAndSize(
374+
buf + ((BufferObject *)data)->pos, (Py_ssize_t)size
375+
);
376+
if (unlikely(res == NULL))
377+
return NULL;
378+
((BufferObject *)data)->pos += size;
379+
return res;
380+
}
381+
382+
static PyObject*
383+
read_bytes(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
384+
static const char * const kwlist[] = {"data", 0};
385+
static CPyArg_Parser parser = {"O:read_bytes", kwlist, 0};
386+
PyObject *data;
387+
if (unlikely(!CPyArg_ParseStackAndKeywordsOneArg(args, nargs, kwnames, &parser, &data))) {
388+
return NULL;
389+
}
390+
return read_bytes_internal(data);
391+
}
392+
393+
static char
394+
write_bytes_internal(PyObject *data, PyObject *value) {
395+
_CHECK_BUFFER(data, CPY_NONE_ERROR)
396+
397+
Py_ssize_t size = PyBytes_GET_SIZE(value);
398+
const char *chunk = PyBytes_AsString(value);
399+
if (unlikely(chunk == NULL))
400+
return CPY_NONE_ERROR;
401+
402+
Py_ssize_t need;
403+
// Write length.
404+
if (likely(size <= MAX_SHORT_LEN)) {
405+
// Common case: short bytes (len <= 127) store as single byte.
406+
need = size + 1;
407+
_CHECK_SIZE(data, need)
408+
_WRITE(data, uint8_t, (uint8_t)size << 1)
409+
} else {
410+
need = size + sizeof(Py_ssize_t) + 1;
411+
_CHECK_SIZE(data, need)
412+
_WRITE(data, uint8_t, LONG_STR_TAG)
413+
_WRITE(data, Py_ssize_t, size)
414+
}
415+
// Write bytes content.
416+
char *buf = ((BufferObject *)data)->buf;
417+
memcpy(buf + ((BufferObject *)data)->pos, chunk, size);
418+
((BufferObject *)data)->pos += size;
419+
((BufferObject *)data)->end += need;
420+
return CPY_NONE;
421+
}
422+
423+
static PyObject*
424+
write_bytes(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
425+
static const char * const kwlist[] = {"data", "value", 0};
426+
static CPyArg_Parser parser = {"OO:write_bytes", kwlist, 0};
427+
PyObject *data;
428+
PyObject *value;
429+
if (unlikely(!CPyArg_ParseStackAndKeywordsSimple(args, nargs, kwnames, &parser, &data, &value))) {
430+
return NULL;
431+
}
432+
if (unlikely(!PyBytes_Check(value))) {
433+
PyErr_SetString(PyExc_TypeError, "value must be a bytes object");
434+
return NULL;
435+
}
436+
if (unlikely(write_bytes_internal(data, value) == CPY_NONE_ERROR)) {
437+
return NULL;
438+
}
439+
Py_INCREF(Py_None);
440+
return Py_None;
441+
}
442+
349443
/*
350444
float format:
351445
stored as a C double
@@ -565,6 +659,8 @@ static PyMethodDef librt_internal_module_methods[] = {
565659
{"read_bool", (PyCFunction)read_bool, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read a bool")},
566660
{"write_str", (PyCFunction)write_str, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write a string")},
567661
{"read_str", (PyCFunction)read_str, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read a string")},
662+
{"write_bytes", (PyCFunction)write_bytes, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write bytes")},
663+
{"read_bytes", (PyCFunction)read_bytes, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read bytes")},
568664
{"write_float", (PyCFunction)write_float, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write a float")},
569665
{"read_float", (PyCFunction)read_float, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read a float")},
570666
{"write_int", (PyCFunction)write_int, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write an int")},
@@ -590,7 +686,7 @@ librt_internal_module_exec(PyObject *m)
590686
}
591687

592688
// Export mypy internal C API, be careful with the order!
593-
static void *NativeInternal_API[14] = {
689+
static void *NativeInternal_API[16] = {
594690
(void *)Buffer_internal,
595691
(void *)Buffer_internal_empty,
596692
(void *)Buffer_getvalue_internal,
@@ -605,6 +701,8 @@ librt_internal_module_exec(PyObject *m)
605701
(void *)write_tag_internal,
606702
(void *)read_tag_internal,
607703
(void *)NativeInternal_ABI_Version,
704+
(void *)write_bytes_internal,
705+
(void *)read_bytes_internal,
608706
};
609707
PyObject *c_api_object = PyCapsule_New((void *)NativeInternal_API, "librt.internal._C_API", NULL);
610708
if (PyModule_Add(m, "_C_API", c_api_object) < 0) {

mypyc/lib-rt/librt_internal.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ static CPyTagged read_int_internal(PyObject *data);
1919
static char write_tag_internal(PyObject *data, uint8_t value);
2020
static uint8_t read_tag_internal(PyObject *data);
2121
static int NativeInternal_ABI_Version(void);
22+
static char write_bytes_internal(PyObject *data, PyObject *value);
23+
static PyObject *read_bytes_internal(PyObject *data);
2224

2325
#else
2426

@@ -38,6 +40,8 @@ static void **NativeInternal_API;
3840
#define write_tag_internal (*(char (*)(PyObject *source, uint8_t value)) NativeInternal_API[11])
3941
#define read_tag_internal (*(uint8_t (*)(PyObject *source)) NativeInternal_API[12])
4042
#define NativeInternal_ABI_Version (*(int (*)(void)) NativeInternal_API[13])
43+
#define write_bytes_internal (*(char (*)(PyObject *source, PyObject *value)) NativeInternal_API[14])
44+
#define read_bytes_internal (*(PyObject* (*)(PyObject *source)) NativeInternal_API[15])
4145

4246
static int
4347
import_librt_internal(void)

mypyc/primitives/misc_ops.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,22 @@
393393
error_kind=ERR_MAGIC,
394394
)
395395

396+
function_op(
397+
name="librt.internal.write_bytes",
398+
arg_types=[object_rprimitive, bytes_rprimitive],
399+
return_type=none_rprimitive,
400+
c_function_name="write_bytes_internal",
401+
error_kind=ERR_MAGIC,
402+
)
403+
404+
function_op(
405+
name="librt.internal.read_bytes",
406+
arg_types=[object_rprimitive],
407+
return_type=bytes_rprimitive,
408+
c_function_name="read_bytes_internal",
409+
error_kind=ERR_MAGIC,
410+
)
411+
396412
function_op(
397413
name="librt.internal.write_float",
398414
arg_types=[object_rprimitive, float_rprimitive],

mypyc/test-data/irbuild-classes.test

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,7 +1454,7 @@ from typing import Final
14541454
from mypy_extensions import u8
14551455
from librt.internal import (
14561456
Buffer, write_bool, read_bool, write_str, read_str, write_float, read_float,
1457-
write_int, read_int, write_tag, read_tag
1457+
write_int, read_int, write_tag, read_tag, write_bytes, read_bytes,
14581458
)
14591459

14601460
Tag = u8
@@ -1463,13 +1463,15 @@ TAG: Final[Tag] = 1
14631463
def foo() -> None:
14641464
b = Buffer()
14651465
write_str(b, "foo")
1466+
write_bytes(b, b"bar")
14661467
write_bool(b, True)
14671468
write_float(b, 0.1)
14681469
write_int(b, 1)
14691470
write_tag(b, TAG)
14701471

14711472
b = Buffer(b.getvalue())
14721473
x = read_str(b)
1474+
xb = read_bytes(b)
14731475
y = read_bool(b)
14741476
z = read_float(b)
14751477
t = read_int(b)
@@ -1478,36 +1480,43 @@ def foo() -> None:
14781480
def foo():
14791481
r0, b :: librt.internal.Buffer
14801482
r1 :: str
1481-
r2, r3, r4, r5, r6 :: None
1482-
r7 :: bytes
1483-
r8 :: librt.internal.Buffer
1484-
r9, x :: str
1485-
r10, y :: bool
1486-
r11, z :: float
1487-
r12, t :: int
1488-
r13, u :: u8
1483+
r2 :: None
1484+
r3 :: bytes
1485+
r4, r5, r6, r7, r8 :: None
1486+
r9 :: bytes
1487+
r10 :: librt.internal.Buffer
1488+
r11, x :: str
1489+
r12, xb :: bytes
1490+
r13, y :: bool
1491+
r14, z :: float
1492+
r15, t :: int
1493+
r16, u :: u8
14891494
L0:
14901495
r0 = Buffer_internal_empty()
14911496
b = r0
14921497
r1 = 'foo'
14931498
r2 = write_str_internal(b, r1)
1494-
r3 = write_bool_internal(b, 1)
1495-
r4 = write_float_internal(b, 0.1)
1496-
r5 = write_int_internal(b, 2)
1497-
r6 = write_tag_internal(b, 1)
1498-
r7 = Buffer_getvalue_internal(b)
1499-
r8 = Buffer_internal(r7)
1500-
b = r8
1501-
r9 = read_str_internal(b)
1502-
x = r9
1503-
r10 = read_bool_internal(b)
1504-
y = r10
1505-
r11 = read_float_internal(b)
1506-
z = r11
1507-
r12 = read_int_internal(b)
1508-
t = r12
1509-
r13 = read_tag_internal(b)
1510-
u = r13
1499+
r3 = b'bar'
1500+
r4 = write_bytes_internal(b, r3)
1501+
r5 = write_bool_internal(b, 1)
1502+
r6 = write_float_internal(b, 0.1)
1503+
r7 = write_int_internal(b, 2)
1504+
r8 = write_tag_internal(b, 1)
1505+
r9 = Buffer_getvalue_internal(b)
1506+
r10 = Buffer_internal(r9)
1507+
b = r10
1508+
r11 = read_str_internal(b)
1509+
x = r11
1510+
r12 = read_bytes_internal(b)
1511+
xb = r12
1512+
r13 = read_bool_internal(b)
1513+
y = r13
1514+
r14 = read_float_internal(b)
1515+
z = r14
1516+
r15 = read_int_internal(b)
1517+
t = r15
1518+
r16 = read_tag_internal(b)
1519+
u = r16
15111520
return 1
15121521

15131522
[case testEnumFastPath]

mypyc/test-data/run-classes.test

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2715,7 +2715,7 @@ from typing import Final
27152715
from mypy_extensions import u8
27162716
from librt.internal import (
27172717
Buffer, write_bool, read_bool, write_str, read_str, write_float, read_float,
2718-
write_int, read_int, write_tag, read_tag
2718+
write_int, read_int, write_tag, read_tag, write_bytes, read_bytes
27192719
)
27202720

27212721
Tag = u8
@@ -2733,6 +2733,8 @@ def test_buffer_roundtrip() -> None:
27332733
write_bool(b, True)
27342734
write_str(b, "bar" * 1000)
27352735
write_bool(b, False)
2736+
write_bytes(b, b"bar")
2737+
write_bytes(b, b"bar" * 100)
27362738
write_float(b, 0.1)
27372739
write_int(b, 0)
27382740
write_int(b, 1)
@@ -2752,6 +2754,8 @@ def test_buffer_roundtrip() -> None:
27522754
assert read_bool(b) is True
27532755
assert read_str(b) == "bar" * 1000
27542756
assert read_bool(b) is False
2757+
assert read_bytes(b) == b"bar"
2758+
assert read_bytes(b) == b"bar" * 100
27552759
assert read_float(b) == 0.1
27562760
assert read_int(b) == 0
27572761
assert read_int(b) == 1
@@ -2806,6 +2810,8 @@ def test_buffer_roundtrip_interpreted() -> None:
28062810
write_bool(b, True)
28072811
write_str(b, "bar" * 1000)
28082812
write_bool(b, False)
2813+
write_bytes(b, b"bar")
2814+
write_bytes(b, b"bar" * 100)
28092815
write_float(b, 0.1)
28102816
write_int(b, 0)
28112817
write_int(b, 1)
@@ -2825,6 +2831,8 @@ def test_buffer_roundtrip_interpreted() -> None:
28252831
assert read_bool(b) is True
28262832
assert read_str(b) == "bar" * 1000
28272833
assert read_bool(b) is False
2834+
assert read_bytes(b) == b"bar"
2835+
assert read_bytes(b) == b"bar" * 100
28282836
assert read_float(b) == 0.1
28292837
assert read_int(b) == 0
28302838
assert read_int(b) == 1

0 commit comments

Comments
 (0)