edfcore 0.2.34 → 0.2.37
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/CHANGELOG.md +25 -0
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/io/bytes.d.ts.map +1 -1
- package/dist/io/bytes.js +37 -4
- package/dist/io/bytes.js.map +1 -1
- package/dist/io/cached.d.ts.map +1 -1
- package/dist/io/cached.js +14 -2
- package/dist/io/cached.js.map +1 -1
- package/dist/io/source.d.ts +8 -0
- package/dist/io/source.d.ts.map +1 -1
- package/dist/io/source.js +11 -5
- package/dist/io/source.js.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +1 -1
- package/src/io/bytes.ts +39 -4
- package/src/io/cached.ts +15 -2
- package/src/io/source.ts +12 -3
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,31 @@ alone does not tell you whether you were affected.
|
|
|
6
6
|
edfcore is pre-1.0. Patch releases have carried behaviour changes where the old behaviour was a
|
|
7
7
|
defect; those are called out below.
|
|
8
8
|
|
|
9
|
+
## 0.2.36
|
|
10
|
+
|
|
11
|
+
- **Fixed** `cachedSource` repopulating itself after `close()`. A read already in flight when close
|
|
12
|
+
was called still resolves, and its continuation still runs — after `blocks.clear()` — so the
|
|
13
|
+
cache refilled itself after being closed and then served that data on later reads, from a source
|
|
14
|
+
whose own `close` had already run. Admission is now refused once closed, so the cache stays empty
|
|
15
|
+
and every later read is delegated to the wrapped source, which behaves exactly as it would
|
|
16
|
+
without the wrapper.
|
|
17
|
+
- **Documented** why the oversized-read path returns the wrapped source's own array rather than a
|
|
18
|
+
copy. It looks like a violation of the "a cache hands back a copy" rule and is not: that rule
|
|
19
|
+
exists because a cache RETAINS its blocks, and a read wider than the whole budget bypasses the
|
|
20
|
+
cache entirely and retains nothing. The path is exactly as safe as calling the wrapped source
|
|
21
|
+
directly, which is what it does.
|
|
22
|
+
|
|
23
|
+
## 0.2.35
|
|
24
|
+
|
|
25
|
+
- **Fixed** `byteSource` building a source over an argument that is not bytes, so the caller's
|
|
26
|
+
mistake was reported as a defect in the file. `new Uint8Array(x)` accepts almost anything: a
|
|
27
|
+
string, a plain object and `null` all yield an empty array, and a `number[]` yields one of the
|
|
28
|
+
wrong length. The source was constructed happily and the failure surfaced later as
|
|
29
|
+
`[SOURCE_TOO_SMALL] the header is 0 bytes` — the file blamed for the argument, which is the one
|
|
30
|
+
confusion this package works hardest to avoid. It now refuses at construction and says what it
|
|
31
|
+
wanted. `Int8Array` is refused by name: one byte per element, so it passes every length check
|
|
32
|
+
and then decodes to fabricated sample values.
|
|
33
|
+
|
|
9
34
|
## 0.2.34
|
|
10
35
|
|
|
11
36
|
- **Added** the golden-value harness this README has withheld a numerical-interop claim for since
|
package/dist/constants.d.ts
CHANGED
|
@@ -109,5 +109,5 @@ export declare const SIGNAL_FIELD_BLOCK_OFFSETS: {
|
|
|
109
109
|
readonly reserved: 224;
|
|
110
110
|
};
|
|
111
111
|
/** Published package version. Kept in sync with package.json by a test. */
|
|
112
|
-
export declare const VERSION = "0.2.
|
|
112
|
+
export declare const VERSION = "0.2.37";
|
|
113
113
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.js
CHANGED
package/dist/io/bytes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bytes.d.ts","sourceRoot":"","sources":["../../src/io/bytes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"bytes.d.ts","sourceRoot":"","sources":["../../src/io/bytes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAe,MAAM,aAAa,CAAC;AAe3D,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,UAAU,GAAG,UAAU,CAsCtE"}
|
package/dist/io/bytes.js
CHANGED
|
@@ -6,11 +6,44 @@
|
|
|
6
6
|
* owned by the caller" rule exists to stop an adapter handing out a view into state it retains,
|
|
7
7
|
* and this adapter retains nothing the caller does not already hold.
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
9
|
+
import { EdfSourceError } from '../errors.js';
|
|
10
|
+
import { assertExactRead, assertReadRange, isByteArray, throwIfAborted } from './source.js';
|
|
11
|
+
/**
|
|
12
|
+
* Describes what arrived, for an argument that is not bytes. Never prints the value: it could be
|
|
13
|
+
* anything, including something large.
|
|
14
|
+
*/
|
|
15
|
+
function describe(value) {
|
|
16
|
+
if (value === null)
|
|
17
|
+
return 'null';
|
|
18
|
+
if (value === undefined)
|
|
19
|
+
return 'undefined';
|
|
20
|
+
if (ArrayBuffer.isView(value))
|
|
21
|
+
return Object.prototype.toString.call(value).slice(8, -1);
|
|
22
|
+
if (Array.isArray(value))
|
|
23
|
+
return 'a plain Array';
|
|
24
|
+
return typeof value === 'object' ? 'a plain object' : `a ${typeof value}`;
|
|
25
|
+
}
|
|
10
26
|
export function byteSource(bytes) {
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
|
|
27
|
+
// Refused at CONSTRUCTION, because the alternative is worse than an error. `new Uint8Array(x)`
|
|
28
|
+
// accepts almost anything: a string, a plain object and `null` all yield an empty array, and a
|
|
29
|
+
// `number[]` yields one of the wrong length. The source was then built happily and the failure
|
|
30
|
+
// surfaced later as `[SOURCE_TOO_SMALL] the header is 0 bytes` — blaming the FILE for a mistake
|
|
31
|
+
// in the caller's argument, which is the one confusion this package works hardest to avoid.
|
|
32
|
+
//
|
|
33
|
+
// `Int8Array` is rejected too, and deliberately: it has one byte per element so it passes any
|
|
34
|
+
// length check, and its already-signed elements are sign-extended a second time during decode
|
|
35
|
+
// (see `assertExactRead`). Fabricated microvolts, with no error anywhere.
|
|
36
|
+
const isBuffer = bytes instanceof ArrayBuffer ||
|
|
37
|
+
Object.prototype.toString.call(bytes) === '[object SharedArrayBuffer]';
|
|
38
|
+
if (!isBuffer && !isByteArray(bytes)) {
|
|
39
|
+
throw new EdfSourceError(`byteSource() needs an ArrayBuffer or a Uint8Array, received ${describe(bytes)}. ` +
|
|
40
|
+
'Next: pass `new Uint8Array(await blob.arrayBuffer())`, `await readFile(path)`, or the ' +
|
|
41
|
+
'ArrayBuffer itself. An Int8Array is not accepted — it has one byte per element, so it ' +
|
|
42
|
+
'would pass every length check and then decode to fabricated sample values.', { offset: 0, requestedLength: 0 });
|
|
43
|
+
}
|
|
44
|
+
// `isByteArray` rather than `instanceof Uint8Array`: `instanceof` is false for a view that
|
|
45
|
+
// crossed a realm boundary (a worker, an iframe), and this is a public entry point.
|
|
46
|
+
const view = isByteArray(bytes) ? bytes : new Uint8Array(bytes);
|
|
14
47
|
const byteLength = view.byteLength;
|
|
15
48
|
return {
|
|
16
49
|
byteLength,
|
package/dist/io/bytes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bytes.js","sourceRoot":"","sources":["../../src/io/bytes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"bytes.js","sourceRoot":"","sources":["../../src/io/bytes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE5F;;;GAGG;AACH,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAClC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,WAAW,CAAC;IAC5C,IAAI,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACzF,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,eAAe,CAAC;IACjD,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,OAAO,KAAK,EAAE,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAA+B;IACxD,+FAA+F;IAC/F,+FAA+F;IAC/F,+FAA+F;IAC/F,gGAAgG;IAChG,4FAA4F;IAC5F,EAAE;IACF,8FAA8F;IAC9F,8FAA8F;IAC9F,0EAA0E;IAC1E,MAAM,QAAQ,GACZ,KAAK,YAAY,WAAW;QAC5B,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,4BAA4B,CAAC;IACzE,IAAI,CAAC,QAAQ,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,cAAc,CACtB,+DAA+D,QAAQ,CAAC,KAAK,CAAC,IAAI;YAChF,wFAAwF;YACxF,wFAAwF;YACxF,4EAA4E,EAC9E,EAAE,MAAM,EAAE,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,CAClC,CAAC;IACJ,CAAC;IAED,2FAA2F;IAC3F,oFAAoF;IACpF,MAAM,IAAI,GAAe,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,KAAoB,CAAC,CAAC;IAC3F,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;IAEnC,OAAO;QACL,UAAU;QACV,KAAK,CAAC,IAAI,CAAC,MAAc,EAAE,MAAc,EAAE,OAAqB;YAC9D,cAAc,CAAC,OAAO,CAAC,CAAC;YACxB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;YAC5C,iFAAiF;YACjF,+EAA+E;YAC/E,OAAO,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QACjF,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/io/cached.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cached.d.ts","sourceRoot":"","sources":["../../src/io/cached.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAe,MAAM,aAAa,CAAC;AAmBzE,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,UAAU,
|
|
1
|
+
{"version":3,"file":"cached.d.ts","sourceRoot":"","sources":["../../src/io/cached.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAe,MAAM,aAAa,CAAC;AAmBzE,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,UAAU,CAsHnF"}
|
package/dist/io/cached.js
CHANGED
|
@@ -52,8 +52,13 @@ export function cachedSource(source, options) {
|
|
|
52
52
|
cachedBytes -= block.byteLength;
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
+
// Set by `close()`. A read that was already in flight when close was called still resolves, and
|
|
56
|
+
// its `.then` still runs — after `blocks.clear()` — so without this the cache repopulated itself
|
|
57
|
+
// AFTER being closed and then served that data on later reads, from a source whose own `close`
|
|
58
|
+
// had already run. `admit` is where that happens, so `admit` is where it is stopped.
|
|
59
|
+
let closed = false;
|
|
55
60
|
function admit(index, block) {
|
|
56
|
-
if (blocks.has(index))
|
|
61
|
+
if (closed || blocks.has(index))
|
|
57
62
|
return;
|
|
58
63
|
blocks.set(index, block);
|
|
59
64
|
cachedBytes += block.byteLength;
|
|
@@ -95,7 +100,11 @@ export function cachedSource(source, options) {
|
|
|
95
100
|
if (length === 0)
|
|
96
101
|
return new Uint8Array(0);
|
|
97
102
|
// A read wider than the entire budget cannot benefit from the cache and would evict every
|
|
98
|
-
// block on its way through, so it goes straight to the source.
|
|
103
|
+
// block on its way through, so it goes straight to the source. The array is the wrapped
|
|
104
|
+
// source's own, not a copy, and that is correct rather than an oversight: the "a cache hands
|
|
105
|
+
// back a copy" rule exists because a cache RETAINS its blocks, and nothing is retained here.
|
|
106
|
+
// This path is exactly as safe as calling the wrapped source directly, because that is what
|
|
107
|
+
// it does.
|
|
99
108
|
if (length > maxBytes) {
|
|
100
109
|
return assertExactRead(await source.read(offset, length, options), offset, length);
|
|
101
110
|
}
|
|
@@ -119,6 +128,9 @@ export function cachedSource(source, options) {
|
|
|
119
128
|
return assertExactRead(out, offset, length);
|
|
120
129
|
},
|
|
121
130
|
async close() {
|
|
131
|
+
// Order matters only in that `closed` must be set before anything awaits: an in-flight read
|
|
132
|
+
// can resolve during `source.close()` and would otherwise re-admit its block.
|
|
133
|
+
closed = true;
|
|
122
134
|
blocks.clear();
|
|
123
135
|
inflight.clear();
|
|
124
136
|
cachedBytes = 0;
|
package/dist/io/cached.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cached.js","sourceRoot":"","sources":["../../src/io/cached.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAEpG,MAAM,mBAAmB,GAAG,IAAI,GAAG,IAAI,CAAC;AACxC,MAAM,iBAAiB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAE3C,yFAAyF;AACzF,SAAS,WAAW,CAAC,MAAkB;IACrC,OAAO;QACL,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,KAAK,CAAC,IAAI,CAAC,MAAc,EAAE,MAAc,EAAE,OAAqB;YAC9D,OAAO,eAAe,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QACrF,CAAC;QACD,KAAK,CAAC,KAAK;YACT,MAAM,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC;QACzB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAkB,EAAE,OAAsB;IACrE,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;IACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CACvB,CAAC,EACD,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,iBAAiB,CAAC,CAAC,CAClF,CAAC;IACF,MAAM,mBAAmB,GAAG,IAAI,CAAC,GAAG,CAClC,CAAC,EACD,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,mBAAmB,CAAC,CAAC,CACxF,CAAC;IACF,0FAA0F;IAC1F,6FAA6F;IAC7F,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,mBAAmB,EAAE,QAAQ,CAAC,CAAC;IAC3D,IAAI,UAAU,GAAG,CAAC;QAAE,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC;IAE/C,wFAAwF;IACxF,MAAM,MAAM,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC7C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA+B,CAAC;IACxD,IAAI,WAAW,GAAG,CAAC,CAAC;IAEpB,SAAS,KAAK;QACZ,2FAA2F;QAC3F,0CAA0C;QAC1C,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;YACpC,IAAI,WAAW,IAAI,QAAQ;gBAAE,OAAO;YACpC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACrB,WAAW,IAAI,KAAK,CAAC,UAAU,CAAC;QAClC,CAAC;IACH,CAAC;IAED,SAAS,KAAK,CAAC,KAAa,EAAE,KAAiB;QAC7C,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,OAAO;
|
|
1
|
+
{"version":3,"file":"cached.js","sourceRoot":"","sources":["../../src/io/cached.ts"],"names":[],"mappings":"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"}
|
package/dist/io/source.d.ts
CHANGED
|
@@ -11,6 +11,14 @@
|
|
|
11
11
|
* cause for the wrong thing.
|
|
12
12
|
*/
|
|
13
13
|
import type { AbortSignalLike, ReadOptions } from '../types.js';
|
|
14
|
+
/**
|
|
15
|
+
* Whether a value is a real one-byte-per-element view: `Uint8Array`, `Uint8ClampedArray`, or
|
|
16
|
+
* Node's `Buffer` (a `Uint8Array` subclass, so it inherits the tag).
|
|
17
|
+
*
|
|
18
|
+
* Shared with `byteSource`, which has to reject the same set at construction time rather than
|
|
19
|
+
* building a source over something that is not bytes.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isByteArray(value: unknown): value is Uint8Array;
|
|
14
22
|
/**
|
|
15
23
|
* Enforces the exact-length contract and returns the value unchanged so it can wrap a read
|
|
16
24
|
* expression directly.
|
package/dist/io/source.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../src/io/source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../src/io/source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA0BhE;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAE/D;AAMD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CAYhG;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAuBxF;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI,CAE1D;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,IAAI,CAKnE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,MAAM,CAQR"}
|
package/dist/io/source.js
CHANGED
|
@@ -34,12 +34,18 @@ import { EdfSourceError } from '../errors.js';
|
|
|
34
34
|
* tag — and `Uint8ClampedArray`, and rejects `Int8Array`, every wider view, and `DataView`.
|
|
35
35
|
*/
|
|
36
36
|
const BYTE_ARRAY_TAGS = new Set(['[object Uint8Array]', '[object Uint8ClampedArray]']);
|
|
37
|
+
/**
|
|
38
|
+
* Whether a value is a real one-byte-per-element view: `Uint8Array`, `Uint8ClampedArray`, or
|
|
39
|
+
* Node's `Buffer` (a `Uint8Array` subclass, so it inherits the tag).
|
|
40
|
+
*
|
|
41
|
+
* Shared with `byteSource`, which has to reject the same set at construction time rather than
|
|
42
|
+
* building a source over something that is not bytes.
|
|
43
|
+
*/
|
|
44
|
+
export function isByteArray(value) {
|
|
45
|
+
return ArrayBuffer.isView(value) && BYTE_ARRAY_TAGS.has(Object.prototype.toString.call(value));
|
|
46
|
+
}
|
|
37
47
|
function receivedLengthOf(received) {
|
|
38
|
-
|
|
39
|
-
return undefined;
|
|
40
|
-
if (!BYTE_ARRAY_TAGS.has(Object.prototype.toString.call(received)))
|
|
41
|
-
return undefined;
|
|
42
|
-
return received.byteLength;
|
|
48
|
+
return isByteArray(received) ? received.byteLength : undefined;
|
|
43
49
|
}
|
|
44
50
|
/**
|
|
45
51
|
* Enforces the exact-length contract and returns the value unchanged so it can wrap a read
|
package/dist/io/source.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source.js","sourceRoot":"","sources":["../../src/io/source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG9C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,qBAAqB,EAAE,4BAA4B,CAAC,CAAC,CAAC;AAEvF,
|
|
1
|
+
{"version":3,"file":"source.js","sourceRoot":"","sources":["../../src/io/source.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/constants.ts
CHANGED
package/src/io/bytes.ts
CHANGED
|
@@ -7,13 +7,48 @@
|
|
|
7
7
|
* and this adapter retains nothing the caller does not already hold.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { EdfSourceError } from '../errors.js';
|
|
10
11
|
import type { ByteSource, ReadOptions } from '../types.js';
|
|
11
|
-
import { assertExactRead, assertReadRange, throwIfAborted } from './source.js';
|
|
12
|
+
import { assertExactRead, assertReadRange, isByteArray, throwIfAborted } from './source.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Describes what arrived, for an argument that is not bytes. Never prints the value: it could be
|
|
16
|
+
* anything, including something large.
|
|
17
|
+
*/
|
|
18
|
+
function describe(value: unknown): string {
|
|
19
|
+
if (value === null) return 'null';
|
|
20
|
+
if (value === undefined) return 'undefined';
|
|
21
|
+
if (ArrayBuffer.isView(value)) return Object.prototype.toString.call(value).slice(8, -1);
|
|
22
|
+
if (Array.isArray(value)) return 'a plain Array';
|
|
23
|
+
return typeof value === 'object' ? 'a plain object' : `a ${typeof value}`;
|
|
24
|
+
}
|
|
12
25
|
|
|
13
26
|
export function byteSource(bytes: ArrayBuffer | Uint8Array): ByteSource {
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
|
|
27
|
+
// Refused at CONSTRUCTION, because the alternative is worse than an error. `new Uint8Array(x)`
|
|
28
|
+
// accepts almost anything: a string, a plain object and `null` all yield an empty array, and a
|
|
29
|
+
// `number[]` yields one of the wrong length. The source was then built happily and the failure
|
|
30
|
+
// surfaced later as `[SOURCE_TOO_SMALL] the header is 0 bytes` — blaming the FILE for a mistake
|
|
31
|
+
// in the caller's argument, which is the one confusion this package works hardest to avoid.
|
|
32
|
+
//
|
|
33
|
+
// `Int8Array` is rejected too, and deliberately: it has one byte per element so it passes any
|
|
34
|
+
// length check, and its already-signed elements are sign-extended a second time during decode
|
|
35
|
+
// (see `assertExactRead`). Fabricated microvolts, with no error anywhere.
|
|
36
|
+
const isBuffer =
|
|
37
|
+
bytes instanceof ArrayBuffer ||
|
|
38
|
+
Object.prototype.toString.call(bytes) === '[object SharedArrayBuffer]';
|
|
39
|
+
if (!isBuffer && !isByteArray(bytes)) {
|
|
40
|
+
throw new EdfSourceError(
|
|
41
|
+
`byteSource() needs an ArrayBuffer or a Uint8Array, received ${describe(bytes)}. ` +
|
|
42
|
+
'Next: pass `new Uint8Array(await blob.arrayBuffer())`, `await readFile(path)`, or the ' +
|
|
43
|
+
'ArrayBuffer itself. An Int8Array is not accepted — it has one byte per element, so it ' +
|
|
44
|
+
'would pass every length check and then decode to fabricated sample values.',
|
|
45
|
+
{ offset: 0, requestedLength: 0 },
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// `isByteArray` rather than `instanceof Uint8Array`: `instanceof` is false for a view that
|
|
50
|
+
// crossed a realm boundary (a worker, an iframe), and this is a public entry point.
|
|
51
|
+
const view: Uint8Array = isByteArray(bytes) ? bytes : new Uint8Array(bytes as ArrayBuffer);
|
|
17
52
|
const byteLength = view.byteLength;
|
|
18
53
|
|
|
19
54
|
return {
|
package/src/io/cached.ts
CHANGED
|
@@ -64,8 +64,14 @@ export function cachedSource(source: ByteSource, options?: CacheOptions): ByteSo
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
// Set by `close()`. A read that was already in flight when close was called still resolves, and
|
|
68
|
+
// its `.then` still runs — after `blocks.clear()` — so without this the cache repopulated itself
|
|
69
|
+
// AFTER being closed and then served that data on later reads, from a source whose own `close`
|
|
70
|
+
// had already run. `admit` is where that happens, so `admit` is where it is stopped.
|
|
71
|
+
let closed = false;
|
|
72
|
+
|
|
67
73
|
function admit(index: number, block: Uint8Array): void {
|
|
68
|
-
if (blocks.has(index)) return;
|
|
74
|
+
if (closed || blocks.has(index)) return;
|
|
69
75
|
blocks.set(index, block);
|
|
70
76
|
cachedBytes += block.byteLength;
|
|
71
77
|
evict();
|
|
@@ -108,7 +114,11 @@ export function cachedSource(source: ByteSource, options?: CacheOptions): ByteSo
|
|
|
108
114
|
assertReadRange(offset, length, byteLength);
|
|
109
115
|
if (length === 0) return new Uint8Array(0);
|
|
110
116
|
// A read wider than the entire budget cannot benefit from the cache and would evict every
|
|
111
|
-
// block on its way through, so it goes straight to the source.
|
|
117
|
+
// block on its way through, so it goes straight to the source. The array is the wrapped
|
|
118
|
+
// source's own, not a copy, and that is correct rather than an oversight: the "a cache hands
|
|
119
|
+
// back a copy" rule exists because a cache RETAINS its blocks, and nothing is retained here.
|
|
120
|
+
// This path is exactly as safe as calling the wrapped source directly, because that is what
|
|
121
|
+
// it does.
|
|
112
122
|
if (length > maxBytes) {
|
|
113
123
|
return assertExactRead(await source.read(offset, length, options), offset, length);
|
|
114
124
|
}
|
|
@@ -133,6 +143,9 @@ export function cachedSource(source: ByteSource, options?: CacheOptions): ByteSo
|
|
|
133
143
|
return assertExactRead(out, offset, length);
|
|
134
144
|
},
|
|
135
145
|
async close(): Promise<void> {
|
|
146
|
+
// Order matters only in that `closed` must be set before anything awaits: an in-flight read
|
|
147
|
+
// can resolve during `source.close()` and would otherwise re-admit its block.
|
|
148
|
+
closed = true;
|
|
136
149
|
blocks.clear();
|
|
137
150
|
inflight.clear();
|
|
138
151
|
cachedBytes = 0;
|
package/src/io/source.ts
CHANGED
|
@@ -38,10 +38,19 @@ import type { AbortSignalLike, ReadOptions } from '../types.js';
|
|
|
38
38
|
*/
|
|
39
39
|
const BYTE_ARRAY_TAGS = new Set(['[object Uint8Array]', '[object Uint8ClampedArray]']);
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Whether a value is a real one-byte-per-element view: `Uint8Array`, `Uint8ClampedArray`, or
|
|
43
|
+
* Node's `Buffer` (a `Uint8Array` subclass, so it inherits the tag).
|
|
44
|
+
*
|
|
45
|
+
* Shared with `byteSource`, which has to reject the same set at construction time rather than
|
|
46
|
+
* building a source over something that is not bytes.
|
|
47
|
+
*/
|
|
48
|
+
export function isByteArray(value: unknown): value is Uint8Array {
|
|
49
|
+
return ArrayBuffer.isView(value) && BYTE_ARRAY_TAGS.has(Object.prototype.toString.call(value));
|
|
50
|
+
}
|
|
51
|
+
|
|
41
52
|
function receivedLengthOf(received: unknown): number | undefined {
|
|
42
|
-
|
|
43
|
-
if (!BYTE_ARRAY_TAGS.has(Object.prototype.toString.call(received))) return undefined;
|
|
44
|
-
return received.byteLength;
|
|
53
|
+
return isByteArray(received) ? received.byteLength : undefined;
|
|
45
54
|
}
|
|
46
55
|
|
|
47
56
|
/**
|