Skip to content

Commit abc1010

Browse files
committed
fixup! feat(marshal,pass-style): admit immutable ArrayBuffer through codecs
Apply the relevant feedback from #2871 to subsume that PR: - @gibson042 #2871:byteArray.js:127 — `uint8ArrayToByteArray` now honors the source `Uint8Array`'s `byteOffset` and `length`, so a view over a slice of a larger buffer round-trips correctly: `uint8ArrayToByteArray(new Uint8Array(buf, byteOffset, length))` represents exactly the bytes in `[byteOffset, byteOffset + length)`. Documented the zero-copy / copy tradeoff with `sliceToImmutable`. - @gibson042 #2871:byteArray.js:130 — comment on `byteArrayToHex` / `hexToByteArray` is now phrased as "Like `encodeHex` but…" / "Like `decodeHex` but…", matching gibson's suggested wording for the byteArray-flavored wrappers. - @erights #2871:encodeToCapData.js:453 / encodeToSmallcaps.js — the factory functions `makeDecodeFromCapData` and `makeDecodeFromSmallcaps` are now `harden`-ed at module scope, matching the existing harden of their encoder counterparts (`makeEncodeToCapData`, `makeEncodeToSmallcaps`). - @erights #2871:marshal-test-data.js — propagate Mark's full byteArray fixture set: `roundTripPairs`, `jsonJustinPairs`, `unsortedSample`, and `sortedSample`. The Justin test compartment exposes `hexToByteArray` so the rendered Justin expression evaluates. Not propagated, deliberately: - `packages/marshal/src/rankOrder.js` — Mark's `compareRankRemotablesTied` refactor is orthogonal to codec admission and lands as its own PR (bots #73 today, awaiting an upstream PR). - `packages/pass-style/src/hex.js` — Mark vendored a private hex implementation in pass-style; we route through the merged `@endo/hex` package (#3208) instead. As a result, `packages/pass-style/index.js` exports `byteArrayToHex` / `hexToByteArray` but not `uint8ArrayToHex` / `hexToUint8Array`; consumers needing raw hex helpers should `import { encodeHex, decodeHex } from '@endo/hex'`. Tests: `@endo/marshal` 79 + 1 skip, `@endo/pass-style` 29. Lint clean.
1 parent 0b55322 commit abc1010

5 files changed

Lines changed: 52 additions & 5 deletions

File tree

packages/marshal/src/encodeToCapData.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,3 +451,4 @@ export const makeDecodeFromCapData = (decodeOptions = {}) => {
451451
};
452452
return harden(decodeFromCapData);
453453
};
454+
harden(makeDecodeFromCapData);

packages/marshal/src/encodeToSmallcaps.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,3 +477,4 @@ export const makeDecodeFromSmallcaps = (decodeOptions = {}) => {
477477
};
478478
return harden(decodeFromSmallcaps);
479479
};
480+
harden(makeDecodeFromSmallcaps);

