Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 71.5% 198 / 0 / 277
Functions: 75.0% 24 / 0 / 32
Branches: 55.9% 128 / 0 / 229

OMCompiler/SimulationRuntime/c/util/base_array.c
Line Branch Exec Source
1 /*
2 * This file belongs to the OpenModelica Run-Time System
3 *
4 * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC), c/o Linköpings
5 * universitet, Department of Computer and Information Science, SE-58183 Linköping, Sweden. All rights
6 * reserved.
7 *
8 * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF THE BSD NEW LICENSE OR THE
9 * AGPL VERSION 3 LICENSE OR THE OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8. ANY
10 * USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES RECIPIENT'S
11 * ACCEPTANCE OF THE BSD NEW LICENSE OR THE OSMC PUBLIC LICENSE OR THE AGPL
12 * VERSION 3, ACCORDING TO RECIPIENTS CHOICE.
13 *
14 * The OpenModelica software and the OSMC (Open Source Modelica Consortium) Public License
15 * (OSMC-PL) are obtained from OSMC, either from the above address, from the URLs:
16 * http://www.openmodelica.org or https://github.com/OpenModelica/ or
17 * http://www.ida.liu.se/projects/OpenModelica, and in the OpenModelica distribution. GNU
18 * AGPL version 3 is obtained from: https://www.gnu.org/licenses/licenses.html#GPL. The BSD NEW
19 * License is obtained from: http://www.opensource.org/licenses/BSD-3-Clause.
20 *
21 * This program is distributed WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY
23 * SET FORTH IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF
24 * OSMC-PL.
25 *
26 */
27
28
29 #include "base_array.h"
30 #if defined(OMC_METAMODELICA_RUNTIME)
31 /* The string vocabulary of this runtime; util/omc_string.h stands down here. */
32 #include "../meta/meta_modelica_string.h"
33 #endif
34 #include "index_spec.h"
35 #include "../gc/omc_gc.h"
36 #include "omc_string.h"
37 #include "omc_error.h"
38
39 #include <stdlib.h>
40 #include <stdio.h>
41 #include <assert.h>
42 #include <stdarg.h>
43
44
45 /**
46 * @brief Calculate flat index for 2D array element access.
47 *
48 * Converts 2D subscripts (i, j) to a flat 1D index using row-major order.
49 *
50 * @param dim Pointer to dimension sizes array (at least 2 elements).
51 * @param i Row index (0-based).
52 * @param j Column index (0-based).
53 *
54 * @return Flat 1D index for row-major storage.
55 *
56 * @note Uses row-major (C-style) array layout: index = i * dim[1] + j
57 */
58 75247 _index_t getIndex_2D(_index_t * dim, int i, int j) {
59 75247 return i * dim[1] + j;
60 }
61
62 /**
63 * @brief Calculate flat index for 3D array element access.
64 *
65 * Converts 3D subscripts (i, j, k) to a flat 1D index using row-major order.
66 *
67 * @param dim Pointer to dimension sizes array (at least 3 elements).
68 * @param i First dimension index (0-based).
69 * @param j Second dimension index (0-based).
70 * @param k Third dimension index (0-based).
71 *
72 * @return Flat 1D index for row-major storage.
73 *
74 * @note Uses row-major array layout: index = (i * dim[1] + j) * dim[2] + k
75 */
76 1061 _index_t getIndex_3D(_index_t * dim, int i, int j, int k) {
77 1061 return (i * dim[1] + j) * dim[2] + k;
78 }
79
80 /**
81 * @brief Calculate flat index for 4D array element access.
82 *
83 * Converts 4D subscripts (i, j, k, l) to a flat 1D index using row-major order.
84 *
85 * @param dim Pointer to dimension sizes array (at least 4 elements).
86 * @param i First dimension index (0-based).
87 * @param j Second dimension index (0-based).
88 * @param k Third dimension index (0-based).
89 * @param l Fourth dimension index (0-based).
90 *
91 * @return Flat 1D index for row-major storage.
92 */
93 ✗ _index_t getIndex_4D(_index_t * dim, int i, int j, int k, int l) {
94 ✗ return ((i * dim[1] + j) * dim[2] + k) * dim[3] + l;
95 }
96
97 /**
98 * @brief Calculate flat index for 5D array element access.
99 *
100 * Converts 5D subscripts (i, j, k, l, m) to a flat 1D index using row-major
101 * order.
102 *
103 * @param dim Pointer to dimension sizes array (at least 5 elements).
104 * @param i First dimension index (0-based).
105 * @param j Second dimension index (0-based).
106 * @param k Third dimension index (0-based).
107 * @param l Fourth dimension index (0-based).
108 * @param m Fifth dimension index (0-based).
109 *
110 * @return Flat 1D index for row-major storage.
111 */
112 ✗ _index_t getIndex_5D(_index_t * dim, int i, int j, int k, int l, int m) {
113 ✗ return (((i * dim[1] + j) * dim[2] + k) * dim[3] + l) * dim[4] + m;
114 }
115
116 /**
117 * @brief Calculate total number of elements in an array.
118 *
119 * Computes the product of all dimension sizes to get the total element count.
120 *
121 * @param a The base array structure.
122 *
123 * @return Total number of elements in the array.
124 */
125 39735226 _index_t base_array_nr_of_elements(const base_array_t a)
126 {
127 int i;
128 _index_t nr_of_elements = 1;
129
2/2
✓ Branch 0 taken 40262787 times.
✓ Branch 1 taken 39735226 times.
79998013 for(i = 0; i < a.ndims; ++i) {
130 40262787 nr_of_elements *= a.dim_size[i];
131 }
132 39735226 return nr_of_elements;
133 }
134
135 /**
136 * @brief Get the size of a specific dimension in an array.
137 *
138 * Returns the size of the i-th dimension (1-based indexing).
139 *
140 * @param a The base array structure.
141 * @param i Dimension index (1-based: 1 to ndims).
142 *
143 * @return Size of the specified dimension, or 0 if dimension index is out of bounds
144 * or any prior dimension has size 0.
145 *
146 * @attention Uses 1-based indexing for dimensions (Modelica convention).
147 */
148 39896380 _index_t size_of_dimension_base_array(const base_array_t a, int i)
149 {
150 /* assert(base_array_ok(&a)); */
151
2/4
✓ Branch 0 taken 39896380 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 39896380 times.
39896380 if ((i > 0) && (i <= a.ndims)) {
152 39896380 return a.dim_size[i-1];
153 }
154 /* This is a weird work-around to return 0 if the dimension is out of bounds and a dimension is 0
155 * The reason is that we lose the dimensions in the DAE.ARRAY after a 0-dimension
156 * Note: We return size(arr,2)=0 if arr has dimensions [0,2], and not the expected 2
157 */
158 ✗ for (i=0; i<a.ndims; i++) {
159 ✗ if (a.dim_size[i] == 0) {
160 return 0;
161 }
162 }
163 ✗ fprintf(stderr, "size_of_dimension_base_array failed for i=%d, ndims=%d (ndims out of bounds)\n", i, a.ndims);
164 ✗ abort();
165 }
166
167 /**
168 * @brief Initialize a base array structure with existing data and dimension information.
169 *
170 * Sets all fields in a base_array structure: data pointer, number of dimensions
171 * (ndims), and dimension sizes. The dimension sizes are extracted from a variable
172 * argument list passed as a va_list.
173 *
174 * @param dest Pointer to the base_array structure to initialize.
175 * @param data Pointer to the pre-allocated data buffer for array elements.
176 * @param ndims Number of dimensions.
177 * @param ap Variable argument list containing ndims dimension size values
178 * (each of type _index_t).
179 *
180 * @pre data pointer should be valid (typically allocated via malloc or gc).
181 * @pre ap must contain exactly ndims dimension size arguments of type _index_t.
182 *
183 * @note This is the low-level initialization function. Type-specific wrappers
184 * like real_array_create() typically call this function internally.
185 *
186 * @attention Sets dest->flexible to 0. The caller is responsible for ensuring
187 * the data buffer is large enough to hold all array elements.
188 */
189 19715007 void base_array_create(base_array_t *dest, void *data, int ndims, va_list ap)
190 {
191 int i;
192
193 19715007 dest->data = data;
194 19715007 dest->ndims = ndims;
195
196 19715007 dest->dim_size = size_alloc(ndims);
197
198
2/2
✓ Branch 0 taken 19860851 times.
✓ Branch 1 taken 19715007 times.
39575858 for(i = 0; i < ndims; ++i) {
199 19860851 dest->dim_size[i] = va_arg(ap, _index_t);
200 }
201
202 19715007 dest->flexible = 0;
203 19715007 dest->owns_data = 0;
204 19715007 }
205
206 /**
207 * @brief Validate that a base_array structure is well-formed.
208 *
209 * Performs comprehensive validity checks on all fields of the base_array structure.
210 *
211 * @param a Pointer to the base_array to validate.
212 *
213 * @return 1 if array is valid, 0 otherwise. Prints error messages to stderr for each
214 * validation failure.
215 *
216 * @attention This function performs diagnostic output to stderr. Failures indicate
217 * critical structural problems with the array.
218 */
219 41910102 int base_array_ok(const base_array_t *a)
220 {
221 int i;
222
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41910102 times.
41910102 if(a == NULL) {
223 ✗ fprintf(stderr, "base_array.c: array is NULL!\n"); fflush(stderr);
224 ✗ return 0;
225 }
226
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41910102 times.
41910102 if(a->ndims < 0) {
227 ✗ fprintf(stderr, "base_array.c: the number of array dimensions are < 0!\n"); fflush(stderr);
228 ✗ return 0;
229 }
230
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41910102 times.
41910102 if(a->dim_size == NULL) {
231 ✗ fprintf(stderr, "base_array.c: array dimensions sizes are NULL!\n"); fflush(stderr);
232 ✗ return 0;
233 }
234
2/2
✓ Branch 0 taken 42846593 times.
✓ Branch 1 taken 41910102 times.
84756695 for(i = 0; i < a->ndims; ++i) {
235
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42846593 times.
42846593 if(a->dim_size[i] < 0) {
236 ✗ fprintf(stderr, "base_array.c: array dimension size for dimension %d is %d < 0!\n", i, (int) a->dim_size[i]); fflush(stderr);
237 ✗ return 0;
238 }
239 }
240 return 1;
241 }
242
243 /**
244 * @brief Verify that multiple arrays have identical dimensions.
245 *
246 * Helper function for operations like concatenation. Asserts that all provided
247 * arrays have the same number of dimensions and matching dimension sizes.
248 *
249 * @param elts Array of base_array pointers to check.
250 * @param n Number of arrays in the elts array.
251 *
252 * @pre All pointers in elts must be valid and non-NULL.
253 *
254 * @note This function uses assertions and will abort if validation fails.
255 *
256 * @attention Used internally by array allocation and concatenation functions.
257 */
258 170189 void check_base_array_dim_sizes(const base_array_t *elts, int n)
259 {
260 int i, curdim;
261 170189 int ndims = elts[0].ndims;
262
2/2
✓ Branch 0 taken 274004 times.
✓ Branch 1 taken 170189 times.
444193 for(i = 1; i < n; ++i) {
263
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 274004 times.
274004 assert(elts[i].ndims == ndims && "Not same number of dimensions");
264 }
265
2/2
✓ Branch 0 taken 170189 times.
✓ Branch 1 taken 170189 times.
340378 for(curdim = 0; curdim < ndims; ++curdim) {
266 170189 int dimsize = elts[0].dim_size[curdim];
267
2/2
✓ Branch 0 taken 274004 times.
✓ Branch 1 taken 170189 times.
444193 for(i = 1; i < n; ++i) {
268
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 274004 times.
274004 assert(dimsize == elts[i].dim_size[curdim]
269 && "Dimensions size not same");
270 }
271 }
272 170189 }
273
274 /**
275 * @brief Verify that multiple arrays have identical dimensions except one.
276 *
277 * Helper function for concatenation operations. Asserts that all provided arrays
278 * have the same number of dimensions and matching dimension sizes for all dimensions
279 * except dimension k (1-based).
280 *
281 * @param k Dimension to exclude from comparison (1-based indexing).
282 * @param elts Array of base_array pointers to check.
283 * @param n Number of arrays in the elts array.
284 *
285 * @pre All pointers in elts must be valid and non-NULL.
286 *
287 * @note This function uses assertions and will abort if validation fails.
288 * @note Dimension k is allowed to differ in size across arrays.
289 *
290 * @attention Used internally by array concatenation functions like cat_alloc_real_array().
291 */
292 ✗ void check_base_array_dim_sizes_except(int k, const base_array_t *elts, int n)
293 {
294 int i, curdim, dimsize;
295 ✗ int k_loc = k - 1;
296 ✗ int ndims = elts[0].ndims;
297 ✗ for(i = 1; i < n; ++i) {
298 ✗ assert(elts[i].ndims == ndims && "Not same number of dimensions");
299 }
300 ✗ for(curdim = 0; curdim < ndims; ++curdim) {
301 ✗ if(curdim != k_loc) {
302 assert(elts);
303 ✗ assert(elts[0].dim_size[curdim]);
304 ✗ dimsize = elts[0].dim_size[curdim];
305
306 ✗ for(i = 1; i < n; ++i) {
307 ✗ assert(dimsize == elts[i].dim_size[curdim]
308 && "Dimensions size not same");
309 }
310 }
311 }
312 ✗ }
313
314 /**
315 * @brief Compare the shapes (dimensions) of two arrays for equality.
316 *
317 * Checks if two arrays have the same number of dimensions and all matching
318 * dimension sizes.
319 *
320 * @param a First array to compare.
321 * @param b Second array to compare.
322 *
323 * @return 1 if arrays have identical shapes, 0 otherwise. Prints diagnostic
324 * messages to stderr if shapes differ.
325 */
326 513 int base_array_shape_eq(const base_array_t *a, const base_array_t *b)
327 {
328 int i;
329
330
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 513 times.
513 if(a->ndims != b->ndims) {
331 ✗ fprintf(stderr, "a->ndims != b->ndims, %d != %d\n", a->ndims, b->ndims);
332 ✗ return 0;
333 }
334
335
2/2
✓ Branch 0 taken 513 times.
✓ Branch 1 taken 513 times.
1026 for(i = 0; i < a->ndims; ++i) {
336
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 513 times.
513 if(a->dim_size[i] != b->dim_size[i]) {
337 ✗ fprintf(stderr, "a->dim_size[%d] != b->dim_size[%d], %d != %d\n",
338 i, i, (int) a->dim_size[i], (int) b->dim_size[i]);
339 ✗ return 0;
340 }
341 }
342
343 return 1;
344 }
345
346 /**
347 * @brief Check if an array contains exactly one element.
348 *
349 * Verifies that all dimensions have size 1, indicating a single-element array
350 * (equivalent to a scalar in array form).
351 *
352 * @param a The array to check.
353 *
354 * @return 1 if array has exactly one element (all dims = 1), 0 otherwise.
355 */
356 ✗ int base_array_one_element_ok(const base_array_t *a)
357 {
358 int i;
359
360 ✗ for(i = 0; i < a->ndims; ++i) {
361 ✗ if(a->dim_size[i] != 1) {
362 return 0;
363 }
364 }
365 return 1;
366 }
367
368 /**
369 * @brief Validate that an index specification fits a base array.
370 *
371 * Checks that an index_spec_t structure is compatible with a base_array structure,
372 * verifying that dimensions agree and all indices are within valid bounds.
373 *
374 * @param s Index specification to validate.
375 * @param a Base array to validate against.
376 *
377 * @return 1 if index spec fits the array, 0 otherwise. Prints diagnostic error
378 * messages to stderr if validation fails.
379 *
380 * @attention Used to validate array indexing operations before execution.
381 */
382 275384 int index_spec_fit_base_array(const index_spec_t *s, const base_array_t *a)
383 {
384 int i, j;
385
386
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 275384 times.
275384 if(s->ndims != a->ndims) {
387 ✗ fprintf(stderr, "index spec dimensions and array dimensions do not agree %d != %d\n",
388 ✗ (int)s->ndims, (int)a->ndims); fflush(stderr);
389 ✗ return 0;
390 }
391
2/2
✓ Branch 0 taken 414766 times.
✓ Branch 1 taken 275384 times.
690150 for(i = 0; i < s->ndims; ++i) {
392
2/2
✓ Branch 0 taken 138879 times.
✓ Branch 1 taken 275887 times.
414766 if(s->index_type[i] == 'S') {
393
1/2
✓ Branch 0 taken 138879 times.
✗ Branch 1 not taken.
138879 if (s->index[i] != NULL)
394 {
395
2/4
✓ Branch 0 taken 138879 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 138879 times.
138879 if((s->index[i][0] < 0) || (s->index[i][0] > a->dim_size[i])) {
396 ✗ fprintf(stderr,
397 "scalar s->index[%d][0] == %d incorrect, a->dim_size[%d] == %d\n",
398 ✗ i, (int) s->index[i][0], i, (int) a->dim_size[i]); fflush(stderr);
399 ✗ return 0;
400 }
401 }
402 }
403
404
2/2
✓ Branch 0 taken 273598 times.
✓ Branch 1 taken 141168 times.
414766 if(s->index[i] != NULL)
405 {
406
2/2
✓ Branch 0 taken 767535 times.
✓ Branch 1 taken 273598 times.
1041133 for(j = 0; j < s->dim_size[i]; ++j) {
407
2/4
✓ Branch 0 taken 767535 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 767535 times.
767535 if((s->index[i][j] <= 0) || (s->index[i][j] > a->dim_size[i])) {
408 ✗ fprintf(stderr,
409 "array s->index[%d][%d] == %d incorrect, a->dim_size[%d] == %d\n",
410 ✗ i, j, (int) s->index[i][j], i, (int) a->dim_size[i]); fflush(stderr);
411 ✗ return 0;
412 }
413 }
414 }
415 }
416
417 return 1;
418 }
419
420 /**
421 * @brief Number of elements an index specification selects.
422 *
423 * @param s Index specification, fitting a.
424 * @param a Array that s indexes.
425 * @return The product of the sizes of the dimensions s keeps.
426 */
427 24016 _index_t index_spec_nr_of_elements(const index_spec_t *s, const base_array_t *a)
428 {
429 int i;
430 _index_t n = 1;
431
432
2/2
✓ Branch 0 taken 48032 times.
✓ Branch 1 taken 24016 times.
72048 for(i = 0; i < s->ndims; ++i) {
433
3/3
✓ Branch 0 taken 8882 times.
✓ Branch 1 taken 15637 times.
✓ Branch 2 taken 23513 times.
48032 switch(s->index_type[i]) {
434 8882 case 'W': n *= a->dim_size[i]; break;
435 15637 case 'A': n *= s->dim_size[i]; break;
436 default: break;
437 }
438 }
439 24016 return n;
440 }
441
442 /**
443 * @brief Initialize a 1D base array with existing data.
444 *
445 * Convenience function for creating a simple 1D array structure with pre-allocated data.
446 *
447 * @param dest Pointer to the base_array structure to initialize.
448 * @param n Size of the 1D array (number of elements).
449 * @param data Pointer to the pre-allocated data buffer.
450 */
451 24323743 void simple_alloc_1d_base_array(base_array_t *dest, int n, void *data)
452 {
453 24323743 dest->ndims = 1;
454 24323743 dest->dim_size = size_alloc(1);
455 24323743 dest->dim_size[0] = n;
456 24323743 dest->data = data;
457 24323743 dest->flexible = 0;
458 24323743 dest->owns_data = 1;
459 24323743 }
460
461 /**
462 * @brief Initialize a 2D base array with existing data.
463 *
464 * Convenience function for creating a simple 2D array structure with pre-allocated data.
465 *
466 * @param dest Pointer to the base_array structure to initialize.
467 * @param r Number of rows.
468 * @param c Number of columns.
469 * @param data Pointer to the pre-allocated data buffer.
470 */
471 3557 void simple_alloc_2d_base_array(base_array_t *dest, int r, int c, void *data)
472 {
473 3557 dest->ndims = 2;
474 3557 dest->dim_size = size_alloc(2);
475 3557 dest->dim_size[0] = r;
476 3557 dest->dim_size[1] = c;
477 3557 dest->data = data;
478 3557 dest->flexible = 0;
479 3557 dest->owns_data = 1;
480 3557 }
481
482 /**
483 * @brief Allocate and initialize a base array structure with variable dimensions.
484 *
485 * Initializes a base_array structure by setting the number of dimensions and
486 * allocating space for dimension sizes. The dimension sizes are extracted from
487 * a variable argument list. Computes the total number of elements needed.
488 *
489 * @param dest Pointer to the base_array structure to initialize.
490 * @param ndims Number of dimensions.
491 * @param ap Variable argument list containing ndims dimension size values
492 * (each of type _index_t).
493 *
494 * @return Total number of elements in the array (product of all dimension sizes).
495 *
496 * @pre ap must contain exactly ndims dimension size arguments of type _index_t.
497 *
498 * @note The caller is responsible for allocating the actual data buffer separately
499 * and assigning it to dest->data. This function only allocates the metadata
500 * (dimension information).
501 *
502 * @attention Sets dest->flexible to 0. Used internally by type-specific allocation
503 * functions like alloc_real_array().
504 */
505 5370891 size_t alloc_base_array(base_array_t *dest, int ndims, va_list ap)
506 {
507 int i;
508 size_t nr_of_elements = 1;
509
510 5370891 dest->ndims = ndims;
511 5370891 dest->dim_size = size_alloc(ndims);
512
513
2/2
✓ Branch 0 taken 5726224 times.
✓ Branch 1 taken 5370891 times.
11097115 for(i = 0; i < ndims; ++i) {
514 5726224 dest->dim_size[i] = va_arg(ap, _index_t);
515 5726224 nr_of_elements *= dest->dim_size[i];
516 }
517
518 5370891 dest->flexible = 0;
519 5370891 dest->owns_data = 1;
520
521 5370891 return nr_of_elements;
522 }
523
524 /**
525 * @brief Copy dimension specification from one array to another.
526 *
527 * Creates a copy of the dimension metadata (number of dimensions and dimension sizes)
528 * from source array to destination array. Does not copy the data pointer.
529 *
530 * @param source Source array to copy specification from.
531 * @param dest Destination array to copy specification to.
532 *
533 * @pre source must be a valid base_array structure.
534 *
535 * @note The destination array should have an uninitialized dim_size pointer,
536 * as new memory will be allocated for it.
537 */
538 25959907 void clone_base_array_spec(const base_array_t *source, base_array_t *dest)
539 {
540 int i;
541
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 25959907 times.
25959907 assert(base_array_ok(source));
542
543 25959907 dest->ndims = source->ndims;
544 25959907 dest->dim_size = size_alloc(dest->ndims);
545
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25959907 times.
25959907 assert(dest->dim_size);
546
547
2/2
✓ Branch 0 taken 26117784 times.
✓ Branch 1 taken 25959907 times.
52077691 for(i = 0; i < dest->ndims; ++i) {
548 26117784 dest->dim_size[i] = source->dim_size[i];
549 }
550
551 25959907 dest->flexible = source->flexible;
552 25959907 dest->owns_data = 1;
553 25959907 }
554
555 /**
556 * @brief Calculate flat index using index specification.
557 *
558 * Converts multi-dimensional indices with an index specification into a flat 1D index.
559 * Handles both scalar indexing and array slicing specifications.
560 *
561 * @param ndims Number of dimensions.
562 * @param idx_vec Array of indices (0-based).
563 * @param arr Base array containing dimension information.
564 * @param spec Index specification defining how to map indices.
565 *
566 * @return Flat 1D index for array element access.
567 *
568 * @pre idx_vec must have exactly ndims elements.
569 * @pre spec and arr dimensions must be compatible.
570 * @pre All indices and specifications must be valid and within bounds.
571 *
572 * @note idx_vec uses 0-based indexing while spec uses 1-based indexing.
573 */
574 4419589 size_t calc_base_index_spec(int ndims, const _index_t *idx_vec,
575 const base_array_t *arr, const index_spec_t *spec)
576 {
577 int i;
578 int d2;
579 size_t index = 0;
580
581 /* index_spec_fit_base_array is O(size of spec); the callers check it once */
582
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4419589 times.
4419589 assert(base_array_ok(arr));
583
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4419589 times.
4419589 assert(index_spec_ok(spec));
584
2/4
✓ Branch 0 taken 4419589 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4419589 times.
4419589 assert((ndims == arr->ndims) && (ndims == spec->ndims));
585
586 index = 0;
587
2/2
✓ Branch 0 taken 4763485 times.
✓ Branch 1 taken 4419589 times.
9183074 for(i = 0; i < ndims; ++i) {
588 4763485 int d = idx_vec[i];
589
2/2
✓ Branch 0 taken 757177 times.
✓ Branch 1 taken 4006308 times.
4763485 if(spec->index_type[i] != 'W') {
590 757177 d2 = spec->index[i][d] - 1;
591 } else {
592 d2 = d;
593 }
594 4763485 index = (index * arr->dim_size[i]) + d2;
595 }
596
597 4419589 return index;
598 }
599
600 /**
601 * @brief Calculate flat index from 0-based multi-dimensional indices.
602 *
603 * Converts multi-dimensional indices (0-based) into a flat 1D index using row-major order.
604 *
605 * @param ndims Number of dimensions.
606 * @param idx_vec Array of 0-based indices.
607 * @param arr Base array containing dimension information.
608 *
609 * @return Flat 1D index for array element access.
610 *
611 * @pre idx_vec must have exactly ndims elements.
612 * @pre All indices must be within valid bounds.
613 */
614 ✗ size_t calc_base_index(int ndims, const _index_t *idx_vec, const base_array_t *arr)
615 {
616 int i;
617 size_t index = 0;
618 ✗ assert(ndims == arr->ndims);
619
620 ✗ for(i = 0; i < ndims; ++i) {
621 /* Assert that idx_vec[i] is not out of bounds */
622 ✗ index = (index * arr->dim_size[i]) + idx_vec[i];
623 }
624
625 ✗ return index;
626 }
627
628 /**
629 * @brief Calculate flat index from dimension sizes and 1-based subscripts.
630 *
631 * Calculates a flat 1D index from variable argument lists containing dimension
632 * sizes followed by 1-based subscripts. Includes bounds checking with assertions.
633 *
634 * @param ndims Number of dimensions.
635 * @param ... Variable arguments: first ndims values are dimension sizes,
636 * next ndims values are 1-based subscripts.
637 *
638 * @return Flat 1D index for array element access.
639 *
640 * @attention Asserts on any out-of-bounds subscript.
641 * @attention Subscripts are converted from 1-based to 0-based internally.
642 */
643 ✗ size_t calc_base_index_dims_subs(int ndims,...)
644 {
645
646 int i;
647 size_t index;
648
649 ✗ _index_t *dims = (_index_t*)omc_alloc_interface.malloc(sizeof(_index_t)*ndims);
650 ✗ _index_t *subs = (_index_t*)omc_alloc_interface.malloc(sizeof(_index_t)*ndims);
651
652 va_list ap;
653 ✗ va_start(ap,ndims);
654 ✗ for(i = 0; i < ndims; ++i) {
655 ✗ dims[i] = va_arg(ap, _index_t);
656 }
657 ✗ for(i = 0; i < ndims; ++i) {
658 ✗ subs[i] = va_arg(ap, _index_t) - 1;
659 }
660 ✗ va_end(ap);
661
662 index = 0;
663 ✗ for(i = 0; i < ndims; ++i) {
664 ✗ if (subs[i] < 0 || subs[i] >= dims[i]) {
665 ✗ omc_assert(NULL, omc_dummyFileInfo, "Dimension %d has bounds 1..%d, got array subscript %d", i+1, dims[i], subs[i]+1);
666 ✗ return 0;
667 }
668 ✗ index = (index * dims[i]) + subs[i];
669 }
670
671
672 return index;
673 }
674
675 /**
676 * @brief Calculate flat index from 1-based subscripts in a variable argument list.
677 *
678 * Converts 1-based subscripts from a va_list into a flat 0-based index.
679 * Includes bounds checking with assertions.
680 *
681 * @param source Base array containing dimension information.
682 * @param ndims Number of dimensions.
683 * @param ap Variable argument list containing ndims 1-based subscripts.
684 *
685 * @return Flat 1D (0-based) index for array element access.
686 *
687 * @pre ap must contain exactly ndims subscript values (1-based).
688 *
689 * @attention Asserts on any out-of-bounds subscript.
690 * @attention Input subscripts are 1-based; output index is 0-based.
691 */
692 11060 size_t calc_base_index_va(const base_array_t *source, int ndims, va_list ap)
693 {
694 int i;
695 size_t index;
696
697 index = 0;
698
2/2
✓ Branch 0 taken 11060 times.
✓ Branch 1 taken 11060 times.
22120 for(i = 0; i < ndims; ++i) {
699 11060 int sub_i = va_arg(ap, _index_t) - 1;
700
2/4
✓ Branch 0 taken 11060 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 11060 times.
11060 if (sub_i < 0 || sub_i >= source->dim_size[i]) {
701 ✗ omc_assert(NULL, omc_dummyFileInfo, "Dimension %d has bounds 1..%d, got array subscript %d", i+1, source->dim_size[i], sub_i+1);
702 ✗ return 0;
703 }
704 11060 index = (index * source->dim_size[i]) + sub_i;
705 }
706
707 return index;
708 }
709
710 ✗ size_t omc_array_bounds_error(int dim, _index_t dim_size, _index_t sub)
711 {
712 ✗ omc_assert(NULL, omc_dummyFileInfo, "Dimension %d has bounds 1..%d, got array subscript %d", dim, (int)dim_size, (int)sub);
713 ✗ return 0;
714 }
715
716 /**
717 * @brief Get the number of dimensions in an array.
718 *
719 * @param[in] a The base array structure.
720 *
721 * @return Number of dimensions (ndims).
722 *
723 * @pre a must be a valid base_array structure.
724 */
725 5543 int ndims_base_array(const base_array_t* a)
726 {
727
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5543 times.
5543 assert(base_array_ok(a));
728 5543 return a->ndims;
729 }
730
731 /**
732 * @brief Clone array dimensions in reversed order.
733 *
734 * Creates a copy of the source array's dimension specification but with
735 * dimensions in reverse order. For example, a 2x3x4 array becomes 4x3x2.
736 *
737 * @param source The source array with dimensions to reverse.
738 * @param dest The destination array where reversed dimensions are stored.
739 *
740 * @pre source must be a valid base_array structure.
741 * @pre dest must not be NULL.
742 * @attention Allocates new memory for dest->dim_size; caller is responsible
743 * for cleanup.
744 */
745 319827 void clone_reverse_base_array_spec(const base_array_t* source, base_array_t* dest)
746 {
747 int i;
748
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 319827 times.
319827 assert(base_array_ok(source));
749
750 319827 dest->ndims = source->ndims;
751 319827 dest->dim_size = size_alloc(dest->ndims);
752 319827 dest->owns_data = 1;
753
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 319827 times.
319827 assert(dest->dim_size);
754
755
2/2
✓ Branch 0 taken 400558 times.
✓ Branch 1 taken 319827 times.
720385 for(i = 0; i < dest->ndims; ++i) {
756 400558 dest->dim_size[i] = source->dim_size[dest->ndims - 1 - i];
757 }
758 319827 }
759
760 /**
761 * @brief Allocate and compute destination array dimensions from indexed access.
762 *
763 * Determines the resulting array dimensions when an index_spec is applied to
764 * a source array, handling 'A' (all) and 'W' (wildcard) index dimensions.
765 * Only counts non-zero dimension sizes.
766 *
767 * #### Example
768 * For a 3x4x5 source array with index spec that selects [2, :, :],
769 * the destination array will have dimensions 4x5 (skipping the indexed dimension).
770 *
771 * @param source The source array to index into.
772 * @param source_spec The index specification defining which dimensions/indices to access.
773 * @param dest The destination array where computed dimensions are stored.
774 *
775 * @pre source must be a valid base_array structure.
776 * @pre source_spec must be valid and fit the source array.
777 * @pre dest must not be NULL.
778 */
779 125684 void index_alloc_base_array_size(const real_array * source,
780 const index_spec_t* source_spec,
781 base_array_t* dest)
782 {
783 int i;
784 int j;
785
786
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 125684 times.
125684 omc_assert_macro(base_array_ok(source));
787
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 125684 times.
125684 omc_assert_macro(index_spec_ok(source_spec));
788
1/2
✓ Branch 1 taken 125684 times.
✗ Branch 2 not taken.
125684 omc_assert_macro(index_spec_fit_base_array(source_spec, source));
789
790
2/2
✓ Branch 0 taken 183367 times.
✓ Branch 1 taken 125684 times.
309051 for(i = 0, j = 0; i < source_spec->ndims; ++i) {
791
2/2
✓ Branch 0 taken 125684 times.
✓ Branch 1 taken 57683 times.
183367 if(source_spec->index_type[i] != 'S') {
792 125684 ++j;
793 }
794 }
795
796 125684 dest->ndims = imax(j,1);
797 125684 dest->dim_size = size_alloc(dest->ndims);
798 125684 dest->owns_data = 1;
799
2/2
✓ Branch 0 taken 125684 times.
✓ Branch 1 taken 125684 times.
251368 for(i = 0; i < dest->ndims; ++i) {
800 125684 dest->dim_size[i] = 0;
801 }
802
803
2/2
✓ Branch 0 taken 183367 times.
✓ Branch 1 taken 125684 times.
309051 for(i = 0, j = 0; i < source_spec->ndims; ++i) {
804
2/2
✓ Branch 0 taken 125684 times.
✓ Branch 1 taken 57683 times.
183367 if(source_spec->index_type[i] != 'S') {
805
2/2
✓ Branch 0 taken 59541 times.
✓ Branch 1 taken 66143 times.
125684 if(source_spec->index_type[i] == 'A') {
806 59541 dest->dim_size[j] = source_spec->dim_size[i];
807 } else { /* is 'W' */
808 66143 dest->dim_size[j] = source->dim_size[i];
809 }
810
811 125684 ++j;
812 }
813 }
814 125684 }
815
816 /**
817 * @brief Allocate and initialize index vectors for indexed assignment.
818 *
819 * Prepares index iteration structures for assigning elements to a destination
820 * array via an index specification. Allocates vectors for tracking current
821 * indices and dimension sizes in the indexed assignment operation.
822 *
823 * #### Example
824 * For assigning a 4x5 source array to elements dest[2, :, :] of a 3x4x5 array,
825 * this initializes the iteration indices and size boundaries.
826 *
827 * @param source The source array being assigned.
828 * @param dest The destination array being assigned to.
829 * @param dest_spec The index specification defining target locations in dest.
830 * @param _idx_vec1 Output: current iteration indices for destination.
831 * @param _idx_size Output: dimension size boundaries for destination iteration.
832 *
833 * @pre source must be a valid base_array with dimensions matching indexed dest.
834 * @pre dest must be a valid base_array structure.
835 * @pre dest_spec must be valid and fit the dest array.
836 * @pre _idx_vec1 and _idx_size must be valid pointers to _index_t*.
837 */
838 24016 void indexed_assign_base_array_size_alloc(const base_array_t *source, base_array_t *dest, const index_spec_t *dest_spec, _index_t** _idx_vec1, _index_t** _idx_size)
839 {
840 _index_t* idx_vec1;
841 _index_t* idx_size;
842 int i, j;
843
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 24016 times.
24016 omc_assert_macro(base_array_ok(source));
844
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 24016 times.
24016 omc_assert_macro(base_array_ok(dest));
845
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 24016 times.
24016 omc_assert_macro(index_spec_ok(dest_spec));
846
1/2
✓ Branch 1 taken 24016 times.
✗ Branch 2 not taken.
24016 omc_assert_macro(index_spec_fit_base_array(dest_spec, dest));
847
2/2
✓ Branch 0 taken 48032 times.
✓ Branch 1 taken 24016 times.
72048 for(i = 0,j = 0; i < dest_spec->ndims; ++i) {
848
2/2
✓ Branch 0 taken 24519 times.
✓ Branch 1 taken 23513 times.
48032 if(dest_spec->index_type[i] != 'S') {
849 24519 ++j;
850 }
851 }
852
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24016 times.
24016 omc_assert_macro(j == source->ndims);
853
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 24016 times.
24016 omc_assert_macro(index_spec_nr_of_elements(dest_spec, dest) == base_array_nr_of_elements(*source));
854
855 24016 idx_vec1 = size_alloc(dest->ndims);
856 24016 idx_size = size_alloc(dest_spec->ndims);
857
858
2/2
✓ Branch 0 taken 48032 times.
✓ Branch 1 taken 24016 times.
72048 for(i = 0; i < dest_spec->ndims; ++i) {
859 48032 idx_vec1[i] = 0;
860
861
2/2
✓ Branch 0 taken 39150 times.
✓ Branch 1 taken 8882 times.
48032 if(dest_spec->index_type[i] != 'W') {
862 39150 idx_size[i] = imax(dest_spec->dim_size[i],1);
863 } else { /* is 'W' */
864 8882 idx_size[i] = dest->dim_size[i];
865 }
866 }
867 24016 *_idx_vec1 = idx_vec1;
868 24016 *_idx_size = idx_size;
869 24016 }
870
871 37610590 void omc_array_retain(base_array_t *a)
872 {
873
1/2
✓ Branch 0 taken 37610590 times.
✗ Branch 1 not taken.
37610590 if (!a) {
874 return;
875 }
876
2/2
✓ Branch 0 taken 1887211 times.
✓ Branch 1 taken 35723379 times.
37610590 if (a->owns_data) {
877
1/2
✓ Branch 0 taken 1887211 times.
✗ Branch 1 not taken.
1887211 omc_rc_retain_inline(a->data);
878 }
879
1/2
✓ Branch 0 taken 37610590 times.
✗ Branch 1 not taken.
37610590 omc_rc_retain_inline(a->dim_size);
880 }
881
882 231361970 void omc_array_release(base_array_t *a)
883 {
884
1/2
✓ Branch 0 taken 231361970 times.
✗ Branch 1 not taken.
231361970 if (!a) {
885 return;
886 }
887
2/2
✓ Branch 0 taken 57240009 times.
✓ Branch 1 taken 174121961 times.
231361970 if (a->owns_data) {
888 57240009 omc_rc_release_inline(a->data);
889 }
890 231361970 omc_rc_release_inline(a->dim_size);
891 231361970 a->data = NULL;
892 231361970 a->dim_size = NULL;
893 231361970 a->ndims = 0;
894 231361970 a->owns_data = 0;
895 }
896
897 ✗ void omc_record_array_release(base_array_t *a, void (*release)(void*), size_t elem_size)
898 {
899 ✗ if (a && a->owns_data && a->data && omc_rc_is_last(a->data)) {
900 ✗ size_t i, n = base_array_nr_of_elements(*a);
901 ✗ char *elems = (char*) a->data;
902 ✗ for (i = 0; i < n; ++i) {
903 ✗ release(elems + i*elem_size);
904 }
905 }
906 ✗ omc_array_release(a);
907 ✗ }
908
909 1801 void omc_string_array_release(base_array_t *a)
910 {
911
5/8
✓ Branch 0 taken 1801 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 141 times.
✓ Branch 3 taken 1660 times.
✓ Branch 4 taken 141 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 141 times.
✗ Branch 8 not taken.
1801 if (a && a->owns_data && a->data && omc_rc_is_last(a->data)) {
912 141 size_t i, n = base_array_nr_of_elements(*a);
913 141 modelica_string *elems = (modelica_string*) a->data;
914
2/2
✓ Branch 0 taken 212 times.
✓ Branch 1 taken 141 times.
353 for (i = 0; i < n; ++i) {
915 212 omc_string_release(elems[i]);
916 }
917 }
918 1801 omc_array_release(a);
919 1801 }
920
921 /**
922 * @brief Write vector into null-terminated string.
923 *
924 * Scalars are written as `v`, vectors as `{v1, v2, ...}`. If `buffer` is too
925 * small the output is truncated with `"...}"`.
926 *
927 * @param source Vector to write to `buffer`.
928 * @param isScalar Treat vector of length one as scalar.
929 * @param format_element Writes element `i` of `data` into a buffer, like `snprintf`.
930 * @param buffer Buffer to write into.
931 * @param bufsize Length of `buffer`.
932 */
933 16 void base_vector_to_string(const base_array_t *source,
934 modelica_boolean isScalar,
935 base_array_format_element_t format_element,
936 char *buffer,
937 size_t bufsize)
938 {
939 _index_t i;
940 size_t pos = 0;
941 int ret;
942 size_t remaining;
943
944 /* Validate input parameters */
945
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (buffer == NULL || bufsize == 0) {
946 return;
947 }
948 16 buffer[0] = '\0';
949
950
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
16 omc_assert_macro(base_array_ok(source));
951
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 assert(source->ndims == 1);
952
953
3/4
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
16 if (isScalar && source->ndims == 1 && source->dim_size[0] == 1)
954 {
955 /* Write scalar into buffer */
956 11 format_element(buffer + pos, bufsize - pos, source->data, 0);
957 11 return;
958 }
959
960 /* Start brace */
961 ret = snprintf(buffer + pos, (bufsize > pos) ? bufsize - pos : 0, "{");
962 if (ret < 0) ret = 0;
963
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if ((size_t)ret >= bufsize - pos) {
964 return;
965 }
966 pos += (size_t)ret;
967
968
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 5 times.
18 for (i = 0; i < source->dim_size[0]; i++)
969 {
970
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 remaining = (bufsize > pos) ? bufsize - pos : 0;
971
972 /* If not enough room to write an element, try to append "...}" and stop */
973
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if (remaining <= 5) {
974 ✗ snprintf(buffer + pos, remaining, "...}");
975 ✗ return;
976 }
977
978 /* Format element, use comma+space for non-last elements */
979 13 ret = format_element(buffer + pos, remaining, source->data, i);
980
4/6
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 5 times.
13 if (ret >= 0 && (size_t)ret < remaining && i < source->dim_size[0] - 1) {
981 8 ret += snprintf(buffer + pos + ret, remaining - ret, ", ");
982 }
983
984 if (ret < 0) ret = 0;
985
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if (ret >= remaining - 5) {
986 /* Not enough space for more elements; try to write "...}" instead */
987 snprintf(buffer + pos, remaining, "...}");
988 ✗ return;
989 }
990 13 pos += (size_t)ret;
991 }
992
993 /* Append closing brace */
994
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 remaining = (bufsize > pos) ? bufsize - pos : 0;
995
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (remaining > 0) {
996 5 snprintf(buffer + pos, remaining, "}");
997 }
998 }
999