@fideus-labs/fizarrita 1.0.0

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.
Files changed (52) hide show
  1. package/LICENSE.txt +9 -0
  2. package/dist/codec-worker.d.ts +23 -0
  3. package/dist/codec-worker.d.ts.map +1 -0
  4. package/dist/codec-worker.js +149 -0
  5. package/dist/codec-worker.js.map +1 -0
  6. package/dist/get-worker.d.ts +40 -0
  7. package/dist/get-worker.d.ts.map +1 -0
  8. package/dist/get-worker.js +203 -0
  9. package/dist/get-worker.js.map +1 -0
  10. package/dist/index.d.ts +11 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +10 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/internals/codec-pipeline.d.ts +29 -0
  15. package/dist/internals/codec-pipeline.d.ts.map +1 -0
  16. package/dist/internals/codec-pipeline.js +135 -0
  17. package/dist/internals/codec-pipeline.js.map +1 -0
  18. package/dist/internals/indexer.d.ts +81 -0
  19. package/dist/internals/indexer.d.ts.map +1 -0
  20. package/dist/internals/indexer.js +249 -0
  21. package/dist/internals/indexer.js.map +1 -0
  22. package/dist/internals/setter.d.ts +27 -0
  23. package/dist/internals/setter.d.ts.map +1 -0
  24. package/dist/internals/setter.js +132 -0
  25. package/dist/internals/setter.js.map +1 -0
  26. package/dist/internals/util.d.ts +59 -0
  27. package/dist/internals/util.d.ts.map +1 -0
  28. package/dist/internals/util.js +137 -0
  29. package/dist/internals/util.js.map +1 -0
  30. package/dist/set-worker.d.ts +44 -0
  31. package/dist/set-worker.d.ts.map +1 -0
  32. package/dist/set-worker.js +234 -0
  33. package/dist/set-worker.js.map +1 -0
  34. package/dist/types.d.ts +129 -0
  35. package/dist/types.d.ts.map +1 -0
  36. package/dist/types.js +8 -0
  37. package/dist/types.js.map +1 -0
  38. package/dist/worker-rpc.d.ts +43 -0
  39. package/dist/worker-rpc.d.ts.map +1 -0
  40. package/dist/worker-rpc.js +219 -0
  41. package/dist/worker-rpc.js.map +1 -0
  42. package/package.json +53 -0
  43. package/src/codec-worker.ts +207 -0
  44. package/src/get-worker.ts +275 -0
  45. package/src/index.ts +15 -0
  46. package/src/internals/codec-pipeline.ts +200 -0
  47. package/src/internals/indexer.ts +346 -0
  48. package/src/internals/setter.ts +227 -0
  49. package/src/internals/util.ts +183 -0
  50. package/src/set-worker.ts +305 -0
  51. package/src/types.ts +158 -0
  52. package/src/worker-rpc.ts +347 -0
