PyDecisionGraph 0.3.2.post1__cp315-cp315t-win_amd64.whl
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.
- decision_graph/__infra__.pxd +1 -0
- decision_graph/__init__.pxd +1 -0
- decision_graph/__init__.py +39 -0
- decision_graph/decision_tree/__init__.pxd +2 -0
- decision_graph/decision_tree/__init__.py +85 -0
- decision_graph/decision_tree/bake/__infra__.pxd +502 -0
- decision_graph/decision_tree/bake/__init__.pxd +502 -0
- decision_graph/decision_tree/bake/__init__.py +29 -0
- decision_graph/decision_tree/bake/c_action.cp315t-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_action.h +324 -0
- decision_graph/decision_tree/bake/c_action.pxd +54 -0
- decision_graph/decision_tree/bake/c_action.pyi +126 -0
- decision_graph/decision_tree/bake/c_action.pyx +65 -0
- decision_graph/decision_tree/bake/c_allocator_protocol.cp315t-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_allocator_protocol.pxd +24 -0
- decision_graph/decision_tree/bake/c_allocator_protocol.pyi +61 -0
- decision_graph/decision_tree/bake/c_allocator_protocol.pyx +109 -0
- decision_graph/decision_tree/bake/c_bake.cp315t-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_bake.h +629 -0
- decision_graph/decision_tree/bake/c_bake.pxd +52 -0
- decision_graph/decision_tree/bake/c_bake.pyi +151 -0
- decision_graph/decision_tree/bake/c_bake.pyx +142 -0
- decision_graph/decision_tree/bake/c_collections.cp315t-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_collections.h +692 -0
- decision_graph/decision_tree/bake/c_collections.pxd +52 -0
- decision_graph/decision_tree/bake/c_collections.pyi +193 -0
- decision_graph/decision_tree/bake/c_collections.pyx +161 -0
- decision_graph/decision_tree/bake/c_const.cp315t-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_const.h +395 -0
- decision_graph/decision_tree/bake/c_const.pxd +56 -0
- decision_graph/decision_tree/bake/c_const.pyi +305 -0
- decision_graph/decision_tree/bake/c_const.pyx +278 -0
- decision_graph/decision_tree/bake/c_edge.cp315t-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_edge.h +511 -0
- decision_graph/decision_tree/bake/c_edge.pxd +117 -0
- decision_graph/decision_tree/bake/c_edge.pyi +192 -0
- decision_graph/decision_tree/bake/c_edge.pyx +270 -0
- decision_graph/decision_tree/bake/c_eval.h +848 -0
- decision_graph/decision_tree/bake/c_eval.pxd +40 -0
- decision_graph/decision_tree/bake/c_expr.cp315t-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_expr.h +1900 -0
- decision_graph/decision_tree/bake/c_expr.pxd +129 -0
- decision_graph/decision_tree/bake/c_expr.pyi +280 -0
- decision_graph/decision_tree/bake/c_expr.pyx +272 -0
- decision_graph/decision_tree/bake/c_hierarchy.cp315t-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_hierarchy.h +731 -0
- decision_graph/decision_tree/bake/c_hierarchy.pxd +71 -0
- decision_graph/decision_tree/bake/c_hierarchy.pyi +409 -0
- decision_graph/decision_tree/bake/c_hierarchy.pyx +277 -0
- decision_graph/decision_tree/bake/c_logic_group.cp315t-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_logic_group.h +1226 -0
- decision_graph/decision_tree/bake/c_logic_group.pxd +162 -0
- decision_graph/decision_tree/bake/c_logic_group.pyi +264 -0
- decision_graph/decision_tree/bake/c_logic_group.pyx +308 -0
- decision_graph/decision_tree/bake/c_node.cp315t-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_node.h +2660 -0
- decision_graph/decision_tree/bake/c_node.pxd +381 -0
- decision_graph/decision_tree/bake/c_node.pyi +438 -0
- decision_graph/decision_tree/bake/c_node.pyx +639 -0
- decision_graph/decision_tree/bake/c_reconstruct.cp315t-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_reconstruct.pxd +63 -0
- decision_graph/decision_tree/bake/c_reconstruct.pyi +60 -0
- decision_graph/decision_tree/bake/c_reconstruct.pyx +252 -0
- decision_graph/decision_tree/bake/c_var.cp315t-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_var.h +2054 -0
- decision_graph/decision_tree/bake/c_var.pxd +205 -0
- decision_graph/decision_tree/bake/c_var.pyi +271 -0
- decision_graph/decision_tree/bake/c_var.pyx +228 -0
- decision_graph/decision_tree/capi/__infra__.pxd +33 -0
- decision_graph/decision_tree/capi/__init__.pxd +33 -0
- decision_graph/decision_tree/capi/__init__.py +57 -0
- decision_graph/decision_tree/capi/c_abc.cp315t-win_amd64.pyd +0 -0
- decision_graph/decision_tree/capi/c_abc.pxd +246 -0
- decision_graph/decision_tree/capi/c_abc.pyi +770 -0
- decision_graph/decision_tree/capi/c_abc.pyx +1532 -0
- decision_graph/decision_tree/capi/c_collection.cp315t-win_amd64.pyd +0 -0
- decision_graph/decision_tree/capi/c_collection.pxd +22 -0
- decision_graph/decision_tree/capi/c_collection.pyi +245 -0
- decision_graph/decision_tree/capi/c_collection.pyx +138 -0
- decision_graph/decision_tree/capi/c_node.cp315t-win_amd64.pyd +0 -0
- decision_graph/decision_tree/capi/c_node.pxd +95 -0
- decision_graph/decision_tree/capi/c_node.pyi +492 -0
- decision_graph/decision_tree/capi/c_node.pyx +551 -0
- decision_graph/decision_tree/exc.py +189 -0
- decision_graph/decision_tree/native/__init__.py +57 -0
- decision_graph/decision_tree/native/abc.py +1167 -0
- decision_graph/decision_tree/native/collection.py +134 -0
- decision_graph/decision_tree/native/node.py +521 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_action.c +16320 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_action.h +324 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_allocator_protocol.c +12473 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_bake.c +16485 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_bake.h +629 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_collections.c +17381 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_collections.h +692 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_const.c +20815 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_const.h +395 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_edge.c +20696 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_edge.h +511 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_eval.h +848 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_expr.c +22065 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_expr.h +1900 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_hierarchy.c +23267 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_hierarchy.h +731 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_logic_group.c +22434 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_logic_group.h +1226 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_node.c +28385 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_node.h +2660 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_reconstruct.c +15955 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_var.c +16881 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_var.h +2054 -0
- decision_graph/includes/decision_graph/decision_tree/capi/c_abc.c +59196 -0
- decision_graph/includes/decision_graph/decision_tree/capi/c_collection.c +19967 -0
- decision_graph/includes/decision_graph/decision_tree/capi/c_node.c +39229 -0
- decision_graph/logic_group/__init__.py +25 -0
- decision_graph/logic_group/base.py +100 -0
- decision_graph/logic_group/pending_request.py +384 -0
- decision_graph/webui/__init__.py +24 -0
- decision_graph/webui/bake/__init__.py +65 -0
- decision_graph/webui/bake/app.py +488 -0
- decision_graph/webui/bake/static/d3.v7.min.js +2 -0
- decision_graph/webui/bake/static/script.js +1440 -0
- decision_graph/webui/bake/static/style.css +920 -0
- decision_graph/webui/bake/templates/index.html +183 -0
- decision_graph/webui/bake/templates/offline.html +184 -0
- decision_graph/webui/capi/__init__.py +54 -0
- decision_graph/webui/capi/app.py +366 -0
- decision_graph/webui/capi/main.py +53 -0
- decision_graph/webui/capi/static/d3.v7.min.js +2 -0
- decision_graph/webui/capi/static/script.js +1026 -0
- decision_graph/webui/capi/static/style.css +656 -0
- decision_graph/webui/capi/templates/index.html +93 -0
- decision_graph/webui/capi/templates/offline.html +94 -0
- decision_graph/webui/native/__init__.py +54 -0
- decision_graph/webui/native/app.py +366 -0
- decision_graph/webui/native/main.py +53 -0
- decision_graph/webui/native/static/d3.v7.min.js +2 -0
- decision_graph/webui/native/static/script.js +1026 -0
- decision_graph/webui/native/static/style.css +656 -0
- decision_graph/webui/native/templates/index.html +93 -0
- decision_graph/webui/native/templates/offline.html +94 -0
- pydecisiongraph-0.3.2.post1.dist-info/METADATA +136 -0
- pydecisiongraph-0.3.2.post1.dist-info/RECORD +148 -0
- pydecisiongraph-0.3.2.post1.dist-info/WHEEL +5 -0
- pydecisiongraph-0.3.2.post1.dist-info/licenses/LICENSE +373 -0
- pydecisiongraph-0.3.2.post1.dist-info/top_level.txt +2 -0
- tests/bake_c_evaluator.cp315t-win_amd64.pyd +0 -0
- tests/bake_eval_probe.cp315t-win_amd64.pyd +0 -0
|
@@ -0,0 +1,2054 @@
|
|
|
1
|
+
#ifndef C_DCG_BAKE_VAR_H
|
|
2
|
+
#define C_DCG_BAKE_VAR_H
|
|
3
|
+
|
|
4
|
+
#include <stdbool.h>
|
|
5
|
+
#include <stddef.h>
|
|
6
|
+
#include <stdint.h>
|
|
7
|
+
#include <stdio.h>
|
|
8
|
+
#include <stdlib.h>
|
|
9
|
+
#include <string.h>
|
|
10
|
+
#include <sys/types.h>
|
|
11
|
+
|
|
12
|
+
#include <cbase/allocator_protocol/c_allocator_protocol.h>
|
|
13
|
+
|
|
14
|
+
#ifdef _WIN32
|
|
15
|
+
#include <BaseTsd.h>
|
|
16
|
+
typedef SSIZE_T ssize_t; // MSVC lacks the POSIX ssize_t; every header below this one names it.
|
|
17
|
+
#endif
|
|
18
|
+
|
|
19
|
+
// ========== Constants ==========
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @brief Compile-time switch for the session time/date payload types.
|
|
23
|
+
*
|
|
24
|
+
* The session types (session_time_t / session_date_t / session_datetime_t)
|
|
25
|
+
* live in PyAlgoEngine's exchange_profile header. The bake module is
|
|
26
|
+
* deliberately self-contained, so the dependency is OFF by default and
|
|
27
|
+
* `as_ptr` is the portable accessor for the TIME / DATE / DATETIME tags.
|
|
28
|
+
*
|
|
29
|
+
* Every union member involved is pointer-sized, so enabling this switch
|
|
30
|
+
* never changes the struct ABI - it only adds source-level field names.
|
|
31
|
+
*/
|
|
32
|
+
#ifndef DCG_VAR_HAS_SESSION_TIME
|
|
33
|
+
#define DCG_VAR_HAS_SESSION_TIME 0
|
|
34
|
+
#endif
|
|
35
|
+
|
|
36
|
+
#if DCG_VAR_HAS_SESSION_TIME
|
|
37
|
+
#include <algo_engine/exchange_profile/c_ex_profile_base.h>
|
|
38
|
+
#endif
|
|
39
|
+
|
|
40
|
+
/** Capacity of the local formatting buffer used by c_dcg_var_print. */
|
|
41
|
+
#ifndef DCG_VAR_STRING_MAXLEN
|
|
42
|
+
#define DCG_VAR_STRING_MAXLEN 128
|
|
43
|
+
#endif
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @brief Vigilant mode: refuse a read the value's tag cannot answer.
|
|
47
|
+
*
|
|
48
|
+
* Two reads have no correct value to return: one asked for a type the value does
|
|
49
|
+
* not carry, and one through a reference that points at nothing. A 0.0 or a NULL
|
|
50
|
+
* would read as a real payload and let the mistake travel to wherever it
|
|
51
|
+
* surfaces next. In vigilant mode (the default) both print the reader and the
|
|
52
|
+
* tag to stderr and abort, so the mistake lands where it was made. Define this
|
|
53
|
+
* to 0 to have the readers hand back their empty value instead.
|
|
54
|
+
*/
|
|
55
|
+
#ifndef DCG_VIGILANT
|
|
56
|
+
#define DCG_VIGILANT 1
|
|
57
|
+
#endif
|
|
58
|
+
|
|
59
|
+
// ========== Structs ==========
|
|
60
|
+
|
|
61
|
+
// clang-format off
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @brief Module-wide return codes for the bake layer (c_var / c_edge / c_node).
|
|
65
|
+
*
|
|
66
|
+
* 0 is success, everything else is negative. Functions that have no
|
|
67
|
+
* exceptional state return their value directly (predicates return bool,
|
|
68
|
+
* counts return size_t) - this enum is only for the error-prone ones.
|
|
69
|
+
*/
|
|
70
|
+
typedef enum dcg_ret_code {
|
|
71
|
+
DCG_OK = 0, // Success.
|
|
72
|
+
DCG_ERR_INVALID_ARG = -1, // NULL / out-of-range argument.
|
|
73
|
+
DCG_ERR_INVALID_BUF = -2, // Buf is not a block start / not zeroed.
|
|
74
|
+
DCG_ERR_OOM = -3, // Allocation failed.
|
|
75
|
+
DCG_ERR_NOT_FOUND = -4, // Lookup miss.
|
|
76
|
+
DCG_ERR_FULL = -5, // Caller-provided buffer is too small.
|
|
77
|
+
DCG_ERR_BAD_CAST = -6, // Value tags are not convertible.
|
|
78
|
+
DCG_ERR_FORMAT = -7, // Formatting failed.
|
|
79
|
+
DCG_ERR_TYPE = -8, // Node/condition type does not allow the operation.
|
|
80
|
+
DCG_ERR_CYCLE = -9, // Operation would create a parent/child cycle.
|
|
81
|
+
DCG_ERR_BUSY = -10, // Object is in a state that forbids the operation.
|
|
82
|
+
DCG_ERR_DUPLICATE = -11, // The edge condition is already registered on the parent.
|
|
83
|
+
DCG_ERR_EDGE = -12, // The edge condition is not acceptable for this parent.
|
|
84
|
+
DCG_ERR_UNRESOLVED = -13, // No condition could be inferred for the edge.
|
|
85
|
+
DCG_ERR_RANGE = -14, // Index outside the container.
|
|
86
|
+
DCG_ERR_HOOK = -15, // An eval hook failed - the failure it reported is the hook's own.
|
|
87
|
+
DCG_ERR_NO_MATCH = -16, // No edge matched the value and the node has no else arm.
|
|
88
|
+
DCG_ERR_MATH = -17, // Operator operands are outside the operator's domain.
|
|
89
|
+
DCG_ERR_UNBOUND = -18 // The value a node reads has no type yet - nothing has landed in it.
|
|
90
|
+
} dcg_ret_code;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @brief How a value reads as a number - the shape of the answer, not the value.
|
|
94
|
+
*/
|
|
95
|
+
typedef enum dcg_var_numeric {
|
|
96
|
+
VAR_NUMERIC_NONE = 0, // Not a number.
|
|
97
|
+
VAR_NUMERIC_INT = 1, // bool, int or offset: a whole number.
|
|
98
|
+
VAR_NUMERIC_DOUBLE = 2 // double.
|
|
99
|
+
} dcg_var_numeric;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @brief Field layout of a value tag (see dcg_var_type).
|
|
103
|
+
*/
|
|
104
|
+
typedef enum dcg_var_type_mask {
|
|
105
|
+
VAR_TYPE_BASE_MASK = 0x00FF, // Extracts the referred-to tag of a value tag.
|
|
106
|
+
VAR_TYPE_REF_MASK = 0x0F00, // Extracts the reference level of a value tag.
|
|
107
|
+
VAR_TYPE_REF_SHIFT = 8, // Bits to shift a masked level down to a count.
|
|
108
|
+
VAR_TYPE_REF_LEVEL1 = 0x0100, // Level of a _REF tag: one hop to the value.
|
|
109
|
+
VAR_TYPE_REF_LEVEL2 = 0x0200 // Level of a _REF_REF tag: two hops to it.
|
|
110
|
+
} dcg_var_type_mask;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @brief Value tag of a dcg_var_t.
|
|
114
|
+
*
|
|
115
|
+
* The tag is what the evaluator dispatches on; the payload is read through
|
|
116
|
+
* the matching dcg_var_variant member.
|
|
117
|
+
*
|
|
118
|
+
* A tag with no reference bits names a plain value; a tag that has them names a
|
|
119
|
+
* REFERENCE to another value. `as_ref` is the address of the referred-to value at
|
|
120
|
+
* level 1, the address of that address at level 2 - one more star per rung - and
|
|
121
|
+
* the level in VAR_TYPE_REF_MASK is how many there are. The level is a mask and a
|
|
122
|
+
* shift rather than a pair of special cases, so references nest as deep as they
|
|
123
|
+
* are made to: a reference to a double is a _REF holding a double*, a reference
|
|
124
|
+
* to that reference is a _REF_REF holding a double**, and the one after it is
|
|
125
|
+
* level 3. At every level `dtype & VAR_TYPE_BASE_MASK` still names what sits at
|
|
126
|
+
* the end of the walk, which is the value the readers hand back.
|
|
127
|
+
*
|
|
128
|
+
* A reference owns nothing: the value it refers to belongs to whoever set it up,
|
|
129
|
+
* must outlive the reference, must keep the shape the level assumes, and must
|
|
130
|
+
* not be cleared to NULL while a reference to it is read.
|
|
131
|
+
*/
|
|
132
|
+
/* A node, named but never dereferenced: the value layer sits BELOW the node
|
|
133
|
+
* layer, so all it needs of one is the address - an action's slot holds the node
|
|
134
|
+
* it stands for (VAR_TYPE_NODE). The definition is c_node.h's; this is the
|
|
135
|
+
* forward declaration that lets the pointer cross with a type on it, exactly as
|
|
136
|
+
* c_node.h itself does for a logic group. */
|
|
137
|
+
typedef struct dcg_node dcg_node;
|
|
138
|
+
|
|
139
|
+
typedef enum dcg_var_type {
|
|
140
|
+
VAR_TYPE_RAW_PTR = 0, // Opaque pointer payload (as_ptr).
|
|
141
|
+
VAR_TYPE_STRING = 1, // NUL-terminated string payload (as_string).
|
|
142
|
+
VAR_TYPE_BOOL = 2, // Boolean payload (as_bool).
|
|
143
|
+
VAR_TYPE_DOUBLE = 3, // Double payload (as_double).
|
|
144
|
+
VAR_TYPE_INT = 4, // Signed integer payload (as_int).
|
|
145
|
+
VAR_TYPE_OFFSET = 5, // Signed offset payload (as_offset).
|
|
146
|
+
VAR_TYPE_TIME = 6, // Session time payload (as_ptr, or as_time).
|
|
147
|
+
VAR_TYPE_DATE = 7, // Session date payload (as_ptr, or as_date).
|
|
148
|
+
VAR_TYPE_DATETIME = 8, // Session datetime payload (as_ptr, or as_datetime).
|
|
149
|
+
VAR_TYPE_D_VECTOR = 9, // Contiguous double vector (as_dvector).
|
|
150
|
+
VAR_TYPE_D_MATRIX = 10, // Contiguous double matrix (as_dmatrix).
|
|
151
|
+
VAR_TYPE_NODE = 11, // A dcg_node* (as_node). Named, never dereferenced - see the forward declaration above.
|
|
152
|
+
VAR_TYPE_RESERVED = 12,
|
|
153
|
+
|
|
154
|
+
// One hop: as_ref is the address of the slot holding the value. The tag is
|
|
155
|
+
// cast to int where the level bits (dcg_var_type_mask) OR into it: the two
|
|
156
|
+
// enums are different types, and MSVC rejects the mix without the cast
|
|
157
|
+
// (C5287) - the same cast the mask predicates below already carry.
|
|
158
|
+
VAR_TYPE_RAW_PTR_REF = VAR_TYPE_REF_LEVEL1 | (int) VAR_TYPE_RAW_PTR,
|
|
159
|
+
VAR_TYPE_STRING_REF = VAR_TYPE_REF_LEVEL1 | (int) VAR_TYPE_STRING,
|
|
160
|
+
VAR_TYPE_BOOL_REF = VAR_TYPE_REF_LEVEL1 | (int) VAR_TYPE_BOOL,
|
|
161
|
+
VAR_TYPE_DOUBLE_REF = VAR_TYPE_REF_LEVEL1 | (int) VAR_TYPE_DOUBLE,
|
|
162
|
+
VAR_TYPE_INT_REF = VAR_TYPE_REF_LEVEL1 | (int) VAR_TYPE_INT,
|
|
163
|
+
VAR_TYPE_OFFSET_REF = VAR_TYPE_REF_LEVEL1 | (int) VAR_TYPE_OFFSET,
|
|
164
|
+
VAR_TYPE_TIME_REF = VAR_TYPE_REF_LEVEL1 | (int) VAR_TYPE_TIME,
|
|
165
|
+
VAR_TYPE_DATE_REF = VAR_TYPE_REF_LEVEL1 | (int) VAR_TYPE_DATE,
|
|
166
|
+
VAR_TYPE_DATETIME_REF = VAR_TYPE_REF_LEVEL1 | (int) VAR_TYPE_DATETIME,
|
|
167
|
+
VAR_TYPE_D_VECTOR_REF = VAR_TYPE_REF_LEVEL1 | (int) VAR_TYPE_D_VECTOR,
|
|
168
|
+
VAR_TYPE_D_MATRIX_REF = VAR_TYPE_REF_LEVEL1 | (int) VAR_TYPE_D_MATRIX,
|
|
169
|
+
VAR_TYPE_NODE_REF = VAR_TYPE_REF_LEVEL1 | (int) VAR_TYPE_NODE,
|
|
170
|
+
VAR_TYPE_INFERRED = VAR_TYPE_REF_LEVEL1 | (int) VAR_TYPE_RESERVED,
|
|
171
|
+
|
|
172
|
+
// Two hops: as_ref is the address of the slot holding the one-hop reference.
|
|
173
|
+
VAR_TYPE_RAW_PTR_REF_REF = VAR_TYPE_REF_LEVEL2 | (int) VAR_TYPE_RAW_PTR,
|
|
174
|
+
VAR_TYPE_STRING_REF_REF = VAR_TYPE_REF_LEVEL2 | (int) VAR_TYPE_STRING,
|
|
175
|
+
VAR_TYPE_BOOL_REF_REF = VAR_TYPE_REF_LEVEL2 | (int) VAR_TYPE_BOOL,
|
|
176
|
+
VAR_TYPE_DOUBLE_REF_REF = VAR_TYPE_REF_LEVEL2 | (int) VAR_TYPE_DOUBLE,
|
|
177
|
+
VAR_TYPE_INT_REF_REF = VAR_TYPE_REF_LEVEL2 | (int) VAR_TYPE_INT,
|
|
178
|
+
VAR_TYPE_OFFSET_REF_REF = VAR_TYPE_REF_LEVEL2 | (int) VAR_TYPE_OFFSET,
|
|
179
|
+
VAR_TYPE_TIME_REF_REF = VAR_TYPE_REF_LEVEL2 | (int) VAR_TYPE_TIME,
|
|
180
|
+
VAR_TYPE_DATE_REF_REF = VAR_TYPE_REF_LEVEL2 | (int) VAR_TYPE_DATE,
|
|
181
|
+
VAR_TYPE_DATETIME_REF_REF = VAR_TYPE_REF_LEVEL2 | (int) VAR_TYPE_DATETIME,
|
|
182
|
+
VAR_TYPE_D_VECTOR_REF_REF = VAR_TYPE_REF_LEVEL2 | (int) VAR_TYPE_D_VECTOR,
|
|
183
|
+
VAR_TYPE_D_MATRIX_REF_REF = VAR_TYPE_REF_LEVEL2 | (int) VAR_TYPE_D_MATRIX,
|
|
184
|
+
VAR_TYPE_NODE_REF_REF = VAR_TYPE_REF_LEVEL2 | (int) VAR_TYPE_NODE
|
|
185
|
+
} dcg_var_type;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @brief A contiguous vector of doubles.
|
|
189
|
+
*
|
|
190
|
+
* `data` is a block of its own, allocated as a child of the vector, so freeing
|
|
191
|
+
* the vector (c_dcg_d_vector_free) frees the data with it - the payload can
|
|
192
|
+
* never be orphaned.
|
|
193
|
+
*
|
|
194
|
+
* A var embeds the same struct (dcg_var_t.container), where `data` is the
|
|
195
|
+
* caller's buffer instead; the layout is shared so a var's vector can be read
|
|
196
|
+
* through the very same helpers.
|
|
197
|
+
*/
|
|
198
|
+
typedef struct dcg_d_vector_t {
|
|
199
|
+
double* data; // OWNED when standalone - nested block of n doubles. Borrowed inside a var.
|
|
200
|
+
size_t n; // Number of doubles.
|
|
201
|
+
} dcg_d_vector_t;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* @brief A contiguous matrix of doubles, with its layout flag.
|
|
205
|
+
*
|
|
206
|
+
* The layout decides how (r, c) maps onto the flat buffer:
|
|
207
|
+
* - row_major -> data[r * n_cols + c]
|
|
208
|
+
* - column-major -> data[c * n_rows + r]
|
|
209
|
+
*
|
|
210
|
+
* As with the vector, `data` is a child block of a standalone matrix and dies
|
|
211
|
+
* with it; inside a var it is the caller's buffer.
|
|
212
|
+
*/
|
|
213
|
+
typedef struct dcg_d_matrix_t {
|
|
214
|
+
double* data; // OWNED when standalone - nested block. Borrowed inside a var.
|
|
215
|
+
size_t n_rows; // Number of rows.
|
|
216
|
+
size_t n_cols; // Number of columns.
|
|
217
|
+
bool row_major; // true: row-major, false: column-major.
|
|
218
|
+
} dcg_d_matrix_t;
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* @brief Payload storage of a dcg_var_t.
|
|
222
|
+
*
|
|
223
|
+
* Every member is pointer-sized or smaller, so the union is 8 bytes on LP64
|
|
224
|
+
* whatever DCG_VAR_HAS_SESSION_TIME is. A container tag carries a pointer to
|
|
225
|
+
* its shape-carrying struct, which the var OWNS: it is allocated as a child
|
|
226
|
+
* block of the var, so freeing the var frees the container, its data block
|
|
227
|
+
* and the var in one c_ap_free_owned() walk.
|
|
228
|
+
*
|
|
229
|
+
* A reference tag carries a POINTER to the referred-to slot in `as_ref`, and the
|
|
230
|
+
* level says how many stars it has: the slot holds the value at level 1, the
|
|
231
|
+
* pointer to it at level 2, a pointer to that pointer at level 3. The tag is
|
|
232
|
+
* therefore all a reader needs - the level is the number of hops, the base is
|
|
233
|
+
* what sits at the end of them - and nothing in the payload is ever read as a
|
|
234
|
+
* value header to find the way.
|
|
235
|
+
*/
|
|
236
|
+
typedef union dcg_var_variant {
|
|
237
|
+
void* as_ptr; // VAR_TYPE_RAW_PTR, TIME, DATE, DATETIME.
|
|
238
|
+
const char* as_string; // VAR_TYPE_STRING. NOT owned by the var - see c_dcg_var_new_string.
|
|
239
|
+
bool as_bool; // VAR_TYPE_BOOL.
|
|
240
|
+
double as_double; // VAR_TYPE_DOUBLE.
|
|
241
|
+
ssize_t as_int; // VAR_TYPE_INT.
|
|
242
|
+
ssize_t as_offset; // VAR_TYPE_OFFSET.
|
|
243
|
+
uint64_t as_bits; // Raw 64-bit view of any scalar payload.
|
|
244
|
+
dcg_d_vector_t* as_dvector; // VAR_TYPE_D_VECTOR. OWNED - child block of the var.
|
|
245
|
+
dcg_d_matrix_t* as_dmatrix; // VAR_TYPE_D_MATRIX. OWNED - child block of the var.
|
|
246
|
+
const dcg_node* as_node; // VAR_TYPE_NODE. The node, named but never read through - see VAR_TYPE_NODE.
|
|
247
|
+
const void* as_ref; // Any _REF tag: the referred-to slot - T* at level 1, T** at level 2, a star per rung.
|
|
248
|
+
#if DCG_VAR_HAS_SESSION_TIME
|
|
249
|
+
session_time_t* as_time; // VAR_TYPE_TIME.
|
|
250
|
+
session_date_t* as_date; // VAR_TYPE_DATE.
|
|
251
|
+
session_datetime_t* as_datetime; // VAR_TYPE_DATETIME.
|
|
252
|
+
#endif
|
|
253
|
+
} dcg_var_variant;
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* @brief A tagged value - the unit of data flowing through the node graph.
|
|
257
|
+
*
|
|
258
|
+
* Two ways to make one, and the difference is ownership:
|
|
259
|
+
*
|
|
260
|
+
* - c_dcg_var_new_*() allocates the var (and, where there is one, its
|
|
261
|
+
* payload) through the allocator protocol, nesting the payload under the
|
|
262
|
+
* var - c_dcg_var_free() then releases the whole thing in one call and
|
|
263
|
+
* nothing can leak.
|
|
264
|
+
* - c_dcg_var_init_*() populates a buffer the caller owns, most often a
|
|
265
|
+
* node's `out` field. Only the container initializers allocate, and only
|
|
266
|
+
* the shape struct: a string payload stays borrowed, and a container
|
|
267
|
+
* either wraps the caller's buffer or snapshots it.
|
|
268
|
+
*
|
|
269
|
+
* A container tag points at a dcg_d_vector_t / dcg_d_matrix_t that the var
|
|
270
|
+
* owns as a child block, so the shape travels with the value and one free
|
|
271
|
+
* releases the var, the container and (when the var allocated it) its data.
|
|
272
|
+
* A reference tag owns nothing at all: it is an address of a slot that belongs
|
|
273
|
+
* to whoever set it up, and it reads that slot every time it is read.
|
|
274
|
+
*/
|
|
275
|
+
typedef struct dcg_var_t {
|
|
276
|
+
dcg_var_type dtype; // Active tag; selects the variant member.
|
|
277
|
+
dcg_var_variant value; // Payload.
|
|
278
|
+
} dcg_var_t;
|
|
279
|
+
|
|
280
|
+
// clang-format on
|
|
281
|
+
|
|
282
|
+
// ========== Forward Declarations ==========
|
|
283
|
+
|
|
284
|
+
// Containers (contiguous double payloads with their own metadata)
|
|
285
|
+
static inline dcg_d_vector_t* c_dcg_d_vector_new(size_t n, allocator_protocol* allocator);
|
|
286
|
+
static inline dcg_d_vector_t* c_dcg_d_vector_new_child(size_t n, allocator_protocol* allocator, const void* parent);
|
|
287
|
+
static inline void c_dcg_d_vector_dealloc(dcg_d_vector_t* vector);
|
|
288
|
+
static inline void c_dcg_d_vector_free(dcg_d_vector_t* vector);
|
|
289
|
+
static inline double* c_dcg_d_vector_data(const dcg_d_vector_t* vector);
|
|
290
|
+
static inline size_t c_dcg_d_vector_size(const dcg_d_vector_t* vector);
|
|
291
|
+
static inline double c_dcg_d_vector_at(const dcg_d_vector_t* vector, size_t index);
|
|
292
|
+
static inline int c_dcg_d_vector_set(dcg_d_vector_t* vector, size_t index, double value);
|
|
293
|
+
|
|
294
|
+
static inline dcg_d_matrix_t* c_dcg_d_matrix_new(size_t n_rows, size_t n_cols, bool row_major, allocator_protocol* allocator);
|
|
295
|
+
static inline dcg_d_matrix_t* c_dcg_d_matrix_new_child(size_t n_rows, size_t n_cols, bool row_major, allocator_protocol* allocator, const void* parent);
|
|
296
|
+
static inline void c_dcg_d_matrix_dealloc(dcg_d_matrix_t* matrix);
|
|
297
|
+
static inline void c_dcg_d_matrix_free(dcg_d_matrix_t* matrix);
|
|
298
|
+
static inline double* c_dcg_d_matrix_data(const dcg_d_matrix_t* matrix);
|
|
299
|
+
static inline size_t c_dcg_d_matrix_rows(const dcg_d_matrix_t* matrix);
|
|
300
|
+
static inline size_t c_dcg_d_matrix_cols(const dcg_d_matrix_t* matrix);
|
|
301
|
+
static inline double c_dcg_d_matrix_at(const dcg_d_matrix_t* matrix, size_t row, size_t col);
|
|
302
|
+
static inline int c_dcg_d_matrix_set(dcg_d_matrix_t* matrix, size_t row, size_t col, double value);
|
|
303
|
+
|
|
304
|
+
// Lifecycle (allocating - the var owns its payload)
|
|
305
|
+
static inline dcg_var_t* c_dcg_var_new(allocator_protocol* allocator);
|
|
306
|
+
static inline dcg_var_t* c_dcg_var_new_bool(bool value, allocator_protocol* allocator);
|
|
307
|
+
static inline dcg_var_t* c_dcg_var_new_double(double value, allocator_protocol* allocator);
|
|
308
|
+
static inline dcg_var_t* c_dcg_var_new_int(ssize_t value, allocator_protocol* allocator);
|
|
309
|
+
static inline dcg_var_t* c_dcg_var_new_offset(ssize_t value, allocator_protocol* allocator);
|
|
310
|
+
static inline dcg_var_t* c_dcg_var_new_string(const char* value, allocator_protocol* allocator);
|
|
311
|
+
static inline dcg_var_t* c_dcg_var_new_ptr(void* value, allocator_protocol* allocator);
|
|
312
|
+
static inline dcg_var_t* c_dcg_var_new_dvector(size_t n, allocator_protocol* allocator);
|
|
313
|
+
static inline dcg_var_t* c_dcg_var_new_dmatrix(size_t n_rows, size_t n_cols, bool row_major, allocator_protocol* allocator);
|
|
314
|
+
static inline dcg_var_t* c_dcg_var_new_ref(const dcg_var_t* src, allocator_protocol* allocator);
|
|
315
|
+
static inline void c_dcg_var_dealloc(dcg_var_t* var);
|
|
316
|
+
static inline void c_dcg_var_free(dcg_var_t* var);
|
|
317
|
+
|
|
318
|
+
// Population (caller-owned buffer - never allocates)
|
|
319
|
+
static inline int c_dcg_var_init(dcg_var_t* var);
|
|
320
|
+
static inline int c_dcg_var_init_reserved(dcg_var_t* var);
|
|
321
|
+
static inline int c_dcg_var_init_bool(dcg_var_t* var, bool value);
|
|
322
|
+
static inline int c_dcg_var_init_double(dcg_var_t* var, double value);
|
|
323
|
+
static inline int c_dcg_var_init_int(dcg_var_t* var, ssize_t value);
|
|
324
|
+
static inline int c_dcg_var_init_offset(dcg_var_t* var, ssize_t value);
|
|
325
|
+
static inline int c_dcg_var_init_string(dcg_var_t* var, const char* value);
|
|
326
|
+
static inline int c_dcg_var_init_ref_raw(dcg_var_t* var, dcg_var_type dtype, const void* ref);
|
|
327
|
+
static inline int c_dcg_var_init_ref(dcg_var_t* var, const dcg_var_t* src);
|
|
328
|
+
static inline int c_dcg_var_init_ptr(dcg_var_t* var, void* value);
|
|
329
|
+
static inline int c_dcg_var_init_node(dcg_var_t* var, const dcg_node* node);
|
|
330
|
+
static inline int c_dcg_var_init_dvector(dcg_var_t* var, double* value, size_t n, bool copy, allocator_protocol* allocator);
|
|
331
|
+
static inline int c_dcg_var_init_dmatrix(dcg_var_t* var, double* value, size_t n_rows, size_t n_cols, bool row_major, bool copy, allocator_protocol* allocator);
|
|
332
|
+
|
|
333
|
+
// References
|
|
334
|
+
static inline int c_dcg_var_ref_level(dcg_var_type dtype);
|
|
335
|
+
static inline bool c_dcg_var_is_ref(dcg_var_type dtype);
|
|
336
|
+
static inline dcg_var_type c_dcg_var_ref_base(dcg_var_type dtype);
|
|
337
|
+
|
|
338
|
+
// Introspection
|
|
339
|
+
static inline const char* c_dcg_ret_code_name(dcg_ret_code code);
|
|
340
|
+
static inline const char* c_dcg_var_type_name(dcg_var_type dtype);
|
|
341
|
+
static inline bool c_dcg_var_is_numeric(const dcg_var_t* var);
|
|
342
|
+
static inline dcg_var_numeric c_dcg_var_numeric_of(const dcg_var_t* var);
|
|
343
|
+
static inline bool c_dcg_var_is_container(const dcg_var_t* var);
|
|
344
|
+
static inline bool c_dcg_var_is_null(const dcg_var_t* var);
|
|
345
|
+
static inline bool c_dcg_var_is_truthy(const dcg_var_t* var);
|
|
346
|
+
static inline bool c_dcg_var_equals(const dcg_var_t* lhs, const dcg_var_t* rhs);
|
|
347
|
+
|
|
348
|
+
// Reading (numeric readers coerce; the rest read their tag only)
|
|
349
|
+
static inline void c_dcg_var_vigilant_abort(const char* getter, dcg_var_type dtype, const char* what);
|
|
350
|
+
static inline const void* c_dcg_var_ref_slot(const dcg_var_t* var, const char* getter);
|
|
351
|
+
static inline bool c_dcg_var_as_bool(const dcg_var_t* var);
|
|
352
|
+
static inline double c_dcg_var_as_double(const dcg_var_t* var);
|
|
353
|
+
static inline ssize_t c_dcg_var_as_int(const dcg_var_t* var);
|
|
354
|
+
static inline ssize_t c_dcg_var_as_offset(const dcg_var_t* var);
|
|
355
|
+
static inline const char* c_dcg_var_as_string(const dcg_var_t* var);
|
|
356
|
+
static inline const void* c_dcg_var_as_ref(const dcg_var_t* var);
|
|
357
|
+
static inline void* c_dcg_var_as_ptr(const dcg_var_t* var);
|
|
358
|
+
static inline const dcg_node* c_dcg_var_as_node(const dcg_var_t* var);
|
|
359
|
+
static inline dcg_d_vector_t* c_dcg_var_as_dvector(const dcg_var_t* var);
|
|
360
|
+
static inline dcg_d_matrix_t* c_dcg_var_as_dmatrix(const dcg_var_t* var);
|
|
361
|
+
static inline int c_dcg_var_cast(dcg_var_t* out, const dcg_var_t* var, dcg_var_type dtype);
|
|
362
|
+
static inline void c_dcg_var_snapshot(dcg_var_t* snapshot, const dcg_var_t* slot);
|
|
363
|
+
|
|
364
|
+
// Output
|
|
365
|
+
static inline int c_dcg_var_format(const dcg_var_t* var, char* out, size_t cap);
|
|
366
|
+
static inline int c_dcg_var_print(const dcg_var_t* var, FILE* stream);
|
|
367
|
+
|
|
368
|
+
// ========== Containers ==========
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* @brief Allocate a vector of n doubles, zeroed.
|
|
372
|
+
*
|
|
373
|
+
* The data block is allocated as a CHILD of the vector, so it is released
|
|
374
|
+
* together with it and can never be orphaned.
|
|
375
|
+
*
|
|
376
|
+
* @param n Number of elements.
|
|
377
|
+
* @param allocator Allocator for both blocks; NULL falls back to the plain heap.
|
|
378
|
+
* @return The vector, or NULL on OOM.
|
|
379
|
+
*/
|
|
380
|
+
static inline dcg_d_vector_t* c_dcg_d_vector_new(size_t n, allocator_protocol* allocator) {
|
|
381
|
+
return c_dcg_d_vector_new_child(n, allocator, NULL);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* @brief Allocate a vector as a child of another block.
|
|
386
|
+
*
|
|
387
|
+
* @param n Number of elements.
|
|
388
|
+
* @param allocator Allocator for both blocks; NULL derives it from the parent.
|
|
389
|
+
* @param parent Owning block start, or NULL for an independent vector.
|
|
390
|
+
* @return The vector, or NULL on OOM.
|
|
391
|
+
*/
|
|
392
|
+
static inline dcg_d_vector_t* c_dcg_d_vector_new_child(size_t n, allocator_protocol* allocator, const void* parent) {
|
|
393
|
+
dcg_d_vector_t* vector = (dcg_d_vector_t*) c_ap_alloc_child(sizeof(dcg_d_vector_t), allocator, parent);
|
|
394
|
+
if (!vector) return NULL;
|
|
395
|
+
|
|
396
|
+
vector->data = (double*) c_ap_alloc_child(n * sizeof(double), NULL, vector);
|
|
397
|
+
if (!vector->data) {
|
|
398
|
+
c_ap_free_owned(vector);
|
|
399
|
+
return NULL;
|
|
400
|
+
}
|
|
401
|
+
vector->n = n;
|
|
402
|
+
return vector;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
static inline void c_dcg_d_vector_dealloc(dcg_d_vector_t* vector) {
|
|
406
|
+
if (!vector) return;
|
|
407
|
+
c_ap_free_owned(vector->data);
|
|
408
|
+
memset(vector, 0, sizeof(*vector));
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* @brief Release a vector and its data.
|
|
413
|
+
*
|
|
414
|
+
* The clean half first, then the block.
|
|
415
|
+
*
|
|
416
|
+
* @param vector Vector to free (NULL-safe).
|
|
417
|
+
*/
|
|
418
|
+
static inline void c_dcg_d_vector_free(dcg_d_vector_t* vector) {
|
|
419
|
+
if (!vector) return;
|
|
420
|
+
c_dcg_d_vector_dealloc(vector);
|
|
421
|
+
c_ap_free_owned(vector);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* @brief The vector's contiguous data.
|
|
426
|
+
*
|
|
427
|
+
* @param vector Vector to inspect (NULL-safe).
|
|
428
|
+
* @return The data block, or NULL.
|
|
429
|
+
*/
|
|
430
|
+
static inline double* c_dcg_d_vector_data(const dcg_d_vector_t* vector) {
|
|
431
|
+
if (!vector) return NULL;
|
|
432
|
+
return vector->data;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* @brief Number of elements in a vector.
|
|
437
|
+
*
|
|
438
|
+
* @param vector Vector to inspect (NULL-safe).
|
|
439
|
+
* @return The element count, or 0.
|
|
440
|
+
*/
|
|
441
|
+
static inline size_t c_dcg_d_vector_size(const dcg_d_vector_t* vector) {
|
|
442
|
+
if (!vector) return 0;
|
|
443
|
+
return vector->n;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* @brief Read one element of a vector.
|
|
448
|
+
*
|
|
449
|
+
* @param vector Vector to read (NULL-safe).
|
|
450
|
+
* @param index Element index.
|
|
451
|
+
* @return The element, or 0.0 when the index is out of range.
|
|
452
|
+
*/
|
|
453
|
+
static inline double c_dcg_d_vector_at(const dcg_d_vector_t* vector, size_t index) {
|
|
454
|
+
if (!vector || !vector->data || index >= vector->n) return 0.0;
|
|
455
|
+
return vector->data[index];
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* @brief Write one element of a vector.
|
|
460
|
+
*
|
|
461
|
+
* @param vector Vector to write (NULL-safe).
|
|
462
|
+
* @param index Element index.
|
|
463
|
+
* @param value Value to store.
|
|
464
|
+
* @return DCG_OK, or DCG_ERR_INVALID_ARG / DCG_ERR_RANGE.
|
|
465
|
+
*/
|
|
466
|
+
static inline int c_dcg_d_vector_set(dcg_d_vector_t* vector, size_t index, double value) {
|
|
467
|
+
if (!vector || !vector->data) return DCG_ERR_INVALID_ARG;
|
|
468
|
+
if (index >= vector->n) return DCG_ERR_RANGE;
|
|
469
|
+
vector->data[index] = value;
|
|
470
|
+
return DCG_OK;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* @brief Allocate an (n_rows x n_cols) matrix of doubles, zeroed.
|
|
475
|
+
*
|
|
476
|
+
* @param n_rows Number of rows.
|
|
477
|
+
* @param n_cols Number of columns.
|
|
478
|
+
* @param row_major true for row-major, false for column-major.
|
|
479
|
+
* @param allocator Allocator for both blocks; NULL falls back to the plain heap.
|
|
480
|
+
* @return The matrix, or NULL on OOM.
|
|
481
|
+
*/
|
|
482
|
+
static inline dcg_d_matrix_t* c_dcg_d_matrix_new(size_t n_rows, size_t n_cols, bool row_major, allocator_protocol* allocator) {
|
|
483
|
+
return c_dcg_d_matrix_new_child(n_rows, n_cols, row_major, allocator, NULL);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* @brief Allocate a matrix as a child of another block.
|
|
488
|
+
*
|
|
489
|
+
* @param n_rows Number of rows.
|
|
490
|
+
* @param n_cols Number of columns.
|
|
491
|
+
* @param row_major true for row-major, false for column-major.
|
|
492
|
+
* @param allocator Allocator for both blocks; NULL derives it from the parent.
|
|
493
|
+
* @param parent Owning block start, or NULL for an independent matrix.
|
|
494
|
+
* @return The matrix, or NULL on OOM.
|
|
495
|
+
*/
|
|
496
|
+
static inline dcg_d_matrix_t* c_dcg_d_matrix_new_child(size_t n_rows, size_t n_cols, bool row_major, allocator_protocol* allocator, const void* parent) {
|
|
497
|
+
dcg_d_matrix_t* matrix = (dcg_d_matrix_t*) c_ap_alloc_child(sizeof(dcg_d_matrix_t), allocator, parent);
|
|
498
|
+
if (!matrix) return NULL;
|
|
499
|
+
|
|
500
|
+
matrix->data = (double*) c_ap_alloc_child(n_rows * n_cols * sizeof(double), NULL, matrix);
|
|
501
|
+
if (!matrix->data) {
|
|
502
|
+
c_ap_free_owned(matrix);
|
|
503
|
+
return NULL;
|
|
504
|
+
}
|
|
505
|
+
matrix->n_rows = n_rows;
|
|
506
|
+
matrix->n_cols = n_cols;
|
|
507
|
+
matrix->row_major = row_major;
|
|
508
|
+
return matrix;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
static inline void c_dcg_d_matrix_dealloc(dcg_d_matrix_t* matrix) {
|
|
512
|
+
if (!matrix) return;
|
|
513
|
+
c_ap_free_owned(matrix->data);
|
|
514
|
+
memset(matrix, 0, sizeof(*matrix));
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* @brief Release a matrix and its data.
|
|
519
|
+
*
|
|
520
|
+
* The clean half first, then the block.
|
|
521
|
+
*
|
|
522
|
+
* @param matrix Matrix to free (NULL-safe).
|
|
523
|
+
*/
|
|
524
|
+
static inline void c_dcg_d_matrix_free(dcg_d_matrix_t* matrix) {
|
|
525
|
+
if (!matrix) return;
|
|
526
|
+
c_dcg_d_matrix_dealloc(matrix);
|
|
527
|
+
c_ap_free_owned(matrix);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* @brief The matrix's contiguous, row-major data.
|
|
532
|
+
*
|
|
533
|
+
* @param matrix Matrix to inspect (NULL-safe).
|
|
534
|
+
* @return The data block, or NULL.
|
|
535
|
+
*/
|
|
536
|
+
static inline double* c_dcg_d_matrix_data(const dcg_d_matrix_t* matrix) {
|
|
537
|
+
if (!matrix) return NULL;
|
|
538
|
+
return matrix->data;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* @brief Number of rows.
|
|
543
|
+
*
|
|
544
|
+
* @param matrix Matrix to inspect (NULL-safe).
|
|
545
|
+
* @return The row count, or 0.
|
|
546
|
+
*/
|
|
547
|
+
static inline size_t c_dcg_d_matrix_rows(const dcg_d_matrix_t* matrix) {
|
|
548
|
+
if (!matrix) return 0;
|
|
549
|
+
return matrix->n_rows;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* @brief Number of columns.
|
|
554
|
+
*
|
|
555
|
+
* @param matrix Matrix to inspect (NULL-safe).
|
|
556
|
+
* @return The column count, or 0.
|
|
557
|
+
*/
|
|
558
|
+
static inline size_t c_dcg_d_matrix_cols(const dcg_d_matrix_t* matrix) {
|
|
559
|
+
if (!matrix) return 0;
|
|
560
|
+
return matrix->n_cols;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* @brief Read one matrix element.
|
|
565
|
+
*
|
|
566
|
+
* @param matrix Matrix to read (NULL-safe).
|
|
567
|
+
* @param row Row index.
|
|
568
|
+
* @param col Column index.
|
|
569
|
+
* @return The element, or 0.0 when the indices are out of range.
|
|
570
|
+
*/
|
|
571
|
+
static inline double c_dcg_d_matrix_at(const dcg_d_matrix_t* matrix, size_t row, size_t col) {
|
|
572
|
+
if (!matrix || !matrix->data) return 0.0;
|
|
573
|
+
if (row >= matrix->n_rows || col >= matrix->n_cols) return 0.0;
|
|
574
|
+
size_t index = matrix->row_major ? (row * matrix->n_cols + col) : (col * matrix->n_rows + row);
|
|
575
|
+
return matrix->data[index];
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* @brief Write one matrix element.
|
|
580
|
+
*
|
|
581
|
+
* @param matrix Matrix to write (NULL-safe).
|
|
582
|
+
* @param row Row index.
|
|
583
|
+
* @param col Column index.
|
|
584
|
+
* @param value Value to store.
|
|
585
|
+
* @return DCG_OK, or DCG_ERR_INVALID_ARG / DCG_ERR_RANGE.
|
|
586
|
+
*/
|
|
587
|
+
static inline int c_dcg_d_matrix_set(dcg_d_matrix_t* matrix, size_t row, size_t col, double value) {
|
|
588
|
+
if (!matrix || !matrix->data) return DCG_ERR_INVALID_ARG;
|
|
589
|
+
if (row >= matrix->n_rows || col >= matrix->n_cols) return DCG_ERR_RANGE;
|
|
590
|
+
size_t index = matrix->row_major ? (row * matrix->n_cols + col) : (col * matrix->n_rows + row);
|
|
591
|
+
matrix->data[index] = value;
|
|
592
|
+
return DCG_OK;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// ========== Lifecycle ==========
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* @brief Allocate a value, tagged RAW_PTR with a NULL payload.
|
|
599
|
+
*
|
|
600
|
+
* @param allocator Allocator for the block; NULL falls back to the plain heap.
|
|
601
|
+
* @return The value, or NULL on OOM.
|
|
602
|
+
*/
|
|
603
|
+
static inline dcg_var_t* c_dcg_var_new(allocator_protocol* allocator) {
|
|
604
|
+
dcg_var_t* var = (dcg_var_t*) c_ap_alloc(sizeof(dcg_var_t), allocator);
|
|
605
|
+
if (!var) return NULL;
|
|
606
|
+
c_dcg_var_init(var);
|
|
607
|
+
return var;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* @brief Allocate a bool-tagged value.
|
|
612
|
+
*
|
|
613
|
+
* @param value Payload.
|
|
614
|
+
* @param allocator Allocator for the block (may be NULL).
|
|
615
|
+
* @return The value, or NULL on OOM.
|
|
616
|
+
*/
|
|
617
|
+
static inline dcg_var_t* c_dcg_var_new_bool(bool value, allocator_protocol* allocator) {
|
|
618
|
+
dcg_var_t* var = c_dcg_var_new(allocator);
|
|
619
|
+
if (var) c_dcg_var_init_bool(var, value);
|
|
620
|
+
return var;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* @brief Allocate a double-tagged value.
|
|
625
|
+
*
|
|
626
|
+
* @param value Payload.
|
|
627
|
+
* @param allocator Allocator for the block (may be NULL).
|
|
628
|
+
* @return The value, or NULL on OOM.
|
|
629
|
+
*/
|
|
630
|
+
static inline dcg_var_t* c_dcg_var_new_double(double value, allocator_protocol* allocator) {
|
|
631
|
+
dcg_var_t* var = c_dcg_var_new(allocator);
|
|
632
|
+
if (var) c_dcg_var_init_double(var, value);
|
|
633
|
+
return var;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* @brief Allocate an int-tagged value.
|
|
638
|
+
*
|
|
639
|
+
* @param value Payload.
|
|
640
|
+
* @param allocator Allocator for the block (may be NULL).
|
|
641
|
+
* @return The value, or NULL on OOM.
|
|
642
|
+
*/
|
|
643
|
+
static inline dcg_var_t* c_dcg_var_new_int(ssize_t value, allocator_protocol* allocator) {
|
|
644
|
+
dcg_var_t* var = c_dcg_var_new(allocator);
|
|
645
|
+
if (var) c_dcg_var_init_int(var, value);
|
|
646
|
+
return var;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* @brief Allocate an offset-tagged value.
|
|
651
|
+
*
|
|
652
|
+
* @param value Payload.
|
|
653
|
+
* @param allocator Allocator for the block (may be NULL).
|
|
654
|
+
* @return The value, or NULL on OOM.
|
|
655
|
+
*/
|
|
656
|
+
static inline dcg_var_t* c_dcg_var_new_offset(ssize_t value, allocator_protocol* allocator) {
|
|
657
|
+
dcg_var_t* var = c_dcg_var_new(allocator);
|
|
658
|
+
if (var) c_dcg_var_init_offset(var, value);
|
|
659
|
+
return var;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* @brief Allocate a string-tagged value owning a COPY of the string.
|
|
664
|
+
*
|
|
665
|
+
* The copy is a child block of the value, so c_dcg_var_free() releases both
|
|
666
|
+
* and the text can never be orphaned - this is the owning counterpart of
|
|
667
|
+
* c_dcg_var_init_string(), whose payload stays borrowed.
|
|
668
|
+
*
|
|
669
|
+
* @param value String to copy (NULL gives an empty string value).
|
|
670
|
+
* @param allocator Allocator for both blocks (may be NULL).
|
|
671
|
+
* @return The value, or NULL on OOM.
|
|
672
|
+
*/
|
|
673
|
+
static inline dcg_var_t* c_dcg_var_new_string(const char* value, allocator_protocol* allocator) {
|
|
674
|
+
dcg_var_t* var = c_dcg_var_new(allocator);
|
|
675
|
+
if (!var) return NULL;
|
|
676
|
+
|
|
677
|
+
const char* copy = NULL;
|
|
678
|
+
if (value) {
|
|
679
|
+
size_t len = strlen(value);
|
|
680
|
+
char* buf = (char*) c_ap_alloc_child(len + 1, NULL, var);
|
|
681
|
+
if (!buf) {
|
|
682
|
+
c_ap_free_owned(var);
|
|
683
|
+
return NULL;
|
|
684
|
+
}
|
|
685
|
+
memcpy(buf, value, len + 1);
|
|
686
|
+
copy = buf;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
var->dtype = VAR_TYPE_STRING;
|
|
690
|
+
var->value.as_string = copy;
|
|
691
|
+
return var;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* @brief Allocate a raw-pointer-tagged value.
|
|
696
|
+
*
|
|
697
|
+
* @param value Opaque pointer payload (not owned).
|
|
698
|
+
* @param allocator Allocator for the block (may be NULL).
|
|
699
|
+
* @return The value, or NULL on OOM.
|
|
700
|
+
*/
|
|
701
|
+
static inline dcg_var_t* c_dcg_var_new_ptr(void* value, allocator_protocol* allocator) {
|
|
702
|
+
dcg_var_t* var = c_dcg_var_new(allocator);
|
|
703
|
+
if (var) c_dcg_var_init_ptr(var, value);
|
|
704
|
+
return var;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* @brief Allocate a vector value owning an n-element double buffer.
|
|
709
|
+
*
|
|
710
|
+
* The value and its data are one nested ownership chain: a single
|
|
711
|
+
* c_dcg_var_free() releases both.
|
|
712
|
+
*
|
|
713
|
+
* @param n Number of elements.
|
|
714
|
+
* @param allocator Allocator for the whole chain (may be NULL).
|
|
715
|
+
* @return The value, or NULL on OOM.
|
|
716
|
+
*/
|
|
717
|
+
static inline dcg_var_t* c_dcg_var_new_dvector(size_t n, allocator_protocol* allocator) {
|
|
718
|
+
dcg_var_t* var = c_dcg_var_new(allocator);
|
|
719
|
+
if (!var) return NULL;
|
|
720
|
+
|
|
721
|
+
dcg_d_vector_t* vector = c_dcg_d_vector_new_child(n, NULL, var);
|
|
722
|
+
if (!vector) {
|
|
723
|
+
c_ap_free_owned(var);
|
|
724
|
+
return NULL;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
var->dtype = VAR_TYPE_D_VECTOR;
|
|
728
|
+
var->value.as_dvector = vector;
|
|
729
|
+
return var;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* @brief Allocate a matrix value owning an (n_rows x n_cols) double matrix.
|
|
734
|
+
*
|
|
735
|
+
* @param n_rows Number of rows.
|
|
736
|
+
* @param n_cols Number of columns.
|
|
737
|
+
* @param row_major true for row-major, false for column-major.
|
|
738
|
+
* @param allocator Allocator for the whole chain (may be NULL).
|
|
739
|
+
* @return The value, or NULL on OOM.
|
|
740
|
+
*/
|
|
741
|
+
static inline dcg_var_t* c_dcg_var_new_dmatrix(size_t n_rows, size_t n_cols, bool row_major, allocator_protocol* allocator) {
|
|
742
|
+
dcg_var_t* var = c_dcg_var_new(allocator);
|
|
743
|
+
if (!var) return NULL;
|
|
744
|
+
|
|
745
|
+
dcg_d_matrix_t* matrix = c_dcg_d_matrix_new_child(n_rows, n_cols, row_major, NULL, var);
|
|
746
|
+
if (!matrix) {
|
|
747
|
+
c_ap_free_owned(var);
|
|
748
|
+
return NULL;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
var->dtype = VAR_TYPE_D_MATRIX;
|
|
752
|
+
var->value.as_dmatrix = matrix;
|
|
753
|
+
return var;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* @brief Allocate a value that references the CONTENT of another value.
|
|
758
|
+
*
|
|
759
|
+
* The new value owns nothing: the block is its own, the reference inside it is
|
|
760
|
+
* the caller's to keep alive, and c_dcg_var_free() leaves the referenced value
|
|
761
|
+
* untouched.
|
|
762
|
+
*
|
|
763
|
+
* @param src Value whose content is referred to (must outlive the new value).
|
|
764
|
+
* @param allocator Allocator for the block (may be NULL).
|
|
765
|
+
* @return The value, or NULL on OOM / a NULL source.
|
|
766
|
+
*/
|
|
767
|
+
static inline dcg_var_t* c_dcg_var_new_ref(const dcg_var_t* src, allocator_protocol* allocator) {
|
|
768
|
+
if (!src) return NULL;
|
|
769
|
+
|
|
770
|
+
dcg_var_t* var = c_dcg_var_new(allocator);
|
|
771
|
+
if (var) (void) c_dcg_var_init_ref(var, src);
|
|
772
|
+
return var;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* @brief Release what a value owns, leaving the buf to its holder.
|
|
777
|
+
*
|
|
778
|
+
* A payload the value OWNS is a block nested under it - a string copy, a
|
|
779
|
+
* vector, a matrix - and it is handed back here, exactly as
|
|
780
|
+
* c_dcg_node_dealloc() hands back node->out. A payload the value only REFERS to
|
|
781
|
+
* (the _REF tags) belongs to whatever it points at and is left alone. The tag is
|
|
782
|
+
* reset afterwards, so a buf that is reused owns nothing.
|
|
783
|
+
*
|
|
784
|
+
* @param var Value to tear down (NULL-safe).
|
|
785
|
+
*/
|
|
786
|
+
static inline void c_dcg_var_dealloc(dcg_var_t* var) {
|
|
787
|
+
if (!var) return;
|
|
788
|
+
|
|
789
|
+
if (var->dtype == VAR_TYPE_STRING && var->value.as_string) {
|
|
790
|
+
c_ap_free_owned((void*) var->value.as_string);
|
|
791
|
+
}
|
|
792
|
+
else if (var->dtype == VAR_TYPE_D_VECTOR && var->value.as_dvector) {
|
|
793
|
+
c_ap_free_owned(var->value.as_dvector);
|
|
794
|
+
}
|
|
795
|
+
else if (var->dtype == VAR_TYPE_D_MATRIX && var->value.as_dmatrix) {
|
|
796
|
+
c_ap_free_owned(var->value.as_dmatrix);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
memset(var, 0, sizeof(*var));
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* @brief Release a value and its block.
|
|
804
|
+
*
|
|
805
|
+
* The clean half first, then the block.
|
|
806
|
+
*
|
|
807
|
+
* @param var Value to free (NULL-safe). Must be a c_dcg_var_new_*() value,
|
|
808
|
+
* never one that only went through c_dcg_var_init_*().
|
|
809
|
+
*/
|
|
810
|
+
static inline void c_dcg_var_free(dcg_var_t* var) {
|
|
811
|
+
if (!var) return;
|
|
812
|
+
c_dcg_var_dealloc(var);
|
|
813
|
+
c_ap_free_owned(var);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// ========== Population ==========
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* @brief Reset a caller-owned value to the empty tag (RAW_PTR / NULL).
|
|
820
|
+
*
|
|
821
|
+
* @param var Value to populate.
|
|
822
|
+
* @return DCG_OK, or DCG_ERR_INVALID_ARG when var is NULL.
|
|
823
|
+
*/
|
|
824
|
+
static inline int c_dcg_var_init(dcg_var_t* var) {
|
|
825
|
+
if (!var) return DCG_ERR_INVALID_ARG;
|
|
826
|
+
memset(var, 0, sizeof(dcg_var_t));
|
|
827
|
+
// var->dtype = VAR_TYPE_RAW_PTR;
|
|
828
|
+
return DCG_OK;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* @brief Reset a caller-owned value to the RESERVED tag: an entry with no
|
|
833
|
+
* value and no type yet.
|
|
834
|
+
*
|
|
835
|
+
* The state a store creates an entry in when the caller has no type to give it
|
|
836
|
+
* (see c_dcg_mapping_lgroup_get_create_slot). The slot is there to be written
|
|
837
|
+
* into, and nothing about it is decided until a value lands.
|
|
838
|
+
*
|
|
839
|
+
* @param var Value to populate.
|
|
840
|
+
* @return DCG_OK, or DCG_ERR_INVALID_ARG when var is NULL.
|
|
841
|
+
*/
|
|
842
|
+
static inline int c_dcg_var_init_reserved(dcg_var_t* var) {
|
|
843
|
+
int ret = c_dcg_var_init(var);
|
|
844
|
+
if (ret != DCG_OK) return ret;
|
|
845
|
+
var->dtype = VAR_TYPE_RESERVED;
|
|
846
|
+
return DCG_OK;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* @brief Populate a caller-owned value with a bool.
|
|
851
|
+
*
|
|
852
|
+
* @param var Value to populate.
|
|
853
|
+
* @param value Payload.
|
|
854
|
+
* @return DCG_OK, or DCG_ERR_INVALID_ARG.
|
|
855
|
+
*/
|
|
856
|
+
static inline int c_dcg_var_init_bool(dcg_var_t* var, bool value) {
|
|
857
|
+
int ret = c_dcg_var_init(var);
|
|
858
|
+
if (ret != DCG_OK) return ret;
|
|
859
|
+
var->dtype = VAR_TYPE_BOOL;
|
|
860
|
+
var->value.as_bool = value;
|
|
861
|
+
return DCG_OK;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* @brief Populate a caller-owned value with a double.
|
|
866
|
+
*
|
|
867
|
+
* @param var Value to populate.
|
|
868
|
+
* @param value Payload.
|
|
869
|
+
* @return DCG_OK, or DCG_ERR_INVALID_ARG.
|
|
870
|
+
*/
|
|
871
|
+
static inline int c_dcg_var_init_double(dcg_var_t* var, double value) {
|
|
872
|
+
int ret = c_dcg_var_init(var);
|
|
873
|
+
if (ret != DCG_OK) return ret;
|
|
874
|
+
var->dtype = VAR_TYPE_DOUBLE;
|
|
875
|
+
var->value.as_double = value;
|
|
876
|
+
return DCG_OK;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
* @brief Populate a caller-owned value with an integer.
|
|
881
|
+
*
|
|
882
|
+
* @param var Value to populate.
|
|
883
|
+
* @param value Payload.
|
|
884
|
+
* @return DCG_OK, or DCG_ERR_INVALID_ARG.
|
|
885
|
+
*/
|
|
886
|
+
static inline int c_dcg_var_init_int(dcg_var_t* var, ssize_t value) {
|
|
887
|
+
int ret = c_dcg_var_init(var);
|
|
888
|
+
if (ret != DCG_OK) return ret;
|
|
889
|
+
var->dtype = VAR_TYPE_INT;
|
|
890
|
+
var->value.as_int = value;
|
|
891
|
+
return DCG_OK;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* @brief Populate a caller-owned value with an offset.
|
|
896
|
+
*
|
|
897
|
+
* @param var Value to populate.
|
|
898
|
+
* @param value Payload.
|
|
899
|
+
* @return DCG_OK, or DCG_ERR_INVALID_ARG.
|
|
900
|
+
*/
|
|
901
|
+
static inline int c_dcg_var_init_offset(dcg_var_t* var, ssize_t value) {
|
|
902
|
+
int ret = c_dcg_var_init(var);
|
|
903
|
+
if (ret != DCG_OK) return ret;
|
|
904
|
+
var->dtype = VAR_TYPE_OFFSET;
|
|
905
|
+
var->value.as_offset = value;
|
|
906
|
+
return DCG_OK;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/**
|
|
910
|
+
* @brief Populate a caller-owned value with a BORROWED string.
|
|
911
|
+
*
|
|
912
|
+
* Nothing is copied: the caller keeps the text alive, or hands the value to a
|
|
913
|
+
* node, which copies it into a block of its own. Use c_dcg_var_new_string()
|
|
914
|
+
* when the value itself should own the text.
|
|
915
|
+
*
|
|
916
|
+
* @param var Value to populate.
|
|
917
|
+
* @param value NUL-terminated string (may be NULL).
|
|
918
|
+
* @return DCG_OK, or DCG_ERR_INVALID_ARG.
|
|
919
|
+
*/
|
|
920
|
+
static inline int c_dcg_var_init_string(dcg_var_t* var, const char* value) {
|
|
921
|
+
int ret = c_dcg_var_init(var);
|
|
922
|
+
if (ret != DCG_OK) return ret;
|
|
923
|
+
var->dtype = VAR_TYPE_STRING;
|
|
924
|
+
var->value.as_string = value;
|
|
925
|
+
return DCG_OK;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* @brief Populate a caller-owned value with a BORROWED reference to another
|
|
930
|
+
* value, at an explicit level - the primitive behind every reference.
|
|
931
|
+
*
|
|
932
|
+
* `ref` is the pointer the level calls for, and `dtype` is that level: a double*
|
|
933
|
+
* for a _REF to a double, a double** for a _REF_REF to one, one more star per
|
|
934
|
+
* rung after that. c_dcg_var_init_ref() derives both from a value; reach for this
|
|
935
|
+
* when the storage is not a dcg_var_t at all - a caller's buffer, a field of a
|
|
936
|
+
* foreign struct, an element of an array - or when the level is the caller's
|
|
937
|
+
* decision rather than the source's.
|
|
938
|
+
*
|
|
939
|
+
* A reference owns nothing. The storage it points at must outlive it and keep the
|
|
940
|
+
* shape the level assumes.
|
|
941
|
+
*
|
|
942
|
+
* @param var Value to populate.
|
|
943
|
+
* @param dtype A reference tag (VAR_TYPE_*_REF / _REF_REF, or any level above).
|
|
944
|
+
* @param ref Pointer to the referred-to slot of that shape. It may be NULL,
|
|
945
|
+
* but that is a reference to nothing rather than a value: every
|
|
946
|
+
* read through it refuses (see c_dcg_var_ref_slot).
|
|
947
|
+
* @return DCG_OK, DCG_ERR_INVALID_ARG or DCG_ERR_TYPE (dtype is not a reference).
|
|
948
|
+
*/
|
|
949
|
+
static inline int c_dcg_var_init_ref_raw(dcg_var_t* var, dcg_var_type dtype, const void* ref) {
|
|
950
|
+
if (!var) return DCG_ERR_INVALID_ARG;
|
|
951
|
+
if (!c_dcg_var_is_ref(dtype)) return DCG_ERR_TYPE;
|
|
952
|
+
|
|
953
|
+
int ret = c_dcg_var_init(var);
|
|
954
|
+
if (ret != DCG_OK) return ret;
|
|
955
|
+
var->dtype = dtype;
|
|
956
|
+
var->value.as_ref = ref;
|
|
957
|
+
return DCG_OK;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
/**
|
|
961
|
+
* @brief Populate a caller-owned value with a BORROWED reference to another
|
|
962
|
+
* value - always one level deeper than what it is given, and one more level of
|
|
963
|
+
* POINTER in the payload.
|
|
964
|
+
*
|
|
965
|
+
* The reference is the address of the source's own payload slot, so what lands
|
|
966
|
+
* in `as_ref` is exactly what the level calls for:
|
|
967
|
+
*
|
|
968
|
+
* - a plain double holds a double, so the address of that slot is a double*,
|
|
969
|
+
* and the reference is a _REF;
|
|
970
|
+
* - a double_ref holds a double*, so the address of that slot is a double**,
|
|
971
|
+
* and the reference is a _REF_REF;
|
|
972
|
+
* - and one more rung adds one more star, as far as the level field holds.
|
|
973
|
+
*
|
|
974
|
+
* That is what makes this form and c_dcg_var_init_ref_raw() interchangeable:
|
|
975
|
+
* c_dcg_var_init_ref(var, src) builds precisely the var_t that
|
|
976
|
+
* c_dcg_var_init_ref_raw(var, level + 1 | base, &src->value) builds, so there is
|
|
977
|
+
* one representation of a reference and one way to read it.
|
|
978
|
+
*
|
|
979
|
+
* Nothing is copied and nothing is owned: the reference is an address inside the
|
|
980
|
+
* source, which must outlive it - and must keep the shape the level assumes, so
|
|
981
|
+
* a middle rung stays a reference for as long as a rung above it exists.
|
|
982
|
+
*
|
|
983
|
+
* @param var Value to populate.
|
|
984
|
+
* @param src Value whose content is referred to (must outlive the reference).
|
|
985
|
+
* @return DCG_OK, or DCG_ERR_INVALID_ARG.
|
|
986
|
+
*/
|
|
987
|
+
static inline int c_dcg_var_init_ref(dcg_var_t* var, const dcg_var_t* src) {
|
|
988
|
+
if (!var || !src) return DCG_ERR_INVALID_ARG;
|
|
989
|
+
|
|
990
|
+
/* One rung above the source, and one more pointer: the address of the slot
|
|
991
|
+
* the source holds its content (or its own reference) in. A level that
|
|
992
|
+
* outgrows VAR_TYPE_REF_MASK is not a reference at all, and init_ref_raw
|
|
993
|
+
* refuses it rather than storing a tag that would read as a plain value. */
|
|
994
|
+
int level = c_dcg_var_ref_level(src->dtype) + 1;
|
|
995
|
+
|
|
996
|
+
return c_dcg_var_init_ref_raw(var, (dcg_var_type) ((level << VAR_TYPE_REF_SHIFT) | c_dcg_var_ref_base(src->dtype)), (const void*) &src->value);
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
/**
|
|
1000
|
+
* @brief Populate a caller-owned value with a NODE.
|
|
1001
|
+
*
|
|
1002
|
+
* A node is what a node-shaped value stands for - the node itself is the value,
|
|
1003
|
+
* which is how an action comes to hold its own address and how a walk hands back
|
|
1004
|
+
* the leaf it landed on. The pointer carries the node TYPE rather than a void*,
|
|
1005
|
+
* and that is the whole of what the value layer knows about one: the definition
|
|
1006
|
+
* belongs to c_node.h, nothing here reads through it, and the wrapper layer is
|
|
1007
|
+
* where it becomes a wrapper again (see c_dcg_var_pyunpack).
|
|
1008
|
+
*
|
|
1009
|
+
* @param var Value to populate.
|
|
1010
|
+
* @param node The node to hold (may be NULL: a node-shaped value of nothing).
|
|
1011
|
+
* Kept as given, never read through.
|
|
1012
|
+
* @return DCG_OK, or DCG_ERR_INVALID_ARG.
|
|
1013
|
+
*/
|
|
1014
|
+
static inline int c_dcg_var_init_node(dcg_var_t* var, const dcg_node* node) {
|
|
1015
|
+
if (!var) return DCG_ERR_INVALID_ARG;
|
|
1016
|
+
|
|
1017
|
+
if (c_dcg_var_init(var) != DCG_OK) return DCG_ERR_INVALID_ARG;
|
|
1018
|
+
var->dtype = VAR_TYPE_NODE;
|
|
1019
|
+
var->value.as_node = node;
|
|
1020
|
+
return DCG_OK;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
/**
|
|
1024
|
+
* @brief Populate a caller-owned value with a raw pointer.
|
|
1025
|
+
*
|
|
1026
|
+
* @param var Value to populate.
|
|
1027
|
+
* @param value Opaque pointer payload (not owned).
|
|
1028
|
+
* @return DCG_OK, or DCG_ERR_INVALID_ARG.
|
|
1029
|
+
*/
|
|
1030
|
+
static inline int c_dcg_var_init_ptr(dcg_var_t* var, void* value) {
|
|
1031
|
+
int ret = c_dcg_var_init(var);
|
|
1032
|
+
if (ret != DCG_OK) return ret;
|
|
1033
|
+
var->dtype = VAR_TYPE_RAW_PTR;
|
|
1034
|
+
var->value.as_ptr = value;
|
|
1035
|
+
return DCG_OK;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
/**
|
|
1039
|
+
* @brief Populate a value with a contiguous vector, wrapped or snapshotted.
|
|
1040
|
+
*
|
|
1041
|
+
* Either way the shape struct is allocated as a child block of the var, so
|
|
1042
|
+
* the var must be a block start - a heap var. The BUFFER differs:
|
|
1043
|
+
*
|
|
1044
|
+
* - copy = false wraps the caller's buffer: borrowed, not adopted, and it
|
|
1045
|
+
* must stay alive and unchanged for as long as the value is read.
|
|
1046
|
+
* - copy = true takes a snapshot: the buffer is copied into a block owned
|
|
1047
|
+
* by the shape struct, so the value is immune to whatever the caller does
|
|
1048
|
+
* to the source afterwards. It is that copy the var frees, never the
|
|
1049
|
+
* source.
|
|
1050
|
+
*
|
|
1051
|
+
* @param var Value to populate. Must be an allocator-protocol block.
|
|
1052
|
+
* @param value Contiguous buffer of n doubles (may be NULL).
|
|
1053
|
+
* @param n Number of doubles.
|
|
1054
|
+
* @param copy true to snapshot the buffer, false to wrap it.
|
|
1055
|
+
* @param allocator Allocator for the shape struct; NULL derives it from var.
|
|
1056
|
+
* @return DCG_OK, DCG_ERR_INVALID_ARG, DCG_ERR_INVALID_BUF or DCG_ERR_OOM.
|
|
1057
|
+
*/
|
|
1058
|
+
static inline int c_dcg_var_init_dvector(dcg_var_t* var, double* value, size_t n, bool copy, allocator_protocol* allocator) {
|
|
1059
|
+
if (!var) return DCG_ERR_INVALID_ARG;
|
|
1060
|
+
// Checked before the buf is touched: a refused init leaves it untouched.
|
|
1061
|
+
if (!c_ap_is_allocator_buf(var)) return DCG_ERR_INVALID_BUF;
|
|
1062
|
+
|
|
1063
|
+
int ret = c_dcg_var_init(var);
|
|
1064
|
+
if (ret != DCG_OK) return ret;
|
|
1065
|
+
|
|
1066
|
+
dcg_d_vector_t* vector = (dcg_d_vector_t*) c_ap_alloc_child(sizeof(dcg_d_vector_t), allocator, var);
|
|
1067
|
+
if (!vector) return DCG_ERR_OOM;
|
|
1068
|
+
|
|
1069
|
+
vector->data = value; // wrapped by default: the caller's buffer, not adopted
|
|
1070
|
+
vector->n = n;
|
|
1071
|
+
|
|
1072
|
+
if (copy && value && n > 0) {
|
|
1073
|
+
double* snapshot = (double*) c_ap_alloc_child(n * sizeof(double), NULL, vector);
|
|
1074
|
+
if (!snapshot) {
|
|
1075
|
+
c_ap_free_owned(vector); // unlinks from the var; the value stays empty
|
|
1076
|
+
return DCG_ERR_OOM;
|
|
1077
|
+
}
|
|
1078
|
+
memcpy(snapshot, value, n * sizeof(double));
|
|
1079
|
+
vector->data = snapshot; // owned from here on, and freed with the var
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
var->dtype = VAR_TYPE_D_VECTOR;
|
|
1083
|
+
var->value.as_dvector = vector;
|
|
1084
|
+
return DCG_OK;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
/**
|
|
1088
|
+
* @brief Populate a value with a contiguous matrix, wrapped or snapshotted.
|
|
1089
|
+
*
|
|
1090
|
+
* As with the vector: the shape struct is a child block of the var (so the
|
|
1091
|
+
* var must be a block start), while the BUFFER is either wrapped (borrowed,
|
|
1092
|
+
* copy = false) or snapshotted into an owned block (copy = true, immune to
|
|
1093
|
+
* later writes to the source). The layout flag is part of the value, so reads
|
|
1094
|
+
* honour it (see c_dcg_d_matrix_t).
|
|
1095
|
+
*
|
|
1096
|
+
* @param var Value to populate. Must be an allocator-protocol block.
|
|
1097
|
+
* @param value Contiguous buffer of n_rows * n_cols doubles (may be NULL).
|
|
1098
|
+
* @param n_rows Number of rows.
|
|
1099
|
+
* @param n_cols Number of columns.
|
|
1100
|
+
* @param row_major true for row-major, false for column-major.
|
|
1101
|
+
* @param copy true to snapshot the buffer, false to wrap it.
|
|
1102
|
+
* @param allocator Allocator for the shape struct; NULL derives it from var.
|
|
1103
|
+
* @return DCG_OK, DCG_ERR_INVALID_ARG, DCG_ERR_INVALID_BUF or DCG_ERR_OOM.
|
|
1104
|
+
*/
|
|
1105
|
+
static inline int c_dcg_var_init_dmatrix(dcg_var_t* var, double* value, size_t n_rows, size_t n_cols, bool row_major, bool copy, allocator_protocol* allocator) {
|
|
1106
|
+
if (!var) return DCG_ERR_INVALID_ARG;
|
|
1107
|
+
// Checked before the buf is touched: a refused init leaves it untouched.
|
|
1108
|
+
if (!c_ap_is_allocator_buf(var)) return DCG_ERR_INVALID_BUF;
|
|
1109
|
+
|
|
1110
|
+
int ret = c_dcg_var_init(var);
|
|
1111
|
+
if (ret != DCG_OK) return ret;
|
|
1112
|
+
|
|
1113
|
+
dcg_d_matrix_t* matrix = (dcg_d_matrix_t*) c_ap_alloc_child(sizeof(dcg_d_matrix_t), allocator, var);
|
|
1114
|
+
if (!matrix) return DCG_ERR_OOM;
|
|
1115
|
+
|
|
1116
|
+
matrix->data = value; // wrapped by default: the caller's buffer, not adopted
|
|
1117
|
+
matrix->n_rows = n_rows;
|
|
1118
|
+
matrix->n_cols = n_cols;
|
|
1119
|
+
matrix->row_major = row_major;
|
|
1120
|
+
|
|
1121
|
+
size_t total = n_rows * n_cols;
|
|
1122
|
+
if (copy && value && total > 0) {
|
|
1123
|
+
double* snapshot = (double*) c_ap_alloc_child(total * sizeof(double), NULL, matrix);
|
|
1124
|
+
if (!snapshot) {
|
|
1125
|
+
c_ap_free_owned(matrix); // unlinks from the var; the value stays empty
|
|
1126
|
+
return DCG_ERR_OOM;
|
|
1127
|
+
}
|
|
1128
|
+
memcpy(snapshot, value, total * sizeof(double));
|
|
1129
|
+
matrix->data = snapshot; // owned from here on, and freed with the var
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
var->dtype = VAR_TYPE_D_MATRIX;
|
|
1133
|
+
var->value.as_dmatrix = matrix;
|
|
1134
|
+
return DCG_OK;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
// ========== Introspection ==========
|
|
1138
|
+
|
|
1139
|
+
/**
|
|
1140
|
+
* @brief Stable display name of a return code.
|
|
1141
|
+
*
|
|
1142
|
+
* @param code Return code.
|
|
1143
|
+
* @return Static string; "UNKNOWN" for a code outside the enum.
|
|
1144
|
+
*/
|
|
1145
|
+
static inline const char* c_dcg_ret_code_name(dcg_ret_code code) {
|
|
1146
|
+
switch (code) {
|
|
1147
|
+
case DCG_OK:
|
|
1148
|
+
return "OK";
|
|
1149
|
+
case DCG_ERR_INVALID_ARG:
|
|
1150
|
+
return "INVALID_ARG";
|
|
1151
|
+
case DCG_ERR_INVALID_BUF:
|
|
1152
|
+
return "INVALID_BUF";
|
|
1153
|
+
case DCG_ERR_OOM:
|
|
1154
|
+
return "OOM";
|
|
1155
|
+
case DCG_ERR_NOT_FOUND:
|
|
1156
|
+
return "NOT_FOUND";
|
|
1157
|
+
case DCG_ERR_FULL:
|
|
1158
|
+
return "FULL";
|
|
1159
|
+
case DCG_ERR_BAD_CAST:
|
|
1160
|
+
return "BAD_CAST";
|
|
1161
|
+
case DCG_ERR_FORMAT:
|
|
1162
|
+
return "FORMAT";
|
|
1163
|
+
case DCG_ERR_TYPE:
|
|
1164
|
+
return "TYPE";
|
|
1165
|
+
case DCG_ERR_CYCLE:
|
|
1166
|
+
return "CYCLE";
|
|
1167
|
+
case DCG_ERR_BUSY:
|
|
1168
|
+
return "BUSY";
|
|
1169
|
+
case DCG_ERR_DUPLICATE:
|
|
1170
|
+
return "DUPLICATE";
|
|
1171
|
+
case DCG_ERR_EDGE:
|
|
1172
|
+
return "EDGE";
|
|
1173
|
+
case DCG_ERR_UNRESOLVED:
|
|
1174
|
+
return "UNRESOLVED";
|
|
1175
|
+
case DCG_ERR_RANGE:
|
|
1176
|
+
return "RANGE";
|
|
1177
|
+
case DCG_ERR_HOOK:
|
|
1178
|
+
return "HOOK";
|
|
1179
|
+
case DCG_ERR_NO_MATCH:
|
|
1180
|
+
return "NO_MATCH";
|
|
1181
|
+
case DCG_ERR_MATH:
|
|
1182
|
+
return "MATH";
|
|
1183
|
+
case DCG_ERR_UNBOUND:
|
|
1184
|
+
return "UNBOUND";
|
|
1185
|
+
default:
|
|
1186
|
+
return "UNKNOWN";
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
/**
|
|
1191
|
+
* @brief How many hops a tag refers through.
|
|
1192
|
+
*
|
|
1193
|
+
* The level is read straight out of the tag - a mask and a shift - so it is
|
|
1194
|
+
* whatever the tag was built with rather than a pair of known cases: 1 for a
|
|
1195
|
+
* _REF, 2 for a _REF_REF, 3 for a reference to one of those, and so on.
|
|
1196
|
+
*
|
|
1197
|
+
* @param dtype Value tag.
|
|
1198
|
+
* @return The number of hops, or 0 for a plain tag.
|
|
1199
|
+
*/
|
|
1200
|
+
static inline int c_dcg_var_ref_level(dcg_var_type dtype) {
|
|
1201
|
+
return (int) (((int) dtype & VAR_TYPE_REF_MASK) >> VAR_TYPE_REF_SHIFT);
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
/**
|
|
1205
|
+
* @brief Predicate: does the tag name a reference rather than a value?
|
|
1206
|
+
*
|
|
1207
|
+
* @param dtype Value tag.
|
|
1208
|
+
* @return true for the _REF and _REF_REF tags.
|
|
1209
|
+
*/
|
|
1210
|
+
static inline bool c_dcg_var_is_ref(dcg_var_type dtype) {
|
|
1211
|
+
return c_dcg_var_ref_level(dtype) != 0;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
/**
|
|
1215
|
+
* @brief The tag a value tag refers to.
|
|
1216
|
+
*
|
|
1217
|
+
* A plain tag refers to itself, since its reference bits are already clear, so
|
|
1218
|
+
* this is also the honest way to ask "what is this really?" of any tag.
|
|
1219
|
+
*
|
|
1220
|
+
* @param dtype Value tag.
|
|
1221
|
+
* @return The referred-to tag (dtype & VAR_TYPE_BASE_MASK).
|
|
1222
|
+
*/
|
|
1223
|
+
static inline dcg_var_type c_dcg_var_ref_base(dcg_var_type dtype) {
|
|
1224
|
+
return (dcg_var_type) ((int) dtype & VAR_TYPE_BASE_MASK);
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
/** Display names of the plain tags, indexed by the tag itself. */
|
|
1228
|
+
static const char* const DCG_VAR_TYPE_NAMES[] = {"raw_ptr", "string", "bool", "double", "int", "offset", "time", "date", "datetime", "d_vector", "d_matrix", "node", "reserved"};
|
|
1229
|
+
|
|
1230
|
+
/** Display names of the one-hop references, indexed by the tag referred to. */
|
|
1231
|
+
static const char* const DCG_VAR_TYPE_REF_NAMES[] = {"raw_ptr_ref", "string_ref", "bool_ref", "double_ref", "int_ref", "offset_ref", "time_ref", "date_ref", "datetime_ref", "d_vector_ref", "d_matrix_ref", "node_ref", "inferred"};
|
|
1232
|
+
|
|
1233
|
+
/** Display names of the two-hop references, indexed by the tag referred to. */
|
|
1234
|
+
static const char* const DCG_VAR_TYPE_REF_REF_NAMES[] = {"raw_ptr_ref_ref", "string_ref_ref", "bool_ref_ref", "double_ref_ref", "int_ref_ref", "offset_ref_ref", "time_ref_ref", "date_ref_ref", "datetime_ref_ref", "d_vector_ref_ref", "d_matrix_ref_ref", "node_ref_ref", "inferred_ref"};
|
|
1235
|
+
|
|
1236
|
+
/**
|
|
1237
|
+
* @brief Stable display name of a value tag.
|
|
1238
|
+
*
|
|
1239
|
+
* The tag's level picks the table and its base picks the entry, so a reference
|
|
1240
|
+
* is named after what it refers to plus its own suffix - no parsing, no switch
|
|
1241
|
+
* over tags that grows with every new type.
|
|
1242
|
+
*
|
|
1243
|
+
* The tables stop at the two levels that are commonly used: a reference nested
|
|
1244
|
+
* deeper keeps the _REF_REF name rather than pretending to count the rungs, and
|
|
1245
|
+
* c_dcg_var_ref_level() is where an exact count is read.
|
|
1246
|
+
*
|
|
1247
|
+
* @param dtype Value tag.
|
|
1248
|
+
* @return Static string; "invalid" for a tag outside the tables.
|
|
1249
|
+
*/
|
|
1250
|
+
static inline const char* c_dcg_var_type_name(dcg_var_type dtype) {
|
|
1251
|
+
const char* const* names = DCG_VAR_TYPE_NAMES;
|
|
1252
|
+
size_t count = sizeof(DCG_VAR_TYPE_NAMES) / sizeof(char*);
|
|
1253
|
+
|
|
1254
|
+
switch (c_dcg_var_ref_level(dtype)) {
|
|
1255
|
+
case 0:
|
|
1256
|
+
break;
|
|
1257
|
+
case 1:
|
|
1258
|
+
names = DCG_VAR_TYPE_REF_NAMES;
|
|
1259
|
+
count = sizeof(DCG_VAR_TYPE_REF_NAMES) / sizeof(char*);
|
|
1260
|
+
break;
|
|
1261
|
+
default:
|
|
1262
|
+
names = DCG_VAR_TYPE_REF_REF_NAMES;
|
|
1263
|
+
count = sizeof(DCG_VAR_TYPE_REF_REF_NAMES) / sizeof(char*);
|
|
1264
|
+
break;
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
size_t base = (size_t) ((int) dtype & VAR_TYPE_BASE_MASK);
|
|
1268
|
+
return base < count ? names[base] : "invalid";
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
/**
|
|
1272
|
+
* @brief Predicate: is the payload a scalar number (int / offset / double)?
|
|
1273
|
+
*
|
|
1274
|
+
* A reference answers for what it points at, so an evaluator can ask before it
|
|
1275
|
+
* follows.
|
|
1276
|
+
*
|
|
1277
|
+
* @param var Value to inspect (NULL-safe).
|
|
1278
|
+
* @return true when the payload can take part in scalar arithmetic.
|
|
1279
|
+
*/
|
|
1280
|
+
static inline bool c_dcg_var_is_numeric(const dcg_var_t* var) {
|
|
1281
|
+
if (!var) return false;
|
|
1282
|
+
dcg_var_type base = c_dcg_var_ref_base(var->dtype); // a reference is as numeric as its target
|
|
1283
|
+
return base == VAR_TYPE_INT || base == VAR_TYPE_DOUBLE || base == VAR_TYPE_OFFSET;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
/**
|
|
1287
|
+
* @brief How a value reads as a number: as a whole number, as a double, or not
|
|
1288
|
+
* at all.
|
|
1289
|
+
*
|
|
1290
|
+
* `is_numeric` answers the same question with a yes or a no. This answers it
|
|
1291
|
+
* with the two ways a number can be one, because that is what decides the type
|
|
1292
|
+
* of an arithmetic result: an operation with a double in it is a double, and one
|
|
1293
|
+
* without is a whole number.
|
|
1294
|
+
*/
|
|
1295
|
+
static inline dcg_var_numeric c_dcg_var_numeric_of(const dcg_var_t* var) {
|
|
1296
|
+
if (!var) return VAR_NUMERIC_NONE;
|
|
1297
|
+
|
|
1298
|
+
switch (c_dcg_var_ref_base(var->dtype)) {
|
|
1299
|
+
case VAR_TYPE_BOOL:
|
|
1300
|
+
case VAR_TYPE_INT:
|
|
1301
|
+
case VAR_TYPE_OFFSET:
|
|
1302
|
+
return VAR_NUMERIC_INT;
|
|
1303
|
+
case VAR_TYPE_DOUBLE:
|
|
1304
|
+
return VAR_NUMERIC_DOUBLE;
|
|
1305
|
+
default:
|
|
1306
|
+
return VAR_NUMERIC_NONE;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
/**
|
|
1311
|
+
* @brief Predicate: is the payload a container (double vector / matrix)?
|
|
1312
|
+
*
|
|
1313
|
+
* @param var Value to inspect (NULL-safe).
|
|
1314
|
+
* @return true for the container tags and the references to them.
|
|
1315
|
+
*/
|
|
1316
|
+
static inline bool c_dcg_var_is_container(const dcg_var_t* var) {
|
|
1317
|
+
if (!var) return false;
|
|
1318
|
+
dcg_var_type base = c_dcg_var_ref_base(var->dtype);
|
|
1319
|
+
return base == VAR_TYPE_D_VECTOR || base == VAR_TYPE_D_MATRIX;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
/**
|
|
1323
|
+
* @brief Predicate: is the value absent (no payload of any type)?
|
|
1324
|
+
*
|
|
1325
|
+
* Absent is about the VALUE: a live reference whose slot holds NULL is absent,
|
|
1326
|
+
* like a NULL string or a vector with no buffer, and so is a RESERVED slot -
|
|
1327
|
+
* an entry created before its value, which holds nothing until one arrives. A
|
|
1328
|
+
* reference that points at nothing is not absent, it is broken - the read
|
|
1329
|
+
* refuses, and this predicate refuses with it.
|
|
1330
|
+
*
|
|
1331
|
+
* @param var Value to inspect (NULL-safe).
|
|
1332
|
+
* @return true for a NULL value or a NULL payload.
|
|
1333
|
+
*/
|
|
1334
|
+
static inline bool c_dcg_var_is_null(const dcg_var_t* var) {
|
|
1335
|
+
if (!var) return true;
|
|
1336
|
+
|
|
1337
|
+
/* The tag behind any reference, or the tag itself: the readers follow the
|
|
1338
|
+
* reference, so asking them answers for the value at the end of it. */
|
|
1339
|
+
switch (c_dcg_var_ref_base(var->dtype)) {
|
|
1340
|
+
case VAR_TYPE_RESERVED: /* nothing has landed in it, so it holds nothing */
|
|
1341
|
+
return true;
|
|
1342
|
+
case VAR_TYPE_RAW_PTR:
|
|
1343
|
+
case VAR_TYPE_TIME:
|
|
1344
|
+
case VAR_TYPE_DATE:
|
|
1345
|
+
case VAR_TYPE_DATETIME:
|
|
1346
|
+
return c_dcg_var_as_ptr(var) == NULL;
|
|
1347
|
+
case VAR_TYPE_STRING:
|
|
1348
|
+
return c_dcg_var_as_string(var) == NULL;
|
|
1349
|
+
case VAR_TYPE_D_VECTOR: {
|
|
1350
|
+
const dcg_d_vector_t* vector = c_dcg_var_as_dvector(var);
|
|
1351
|
+
return vector == NULL || vector->data == NULL;
|
|
1352
|
+
}
|
|
1353
|
+
case VAR_TYPE_D_MATRIX: {
|
|
1354
|
+
const dcg_d_matrix_t* matrix = c_dcg_var_as_dmatrix(var);
|
|
1355
|
+
return matrix == NULL || matrix->data == NULL;
|
|
1356
|
+
}
|
|
1357
|
+
default:
|
|
1358
|
+
return false; // a scalar is never absent, however empty it reads
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
/**
|
|
1363
|
+
* @brief Truthiness of a value, following Python's truth rules.
|
|
1364
|
+
*
|
|
1365
|
+
* Containers follow the sequence rule too: present and non-empty is truthy,
|
|
1366
|
+
* absent or empty is falsy. A reference is judged by what it points at, read at
|
|
1367
|
+
* the moment of the call - which is what makes a reference usable directly as a
|
|
1368
|
+
* branch condition.
|
|
1369
|
+
*
|
|
1370
|
+
* @param var Value to inspect (NULL-safe; NULL is falsy).
|
|
1371
|
+
* @return true when the value counts as true in a branch condition.
|
|
1372
|
+
*/
|
|
1373
|
+
static inline bool c_dcg_var_is_truthy(const dcg_var_t* var) {
|
|
1374
|
+
if (!var) return false;
|
|
1375
|
+
|
|
1376
|
+
switch (c_dcg_var_ref_base(var->dtype)) {
|
|
1377
|
+
case VAR_TYPE_BOOL: // reads as 1 / 0 through the int reader
|
|
1378
|
+
case VAR_TYPE_INT:
|
|
1379
|
+
case VAR_TYPE_OFFSET:
|
|
1380
|
+
return c_dcg_var_as_int(var) != 0;
|
|
1381
|
+
case VAR_TYPE_DOUBLE:
|
|
1382
|
+
return c_dcg_var_as_double(var) != 0.0;
|
|
1383
|
+
case VAR_TYPE_STRING: {
|
|
1384
|
+
const char* text = c_dcg_var_as_string(var);
|
|
1385
|
+
return text != NULL && text[0] != '\0';
|
|
1386
|
+
}
|
|
1387
|
+
case VAR_TYPE_D_VECTOR: {
|
|
1388
|
+
const dcg_d_vector_t* vector = c_dcg_var_as_dvector(var);
|
|
1389
|
+
return vector != NULL && vector->n > 0;
|
|
1390
|
+
}
|
|
1391
|
+
case VAR_TYPE_D_MATRIX: {
|
|
1392
|
+
const dcg_d_matrix_t* matrix = c_dcg_var_as_dmatrix(var);
|
|
1393
|
+
return matrix != NULL && matrix->n_rows > 0 && matrix->n_cols > 0;
|
|
1394
|
+
}
|
|
1395
|
+
case VAR_TYPE_NODE:
|
|
1396
|
+
return c_dcg_var_as_node(var) != NULL;
|
|
1397
|
+
case VAR_TYPE_RAW_PTR:
|
|
1398
|
+
case VAR_TYPE_TIME:
|
|
1399
|
+
case VAR_TYPE_DATE:
|
|
1400
|
+
case VAR_TYPE_DATETIME:
|
|
1401
|
+
return c_dcg_var_as_ptr(var) != NULL;
|
|
1402
|
+
default:
|
|
1403
|
+
return false;
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
/**
|
|
1408
|
+
* @brief Equality of two values, comparing tag first, then payload.
|
|
1409
|
+
*
|
|
1410
|
+
* Two values of different tags are never equal. Strings compare by content,
|
|
1411
|
+
* pointers by address, and the containers element-wise (which is what makes
|
|
1412
|
+
* a container usable as a branch condition - and what makes the comparison
|
|
1413
|
+
* proportional to the payload). A reference compares as the value it points at,
|
|
1414
|
+
* read at the moment of the call.
|
|
1415
|
+
*
|
|
1416
|
+
* @param lhs Left value (NULL-safe).
|
|
1417
|
+
* @param rhs Right value (NULL-safe).
|
|
1418
|
+
* @return true when both tag and payload match.
|
|
1419
|
+
*/
|
|
1420
|
+
static inline bool c_dcg_var_equals(const dcg_var_t* lhs, const dcg_var_t* rhs) {
|
|
1421
|
+
if (lhs == rhs) return true;
|
|
1422
|
+
if (!lhs || !rhs) return false;
|
|
1423
|
+
|
|
1424
|
+
/* The tags are compared behind any reference, because that is what the
|
|
1425
|
+
* values really are: a reference to a string and a plain string holding the
|
|
1426
|
+
* same text are the same condition, which is what an edge has to match. The
|
|
1427
|
+
* readers below follow the reference, so nothing has to be copied to find
|
|
1428
|
+
* out. */
|
|
1429
|
+
dcg_var_type left = c_dcg_var_ref_base(lhs->dtype);
|
|
1430
|
+
dcg_var_type right = c_dcg_var_ref_base(rhs->dtype);
|
|
1431
|
+
if (left != right) return false;
|
|
1432
|
+
|
|
1433
|
+
switch (left) {
|
|
1434
|
+
case VAR_TYPE_STRING: {
|
|
1435
|
+
const char* a = c_dcg_var_as_string(lhs);
|
|
1436
|
+
const char* b = c_dcg_var_as_string(rhs);
|
|
1437
|
+
if (a == b) return true;
|
|
1438
|
+
if (!a || !b) return false;
|
|
1439
|
+
return strcmp(a, b) == 0;
|
|
1440
|
+
}
|
|
1441
|
+
case VAR_TYPE_BOOL:
|
|
1442
|
+
return c_dcg_var_as_bool(lhs) == c_dcg_var_as_bool(rhs);
|
|
1443
|
+
case VAR_TYPE_DOUBLE:
|
|
1444
|
+
return c_dcg_var_as_double(lhs) == c_dcg_var_as_double(rhs);
|
|
1445
|
+
case VAR_TYPE_INT:
|
|
1446
|
+
case VAR_TYPE_OFFSET:
|
|
1447
|
+
return c_dcg_var_as_int(lhs) == c_dcg_var_as_int(rhs);
|
|
1448
|
+
case VAR_TYPE_D_VECTOR: {
|
|
1449
|
+
const dcg_d_vector_t* a = c_dcg_var_as_dvector(lhs);
|
|
1450
|
+
const dcg_d_vector_t* b = c_dcg_var_as_dvector(rhs);
|
|
1451
|
+
if (a == b) return true;
|
|
1452
|
+
if (!a || !b || a->n != b->n) return false;
|
|
1453
|
+
return a->n == 0 || memcmp(a->data, b->data, a->n * sizeof(double)) == 0;
|
|
1454
|
+
}
|
|
1455
|
+
case VAR_TYPE_D_MATRIX: {
|
|
1456
|
+
const dcg_d_matrix_t* a = c_dcg_var_as_dmatrix(lhs);
|
|
1457
|
+
const dcg_d_matrix_t* b = c_dcg_var_as_dmatrix(rhs);
|
|
1458
|
+
if (a == b) return true;
|
|
1459
|
+
if (!a || !b || a->n_rows != b->n_rows || a->n_cols != b->n_cols || a->row_major != b->row_major) return false;
|
|
1460
|
+
size_t total = a->n_rows * a->n_cols;
|
|
1461
|
+
return total == 0 || memcmp(a->data, b->data, total * sizeof(double)) == 0;
|
|
1462
|
+
}
|
|
1463
|
+
case VAR_TYPE_NODE:
|
|
1464
|
+
return c_dcg_var_as_node(lhs) == c_dcg_var_as_node(rhs);
|
|
1465
|
+
default:
|
|
1466
|
+
return c_dcg_var_as_ptr(lhs) == c_dcg_var_as_ptr(rhs);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
// ========== Reading ==========
|
|
1471
|
+
|
|
1472
|
+
/**
|
|
1473
|
+
* @brief Refuse a read the value's tag cannot answer - loudly, in vigilant mode.
|
|
1474
|
+
*
|
|
1475
|
+
* Called by a reader that has already found the tag is not one it can read and
|
|
1476
|
+
* not a reference to one. In vigilant mode (DCG_VIGILANT, on by default) this
|
|
1477
|
+
* names the reader and the tag on stderr and aborts; with DCG_VIGILANT = 0 the
|
|
1478
|
+
* message is compiled out and the reader returns its empty value.
|
|
1479
|
+
*
|
|
1480
|
+
* @param getter Name of the reader that was asked.
|
|
1481
|
+
* @param dtype Tag of the value it was asked about.
|
|
1482
|
+
* @param what What is wrong with the read, for the message.
|
|
1483
|
+
*/
|
|
1484
|
+
static inline void c_dcg_var_vigilant_abort(const char* getter, dcg_var_type dtype, const char* what) {
|
|
1485
|
+
#if DCG_VIGILANT
|
|
1486
|
+
(void) fprintf(stderr, "[DCG] %s: %s (tag %s) - invalid access\n", getter, what, c_dcg_var_type_name(dtype));
|
|
1487
|
+
(void) fflush(stderr);
|
|
1488
|
+
abort();
|
|
1489
|
+
#else
|
|
1490
|
+
(void) getter;
|
|
1491
|
+
(void) dtype;
|
|
1492
|
+
(void) what;
|
|
1493
|
+
#endif
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
/**
|
|
1497
|
+
* @brief The slot a live reference holds - refusing one that points at nothing.
|
|
1498
|
+
*
|
|
1499
|
+
* A reference with a NULL slot is not a value that reads as absent, it is a
|
|
1500
|
+
* reference that was never pointed at anything (or was pointed at a slot that
|
|
1501
|
+
* has since been cleared): reading through it is a defect, so in vigilant mode
|
|
1502
|
+
* this names the reader and refuses before the dereference happens. With
|
|
1503
|
+
* DCG_VIGILANT = 0 it answers NULL and the caller reads the empty value.
|
|
1504
|
+
*
|
|
1505
|
+
* @param var Reference to read the slot of.
|
|
1506
|
+
* @param getter Name of the reader that is asking, for the message.
|
|
1507
|
+
* @return The slot, or NULL when the reference points at nothing.
|
|
1508
|
+
*/
|
|
1509
|
+
static inline const void* c_dcg_var_ref_slot(const dcg_var_t* var, const char* getter) {
|
|
1510
|
+
if (var->value.as_ref) return var->value.as_ref;
|
|
1511
|
+
|
|
1512
|
+
c_dcg_var_vigilant_abort(getter, var->dtype, "the reference points at nothing");
|
|
1513
|
+
return NULL; /* with the vigil off */
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
/**
|
|
1517
|
+
* @brief Boolean view of a value.
|
|
1518
|
+
*
|
|
1519
|
+
* Never refuses: every tag has a truth value, so this is the one reader that can
|
|
1520
|
+
* always answer.
|
|
1521
|
+
*
|
|
1522
|
+
* @param var Value to read (NULL-safe).
|
|
1523
|
+
* @return c_dcg_var_is_truthy() of the value.
|
|
1524
|
+
*/
|
|
1525
|
+
static inline bool c_dcg_var_as_bool(const dcg_var_t* var) {
|
|
1526
|
+
return c_dcg_var_is_truthy(var);
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
/**
|
|
1530
|
+
* @brief Numeric view of a value (coercing).
|
|
1531
|
+
*
|
|
1532
|
+
* A reference is followed to the number behind it, so an operand that is a
|
|
1533
|
+
* reference into the caller's environment evaluates like a plain operand. The
|
|
1534
|
+
* number is read out of the slot, never copied out and kept: there is nothing
|
|
1535
|
+
* to own, and the next call sees whatever the slot holds then.
|
|
1536
|
+
*
|
|
1537
|
+
* @param var Value to read (NULL-safe).
|
|
1538
|
+
* @return The payload as double; 0.0 for a non-numeric or NULL value.
|
|
1539
|
+
*/
|
|
1540
|
+
static inline double c_dcg_var_as_double(const dcg_var_t* var) {
|
|
1541
|
+
if (!var) return 0.0;
|
|
1542
|
+
switch (var->dtype) {
|
|
1543
|
+
case VAR_TYPE_DOUBLE:
|
|
1544
|
+
return var->value.as_double;
|
|
1545
|
+
case VAR_TYPE_INT:
|
|
1546
|
+
return (double) var->value.as_int;
|
|
1547
|
+
case VAR_TYPE_OFFSET:
|
|
1548
|
+
return (double) var->value.as_offset;
|
|
1549
|
+
case VAR_TYPE_BOOL:
|
|
1550
|
+
return var->value.as_bool ? 1.0 : 0.0;
|
|
1551
|
+
|
|
1552
|
+
/* Level 1 is the common case - an operand reaching into the caller's
|
|
1553
|
+
* values outnumbers one holding its own - so it reads the slot right
|
|
1554
|
+
* here, and only deeper ladders pay for the walk below. */
|
|
1555
|
+
case VAR_TYPE_DOUBLE_REF: {
|
|
1556
|
+
const double* at = (const double*) c_dcg_var_ref_slot(var, "c_dcg_var_as_double");
|
|
1557
|
+
return at ? *at : 0.0; // NULL only with the vigil off
|
|
1558
|
+
}
|
|
1559
|
+
case VAR_TYPE_INT_REF:
|
|
1560
|
+
case VAR_TYPE_OFFSET_REF: {
|
|
1561
|
+
const ssize_t* at = (const ssize_t*) c_dcg_var_ref_slot(var, "c_dcg_var_as_double");
|
|
1562
|
+
return at ? (double) *at : 0.0;
|
|
1563
|
+
}
|
|
1564
|
+
case VAR_TYPE_BOOL_REF: {
|
|
1565
|
+
const bool* at = (const bool*) c_dcg_var_ref_slot(var, "c_dcg_var_as_double");
|
|
1566
|
+
return at ? (*at ? 1.0 : 0.0) : 0.0;
|
|
1567
|
+
}
|
|
1568
|
+
default:
|
|
1569
|
+
break;
|
|
1570
|
+
}
|
|
1571
|
+
/* Not a number by tag: only a reference to one - or to a bool, which counts
|
|
1572
|
+
* as one - has an answer to give. */
|
|
1573
|
+
dcg_var_type base = c_dcg_var_ref_base(var->dtype);
|
|
1574
|
+
if (base != VAR_TYPE_DOUBLE && base != VAR_TYPE_INT && base != VAR_TYPE_OFFSET && base != VAR_TYPE_BOOL) c_dcg_var_vigilant_abort("c_dcg_var_as_double", var->dtype, "cannot read this type");
|
|
1575
|
+
|
|
1576
|
+
if (c_dcg_var_is_ref(var->dtype)) {
|
|
1577
|
+
/* Level 2 and deeper: the slot holds the pointer to the number, one star
|
|
1578
|
+
* per rung after that, so hop and then read what the tag names. */
|
|
1579
|
+
const void* slot = var->value.as_ref;
|
|
1580
|
+
for (int level = c_dcg_var_ref_level(var->dtype); level > 1 && slot; level--) slot = *(const void* const*) slot;
|
|
1581
|
+
if (!slot) {
|
|
1582
|
+
c_dcg_var_vigilant_abort("c_dcg_var_as_double", var->dtype, "the reference points at nothing");
|
|
1583
|
+
return 0.0; // with the vigil off
|
|
1584
|
+
}
|
|
1585
|
+
switch (base) {
|
|
1586
|
+
case VAR_TYPE_DOUBLE:
|
|
1587
|
+
return *(const double*) slot;
|
|
1588
|
+
case VAR_TYPE_INT:
|
|
1589
|
+
case VAR_TYPE_OFFSET:
|
|
1590
|
+
return (double) *(const ssize_t*) slot;
|
|
1591
|
+
case VAR_TYPE_BOOL:
|
|
1592
|
+
return *(const bool*) slot ? 1.0 : 0.0;
|
|
1593
|
+
default:
|
|
1594
|
+
return 0.0; // unreachable in vigilant mode
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
return 0.0;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
/**
|
|
1601
|
+
* @brief Integer view of a value (coercing, doubles truncate toward zero).
|
|
1602
|
+
*
|
|
1603
|
+
* @param var Value to read (NULL-safe).
|
|
1604
|
+
* @return The payload as ssize_t; 0 for a non-numeric or NULL value.
|
|
1605
|
+
*/
|
|
1606
|
+
static inline ssize_t c_dcg_var_as_int(const dcg_var_t* var) {
|
|
1607
|
+
if (!var) return 0;
|
|
1608
|
+
switch (var->dtype) {
|
|
1609
|
+
case VAR_TYPE_INT:
|
|
1610
|
+
return var->value.as_int;
|
|
1611
|
+
case VAR_TYPE_OFFSET:
|
|
1612
|
+
return var->value.as_offset;
|
|
1613
|
+
case VAR_TYPE_DOUBLE:
|
|
1614
|
+
return (ssize_t) var->value.as_double;
|
|
1615
|
+
case VAR_TYPE_BOOL:
|
|
1616
|
+
return var->value.as_bool ? 1 : 0;
|
|
1617
|
+
|
|
1618
|
+
/* Level 1 reads the slot right here; only deeper ladders walk below. */
|
|
1619
|
+
case VAR_TYPE_INT_REF:
|
|
1620
|
+
case VAR_TYPE_OFFSET_REF: {
|
|
1621
|
+
const ssize_t* at = (const ssize_t*) c_dcg_var_ref_slot(var, "c_dcg_var_as_int");
|
|
1622
|
+
return at ? *at : 0; // NULL only with the vigil off
|
|
1623
|
+
}
|
|
1624
|
+
case VAR_TYPE_DOUBLE_REF: {
|
|
1625
|
+
const double* at = (const double*) c_dcg_var_ref_slot(var, "c_dcg_var_as_int");
|
|
1626
|
+
return at ? (ssize_t) *at : 0;
|
|
1627
|
+
}
|
|
1628
|
+
case VAR_TYPE_BOOL_REF: {
|
|
1629
|
+
const bool* at = (const bool*) c_dcg_var_ref_slot(var, "c_dcg_var_as_int");
|
|
1630
|
+
return at ? (*at ? 1 : 0) : 0;
|
|
1631
|
+
}
|
|
1632
|
+
default:
|
|
1633
|
+
break;
|
|
1634
|
+
}
|
|
1635
|
+
dcg_var_type base = c_dcg_var_ref_base(var->dtype);
|
|
1636
|
+
if (base != VAR_TYPE_INT && base != VAR_TYPE_OFFSET && base != VAR_TYPE_DOUBLE && base != VAR_TYPE_BOOL) c_dcg_var_vigilant_abort("c_dcg_var_as_int", var->dtype, "cannot read this type");
|
|
1637
|
+
|
|
1638
|
+
if (c_dcg_var_is_ref(var->dtype)) {
|
|
1639
|
+
const void* slot = var->value.as_ref;
|
|
1640
|
+
for (int level = c_dcg_var_ref_level(var->dtype); level > 1 && slot; level--) slot = *(const void* const*) slot;
|
|
1641
|
+
if (!slot) {
|
|
1642
|
+
c_dcg_var_vigilant_abort("c_dcg_var_as_int", var->dtype, "the reference points at nothing");
|
|
1643
|
+
return 0; // with the vigil off
|
|
1644
|
+
}
|
|
1645
|
+
switch (base) {
|
|
1646
|
+
case VAR_TYPE_INT:
|
|
1647
|
+
case VAR_TYPE_OFFSET:
|
|
1648
|
+
return *(const ssize_t*) slot;
|
|
1649
|
+
case VAR_TYPE_DOUBLE:
|
|
1650
|
+
return (ssize_t) * (const double*) slot;
|
|
1651
|
+
case VAR_TYPE_BOOL:
|
|
1652
|
+
return *(const bool*) slot ? 1 : 0;
|
|
1653
|
+
default:
|
|
1654
|
+
return 0; // unreachable in vigilant mode
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
return 0;
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
/**
|
|
1661
|
+
* @brief Offset view of a value (coercing).
|
|
1662
|
+
*
|
|
1663
|
+
* @param var Value to read (NULL-safe).
|
|
1664
|
+
* @return The payload as ssize_t; 0 for a non-numeric or NULL value.
|
|
1665
|
+
*/
|
|
1666
|
+
static inline ssize_t c_dcg_var_as_offset(const dcg_var_t* var) {
|
|
1667
|
+
return c_dcg_var_as_int(var);
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
/**
|
|
1671
|
+
* @brief String view of a value (tag-faithful, no coercion).
|
|
1672
|
+
*
|
|
1673
|
+
* A reference tag is followed to the string it points at - one hop for a _REF,
|
|
1674
|
+
* two for a _REF_REF - so the text read is whatever the slot holds at the
|
|
1675
|
+
* moment of the call. Every hop is NULL-checked, so a dangling slot reads as
|
|
1676
|
+
* NULL rather than crashing.
|
|
1677
|
+
*
|
|
1678
|
+
* @param var Value to read (NULL-safe).
|
|
1679
|
+
* @return The string, or NULL when the tag is not string-shaped.
|
|
1680
|
+
*/
|
|
1681
|
+
static inline const char* c_dcg_var_as_string(const dcg_var_t* var) {
|
|
1682
|
+
if (!var) return NULL;
|
|
1683
|
+
if (var->dtype == VAR_TYPE_STRING) return var->value.as_string;
|
|
1684
|
+
if (var->dtype == VAR_TYPE_STRING_REF) { // level 1: the slot holds the text
|
|
1685
|
+
const char* const* at = (const char* const*) c_dcg_var_ref_slot(var, "c_dcg_var_as_string");
|
|
1686
|
+
return at ? *at : NULL;
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
/* Not a string by tag: only a reference to one has an answer to give. */
|
|
1690
|
+
if (c_dcg_var_ref_base(var->dtype) != VAR_TYPE_STRING) c_dcg_var_vigilant_abort("c_dcg_var_as_string", var->dtype, "cannot read this type");
|
|
1691
|
+
|
|
1692
|
+
if (c_dcg_var_is_ref(var->dtype)) {
|
|
1693
|
+
const void* slot = var->value.as_ref;
|
|
1694
|
+
for (int level = c_dcg_var_ref_level(var->dtype); level > 1 && slot; level--) slot = *(const void* const*) slot;
|
|
1695
|
+
if (!slot) {
|
|
1696
|
+
c_dcg_var_vigilant_abort("c_dcg_var_as_string", var->dtype, "the reference points at nothing");
|
|
1697
|
+
return NULL; // with the vigil off
|
|
1698
|
+
}
|
|
1699
|
+
return *(const char* const*) slot;
|
|
1700
|
+
}
|
|
1701
|
+
return NULL;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
/**
|
|
1705
|
+
* @brief The pointer a reference tag holds, exactly as stored (no hop).
|
|
1706
|
+
*
|
|
1707
|
+
* A T* at level 1, a T** at level 2, one more star per rung - which is what
|
|
1708
|
+
* identity checks want: two references are the same reference when they are the
|
|
1709
|
+
* same address, whatever it points at. The value at the end of it is what the
|
|
1710
|
+
* readers return.
|
|
1711
|
+
*
|
|
1712
|
+
* @param var Value to read (NULL-safe).
|
|
1713
|
+
* @return The stored pointer, or NULL when the tag is not a reference.
|
|
1714
|
+
*/
|
|
1715
|
+
static inline const void* c_dcg_var_as_ref(const dcg_var_t* var) {
|
|
1716
|
+
if (!var) return NULL;
|
|
1717
|
+
if (!c_dcg_var_is_ref(var->dtype)) c_dcg_var_vigilant_abort("c_dcg_var_as_ref", var->dtype, "cannot read this type");
|
|
1718
|
+
return var->value.as_ref;
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
/**
|
|
1722
|
+
* @brief The node a node-shaped value holds, as an opaque address.
|
|
1723
|
+
*
|
|
1724
|
+
* The reader for the VAR_TYPE_NODE tag, and the one place the value layer hands a
|
|
1725
|
+
* node pointer back: what to do with it is the wrapper layer's business - it is
|
|
1726
|
+
* the address a wrapper is looked up by (see c_dcg_var_pyunpack). A reference to
|
|
1727
|
+
* a node is followed like any other.
|
|
1728
|
+
*
|
|
1729
|
+
* @param var Value to read (NULL-safe).
|
|
1730
|
+
* @return The node pointer, or NULL when there is none.
|
|
1731
|
+
*/
|
|
1732
|
+
static inline const dcg_node* c_dcg_var_as_node(const dcg_var_t* var) {
|
|
1733
|
+
if (!var) return NULL;
|
|
1734
|
+
switch (var->dtype) {
|
|
1735
|
+
case VAR_TYPE_NODE:
|
|
1736
|
+
return var->value.as_node;
|
|
1737
|
+
|
|
1738
|
+
case VAR_TYPE_NODE_REF: {
|
|
1739
|
+
const dcg_node* const* at = (const dcg_node* const*) c_dcg_var_ref_slot(var, "c_dcg_var_as_node");
|
|
1740
|
+
return at ? *at : NULL;
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
default:
|
|
1744
|
+
break;
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
if (c_dcg_var_ref_base(var->dtype) != VAR_TYPE_NODE) c_dcg_var_vigilant_abort("c_dcg_var_as_node", var->dtype, "cannot read this type");
|
|
1748
|
+
|
|
1749
|
+
if (c_dcg_var_is_ref(var->dtype)) {
|
|
1750
|
+
const void* slot = var->value.as_ref;
|
|
1751
|
+
for (int level = c_dcg_var_ref_level(var->dtype); level > 1 && slot; level--) slot = *(const void* const*) slot;
|
|
1752
|
+
if (!slot) {
|
|
1753
|
+
c_dcg_var_vigilant_abort("c_dcg_var_as_node", var->dtype, "the reference points at nothing");
|
|
1754
|
+
return NULL; // with the vigil off
|
|
1755
|
+
}
|
|
1756
|
+
return *(const dcg_node* const*) slot;
|
|
1757
|
+
}
|
|
1758
|
+
return NULL;
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
/**
|
|
1762
|
+
* @brief Raw-pointer view of a value (tag-faithful).
|
|
1763
|
+
*
|
|
1764
|
+
* @param var Value to read (NULL-safe).
|
|
1765
|
+
* @return The pointer payload, or NULL when the tag is not pointer-shaped.
|
|
1766
|
+
*/
|
|
1767
|
+
static inline void* c_dcg_var_as_ptr(const dcg_var_t* var) {
|
|
1768
|
+
if (!var) return NULL;
|
|
1769
|
+
switch (var->dtype) {
|
|
1770
|
+
case VAR_TYPE_RAW_PTR:
|
|
1771
|
+
case VAR_TYPE_TIME:
|
|
1772
|
+
case VAR_TYPE_DATE:
|
|
1773
|
+
case VAR_TYPE_DATETIME:
|
|
1774
|
+
return var->value.as_ptr;
|
|
1775
|
+
|
|
1776
|
+
/* Level 1 of the pointer-shaped tags: the slot holds the pointer. */
|
|
1777
|
+
case VAR_TYPE_RAW_PTR_REF:
|
|
1778
|
+
case VAR_TYPE_TIME_REF:
|
|
1779
|
+
case VAR_TYPE_DATE_REF:
|
|
1780
|
+
case VAR_TYPE_DATETIME_REF: {
|
|
1781
|
+
void* const* at = (void* const*) c_dcg_var_ref_slot(var, "c_dcg_var_as_ptr");
|
|
1782
|
+
return at ? *at : NULL;
|
|
1783
|
+
}
|
|
1784
|
+
default:
|
|
1785
|
+
break;
|
|
1786
|
+
}
|
|
1787
|
+
/* Not pointer-shaped by tag: only a reference to something that is has an
|
|
1788
|
+
* answer to give. */
|
|
1789
|
+
dcg_var_type base = c_dcg_var_ref_base(var->dtype);
|
|
1790
|
+
if (base != VAR_TYPE_RAW_PTR && base != VAR_TYPE_TIME && base != VAR_TYPE_DATE && base != VAR_TYPE_DATETIME) c_dcg_var_vigilant_abort("c_dcg_var_as_ptr", var->dtype, "cannot read this type");
|
|
1791
|
+
|
|
1792
|
+
if (c_dcg_var_is_ref(var->dtype)) {
|
|
1793
|
+
const void* slot = var->value.as_ref;
|
|
1794
|
+
for (int level = c_dcg_var_ref_level(var->dtype); level > 1 && slot; level--) slot = *(const void* const*) slot;
|
|
1795
|
+
if (!slot) {
|
|
1796
|
+
c_dcg_var_vigilant_abort("c_dcg_var_as_ptr", var->dtype, "the reference points at nothing");
|
|
1797
|
+
return NULL; // with the vigil off
|
|
1798
|
+
}
|
|
1799
|
+
return *(void* const*) slot;
|
|
1800
|
+
}
|
|
1801
|
+
return NULL;
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
/**
|
|
1805
|
+
* @brief Vector view of a value (tag-faithful).
|
|
1806
|
+
*
|
|
1807
|
+
* @param var Value to read (NULL-safe).
|
|
1808
|
+
* @return The vector, or NULL when the tag is not a vector or a reference to one.
|
|
1809
|
+
*/
|
|
1810
|
+
static inline dcg_d_vector_t* c_dcg_var_as_dvector(const dcg_var_t* var) {
|
|
1811
|
+
if (!var) return NULL;
|
|
1812
|
+
if (var->dtype == VAR_TYPE_D_VECTOR) return var->value.as_dvector;
|
|
1813
|
+
if (var->dtype == VAR_TYPE_D_VECTOR_REF) { // level 1: the slot holds the container
|
|
1814
|
+
dcg_d_vector_t* const* at = (dcg_d_vector_t* const*) c_dcg_var_ref_slot(var, "c_dcg_var_as_dvector");
|
|
1815
|
+
return at ? *at : NULL;
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
/* Not a vector by tag: only a reference to one has an answer to give. */
|
|
1819
|
+
if (c_dcg_var_ref_base(var->dtype) != VAR_TYPE_D_VECTOR) c_dcg_var_vigilant_abort("c_dcg_var_as_dvector", var->dtype, "cannot read this type");
|
|
1820
|
+
|
|
1821
|
+
if (c_dcg_var_is_ref(var->dtype)) {
|
|
1822
|
+
const void* slot = var->value.as_ref;
|
|
1823
|
+
for (int level = c_dcg_var_ref_level(var->dtype); level > 1 && slot; level--) slot = *(const void* const*) slot;
|
|
1824
|
+
if (!slot) {
|
|
1825
|
+
c_dcg_var_vigilant_abort("c_dcg_var_as_dvector", var->dtype, "the reference points at nothing");
|
|
1826
|
+
return NULL; // with the vigil off
|
|
1827
|
+
}
|
|
1828
|
+
return *(dcg_d_vector_t* const*) slot;
|
|
1829
|
+
}
|
|
1830
|
+
return NULL;
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
/**
|
|
1834
|
+
* @brief Matrix view of a value (tag-faithful).
|
|
1835
|
+
*
|
|
1836
|
+
* @param var Value to read (NULL-safe).
|
|
1837
|
+
* @return The matrix, or NULL when the tag is not a matrix or a reference to one.
|
|
1838
|
+
*/
|
|
1839
|
+
static inline dcg_d_matrix_t* c_dcg_var_as_dmatrix(const dcg_var_t* var) {
|
|
1840
|
+
if (!var) return NULL;
|
|
1841
|
+
if (var->dtype == VAR_TYPE_D_MATRIX) return var->value.as_dmatrix;
|
|
1842
|
+
if (var->dtype == VAR_TYPE_D_MATRIX_REF) { // level 1: the slot holds the container
|
|
1843
|
+
dcg_d_matrix_t* const* at = (dcg_d_matrix_t* const*) c_dcg_var_ref_slot(var, "c_dcg_var_as_dmatrix");
|
|
1844
|
+
return at ? *at : NULL;
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
/* Not a matrix by tag: only a reference to one has an answer to give. */
|
|
1848
|
+
if (c_dcg_var_ref_base(var->dtype) != VAR_TYPE_D_MATRIX) c_dcg_var_vigilant_abort("c_dcg_var_as_dmatrix", var->dtype, "cannot read this type");
|
|
1849
|
+
|
|
1850
|
+
if (c_dcg_var_is_ref(var->dtype)) {
|
|
1851
|
+
const void* slot = var->value.as_ref;
|
|
1852
|
+
for (int level = c_dcg_var_ref_level(var->dtype); level > 1 && slot; level--) slot = *(const void* const*) slot;
|
|
1853
|
+
if (!slot) {
|
|
1854
|
+
c_dcg_var_vigilant_abort("c_dcg_var_as_dmatrix", var->dtype, "the reference points at nothing");
|
|
1855
|
+
return NULL; // with the vigil off
|
|
1856
|
+
}
|
|
1857
|
+
return *(dcg_d_matrix_t* const*) slot;
|
|
1858
|
+
}
|
|
1859
|
+
return NULL;
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
/**
|
|
1863
|
+
* @brief Retag a value, converting the payload when the tags differ.
|
|
1864
|
+
*
|
|
1865
|
+
* Supported conversions: within the scalar family (bool / int / offset /
|
|
1866
|
+
* double) and from any pointer-shaped tag to RAW_PTR. Strings and containers
|
|
1867
|
+
* are never silently reinterpreted: a baked graph keeps its dtypes honest, so
|
|
1868
|
+
* a mismatch surfaces at bake time rather than producing 0.0 at eval time.
|
|
1869
|
+
*
|
|
1870
|
+
* A reference is followed on the way in, so casting a reference to a number
|
|
1871
|
+
* gives the number the slot holds. The way back does not exist: a reference tag
|
|
1872
|
+
* is a borrowed slot, and only c_dcg_var_init_ref() hands one over.
|
|
1873
|
+
*
|
|
1874
|
+
* @param out Receives the converted value (NULL-safe: skipped when NULL).
|
|
1875
|
+
* @param var Value to convert.
|
|
1876
|
+
* @param dtype Target tag.
|
|
1877
|
+
* @return DCG_OK on success, DCG_ERR_INVALID_ARG / DCG_ERR_BAD_CAST otherwise.
|
|
1878
|
+
*/
|
|
1879
|
+
static inline int c_dcg_var_cast(dcg_var_t* out, const dcg_var_t* var, dcg_var_type dtype) {
|
|
1880
|
+
if (!var) return DCG_ERR_INVALID_ARG;
|
|
1881
|
+
|
|
1882
|
+
dcg_var_t converted;
|
|
1883
|
+
(void) c_dcg_var_init(&converted);
|
|
1884
|
+
converted.dtype = dtype;
|
|
1885
|
+
|
|
1886
|
+
/* What the source really is: its own tag, or the tag behind its reference.
|
|
1887
|
+
* Every branch below reads through the readers, which follow the reference
|
|
1888
|
+
* themselves - so nothing is ever resolved into a value of its own. */
|
|
1889
|
+
dcg_var_type base = c_dcg_var_ref_base(var->dtype);
|
|
1890
|
+
|
|
1891
|
+
if (base == VAR_TYPE_RESERVED) return DCG_ERR_TYPE; // nothing is there to convert yet
|
|
1892
|
+
|
|
1893
|
+
if (var->dtype == dtype) {
|
|
1894
|
+
converted = *var; // Same tag: a copy of the value, a reference included.
|
|
1895
|
+
}
|
|
1896
|
+
else if (c_dcg_var_is_ref(dtype)) {
|
|
1897
|
+
return DCG_ERR_BAD_CAST; // never synthesized - only init_ref / init_ref_raw hand one over
|
|
1898
|
+
}
|
|
1899
|
+
else if (dtype == VAR_TYPE_D_VECTOR || dtype == VAR_TYPE_D_MATRIX) {
|
|
1900
|
+
if (base != dtype) return DCG_ERR_BAD_CAST; // a vector is not a matrix
|
|
1901
|
+
if (dtype == VAR_TYPE_D_VECTOR) converted.value.as_dvector = c_dcg_var_as_dvector(var);
|
|
1902
|
+
else converted.value.as_dmatrix = c_dcg_var_as_dmatrix(var);
|
|
1903
|
+
}
|
|
1904
|
+
else if (dtype == VAR_TYPE_BOOL) {
|
|
1905
|
+
converted.value.as_bool = c_dcg_var_is_truthy(var);
|
|
1906
|
+
}
|
|
1907
|
+
else if (dtype == VAR_TYPE_INT || dtype == VAR_TYPE_OFFSET || dtype == VAR_TYPE_DOUBLE) {
|
|
1908
|
+
if (base == VAR_TYPE_STRING || c_dcg_var_is_container(var)) return DCG_ERR_BAD_CAST;
|
|
1909
|
+
if (dtype == VAR_TYPE_INT) converted.value.as_int = c_dcg_var_as_int(var);
|
|
1910
|
+
else if (dtype == VAR_TYPE_OFFSET) converted.value.as_offset = c_dcg_var_as_int(var);
|
|
1911
|
+
else converted.value.as_double = c_dcg_var_as_double(var);
|
|
1912
|
+
}
|
|
1913
|
+
else if (dtype == VAR_TYPE_RAW_PTR) {
|
|
1914
|
+
if (base == VAR_TYPE_STRING) converted.value.as_ptr = (void*) c_dcg_var_as_string(var);
|
|
1915
|
+
else if (base == VAR_TYPE_RAW_PTR || base == VAR_TYPE_TIME || base == VAR_TYPE_DATE || base == VAR_TYPE_DATETIME) converted.value.as_ptr = c_dcg_var_as_ptr(var);
|
|
1916
|
+
else return DCG_ERR_BAD_CAST;
|
|
1917
|
+
}
|
|
1918
|
+
else if (dtype == VAR_TYPE_STRING) {
|
|
1919
|
+
converted.value.as_string = c_dcg_var_as_string(var);
|
|
1920
|
+
}
|
|
1921
|
+
else {
|
|
1922
|
+
return DCG_ERR_BAD_CAST;
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
if (out) *out = converted;
|
|
1926
|
+
return DCG_OK;
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
/**
|
|
1930
|
+
* @brief Overwrite a slot with what another one reads as - the literal value.
|
|
1931
|
+
*
|
|
1932
|
+
* Two cases, and the first is the one that matters: a slot holding a VALUE is
|
|
1933
|
+
* copied as it stands - tag and payload, no conversion, no question asked. A
|
|
1934
|
+
* slot holding a REFERENCE is a pointer to a value that lives somewhere else, so
|
|
1935
|
+
* it is read THROUGH, into the snapshot, which then holds the value itself (see
|
|
1936
|
+
* c_dcg_var_cast). A reference to a slot that has no type yet has nothing at the
|
|
1937
|
+
* end of it to read, so the reference is copied as it stands and the snapshot
|
|
1938
|
+
* reports the same "nothing there" its source does.
|
|
1939
|
+
*
|
|
1940
|
+
* This is how a workspace is filled and how a value is recorded: a reader that
|
|
1941
|
+
* wants the VALUE rather than the slot it sits in, one branch wide, with no
|
|
1942
|
+
* error to report and no answer to wait for.
|
|
1943
|
+
*
|
|
1944
|
+
* @param snapshot Receives the value (never NULL; may be the same object as the
|
|
1945
|
+
* slot it reads, which is a no-op).
|
|
1946
|
+
* @param slot Slot to read (NULL leaves the snapshot empty).
|
|
1947
|
+
*/
|
|
1948
|
+
static inline void c_dcg_var_snapshot(dcg_var_t* snapshot, const dcg_var_t* slot) {
|
|
1949
|
+
if (!snapshot) return;
|
|
1950
|
+
if (!slot) {
|
|
1951
|
+
(void) c_dcg_var_init(snapshot);
|
|
1952
|
+
return;
|
|
1953
|
+
}
|
|
1954
|
+
if (!c_dcg_var_is_ref(slot->dtype)) {
|
|
1955
|
+
*snapshot = *slot;
|
|
1956
|
+
return;
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
dcg_var_type base = c_dcg_var_ref_base(slot->dtype);
|
|
1960
|
+
if (base == VAR_TYPE_RESERVED) {
|
|
1961
|
+
*snapshot = *slot; /* nothing has landed behind it yet */
|
|
1962
|
+
return;
|
|
1963
|
+
}
|
|
1964
|
+
(void) c_dcg_var_cast(snapshot, slot, base);
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
// ========== Output ==========
|
|
1968
|
+
|
|
1969
|
+
/**
|
|
1970
|
+
* @brief Format a value into a caller-provided buffer (renderer support).
|
|
1971
|
+
*
|
|
1972
|
+
* Strings render quoted, doubles with %g, pointers as 0x addresses, bools as
|
|
1973
|
+
* true/false, and containers as their shape with their first elements. A
|
|
1974
|
+
* reference renders as the value it points at - c_dcg_var_type_name() is where
|
|
1975
|
+
* the storage shows, so a renderer can tell a read-through from a plain value.
|
|
1976
|
+
*
|
|
1977
|
+
* @param var Value to format (NULL-safe: renders "(null)").
|
|
1978
|
+
* @param out Destination buffer.
|
|
1979
|
+
* @param cap Capacity of out.
|
|
1980
|
+
* @return Number of characters written (excluding NUL), or DCG_ERR_INVALID_ARG
|
|
1981
|
+
* / DCG_ERR_FORMAT.
|
|
1982
|
+
*/
|
|
1983
|
+
static inline int c_dcg_var_format(const dcg_var_t* var, char* out, size_t cap) {
|
|
1984
|
+
if (!out || cap == 0) return DCG_ERR_INVALID_ARG;
|
|
1985
|
+
if (!var) {
|
|
1986
|
+
int n = snprintf(out, cap, "(null)");
|
|
1987
|
+
return n < 0 ? DCG_ERR_FORMAT : n;
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
/* The tag behind any reference, or the tag itself: the readers follow the
|
|
1991
|
+
* reference, so the value at the end of it is what gets rendered. */
|
|
1992
|
+
int n = 0;
|
|
1993
|
+
switch (c_dcg_var_ref_base(var->dtype)) {
|
|
1994
|
+
case VAR_TYPE_RESERVED:
|
|
1995
|
+
n = snprintf(out, cap, "(reserved)");
|
|
1996
|
+
break;
|
|
1997
|
+
case VAR_TYPE_BOOL:
|
|
1998
|
+
n = snprintf(out, cap, "%s", c_dcg_var_as_bool(var) ? "true" : "false");
|
|
1999
|
+
break;
|
|
2000
|
+
case VAR_TYPE_INT:
|
|
2001
|
+
case VAR_TYPE_OFFSET:
|
|
2002
|
+
n = snprintf(out, cap, "%zd", c_dcg_var_as_int(var));
|
|
2003
|
+
break;
|
|
2004
|
+
case VAR_TYPE_DOUBLE:
|
|
2005
|
+
n = snprintf(out, cap, "%g", c_dcg_var_as_double(var));
|
|
2006
|
+
break;
|
|
2007
|
+
case VAR_TYPE_STRING: {
|
|
2008
|
+
const char* text = c_dcg_var_as_string(var);
|
|
2009
|
+
n = text ? snprintf(out, cap, "\"%s\"", text) : snprintf(out, cap, "NULL");
|
|
2010
|
+
break;
|
|
2011
|
+
}
|
|
2012
|
+
case VAR_TYPE_D_VECTOR: {
|
|
2013
|
+
const dcg_d_vector_t* vector = c_dcg_var_as_dvector(var);
|
|
2014
|
+
n = vector ? snprintf(out, cap, "d_vector(n=%zu)", vector->n) : snprintf(out, cap, "d_vector(NULL)");
|
|
2015
|
+
break;
|
|
2016
|
+
}
|
|
2017
|
+
case VAR_TYPE_D_MATRIX: {
|
|
2018
|
+
const dcg_d_matrix_t* matrix = c_dcg_var_as_dmatrix(var);
|
|
2019
|
+
n = matrix ? snprintf(out, cap, "d_matrix(%zux%zu)", matrix->n_rows, matrix->n_cols) : snprintf(out, cap, "d_matrix(NULL)");
|
|
2020
|
+
break;
|
|
2021
|
+
}
|
|
2022
|
+
case VAR_TYPE_NODE:
|
|
2023
|
+
n = snprintf(out, cap, "node 0x%zx", (size_t) (uintptr_t) c_dcg_var_as_node(var));
|
|
2024
|
+
break;
|
|
2025
|
+
case VAR_TYPE_RAW_PTR:
|
|
2026
|
+
case VAR_TYPE_TIME:
|
|
2027
|
+
case VAR_TYPE_DATE:
|
|
2028
|
+
case VAR_TYPE_DATETIME:
|
|
2029
|
+
n = snprintf(out, cap, "0x%zx", (size_t) (uintptr_t) c_dcg_var_as_ptr(var));
|
|
2030
|
+
break;
|
|
2031
|
+
default:
|
|
2032
|
+
n = snprintf(out, cap, "<invalid:%d>", (int) var->dtype);
|
|
2033
|
+
break;
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
return n < 0 ? DCG_ERR_FORMAT : n;
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
/**
|
|
2040
|
+
* @brief Print a value to a stream (rendering convenience).
|
|
2041
|
+
*
|
|
2042
|
+
* @param var Value to print (NULL-safe).
|
|
2043
|
+
* @param stream Destination stream.
|
|
2044
|
+
* @return Number of characters written, or DCG_ERR_INVALID_ARG / DCG_ERR_FORMAT.
|
|
2045
|
+
*/
|
|
2046
|
+
static inline int c_dcg_var_print(const dcg_var_t* var, FILE* stream) {
|
|
2047
|
+
if (!stream) return DCG_ERR_INVALID_ARG;
|
|
2048
|
+
char buf[DCG_VAR_STRING_MAXLEN];
|
|
2049
|
+
int n = c_dcg_var_format(var, buf, sizeof(buf));
|
|
2050
|
+
if (n < 0) return n;
|
|
2051
|
+
return fprintf(stream, "%s", buf);
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
#endif // C_DCG_BAKE_VAR_H
|