OMCompiler/SimulationRuntime/c/util/read_csv.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 <string.h> | ||
| 29 | #include <stdlib.h> | ||
| 30 | #include <stdio.h> | ||
| 31 | #include "read_csv.h" | ||
| 32 | #include "read_matlab4.h" | ||
| 33 | #include "libcsv.h" | ||
| 34 | #include "omc_file.h" | ||
| 35 | #include "omc_numbers.h" | ||
| 36 | #include "omc_strdup.h" | ||
| 37 | |||
| 38 | struct cell_row_count | ||
| 39 | { | ||
| 40 | int cell_count; | ||
| 41 | int row_count; | ||
| 42 | }; | ||
| 43 | |||
| 44 | struct csv_head | ||
| 45 | { | ||
| 46 | char **variables; | ||
| 47 | int size; | ||
| 48 | int buffer_size; | ||
| 49 | int found_row; | ||
| 50 | }; | ||
| 51 | |||
| 52 | struct csv_body | ||
| 53 | { | ||
| 54 | double *res; | ||
| 55 | int size; | ||
| 56 | int buffer_size; | ||
| 57 | int found_first_row; | ||
| 58 | int cur_size; | ||
| 59 | int row_length; | ||
| 60 | int error; | ||
| 61 | }; | ||
| 62 | |||
| 63 | ✗ | static void do_nothing(void *data, size_t len, void *t) | |
| 64 | { | ||
| 65 | ✗ | } | |
| 66 | |||
| 67 | 305 | static void found_first_row(int c, void *t) | |
| 68 | { | ||
| 69 | struct csv_head *head = (struct csv_head*) t; | ||
| 70 | 305 | head->found_row++; | |
| 71 | 305 | } | |
| 72 | |||
| 73 | 4302 | static void add_variable(void *data, size_t len, void *t) | |
| 74 | { | ||
| 75 | struct csv_head *head = (struct csv_head*) t; | ||
| 76 |
2/2✓ Branch 0 taken 172 times.
✓ Branch 1 taken 4130 times.
|
4302 | if (head->found_row) { |
| 77 | return; | ||
| 78 | } | ||
| 79 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 166 times.
|
172 | if (head->size+1 >= head->buffer_size) { |
| 80 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | head->buffer_size = head->buffer_size ? 2*head->buffer_size : 512; |
| 81 | 6 | head->variables = (char**) realloc(head->variables, sizeof(char*)*head->buffer_size); | |
| 82 | } | ||
| 83 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 172 times.
|
172 | head->variables[head->size++] = omc_strdup(data ? (char*) data : ""); |
| 84 | } | ||
| 85 | |||
| 86 | ✗ | static void row_count(int c, void *t) | |
| 87 | { | ||
| 88 | struct cell_row_count *s = (struct cell_row_count *) t; | ||
| 89 | ✗ | s->row_count++; | |
| 90 | ✗ | } | |
| 91 | |||
| 92 | ✗ | int read_csv_dataset_size(const char* filename) | |
| 93 | { | ||
| 94 | const int buf_size = 4096; | ||
| 95 | char buf[4096]; | ||
| 96 | FILE *f; | ||
| 97 | struct csv_parser p; | ||
| 98 | ✗ | struct cell_row_count count = {0}; | |
| 99 | size_t offset=0; | ||
| 100 | ✗ | unsigned char delim = CSV_COMMA; | |
| 101 | ✗ | f = omc_fopen(filename,"r"); | |
| 102 | ✗ | if (f == NULL) { | |
| 103 | return -1; | ||
| 104 | } | ||
| 105 | |||
| 106 | /* determine delim */ | ||
| 107 | ✗ | omc_fread(buf, 1, 5, f, 0); | |
| 108 | ✗ | if (0 == strcmp(buf, "\"sep=")) | |
| 109 | { | ||
| 110 | ✗ | omc_fread(&delim, 1, 1, f, 0); | |
| 111 | offset = 8; | ||
| 112 | } | ||
| 113 | ✗ | fseek(f, offset, SEEK_SET); | |
| 114 | |||
| 115 | ✗ | csv_init(&p, CSV_STRICT | CSV_REPALL_NL | CSV_STRICT_FINI | CSV_APPEND_NULL | CSV_EMPTY_IS_NULL, delim); | |
| 116 | ✗ | csv_set_realloc_func(&p, realloc); | |
| 117 | ✗ | csv_set_free_func(&p, free); | |
| 118 | do { | ||
| 119 | ✗ | size_t len = omc_fread(buf, 1, buf_size, f, 1); | |
| 120 | ✗ | if (len != buf_size && !feof(f)) { | |
| 121 | ✗ | csv_free(&p); | |
| 122 | ✗ | fclose(f); | |
| 123 | ✗ | return -1; | |
| 124 | } | ||
| 125 | ✗ | csv_parse(&p,buf,len,do_nothing,row_count,&count); | |
| 126 | ✗ | } while (!feof(f)); | |
| 127 | ✗ | csv_fini(&p,do_nothing,row_count,&count); | |
| 128 | ✗ | csv_free(&p); | |
| 129 | ✗ | fclose(f); | |
| 130 | ✗ | return count.row_count - 1; /* The header is excluded */ | |
| 131 | } | ||
| 132 | |||
| 133 | 6 | char** read_csv_variables(FILE *fin, int *length, unsigned char delim) | |
| 134 | { | ||
| 135 | const int buf_size = 4096; | ||
| 136 | char buf[4096]; | ||
| 137 | char **res; | ||
| 138 | struct csv_parser p; | ||
| 139 | 6 | struct csv_head head = {0}; | |
| 140 | 6 | csv_init(&p, CSV_STRICT | CSV_REPALL_NL | CSV_STRICT_FINI | CSV_APPEND_NULL | CSV_EMPTY_IS_NULL, delim); | |
| 141 | 6 | csv_set_realloc_func(&p, realloc); | |
| 142 | 6 | csv_set_free_func(&p, free); | |
| 143 | do { | ||
| 144 | 6 | size_t len = omc_fread(buf, 1, buf_size, fin, 1); | |
| 145 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
6 | if (len != buf_size && !feof(fin)) { |
| 146 | ✗ | csv_free(&p); | |
| 147 | ✗ | return NULL; | |
| 148 | } | ||
| 149 | 6 | csv_parse(&p,buf,len,add_variable,found_first_row,&head); | |
| 150 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
6 | } while (!head.found_row && !feof(fin)); |
| 151 | 6 | csv_free(&p); | |
| 152 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if (!head.found_row) { |
| 153 | return NULL; | ||
| 154 | } | ||
| 155 | 6 | *length = head.size-1; | |
| 156 | 6 | return head.variables; | |
| 157 | } | ||
| 158 | |||
| 159 | 866595 | static void add_cell(void *data, size_t len, void *t) | |
| 160 | { | ||
| 161 | struct csv_body *body = (struct csv_body*) t; | ||
| 162 | 866595 | char *endptr = ""; | |
| 163 |
1/2✓ Branch 0 taken 866595 times.
✗ Branch 1 not taken.
|
866595 | if (body->error) { |
| 164 | 172 | return; | |
| 165 | } | ||
| 166 |
2/2✓ Branch 0 taken 172 times.
✓ Branch 1 taken 866423 times.
|
866595 | if (!body->found_first_row) { |
| 167 | 172 | body->cur_size++; | |
| 168 | 172 | body->row_length++; | |
| 169 | 172 | return; | |
| 170 | } | ||
| 171 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 866408 times.
|
866423 | if (body->size+1 >= body->buffer_size) { |
| 172 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 6 times.
|
15 | body->buffer_size = body->res ? 2*body->buffer_size : body->row_length*1024; /* Guess it's 1024 time points; we could also take the size of the file or something, but this is cool too */ |
| 173 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
15 | body->buffer_size = body->buffer_size > 0 ? body->buffer_size : 1024; |
| 174 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 6 times.
|
15 | body->res = body->res ? (double*)realloc(body->res, sizeof(double)*body->buffer_size) : (double*) malloc(sizeof(double)*body->buffer_size); |
| 175 | } | ||
| 176 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 866423 times.
|
866423 | if (data == NULL) { |
| 177 | ✗ | body->res[body->size++] = 0.0; | |
| 178 | ✗ | return; | |
| 179 | } | ||
| 180 | 866423 | body->res[body->size++] = data ? om_strtod((const char*)data,&endptr) : 0; | |
| 181 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 866423 times.
|
866423 | if (*endptr) { |
| 182 | ✗ | fprintf(stderr,"Found non-double data in csv result-file: %s\n", (char*) data); | |
| 183 | ✗ | body->error = 1; | |
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | 19075 | static void add_row(int c, void *t) | |
| 188 | { | ||
| 189 | struct csv_body *body = (struct csv_body*) t; | ||
| 190 | 19075 | body->found_first_row++; | |
| 191 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19075 times.
|
19075 | if (body->cur_size != body->row_length) { |
| 192 | ✗ | fprintf(stderr,"Did not find time points for all variables for row: %d\n", body->found_first_row); | |
| 193 | ✗ | body->error = 1; | |
| 194 | ✗ | return; | |
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | ✗ | double* read_csv_dataset_var(const char *filename, const char *var, int dimsize) | |
| 199 | { | ||
| 200 | const int buf_size = 4096; | ||
| 201 | char buf[4096]; | ||
| 202 | char **res; | ||
| 203 | struct csv_parser p; | ||
| 204 | ✗ | struct csv_body body = {0}; | |
| 205 | ✗ | FILE *fin = omc_fopen(filename, "r"); | |
| 206 | size_t offset = 0; | ||
| 207 | ✗ | unsigned char delim = CSV_COMMA; | |
| 208 | ✗ | if (!fin) { | |
| 209 | return NULL; | ||
| 210 | } | ||
| 211 | |||
| 212 | /* determine delim */ | ||
| 213 | ✗ | omc_fread(buf, 1, 5, fin, 0); | |
| 214 | ✗ | if (0 == strcmp(buf, "\"sep=")) | |
| 215 | { | ||
| 216 | ✗ | omc_fread(&delim, 1, 1, fin, 0); | |
| 217 | offset = 8; | ||
| 218 | } | ||
| 219 | ✗ | fseek(fin, offset, SEEK_SET); | |
| 220 | |||
| 221 | ✗ | csv_init(&p, CSV_STRICT | CSV_REPALL_NL | CSV_STRICT_FINI | CSV_APPEND_NULL | CSV_EMPTY_IS_NULL, delim); | |
| 222 | ✗ | csv_set_realloc_func(&p, realloc); | |
| 223 | ✗ | csv_set_free_func(&p, free); | |
| 224 | do { | ||
| 225 | ✗ | size_t len = omc_fread(buf, 1, buf_size, fin, 1); | |
| 226 | ✗ | if (len != buf_size && !feof(fin)) { | |
| 227 | ✗ | csv_free(&p); | |
| 228 | ✗ | fclose(fin); | |
| 229 | ✗ | return NULL; | |
| 230 | } | ||
| 231 | ✗ | csv_parse(&p,buf,len,add_cell,add_row,&body); | |
| 232 | ✗ | } while (!body.error && !feof(fin)); | |
| 233 | ✗ | csv_fini(&p,add_cell,add_row,&body); | |
| 234 | ✗ | csv_free(&p); | |
| 235 | ✗ | fclose(fin); | |
| 236 | ✗ | if (body.error) { | |
| 237 | return NULL; | ||
| 238 | } | ||
| 239 | ✗ | return body.res; | |
| 240 | } | ||
| 241 | |||
| 242 | 6 | struct csv_data* read_csv(const char *filename) | |
| 243 | { | ||
| 244 | const int buf_size = 4096; | ||
| 245 | char buf[4096]; | ||
| 246 | char **variables; | ||
| 247 | int dummy; | ||
| 248 | struct csv_parser p; | ||
| 249 | 6 | struct csv_body body = {0}; | |
| 250 | struct csv_data *res; | ||
| 251 | size_t offset = 0; | ||
| 252 | 6 | unsigned char delim = CSV_COMMA; | |
| 253 | size_t len; | ||
| 254 | |||
| 255 | 6 | FILE *fin = omc_fopen(filename, "r"); | |
| 256 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if (!fin) { |
| 257 | return NULL; | ||
| 258 | } | ||
| 259 | |||
| 260 | /* determine delim */ | ||
| 261 | 6 | len = omc_fread(buf, 1, 5, fin, 0); | |
| 262 | // Terminate the string in the buffer to make sure strcmp works as expected. | ||
| 263 | 6 | buf[len] = '\0'; | |
| 264 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (0 == strcmp(buf, "\"sep=")) |
| 265 | { | ||
| 266 | ✗ | omc_fread(&delim, 1, 1, fin, 0); | |
| 267 | offset = 8; | ||
| 268 | } | ||
| 269 | 6 | fseek(fin, offset, SEEK_SET); | |
| 270 | |||
| 271 | 6 | variables = read_csv_variables(fin, &dummy, delim); | |
| 272 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (!variables) { |
| 273 | ✗ | fclose(fin); | |
| 274 | ✗ | return NULL; | |
| 275 | } | ||
| 276 | 6 | fseek(fin,offset,SEEK_SET); | |
| 277 | |||
| 278 | 6 | csv_init(&p, CSV_STRICT | CSV_REPALL_NL | CSV_STRICT_FINI | CSV_APPEND_NULL | CSV_EMPTY_IS_NULL, delim); | |
| 279 | 6 | csv_set_realloc_func(&p, realloc); | |
| 280 | 6 | csv_set_free_func(&p, free); | |
| 281 | do { | ||
| 282 | 3229 | len = omc_fread(buf, 1, buf_size, fin, 1); | |
| 283 |
3/4✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3223 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
|
3229 | if (len != buf_size && !feof(fin)) { |
| 284 | ✗ | csv_free(&p); | |
| 285 | ✗ | fclose(fin); | |
| 286 | ✗ | return NULL; | |
| 287 | } | ||
| 288 | 3229 | csv_parse(&p,buf,len,add_cell,add_row,&body); | |
| 289 |
3/4✓ Branch 0 taken 3229 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 3223 times.
✓ Branch 4 taken 6 times.
|
3229 | } while (!body.error && !feof(fin)); |
| 290 | 6 | csv_fini(&p,add_cell,add_row,&body); | |
| 291 | 6 | csv_free(&p); | |
| 292 | 6 | fclose(fin); | |
| 293 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if (body.error) { |
| 294 | return NULL; | ||
| 295 | } | ||
| 296 | 6 | res = (struct csv_data*) malloc(sizeof(struct csv_data)); | |
| 297 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if (!res) { |
| 298 | return NULL; | ||
| 299 | } | ||
| 300 | 6 | res->variables = variables; | |
| 301 | 6 | res->data = body.res; | |
| 302 | 6 | res->numvars = body.row_length; | |
| 303 | 6 | res->numsteps = body.size / body.row_length; | |
| 304 | 6 | matrix_transpose(res->data,res->numvars,res->numsteps); | |
| 305 | /* printf("num vars %d in %s num steps %d\n", body.row_length, filename, res->numsteps); */ | ||
| 306 | 6 | return res; | |
| 307 | } | ||
| 308 | |||
| 309 | 15 | double* read_csv_dataset(struct csv_data *data, const char *var) | |
| 310 | { | ||
| 311 | int i,found=-1; | ||
| 312 |
1/2✓ Branch 0 taken 92 times.
✗ Branch 1 not taken.
|
92 | for (i=0; i<data->numvars; i++) { |
| 313 |
2/2✓ Branch 0 taken 77 times.
✓ Branch 1 taken 15 times.
|
92 | if (0==strcmp(data->variables[i],var)) { |
| 314 | found=i; | ||
| 315 | break; | ||
| 316 | } | ||
| 317 | } | ||
| 318 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
15 | if (found == -1) { |
| 319 | return NULL; | ||
| 320 | } | ||
| 321 | 15 | return data->data + i*data->numsteps; | |
| 322 | } | ||
| 323 | |||
| 324 | 5 | void omc_free_csv_reader(struct csv_data *data) | |
| 325 | { | ||
| 326 | int i; | ||
| 327 |
2/2✓ Branch 0 taken 155 times.
✓ Branch 1 taken 5 times.
|
160 | for (i=0; i<data->numvars; i++) { |
| 328 | 155 | free(data->variables[i]); | |
| 329 | } | ||
| 330 | 5 | free(data->variables); | |
| 331 | 5 | free(data->data); | |
| 332 | data->variables = 0; | ||
| 333 | data->data = 0; | ||
| 334 | 5 | free(data); | |
| 335 | 5 | } | |
| 336 |