@solana/codecs-data-structures 8.3.0 → 8.4.0-canary-20260918134912
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -2
- package/dist/index.browser.cjs +48 -4
- package/dist/index.browser.cjs.map +1 -1
- package/dist/index.browser.mjs +50 -6
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.native.mjs +50 -6
- package/dist/index.native.mjs.map +1 -1
- package/dist/index.node.cjs +48 -4
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.mjs +50 -6
- package/dist/index.node.mjs.map +1 -1
- package/dist/types/array.d.ts +86 -2
- package/dist/types/array.d.ts.map +1 -1
- package/dist/types/map.d.ts +1 -0
- package/dist/types/map.d.ts.map +1 -1
- package/dist/types/set.d.ts +1 -0
- package/dist/types/set.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/array.ts +155 -4
- package/src/map.ts +1 -0
- package/src/set.ts +1 -0
package/dist/types/array.d.ts
CHANGED
|
@@ -1,5 +1,71 @@
|
|
|
1
|
-
import { Codec, Decoder, Encoder, FixedSizeCodec, FixedSizeDecoder, FixedSizeEncoder, VariableSizeCodec, VariableSizeDecoder, VariableSizeEncoder } from '@solana/codecs-core';
|
|
1
|
+
import { Codec, Decoder, Encoder, FixedSizeCodec, FixedSizeDecoder, FixedSizeEncoder, ReadonlyUint8Array, VariableSizeCodec, VariableSizeDecoder, VariableSizeEncoder } from '@solana/codecs-core';
|
|
2
2
|
import { NumberCodec, NumberDecoder, NumberEncoder } from '@solana/codecs-numbers';
|
|
3
|
+
/**
|
|
4
|
+
* Defines whether the sentinel of a {@link ArrayLikeCodecSentinelSize} strategy is written when
|
|
5
|
+
* encoding and required when decoding.
|
|
6
|
+
*
|
|
7
|
+
* This mirrors the `sentinelCountStrategy` enumeration of Codama's `sentinelCountNode`.
|
|
8
|
+
*
|
|
9
|
+
* - `"required"` — The sentinel is written after the last item and must be present when decoding.
|
|
10
|
+
* Reaching the end of the byte array without it is an error. This is the default.
|
|
11
|
+
* - `"optional"` — The sentinel is written after the last item; when decoding, it is consumed if
|
|
12
|
+
* present but the collection may also end at the end of the byte array. Use this to tolerate
|
|
13
|
+
* tightly sized or legacy data that lacks the sentinel.
|
|
14
|
+
* - `"omitted"` — The sentinel is never written; when decoding, it is consumed if present and the
|
|
15
|
+
* collection also ends at the end of the byte array. Only meaningful when the collection is
|
|
16
|
+
* followed by unused space or the end of the byte array.
|
|
17
|
+
*
|
|
18
|
+
* Under `"optional"` and `"omitted"`, the sentinel must be no wider than the smallest possible item
|
|
19
|
+
* (see the constraints on {@link ArrayLikeCodecSentinelSize}).
|
|
20
|
+
*
|
|
21
|
+
* @see {@link ArrayLikeCodecSentinelSize}
|
|
22
|
+
*/
|
|
23
|
+
export type SentinelCountStrategy = 'omitted' | 'optional' | 'required';
|
|
24
|
+
/**
|
|
25
|
+
* A size strategy for array-like codecs where the collection ends when the bytes at the next item
|
|
26
|
+
* position match a constant `sentinel`, compared at item boundaries only.
|
|
27
|
+
*
|
|
28
|
+
* Unlike {@link addCodecSentinel}, the sentinel is never searched for within an item's bytes, so its
|
|
29
|
+
* bytes may occur _inside_ an item without terminating the collection. This mirrors Codama's
|
|
30
|
+
* `sentinelCountNode`.
|
|
31
|
+
*
|
|
32
|
+
* @remarks
|
|
33
|
+
* Because the sentinel is only compared at the start of the next item slot, two invariants must hold
|
|
34
|
+
* for the collection to round-trip correctly. The codec does **not** enforce them — like Codama's
|
|
35
|
+
* `sentinelCountNode`, it is the caller's (or IDL author's) responsibility to guarantee them:
|
|
36
|
+
*
|
|
37
|
+
* 1. **No item may _begin_ with the sentinel's bytes.** A valid item that starts with the sentinel
|
|
38
|
+
* is indistinguishable from the terminator, so decoding would stop early at that item. The
|
|
39
|
+
* sentinel may still appear _inside_ an item, just never at its start. For instance, a single
|
|
40
|
+
* `0xff` byte is a poor sentinel for a list of public keys: roughly one key in 256 starts with
|
|
41
|
+
* `0xff`, so such a key would prematurely terminate the list. A sentinel as wide as an item — for
|
|
42
|
+
* instance the all-zero (default) public key — avoids this, since only that exact key can ever
|
|
43
|
+
* match the terminator.
|
|
44
|
+
* 2. **Under `"optional"` and `"omitted"`, the sentinel must be no wider than the smallest possible
|
|
45
|
+
* item.** Otherwise a trailing region shorter than the sentinel but large enough to hold a valid
|
|
46
|
+
* item would be skipped: decoding stops as soon as fewer bytes than the sentinel remain, so that
|
|
47
|
+
* final item would never be read. This cannot arise under `"required"` because a terminator is
|
|
48
|
+
* always written.
|
|
49
|
+
*
|
|
50
|
+
* @see {@link SentinelCountStrategy}
|
|
51
|
+
*/
|
|
52
|
+
export type ArrayLikeCodecSentinelSize = {
|
|
53
|
+
/** Internal discriminator identifying this object as a sentinel size strategy. */
|
|
54
|
+
readonly __kind: 'sentinel';
|
|
55
|
+
/**
|
|
56
|
+
* The fixed-size constant compared against the bytes at each item position.
|
|
57
|
+
*
|
|
58
|
+
* No valid item may begin with these bytes, and under the `"optional"` / `"omitted"` strategies
|
|
59
|
+
* this must be no wider than the smallest possible item. See the remarks above.
|
|
60
|
+
*/
|
|
61
|
+
readonly sentinel: ReadonlyUint8Array;
|
|
62
|
+
/**
|
|
63
|
+
* Whether the sentinel is written when encoding and required when decoding.
|
|
64
|
+
*
|
|
65
|
+
* @defaultValue `"required"`
|
|
66
|
+
*/
|
|
67
|
+
readonly strategy?: SentinelCountStrategy;
|
|
68
|
+
};
|
|
3
69
|
/**
|
|
4
70
|
* Defines the possible size strategies for array-like codecs (`array`, `map`, and `set`).
|
|
5
71
|
*
|
|
@@ -7,10 +73,12 @@ import { NumberCodec, NumberDecoder, NumberEncoder } from '@solana/codecs-number
|
|
|
7
73
|
* - A {@link NumberCodec}, {@link NumberDecoder}, or {@link NumberEncoder} to store a size prefix.
|
|
8
74
|
* - A fixed `number` of items, enforcing an exact length.
|
|
9
75
|
* - The string `"remainder"`, which infers the number of items by consuming the rest of the available bytes.
|
|
76
|
+
* - An {@link ArrayLikeCodecSentinelSize} object, which ends the collection when the bytes at the next
|
|
77
|
+
* item position match a constant sentinel.
|
|
10
78
|
*
|
|
11
79
|
* @typeParam TPrefix - A number codec, decoder, or encoder used for size prefixing.
|
|
12
80
|
*/
|
|
13
|
-
export type ArrayLikeCodecSize<TPrefix extends NumberCodec | NumberDecoder | NumberEncoder> = TPrefix | number | 'remainder';
|
|
81
|
+
export type ArrayLikeCodecSize<TPrefix extends NumberCodec | NumberDecoder | NumberEncoder> = ArrayLikeCodecSentinelSize | TPrefix | number | 'remainder';
|
|
14
82
|
/**
|
|
15
83
|
* Defines the configuration options for array codecs.
|
|
16
84
|
*
|
|
@@ -40,6 +108,8 @@ export type ArrayCodecConfig<TPrefix extends NumberCodec | NumberDecoder | Numbe
|
|
|
40
108
|
* - A {@link NumberCodec}, {@link NumberDecoder}, or {@link NumberEncoder} stores a size prefix before encoding the array.
|
|
41
109
|
* - A `number` enforces a fixed number of elements.
|
|
42
110
|
* - `"remainder"` uses all remaining bytes to infer the array length (only for fixed-size items).
|
|
111
|
+
* - An {@link ArrayLikeCodecSentinelSize} object ends the array when the bytes at the next item
|
|
112
|
+
* position match a constant sentinel.
|
|
43
113
|
*
|
|
44
114
|
* @defaultValue A `u32` size prefix.
|
|
45
115
|
*/
|
|
@@ -170,6 +240,18 @@ export declare function getArrayDecoder<TTo>(item: Decoder<TTo>, config?: ArrayC
|
|
|
170
240
|
* ```
|
|
171
241
|
*
|
|
172
242
|
* @example
|
|
243
|
+
* Using a sentinel to mark the end of the array. Note that no valid item may begin with the
|
|
244
|
+
* sentinel's bytes, or decoding would stop early — see {@link ArrayLikeCodecSentinelSize} for the
|
|
245
|
+
* full constraints.
|
|
246
|
+
* ```ts
|
|
247
|
+
* const codec = getArrayCodec(getU8Codec(), { size: { __kind: 'sentinel', sentinel: new Uint8Array([0]) } });
|
|
248
|
+
* codec.encode([1, 2, 3]);
|
|
249
|
+
* // 0x01020300
|
|
250
|
+
* // | └-- The sentinel that marks the end of the array.
|
|
251
|
+
* // └-- 3 items of 1 byte each.
|
|
252
|
+
* ```
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
173
255
|
* Requiring the size prefix to be present when decoding.
|
|
174
256
|
* ```ts
|
|
175
257
|
* getArrayCodec(getU8Codec()).decode(new Uint8Array([]));
|
|
@@ -184,6 +266,8 @@ export declare function getArrayDecoder<TTo>(item: Decoder<TTo>, config?: ArrayC
|
|
|
184
266
|
* - A `Codec<number>` (e.g. `getU16Codec()`) stores a size prefix before the array.
|
|
185
267
|
* - A `number` enforces a fixed number of elements.
|
|
186
268
|
* - `"remainder"` uses all remaining bytes to infer the array length.
|
|
269
|
+
* - A sentinel object (see {@link ArrayLikeCodecSentinelSize}) ends the array when the bytes at the
|
|
270
|
+
* next item position match a constant sentinel.
|
|
187
271
|
*
|
|
188
272
|
* When the size is stored as a prefix, decoding an exhausted byte array yields an empty array,
|
|
189
273
|
* which allows arrays to be appended to existing data layouts without breaking older data.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"array.d.ts","sourceRoot":"","sources":["../../src/array.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,
|
|
1
|
+
{"version":3,"file":"array.d.ts","sourceRoot":"","sources":["../../src/array.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,EAKL,OAAO,EACP,OAAO,EACP,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAEhB,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAgC,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAUjH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACrC,kFAAkF;IAClF,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,qBAAqB,CAAC;CAC7C,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,kBAAkB,CAAC,OAAO,SAAS,WAAW,GAAG,aAAa,GAAG,aAAa,IACpF,0BAA0B,GAC1B,OAAO,GACP,MAAM,GACN,WAAW,CAAC;AAElB;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,CAAC,OAAO,SAAS,WAAW,GAAG,aAAa,GAAG,aAAa,IAAI;IACxF;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;;;;;OAWG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAC;CACtC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,eAAe,CAAC,KAAK,EACjC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,EACpB,MAAM,EAAE,gBAAgB,CAAC,aAAa,CAAC,GAAG;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,GACtD,gBAAgB,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;AAChC,wBAAgB,eAAe,CAAC,KAAK,EACjC,IAAI,EAAE,gBAAgB,CAAC,KAAK,CAAC,EAC7B,MAAM,EAAE,gBAAgB,CAAC,aAAa,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAC3D,gBAAgB,CAAC,KAAK,EAAE,CAAC,CAAC;AAC7B,wBAAgB,eAAe,CAAC,KAAK,EACjC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,EACpB,MAAM,CAAC,EAAE,gBAAgB,CAAC,aAAa,CAAC,GACzC,mBAAmB,CAAC,KAAK,EAAE,CAAC,CAAC;AA4ChC;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAC/B,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,EAClB,MAAM,EAAE,gBAAgB,CAAC,aAAa,CAAC,GAAG;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,GACtD,gBAAgB,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;AAC9B,wBAAgB,eAAe,CAAC,GAAG,EAC/B,IAAI,EAAE,gBAAgB,CAAC,GAAG,CAAC,EAC3B,MAAM,EAAE,gBAAgB,CAAC,aAAa,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAC3D,gBAAgB,CAAC,GAAG,EAAE,CAAC,CAAC;AAC3B,wBAAgB,eAAe,CAAC,GAAG,EAC/B,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,EAClB,MAAM,CAAC,EAAE,gBAAgB,CAAC,aAAa,CAAC,GACzC,mBAAmB,CAAC,GAAG,EAAE,CAAC,CAAC;AA+D9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,SAAS,KAAK,GAAG,KAAK,EAC1D,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,EACvB,MAAM,EAAE,gBAAgB,CAAC,WAAW,CAAC,GAAG;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,GACpD,cAAc,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;AACrC,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,SAAS,KAAK,GAAG,KAAK,EAC1D,IAAI,EAAE,cAAc,CAAC,KAAK,EAAE,GAAG,CAAC,EAChC,MAAM,EAAE,gBAAgB,CAAC,WAAW,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GACzD,cAAc,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;AAClC,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,SAAS,KAAK,GAAG,KAAK,EAC1D,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,EACvB,MAAM,CAAC,EAAE,gBAAgB,CAAC,WAAW,CAAC,GACvC,iBAAiB,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC"}
|
package/dist/types/map.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { ArrayLikeCodecSize } from './array';
|
|
|
10
10
|
* - A fixed number of entries.
|
|
11
11
|
* - `'remainder'`, which infers the number of entries based on the remaining bytes.
|
|
12
12
|
* This option is only available for fixed-size keys and values.
|
|
13
|
+
* - An {@link ArrayLikeCodecSentinelSize} object, which ends the map when the bytes at the next entry position match a constant sentinel.
|
|
13
14
|
*
|
|
14
15
|
* @typeParam TPrefix - A number codec, encoder, or decoder used for the size prefix.
|
|
15
16
|
*/
|
package/dist/types/map.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../../src/map.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,EAEL,OAAO,EACP,OAAO,EACP,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAGhB,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEnF,OAAO,EAAE,kBAAkB,EAAoC,MAAM,SAAS,CAAC;AAG/E
|
|
1
|
+
{"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../../src/map.ts"],"names":[],"mappings":"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"}
|
package/dist/types/set.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { ArrayLikeCodecSize } from './array';
|
|
|
10
10
|
* - A {@link NumberCodec}, {@link NumberEncoder}, or {@link NumberDecoder} to store the size as a prefix.
|
|
11
11
|
* - A fixed number of items, enforcing a strict length.
|
|
12
12
|
* - The string `'remainder'` to infer the set size from the remaining bytes (only for fixed-size items).
|
|
13
|
+
* - An {@link ArrayLikeCodecSentinelSize} object to end the set when the bytes at the next item position match a constant sentinel.
|
|
13
14
|
*
|
|
14
15
|
* @typeParam TPrefix - The type used for encoding the size of the set.
|
|
15
16
|
*/
|
package/dist/types/set.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"set.d.ts","sourceRoot":"","sources":["../../src/set.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,EAEL,OAAO,EACP,OAAO,EACP,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAGhB,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEnF,OAAO,EAAE,kBAAkB,EAAoC,MAAM,SAAS,CAAC;AAE/E
|
|
1
|
+
{"version":3,"file":"set.d.ts","sourceRoot":"","sources":["../../src/set.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solana/codecs-data-structures",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.4.0-canary-20260918134912",
|
|
4
4
|
"description": "Codecs for various data structures",
|
|
5
5
|
"homepage": "https://www.solanakit.com/api#solanacodecs-data-structures",
|
|
6
6
|
"exports": {
|
|
@@ -56,9 +56,9 @@
|
|
|
56
56
|
"maintained node versions"
|
|
57
57
|
],
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@solana/codecs-core": "8.
|
|
60
|
-
"@solana/codecs-numbers": "8.
|
|
61
|
-
"@solana/errors": "8.
|
|
59
|
+
"@solana/codecs-core": "8.4.0-canary-20260918134912",
|
|
60
|
+
"@solana/codecs-numbers": "8.4.0-canary-20260918134912",
|
|
61
|
+
"@solana/errors": "8.4.0-canary-20260918134912"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
64
|
"typescript": ">=5.4.0"
|
package/src/array.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Codec,
|
|
3
3
|
combineCodec,
|
|
4
|
+
containsBytes,
|
|
4
5
|
createDecoder,
|
|
5
6
|
createEncoder,
|
|
6
7
|
Decoder,
|
|
@@ -15,10 +16,83 @@ import {
|
|
|
15
16
|
VariableSizeEncoder,
|
|
16
17
|
} from '@solana/codecs-core';
|
|
17
18
|
import { getU32Decoder, getU32Encoder, NumberCodec, NumberDecoder, NumberEncoder } from '@solana/codecs-numbers';
|
|
19
|
+
import {
|
|
20
|
+
SOLANA_ERROR__CODECS__SENTINEL_MISSING_AT_END_OF_BYTES,
|
|
21
|
+
SOLANA_ERROR__CODECS__SENTINEL_MUST_NOT_BE_EMPTY,
|
|
22
|
+
SolanaError,
|
|
23
|
+
} from '@solana/errors';
|
|
18
24
|
|
|
19
25
|
import { assertValidNumberOfItemsForCodec } from './assertions';
|
|
20
26
|
import { getFixedSize, getMaxSize } from './utils';
|
|
21
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Defines whether the sentinel of a {@link ArrayLikeCodecSentinelSize} strategy is written when
|
|
30
|
+
* encoding and required when decoding.
|
|
31
|
+
*
|
|
32
|
+
* This mirrors the `sentinelCountStrategy` enumeration of Codama's `sentinelCountNode`.
|
|
33
|
+
*
|
|
34
|
+
* - `"required"` — The sentinel is written after the last item and must be present when decoding.
|
|
35
|
+
* Reaching the end of the byte array without it is an error. This is the default.
|
|
36
|
+
* - `"optional"` — The sentinel is written after the last item; when decoding, it is consumed if
|
|
37
|
+
* present but the collection may also end at the end of the byte array. Use this to tolerate
|
|
38
|
+
* tightly sized or legacy data that lacks the sentinel.
|
|
39
|
+
* - `"omitted"` — The sentinel is never written; when decoding, it is consumed if present and the
|
|
40
|
+
* collection also ends at the end of the byte array. Only meaningful when the collection is
|
|
41
|
+
* followed by unused space or the end of the byte array.
|
|
42
|
+
*
|
|
43
|
+
* Under `"optional"` and `"omitted"`, the sentinel must be no wider than the smallest possible item
|
|
44
|
+
* (see the constraints on {@link ArrayLikeCodecSentinelSize}).
|
|
45
|
+
*
|
|
46
|
+
* @see {@link ArrayLikeCodecSentinelSize}
|
|
47
|
+
*/
|
|
48
|
+
export type SentinelCountStrategy = 'omitted' | 'optional' | 'required';
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* A size strategy for array-like codecs where the collection ends when the bytes at the next item
|
|
52
|
+
* position match a constant `sentinel`, compared at item boundaries only.
|
|
53
|
+
*
|
|
54
|
+
* Unlike {@link addCodecSentinel}, the sentinel is never searched for within an item's bytes, so its
|
|
55
|
+
* bytes may occur _inside_ an item without terminating the collection. This mirrors Codama's
|
|
56
|
+
* `sentinelCountNode`.
|
|
57
|
+
*
|
|
58
|
+
* @remarks
|
|
59
|
+
* Because the sentinel is only compared at the start of the next item slot, two invariants must hold
|
|
60
|
+
* for the collection to round-trip correctly. The codec does **not** enforce them — like Codama's
|
|
61
|
+
* `sentinelCountNode`, it is the caller's (or IDL author's) responsibility to guarantee them:
|
|
62
|
+
*
|
|
63
|
+
* 1. **No item may _begin_ with the sentinel's bytes.** A valid item that starts with the sentinel
|
|
64
|
+
* is indistinguishable from the terminator, so decoding would stop early at that item. The
|
|
65
|
+
* sentinel may still appear _inside_ an item, just never at its start. For instance, a single
|
|
66
|
+
* `0xff` byte is a poor sentinel for a list of public keys: roughly one key in 256 starts with
|
|
67
|
+
* `0xff`, so such a key would prematurely terminate the list. A sentinel as wide as an item — for
|
|
68
|
+
* instance the all-zero (default) public key — avoids this, since only that exact key can ever
|
|
69
|
+
* match the terminator.
|
|
70
|
+
* 2. **Under `"optional"` and `"omitted"`, the sentinel must be no wider than the smallest possible
|
|
71
|
+
* item.** Otherwise a trailing region shorter than the sentinel but large enough to hold a valid
|
|
72
|
+
* item would be skipped: decoding stops as soon as fewer bytes than the sentinel remain, so that
|
|
73
|
+
* final item would never be read. This cannot arise under `"required"` because a terminator is
|
|
74
|
+
* always written.
|
|
75
|
+
*
|
|
76
|
+
* @see {@link SentinelCountStrategy}
|
|
77
|
+
*/
|
|
78
|
+
export type ArrayLikeCodecSentinelSize = {
|
|
79
|
+
/** Internal discriminator identifying this object as a sentinel size strategy. */
|
|
80
|
+
readonly __kind: 'sentinel';
|
|
81
|
+
/**
|
|
82
|
+
* The fixed-size constant compared against the bytes at each item position.
|
|
83
|
+
*
|
|
84
|
+
* No valid item may begin with these bytes, and under the `"optional"` / `"omitted"` strategies
|
|
85
|
+
* this must be no wider than the smallest possible item. See the remarks above.
|
|
86
|
+
*/
|
|
87
|
+
readonly sentinel: ReadonlyUint8Array;
|
|
88
|
+
/**
|
|
89
|
+
* Whether the sentinel is written when encoding and required when decoding.
|
|
90
|
+
*
|
|
91
|
+
* @defaultValue `"required"`
|
|
92
|
+
*/
|
|
93
|
+
readonly strategy?: SentinelCountStrategy;
|
|
94
|
+
};
|
|
95
|
+
|
|
22
96
|
/**
|
|
23
97
|
* Defines the possible size strategies for array-like codecs (`array`, `map`, and `set`).
|
|
24
98
|
*
|
|
@@ -26,10 +100,13 @@ import { getFixedSize, getMaxSize } from './utils';
|
|
|
26
100
|
* - A {@link NumberCodec}, {@link NumberDecoder}, or {@link NumberEncoder} to store a size prefix.
|
|
27
101
|
* - A fixed `number` of items, enforcing an exact length.
|
|
28
102
|
* - The string `"remainder"`, which infers the number of items by consuming the rest of the available bytes.
|
|
103
|
+
* - An {@link ArrayLikeCodecSentinelSize} object, which ends the collection when the bytes at the next
|
|
104
|
+
* item position match a constant sentinel.
|
|
29
105
|
*
|
|
30
106
|
* @typeParam TPrefix - A number codec, decoder, or encoder used for size prefixing.
|
|
31
107
|
*/
|
|
32
108
|
export type ArrayLikeCodecSize<TPrefix extends NumberCodec | NumberDecoder | NumberEncoder> =
|
|
109
|
+
| ArrayLikeCodecSentinelSize
|
|
33
110
|
| TPrefix
|
|
34
111
|
| number
|
|
35
112
|
| 'remainder';
|
|
@@ -63,6 +140,8 @@ export type ArrayCodecConfig<TPrefix extends NumberCodec | NumberDecoder | Numbe
|
|
|
63
140
|
* - A {@link NumberCodec}, {@link NumberDecoder}, or {@link NumberEncoder} stores a size prefix before encoding the array.
|
|
64
141
|
* - A `number` enforces a fixed number of elements.
|
|
65
142
|
* - `"remainder"` uses all remaining bytes to infer the array length (only for fixed-size items).
|
|
143
|
+
* - An {@link ArrayLikeCodecSentinelSize} object ends the array when the bytes at the next item
|
|
144
|
+
* position match a constant sentinel.
|
|
66
145
|
*
|
|
67
146
|
* @defaultValue A `u32` size prefix.
|
|
68
147
|
*/
|
|
@@ -113,6 +192,7 @@ export function getArrayEncoder<TFrom>(
|
|
|
113
192
|
config: ArrayCodecConfig<NumberEncoder> = {},
|
|
114
193
|
): Encoder<TFrom[]> {
|
|
115
194
|
const size = config.size ?? getU32Encoder();
|
|
195
|
+
assertValidSize(size);
|
|
116
196
|
const fixedSize = computeArrayLikeCodecSize(size, getFixedSize(item));
|
|
117
197
|
const maxSize = computeArrayLikeCodecSize(size, getMaxSize(item)) ?? undefined;
|
|
118
198
|
|
|
@@ -121,8 +201,13 @@ export function getArrayEncoder<TFrom>(
|
|
|
121
201
|
? { fixedSize }
|
|
122
202
|
: {
|
|
123
203
|
getSizeFromValue: (array: TFrom[]) => {
|
|
124
|
-
const prefixSize =
|
|
125
|
-
|
|
204
|
+
const prefixSize = isPrefixSize(size) ? getEncodedSize(array.length, size) : 0;
|
|
205
|
+
const suffixSize = isSentinelSize(size) && size.strategy !== 'omitted' ? size.sentinel.length : 0;
|
|
206
|
+
return (
|
|
207
|
+
prefixSize +
|
|
208
|
+
suffixSize +
|
|
209
|
+
[...array].reduce((all, value) => all + getEncodedSize(value, item), 0)
|
|
210
|
+
);
|
|
126
211
|
},
|
|
127
212
|
maxSize,
|
|
128
213
|
}),
|
|
@@ -130,12 +215,16 @@ export function getArrayEncoder<TFrom>(
|
|
|
130
215
|
if (typeof size === 'number') {
|
|
131
216
|
assertValidNumberOfItemsForCodec(config.description ?? 'array', size, array.length);
|
|
132
217
|
}
|
|
133
|
-
if (
|
|
218
|
+
if (isPrefixSize(size)) {
|
|
134
219
|
offset = size.write(array.length, bytes, offset);
|
|
135
220
|
}
|
|
136
221
|
array.forEach(value => {
|
|
137
222
|
offset = item.write(value, bytes, offset);
|
|
138
223
|
});
|
|
224
|
+
if (isSentinelSize(size) && size.strategy !== 'omitted') {
|
|
225
|
+
bytes.set(size.sentinel, offset);
|
|
226
|
+
offset += size.sentinel.length;
|
|
227
|
+
}
|
|
139
228
|
return offset;
|
|
140
229
|
},
|
|
141
230
|
});
|
|
@@ -183,6 +272,7 @@ export function getArrayDecoder<TTo>(
|
|
|
183
272
|
): VariableSizeDecoder<TTo[]>;
|
|
184
273
|
export function getArrayDecoder<TTo>(item: Decoder<TTo>, config: ArrayCodecConfig<NumberDecoder> = {}): Decoder<TTo[]> {
|
|
185
274
|
const size = config.size ?? getU32Decoder();
|
|
275
|
+
assertValidSize(size);
|
|
186
276
|
const itemSize = getFixedSize(item);
|
|
187
277
|
const fixedSize = computeArrayLikeCodecSize(size, itemSize);
|
|
188
278
|
const maxSize = computeArrayLikeCodecSize(size, getMaxSize(item)) ?? undefined;
|
|
@@ -191,7 +281,7 @@ export function getArrayDecoder<TTo>(item: Decoder<TTo>, config: ArrayCodecConfi
|
|
|
191
281
|
...(fixedSize !== null ? { fixedSize } : { maxSize }),
|
|
192
282
|
read: (bytes: ReadonlyUint8Array | Uint8Array, offset) => {
|
|
193
283
|
const array: TTo[] = [];
|
|
194
|
-
if (
|
|
284
|
+
if (isPrefixSize(size) && !config.requireSizePrefix && offset >= bytes.length) {
|
|
195
285
|
return [array, offset];
|
|
196
286
|
}
|
|
197
287
|
|
|
@@ -204,6 +294,32 @@ export function getArrayDecoder<TTo>(item: Decoder<TTo>, config: ArrayCodecConfi
|
|
|
204
294
|
return [array, offset];
|
|
205
295
|
}
|
|
206
296
|
|
|
297
|
+
if (isSentinelSize(size)) {
|
|
298
|
+
const { sentinel, strategy = 'required' } = size;
|
|
299
|
+
while (true) {
|
|
300
|
+
if (offset + sentinel.length > bytes.length) {
|
|
301
|
+
// Not enough bytes remain to hold the sentinel.
|
|
302
|
+
if (strategy === 'required') {
|
|
303
|
+
throw new SolanaError(SOLANA_ERROR__CODECS__SENTINEL_MISSING_AT_END_OF_BYTES, {
|
|
304
|
+
codecDescription: config.description ?? 'array',
|
|
305
|
+
hexSentinel: hexBytes(sentinel),
|
|
306
|
+
sentinel,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
if (containsBytes(bytes, sentinel, offset)) {
|
|
312
|
+
// The sentinel is present; consume it and stop.
|
|
313
|
+
offset += sentinel.length;
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
const [value, newOffset] = item.read(bytes, offset);
|
|
317
|
+
offset = newOffset;
|
|
318
|
+
array.push(value);
|
|
319
|
+
}
|
|
320
|
+
return [array, offset];
|
|
321
|
+
}
|
|
322
|
+
|
|
207
323
|
const [resolvedSize, newOffset] = typeof size === 'number' ? [size, offset] : size.read(bytes, offset);
|
|
208
324
|
offset = newOffset;
|
|
209
325
|
for (let i = 0; i < resolvedSize; i += 1) {
|
|
@@ -272,6 +388,18 @@ export function getArrayDecoder<TTo>(item: Decoder<TTo>, config: ArrayCodecConfi
|
|
|
272
388
|
* ```
|
|
273
389
|
*
|
|
274
390
|
* @example
|
|
391
|
+
* Using a sentinel to mark the end of the array. Note that no valid item may begin with the
|
|
392
|
+
* sentinel's bytes, or decoding would stop early — see {@link ArrayLikeCodecSentinelSize} for the
|
|
393
|
+
* full constraints.
|
|
394
|
+
* ```ts
|
|
395
|
+
* const codec = getArrayCodec(getU8Codec(), { size: { __kind: 'sentinel', sentinel: new Uint8Array([0]) } });
|
|
396
|
+
* codec.encode([1, 2, 3]);
|
|
397
|
+
* // 0x01020300
|
|
398
|
+
* // | └-- The sentinel that marks the end of the array.
|
|
399
|
+
* // └-- 3 items of 1 byte each.
|
|
400
|
+
* ```
|
|
401
|
+
*
|
|
402
|
+
* @example
|
|
275
403
|
* Requiring the size prefix to be present when decoding.
|
|
276
404
|
* ```ts
|
|
277
405
|
* getArrayCodec(getU8Codec()).decode(new Uint8Array([]));
|
|
@@ -286,6 +414,8 @@ export function getArrayDecoder<TTo>(item: Decoder<TTo>, config: ArrayCodecConfi
|
|
|
286
414
|
* - A `Codec<number>` (e.g. `getU16Codec()`) stores a size prefix before the array.
|
|
287
415
|
* - A `number` enforces a fixed number of elements.
|
|
288
416
|
* - `"remainder"` uses all remaining bytes to infer the array length.
|
|
417
|
+
* - A sentinel object (see {@link ArrayLikeCodecSentinelSize}) ends the array when the bytes at the
|
|
418
|
+
* next item position match a constant sentinel.
|
|
289
419
|
*
|
|
290
420
|
* When the size is stored as a prefix, decoding an exhausted byte array yields an empty array,
|
|
291
421
|
* which allows arrays to be appended to existing data layouts without breaking older data.
|
|
@@ -325,3 +455,24 @@ function computeArrayLikeCodecSize(size: number | object | 'remainder', itemSize
|
|
|
325
455
|
if (size === 0) return 0;
|
|
326
456
|
return itemSize === null ? null : itemSize * size;
|
|
327
457
|
}
|
|
458
|
+
|
|
459
|
+
/** Narrows an array-like size to a numeric prefix codec, decoder, or encoder. */
|
|
460
|
+
function isPrefixSize(size: unknown): size is NumberCodec | NumberDecoder | NumberEncoder {
|
|
461
|
+
return typeof size === 'object' && size !== null && !isSentinelSize(size);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/** Narrows an array-like size to an {@link ArrayLikeCodecSentinelSize} object. */
|
|
465
|
+
function isSentinelSize(size: unknown): size is ArrayLikeCodecSentinelSize {
|
|
466
|
+
return typeof size === 'object' && size !== null && '__kind' in size && size.__kind === 'sentinel';
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/** Throws if an array-like size strategy is misconfigured (e.g. a sentinel that can never be matched). */
|
|
470
|
+
function assertValidSize(size: number | object | 'remainder'): void {
|
|
471
|
+
if (isSentinelSize(size) && size.sentinel.length === 0) {
|
|
472
|
+
throw new SolanaError(SOLANA_ERROR__CODECS__SENTINEL_MUST_NOT_BE_EMPTY);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function hexBytes(bytes: ReadonlyUint8Array): string {
|
|
477
|
+
return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
|
|
478
|
+
}
|
package/src/map.ts
CHANGED
|
@@ -26,6 +26,7 @@ import { getTupleDecoder, getTupleEncoder } from './tuple';
|
|
|
26
26
|
* - A fixed number of entries.
|
|
27
27
|
* - `'remainder'`, which infers the number of entries based on the remaining bytes.
|
|
28
28
|
* This option is only available for fixed-size keys and values.
|
|
29
|
+
* - An {@link ArrayLikeCodecSentinelSize} object, which ends the map when the bytes at the next entry position match a constant sentinel.
|
|
29
30
|
*
|
|
30
31
|
* @typeParam TPrefix - A number codec, encoder, or decoder used for the size prefix.
|
|
31
32
|
*/
|
package/src/set.ts
CHANGED
|
@@ -25,6 +25,7 @@ import { ArrayLikeCodecSize, getArrayDecoder, getArrayEncoder } from './array';
|
|
|
25
25
|
* - A {@link NumberCodec}, {@link NumberEncoder}, or {@link NumberDecoder} to store the size as a prefix.
|
|
26
26
|
* - A fixed number of items, enforcing a strict length.
|
|
27
27
|
* - The string `'remainder'` to infer the set size from the remaining bytes (only for fixed-size items).
|
|
28
|
+
* - An {@link ArrayLikeCodecSentinelSize} object to end the set when the bytes at the next item position match a constant sentinel.
|
|
28
29
|
*
|
|
29
30
|
* @typeParam TPrefix - The type used for encoding the size of the set.
|
|
30
31
|
*/
|