@tty-pt/islet 0.0.0-stage → 1.3.1

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.
@@ -0,0 +1,1187 @@
1
+ #ifndef ISLET_H
2
+ #define ISLET_H
3
+
4
+ /**
5
+ * @file islet.h
6
+ * @brief Public API for libislet — spatial indexing with Morton codes.
7
+ *
8
+ * Islet provides efficient spatial database operations using Morton codes
9
+ * (Z-order space-filling curves) for multi-dimensional coordinate indexing.
10
+ * Built on top of libcorm for persistence and hash table operations.
11
+ *
12
+ * Coordinates are signed 16-bit integers (int16_t) ranging from -32768 to 32767,
13
+ * suitable for game worlds, voxel engines, and spatial simulations.
14
+ *
15
+ * @note Depends on libcorm >= 0.8.0 (per-key multi-value chains,
16
+ * corm_get_multi, rec.h) and libstele.
17
+ */
18
+
19
+ #include <stdint.h>
20
+ #include <ttypt/corm.h>
21
+
22
+ /* Optimization tunable — a 0/1 flag. ISLET_SIMD_MORTON gates the batch
23
+ * encoders (morton_set_bulk / morton_set_bulk4); it defaults to 1.
24
+ * Override with -DISLET_SIMD_MORTON=0/1 (note: -U does NOT work — this
25
+ * block re-defines an undefined macro to its default). The former
26
+ * per-path tunables (INLINE/HOIST/CLZ/UNROLL) were promoted to
27
+ * unconditional after measurement showed the generic fallbacks never
28
+ * win; see docs/PERF.md. */
29
+ #ifndef ISLET_SIMD_MORTON
30
+ #define ISLET_SIMD_MORTON 1
31
+ #endif
32
+
33
+ #include "morton.h"
34
+
35
+ /** @defgroup islet_core Islet core API
36
+ * @brief Spatial map operations using Morton code indexing.
37
+ *
38
+ * Islet provides a spatial database that maps multi-dimensional coordinates
39
+ * to arbitrary uint32_t values. Internally, coordinates are converted to
40
+ * Morton codes (Z-order) for efficient spatial queries and storage.
41
+ *
42
+ * Value Semantics (multi-value cells):
43
+ * - A grid cell may hold MULTIPLE values (CM_MULTIVALUE map). islet_put_N()
44
+ * appends; islet_get_N() returns the first value; islet_get_multi_N()
45
+ * iterates all values in insertion order; islet_del_N() removes the
46
+ * first value; islet_del_all_N() removes every value; islet_set_N()
47
+ * replaces all values with a single new one (N = 1..4).
48
+ *
49
+ * Coordinate System:
50
+ * - Type: int16_t (signed 16-bit integers)
51
+ * - Range: -32768 to 32767 per dimension
52
+ * - Dimensions 1..4 supported; optimized for 3D (dim=3) with 4D
53
+ * (dim=4) support. 1D/2D/3D codes are bit-identical to v0.5.0;
54
+ * 4D codes densely fill all 64 key bits.
55
+ *
56
+ * The 2D x 32-bit config (int32_t lanes, islet_*_2_32 / morton_*_2_32)
57
+ * is a second dense family member: 2 x 32 = 64 key bits, no reserved
58
+ * bits, lane range -2147483648..2147483647.
59
+ *
60
+ * @note Keyspace: all dimensions and configs share the uint64 Morton
61
+ * key space. Use one dimension AND one config per database, and
62
+ * never reopen a database with a different config's functions:
63
+ * the file carries no config tag, so a cross-config reopen
64
+ * silently decodes garbage.
65
+ *
66
+ * @note islet_ops[] covers the int16-lane configs (1..4) only; the
67
+ * 2D x 32-bit config is reached through its suffixed monomorphs
68
+ * (islet_put_2_32(), islet_iter_2_32(), ...) directly.
69
+ *
70
+ * @note Preferred surface: the config objects in pointcfg.h
71
+ * (Point1_2..Point4_2, Point2_4) group everything above per
72
+ * config as static-method structs. This header's flat functions
73
+ * remain the ABI and the tight-loop fast path.
74
+ *
75
+ * @note Thread Safety: Islet inherits libcorm's thread-safety properties.
76
+ * It uses global state and is NOT thread-safe. Use external
77
+ * synchronization if accessing from multiple threads.
78
+ *
79
+ * @note Capacity: The mask parameter sizes the initial hash table
80
+ * (capacity = mask + 1). The map AUTO-GROWS by doubling when it
81
+ * fills (inherited from libcorm; libislet never passes CM_NOGROW),
82
+ * so exceeding the initial capacity is safe. Choose a mask near
83
+ * your expected entry count to avoid early regrows.
84
+ *
85
+ * @note Memory: Malloc failures terminate the process immediately via CBUG().
86
+ * There is no graceful error handling for out-of-memory conditions.
87
+ *
88
+ * @note ID-uniformity contract (see RECALL-KERNEL.md): the per-cell
89
+ * stored value ("thing"/"ref") is intentionally `uint32_t`, not
90
+ * the wider `rec_ref_t` — which is itself `uint32_t` (from
91
+ * rec.h), matching what libsepal/liblemma use natively and
92
+ * libjoint widens to at its fill boundary, so the fill widen is
93
+ * now an identity cast. Islet's storage/SIMD paths are
94
+ * tuned around the 4-byte value; doubling it is a real cost, not
95
+ * a free uniformity win. The uniformity boundary is enforced only
96
+ * at rec_axis_fill_bbox_N(), which already widens every stored
97
+ * uint32_t to rec_ref_t before pushing it into the caller's
98
+ * rec_set_t. Do not assume ids above UINT32_MAX round-trip through
99
+ * islet_put_N()/islet_get_N().
100
+ *
101
+ * @warning File Persistence: File-backed databases are automatically saved
102
+ * at process exit via libcorm. Explicit corm_save() calls are only
103
+ * needed for mid-execution checkpointing.
104
+ *
105
+ * @see islet_morton
106
+ * @see islet_point
107
+ * @{
108
+ */
109
+
110
+ /**
111
+ * Sentinel value returned by islet_get when no entry exists.
112
+ */
113
+ #define ISLET_MISS UINT32_MAX
114
+
115
+ /**
116
+ * Maximum bounding-box volume (in cells) accepted by the rec_axis_fill_bbox
117
+ * family (including rec_axis_fill_bbox_2_32).
118
+ * Boxes whose volume exceeds this are rejected with -1: they are almost
119
+ * certainly a caller bug, and the sealed set would be huge. Raw
120
+ * islet_iter_N() carries no such cap — it only allocates for entries found.
121
+ */
122
+ #define ISLET_FILL_MAX_VOL 1048576u
123
+
124
+ /**
125
+ * Maximum point count accepted by the phase-2A `rec_axis_store` value
126
+ * grammar (`"x,y[,z];x2,y2"`). A value listing more points is rejected
127
+ * with -1 (`errno` = `ERANGE`): almost certainly a caller bug, and the
128
+ * grid + `rev` writes would be large. The point count is the number of
129
+ * `;`-separated points, whatever their dimension.
130
+ */
131
+ #define ISLET_AXIS_MAX_POINTS 1024u
132
+
133
+ /**
134
+ * @brief Initialize the islet subsystem.
135
+ *
136
+ * Registers custom types with libcorm (uint64_t for Morton codes, uint32_t
137
+ * for values) and sets up the Morton code comparator for sorted iteration.
138
+ * Also initializes the internal IDM (ID Manager) for iterator handles.
139
+ *
140
+ * @warning Must be called before any other islet functions. Calling other
141
+ * functions without initialization results in undefined behavior.
142
+ *
143
+ * @note This function can be called multiple times safely (idempotent if
144
+ * corm types are already registered).
145
+ *
146
+ * Example:
147
+ * @code
148
+ * int main() {
149
+ * islet_init(); // Always call first
150
+ * uint32_t db = islet_open(NULL, NULL, 0xFF);
151
+ * // ... use database
152
+ * return 0;
153
+ * }
154
+ * @endcode
155
+ *
156
+ * @see islet_open
157
+ */
158
+ void islet_init(void);
159
+
160
+ /**
161
+ * @brief Open or create a spatial database.
162
+ *
163
+ * Creates a spatial map backed by libcorm with CM_SORTED for efficient
164
+ * range queries. Internally uses Morton codes (uint64_t) as keys and
165
+ * uint32_t for values. If a filename is provided, data is automatically
166
+ * loaded from disk if the file exists.
167
+ *
168
+ * @param[in] filename File path for persistence, or NULL for in-memory only.
169
+ * Example: "world.db"
170
+ * @param[in] database Logical database name within the file, or NULL to
171
+ * skip file association. Multiple databases can share
172
+ * one file. Example: "main", "players", "chunks"
173
+ * @param[in] mask Hash table mask (initial capacity = mask + 1).
174
+ * Must be 2^n - 1 for optimal performance. Examples:
175
+ * 0xFF (256 entries), 0xFFF (4096 entries),
176
+ * 0xFFFF (65536 entries). The map auto-grows past this;
177
+ * pick a mask near your expected entry count.
178
+ *
179
+ * @return Database handle for use with other islet functions.
180
+ *
181
+ * @note File Persistence: File-backed databases automatically load existing
182
+ * data when opened. Data is automatically saved at process exit via
183
+ * libcorm's destructor. Call corm_save() explicitly only if you need
184
+ * mid-execution checkpointing.
185
+ *
186
+ * @note Capacity: The mask sizes the initial table; the map auto-grows
187
+ * by doubling when it fills. Nothing terminates on growth.
188
+ *
189
+ * @note The database is created with CM_SORTED | CM_MULTIVALUE: ordered
190
+ * iteration by Morton code for efficient spatial range queries,
191
+ * with multi-value cells (see the Value Semantics note above).
192
+ *
193
+ * @warning The filename and database parameters are not copied. If providing
194
+ * string literals, they must remain valid for the database lifetime.
195
+ *
196
+ * Example (in-memory):
197
+ * @code
198
+ * islet_init();
199
+ * uint32_t db = islet_open(NULL, NULL, 0xFF); // 256-entry initial table
200
+ * @endcode
201
+ *
202
+ * Example (persistent):
203
+ * @code
204
+ * islet_init();
205
+ * uint32_t db = islet_open("world.db", "main", 0xFFF); // 4096-entry initial table
206
+ * @endcode
207
+ *
208
+ * @see islet_init
209
+ * @see corm_open
210
+ * @see corm_save
211
+ * @see corm_close
212
+ */
213
+ uint32_t islet_open(char *filename, char *database, uint32_t mask);
214
+
215
+ /**
216
+ * @brief Create an iterator for all points within a rectangular bounding box.
217
+ *
218
+ * Queries the spatial database for all entries within the axis-aligned
219
+ * bounding box defined by start point 's' and lengths 'l'. The iterator
220
+ * pre-allocates and collects all matching results using Morton code range
221
+ * queries internally.
222
+ *
223
+ * @param[in] pdb_hd Database handle from islet_open().
224
+ * @param[in] s Start point (minimum corner of bounding box).
225
+ * Array of int16_t with at least the dimension count
226
+ * of the function used.
227
+ * @param[in] l Lengths of bounding box per dimension (unsigned).
228
+ * Array of uint16_t with at least the dimension count
229
+ * of the function used.
230
+ * The box covers s[i]..s[i]+l[i] inclusive per dimension.
231
+ *
232
+ * @note Per-dimension variants islet_iter_1..4; the function name carries
233
+ * the dimension count (1..4).
234
+ *
235
+ * @return Iterator handle for use with islet_next(). The handle is
236
+ * automatically freed when islet_next() returns 0.
237
+ *
238
+ * @note Results are collected in Morton code order (Z-order space-filling
239
+ * curve), NOT spatial order. Nearby points may not be adjacent in
240
+ * iteration sequence.
241
+ *
242
+ * @note Every stored (point, value) pair is returned exactly once, including
243
+ * multiple values stored at the same cell. Results are NOT deduplicated.
244
+ *
245
+ * @note Memory: Allocates a flat list that grows with the number of matching
246
+ * entries (not the bounding box volume), so sparse queries over large
247
+ * boxes stay cheap.
248
+ *
249
+ * @warning The volume calculation can overflow for very large boxes.
250
+ * Reasonable box sizes are recommended (< 1 million points).
251
+ *
252
+ * Example (2D region):
253
+ * @code
254
+ * int16_t start[2] = {0, 0};
255
+ * uint16_t lengths[2] = {100, 100}; // 100x100 region
256
+ * uint32_t iter = islet_iter_2(db, start, lengths);
257
+ * @endcode
258
+ *
259
+ * Example (3D region):
260
+ * @code
261
+ * int16_t start[3] = {-10, -10, -10};
262
+ * uint16_t lengths[3] = {20, 20, 20}; // 20x20x20 cube (8000 points)
263
+ * uint32_t iter = islet_iter_3(db, start, lengths);
264
+ * @endcode
265
+ *
266
+ * @see islet_next
267
+ * @see morton_set_1 morton_set_2 morton_set_3 morton_set_4
268
+ */
269
+ uint32_t islet_iter_1(uint32_t pdb_hd, int16_t *s, uint16_t *l);
270
+ uint32_t islet_iter_2(uint32_t pdb_hd, int16_t *s, uint16_t *l);
271
+ uint32_t islet_iter_3(uint32_t pdb_hd, int16_t *s, uint16_t *l);
272
+ uint32_t islet_iter_4(uint32_t pdb_hd, int16_t *s, uint16_t *l);
273
+
274
+ /**
275
+ * @brief 2D x 32-bit box iterator. Same contract as islet_iter_N(),
276
+ * on int32_t lanes (start AND lengths). Use with islet_next32().
277
+ */
278
+ uint32_t islet_iter_2_32(uint32_t pdb_hd, int32_t *s, int32_t *l);
279
+
280
+ /**
281
+ * @brief Advance iterator and retrieve the next point/value pair.
282
+ *
283
+ * Retrieves the next entry from the iterator created by islet_iter_N().
284
+ * Skips empty cells in the bounding box. When all entries have been
285
+ * returned (or the box was empty), returns 0 and automatically frees
286
+ * the iterator's internal memory.
287
+ *
288
+ * @param[out] p Output point array. Must have space for the dimension
289
+ * count of the matching islet_iter_N() call. Filled with
290
+ * the coordinates of the next point.
291
+ * @param[out] ref Output pointer for the stored value (uint32_t).
292
+ * Filled with the value stored at this point.
293
+ * @param[in] cur Iterator handle from islet_iter_N().
294
+ *
295
+ * @return 1 if a point/value pair was retrieved (output written to p and ref).
296
+ * 0 when iteration is complete (no more entries). The iterator is
297
+ * automatically freed on return 0.
298
+ *
299
+ * @note The iterator is stateful and maintains position. Each call advances
300
+ * to the next entry.
301
+ *
302
+ * @note Empty cells in the bounding box are automatically skipped.
303
+ * Only coordinates with stored values are returned.
304
+ *
305
+ * @warning After this function returns 0, the iterator handle becomes invalid.
306
+ * Do not call islet_next() again with the same handle.
307
+ *
308
+ * @warning The iterator's internal memory is freed when this returns 0.
309
+ * There is no separate "close" or "free" function.
310
+ *
311
+ * Typical usage pattern:
312
+ * @code
313
+ * int16_t start[3] = {0, 0, 0};
314
+ * uint16_t lengths[3] = {10, 10, 10};
315
+ * uint32_t iter = islet_iter(db, start, lengths, 3);
316
+ *
317
+ * int16_t point[3];
318
+ * uint32_t value;
319
+ * while (islet_next(point, &value, iter)) {
320
+ * printf("Point (%d,%d,%d) = %u\n",
321
+ * point[0], point[1], point[2], value);
322
+ * }
323
+ * // Iterator is automatically freed after loop
324
+ * @endcode
325
+ *
326
+ * @see islet_iter
327
+ */
328
+ int islet_next(int16_t *p, uint32_t *ref, uint32_t cur);
329
+
330
+ /**
331
+ * @brief Advance a 2D x 32-bit iterator (islet_iter_2_32 handle).
332
+ *
333
+ * Same contract as islet_next(), on int32_t lanes. Iterators from
334
+ * islet_iter_1..4 use islet_next(); 2D x 32-bit iterators use this.
335
+ *
336
+ * @param[out] p Output point array (int32_t[2]).
337
+ * @param[out] ref Output pointer for the stored value (uint32_t).
338
+ * @param[in] cur Iterator handle from islet_iter_2_32().
339
+ *
340
+ * @return 1 on entry, 0 when complete (handle freed).
341
+ */
342
+ int islet_next32(int32_t *p, uint32_t *ref, uint32_t cur);
343
+
344
+ /**
345
+ * @brief Delete the first value stored at a spatial coordinate.
346
+ *
347
+ * Per-dimension variants (islet_del_1..4); the function name carries the
348
+ * dimension count. Removes the first value stored at the given point
349
+ * from the database. Internally converts the coordinate to a Morton
350
+ * code and calls corm_del(). If no entry exists at the coordinate, this
351
+ * is a no-op (safe to call). When several values share the cell, only
352
+ * the earliest-inserted one is removed; use islet_del_all_3() to clear
353
+ * the cell.
354
+ *
355
+ * @param[in] pdb_hd Database handle from islet_open().
356
+ * @param[in] p Point coordinate. Array of int16_t with at least the
357
+ * dimension count of the function used.
358
+ *
359
+ * @note This operation invalidates any pointers obtained from islet_get_3()
360
+ * or islet_next() that refer to this coordinate.
361
+ *
362
+ * @note Safe to call on non-existent coordinates (no error, no effect).
363
+ *
364
+ * Example:
365
+ * @code
366
+ * int16_t pos[3] = {10, 20, 30};
367
+ * islet_del_3(db, pos); // Remove entry at (10,20,30)
368
+ * @endcode
369
+ *
370
+ * @see islet_get_3
371
+ * @see islet_put_3
372
+ * @see corm_del
373
+ */
374
+ static inline void
375
+ islet_del_1(uint32_t pdb_hd, int16_t *p)
376
+ {
377
+ uint64_t at = morton_set_1(p);
378
+ corm_del(pdb_hd, &at);
379
+ }
380
+
381
+ /**
382
+ * @brief 2D delete. See islet_del_1() for the family docs.
383
+ */
384
+ static inline void
385
+ islet_del_2(uint32_t pdb_hd, int16_t *p)
386
+ {
387
+ uint64_t at = morton_set_2(p);
388
+ corm_del(pdb_hd, &at);
389
+ }
390
+
391
+ /**
392
+ * @brief 3D delete. See islet_del_1() for the family docs.
393
+ */
394
+ static inline void
395
+ islet_del_3(uint32_t pdb_hd, int16_t *p)
396
+ {
397
+ uint64_t at = morton_set_3(p);
398
+ corm_del(pdb_hd, &at);
399
+ }
400
+
401
+ /**
402
+ * @brief 4D delete. See islet_del_1() for the family docs.
403
+ */
404
+ static inline void
405
+ islet_del_4(uint32_t pdb_hd, int16_t *p)
406
+ {
407
+ uint64_t at = morton_set_4(p);
408
+ corm_del(pdb_hd, &at);
409
+ }
410
+
411
+ /**
412
+ * @brief 2D x 32-bit delete. See islet_del_1() for the family docs.
413
+ */
414
+ static inline void
415
+ islet_del_2_32(uint32_t pdb_hd, int32_t *p)
416
+ {
417
+ uint64_t at = morton_set_2_32(p);
418
+ corm_del(pdb_hd, &at);
419
+ }
420
+
421
+ /**
422
+ * @brief Delete every value stored at a spatial coordinate.
423
+ *
424
+ * Per-dimension variants (islet_del_all_1..4). Removes all values stored
425
+ * at the given point from the database. Internally converts the
426
+ * coordinate to a Morton code and calls corm_del_all(). If no entry
427
+ * exists at the coordinate, this is a no-op (safe to call, returns 0).
428
+ *
429
+ * @param[in] pdb_hd Database handle from islet_open().
430
+ * @param[in] p Point coordinate. Array of int16_t with at least the
431
+ * dimension count of the function used.
432
+ *
433
+ * @return Number of values removed (0 if the cell was empty).
434
+ *
435
+ * Example:
436
+ * @code
437
+ * int16_t pos[3] = {10, 20, 30};
438
+ * uint32_t n = islet_del_all_3(db, pos); // Clear the cell
439
+ * @endcode
440
+ *
441
+ * @see islet_del_3
442
+ * @see islet_set_3
443
+ * @see corm_del_all
444
+ */
445
+ static inline uint32_t
446
+ islet_del_all_1(uint32_t pdb_hd, int16_t *p)
447
+ {
448
+ uint64_t at = morton_set_1(p);
449
+ uint32_t n = corm_count(pdb_hd, &at);
450
+
451
+ if (n)
452
+ corm_del_all(pdb_hd, &at);
453
+
454
+ return n;
455
+ }
456
+
457
+ /**
458
+ * @brief 2D delete-all. See islet_del_all_1() for the family docs.
459
+ */
460
+ static inline uint32_t
461
+ islet_del_all_2(uint32_t pdb_hd, int16_t *p)
462
+ {
463
+ uint64_t at = morton_set_2(p);
464
+ uint32_t n = corm_count(pdb_hd, &at);
465
+
466
+ if (n)
467
+ corm_del_all(pdb_hd, &at);
468
+
469
+ return n;
470
+ }
471
+
472
+ /**
473
+ * @brief 3D delete-all. See islet_del_all_1() for the family docs.
474
+ */
475
+ static inline uint32_t
476
+ islet_del_all_3(uint32_t pdb_hd, int16_t *p)
477
+ {
478
+ uint64_t at = morton_set_3(p);
479
+ uint32_t n = corm_count(pdb_hd, &at);
480
+
481
+ if (n)
482
+ corm_del_all(pdb_hd, &at);
483
+
484
+ return n;
485
+ }
486
+
487
+ /**
488
+ * @brief 4D delete-all. See islet_del_all_1() for the family docs.
489
+ */
490
+ static inline uint32_t
491
+ islet_del_all_4(uint32_t pdb_hd, int16_t *p)
492
+ {
493
+ uint64_t at = morton_set_4(p);
494
+ uint32_t n = corm_count(pdb_hd, &at);
495
+
496
+ if (n)
497
+ corm_del_all(pdb_hd, &at);
498
+
499
+ return n;
500
+ }
501
+
502
+ /**
503
+ * @brief 2D x 32-bit delete-all. See islet_del_all_1() for the
504
+ * family docs.
505
+ */
506
+ static inline uint32_t
507
+ islet_del_all_2_32(uint32_t pdb_hd, int32_t *p)
508
+ {
509
+ uint64_t at = morton_set_2_32(p);
510
+ uint32_t n = corm_count(pdb_hd, &at);
511
+
512
+ if (n)
513
+ corm_del_all(pdb_hd, &at);
514
+
515
+ return n;
516
+ }
517
+
518
+ /**
519
+ * @brief 2D x 32-bit delete-by-value. See islet_del_value_1() for the
520
+ * family docs.
521
+ */
522
+ uint32_t islet_del_value_2_32(uint32_t pdb_hd, int32_t *p, uint32_t thing);
523
+
524
+ /**
525
+ * @brief Retrieve the value stored at a spatial coordinate.
526
+ *
527
+ * Per-dimension variants (islet_get_1..4). Looks up the value at the given
528
+ * point in the database. Internally converts the coordinate to a Morton
529
+ * code and calls corm_get().
530
+ *
531
+ * @param[in] pdb_hd Database handle from islet_open().
532
+ * @param[in] p Point coordinate. Array of int16_t with at least the
533
+ * dimension count of the function used. Coordinates are
534
+ * signed 16-bit integers ranging from -32768 to 32767.
535
+ *
536
+ * @return The first stored uint32_t value at this coordinate, or ISLET_MISS
537
+ * (UINT32_MAX) if no entry exists at this point. When several
538
+ * values share the cell, the earliest-inserted one is returned;
539
+ * use islet_get_multi_3() to retrieve them all.
540
+ *
541
+ * @note ISLET_MISS equals UINT32_MAX (0xFFFFFFFF), the same as CM_MISS.
542
+ * This is the standard sentinel value for missing entries.
543
+ *
544
+ * @note The returned value is a copy, not a pointer. Unlike corm_get()
545
+ * which returns a pointer, islet_get_3() returns the actual uint32_t
546
+ * value.
547
+ *
548
+ * Example:
549
+ * @code
550
+ * int16_t pos[3] = {10, 20, 30};
551
+ * uint32_t value = islet_get_3(db, pos);
552
+ * if (value == ISLET_MISS) {
553
+ * printf("No entry at (10,20,30)\n");
554
+ * } else {
555
+ * printf("Value at (10,20,30) = %u\n", value);
556
+ * }
557
+ * @endcode
558
+ *
559
+ * @see islet_put_3
560
+ * @see islet_del_3
561
+ * @see ISLET_MISS
562
+ * @see corm_get
563
+ */
564
+ static inline uint32_t
565
+ islet_get_1(uint32_t pdb_hd, int16_t *p)
566
+ {
567
+ uint64_t at = morton_set_1(p);
568
+ const void *ref = corm_get(pdb_hd, &at);
569
+
570
+ if (ref)
571
+ return * (uint32_t *) ref;
572
+
573
+ return ISLET_MISS;
574
+ }
575
+
576
+ /**
577
+ * @brief 2D retrieve. See islet_get_1() for the family docs.
578
+ */
579
+ static inline uint32_t
580
+ islet_get_2(uint32_t pdb_hd, int16_t *p)
581
+ {
582
+ uint64_t at = morton_set_2(p);
583
+ const void *ref = corm_get(pdb_hd, &at);
584
+
585
+ if (ref)
586
+ return * (uint32_t *) ref;
587
+
588
+ return ISLET_MISS;
589
+ }
590
+
591
+ /**
592
+ * @brief 3D retrieve. See islet_get_1() for the family docs.
593
+ */
594
+ static inline uint32_t
595
+ islet_get_3(uint32_t pdb_hd, int16_t *p)
596
+ {
597
+ uint64_t at = morton_set_3(p);
598
+ const void *ref = corm_get(pdb_hd, &at);
599
+
600
+ if (ref)
601
+ return * (uint32_t *) ref;
602
+
603
+ return ISLET_MISS;
604
+ }
605
+
606
+ /**
607
+ * @brief 4D retrieve. See islet_get_1() for the family docs.
608
+ */
609
+ static inline uint32_t
610
+ islet_get_4(uint32_t pdb_hd, int16_t *p)
611
+ {
612
+ uint64_t at = morton_set_4(p);
613
+ const void *ref = corm_get(pdb_hd, &at);
614
+
615
+ if (ref)
616
+ return * (uint32_t *) ref;
617
+
618
+ return ISLET_MISS;
619
+ }
620
+
621
+ /**
622
+ * @brief 2D x 32-bit retrieve. See islet_get_1() for the family docs.
623
+ */
624
+ static inline uint32_t
625
+ islet_get_2_32(uint32_t pdb_hd, int32_t *p)
626
+ {
627
+ uint64_t at = morton_set_2_32(p);
628
+ const void *ref = corm_get(pdb_hd, &at);
629
+
630
+ if (ref)
631
+ return * (uint32_t *) ref;
632
+
633
+ return ISLET_MISS;
634
+ }
635
+
636
+ /**
637
+ * @brief Count the values stored at a spatial coordinate.
638
+ *
639
+ * @param[in] pdb_hd Database handle from islet_open().
640
+ * @param[in] p Point coordinate. Array of int16_t with at least the
641
+ * dimension count of the function used.
642
+ *
643
+ * @return Number of values stored at this coordinate (0 if empty).
644
+ *
645
+ * @see islet_get_1 islet_get_2 islet_get_3 islet_get_4
646
+ * @see islet_get_multi_1 islet_get_multi_2 islet_get_multi_3 islet_get_multi_4
647
+ * @see corm_count
648
+ */
649
+ static inline uint32_t
650
+ islet_cell_count_1(uint32_t pdb_hd, int16_t *p)
651
+ {
652
+ uint64_t at = morton_set_1(p);
653
+
654
+ return corm_count(pdb_hd, &at);
655
+ }
656
+
657
+ /**
658
+ * @brief 2D cell count. See islet_cell_count_1() for the family docs.
659
+ */
660
+ static inline uint32_t
661
+ islet_cell_count_2(uint32_t pdb_hd, int16_t *p)
662
+ {
663
+ uint64_t at = morton_set_2(p);
664
+
665
+ return corm_count(pdb_hd, &at);
666
+ }
667
+
668
+ /**
669
+ * @brief 3D cell count. See islet_cell_count_1() for the family docs.
670
+ */
671
+ static inline uint32_t
672
+ islet_cell_count_3(uint32_t pdb_hd, int16_t *p)
673
+ {
674
+ uint64_t at = morton_set_3(p);
675
+
676
+ return corm_count(pdb_hd, &at);
677
+ }
678
+
679
+ /**
680
+ * @brief 4D cell count. See islet_cell_count_1() for the family docs.
681
+ */
682
+ static inline uint32_t
683
+ islet_cell_count_4(uint32_t pdb_hd, int16_t *p)
684
+ {
685
+ uint64_t at = morton_set_4(p);
686
+
687
+ return corm_count(pdb_hd, &at);
688
+ }
689
+
690
+ /**
691
+ * @brief 2D x 32-bit cell count. See islet_cell_count_1() for the
692
+ * family docs.
693
+ */
694
+ static inline uint32_t
695
+ islet_cell_count_2_32(uint32_t pdb_hd, int32_t *p)
696
+ {
697
+ uint64_t at = morton_set_2_32(p);
698
+
699
+ return corm_count(pdb_hd, &at);
700
+ }
701
+
702
+ /**
703
+ * @brief Diagnostic: index entries examined by the last box walk.
704
+ *
705
+ * Returns the number of index entries examined by the most recent
706
+ * islet_iter_N()/rec_axis_fill_bbox_N() box walk in this process. Tests and
707
+ * benchmarks use it to prove the Z-interval skip engages (entries
708
+ * examined well below the morton-interval width on sparse boxes).
709
+ *
710
+ * @return Entry-examination count of the last box walk (0 if none ran yet).
711
+ */
712
+ uint32_t islet_last_scan_count(void);
713
+
714
+ /**
715
+ * @brief Create an iterator for all values stored at one coordinate.
716
+ *
717
+ * Opens a chain-aware cursor over every value stored at the given point,
718
+ * in insertion order. Use islet_cell_next() to retrieve the values.
719
+ *
720
+ * @param[in] pdb_hd Database handle from islet_open().
721
+ * @param[in] p Point coordinate. Array of int16_t with at least the
722
+ * dimension count of the function used.
723
+ *
724
+ * @note Per-dimension variants islet_get_multi_1..4.
725
+ *
726
+ * @return Iterator handle for use with islet_cell_next(), or CM_MISS when
727
+ * the cell holds no values.
728
+ *
729
+ * Example:
730
+ * @code
731
+ * int16_t pos[3] = {10, 20, 30};
732
+ * uint32_t cur = islet_get_multi_3(db, pos);
733
+ * if (cur != CM_MISS) {
734
+ * uint32_t value;
735
+ * while (islet_cell_next(&value, cur))
736
+ * printf("value %u\n", value);
737
+ * }
738
+ * @endcode
739
+ *
740
+ * @see islet_cell_next
741
+ * @see islet_cell_count_1 islet_cell_count_2 islet_cell_count_3 islet_cell_count_4
742
+ */
743
+ uint32_t islet_get_multi_1(uint32_t pdb_hd, int16_t *p);
744
+ uint32_t islet_get_multi_2(uint32_t pdb_hd, int16_t *p);
745
+ uint32_t islet_get_multi_3(uint32_t pdb_hd, int16_t *p);
746
+ uint32_t islet_get_multi_4(uint32_t pdb_hd, int16_t *p);
747
+
748
+ /**
749
+ * @brief 2D x 32-bit cell iterator. Same contract as islet_get_multi_N()
750
+ * (use the shared islet_cell_next() to read values).
751
+ */
752
+ uint32_t islet_get_multi_2_32(uint32_t pdb_hd, int32_t *p);
753
+
754
+ /**
755
+ * @brief Advance a cell iterator and retrieve the next value.
756
+ *
757
+ * Retrieves the next value from the iterator created by
758
+ * islet_get_multi_N().
759
+ * When all values have been returned, returns 0 and automatically frees
760
+ * the iterator.
761
+ *
762
+ * @param[out] ref Output pointer for the stored value (uint32_t).
763
+ * @param[in] cur Iterator handle from islet_get_multi_N().
764
+ *
765
+ * @return 1 if a value was retrieved. 0 when iteration is complete; the
766
+ * iterator is automatically freed on return 0.
767
+ *
768
+ * @warning After this function returns 0, the iterator handle becomes invalid.
769
+ *
770
+ * @see islet_get_multi_1 islet_get_multi_2 islet_get_multi_3 islet_get_multi_4
771
+ */
772
+ int islet_cell_next(uint32_t *ref, uint32_t cur);
773
+
774
+ /**
775
+ * @brief Delete one value from a spatial coordinate (phase-2A adapter
776
+ * primitive).
777
+ *
778
+ * Per-dimension variants (islet_del_value_1..4). Removes every entry
779
+ * holding `thing` from the cell at the given point, leaving the cell's
780
+ * other values untouched (in their relative order). Where islet_del_N()
781
+ * removes the first value and islet_del_all_N() clears the cell, this one
782
+ * removes by value — exactly what `rec_axis_unstore` needs when several
783
+ * refs share one cell.
784
+ *
785
+ * @param[in] pdb_hd Database handle from islet_open().
786
+ * @param[in] p Point coordinate. Array of int16_t with at least the
787
+ * dimension count of the function used.
788
+ * @param[in] thing Value to remove (uint32_t).
789
+ *
790
+ * @return Number of entries removed (0 when the cell is empty or holds
791
+ * no such value).
792
+ *
793
+ * @note A pure cell primitive: it knows nothing of the phase-2A `rev`
794
+ * manifest. The `rec_axis_unstore` adapter (the rev-aware path) keeps
795
+ * cell and manifest in sync; callers manipulating the grid directly
796
+ * own that sync themselves.
797
+ *
798
+ * Example:
799
+ * @code
800
+ * int16_t pos[3] = {10, 20, 30};
801
+ * islet_put_3(db, pos, 7);
802
+ * islet_put_3(db, pos, 9);
803
+ * islet_del_value_3(db, pos, 9); // Cell now holds exactly {7}
804
+ * @endcode
805
+ *
806
+ * @see islet_del_1 islet_del_all_1 islet_cell_count_1
807
+ */
808
+ uint32_t islet_del_value_1(uint32_t pdb_hd, int16_t *p, uint32_t thing);
809
+ uint32_t islet_del_value_2(uint32_t pdb_hd, int16_t *p, uint32_t thing);
810
+ uint32_t islet_del_value_3(uint32_t pdb_hd, int16_t *p, uint32_t thing);
811
+ uint32_t islet_del_value_4(uint32_t pdb_hd, int16_t *p, uint32_t thing);
812
+
813
+ /**
814
+ * @brief Store a value at a spatial coordinate (append).
815
+ *
816
+ * Per-dimension variants (islet_put_1..4). Appends the value at the given
817
+ * point in the database. Internally converts the coordinate to a Morton
818
+ * code and calls corm_put(). If entries already exist at this coordinate,
819
+ * the new value is ADDED alongside them (multi-value cell) - nothing is
820
+ * replaced. Use islet_set_N() for replace semantics, islet_get_multi_N()
821
+ * to read all values back.
822
+ *
823
+ * @param[in] pdb_hd Database handle from islet_open().
824
+ * @param[in] p Point coordinate. Array of int16_t with at least the
825
+ * dimension count of the function used. Coordinates are
826
+ * signed 16-bit integers ranging from -32768 to 32767.
827
+ * @param[in] thing Value to store (uint32_t). Can be any 32-bit value
828
+ * including 0. Avoid using CM_MISS (0xFFFFFFFF) as it
829
+ * may cause confusion, though it's technically valid.
830
+ *
831
+ * @note If the database is file-backed (filename provided to islet_open()),
832
+ * changes are automatically saved at process exit. Call corm_save()
833
+ * explicitly for mid-execution persistence.
834
+ *
835
+ * Example (store single value):
836
+ * @code
837
+ * int16_t pos[3] = {10, 20, 30};
838
+ * islet_put_3(db, pos, 42); // Store value 42 at (10,20,30)
839
+ * @endcode
840
+ *
841
+ * Example (update existing value):
842
+ * @code
843
+ * int16_t pos[3] = {10, 20, 30};
844
+ * uint32_t old = islet_get_3(db, pos);
845
+ * if (old != ISLET_MISS) {
846
+ * islet_set_3(db, pos, old + 1); // Increment (replace)
847
+ * }
848
+ * @endcode
849
+ *
850
+ * Example (populate a 2D grid):
851
+ * @code
852
+ * for (int16_t x = 0; x < 10; x++) {
853
+ * for (int16_t y = 0; y < 10; y++) {
854
+ * int16_t pos[2] = {x, y};
855
+ * islet_put_2(db, pos, x * 10 + y);
856
+ * }
857
+ * }
858
+ * @endcode
859
+ *
860
+ * @see islet_get_3
861
+ * @see islet_del_3
862
+ * @see corm_put
863
+ * @see corm_save
864
+ */
865
+ static inline void
866
+ islet_put_1(uint32_t pdb_hd, int16_t *p, uint32_t thing)
867
+ {
868
+ uint64_t code = morton_set_1(p);
869
+ corm_put(pdb_hd, &code, &thing);
870
+ }
871
+
872
+ /**
873
+ * @brief 2D append. See islet_put_1() for the family docs.
874
+ */
875
+ static inline void
876
+ islet_put_2(uint32_t pdb_hd, int16_t *p, uint32_t thing)
877
+ {
878
+ uint64_t code = morton_set_2(p);
879
+ corm_put(pdb_hd, &code, &thing);
880
+ }
881
+
882
+ /**
883
+ * @brief 3D append. See islet_put_1() for the family docs.
884
+ */
885
+ static inline void
886
+ islet_put_3(uint32_t pdb_hd, int16_t *p, uint32_t thing)
887
+ {
888
+ uint64_t code = morton_set_3(p);
889
+ corm_put(pdb_hd, &code, &thing);
890
+ }
891
+
892
+ /**
893
+ * @brief 4D append. See islet_put_1() for the family docs.
894
+ */
895
+ static inline void
896
+ islet_put_4(uint32_t pdb_hd, int16_t *p, uint32_t thing)
897
+ {
898
+ uint64_t code = morton_set_4(p);
899
+ corm_put(pdb_hd, &code, &thing);
900
+ }
901
+
902
+ /**
903
+ * @brief 2D x 32-bit append. See islet_put_1() for the family docs.
904
+ */
905
+ static inline void
906
+ islet_put_2_32(uint32_t pdb_hd, int32_t *p, uint32_t thing)
907
+ {
908
+ uint64_t code = morton_set_2_32(p);
909
+ corm_put(pdb_hd, &code, &thing);
910
+ }
911
+
912
+ /**
913
+ * @brief Store a value at a spatial coordinate (replace).
914
+ *
915
+ * Per-dimension variants (islet_set_1..4). Replaces every value at the
916
+ * given point with a single new value: islet_del_all_N() followed by
917
+ * islet_put_N(). This preserves the historical single-value overwrite
918
+ * convenience on top of multi-value cells.
919
+ *
920
+ * @param[in] pdb_hd Database handle from islet_open().
921
+ * @param[in] p Point coordinate. Array of int16_t with at least the
922
+ * dimension count of the function used.
923
+ * @param[in] thing Value to store (uint32_t).
924
+ *
925
+ * Example:
926
+ * @code
927
+ * int16_t pos[3] = {10, 20, 30};
928
+ * islet_put_3(db, pos, 42);
929
+ * islet_set_3(db, pos, 99); // Cell now holds exactly {99}
930
+ * @endcode
931
+ *
932
+ * @see islet_put_3
933
+ * @see islet_del_all_3
934
+ * @see islet_get_3
935
+ */
936
+ static inline void
937
+ islet_set_1(uint32_t pdb_hd, int16_t *p, uint32_t thing)
938
+ {
939
+ uint64_t code = morton_set_1(p);
940
+ corm_del_all(pdb_hd, &code);
941
+ corm_put(pdb_hd, &code, &thing);
942
+ }
943
+
944
+ /**
945
+ * @brief 2D replace. See islet_set_1() for the family docs.
946
+ */
947
+ static inline void
948
+ islet_set_2(uint32_t pdb_hd, int16_t *p, uint32_t thing)
949
+ {
950
+ uint64_t code = morton_set_2(p);
951
+ corm_del_all(pdb_hd, &code);
952
+ corm_put(pdb_hd, &code, &thing);
953
+ }
954
+
955
+ /**
956
+ * @brief 3D replace. See islet_set_1() for the family docs.
957
+ */
958
+ static inline void
959
+ islet_set_3(uint32_t pdb_hd, int16_t *p, uint32_t thing)
960
+ {
961
+ uint64_t code = morton_set_3(p);
962
+ corm_del_all(pdb_hd, &code);
963
+ corm_put(pdb_hd, &code, &thing);
964
+ }
965
+
966
+ /**
967
+ * @brief 4D replace. See islet_set_1() for the family docs.
968
+ */
969
+ static inline void
970
+ islet_set_4(uint32_t pdb_hd, int16_t *p, uint32_t thing)
971
+ {
972
+ uint64_t code = morton_set_4(p);
973
+ corm_del_all(pdb_hd, &code);
974
+ corm_put(pdb_hd, &code, &thing);
975
+ }
976
+
977
+ /**
978
+ * @brief 2D x 32-bit replace. See islet_set_1() for the family docs.
979
+ */
980
+ static inline void
981
+ islet_set_2_32(uint32_t pdb_hd, int32_t *p, uint32_t thing)
982
+ {
983
+ uint64_t code = morton_set_2_32(p);
984
+ corm_del_all(pdb_hd, &code);
985
+ corm_put(pdb_hd, &code, &thing);
986
+ }
987
+
988
+ /**
989
+ * @brief Fill a recall candidate set with the values in a bounding box.
990
+ *
991
+ * Per-dimension variants (rec_axis_fill_bbox_1..4). Kernel-form
992
+ * space-axis adapter: streams every value stored in the axis-aligned
993
+ * box [s, s+l] into a recall candidate set, then seals it (sort +
994
+ * dedup). Multiple values sharing one cell each enter the set; sealing
995
+ * collapses exact duplicates. The walk never materializes the box
996
+ * volume — sparse queries over large boxes stay cheap.
997
+ *
998
+ * @param[in] pdb_hd Database handle from islet_open().
999
+ * @param[in] s Start point (minimum corner). Array of int16_t.
1000
+ * @param[in] l Lengths per dimension. Array of uint16_t.
1001
+ * @param[out] out Caller-owned candidate set; appended to, then sealed.
1002
+ * May already hold refs (result is the union, sealed).
1003
+ *
1004
+ * @return 0 on success (out sealed). -1 when out is NULL, or the box
1005
+ * volume exceeds ISLET_FILL_MAX_VOL (note: 4D volumes are 4-way
1006
+ * products, so keep each side small).
1007
+ *
1008
+ * Example:
1009
+ * @code
1010
+ * rec_set_t *cands = rec_set_new();
1011
+ * int16_t s[3] = {0, 0, 0};
1012
+ * uint16_t l[3] = {16, 16, 16};
1013
+ * if (rec_axis_fill_bbox_3(db, s, l, cands) == 0) {
1014
+ * // rec_set_count(cands) distinct refs, sorted
1015
+ * }
1016
+ * rec_set_free(cands);
1017
+ * @endcode
1018
+ *
1019
+ * @see ISLET_FILL_MAX_VOL
1020
+ * @see islet_iter_1 islet_iter_2 islet_iter_3 islet_iter_4
1021
+ */
1022
+ int rec_axis_fill_bbox_1(uint32_t pdb_hd, int16_t *s,
1023
+ uint16_t *l, rec_set_t *out);
1024
+ int rec_axis_fill_bbox_2(uint32_t pdb_hd, int16_t *s,
1025
+ uint16_t *l, rec_set_t *out);
1026
+ int rec_axis_fill_bbox_3(uint32_t pdb_hd, int16_t *s,
1027
+ uint16_t *l, rec_set_t *out);
1028
+ int rec_axis_fill_bbox_4(uint32_t pdb_hd, int16_t *s,
1029
+ uint16_t *l, rec_set_t *out);
1030
+
1031
+ /*
1032
+ * rec_axis_open convention (RECALL-KERNEL.md "rec_axis_open convention", optional CLI-open
1033
+ * convention, not part of libcorm's core rec_query registry API): opens an islet
1034
+ * store from an opaque "filename:database:mask" spec string (`:`-
1035
+ * separated; any/all fields may be empty for islet_open()'s NULL/0
1036
+ * defaults) and returns the ctx a caller then passes to
1037
+ * rec_axis_set_ctx() (the uint32_t db handle widened to a pointer via
1038
+ * uintptr_t).
1039
+ */
1040
+ void *rec_axis_open(const char *spec);
1041
+
1042
+ /*
1043
+ * rec_axis_store / rec_axis_unstore / rec_axis_readback (phase-2A store
1044
+ * contract, RECALL-KERNEL.md "rec_axis_store convention", optional
1045
+ * CLI-specific — not libcorm core API). The consumer passes (ref, value)
1046
+ * blindly — one opaque string, never split; each axis parses the whole
1047
+ * string in its own grammar.
1048
+ *
1049
+ * Grammar: a point list "x,y[,z];x2,y2" (`;`-separated points,
1050
+ * `,`-separated int16 coords; dim = first point's coord count, 1..4;
1051
+ * int16 lanes only). store is replace-in-place (parse-first, then
1052
+ * unstore-then-put, in-call dups deduped; over ISLET_AXIS_MAX_POINTS →
1053
+ * ERANGE, else EINVAL). Cells are recorded in the rev manifest (the
1054
+ * grid's own single-map <fname>.ridx sidecar, opened by rec_axis_open for
1055
+ * non-empty fname; in-memory rev otherwise) — unstore walks it backwards,
1056
+ * O(cells-of-ref), never a scan; readback is the ref's cells NUL-joined
1057
+ * in the store grammar (round-trippable). Different refs may share a
1058
+ * cell; del_value removes only the target. ctx == NULL → -1.
1059
+ */
1060
+ int rec_axis_store(void *ctx, const char *spec, rec_ref_t ref,
1061
+ const char *value);
1062
+ int rec_axis_unstore(void *ctx, rec_ref_t ref);
1063
+ int rec_axis_readback(void *ctx, rec_ref_t ref, char **blob_out,
1064
+ size_t *n_out);
1065
+
1066
+ /**
1067
+ * @brief 2D x 32-bit box fill. Same contract as
1068
+ * rec_axis_fill_bbox_N(), on int32_t lanes (start AND
1069
+ * lengths). ISLET_FILL_MAX_VOL caps every config alike.
1070
+ */
1071
+ int rec_axis_fill_bbox_2_32(uint32_t pdb_hd, int32_t *s,
1072
+ int32_t *l, rec_set_t *out);
1073
+
1074
+ /**
1075
+ * @brief Per-dimension operation table.
1076
+ *
1077
+ * A single global, indexed by dimension count (1..4), giving the
1078
+ * per-dimension implementation of each core op. Each function pointer
1079
+ * takes NO dimension argument — the index into islet_ops[] is the dim.
1080
+ * This is the runtime-dim mechanism: when `dim` is a variable, call
1081
+ * `islet_ops[dim].morton_set(p)` instead of switching on the value
1082
+ * yourself. The type-safe alternative is to call the per-dimension
1083
+ * function directly (morton_set_3(), etc.), which the compiler fully
1084
+ * unrolls.
1085
+ *
1086
+ * islet_ops[0] is all-NULL (dimension 0 is invalid); islet_ops[1..4] are
1087
+ * initialized by the library.
1088
+ *
1089
+ * Example (runtime dimension):
1090
+ * @code
1091
+ * islet_ops[dim].morton_set(p); // dim in 1..4
1092
+ * islet_ops[dim].iter(db, s, l); // box-iterate the runtime dim
1093
+ * @endcode
1094
+ *
1095
+ * @see morton_set_1 morton_set_2 morton_set_3 morton_set_4
1096
+ * @see islet_iter_1 islet_iter_2 islet_iter_3 islet_iter_4
1097
+ */
1098
+ typedef struct {
1099
+ uint64_t (*morton_set)(int16_t *p);
1100
+ void (*morton_get)(int16_t *p, uint64_t code);
1101
+ void (*point_add)(int16_t *tar, int16_t *a, int16_t *b);
1102
+ void (*point_copy)(int16_t *tar, int16_t *src);
1103
+ void (*put)(uint32_t pdb_hd, int16_t *p, uint32_t thing);
1104
+ uint32_t (*get)(uint32_t pdb_hd, int16_t *p);
1105
+ void (*set)(uint32_t pdb_hd, int16_t *p, uint32_t thing);
1106
+ void (*del)(uint32_t pdb_hd, int16_t *p);
1107
+ uint32_t (*del_all)(uint32_t pdb_hd, int16_t *p);
1108
+ uint32_t (*cell_count)(uint32_t pdb_hd, int16_t *p);
1109
+ uint32_t (*iter)(uint32_t pdb_hd, int16_t *s, uint16_t *l);
1110
+ uint32_t (*get_multi)(uint32_t pdb_hd, int16_t *p);
1111
+ int (*fill)(uint32_t pdb_hd, int16_t *s, uint16_t *l,
1112
+ rec_set_t *out);
1113
+ } islet_ops_t;
1114
+
1115
+ /**
1116
+ * @brief The per-dimension operation table (islet_ops[0] = all NULL).
1117
+ *
1118
+ * NOTE: literal 5 (MAX_DIM + 1) here — MAX_DIM lives in libislet.c and
1119
+ * this header stays standalone. Keep the two in sync.
1120
+ */
1121
+ extern const islet_ops_t islet_ops[5];
1122
+
1123
+ #if ISLET_SIMD_MORTON
1124
+ /**
1125
+ * @brief Batch-encode multiple 3D points to Morton codes using SIMD.
1126
+ *
1127
+ * Encodes up to 4 points (AVX2) or 2 points (NEON) in parallel,
1128
+ * falling back to scalar for remaining points. The output array must
1129
+ * have space for at least @p n elements.
1130
+ *
1131
+ * @param[out] out Output Morton codes. Array of uint64_t with 'n' elements.
1132
+ * @param[in] points Input points. Array of int16_t[3] with 'n' entries.
1133
+ * @param[in] n Number of points to encode (0..UINT32_MAX).
1134
+ *
1135
+ * @return Number of points encoded (always == n).
1136
+ */
1137
+ uint32_t morton_set_bulk(uint64_t *out, int16_t points[][3], uint32_t n);
1138
+
1139
+ /**
1140
+ * @brief Batch-encode multiple 4D points to Morton codes using SIMD.
1141
+ *
1142
+ * 4D analogue of morton_set_bulk(): encodes 4 points at a time under
1143
+ * AVX2 (dense stride-4 packing), scalar tail + fallback otherwise.
1144
+ * Output codes match morton_set_4() exactly.
1145
+ *
1146
+ * @param[out] out Output Morton codes. Array of uint64_t with 'n' elements.
1147
+ * @param[in] points Input points. Array of int16_t[4] with 'n' entries.
1148
+ * @param[in] n Number of points to encode (0..UINT32_MAX).
1149
+ *
1150
+ * @return Number of points encoded (always == n).
1151
+ */
1152
+ uint32_t morton_set_bulk4(uint64_t *out, int16_t points[][4], uint32_t n);
1153
+
1154
+ /**
1155
+ * @brief Batch-decode multiple 3D Morton codes to points using SIMD.
1156
+ *
1157
+ * Inverse of morton_set_bulk(): decodes 4 codes at a time under AVX2
1158
+ * (dense compact, scalar tail + fallback otherwise). Output points
1159
+ * match morton_get_3() exactly.
1160
+ *
1161
+ * @param[out] points Output points. Array of int16_t[3] with 'n' entries.
1162
+ * @param[in] codes Input Morton codes. Array of uint64_t with 'n' elements.
1163
+ * @param[in] n Number of codes to decode (0..UINT32_MAX).
1164
+ *
1165
+ * @return Number of points decoded (always == n).
1166
+ */
1167
+ uint32_t morton_get_bulk(int16_t points[][3], const uint64_t *codes, uint32_t n);
1168
+
1169
+ /**
1170
+ * @brief Batch-decode multiple 4D Morton codes to points using SIMD.
1171
+ *
1172
+ * 4D analogue of morton_get_bulk(): decodes 4 codes at a time under
1173
+ * AVX2 (dense stride-4 compact), scalar tail + fallback otherwise.
1174
+ * Output points match morton_get_4() exactly.
1175
+ *
1176
+ * @param[out] points Output points. Array of int16_t[4] with 'n' entries.
1177
+ * @param[in] codes Input Morton codes. Array of uint64_t with 'n' elements.
1178
+ * @param[in] n Number of codes to decode (0..UINT32_MAX).
1179
+ *
1180
+ * @return Number of points decoded (always == n).
1181
+ */
1182
+ uint32_t morton_get_bulk4(int16_t points[][4], const uint64_t *codes, uint32_t n);
1183
+ #endif
1184
+
1185
+ /** @} */
1186
+
1187
+ #endif