packages/marshal/test/marshal-justin.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { makeError, X } from '@endo/errors';
55
import {
66
Far,
77
Remotable,
8+
hexToByteArray,
89
makeTagged,
910
passableSymbolForName,
1011
} from '@endo/pass-style';
@@ -42,6 +43,7 @@ const fakeJustinCompartment = () => {
4243
return new Compartment({
4344
slot,
4445
slotToVal,
46+
hexToByteArray,
4547
makeTagged,
4648
passableSymbolForName,
4749
});

packages/marshal/tools/marshal-test-data.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import harden from '@endo/harden';
2-
import { makeTagged, passableSymbolForName } from '@endo/pass-style';
2+
import {
3+
hexToByteArray,
4+
makeTagged,
5+
passableSymbolForName,
6+
} from '@endo/pass-style';
37
import {
48
exampleAlice,
59
exampleBob,
@@ -129,6 +133,15 @@ export const roundTripPairs = harden([
129133
[[undefined], [{ '@qclass': 'undefined' }]],
130134
[{ foo: undefined }, { foo: { '@qclass': 'undefined' } }],
131135

136+
// byteArray
137+
[
138+
hexToByteArray('0f'),
139+
{
140+
'@qclass': 'byteArray',
141+
data: '0f',
142+
},
143+
],
144+
132145
// tagged
133146
[
134147
makeTagged('x', 8),
@@ -258,6 +271,9 @@ export const jsonJustinPairs = harden([
258271
['{"@qclass":"symbol","name":"foo"}', 'passableSymbolForName("foo")'],
259272
['{"@qclass":"symbol","name":"@@@@foo"}', 'passableSymbolForName("@@@@foo")'],
260273

274+
// byteArray
275+
['{"@qclass":"byteArray","data":"0aff"}', 'hexToByteArray("0aff")'],
276+
261277
// Arrays and objects
262278
['[{"@qclass":"undefined"}]', '[undefined]'],
263279
['{"foo":{"@qclass":"undefined"}}', '{foo:undefined}'],
@@ -334,6 +350,7 @@ export const unsortedSample = harden([
334350
undefined,
335351
-Infinity,
336352
[5],
353+
hexToByteArray('0f'),
337354
exampleAlice,
338355
[],
339356
passableSymbolForName('foo'),
@@ -347,6 +364,7 @@ export const unsortedSample = harden([
347364
[exampleAlice, 'a'],
348365
[exampleBob, 'z'],
349366
-0,
367+
hexToByteArray('aa'),
350368
{},
351369
[5, undefined],
352370
-3,
@@ -356,6 +374,7 @@ export const unsortedSample = harden([
356374
]),
357375
true,
358376
'bar',
377+
hexToByteArray('0a'),
359378
[5, null],
360379
new Promise(() => {}), // forever unresolved
361380
makeTagged('nonsense', [
@@ -429,6 +448,10 @@ export const sortedSample = harden([
429448
[exampleCarol, 'm'],
430449
[exampleBob, 'z'],
431450

451+
hexToByteArray('0a'),
452+
hexToByteArray('0f'),
453+
hexToByteArray('aa'),
454+
432455
false,
433456
true,
434457
true,

packages/pass-style/src/byteArray.js

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,16 +97,34 @@ harden(byteArrayToUint8Array);
9797
* Converts a `Uint8Array` to a `ByteArray`, i.e., a hardened Immutable
9898
* ArrayBuffer whose `passStyleOf` is `'byteArray'`.
9999
*
100+
* Honors the `Uint8Array`'s `byteOffset` and `length` so that a view
101+
* over a slice of a larger buffer round-trips correctly:
102+
* `uint8ArrayToByteArray(new Uint8Array(buf, byteOffset, length))`
103+
* always represents exactly the bytes in `[byteOffset, byteOffset +
104+
* length)`.
105+
*
106+
* If the underlying platform implements `sliceToImmutable` natively
107+
* with the obvious zero-copy optimization on an already-immutable
108+
* source buffer, this conversion is zero-copy. Otherwise, it pays
109+
* the copy cost implicit in `sliceToImmutable`.
110+
*
100111
* @param {Uint8Array} uint8Array
101112
* @returns {ArrayBuffer}
102113
*/
103-
export const uint8ArrayToByteArray = uint8Array =>
114+
export const uint8ArrayToByteArray = uint8Array => {
115+
const { byteOffset, length } = uint8Array;
104116
// @ts-expect-error shim-augmented ArrayBuffer type
105-
harden(uint8Array.buffer.sliceToImmutable(0, uint8Array.length));
117+
const byteArray = uint8Array.buffer.sliceToImmutable(
118+
byteOffset,
119+
byteOffset + length,
120+
);
121+
return harden(byteArray);
122+
};
106123
harden(uint8ArrayToByteArray);
107124

108125
/**
109-
* Hex-encodes the contents of a ByteArray.
126+
* Like `encodeHex` but starting from a presumably frozen (immutable)
127+
* ByteArray.
110128
*
111129
* @param {ByteArray} byteArray
112130
* @returns {string}
@@ -116,7 +134,9 @@ export const byteArrayToHex = byteArray =>
116134
harden(byteArrayToHex);
117135

118136
/**
119-
* Decodes a hex string into a ByteArray (hardened Immutable ArrayBuffer).
137+
* Like `decodeHex` but producing a hardened Immutable ArrayBuffer
138+
* (`passStyleOf === 'byteArray'`) instead of a fresh mutable
139+
* `Uint8Array`.
120140
*
121141
* @param {string} hex
122142
* @param {string} [name]

0 commit comments

Comments
 (0)