das2C
das core C utilities (v3)
Loading...
Searching...
No Matches
generator.h
Go to the documentation of this file.
1/* Copyright (C) 2026 Chris Piker <chris-piker@uiowa.edu>
2 *
3 * Author: C. Piker, via Claude Fable 5
4 *
5 * This file is part of das2C, the Core Das2 C Library.
6 *
7 * Das2C is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Lesser General Public License version 2.1 as published
9 * by the Free Software Foundation.
10 *
11 * Das2C is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * version 2.1 along with das2C; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20/* Note this is a library-internal file, not stable public API is defined here */
21
24#ifndef _das_generator_h_
25#define _das_generator_h_
26
27#include <das3/value.h>
28#include <das3/array.h>
29
30#ifdef __cplusplus
31extern "C" {
32#endif
33
34/* ------------------------------------------------------------------------- *
35 * The index vocabulary.
36 *
37 * An array declares extents that are either a real count or unbounded, and it
38 * says so with unsigned values (see ARYIDX_UNBOUND in array.h). From here up
39 * the stack an extent can also be BORROWED from the container or entirely
40 * UNUSED by this variable, so the model needs signed values and three distinct
41 * negative markers. This is where those first mean anything, which is why they
42 * live here rather than one layer down.
43 *
44 * The flags walk DOWNWARD from VARIDX_RAGGED on purpose. das_varlength_merge()
45 * implements the precedence lattice
46 *
47 * Ragged > Number > Borrow > Unused
48 *
49 * so the flags have to descend in precedence order for it to be right.
50 * ------------------------------------------------------------------------- */
51
55#define VARIDX_MAX ARYIDX_MAX
56
57/* VARIDX_RAGGED lives in array.h: DasAry_shape() has to write it. */
58#define VARIDX_BORROW (VARIDX_RAGGED - 1) /* no intrinsic extent, take the container's */
59#define VARIDX_UNUSED (VARIDX_BORROW - 1) /* a change in this index changes nothing here */
60
62#define VARIDX_MAXFLG VARIDX_RAGGED
63
64#define VARIDX_INIT_UNUSED {-3,-3,-3,-3,-3,-3,-3,-3}
65#define VARIDX_INIT_BEGIN { 0, 0, 0, 0, 0, 0, 0, 0}
66
76DAS_API void das_varindex_merge(int nRank, ptrdiff_t* pDest, ptrdiff_t* pSrc);
77
83DAS_API ptrdiff_t das_varlength_merge(ptrdiff_t nLeft, ptrdiff_t nRight);
84
100 const char* sShape, int nMaxRank, bool bAllowFlags, ptrdiff_t* pShape,
101 int* pnRank, const char* sWhere
102);
103
115DAS_API char* das_shape_toStr(
116 const ptrdiff_t* pShape, int nRank, char* sBuf, size_t uLen
117);
118
138DAS_API int das_shape_info(
139 const ptrdiff_t* pShape, int nExtRank, int nShapeLen, char* sBuf, size_t uLen
140);
141
149DAS_API void das_varindex_prndir(bool bFastLast);
150
151/* ------------------------------------------------------------------------- *
152 * The generator (gt).
153 *
154 * A generator is a value source and nothing more. It answers one question:
155 * what raw elements sit at a given external index? It does not know what
156 * those elements mean. It does not know it is a vector, a complex, or a
157 * timestamp. That meaning is the variable's job, one layer up in variable.h.
158 *
159 * Generators varies independently of the variable type. The same
160 * geometric vector can be backed by an array on Monday and a sequence on
161 * Tuesday, and it is the same vector both days. So a variable owns a
162 * generator by composition. A variable is not a generator.
163 * ------------------------------------------------------------------------- */
164
165
166
167/* Generator type (gt). How the values are produced. */
168typedef enum das_gen_type_e {
169 gtArray = 1, /* a lookup into a backing DasAry */
170 gtSeq, /* an intercept plus an interval, computed on demand */
171 gtConst, /* one value, everywhere */
172 gtUnop, /* a unary operation on one child generator */
173 gtBinop /* a binary operation on two child generators */
174} das_gen_type;
175
176
177typedef struct das_generator DasGen;
178
179typedef struct DasGen_VTbl {
180
181 /* Fill the caller's buffer with the internal run of raw elements this
182 generator produces at one external index. The run length is the
183 variable's item count: one for a scalar, ncomp for a composite.
184 Returns the count written, or a negative das error code. */
185 int (*eval)(
186 const DasGen* pThis, const ptrdiff_t* pExtLoc, ubyte* pRun, size_t uRunMax
187 );
188
189 /* The external shape this generator can address, VARIDX_RAGGED /
190 VARIDX_BORROW / VARIDX_UNUSED vocabulary. For an array this is the
191 array shape minus the internal indices. For a sequence it is the
192 declared extent. */
193 int (*extShape)(const DasGen* pThis, ptrdiff_t* pShape);
194
195 /* External length at a partial index, for ragged external runs. Keep the
196 MIN merge semantics that DasDs_lengthIn already relies on. */
197 ptrdiff_t (*lengthIn)(const DasGen* pThis, int nIdx, ptrdiff_t* pLoc);
198
199 /* A pointer to the item run IN PLACE, for values that are views rather
200 than copies: a string datum carries a pointer into the backing array,
201 a blob carries pointer plus length, and both can exceed any fixed
202 buffer. Only a backed generator can answer (gtArray); computed
203 sources return NULL, a computed string having no home to point at.
204 *pCount receives the run element count (ragged aware). */
205 const ubyte* (*at)(
206 const DasGen* pThis, const ptrdiff_t* pExtLoc, size_t* pCount
207 );
208
209 /* Hand back a rectangular DasAry covering the external range [pMin,pMax)
210 without copying, when the generator's storage already has that shape.
211
212 Only an array-backed generators provide this feature, others return NULL
213 which means "not supported, allocate and call subsetInto" which is the
214 same not-my-job answer that at() gives.
215
216 The returned array holds one reference for the caller and saves its
217 memory owner, exactly as new_DasAry() would
218 @see DasAry_subSetIn.
219 */
220 DasAry* (*subsetView)(
221 const DasGen* pThis, int nExtRank, const ptrdiff_t* pMin,
222 const ptrdiff_t* pMax
223 );
224
225 /* What you use if subsetView() can provide a fast wrapper over existing
226 data. Write every item run in [pMin,pMax) into the caller's buffer in
227 row-major order (last external index fastest).
228
229 Ragged index ranges are padded out with fill values as needed.
230 So a subset of any variable, ragged or not is *always* rectangular.
231
232 This property is needed for writing CDFs and other formats that will not
233 accept variable length records.
234
235 Returns item runs written, or a negative das error code. */
236 int (*subsetInto)(
237 const DasGen* pThis, int nExtRank, const ptrdiff_t* pMin,
238 const ptrdiff_t* pMax, ubyte* pBuf, size_t uBufLen
239 );
240
241 void (*destroy)(DasGen* pThis); /* called by DasGen_decRef at zero */
242
243} DasGen_VTbl;
244
245
246struct das_generator {
247
248 das_gen_type kind; /* how values are produced */
249
250 const DasGen_VTbl* pVTbl;
251
252 /* Element types are answered by the generator, because "what one element
253 holds" is a fact about the source, not the index presentation or the
254 mathematical formalism. */
255 das_elem_type elem;
256
257 int nRef;
258};
259
260/* Nothing below is tagged DAS_API. A generator is reached through the DasVar
261 that owns it, so in a language with the notion these would be package
262 private: visible across the library, not part of its published surface.
263 Test programs link the static library and so still see them. */
264
275#define DasGen_type(P) ((P)->kind)
276
279#define DasGen_elemType(P) ((P)->elem)
280
286DAS_API int DasGen_incRef(DasGen* pThis);
287
303DAS_API int DasGen_decRef(DasGen* pThis);
304
318int DasGen_eval(
319 const DasGen* pThis, const ptrdiff_t* pExtLoc, ubyte* pRun, size_t uRunMax
320);
321
335int DasGen_extShape(const DasGen* pThis, ptrdiff_t* pShape);
336
346ptrdiff_t DasGen_lengthIn(const DasGen* pThis, int nIdx, ptrdiff_t* pLoc);
347
361const ubyte* DasGen_at(
362 const DasGen* pThis, const ptrdiff_t* pExtLoc, size_t* pCount
363);
364
376DasAry* DasGen_subsetView(
377 const DasGen* pThis, int nExtRank, const ptrdiff_t* pMin, const ptrdiff_t* pMax
378);
379
394int DasGen_subsetInto(
395 const DasGen* pThis, int nExtRank, const ptrdiff_t* pMin,
396 const ptrdiff_t* pMax, ubyte* pBuf, size_t uBufLen
397);
398
409DAS_API const ubyte* DasGen_getFill(const DasGen* pThis);
410
419size_t DasGen_itemElems(const DasGen* pThis);
420
430DasGen* DasGen_copy(const DasGen* pThis);
431
444char* DasGen_expression(const DasGen* pThis, char* sBuf, size_t uLen);
445
453DasAry* DasGen_getArray(const DasGen* pThis);
454
466bool DasGen_setArray(DasGen* pThis, DasAry* pNew);
467
468
469/* The concrete generators. ------------------------------------------------ */
470
471typedef struct das_gen_array {
472 DasGen base;
473
474 DasAry* pAry; /* referenced (incRef held), owned by the dataset */
475
476 /* external index -> array index; VARIDX_UNUSED marks a degenerate
477 external index. Array indices past the mapped ones are the item run. */
478 int nExtRank;
479 int8_t idxmap[VARIDX_MAX];
480
481 /* elements in one item run, product of the unmapped (internal) array
482 extents. v1 handles cubic internal shapes; a ragged INTERNAL extent
483 fails loud at construction until the run walk migrates. */
484 size_t uItemElems;
485} DasGenAry;
486
487/* Every intercept and slope sits in a slot this wide, the largest element,
488 a broken-down time */
489#define DASGEN_SEQ_SLOT sizeof(das_time)
490
491typedef struct das_gen_seq {
492 DasGen base;
493
494 /* One intercept per component, one interval per (component, external
495 index), in one heap block sized by the component count: a sequence has
496 as many components as the item it fills. For etTime the intercept is a
497 das_time and the slopes are doubles in seconds (the affine rule at the
498 storage layer). */
499 int nComps; /* 1 for a scalar sequence */
500 ubyte* pIntercept; /* nComps slots */
501 ubyte* pInterval; /* nComps * VARIDX_MAX slots, component major */
502
503 int nExtRank; /* declared extent, since a sequence */
504 ptrdiff_t aExtShape[VARIDX_MAX]; /* has no backing store to derive one */
505} DasGenSeq;
506
508#define DasGenSeq_intercept(P,c) ((P)->pIntercept + (size_t)(c)*DASGEN_SEQ_SLOT)
509
511#define DasGenSeq_interval(P,c,i) \
512 ((P)->pInterval + ((size_t)(c)*VARIDX_MAX + (size_t)(i))*DASGEN_SEQ_SLOT)
513
514typedef struct das_gen_const {
515 DasGen base;
516 ubyte aValue[sizeof(double)];
517
518 int nExtRank;
519 ptrdiff_t aExtShape[VARIDX_MAX];
520} DasGenConst;
521
522/* gtUnop / gtBinop. The generator stays formalism-ignorant: it holds a bare
523 bytes-in bytes-out apply function handed down by the variable layer, which got
524 it from the binop registry. Scalar-only in v1: children must produce
525 one-element runs, fail loud otherwise. */
526typedef bool (*das_gen_applyfn)(
527 das_elem_type etL, das_elem_type etR,
528 const ubyte* pL, const ubyte* pR, ubyte* pOut
529);
530
531typedef struct das_gen_op {
532 DasGen base; /* base.elem is the RESULT element type */
533 das_gen_applyfn apply;
534 DasGen* pLeft;
535 DasGen* pRight; /* NULL for gtUnop (none exist yet) */
536 double rRightScale; /* brings right values into left scale; 1.0
537 when none needed. Scaling promotes the
538 right operand to double before apply. */
539} DasGenOp;
540
541
564DAS_API DasGen* new_DasGenAry(DasAry* pAry, int nExtRank, const int8_t* pIdxMap);
565
578DAS_API DasGen* new_DasGenSeq(
579 das_elem_type et, const ubyte* pIntercept, int nExtRank,
580 const ubyte* pIntervals, const ptrdiff_t* pExtShape
581);
582
593DAS_API DasGen* new_DasGenSeqN(
594 das_elem_type et, int nComps, const ubyte* pIntercepts, int nExtRank,
595 const ubyte* pIntervals, const ptrdiff_t* pExtShape
596);
597
609DAS_API DasGen* new_DasGenConst(
610 das_elem_type et, const ubyte* pVal, int nExtRank, const ptrdiff_t* pExtShape
611);
612
627DAS_API DasGen* new_DasGenBinop(
628 das_gen_applyfn apply, das_elem_type etOut, DasGen* pLeft, DasGen* pRight,
629 double rRightScale
630);
631
632
633#ifdef __cplusplus
634}
635#endif
636
637#endif /* _das_generator_h_ */
A dynamic buffer with multi-dimensional array style access.
DAS_API void das_varindex_merge(int nRank, ptrdiff_t *pDest, ptrdiff_t *pSrc)
Merge two shapes in the model's index space, taking the lattice.
DAS_API DasErrCode das_shape_fromStr(const char *sShape, int nMaxRank, bool bAllowFlags, ptrdiff_t *pShape, int *pnRank, const char *sWhere)
Parse a shape string in the model's index vocabulary.
DAS_API ptrdiff_t das_varlength_merge(ptrdiff_t nLeft, ptrdiff_t nRight)
Merge the length of one index position, same lattice as above.
DAS_API char * das_shape_toStr(const ptrdiff_t *pShape, int nRank, char *sBuf, size_t uLen)
Emit a shape string, the exact inverse of das_shape_fromStr().
#define VARIDX_MAX
Max index count for the model.
Definition generator.h:55
DAS_API int das_shape_info(const ptrdiff_t *pShape, int nExtRank, int nShapeLen, char *sBuf, size_t uLen)
Print shape information using symbols i,j,k etc for index positions.
DAS_API void das_varindex_prndir(bool bFastLast)
Set index printing direction for das_shape_info().
int DasErrCode
return code type 0 indicates success, negative integer indicates failure
Definition defs.h:189
Dynamic recursive ragged arrays.
Definition array.h:243
A generic value type for use in arrays, datums and variables.