@@ -0,0 +1,346 @@
1
+ /**
2
+ * BasicIndexer — ported from zarrita.js/src/indexing/indexer.ts
3
+ *
4
+ * Handles multi-dimensional selection (slices, integer indices) and iterates
5
+ * over all chunk projections, yielding { chunk_coords, mapping } for each
6
+ * chunk that overlaps the selection.
7
+ *
8
+ * Self-contained — no deep imports from zarrita.
9
+ */
10
+
11
+ import type { Indices, Slice } from 'zarrita'
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // Helpers
15
+ // ---------------------------------------------------------------------------
16
+
17
+ export class IndexError extends Error {
18
+ constructor(msg: string) {
19
+ super(msg)
20
+ this.name = 'IndexError'
21
+ }
22
+ }
23
+
24
+ function err_too_many_indices(
25
+ selection: (number | Slice)[],
26
+ shape: readonly number[],
27
+ ): never {
28
+ throw new IndexError(
29
+ `too many indicies for array; expected ${shape.length}, got ${selection.length}`,
30
+ )
31
+ }
32
+
33
+ function err_boundscheck(dim_len: number): never {
34
+ throw new IndexError(
35
+ `index out of bounds for dimension with length ${dim_len}`,
36
+ )
37
+ }
38
+
39
+ function err_negative_step(): never {
40
+ throw new IndexError('only slices with step >= 1 are supported')
41
+ }
42
+
43
+ function check_selection_length(
44
+ selection: (number | Slice)[],
45
+ shape: readonly number[],
46
+ ): void {
47
+ if (selection.length > shape.length) {
48
+ err_too_many_indices(selection, shape)
49
+ }
50
+ }
51
+
52
+ // ---------------------------------------------------------------------------
53
+ // Slice utilities
54
+ // ---------------------------------------------------------------------------
55
+
56
+ export function slice(stop: number | null): Slice
57
+ export function slice(
58
+ start: number | null,
59
+ stop?: number | null,
60
+ step?: number | null,
61
+ ): Slice
62
+ export function slice(
63
+ start: number | null,
64
+ stop?: number | null,
65
+ step: number | null = null,
66
+ ): Slice {
67
+ if (stop === undefined) {
68
+ stop = start
69
+ start = null
70
+ }
71
+ return { start, stop, step }
72
+ }
73
+
74
+ /**
75
+ * Resolve a Slice to concrete [start, stop, step] given dimension length.
76
+ * Matches Python's slice.indices().
77
+ */
78
+ export function slice_indices(
79
+ { start, stop, step }: Slice,
80
+ length: number,
81
+ ): Indices {
82
+ if (step === 0) throw new Error('slice step cannot be zero')
83
+ step = step ?? 1
84
+ const step_is_negative = step < 0
85
+ const [lower, upper] = step_is_negative ? [-1, length - 1] : [0, length]
86
+
87
+ if (start === null) {
88
+ start = step_is_negative ? upper : lower
89
+ } else {
90
+ if (start < 0) {
91
+ start += length
92
+ if (start < lower) start = lower
93
+ } else if (start > upper) {
94
+ start = upper
95
+ }
96
+ }
97
+
98
+ if (stop === null) {
99
+ stop = step_is_negative ? lower : upper
100
+ } else {
101
+ if (stop < 0) {
102
+ stop += length
103
+ if (stop < lower) stop = lower
104
+ } else if (stop > upper) {
105
+ stop = upper
106
+ }
107
+ }
108
+
109
+ return [start, stop, step]
110
+ }
111
+
112
+ // ---------------------------------------------------------------------------
113
+ // Range / Product generators
114
+ // ---------------------------------------------------------------------------
115
+
116
+ function* range(start: number, stop?: number, step = 1): Iterable<number> {
117
+ if (stop === undefined) {
118
+ stop = start
119
+ start = 0
120
+ }
121
+ for (let i = start; i < stop; i += step) {
122
+ yield i
123
+ }
124
+ }
125
+
126
+ function* product<T extends Iterable<unknown>[]>(
127
+ ...iterables: T
128
+ ): IterableIterator<unknown[]> {
129
+ if (iterables.length === 0) return
130
+ const iterators = iterables.map((it) => it[Symbol.iterator]())
131
+ const results = iterators.map((it) => it.next())
132
+ if (results.some((r) => r.done)) {
133
+ throw new Error('Input contains an empty iterator.')
134
+ }
135
+ for (let i = 0; ; ) {
136
+ if (results[i].done) {
137
+ iterators[i] = iterables[i][Symbol.iterator]()
138
+ results[i] = iterators[i].next()
139
+ if (++i >= iterators.length) return
140
+ } else {
141
+ yield results.map(({ value }) => value)
142
+ i = 0
143
+ }
144
+ results[i] = iterators[i].next()
145
+ }
146
+ }
147
+
148
+ // ---------------------------------------------------------------------------
149
+ // normalize_integer_selection
150
+ // ---------------------------------------------------------------------------
151
+
152
+ export function normalize_integer_selection(
153
+ dim_sel: number,
154
+ dim_len: number,
155
+ ): number {
156
+ dim_sel = Math.trunc(dim_sel)
157
+ if (dim_sel < 0) dim_sel = dim_len + dim_sel
158
+ if (dim_sel >= dim_len || dim_sel < 0) err_boundscheck(dim_len)
159
+ return dim_sel
160
+ }
161
+
162
+ // ---------------------------------------------------------------------------
163
+ // IntDimIndexer
164
+ // ---------------------------------------------------------------------------
165
+
166
+ interface IntChunkDimProjection {
167
+ dim_chunk_ix: number
168
+ dim_chunk_sel: number
169
+ }
170
+
171
+ class IntDimIndexer {
172
+ dim_sel: number
173
+ dim_len: number
174
+ dim_chunk_len: number
175
+ nitems: 1 = 1
176
+
177
+ constructor(opts: { dim_sel: number; dim_len: number; dim_chunk_len: number }) {
178
+ this.dim_sel = normalize_integer_selection(opts.dim_sel, opts.dim_len)
179
+ this.dim_len = opts.dim_len
180
+ this.dim_chunk_len = opts.dim_chunk_len
181
+ }
182
+
183
+ *[Symbol.iterator](): IterableIterator<IntChunkDimProjection> {
184
+ const dim_chunk_ix = Math.floor(this.dim_sel / this.dim_chunk_len)
185
+ const dim_offset = dim_chunk_ix * this.dim_chunk_len
186
+ const dim_chunk_sel = this.dim_sel - dim_offset
187
+ yield { dim_chunk_ix, dim_chunk_sel }
188
+ }
189
+ }
190
+
191
+ // ---------------------------------------------------------------------------
192
+ // SliceDimIndexer
193
+ // ---------------------------------------------------------------------------
194
+
195
+ interface SliceChunkDimProjection {
196
+ dim_chunk_ix: number
197
+ dim_chunk_sel: Indices
198
+ dim_out_sel: Indices
199
+ }
200
+
201
+ class SliceDimIndexer {
202
+ start: number
203
+ stop: number
204
+ step: number
205
+ dim_len: number
206
+ dim_chunk_len: number
207
+ nitems: number
208
+ nchunks: number
209
+
210
+ constructor(opts: { dim_sel: Slice; dim_len: number; dim_chunk_len: number }) {
211
+ const [start, stop, step] = slice_indices(opts.dim_sel, opts.dim_len)
212
+ this.start = start
213
+ this.stop = stop
214
+ this.step = step
215
+ if (this.step < 1) err_negative_step()
216
+ this.dim_len = opts.dim_len
217
+ this.dim_chunk_len = opts.dim_chunk_len
218
+ this.nitems = Math.max(0, Math.ceil((this.stop - this.start) / this.step))
219
+ this.nchunks = Math.ceil(this.dim_len / this.dim_chunk_len)
220
+ }
221
+
222
+ *[Symbol.iterator](): IterableIterator<SliceChunkDimProjection> {
223
+ const dim_chunk_ix_from = Math.floor(this.start / this.dim_chunk_len)
224
+ const dim_chunk_ix_to = Math.ceil(this.stop / this.dim_chunk_len)
225
+ for (const dim_chunk_ix of range(dim_chunk_ix_from, dim_chunk_ix_to)) {
226
+ const dim_offset = dim_chunk_ix * this.dim_chunk_len
227
+ const dim_limit = Math.min(
228
+ this.dim_len,
229
+ (dim_chunk_ix + 1) * this.dim_chunk_len,
230
+ )
231
+ const dim_chunk_len = dim_limit - dim_offset
232
+
233
+ let dim_out_offset = 0
234
+ let dim_chunk_sel_start = 0
235
+ if (this.start < dim_offset) {
236
+ const remainder = (dim_offset - this.start) % this.step
237
+ if (remainder) dim_chunk_sel_start += this.step - remainder
238
+ dim_out_offset = Math.ceil((dim_offset - this.start) / this.step)
239
+ } else {
240
+ dim_chunk_sel_start = this.start - dim_offset
241
+ }
242
+ const dim_chunk_sel_stop =
243
+ this.stop > dim_limit ? dim_chunk_len : this.stop - dim_offset
244
+
245
+ const dim_chunk_sel: Indices = [
246
+ dim_chunk_sel_start,
247
+ dim_chunk_sel_stop,
248
+ this.step,
249
+ ]
250
+ const dim_chunk_nitems = Math.ceil(
251
+ (dim_chunk_sel_stop - dim_chunk_sel_start) / this.step,
252
+ )
253
+ const dim_out_sel: Indices = [
254
+ dim_out_offset,
255
+ dim_out_offset + dim_chunk_nitems,
256
+ 1,
257
+ ]
258
+ yield { dim_chunk_ix, dim_chunk_sel, dim_out_sel }
259
+ }
260
+ }
261
+ }
262
+
263
+ // ---------------------------------------------------------------------------
264
+ // normalize_selection
265
+ // ---------------------------------------------------------------------------
266
+
267
+ export function normalize_selection(
268
+ selection: null | (Slice | null | number)[],
269
+ shape: readonly number[],
270
+ ): (number | Slice)[] {
271
+ let normalized: (number | Slice)[]
272
+ if (selection === null || selection === undefined) {
273
+ normalized = shape.map(() => slice(null))
274
+ } else if (Array.isArray(selection)) {
275
+ normalized = selection.map((s) => s ?? slice(null))
276
+ } else {
277
+ normalized = shape.map(() => slice(null))
278
+ }
279
+ check_selection_length(normalized, shape)
280
+ return normalized
281
+ }
282
+
283
+ // ---------------------------------------------------------------------------
284
+ // Projection types
285
+ // ---------------------------------------------------------------------------
286
+
287
+ export type IndexerProjection =
288
+ | { from: number; to: null }
289
+ | { from: Indices; to: Indices }
290
+
291
+ export interface ChunkProjection {
292
+ chunk_coords: number[]
293
+ mapping: IndexerProjection[]
294
+ }
295
+
296
+ // ---------------------------------------------------------------------------
297
+ // BasicIndexer
298
+ // ---------------------------------------------------------------------------
299
+
300
+ export class BasicIndexer {
301
+ dim_indexers: (SliceDimIndexer | IntDimIndexer)[]
302
+ shape: number[]
303
+
304
+ constructor(opts: {
305
+ selection: null | (null | number | Slice)[]
306
+ shape: readonly number[]
307
+ chunk_shape: readonly number[]
308
+ }) {
309
+ this.dim_indexers = normalize_selection(opts.selection, opts.shape).map(
310
+ (dim_sel, i) => {
311
+ if (typeof dim_sel === 'number') {
312
+ return new IntDimIndexer({
313
+ dim_sel,
314
+ dim_len: opts.shape[i],
315
+ dim_chunk_len: opts.chunk_shape[i],
316
+ })
317
+ }
318
+ return new SliceDimIndexer({
319
+ dim_sel,
320
+ dim_len: opts.shape[i],
321
+ dim_chunk_len: opts.chunk_shape[i],
322
+ })
323
+ },
324
+ )
325
+ this.shape = this.dim_indexers
326
+ .filter((ixr): ixr is SliceDimIndexer => ixr instanceof SliceDimIndexer)
327
+ .map((sixr) => sixr.nitems)
328
+ }
329
+
330
+ *[Symbol.iterator](): IterableIterator<ChunkProjection> {
331
+ for (const dim_projections of product(...this.dim_indexers)) {
332
+ const chunk_coords = (
333
+ dim_projections as (IntChunkDimProjection | SliceChunkDimProjection)[]
334
+ ).map((p) => p.dim_chunk_ix)
335
+ const mapping: IndexerProjection[] = (
336
+ dim_projections as (IntChunkDimProjection | SliceChunkDimProjection)[]
337
+ ).map((p) => {
338
+ if ('dim_out_sel' in p) {
339
+ return { from: p.dim_chunk_sel, to: p.dim_out_sel }
340
+ }
341
+ return { from: p.dim_chunk_sel, to: null }
342
+ })
343
+ yield { chunk_coords, mapping }
344
+ }
345
+ }
346
+ }
@@ -0,0 +1,227 @@
1
+ /**
2
+ * Binary setter — ported from zarrita.js/src/indexing/ops.ts
3
+ *
4
+ * Provides prepare, set_scalar, and set_from_chunk operations on Chunk objects.
5
+ * Works directly on raw TypedArray/Uint8Array memory for performance.
6
+ *
7
+ * Self-contained — no deep imports from zarrita.
8
+ */
9
+
10
+ import type { Chunk, DataType, Indices, Projection, Scalar, TypedArray } from 'zarrita'
11
+
12
+ // ---------------------------------------------------------------------------
13
+ // Internal helpers
14
+ // ---------------------------------------------------------------------------
15
+
16
+ function indices_len(start: number, stop: number, step: number): number {
17
+ if (step < 0 && stop < start) {
18
+ return Math.floor((start - stop - 1) / -step) + 1
19
+ }
20
+ if (start < stop) return Math.floor((stop - start - 1) / step) + 1
21
+ return 0
22
+ }
23
+
24
+ export function compat_chunk<D extends DataType>(
25
+ arr: Chunk<D>,
26
+ ): {
27
+ data: Uint8Array
28
+ stride: number[]
29
+ bytes_per_element: number
30
+ } {
31
+ return {
32
+ data: new Uint8Array(
33
+ (arr.data as unknown as { buffer: ArrayBufferLike }).buffer,
34
+ (arr.data as unknown as { byteOffset: number }).byteOffset,
35
+ (arr.data as unknown as { byteLength: number }).byteLength,
36
+ ),
37
+ stride: arr.stride,
38
+ bytes_per_element: (arr.data as unknown as { BYTES_PER_ELEMENT: number }).BYTES_PER_ELEMENT,
39
+ }
40
+ }
41
+
42
+ function compat_scalar<D extends DataType>(
43
+ arr: Chunk<D>,
44
+ value: Scalar<D>,
45
+ ): Uint8Array {
46
+ const TypedArrayCtor = (arr.data as unknown as { constructor: new (arr: unknown[]) => { buffer: ArrayBuffer; byteOffset: number; byteLength: number } }).constructor
47
+ const data = new TypedArrayCtor([value])
48
+ return new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
49
+ }
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // set_scalar_binary
53
+ // ---------------------------------------------------------------------------
54
+
55
+ function set_scalar_binary(
56
+ out: { data: Uint8Array; stride: number[] },
57
+ out_selection: (Indices | number)[],
58
+ value: Uint8Array,
59
+ bytes_per_element: number,
60
+ ): void {
61
+ if (out_selection.length === 0) {
62
+ out.data.set(value, 0)
63
+ return
64
+ }
65
+ const [sel, ...rest] = out_selection
66
+ const [curr_stride, ...stride] = out.stride
67
+ if (typeof sel === 'number') {
68
+ const data = out.data.subarray(curr_stride * sel * bytes_per_element)
69
+ set_scalar_binary({ data, stride }, rest, value, bytes_per_element)
70
+ return
71
+ }
72
+ const [from, to, step] = sel
73
+ const len = indices_len(from, to, step)
74
+ if (rest.length === 0) {
75
+ for (let i = 0; i < len; i++) {
76
+ out.data.set(value, curr_stride * (from + step * i) * bytes_per_element)
77
+ }
78
+ return
79
+ }
80
+ for (let i = 0; i < len; i++) {
81
+ const data = out.data.subarray(
82
+ curr_stride * (from + step * i) * bytes_per_element,
83
+ )
84
+ set_scalar_binary({ data, stride }, rest, value, bytes_per_element)
85
+ }
86
+ }
87
+
88
+ // ---------------------------------------------------------------------------
89
+ // set_from_chunk_binary
90
+ // ---------------------------------------------------------------------------
91
+
92
+ export function set_from_chunk_binary(
93
+ dest: { data: Uint8Array; stride: number[] },
94
+ src: { data: Uint8Array; stride: number[] },
95
+ bytes_per_element: number,
96
+ projections: Projection[],
97
+ ): void {
98
+ const [proj, ...projs] = projections
99
+ const [dstride, ...dstrides] = dest.stride
100
+ const [sstride, ...sstrides] = src.stride
101
+
102
+ if (proj.from === null) {
103
+ if (projs.length === 0) {
104
+ dest.data.set(
105
+ src.data.subarray(0, bytes_per_element),
106
+ (proj.to as number) * bytes_per_element,
107
+ )
108
+ return
109
+ }
110
+ set_from_chunk_binary(
111
+ {
112
+ data: dest.data.subarray(
113
+ dstride * (proj.to as number) * bytes_per_element,
114
+ ),
115
+ stride: dstrides,
116
+ },
117
+ src,
118
+ bytes_per_element,
119
+ projs,
120
+ )
121
+ return
122
+ }
123
+ if (proj.to === null) {
124
+ if (projs.length === 0) {
125
+ const offset = (proj.from as number) * bytes_per_element
126
+ dest.data.set(src.data.subarray(offset, offset + bytes_per_element), 0)
127
+ return
128
+ }
129
+ set_from_chunk_binary(
130
+ dest,
131
+ {
132
+ data: src.data.subarray(
133
+ sstride * (proj.from as number) * bytes_per_element,
134
+ ),
135
+ stride: sstrides,
136
+ },
137
+ bytes_per_element,
138
+ projs,
139
+ )
140
+ return
141
+ }
142
+
143
+ const [from, to, step] = proj.to as Indices
144
+ const [sfrom, , sstep] = proj.from as Indices
145
+ const len = indices_len(from, to, step)
146
+
147
+ if (projs.length === 0) {
148
+ if (step === 1 && sstep === 1 && dstride === 1 && sstride === 1) {
149
+ const offset = sfrom * bytes_per_element
150
+ const size = len * bytes_per_element
151
+ dest.data.set(
152
+ src.data.subarray(offset, offset + size),
153
+ from * bytes_per_element,
154
+ )
155
+ return
156
+ }
157
+ for (let i = 0; i < len; i++) {
158
+ const offset = sstride * (sfrom + sstep * i) * bytes_per_element
159
+ dest.data.set(
160
+ src.data.subarray(offset, offset + bytes_per_element),
161
+ dstride * (from + step * i) * bytes_per_element,
162
+ )
163
+ }
164
+ return
165
+ }
166
+
167
+ for (let i = 0; i < len; i++) {
168
+ set_from_chunk_binary(
169
+ {
170
+ data: dest.data.subarray(
171
+ dstride * (from + i * step) * bytes_per_element,
172
+ ),
173
+ stride: dstrides,
174
+ },
175
+ {
176
+ data: src.data.subarray(
177
+ sstride * (sfrom + i * sstep) * bytes_per_element,
178
+ ),
179
+ stride: sstrides,
180
+ },
181
+ bytes_per_element,
182
+ projs,
183
+ )
184
+ }
185
+ }
186
+
187
+ // ---------------------------------------------------------------------------
188
+ // Exported setter object
189
+ // ---------------------------------------------------------------------------
190
+
191
+ export const setter = {
192
+ prepare<D extends DataType>(
193
+ data: TypedArray<D>,
194
+ shape: number[],
195
+ stride: number[],
196
+ ): Chunk<D> {
197
+ return { data, shape, stride }
198
+ },
199
+
200
+ set_scalar<D extends DataType>(
201
+ dest: Chunk<D>,
202
+ sel: (number | Indices)[],
203
+ value: Scalar<D>,
204
+ ): void {
205
+ const view = compat_chunk(dest)
206
+ set_scalar_binary(
207
+ view,
208
+ sel,
209
+ compat_scalar(dest, value),
210
+ view.bytes_per_element,
211
+ )
212
+ },
213
+
214
+ set_from_chunk<D extends DataType>(
215
+ dest: Chunk<D>,
216
+ src: Chunk<D>,
217
+ projections: Projection[],
218
+ ): void {
219
+ const view = compat_chunk(dest)
220
+ set_from_chunk_binary(
221
+ view,
222
+ compat_chunk(src),
223
+ view.bytes_per_element,
224
+ projections,
225
+ )
226
+ },
227
+ }