diff --git a/quest/m3/ts-unsafe.md b/quest/m3/ts-unsafe.md index 97782afdb..7ae80681e 100644 --- a/quest/m3/ts-unsafe.md +++ b/quest/m3/ts-unsafe.md @@ -50,10 +50,6 @@ none of its own `unsafe`. `set_len` is refused, and a write past the length fails loudly at run time, since the JS array would grow by it. The spare slots need to live outside the array's length until `set_len` takes them. -- `offset_from` between byte pointers into two views of one buffer, such as a slice's - `as_mut_ptr` and its `iter_mut`'s first element: each is an `rt.AtIndex` into its own - `Uint8Array`, so it fails loudly at run time where Rust measures the distance. Comparing - by buffer and byte offset closes it. - `UnsafeCell::raw_get`, `from_mut`, and `replace`, refused with their span: none is in moq's census at the pin. - A byte pointer moved past its own `Uint8Array` view into the rest of the buffer, such as diff --git a/ts/fixtures/lang/expected/elements.ts b/ts/fixtures/lang/expected/elements.ts index 9e79c751b..096e285b1 100644 --- a/ts/fixtures/lang/expected/elements.ts +++ b/ts/fixtures/lang/expected/elements.ts @@ -205,8 +205,8 @@ export function inline(data: rt.ByteSlice, out: rt.ByteVec): void { * the array of one Rust treats it as, and into a mutable part of an array, measured from * the array, the pointer an empty slice keeps, within a shared part, which JS copies, * a `&mut` into that part, memory nothing wrote copied, null and dangling pointers moved by - * address, kept by empty raw slices, and cast from pointers to arrays, and the crate's - * functions named as std's. + * address, kept by empty raw slices, and cast from pointers to arrays, the crate's + * functions named as std's, and byte pointers into one buffer through its views. */ export function edges(data: rt.ByteSlice, out: rt.ByteVec): void { let _200!: rt.Result<[rt.u32, rt.u32], rt.u8>; @@ -318,6 +318,19 @@ export function edges(data: rt.ByteSlice, out: rt.ByteVec): void { rt.byteVecPush(out, rt.ptrWrappingAdd(rt.byteSliceAsPtr(rt.NULL), n as rt.usize, 1, 1) === rt.NULL ? 1 : 0); rt.byteVecPush(out, rt.ptrWrappingAdd(rt.sliceAsPtr(rt.DANGLING), -1, 2, 2) === rt.NULL ? 1 : 0); } + const bytes2: Uint8Array = Uint8Array.of(option_unwrapOr$0r2bmpr(rt.byteSliceFirst(data), 0), 1, 2, 3, 4, 5); + const s: rt.ByteSlice = rt.byteSliceRange(rt.byteSliceFrom(bytes2), 1); + const base2: rt.Place = rt.byteSliceAsPtr(s); + const first2: rt.Place = rt.optionUnwrap(rt.iterToOption(rt.iterSliceMut(rt.byteSliceView(s)).next())); + const last: rt.Place = rt.optionUnwrap(rt.iterLast(rt.iterSliceMut(rt.byteSliceView(s)))); + const part2: rt.Place = rt.byteSliceAsPtr(rt.byteSliceSplitAt(s, 2)[1]); + rt.byteVecPush(out, rt.ptrOffsetFrom(first2, base2) & 0xff); + rt.byteVecPush(out, rt.ptrOffsetFrom(last, base2) & 0xff); + rt.byteVecPush(out, rt.ptrOffsetFrom(base2, last) & 0xff); + rt.byteVecPush(out, rt.ptrOffsetFrom(part2, first2) & 0xff); + const _433: rt.Place = rt.ptrAdd(last, rt.ptrOffsetFrom(part2, last)); + _433.v = _433.v ^ 255; + rt.iterExtendBytes(out, rt.iterSlice(bytes2), undefined, true); } /** diff --git a/ts/fixtures/lang/src/elements.rs b/ts/fixtures/lang/src/elements.rs index 67e9d7f27..a6b928e13 100644 --- a/ts/fixtures/lang/src/elements.rs +++ b/ts/fixtures/lang/src/elements.rs @@ -256,8 +256,8 @@ pub fn inline(data: &[u8], out: &mut Vec) { /// the array of one Rust treats it as, and into a mutable part of an array, measured from /// the array, the pointer an empty slice keeps, within a shared part, which JS copies, /// a `&mut` into that part, memory nothing wrote copied, null and dangling pointers moved by -/// address, kept by empty raw slices, and cast from pointers to arrays, and the crate's -/// functions named as std's. +/// address, kept by empty raw slices, and cast from pointers to arrays, the crate's +/// functions named as std's, and byte pointers into one buffer through its views. pub fn edges(data: &[u8], out: &mut Vec) { for &n in data { let bytes = [n, 1, 2, 3]; @@ -400,6 +400,23 @@ pub fn edges(data: &[u8], out: &mut Vec) { .is_null() as u8, ); } + // Byte pointers into one buffer through different views of it: a subslice's own, its + // iterator's elements', and its parts'. + let mut bytes = [data.first().copied().unwrap_or(0), 1, 2, 3, 4, 5]; + let s = &mut bytes[1..]; + let base = s.as_mut_ptr(); + let first: *mut u8 = s.iter_mut().next().unwrap(); + let last: *mut u8 = s.iter_mut().last().unwrap(); + let (_, tail) = s.split_at_mut(2); + let part = tail.as_mut_ptr(); + unsafe { + out.push(first.offset_from(base) as u8); + out.push(last.offset_from(base) as u8); + out.push(base.offset_from(last) as u8); + out.push(part.offset_from(first) as u8); + *last.offset(part.offset_from(last)) ^= 0xff; + } + out.extend(bytes); } /// Raw pointers to slices, each where it starts and how long it is: read and written diff --git a/ts/runtime/rt.ts b/ts/runtime/rt.ts index f3f02c6a2..d416cb6e7 100644 --- a/ts/runtime/rt.ts +++ b/ts/runtime/rt.ts @@ -293,13 +293,17 @@ export function ptrWrappingAdd(p: Place, n: number, size: number, align: n return new AtIndex(e.array, wrapIndex(e.index, n | 0, size)); } -/** `p.offset_from(origin)`: how many elements `p` is past `origin`, in the same array. */ +/** `p.offset_from(origin)`: how many elements `p` is past `origin`, in the same array or byte buffer. */ export function ptrOffsetFrom(p: Place, origin: Place): number { // Equal addresses are none apart, in no allocation as in one. if (p === origin || (p instanceof Address && origin instanceof Address && p.addr === origin.addr)) return 0; const [a, b] = [elemOf(p), elemOf(origin)]; - if (a.array !== b.array) throw new Error("rust2-ts: `offset_from` of pointers JS keeps in different arrays"); - return a.index - b.index; + if (a.array === b.array) return a.index - b.index; + // Views of one buffer's bytes, such as a slice's and its iterator's, are one array. + if (a.array instanceof Uint8Array && b.array instanceof Uint8Array && a.array.buffer === b.array.buffer) { + return a.array.byteOffset + a.index - (b.array.byteOffset + b.index); + } + throw new Error("rust2-ts: `offset_from` of pointers JS keeps in different arrays"); } /**