Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 80.2% 361 / 0 / 450
Functions: 87.5% 21 / 0 / 24
Branches: 65.9% 253 / 0 / 384

OMCompiler/SimulationRuntime/c/util/read_matlab4.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 #include <stdlib.h>
29 #include <stdint.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <assert.h>
33 #include <ctype.h>
34 #include "read_matlab4.h"
35 #include "omc_file.h"
36 #include "omc_strdup.h"
37
38 extern const char *omc_mat_Aclass;
39
40 typedef struct {
41 uint32_t type;
42 uint32_t mrows;
43 uint32_t ncols;
44 uint32_t imagf;
45 uint32_t namelen;
46 } MHeader_t;
47
48 static const char *binTrans_char = "binTrans";
49 static const char *binNormal_char = "binNormal";
50
51 /**
52 * @brief Compare two null-terminated strings while ignoring whitespace.
53 *
54 * This comparison advances over any whitespace characters in either
55 * string before comparing the next non-whitespace character. It is
56 * useful for comparing variable names where spacing may differ.
57 *
58 * @param a First null-terminated string.
59 * @param b Second null-terminated string.
60 * @return 0 if equal when ignoring whitespace, >0 if a>b, <0 if a<b.
61 */
62 1236157 static OMC_INLINE int strcmp_iws(const char *a, const char *b)
63 {
64
4/4
✓ Branch 0 taken 16981067 times.
✓ Branch 1 taken 22007 times.
✓ Branch 2 taken 16979745 times.
✓ Branch 3 taken 1322 times.
17003074 while (*a && *b) {
65
2/2
✓ Branch 0 taken 152 times.
✓ Branch 1 taken 16979593 times.
16979745 if (isspace(*a)) {
66 152 a++;
67 152 continue;
68 }
69
2/2
✓ Branch 0 taken 191 times.
✓ Branch 1 taken 16979402 times.
16979593 if (isspace(*b)) {
70 191 b++;
71 191 continue;
72 }
73
2/2
✓ Branch 0 taken 1212828 times.
✓ Branch 1 taken 15766574 times.
16979402 if (*a != *b) {
74
2/2
✓ Branch 0 taken 918200 times.
✓ Branch 1 taken 294628 times.
1212828 return *a > *b ? 1 : -1;
75 }
76 15766574 a++;
77 15766574 b++;
78 }
79
4/4
✓ Branch 0 taken 9875 times.
✓ Branch 1 taken 13454 times.
✓ Branch 2 taken 8553 times.
✓ Branch 3 taken 1322 times.
23329 return *a == *b ? 0 : (*a ? 1 : -1);
80 }
81
82 /**
83 * @brief Comparator wrapper used for sorting/searching Modelica variables.
84 *
85 * Compares two ModelicaMatVariable_t objects by their name fields using
86 * whitespace-ignoring comparison (`strcmp_iws`). This function is suitable
87 * for use with `qsort` and `bsearch`.
88 *
89 * @param a Pointer to the first ModelicaMatVariable_t.
90 * @param b Pointer to the second ModelicaMatVariable_t.
91 * @return See `strcmp_iws`: 0 if equal, >0 if a>b, <0 if a<b.
92 */
93 1236157 int omc_matlab4_comp_var(const void *a, const void *b)
94 {
95 1236157 char *as = ((ModelicaMatVariable_t*)a)->name;
96 1236157 char *bs = ((ModelicaMatVariable_t*)b)->name;
97
98 1236157 return strcmp_iws(as,bs);
99 }
100
101 /**
102 * @brief Determine the byte-length of an element from MATLAB v4 type code.
103 *
104 * The MATLAB v4 matrix type encodes element size and format. This helper
105 * extracts the relevant fields and returns the element length in bytes.
106 *
107 * @param type MATLAB v4 matrix type code.
108 * @return Element length in bytes on success, or -1 if unsupported/invalid.
109 */
110 8130 int mat_element_length(int type)
111 {
112 int m = (type/1000);
113 8130 int o = (type%1000)/100;
114 8130 int p = (type%100)/10;
115 8130 int t = (type%10);
116
1/2
✓ Branch 0 taken 8130 times.
✗ Branch 1 not taken.
8130 if(m) return -1; /* We require IEEE Little Endian for now */
117
1/2
✓ Branch 0 taken 8130 times.
✗ Branch 1 not taken.
8130 if(o) return -1; /* Reserved number; forced 0 */
118
1/2
✓ Branch 0 taken 8130 times.
✗ Branch 1 not taken.
8130 if(t == 1 && p == 0) return 8; /* Double text matrix? */
119
1/2
✓ Branch 0 taken 8130 times.
✗ Branch 1 not taken.
8130 if(t == 1 && p != 5) return -1; /* Text matrix? Force element length=1 */
120
1/2
✓ Branch 0 taken 8130 times.
✗ Branch 1 not taken.
8130 if(t == 2) return -1; /* Sparse matrix fails */
121 switch (p) {
122 case 0: return 8;
123 case 1: return 4;
124 case 2: return 4;
125 case 3: return 2;
126 case 4: return 2;
127 case 5: return 1;
128 default: return -1;
129 }
130 }
131
132 /**
133 * @brief Free resources held by a ModelicaMatReader.
134 *
135 * Frees any allocated memory and closes the file associated with `reader`.
136 * After this call the contents of `reader` should not be accessed.
137 *
138 * @param reader Pointer to an initialized ModelicaMatReader.
139 */
140 968 void omc_free_matlab4_reader(ModelicaMatReader *reader)
141 {
142 unsigned int i;
143
1/2
✓ Branch 0 taken 968 times.
✗ Branch 1 not taken.
968 if (reader->file) {
144 968 fclose(reader->file);
145 968 reader->file = 0;
146 }
147
1/2
✓ Branch 0 taken 968 times.
✗ Branch 1 not taken.
968 if (reader->fileName) {
148 968 free(reader->fileName);
149 968 reader->fileName=NULL;
150 }
151
2/2
✓ Branch 0 taken 153790 times.
✓ Branch 1 taken 968 times.
154758 for(i=0; i<reader->nall; i++) {
152 153790 free(reader->allInfo[i].name);
153 153790 free(reader->allInfo[i].descr);
154 }
155 968 reader->nall = 0;
156
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 968 times.
968 if (reader->file) {
157 ✗ free(reader->allInfo);
158 ✗ reader->allInfo=NULL;
159 }
160
1/2
✓ Branch 0 taken 968 times.
✗ Branch 1 not taken.
968 if (reader->params) {
161 968 free(reader->params);
162 968 reader->params=NULL;
163 }
164
2/2
✓ Branch 0 taken 58356 times.
✓ Branch 1 taken 968 times.
59324 for(i=0; i<reader->nvar*2; i++) {
165
2/2
✓ Branch 0 taken 15353 times.
✓ Branch 1 taken 43003 times.
58356 if (reader->vars[i]) free(reader->vars[i]);
166 }
167 968 reader->nvar = 0;
168
1/2
✓ Branch 0 taken 968 times.
✗ Branch 1 not taken.
968 if (reader->vars) {
169 968 free(reader->vars);
170 968 reader->vars=NULL;
171 }
172 968 }
173
174 /**
175 * @brief Trim leading and trailing whitespace in-place.
176 *
177 * Modifies the provided C string by removing leading and trailing
178 * whitespace characters. The operation moves characters forward when
179 * needed and ensures the result is null-terminated.
180 *
181 * @param str Null-terminated string to trim (modified in-place).
182 */
183 171294 void trimWhitespace(char *str)
184 {
185 char *start = str;
186 char *end;
187 171294 size_t len = strlen(str);
188
4/6
✓ Branch 0 taken 171294 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 171294 times.
✓ Branch 4 taken 1841 times.
✓ Branch 5 taken 169453 times.
171294 if (len > 0 && (isspace(*start) || isspace(str[len-1]))) {
189 /* find first non-whitespace */
190
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1841 times.
1841 while (isspace(*start)) {
191 ✗ ++start;
192 }
193 /* find last non-whitespace */
194 1841 end = start + strlen(start) - 1;
195
4/4
✓ Branch 0 taken 30164 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 28333 times.
✓ Branch 3 taken 1831 times.
30174 while (end > str && isspace(*end)) {
196 28333 --end;
197 }
198 1841 len = end - start + 1;
199 /* shift the string to remove leading whitespace */
200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1841 times.
1841 if (start != str) memmove(str, start, len);
201 /* terminate the string to remove trailing whitespace */
202 1841 *(str+len) = '\0';
203 }
204 171294 }
205
206 /**
207 * @brief Read character data from a MATLAB v4 matrix into a buffer.
208 *
209 * Depending on the encoded precision in `type`, elements may be stored as
210 * doubles or as bytes. If stored as doubles they will be converted to
211 * characters by casting. The function attempts to read exactly `n`
212 * elements into `str`.
213 *
214 * @param type MATLAB v4 matrix type code.
215 * @param n Number of elements to read.
216 * @param file Open FILE pointer positioned at the matrix payload.
217 * @param str Destination buffer with space for at least `n` bytes.
218 * @return 0 on success, non-zero on read/format error.
219 */
220 338373 static int read_chars(int type, size_t n, FILE *file, char *str)
221 {
222 338373 int p = (type%100)/10;
223
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 338373 times.
338373 if (p == 0) { /* Double */
224 ✗ double d=0.0;
225 int k;
226 ✗ for (k=0; k<n; k++) {
227 ✗ if (omc_fread(&d, sizeof(double), 1, file, 0) != 1) {
228 return 1;
229 }
230 ✗ str[k] = (char) d;
231 }
232 return 0;
233
1/2
✓ Branch 0 taken 338373 times.
✗ Branch 1 not taken.
338373 } else if (p == 5) { /* Byte */
234 338373 return omc_fread(str,n,1,file, 0) != 1;
235 }
236 return 1;
237 }
238
239 /**
240 * @brief Read 32-bit integer data from a MATLAB v4 matrix into an array.
241 *
242 * If the stored element type is double, values are read as doubles and
243 * converted to int32_t. If stored as int32 they are read directly.
244 *
245 * @param type MATLAB v4 matrix type code.
246 * @param n Number of integers to read.
247 * @param file Open FILE pointer positioned at the matrix payload.
248 * @param val Destination array of at least `n` int32_t elements.
249 * @return 0 on success, non-zero on read/format error.
250 */
251 1355 static int read_int32(int type, size_t n, FILE *file, int32_t *val)
252 {
253 1355 int p = (type%100)/10;
254
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1355 times.
1355 if (p == 0) { /* Double */
255 ✗ double d=0.0;
256 int k;
257 ✗ for (k=0; k<n; k++) {
258 ✗ if (omc_fread(&d, sizeof(double), 1, file, 0) != 1) {
259 return 1;
260 }
261 ✗ val[k] = (int32_t) d;
262 }
263 return 0;
264
1/2
✓ Branch 0 taken 1355 times.
✗ Branch 1 not taken.
1355 } else if (p == 2) { /* int32 */
265 1355 return omc_fread(val,n*sizeof(int32_t),1,file, 0) != 1;
266 }
267 return 1;
268 }
269
270 /**
271 * @brief Read floating point (double/float) data from a MATLAB v4 matrix.
272 *
273 * If the stored precision is double the function reads doubles directly.
274 * If the stored precision is float the values are read as floats and cast
275 * to double in the destination array.
276 *
277 * @param type MATLAB v4 matrix type code.
278 * @param n Number of floating point elements to read.
279 * @param file Open FILE pointer positioned at the matrix payload.
280 * @param val Destination array of at least `n` doubles.
281 * @return 0 on success, non-zero on read/format error.
282 */
283 1603 static int read_double(int type, size_t n, FILE *file, double *val)
284 {
285 1603 int p = (type%100)/10;
286
1/2
✓ Branch 0 taken 1603 times.
✗ Branch 1 not taken.
1603 if (n==0) {
287 return 0;
288 }
289
2/2
✓ Branch 0 taken 1589 times.
✓ Branch 1 taken 14 times.
1603 if (p == 0) { /* Double */
290 1589 return omc_fread(val,n*sizeof(double),1,file, 0) != 1;
291
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 } else if (p == 1) { /* float */
292 14 float f=0.0;
293 int k;
294
2/2
✓ Branch 0 taken 2760 times.
✓ Branch 1 taken 14 times.
2774 for (k=0; k<n; k++) {
295
1/2
✓ Branch 1 taken 2760 times.
✗ Branch 2 not taken.
2760 if (omc_fread(&f, sizeof(float), 1, file, 0) != 1) {
296 return 1;
297 }
298 2760 val[k] = (double) f;
299 }
300 return 0;
301 }
302 return 1;
303 }
304
305 /**
306 * @brief Open and parse a MATLAB v4 file into a ModelicaMatReader.
307 *
308 * This function opens `filename`, validates that it contains the expected
309 * set of MATLAB v4 matrices for OpenModelica (`Aclass`, `name`,
310 * `description`, `dataInfo`, `data_1`, `data_2`) and populates the
311 * provided `reader` structure with metadata and file offsets for later
312 * value reads. The reader is initialized (zeroed) by this call.
313 *
314 * The function uses a subset of MATLAB v4 files accepted by OpenModelica
315 * and will return a textual error describing the first problem found.
316 *
317 * #### Notes:
318 *
319 * The internal data is free'd by `omc_free_matlab4_reader`.
320 * The data persists until free'd, and is safe to use in your own data
321 * structures.
322 *
323 * @param filename Path to the MATLAB v4 file to open (must be readable).
324 * @param reader Pointer to an allocated ModelicaMatReader that will be
325 * initialized by this function.
326 * @return 0 on success, or a pointer to a static error string on failure.
327 * The returned string should not be freed by the caller.
328 */
329 1357 const char* omc_new_matlab4_reader(const char *filename, ModelicaMatReader *reader)
330 {
331 const int nMatrix=6;
332 static const char *matrixNames[6]={"Aclass","name","description","dataInfo","data_1","data_2"};
333 static const char *matrixNamesMismatch[6]={"Matrix name mismatch: Aclass","Matrix name mismatch: name","Matrix name mismatch: description","Matrix name mismatch: dataInfo","Matrix name mismatch: data_1","Matrix name mismatch: data_2"};
334 const int matrixTypes[6]={51,51,51,20,0,0};
335 int i;
336 char binTrans = 1;
337 memset(reader, 0, sizeof(ModelicaMatReader));
338 1357 reader->startTime = NaN;
339 1357 reader->stopTime = NaN;
340 1357 reader->file = omc_fopen(filename, "rb");
341
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1355 times.
1357 if(!reader->file) return strerror(errno);
342 1355 reader->fileName = omc_strdup(filename);
343 1355 reader->readAll = 0;
344 1355 reader->stopTime = NAN;
345
2/2
✓ Branch 0 taken 8130 times.
✓ Branch 1 taken 1355 times.
9485 for(i=0; i<nMatrix;i++) {
346 MHeader_t hdr;
347 8130 int nr = omc_fread(&hdr,sizeof(MHeader_t),1,reader->file, 0);
348 size_t matrix_length,element_length;
349 char *name;
350
1/2
✓ Branch 0 taken 8130 times.
✗ Branch 1 not taken.
8130 if(nr != 1) return "Corrupt header (1)";
351 /* fprintf(stderr, "Found matrix %d=%s type=%04d mrows=%d ncols=%d imagf=%d namelen=%d\n", i, name, hdr.type, hdr.mrows, hdr.ncols, hdr.imagf, hdr.namelen); */
352
1/2
✓ Branch 0 taken 8130 times.
✗ Branch 1 not taken.
8130 if(hdr.imagf > 1) return "Matrix uses imaginary numbers";
353
1/2
✓ Branch 1 taken 8130 times.
✗ Branch 2 not taken.
8130 if((element_length = mat_element_length(hdr.type)) == -1) return "Could not determine size of matrix elements";
354 8130 name = (char*) malloc(hdr.namelen);
355 8130 nr = omc_fread(name,hdr.namelen,1,reader->file, 0);
356
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8130 times.
8130 if(nr != 1) {
357 ✗ free(name);
358 ✗ return "Corrupt header (2)";
359 }
360
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8130 times.
8130 if(name[hdr.namelen-1]) {
361 ✗ free(name);
362 ✗ return "Corrupt header (3)";
363 }
364 /* fprintf(stderr, " Name of matrix: %s\n", name); */
365 8130 matrix_length = hdr.mrows*hdr.ncols*(1+hdr.imagf)*element_length;
366
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8130 times.
8130 if(0 != strcmp(name,matrixNames[i])) {
367 ✗ free(name);
368 ✗ return matrixNamesMismatch[i];
369 }
370 8130 free(name);
371 name=NULL;
372
6/6
✓ Branch 0 taken 1355 times.
✓ Branch 1 taken 1355 times.
✓ Branch 2 taken 1355 times.
✓ Branch 3 taken 1355 times.
✓ Branch 4 taken 1355 times.
✓ Branch 5 taken 1355 times.
8130 switch (i) {
373 1355 case 0: {
374 unsigned int k;
375 uint32_t j;
376 char tmp[45];
377
1/2
✓ Branch 0 taken 1355 times.
✗ Branch 1 not taken.
1355 if(hdr.mrows != 4) return "Aclass matrix does not have 4 rows";
378
1/2
✓ Branch 0 taken 1355 times.
✗ Branch 1 not taken.
1355 if(hdr.ncols != 11) return "Aclass matrix does not have 11 cols";
379
1/2
✓ Branch 1 taken 1355 times.
✗ Branch 2 not taken.
1355 if (read_chars(hdr.type, hdr.ncols*hdr.mrows, reader->file, tmp)) {
380 return "Could not read: Aclass matrix as text";
381 }
382
2/2
✓ Branch 0 taken 5420 times.
✓ Branch 1 taken 1355 times.
6775 for(k=0; k<hdr.mrows; k++) {
383 char row[12];
384
2/2
✓ Branch 0 taken 59620 times.
✓ Branch 1 taken 5420 times.
65040 for(j=0; j<hdr.ncols; j++) {
385 59620 row[j] = tmp[j*hdr.mrows+k];
386 }
387 5420 row[hdr.ncols] = '\0';
388 /* fprintf(stderr, "Row %s\n", row); */
389
2/2
✓ Branch 0 taken 1355 times.
✓ Branch 1 taken 4065 times.
5420 if(k==3)
390 {
391 /* binTrans */
392
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 1107 times.
1355 if(0 == strncmp(row,binTrans_char,8)) {
393 /* fprintf(stderr, "use binTrans format\n"); */
394 binTrans = 1;
395
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 248 times.
248 } else if(0 == strncmp(row,binNormal_char,9)) {
396 /* binNormal */
397 /* fprintf(stderr, "use binNormal format\n"); */
398 binTrans = 0;
399 } else {
400 /* fprintf(stderr, "row 3: %s\n", row); */
401 ✗ return "Aclass matrix does not match binTrans or binNormal format";
402 }
403 }
404 }
405 1355 break;
406 }
407 1355 case 1: { /* "names" */
408 unsigned int k;
409
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 1107 times.
1355 if(binTrans==0)
410 248 reader->nall = hdr.mrows;
411 else
412 1107 reader->nall = hdr.ncols;
413 1355 reader->allInfo = (ModelicaMatVariable_t*) malloc(sizeof(ModelicaMatVariable_t)*reader->nall);
414
2/2
✓ Branch 0 taken 1107 times.
✓ Branch 1 taken 248 times.
1355 if(binTrans==1) {
415
2/2
✓ Branch 0 taken 168261 times.
✓ Branch 1 taken 1107 times.
169368 for(k=0; k<hdr.ncols; k++) {
416 168261 reader->allInfo[k].name = (char*) malloc(hdr.mrows+1);
417
1/2
✓ Branch 1 taken 168261 times.
✗ Branch 2 not taken.
168261 if (read_chars(hdr.type, hdr.mrows, reader->file, reader->allInfo[k].name)) {
418 return "Could not read: names matrix as text";
419 }
420 168261 reader->allInfo[k].name[hdr.mrows] = '\0';
421 168261 reader->allInfo[k].isParam = -1;
422 168261 reader->allInfo[k].index = -1;
423 168261 trimWhitespace(reader->allInfo[k].name);
424 /* fprintf(stderr, " Adding variable '%s'\n", reader->allInfo[k].name); */
425 }
426 }
427
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 1107 times.
1355 if(binTrans==0) {
428 uint32_t j;
429 248 char* tmp = (char*) malloc(hdr.ncols*hdr.mrows+1);
430
1/2
✓ Branch 1 taken 248 times.
✗ Branch 2 not taken.
248 if (read_chars(hdr.type, hdr.ncols*hdr.mrows, reader->file, tmp)) {
431 return "Could not read: names matrix as text";
432 }
433
2/2
✓ Branch 0 taken 3033 times.
✓ Branch 1 taken 248 times.
3281 for(k=0; k<hdr.mrows; k++) {
434 3033 reader->allInfo[k].name = (char*) malloc(hdr.ncols+1);
435
2/2
✓ Branch 0 taken 83600 times.
✓ Branch 1 taken 3033 times.
86633 for(j=0; j<hdr.ncols; j++) {
436 83600 reader->allInfo[k].name[j] = tmp[j*hdr.mrows+k];
437 }
438 3033 reader->allInfo[k].name[hdr.ncols] = '\0';
439 3033 reader->allInfo[k].isParam = -1;
440 3033 reader->allInfo[k].index = -1;
441 3033 trimWhitespace(reader->allInfo[k].name);
442 /* fprintf(stderr, " Adding variable '%s'\n", reader->allInfo[k].name); */
443 }
444 248 free(tmp);
445 }
446 break;
447 }
448 1355 case 2: { /* description */
449 unsigned int k;
450
2/2
✓ Branch 0 taken 1107 times.
✓ Branch 1 taken 248 times.
1355 if(binTrans==1) {
451
2/2
✓ Branch 0 taken 168261 times.
✓ Branch 1 taken 1107 times.
169368 for(k=0; k<hdr.ncols; k++) {
452 168261 reader->allInfo[k].descr = (char*) malloc(hdr.mrows+1);
453
1/2
✓ Branch 1 taken 168261 times.
✗ Branch 2 not taken.
168261 if (read_chars(hdr.type, hdr.mrows, reader->file, reader->allInfo[k].descr)) {
454 return "Could not read: description matrix as text";
455 }
456 168261 reader->allInfo[k].descr[hdr.mrows] = '\0';
457 /* fprintf(stderr, " Adding description %s\n", reader->allInfo[k].descr); */
458 }
459
1/2
✓ Branch 0 taken 248 times.
✗ Branch 1 not taken.
248 } else if(binTrans==0) {
460 uint32_t j;
461 248 char* tmp = (char*) malloc(hdr.ncols*hdr.mrows+1);
462
1/2
✓ Branch 1 taken 248 times.
✗ Branch 2 not taken.
248 if (read_chars(hdr.type, hdr.ncols*hdr.mrows, reader->file, tmp)) {
463 return "Could not read: description matrix as text";
464 }
465
2/2
✓ Branch 0 taken 3033 times.
✓ Branch 1 taken 248 times.
3281 for(k=0; k<hdr.mrows; k++) {
466 3033 reader->allInfo[k].descr = (char*) malloc(hdr.ncols+1);
467
2/2
✓ Branch 0 taken 75441 times.
✓ Branch 1 taken 3033 times.
78474 for(j=0; j<hdr.ncols; j++) {
468 75441 reader->allInfo[k].descr[j] = tmp[j*hdr.mrows+k];
469 }
470 3033 reader->allInfo[k].descr[hdr.ncols] = '\0';
471 /* fprintf(stderr, " Adding description %s\n", reader->allInfo[k].descr); */
472 }
473 248 free(tmp);
474 }
475 break;
476 }
477 1355 case 3: { /* "dataInfo" */
478 unsigned int k;
479 1355 int32_t *tmp = (int32_t*) malloc(sizeof(int32_t)*hdr.ncols*hdr.mrows);
480
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1355 times.
1355 if (read_int32(hdr.type, hdr.ncols*hdr.mrows, reader->file, tmp)) {
481 ✗ free(tmp); tmp=NULL;
482 ✗ return "Corrupt header: dataInfo matrix";
483 }
484
2/2
✓ Branch 0 taken 1107 times.
✓ Branch 1 taken 248 times.
1355 if(binTrans==1) {
485
2/2
✓ Branch 0 taken 168261 times.
✓ Branch 1 taken 1107 times.
169368 for(k=0; k<hdr.ncols; k++) {
486 168261 reader->allInfo[k].isParam = tmp[k*hdr.mrows] == 1;
487 168261 reader->allInfo[k].index = tmp[k*hdr.mrows+1];
488 /* fprintf(stderr, " Variable %s isParam=%d index=%d\n", reader->allInfo[k].name, reader->allInfo[k].isParam, reader->allInfo[k].index); */
489 }
490 }
491
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 1107 times.
1355 if(binTrans==0) {
492
2/2
✓ Branch 0 taken 3033 times.
✓ Branch 1 taken 248 times.
3281 for(k=0; k<hdr.mrows; k++) {
493 3033 reader->allInfo[k].isParam = tmp[k] == 1;
494 3033 reader->allInfo[k].index = tmp[k + hdr.mrows];
495 /* fprintf(stderr, " Variable %s isParam=%d index=%d\n", reader->allInfo[k].name, reader->allInfo[k].isParam, reader->allInfo[k].index); */
496 }
497 }
498 1355 free(tmp); tmp=NULL;
499 /* Sort the variables so we can do faster lookup */
500 1355 qsort(reader->allInfo,reader->nall,sizeof(ModelicaMatVariable_t),omc_matlab4_comp_var);
501 1355 break;
502 }
503 1355 case 4: { /* "data_1" */
504 unsigned int k;
505
2/2
✓ Branch 0 taken 1107 times.
✓ Branch 1 taken 248 times.
1355 if(binTrans==1) {
506
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1107 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1107 if(hdr.mrows != 0 || hdr.ncols != 0) {
507
1/2
✓ Branch 0 taken 1107 times.
✗ Branch 1 not taken.
1107 if(hdr.ncols != 2 && hdr.ncols != 1) return "data_1 matrix does not have 1 or 2 cols (or 0 rows/columns)";
508 }
509 1107 reader->nparam = hdr.mrows;
510
1/2
✓ Branch 0 taken 1107 times.
✗ Branch 1 not taken.
1107 reader->params = reader->nparam > 0 ? (double*) malloc(hdr.mrows*hdr.ncols*sizeof(double)) : NULL;
511
1/2
✓ Branch 1 taken 1107 times.
✗ Branch 2 not taken.
1107 if (read_double(hdr.type, hdr.mrows*hdr.ncols, reader->file, reader->params)) {
512 return "Corrupt header: data_1 matrix";
513 }
514 }
515
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 1107 times.
1355 if(binTrans==0) {
516 unsigned int j;
517
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 248 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
248 if(hdr.mrows != 0 || hdr.ncols != 0) {
518
1/2
✓ Branch 0 taken 248 times.
✗ Branch 1 not taken.
248 if(hdr.mrows != 2 && hdr.mrows != 1) return "data_1 matrix does not have 1 or 2 rows (or 0 rows/columns)";
519 }
520 248 reader->nparam = hdr.ncols;
521 double *tmp=NULL;
522
1/2
✓ Branch 0 taken 248 times.
✗ Branch 1 not taken.
248 tmp = reader->nparam > 0 ? (double*) malloc(hdr.mrows*hdr.ncols*sizeof(double)) : NULL;
523
1/2
✓ Branch 0 taken 248 times.
✗ Branch 1 not taken.
248 reader->params = reader->nparam > 0 ? (double*) malloc(hdr.mrows*hdr.ncols*sizeof(double)) : NULL;
524
1/2
✓ Branch 1 taken 248 times.
✗ Branch 2 not taken.
248 if (read_double(hdr.type, hdr.mrows*hdr.ncols, reader->file, tmp)) {
525 return "Corrupt header: data_1 matrix";
526 }
527
2/2
✓ Branch 0 taken 496 times.
✓ Branch 1 taken 248 times.
744 for(k=0; k<hdr.mrows; k++) {
528
2/2
✓ Branch 0 taken 1808 times.
✓ Branch 1 taken 496 times.
2304 for(j=0; j<hdr.ncols; j++) {
529 1808 reader->params[k*hdr.ncols+j] = tmp[k +j*hdr.mrows];
530 }
531 }
532
1/2
✓ Branch 0 taken 248 times.
✗ Branch 1 not taken.
248 if (tmp) {
533 248 free(tmp);
534 }
535 }
536 break;
537 }
538 1355 case 5: { /* "data_2" */
539 1355 int p = (hdr.type / 10)%10;
540
2/2
✓ Branch 0 taken 1341 times.
✓ Branch 1 taken 14 times.
1355 if (p==0) {
541 1341 reader->doublePrecision=1;
542
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 } else if (p==1) {
543 14 reader->doublePrecision=0;
544 } else {
545 return "data_2 matrix not in double/float representation";
546 }
547
2/2
✓ Branch 0 taken 1107 times.
✓ Branch 1 taken 248 times.
1355 if(binTrans==1) {
548 1107 reader->nrows = hdr.ncols;
549 /* Allow empty matrix; it's not a complete file, but ok... */
550 /* if(reader->nrows < 2) return "Too few rows in data_2 matrix"; */
551 1107 reader->nvar = hdr.mrows;
552 1107 reader->var_offset = ftell(reader->file);
553 1107 reader->vars = (double**) calloc(reader->nvar*2,sizeof(double*));
554
1/2
✓ Branch 1 taken 1107 times.
✗ Branch 2 not taken.
1107 if(-1==omc_fseek(reader->file,matrix_length,SEEK_CUR)) return "Corrupt header: data_2 matrix";
555 }
556
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 1107 times.
1355 if(binTrans==0) {
557 unsigned int k,j;
558 248 reader->nrows = hdr.mrows;
559 /* Allow empty matrix; it's not a complete file, but ok... */
560 /* if(reader->nrows < 2) return "Too few rows in data_2 matrix"; */
561 248 reader->nvar = hdr.ncols;
562 248 reader->var_offset = ftell(reader->file);
563 248 reader->vars = (double**) calloc(reader->nvar*2,sizeof(double*));
564
565
566 double *tmp=NULL;
567 248 tmp = (double*) malloc(hdr.mrows*hdr.ncols*sizeof(double));
568
1/2
✓ Branch 1 taken 248 times.
✗ Branch 2 not taken.
248 if (read_double(hdr.type, hdr.mrows*hdr.ncols, reader->file, tmp)) {
569 return "Corrupt header: data_2 matrix";
570 }
571
2/2
✓ Branch 0 taken 2258 times.
✓ Branch 1 taken 248 times.
2506 for(k=0; k<hdr.ncols; k++) {
572 2258 reader->vars[k] = (double*) malloc(hdr.mrows*sizeof(double));
573
2/2
✓ Branch 0 taken 6762679 times.
✓ Branch 1 taken 2258 times.
6764937 for(j=0; j<hdr.mrows; j++) {
574 6762679 reader->vars[k][j] = tmp[j+k*hdr.mrows];
575 }
576 }
577
2/2
✓ Branch 0 taken 2258 times.
✓ Branch 1 taken 248 times.
2506 for(k=reader->nvar; k<reader->nvar*2; k++) {
578 2258 reader->vars[k] = (double*) malloc(hdr.mrows*sizeof(double));
579
2/2
✓ Branch 0 taken 6762679 times.
✓ Branch 1 taken 2258 times.
6764937 for(j=0; j<hdr.mrows; j++) {
580 6762679 reader->vars[k][j] = -reader->vars[k-reader->nvar][j];
581 }
582 }
583 248 free(tmp);
584
585
1/2
✓ Branch 1 taken 248 times.
✗ Branch 2 not taken.
248 if(-1==omc_fseek(reader->file,matrix_length,SEEK_CUR)) return "Corrupt header: data_2 matrix";
586 }
587 break;
588 }
589 default:
590 return "Implementation error: Unknown case";
591 }
592 }
593 return 0;
594 }
595
596 /**
597 * @brief Convert an OpenModelica variable name into Dymola "der" style.
598 *
599 * If `varName` contains a dot and is of the form "der(X.Y)" in Dymola
600 * style, this function returns a malloc'd string with the converted
601 * representation. If no conversion is needed the function returns NULL.
602 *
603 * Caller must free the returned string when non-NULL.
604 *
605 * @param varName Input variable name.
606 * @return Newly allocated converted name, or NULL if no conversion was
607 * necessary or on allocation failure.
608 */
609 19 static char* dymolaStyleVariableName(const char *varName)
610 {
611 19 int len,is_der=0==strncmp("der(", varName, 4);
612 const char *has_dot=NULL;
613 const char *c = varName;
614 char *res = NULL;
615
2/2
✓ Branch 0 taken 202 times.
✓ Branch 1 taken 19 times.
221 while (*c) {
616
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 179 times.
202 if (*c=='.') {
617 has_dot = c;
618 }
619 202 c++;
620 }
621
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 12 times.
19 if (!(is_der&&has_dot)) {
622 return NULL; /* The Dymola name is the same as OMC */
623 }
624 7 len = strlen(varName);
625 7 res = (char*) malloc(len+1);
626 7 res[len]='\0';
627
628 7 memcpy(res, varName+4, has_dot-varName-3);
629 7 sprintf(res+(has_dot-varName)-3, "der(%s", has_dot+1);
630
631 /* fprintf(stderr, "Dymola style %s -> %s\n", varName, res); */
632 7 return res;
633 }
634
635 /**
636 * @brief Convert a Dymola-style variable name into OpenModelica style.
637 *
638 * Transforms occurrences where the derivative is expressed at the end of
639 * the name (e.g. "x.der(y)") into OpenModelica's "der(x.y)" style.
640 * Returns a malloc'd string on success or NULL if no transformation is
641 * necessary. Caller must free the returned string.
642 *
643 * @param varName Input variable name.
644 * @return Newly allocated converted name or NULL.
645 */
646 12 char* openmodelicaStyleVariableName(const char *varName)
647 {
648 int len;
649 12 const char *der=strstr(varName, "der(");
650 const char *c = varName;
651 char *res = NULL;
652
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (!der || der == varName) {
653 return NULL;
654 }
655 ✗ len = strlen(varName);
656 ✗ res = (char*) malloc(len+1);
657 ✗ res[len]='\0';
658
659 memcpy(res, "der(", 4);
660 ✗ memcpy(res+4, varName, der-varName);
661 ✗ memcpy(res+(der-varName)+4, der+4, len-(der-varName+4));
662
663 ✗ return res;
664 }
665
666 /**
667 * @brief Find a variable by name in the reader's variable metadata.
668 *
669 * Performs a binary search (via `bsearch`) over the sorted variable list
670 * and tries a few name conversion fallbacks if the direct lookup fails:
671 * - checks for "time"/"Time" aliases
672 * - attempts Dymola/OpenModelica style conversions
673 *
674 * @param reader Pointer to an initialized ModelicaMatReader.
675 * @param varName Null-terminated variable name to search for.
676 * @return Pointer to the matching ModelicaMatVariable_t in `reader->allInfo`,
677 * or NULL if not found.
678 */
679 13472 ModelicaMatVariable_t *omc_matlab4_find_var(ModelicaMatReader *reader, const char *varName)
680 {
681 ModelicaMatVariable_t key;
682 ModelicaMatVariable_t *res;
683 char *dymolaName = NULL;
684
685 13472 key.name = (char*) varName;
686
687 13472 res = (ModelicaMatVariable_t*)bsearch(&key,reader->allInfo,reader->nall,sizeof(ModelicaMatVariable_t),omc_matlab4_comp_var);
688
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 13453 times.
13472 if (res == NULL) { /* Try to convert the name to a Dymola name */
689 /* fprintf(stderr, "Did not find: %s\n", varName); */
690
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 if (0==strcmp(varName, "time")) {
691 ✗ key.name = "Time";
692 ✗ return (ModelicaMatVariable_t*)bsearch(&key,reader->allInfo,reader->nall,sizeof(ModelicaMatVariable_t),omc_matlab4_comp_var);
693
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 } else if (0==strcmp(varName, "Time")) {
694 ✗ key.name = "time";
695 ✗ return (ModelicaMatVariable_t*)bsearch(&key,reader->allInfo,reader->nall,sizeof(ModelicaMatVariable_t),omc_matlab4_comp_var);
696 }
697 19 dymolaName = dymolaStyleVariableName(varName);
698
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 7 times.
19 if (dymolaName == NULL) {
699 12 dymolaName = openmodelicaStyleVariableName(varName);
700 }
701
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 12 times.
19 if (dymolaName == NULL) {
702 return NULL;
703 }
704 7 key.name = dymolaName;
705 /* fprintf(stderr, "Look for dymola style name: %s\n", dymolaName); */
706 7 res = (ModelicaMatVariable_t*)bsearch(&key,reader->allInfo,reader->nall,sizeof(ModelicaMatVariable_t),omc_matlab4_comp_var);
707 7 free(dymolaName);
708 }
709 return res;
710 }
711
712 /**
713 * @brief Read (or lazily load) the full time series for a variable.
714 *
715 * If the requested variable's data has not been loaded yet this function
716 * reads the column from the file, converts if necessary and stores it in
717 * the reader cache (`reader->vars`). The `varIndex` follows the file
718 * convention where negative indices refer to the negative alias of a
719 * variable (i.e. sign-inverted values).
720 *
721 * @param reader Pointer to an initialized ModelicaMatReader.
722 * @param varIndex 1-based variable index; negative values select the
723 * negative alias of the variable.
724 * @return Pointer to an array of `reader->nrows` doubles containing the
725 * time series for the variable, or NULL on failure or if there are
726 * no rows.
727 */
728 11236 double* omc_matlab4_read_vals(ModelicaMatReader *reader, int varIndex)
729 {
730 11236 size_t absVarIndex = abs(varIndex);
731
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 11223 times.
11236 size_t ix = (varIndex < 0 ? absVarIndex + reader->nvar : absVarIndex) -1;
732
2/4
✓ Branch 0 taken 11236 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 11236 times.
11236 assert(absVarIndex > 0 && absVarIndex <= reader->nvar);
733
1/2
✓ Branch 0 taken 11236 times.
✗ Branch 1 not taken.
11236 if (0 == reader->nrows) {
734 return NULL;
735
2/2
✓ Branch 0 taken 3647 times.
✓ Branch 1 taken 7589 times.
11236 } else if(!reader->vars[ix]) {
736 unsigned int i;
737 3647 double *tmp = (double*) malloc(reader->nrows*sizeof(double));
738
2/2
✓ Branch 0 taken 3581 times.
✓ Branch 1 taken 66 times.
3647 if(reader->doublePrecision==1)
739 {
740
2/2
✓ Branch 0 taken 11083754 times.
✓ Branch 1 taken 3581 times.
11087335 for(i=0; i<reader->nrows; i++) {
741 11083754 omc_fseek(reader->file,reader->var_offset + sizeof(double)*(i*reader->nvar + absVarIndex-1), SEEK_SET);
742
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11083754 times.
11083754 if(1 != omc_fread(&tmp[i], sizeof(double), 1, reader->file, 0)) {
743 /* fprintf(stderr, "Corrupt file at %d of %d? nvar %d\n", i, reader->nrows, reader->nvar); */
744 ✗ free(tmp);
745 tmp=NULL;
746 ✗ return NULL;
747 }
748
2/2
✓ Branch 0 taken 265302 times.
✓ Branch 1 taken 10818452 times.
11083754 if(varIndex < 0) tmp[i] = -tmp[i];
749 /* fprintf(stderr, "tmp[%d]=%g\n", i, tmp[i]); */
750 }
751 }
752 else
753 {
754 66 float *buffer = (float*) malloc(reader->nrows*sizeof(float));
755
2/2
✓ Branch 0 taken 36323 times.
✓ Branch 1 taken 66 times.
36389 for(i=0; i<reader->nrows; i++) {
756 36323 omc_fseek(reader->file,reader->var_offset + sizeof(float)*(i*reader->nvar + absVarIndex-1), SEEK_SET);
757
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 36323 times.
36323 if(1 != omc_fread(&buffer[i], sizeof(float), 1, reader->file, 0)) {
758 /* fprintf(stderr, "Corrupt file at %d of %d? nvar %d\n", i, reader->nrows, reader->nvar); */
759 ✗ free(buffer);
760 ✗ free(tmp);
761 tmp=NULL;
762 ✗ return NULL;
763 }
764 }
765
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66 times.
66 if(varIndex < 0)
766 {
767 ✗ for(i=0; i<reader->nrows; i++) {
768 ✗ tmp[i] = -buffer[i];
769 }
770 }
771 else
772 {
773
2/2
✓ Branch 0 taken 36323 times.
✓ Branch 1 taken 66 times.
36389 for(i=0; i<reader->nrows; i++) {
774 36323 tmp[i] = buffer[i];
775 }
776 }
777 66 free(buffer);
778 /* fprintf(stderr, "tmp[%d]=%g\n", i, tmp[i]); */
779 }
780 3647 reader->vars[ix] = tmp;
781 }
782 11236 return reader->vars[ix];
783 }
784
785 /**
786 * @brief In-place transpose of a w-by-h matrix stored in row-major order.
787 *
788 * The algorithm performs the transpose without allocating a separate
789 * buffer. The matrix is assumed to be stored contiguous in `m` with
790 * dimensions w (width) and h (height).
791 *
792 * @param m Pointer to the matrix data (length w*h).
793 * @param w Width (number of columns) of the original matrix.
794 * @param h Height (number of rows) of the original matrix.
795 */
796 138 void matrix_transpose(double *m, int w, int h)
797 {
798 int start;
799 double tmp;
800
801
2/2
✓ Branch 0 taken 2769681 times.
✓ Branch 1 taken 138 times.
2769819 for (start = 0; start <= w * h - 1; start++) {
802 int next = start;
803 int i = 0;
804 27038357 do { i++;
805 27038357 next = (next % h) * w + next / h;
806
2/2
✓ Branch 0 taken 24268676 times.
✓ Branch 1 taken 2769681 times.
27038357 } while (next > start);
807
2/2
✓ Branch 0 taken 2763415 times.
✓ Branch 1 taken 6266 times.
2769681 if (next < start || i == 1) continue;
808
809 6266 tmp = m[next = start];
810 do {
811 2767688 i = (next % h) * w + next / h;
812
2/2
✓ Branch 0 taken 2761422 times.
✓ Branch 1 taken 6266 times.
2767688 m[next] = (i == start) ? tmp : m[i];
813 next = i;
814
2/2
✓ Branch 0 taken 2761422 times.
✓ Branch 1 taken 6266 times.
2767688 } while (next > start);
815 }
816 138 }
817
818 /**
819 * @brief In-place transpose for a uint32_t matrix (same semantics as
820 * matrix_transpose).
821 *
822 * @param m Pointer to the uint32_t matrix data (length w*h).
823 * @param w Width (number of columns) of the original matrix.
824 * @param h Height (number of rows) of the original matrix.
825 */
826 ✗ void matrix_transpose_uint32(uint32_t *m, int w, int h)
827 {
828 int start;
829 uint32_t tmp;
830
831 ✗ for (start = 0; start <= w * h - 1; start++) {
832 int next = start;
833 int i = 0;
834 ✗ do { i++;
835 ✗ next = (next % h) * w + next / h;
836 ✗ } while (next > start);
837 ✗ if (next < start || i == 1) continue;
838
839 ✗ tmp = m[next = start];
840 do {
841 ✗ i = (next % h) * w + next / h;
842 ✗ m[next] = (i == start) ? tmp : m[i];
843 next = i;
844 ✗ } while (next > start);
845 }
846 ✗ }
847
848 /**
849 * @brief Read all variable values from the file into memory.
850 *
851 * Allocates buffers for all variables and reads the entire data block
852 * from disk. After a successful call all `reader->vars[i]` pointers will
853 * be non-NULL and `reader->readAll` will be set.
854 *
855 * @param reader Pointer to an initialized ModelicaMatReader.
856 * @return 0 on success, 1 on failure (allocation or read error).
857 */
858 5476 int omc_matlab4_read_all_vals(ModelicaMatReader *reader)
859 {
860 5476 int done = reader->readAll;
861 int i,j;
862 double *tmp;
863 5476 int nrows = reader->nrows, nvar = reader->nvar;
864
1/2
✓ Branch 0 taken 5476 times.
✗ Branch 1 not taken.
5476 if (nvar == 0 || nrows == 0) {
865 return 1;
866 }
867
2/2
✓ Branch 0 taken 2843190 times.
✓ Branch 1 taken 5476 times.
2848666 for (i=0; i<2*nvar; i++) {
868
2/2
✓ Branch 0 taken 7683 times.
✓ Branch 1 taken 2835507 times.
2843190 if (reader->vars[i] == 0) done = 0;
869 }
870
2/2
✓ Branch 0 taken 5344 times.
✓ Branch 1 taken 132 times.
5476 if (done) {
871 5344 reader->readAll = 1;
872 5344 return 0;
873 }
874 132 tmp = (double*) malloc(2*nvar*nrows*sizeof(double));
875
1/2
✓ Branch 0 taken 132 times.
✗ Branch 1 not taken.
132 if (!tmp) {
876 return 1;
877 }
878 132 omc_fseek(reader->file, reader->var_offset, SEEK_SET);
879
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 132 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 132 times.
132 if (nvar*reader->nrows != omc_fread(tmp, reader->doublePrecision==1 ? sizeof(double) : sizeof(float), nvar*nrows, reader->file, 0)) {
880 ✗ free(tmp);
881 ✗ return 1;
882 }
883
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 132 times.
132 if(reader->doublePrecision != 1) {
884 ✗ for (i=nvar*nrows-1; i>=0; i--) {
885 ✗ tmp[i] = ((float*)tmp)[i];
886 }
887 }
888 132 matrix_transpose(tmp,nvar,nrows);
889 /* Negative aliases */
890
2/2
✓ Branch 0 taken 1903258 times.
✓ Branch 1 taken 132 times.
1903390 for (i=0; i<nrows*nvar; i++) {
891 1903258 tmp[nrows*nvar + i] = -tmp[i];
892 }
893 /* Setup all the pointers */
894
2/2
✓ Branch 0 taken 7702 times.
✓ Branch 1 taken 132 times.
7834 for (i=0; i<2*nvar; i++) {
895
2/2
✓ Branch 0 taken 7683 times.
✓ Branch 1 taken 19 times.
7702 if (!reader->vars[i]) {
896 7683 reader->vars[i] = (double*) malloc(nrows*sizeof(double));
897 7683 memcpy(reader->vars[i], tmp + i*nrows, nrows*sizeof(double));
898 }
899 }
900 132 free(tmp);
901 132 reader->readAll = 1;
902 132 return 0;
903 }
904
905 /**
906 * @brief Read a single value for a variable at a specific time index.
907 *
908 * If the variable has been cached in memory the value is read from the
909 * cache. Otherwise the function seeks to the appropriate file position
910 * and reads either a double or float depending on `reader->doublePrecision`.
911 *
912 * @param res Output pointer where the value will be stored.
913 * @param reader Pointer to an initialized ModelicaMatReader.
914 * @param varIndex 1-based variable index; negative values select the
915 * negative alias of the variable.
916 * @param timeIndex Zero-based index into the time series (0..nrows-1).
917 * @return 0 on success, non-zero on failure (read error or invalid index).
918 */
919 2930 double omc_matlab4_read_single_val(double *res, ModelicaMatReader *reader, int varIndex, int timeIndex)
920 {
921 2930 size_t absVarIndex = abs(varIndex);
922
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2920 times.
2930 size_t ix = (varIndex < 0 ? absVarIndex + reader->nvar : absVarIndex) -1;
923
2/4
✓ Branch 0 taken 2930 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2930 times.
2930 assert(absVarIndex > 0 && absVarIndex <= reader->nvar);
924
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 2903 times.
2930 if(reader->vars[ix]) {
925 27 *res = reader->vars[ix][timeIndex];
926 27 return 0;
927 }
928
1/2
✓ Branch 0 taken 2903 times.
✗ Branch 1 not taken.
2903 if(reader->doublePrecision==1) {
929 2903 omc_fseek(reader->file,reader->var_offset + sizeof(double)*(timeIndex*reader->nvar + absVarIndex-1), SEEK_SET);
930
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2903 times.
2903 if(1 != omc_fread(res, sizeof(double), 1, reader->file, 0)) {
931 ✗ *res = 0;
932 ✗ return 1;
933 }
934 } else {
935 float tmpres;
936 ✗ omc_fseek(reader->file,reader->var_offset + sizeof(float)*(timeIndex*reader->nvar + absVarIndex-1), SEEK_SET);
937 ✗ if(1 != omc_fread(&tmpres, sizeof(float), 1, reader->file, 0)) {
938 ✗ *res = 0;
939 ✗ return 1;
940 }
941 ✗ *res = tmpres;
942 }
943
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2893 times.
2903 if(varIndex < 0) {
944 10 *res = -(*res);
945 }
946 return 0;
947 }
948
949 /**
950 * @brief Find the two closest points surrounding `key` in a sorted vector.
951 *
952 * Performs a binary search over `vec` (length `nelem`) which is expected
953 * to be sorted in ascending order. If an exact match is found the function
954 * returns that index in `index1`, sets `index2` to -1 and `weight1`=1.
955 * Otherwise it returns the two neighboring indices and linear
956 * interpolation weights so that value(key) = weight1 * vec[index1] + weight2 * vec[index2].
957 *
958 * @param key Value to locate.
959 * @param vec Sorted array of doubles (length `nelem`).
960 * @param nelem Number of elements in `vec`.
961 * @param index1 Output index for the right-side neighbour (or exact match).
962 * @param weight1 Output interpolation weight for index1.
963 * @param index2 Output index for the left-side neighbour, or -1 on exact match.
964 * @param weight2 Output interpolation weight for index2.
965 */
966 2446 void find_closest_points(double key, double *vec, int nelem, int *index1, double *weight1, int *index2, double *weight2)
967 {
968 int min = 0;
969 2446 int max = nelem-1;
970 /* fprintf(stderr, "search closest: %g in %d elem\n", key, nelem); */
971 do {
972 15396 int mid = min + (max-min)/2;
973
2/2
✓ Branch 0 taken 1829 times.
✓ Branch 1 taken 13567 times.
15396 if(key == vec[mid]) {
974 /* If we have events (multiple identical time stamps), use the right limit */
975
4/4
✓ Branch 0 taken 1874 times.
✓ Branch 1 taken 802 times.
✓ Branch 2 taken 847 times.
✓ Branch 3 taken 1027 times.
2676 while(mid < max && vec[mid] == vec[mid+1]) mid++;
976 1829 *index1 = mid;
977 1829 *weight1 = 1.0;
978 1829 *index2 = -1;
979 1829 *weight2 = 0.0;
980 1829 return;
981
2/2
✓ Branch 0 taken 6547 times.
✓ Branch 1 taken 7020 times.
13567 } else if(key > vec[mid]) {
982 6547 min = mid + 1;
983 } else {
984 7020 max = mid - 1;
985 }
986
2/2
✓ Branch 0 taken 12950 times.
✓ Branch 1 taken 617 times.
13567 } while(max > min);
987
2/2
✓ Branch 0 taken 595 times.
✓ Branch 1 taken 22 times.
617 if(max == min) {
988
2/2
✓ Branch 0 taken 124 times.
✓ Branch 1 taken 471 times.
595 if(key > vec[max])
989 124 max++;
990 else
991 471 min--;
992 }
993 617 *index1 = max;
994 617 *index2 = min;
995 /* fprintf(stderr, "closest: %g = (%d,%g),(%d,%g)\n", key, min, vec[min], max, vec[max]); */
996 617 *weight1 = (key - vec[min]) / (vec[max]-vec[min]);
997 617 *weight2 = 1.0 - *weight1;
998 }
999
1000 /**
1001 * @brief Populate `reader->startTime` and `reader->stopTime` from the time column.
1002 *
1003 * Attempts to read the time series (variable index 1) and sets start/stop
1004 * time to the first and last entry respectively. If the time vector cannot
1005 * be read the function leaves the values unchanged.
1006 *
1007 * @param reader Pointer to an initialized ModelicaMatReader.
1008 */
1009 426 static void read_start_stop_time(ModelicaMatReader *reader)
1010 {
1011 426 double *d = omc_matlab4_read_vals(reader, 1);
1012
1/2
✓ Branch 0 taken 426 times.
✗ Branch 1 not taken.
426 if (d==NULL) {
1013 return;
1014 }
1015 426 reader->startTime = d[0];
1016 426 reader->stopTime = d[reader->nrows-1];
1017 }
1018
1019 /**
1020 * @brief Get the start time of the dataset managed by `reader`.
1021 *
1022 * If the start time has not yet been determined this function will read
1023 * the time column lazily to extract the first timestamp.
1024 *
1025 * @param reader Pointer to an initialized ModelicaMatReader.
1026 * @return Start time as a double (NaN if unknown/unreadable).
1027 */
1028 2447 double omc_matlab4_startTime(ModelicaMatReader *reader)
1029 {
1030
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2446 times.
2447 if (reader->startTime != reader->startTime /* NaN */) {
1031 1 read_start_stop_time(reader);
1032 }
1033 2447 return reader->startTime;
1034 }
1035
1036 /**
1037 * @brief Get the stop time of the dataset managed by `reader`.
1038 *
1039 * If the stop time has not yet been determined this function will read
1040 * the time column lazily to extract the last timestamp.
1041 *
1042 * @param reader Pointer to an initialized ModelicaMatReader.
1043 * @return Stop time as a double (NaN if unknown/unreadable).
1044 */
1045 2447 double omc_matlab4_stopTime(ModelicaMatReader *reader)
1046 {
1047
2/2
✓ Branch 0 taken 425 times.
✓ Branch 1 taken 2022 times.
2447 if (reader->stopTime != reader->stopTime /* NaN */) {
1048 425 read_start_stop_time(reader);
1049 }
1050 2447 return reader->stopTime;
1051 }
1052
1053 /**
1054 * @brief Evaluate a variable (parameter or time-dependent) at a given time.
1055 *
1056 * If `var` represents a parameter the value is returned directly from
1057 * `reader->params`. For time-dependent variables a nearest-neighbour or
1058 * linear interpolation between the two surrounding time points is used.
1059 *
1060 * @param res Output pointer where the evaluated value will be stored.
1061 * @param reader Pointer to an initialized ModelicaMatReader.
1062 * @param var Pointer to the variable metadata (from `omc_matlab4_find_var`).
1063 * @param time Time at which to evaluate the variable.
1064 * @return 0 on success, non-zero on error (out of range, read failure).
1065 */
1066 2488 int omc_matlab4_val(double *res, ModelicaMatReader *reader, ModelicaMatVariable_t *var, double time)
1067 {
1068
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 2446 times.
2488 if(var->isParam) {
1069
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
42 if(var->index < 0)
1070 ✗ *res = -reader->params[abs(var->index)-1];
1071 else
1072 42 *res = reader->params[var->index-1];
1073 } else {
1074 double w1,w2,y1,y2;
1075 int i1,i2;
1076
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2446 times.
2446 if(time > omc_matlab4_stopTime(reader)) {
1077 ✗ *res = NAN;
1078 ✗ return 1;
1079 }
1080
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2446 times.
2446 if(time < omc_matlab4_startTime(reader)) {
1081 ✗ *res = NAN;
1082 ✗ return 1;
1083 }
1084
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2446 times.
2446 if(!omc_matlab4_read_vals(reader,1)) {
1085 ✗ *res = NAN;
1086 ✗ return 1;
1087 }
1088 2446 find_closest_points(time, reader->vars[0], reader->nrows, &i1, &w1, &i2, &w2);
1089
2/2
✓ Branch 0 taken 1962 times.
✓ Branch 1 taken 484 times.
2446 if(i2 == -1) {
1090 1962 return (int)omc_matlab4_read_single_val(res,reader,var->index,i1);
1091
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 484 times.
484 } else if(i1 == -1) {
1092 ✗ return (int)omc_matlab4_read_single_val(res,reader,var->index,i2);
1093 } else {
1094
1/2
✓ Branch 1 taken 484 times.
✗ Branch 2 not taken.
484 if(omc_matlab4_read_single_val(&y1,reader,var->index,i1)) return 1;
1095
1/2
✓ Branch 1 taken 484 times.
✗ Branch 2 not taken.
484 if(omc_matlab4_read_single_val(&y2,reader,var->index,i2)) return 1;
1096 484 *res = w1*y1 + w2*y2;
1097 484 return 0;
1098 }
1099 }
1100 return 0;
1101 }
1102
1103 /**
1104 * @brief Read multiple variables at a given time into a result array.
1105 *
1106 * For each variable in `vars` this function either returns the parameter
1107 * value or performs interpolation from the time-series data. Results are
1108 * written into the `res` array (length N).
1109 *
1110 * #### Note
1111 *
1112 * This function is not defined for parameters.
1113 * Check `var->isParam` and then send the index.
1114 *
1115 * No bounds checking is performed.
1116 * The returned data persists until the reader is closed.
1117 *
1118 * @param res Output array of length N where values will be stored.
1119 * @param reader Pointer to an initialized ModelicaMatReader.
1120 * @param vars Array of N pointers to ModelicaMatVariable_t descriptors.
1121 * @param N Number of variables to read.
1122 * @param time Time at which to evaluate the variables.
1123 * @return 0 on success, non-zero on failure (out-of-range or read error).
1124 */
1125 ✗ int omc_matlab4_read_vars_val(double *res, ModelicaMatReader *reader, ModelicaMatVariable_t **vars, int N, double time){
1126 double w1,w2,y1,y2;
1127 int i,i1,i2;
1128 ✗ if(time > omc_matlab4_stopTime(reader)) return 1;
1129 ✗ if(time < omc_matlab4_startTime(reader)) return 1;
1130 ✗ if(!omc_matlab4_read_vals(reader,1)) return 1;
1131 ✗ find_closest_points(time, reader->vars[0], reader->nrows, &i1, &w1, &i2, &w2);
1132 ✗ for (i = 0; i< N; i++){
1133 ✗ if(vars[i]->isParam) {
1134 ✗ if(vars[i]->index < 0)
1135 ✗ res[i] = -reader->params[abs(vars[i]->index)-1];
1136 else
1137 ✗ res[i] = reader->params[vars[i]->index-1];
1138 } else {
1139 ✗ if(i2 == -1) {
1140
1141 ✗ if (omc_matlab4_read_single_val(&res[i],reader,vars[i]->index,i1)) return 1;
1142
1143 ✗ } else if(i1 == -1) {
1144
1145 ✗ if (omc_matlab4_read_single_val(&res[i],reader,vars[i]->index,i2)) return 1;
1146
1147 } else {
1148
1149 ✗ if(omc_matlab4_read_single_val(&y1,reader,vars[i]->index,i1)) return 1;
1150 ✗ if(omc_matlab4_read_single_val(&y2,reader,vars[i]->index,i2)) return 1;
1151 ✗ res[i] = w1*y1 + w2*y2;
1152
1153 }
1154 }
1155 }
1156 return 0;
1157 }
1158
1159 /**
1160 * @brief Print all variables to file.
1161 *
1162 * #### Notes:
1163 *
1164 * For debugging purpose
1165 *
1166 * @param stream Text file to write to.
1167 * @param reader Pointer to an initialized ModelicaMatReader.
1168 */
1169 ✗ void omc_matlab4_print_all_vars(FILE *stream, ModelicaMatReader *reader)
1170 {
1171 unsigned int i;
1172 ✗ fprintf(stream, "allSortedVars(\"%s\") => {", reader->fileName);
1173 ✗ for(i=0; i<reader->nall; i++)
1174 ✗ fprintf(stream, "\"%s\",", reader->allInfo[i].name);
1175 fprintf(stream, "}\n");
1176 ✗ }
1177