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
107DAS_API int das_shape_toStr(
108 const ptrdiff_t* pShape, int nRank, char* sBuf, int nLen
109);
110
111/* ------------------------------------------------------------------------- *
112 * The generator (gt).
113 *
114 * A generator is a value source and nothing more. It answers one question:
115 * what raw elements sit at a given external index? It does not know what
116 * those elements mean. It does not know it is a vector, a complex, or a
117 * timestamp. That meaning is the variable's job, one layer up in variable.h.
118 *
119 * Generators varies independently of the variable type. The same
120 * geometric vector can be backed by an array on Monday and a sequence on
121 * Tuesday, and it is the same vector both days. So a variable owns a
122 * generator by composition. A variable is not a generator.
123 * ------------------------------------------------------------------------- */
124
125
126
127/* Generator type (gt). How the values are produced. */
128typedef enum das_gen_type_e {
129 gtArray = 1, /* a lookup into a backing DasAry */
130 gtSeq, /* an intercept plus an interval, computed on demand */
131 gtConst, /* one value, everywhere */
132 gtUnop, /* a unary operation on one child generator */
133 gtBinop /* a binary operation on two child generators */
134} das_gen_type;
135
136
137typedef struct das_generator DasGen;
138
139typedef struct DasGen_VTbl {
140
141 /* Fill the caller's buffer with the internal run of raw elements this
142 generator produces at one external index. The run length is the
143 variable's item count: one for a scalar, ncomp for a composite.
144 Returns the count written, or a negative das error code. */
145 int (*eval)(
146 const DasGen* pThis, const ptrdiff_t* pExtLoc, ubyte* pRun, size_t uRunMax
147 );
148
149 /* The external shape this generator can address, VARIDX_RAGGED /
150 VARIDX_BORROW / VARIDX_UNUSED vocabulary. For an array this is the
151 array shape minus the internal indices. For a sequence it is the
152 declared extent. */
153 int (*extShape)(const DasGen* pThis, ptrdiff_t* pShape);
154
155 /* External length at a partial index, for ragged external runs. Keep the
156 MIN merge semantics that DasDs_lengthIn already relies on. */
157 ptrdiff_t (*lengthIn)(const DasGen* pThis, int nIdx, ptrdiff_t* pLoc);
158
159 /* A pointer to the item run IN PLACE, for values that are views rather
160 than copies: a string datum carries a pointer into the backing array,
161 a blob carries pointer plus length, and both can exceed any fixed
162 buffer. Only a backed generator can answer (gtArray); computed
163 sources return NULL, a computed string having no home to point at.
164 *pCount receives the run element count (ragged aware). */
165 const ubyte* (*at)(
166 const DasGen* pThis, const ptrdiff_t* pExtLoc, size_t* pCount
167 );
168
169 /* Hand back a rectangular DasAry covering the external range [pMin,pMax)
170 without copying, when the generator's storage already has that shape.
171
172 Only an array-backed generators provide this feature, others return NULL
173 which means "not supported, allocate and call subsetInto" which is the
174 same not-my-job answer that at() gives.
175
176 The returned array holds one reference for the caller and saves its
177 memory owner, exactly as new_DasAry() would
178 @see DasAry_subSetIn.
179 */
180 DasAry* (*subsetView)(
181 const DasGen* pThis, int nExtRank, const ptrdiff_t* pMin,
182 const ptrdiff_t* pMax
183 );
184
185 /* What you use if subsetView() can provide a fast wrapper over existing
186 data. Write every item run in [pMin,pMax) into the caller's buffer in
187 row-major order (last external index fastest).
188
189 Ragged index ranges are padded out with fill values as needed.
190 So a subset of any variable, ragged or not is *always* rectangular.
191
192 This property is needed for writing CDFs and other formats that will not
193 accept variable length records.
194
195 Returns item runs written, or a negative das error code. */
196 int (*subsetInto)(
197 const DasGen* pThis, int nExtRank, const ptrdiff_t* pMin,
198 const ptrdiff_t* pMax, ubyte* pBuf, size_t uBufLen
199 );
200
201 void (*destroy)(DasGen* pThis); /* called by DasGen_decRef at zero */
202
203} DasGen_VTbl;
204
205
206struct das_generator {
207
208 das_gen_type kind; /* how values are produced */
209
210 const DasGen_VTbl* pVTbl;
211
212 /* Element types are answered by the generator, because "what one cell holds"
213 is a fact about the source, not the index presentation or the mathematical
214 formalism. */
215 das_elem_type elem;
216
217 int nRef;
218};
219
220/* Nothing below is tagged DAS_API. A generator is reached through the DasVar
221 that owns it, so in a language with the notion these would be package
222 private: visible across the library, not part of its published surface.
223 Test programs link the static library and so still see them. */
224
235#define DasGen_type(P) ((P)->kind)
236
239#define DasGen_elemType(P) ((P)->elem)
240
246int DasGen_incRef(DasGen* pThis);
247
263int DasGen_decRef(DasGen* pThis);
264
278int DasGen_eval(
279 const DasGen* pThis, const ptrdiff_t* pExtLoc, ubyte* pRun, size_t uRunMax
280);
281
295int DasGen_extShape(const DasGen* pThis, ptrdiff_t* pShape);
296
306ptrdiff_t DasGen_lengthIn(const DasGen* pThis, int nIdx, ptrdiff_t* pLoc);
307
321const ubyte* DasGen_at(
322 const DasGen* pThis, const ptrdiff_t* pExtLoc, size_t* pCount
323);
324
336DasAry* DasGen_subsetView(
337 const DasGen* pThis, int nExtRank, const ptrdiff_t* pMin, const ptrdiff_t* pMax
338);
339
354int DasGen_subsetInto(
355 const DasGen* pThis, int nExtRank, const ptrdiff_t* pMin,
356 const ptrdiff_t* pMax, ubyte* pBuf, size_t uBufLen
357);
358
369const ubyte* DasGen_getFill(const DasGen* pThis);
370
379size_t DasGen_itemElems(const DasGen* pThis);
380
402int DasGen_elemShape(const DasGen* pThis, ptrdiff_t* pShape);
403
413DasGen* DasGen_copy(const DasGen* pThis);
414
427char* DasGen_expression(const DasGen* pThis, char* sBuf, int nLen);
428
436DasAry* DasGen_getArray(const DasGen* pThis);
437
449bool DasGen_setArray(DasGen* pThis, DasAry* pNew);
450
451
452/* The concrete generators. ------------------------------------------------ */
453
454typedef struct das_gen_array {
455 DasGen base;
456
457 DasAry* pAry; /* referenced (incRef held), owned by the dataset */
458
459 /* external index -> array index; VARIDX_UNUSED marks a degenerate
460 external index. Array indices past the mapped ones are the item run. */
461 int nExtRank;
462 int8_t idxmap[VARIDX_MAX];
463
464 /* elements in one item run, product of the unmapped (internal) array
465 extents. v1 handles cubic internal shapes; a ragged INTERNAL extent
466 fails loud at construction until the run walk migrates. */
467 size_t uItemElems;
468} DasGenAry;
469
470/* Every intercept and slope sits in a slot this wide, the largest element,
471 a broken-down time */
472#define DASGEN_SEQ_SLOT sizeof(das_time)
473
474typedef struct das_gen_seq {
475 DasGen base;
476
477 /* One intercept per component, one interval per (component, external
478 index), in one heap block sized by the component count: a sequence has
479 as many components as the item it fills. For etTime the intercept is a
480 das_time and the slopes are doubles in seconds (the affine rule at the
481 storage layer). */
482 int nComps; /* 1 for a scalar sequence */
483 ubyte* pIntercept; /* nComps slots */
484 ubyte* pInterval; /* nComps * VARIDX_MAX slots, component major */
485
486 int nExtRank; /* declared extent, since a sequence */
487 ptrdiff_t aExtShape[VARIDX_MAX]; /* has no backing store to derive one */
488} DasGenSeq;
489
491#define DasGenSeq_intercept(P,c) ((P)->pIntercept + (size_t)(c)*DASGEN_SEQ_SLOT)
492
494#define DasGenSeq_interval(P,c,i) \
495 ((P)->pInterval + ((size_t)(c)*VARIDX_MAX + (size_t)(i))*DASGEN_SEQ_SLOT)
496
497typedef struct das_gen_const {
498 DasGen base;
499 ubyte aValue[sizeof(double)];
500
501 int nExtRank;
502 ptrdiff_t aExtShape[VARIDX_MAX];
503} DasGenConst;
504
505/* gtUnop / gtBinop. The generator stays formalism-ignorant: it holds a bare
506 bytes-in bytes-out apply function handed down by the variable layer, which got
507 it from the binop registry. Scalar-only in v1: children must produce
508 one-element runs, fail loud otherwise. */
509typedef bool (*das_gen_applyfn)(
510 das_elem_type etL, das_elem_type etR,
511 const ubyte* pL, const ubyte* pR, ubyte* pOut
512);
513
514typedef struct das_gen_op {
515 DasGen base; /* base.elem is the RESULT element type */
516 das_gen_applyfn apply;
517 DasGen* pLeft;
518 DasGen* pRight; /* NULL for gtUnop (none exist yet) */
519 double rRightScale; /* brings right values into left scale; 1.0
520 when none needed. Scaling promotes the
521 right operand to double before apply. */
522} DasGenOp;
523
524
547DasGen* new_DasGenAry(DasAry* pAry, int nExtRank, const int8_t* pIdxMap);
548
561DasGen* new_DasGenSeq(
562 das_elem_type et, const ubyte* pIntercept, int nExtRank,
563 const ubyte* pIntervals, const ptrdiff_t* pExtShape
564);
565
576DasGen* new_DasGenSeqN(
577 das_elem_type et, int nComps, const ubyte* pIntercepts, int nExtRank,
578 const ubyte* pIntervals, const ptrdiff_t* pExtShape
579);
580
592DasGen* new_DasGenConst(
593 das_elem_type et, const ubyte* pVal, int nExtRank, const ptrdiff_t* pExtShape
594);
595
610DasGen* new_DasGenBinop(
611 das_gen_applyfn apply, das_elem_type etOut, DasGen* pLeft, DasGen* pRight,
612 double rRightScale
613);
614
615
616#ifdef __cplusplus
617}
618#endif
619
620#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.
#define VARIDX_MAX
Max index count for the model.
Definition generator.h:55
DAS_API int das_shape_toStr(const ptrdiff_t *pShape, int nRank, char *sBuf, int nLen)
Emit a shape string, the exact inverse of das_shape_fromStr().
int DasErrCode
return code type 0 indicates success, negative integer indicates failure
Definition defs.h:184
Dynamic recursive ragged arrays.
Definition array.h:271
A generic value type for use in arrays, datums and variables.