Skip to content

Commit aa478ad

Browse files
committed
Accept directly readable ALTREP data in the layout oracle
1 parent be26632 commit aa478ad

3 files changed

Lines changed: 31 additions & 8 deletions

File tree

‎AGENTS.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ All writers are count-then-write pairs: `mori_serialize_count`/`mori_serialize_i
5252

5353
Declared in `mori.h`, C-level only (not `.Call`) — for packages embedding mori layouts under their own SHM management:
5454

55-
- **Layout oracle + writer**: `mori_layout_size(x)` → region size, or 0 for what the writer must not take (non-mori ALTREP nodes — would materialize via `DATAPTR_RO`). The S4 object bit rides the layouts: header flags word at offset 32 (`MORI_FLAG_S4`) for MORH/MORS/MORL roots and nested lists, bit 30 of the directory entry's `sexptype` (`MORI_ELEM_S4`) for vector/string leaves; applied with `Rf_asS4` after attributes land. Vetoes ride the size recursion (`mori_layout_size_impl` with `ok != NULL`); the host path passes `NULL` and vetoes nothing. `mori_layout_write(base, x)` emits exactly `mori_layout_size` bytes and zeroes reserved header bytes [24-63] on every write (embedders may recycle regions).
55+
- **Layout oracle + writer**: `mori_layout_size(x)` → region size, or 0 for what the writer must not take (ALTREP nodes that are neither mori views nor directly readable with no keeper chain — `mori_altrep_readable`; R's S4 data-part wrappers qualify). The S4 object bit rides the layouts: header flags word at offset 32 (`MORI_FLAG_S4`) for MORH/MORS/MORL roots and nested lists, bit 30 of the directory entry's `sexptype` (`MORI_ELEM_S4`) for vector/string leaves; applied with `Rf_asS4` after attributes land. Vetoes ride the size recursion (`mori_layout_size_impl` with `ok != NULL`); the host path passes `NULL` and vetoes nothing. `mori_layout_write(base, x)` emits exactly `mori_layout_size` bytes and zeroes reserved header bytes [24-63] on every write (embedders may recycle regions).
5656
- **Wrap constructors**: `mori_vec_wrap` / `mori_str_wrap` / `mori_list_wrap` build views over embedder memory, pinning `keeper` via the data1 extptr's protected slot; each takes a `release` once-hook (see Internal State). `mori_restore_attrs` reapplies trailing serialized attributes.
5757
- **Introspection + path walk**: `mori_view_check`, `mori_shm_name`, `mori_parse_id`, `mori_walk_path` (walks an index path over an open region; the caller's keeper flows into the result's chain).
5858
- **Wire hooks**: `mori_set_wire_hooks(emit, resolve)` — see Serialization Hooks.

‎src/altrep.c‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -850,8 +850,11 @@ static size_t mori_nested_write(unsigned char *base, SEXP x);
850850
LISTSXP children are coerced locally during recursion.
851851
ok is NULL on the host path. When non-NULL (the embedder layout oracle),
852852
each node is vetted before sizing and the first rejection sets *ok = 0 and
853-
bails out with return 0: a non-mori ALTREP node would materialize through
854-
DATAPTR_RO at write (a compact 1:1e8 becomes an 800 MB memcpy). */
853+
bails out with return 0: an ALTREP node that is neither a mori view
854+
nor directly readable with no keeper chain (mori_altrep_readable)
855+
would materialize through DATAPTR_RO at write or duplicate a wire
856+
identity (a compact 1:1e8 becomes an 800 MB memcpy). R's S4 data-part
857+
wrappers forward to their materialized data part and are accepted. */
855858
static size_t mori_nested_size(SEXP x, int *ok) {
856859

857860
R_xlen_t n = XLENGTH(x);
@@ -861,7 +864,9 @@ static size_t mori_nested_size(SEXP x, int *ok) {
861864
SEXP elt = VECTOR_ELT(x, i);
862865

863866
if (ok != NULL) {
864-
if (ALTREP(elt) && !mori_view_check(elt)) { *ok = 0; return 0; }
867+
if (ALTREP(elt) && !mori_view_check(elt) && !mori_altrep_readable(elt)) {
868+
*ok = 0; return 0;
869+
}
865870
}
866871

867872
int type = TYPEOF(elt);
@@ -1098,7 +1103,9 @@ static void mors_write(unsigned char *base, SEXP x) {
10981103
header. */
10991104
static size_t mori_layout_size_impl(SEXP x, int *ok) {
11001105
if (ok != NULL) {
1101-
if (ALTREP(x) && !mori_view_check(x)) { *ok = 0; return 0; }
1106+
if (ALTREP(x) && !mori_view_check(x) && !mori_altrep_readable(x)) {
1107+
*ok = 0; return 0;
1108+
}
11021109
}
11031110
int type = TYPEOF(x);
11041111
/* An S4 pairlist root passes through: VECSXP coercion drops the bit. */

‎src/mori.h‎

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,20 @@ static inline size_t mori_sizeof_elt(int type) {
8080
}
8181
}
8282

83+
/* R's S4 data-part wrapper is an ALTREP that forwards every data method
84+
to the wrapped vector (data1): directly readable, no compression, no
85+
keeper chain. Recognize it by that shape — a non-ALTREP data1 of the
86+
same type whose data pointer the wrapper shares. Anything else foreign
87+
(a lazy ALTREP, a materialized compact sequence, an extptr-data1 view)
88+
is rejected: the layout write would materialize it or forfeit its
89+
compact wire form. */
90+
static inline int mori_altrep_readable(SEXP x) {
91+
SEXP inner = R_altrep_data1(x);
92+
if (ALTREP(inner) || TYPEOF(inner) != TYPEOF(x)) return 0;
93+
const void *pi = DATAPTR_OR_NULL(inner);
94+
return pi != NULL && DATAPTR_OR_NULL(x) == pi;
95+
}
96+
8397
/* Apply a region header's S4 flag to a freshly wrapped view — after
8498
attributes land, so a read never consults a class definition
8599
(Rf_asS4 with complete = 0 sets the bit in place on a fresh object).
@@ -117,9 +131,11 @@ void mori_restore_attrs(SEXP result, unsigned char *buf, size_t size);
117131

118132
/* Layout oracle and writer for embedder-managed regions: the size pass
119133
walks the tree and returns 0 for anything the layout writer must not
120-
take (a non-mori ALTREP node would materialize through DATAPTR_RO).
121-
The write emits exactly mori_layout_size bytes and zeroes header
122-
reserved bytes. */
134+
take: an ALTREP node is rejected unless it is a mori view (rides the
135+
wire hooks) or mori_altrep_readable (R's S4 data-part wrappers
136+
qualify; a compact 1:1e8 would materialize through DATAPTR_RO at
137+
write). The write emits exactly mori_layout_size bytes and zeroes
138+
header reserved bytes. */
123139
size_t mori_layout_size(SEXP x);
124140
void mori_layout_write(unsigned char *base, SEXP x);
125141

0 commit comments

Comments
 (0)