Skip to content

Commit 49e9a9b

Browse files
authored
Expose buffer cache layout version (#20145)
This idea is quite simple: each time we change how an object is cached in the buffer we bump this version (even if we don't change the ABI version). Notes: * This is *not* the same as ABI version, if the ABI version is different, the capsule import will fail and you can't even use this version of `librt`. We can however use buffer cache layout version to abandon cache files that would result in reading garbage data. * I can't actually start using this in cache metas in the same PR, to avoid breaking self-check in CI. If there are no comments objections, I will merge this soon. Then release a new `librt` version, and make second PR that actually uses this feature. While I am at it I fix a bug in the float deserialization (magic value was not handled).
1 parent 1698432 commit 49e9a9b

File tree

6 files changed

+35
-4
lines changed

6 files changed

+35
-4
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ def write_int(data: Buffer, value: int) -> None: ...
1616
def read_int(data: Buffer) -> int: ...
1717
def write_tag(data: Buffer, value: u8) -> None: ...
1818
def read_tag(data: Buffer) -> u8: ...
19+
def cache_version() -> u8: ...

mypyc/lib-rt/librt_internal.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,16 @@ write_tag(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames
656656
return Py_None;
657657
}
658658

659+
static uint8_t
660+
cache_version_internal(void) {
661+
return 0;
662+
}
663+
664+
static PyObject*
665+
cache_version(PyObject *self, PyObject *Py_UNUSED(ignored)) {
666+
return PyLong_FromLong(cache_version_internal());
667+
}
668+
659669
static PyMethodDef librt_internal_module_methods[] = {
660670
{"write_bool", (PyCFunction)write_bool, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write a bool")},
661671
{"read_bool", (PyCFunction)read_bool, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read a bool")},
@@ -669,6 +679,7 @@ static PyMethodDef librt_internal_module_methods[] = {
669679
{"read_int", (PyCFunction)read_int, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read an int")},
670680
{"write_tag", (PyCFunction)write_tag, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write a short int")},
671681
{"read_tag", (PyCFunction)read_tag, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read a short int")},
682+
{"cache_version", (PyCFunction)cache_version, METH_NOARGS, PyDoc_STR("cache format version")},
672683
{NULL, NULL, 0, NULL}
673684
};
674685

@@ -688,7 +699,7 @@ librt_internal_module_exec(PyObject *m)
688699
}
689700

690701
// Export mypy internal C API, be careful with the order!
691-
static void *NativeInternal_API[16] = {
702+
static void *NativeInternal_API[17] = {
692703
(void *)Buffer_internal,
693704
(void *)Buffer_internal_empty,
694705
(void *)Buffer_getvalue_internal,
@@ -705,6 +716,7 @@ librt_internal_module_exec(PyObject *m)
705716
(void *)NativeInternal_ABI_Version,
706717
(void *)write_bytes_internal,
707718
(void *)read_bytes_internal,
719+
(void *)cache_version_internal,
708720
};
709721
PyObject *c_api_object = PyCapsule_New((void *)NativeInternal_API, "librt.internal._C_API", NULL);
710722
if (PyModule_Add(m, "_C_API", c_api_object) < 0) {

mypyc/lib-rt/librt_internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ static uint8_t read_tag_internal(PyObject *data);
2121
static int NativeInternal_ABI_Version(void);
2222
static char write_bytes_internal(PyObject *data, PyObject *value);
2323
static PyObject *read_bytes_internal(PyObject *data);
24+
static uint8_t cache_version_internal(void);
2425

2526
#else
2627

@@ -42,6 +43,7 @@ static void **NativeInternal_API;
4243
#define NativeInternal_ABI_Version (*(int (*)(void)) NativeInternal_API[13])
4344
#define write_bytes_internal (*(char (*)(PyObject *source, PyObject *value)) NativeInternal_API[14])
4445
#define read_bytes_internal (*(PyObject* (*)(PyObject *source)) NativeInternal_API[15])
46+
#define cache_version_internal (*(uint8_t (*)(void)) NativeInternal_API[16])
4547

4648
static int
4749
import_librt_internal(void)

mypyc/primitives/misc_ops.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@
422422
arg_types=[object_rprimitive],
423423
return_type=float_rprimitive,
424424
c_function_name="read_float_internal",
425-
error_kind=ERR_MAGIC,
425+
error_kind=ERR_MAGIC_OVERLAPPING,
426426
)
427427

428428
function_op(
@@ -456,3 +456,11 @@
456456
c_function_name="read_tag_internal",
457457
error_kind=ERR_MAGIC_OVERLAPPING,
458458
)
459+
460+
function_op(
461+
name="librt.internal.cache_version",
462+
arg_types=[],
463+
return_type=uint8_rprimitive,
464+
c_function_name="cache_version_internal",
465+
error_kind=ERR_NEVER,
466+
)

mypyc/test-data/irbuild-classes.test

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1455,6 +1455,7 @@ from mypy_extensions import u8
14551455
from librt.internal import (
14561456
Buffer, write_bool, read_bool, write_str, read_str, write_float, read_float,
14571457
write_int, read_int, write_tag, read_tag, write_bytes, read_bytes,
1458+
cache_version,
14581459
)
14591460

14601461
Tag = u8
@@ -1476,6 +1477,7 @@ def foo() -> None:
14761477
z = read_float(b)
14771478
t = read_int(b)
14781479
u = read_tag(b)
1480+
v = cache_version()
14791481
[out]
14801482
def foo():
14811483
r0, b :: librt.internal.Buffer
@@ -1490,7 +1492,7 @@ def foo():
14901492
r13, y :: bool
14911493
r14, z :: float
14921494
r15, t :: int
1493-
r16, u :: u8
1495+
r16, u, r17, v :: u8
14941496
L0:
14951497
r0 = Buffer_internal_empty()
14961498
b = r0
@@ -1517,6 +1519,8 @@ L0:
15171519
t = r15
15181520
r16 = read_tag_internal(b)
15191521
u = r16
1522+
r17 = cache_version_internal()
1523+
v = r17
15201524
return 1
15211525

15221526
[case testEnumFastPath]

mypyc/test-data/run-classes.test

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2715,7 +2715,8 @@ 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, write_bytes, read_bytes
2718+
write_int, read_int, write_tag, read_tag, write_bytes, read_bytes,
2719+
cache_version,
27192720
)
27202721

27212722
Tag = u8
@@ -2724,6 +2725,7 @@ TAG_B: Final[Tag] = 255
27242725
TAG_SPECIAL: Final[Tag] = 239
27252726

27262727
def test_buffer_basic() -> None:
2728+
assert cache_version() == 0
27272729
b = Buffer(b"foo")
27282730
assert b.getvalue() == b"foo"
27292731

@@ -2739,6 +2741,7 @@ def test_buffer_roundtrip() -> None:
27392741
write_bytes(b, b"a" * 127)
27402742
write_bytes(b, b"a" * 128)
27412743
write_float(b, 0.1)
2744+
write_float(b, -113.0)
27422745
write_int(b, 0)
27432746
write_int(b, 1)
27442747
write_tag(b, TAG_A)
@@ -2763,6 +2766,7 @@ def test_buffer_roundtrip() -> None:
27632766
assert read_bytes(b) == b"a" * 127
27642767
assert read_bytes(b) == b"a" * 128
27652768
assert read_float(b) == 0.1
2769+
assert read_float(b) == -113.0
27662770
assert read_int(b) == 0
27672771
assert read_int(b) == 1
27682772
assert read_tag(b) == TAG_A

0 commit comments

Comments
 (0)