1 /*
2 * Copyright (c) 2012-2021, Christopher C. Hulbert
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "matio_private.h"
28 #include <stdlib.h>
29 #include <string.h>
30 #if defined(_MSC_VER) || defined(__MINGW32__)
31 #define strdup _strdup
32 #endif
33
34 /** @brief Creates a structure MATLAB variable with the given name and fields
35 *
36 * @ingroup MAT
37 * @param name Name of the structure variable to create
38 * @param rank Rank of the variable
39 * @param dims array of dimensions of the variable of size rank
40 * @param fields Array of @c nfields fieldnames
41 * @param nfields Number of fields in the structure
42 * @return Pointer to the new structure MATLAB variable on success, NULL on error
43 */
44 matvar_t *
Mat_VarCreateStruct(const char * name,int rank,size_t * dims,const char ** fields,unsigned nfields)45 Mat_VarCreateStruct(const char *name, int rank, size_t *dims, const char **fields, unsigned nfields)
46 {
47 size_t nelems = 1;
48 int j;
49 matvar_t *matvar;
50
51 if ( NULL == dims )
52 return NULL;
53
54 matvar = Mat_VarCalloc();
55 if ( NULL == matvar )
56 return NULL;
57
58 matvar->compression = MAT_COMPRESSION_NONE;
59 if ( NULL != name )
60 matvar->name = strdup(name);
61 matvar->rank = rank;
62 matvar->dims = (size_t *)malloc(matvar->rank * sizeof(*matvar->dims));
63 for ( j = 0; j < matvar->rank; j++ ) {
64 matvar->dims[j] = dims[j];
65 nelems *= dims[j];
66 }
67 matvar->class_type = MAT_C_STRUCT;
68 matvar->data_type = MAT_T_STRUCT;
69
70 matvar->data_size = sizeof(matvar_t *);
71
72 if ( nfields ) {
73 matvar->internal->num_fields = nfields;
74 matvar->internal->fieldnames =
75 (char **)malloc(nfields * sizeof(*matvar->internal->fieldnames));
76 if ( NULL == matvar->internal->fieldnames ) {
77 Mat_VarFree(matvar);
78 matvar = NULL;
79 } else {
80 size_t i;
81 for ( i = 0; i < nfields; i++ ) {
82 if ( NULL == fields[i] ) {
83 Mat_VarFree(matvar);
84 matvar = NULL;
85 break;
86 } else {
87 matvar->internal->fieldnames[i] = strdup(fields[i]);
88 }
89 }
90 }
91 if ( NULL != matvar && nelems > 0 ) {
92 size_t nelems_x_nfields;
93 int err = Mul(&nelems_x_nfields, nelems, nfields);
94 err |= Mul(&matvar->nbytes, nelems_x_nfields, matvar->data_size);
95 if ( err ) {
96 Mat_VarFree(matvar);
97 return NULL;
98 }
99 matvar->data = calloc(nelems_x_nfields, matvar->data_size);
100 }
101 }
102
103 return matvar;
104 }
105
106 /** @brief Adds a field to a structure
107 *
108 * Adds the given field to the structure. fields should be an array of matvar_t
109 * pointers of the same size as the structure (i.e. 1 field per structure
110 * element).
111 * @ingroup MAT
112 * @param matvar Pointer to the Structure MAT variable
113 * @param fieldname Name of field to be added
114 * @retval 0 on success
115 */
116 int
Mat_VarAddStructField(matvar_t * matvar,const char * fieldname)117 Mat_VarAddStructField(matvar_t *matvar, const char *fieldname)
118 {
119 int err;
120 int cnt = 0;
121 size_t i, nfields, nelems = 1;
122 matvar_t **new_data, **old_data;
123 char **fieldnames;
124
125 if ( matvar == NULL || fieldname == NULL )
126 return -1;
127
128 err = Mat_MulDims(matvar, &nelems);
129 if ( err )
130 return -1;
131
132 matvar->internal->num_fields++;
133 nfields = matvar->internal->num_fields;
134 fieldnames = (char **)realloc(matvar->internal->fieldnames,
135 nfields * sizeof(*matvar->internal->fieldnames));
136 if ( NULL == fieldnames )
137 return -1;
138 matvar->internal->fieldnames = fieldnames;
139 matvar->internal->fieldnames[nfields - 1] = strdup(fieldname);
140
141 {
142 size_t nelems_x_nfields;
143 err = Mul(&nelems_x_nfields, nelems, nfields);
144 err |= Mul(&matvar->nbytes, nelems_x_nfields, sizeof(*new_data));
145 if ( err ) {
146 matvar->nbytes = 0;
147 return -1;
148 }
149 }
150 new_data = (matvar_t **)malloc(matvar->nbytes);
151 if ( new_data == NULL ) {
152 matvar->nbytes = 0;
153 return -1;
154 }
155
156 old_data = (matvar_t **)matvar->data;
157 for ( i = 0; i < nelems; i++ ) {
158 size_t f;
159 for ( f = 0; f < nfields - 1; f++ )
160 new_data[cnt++] = old_data[i * (nfields - 1) + f];
161 new_data[cnt++] = NULL;
162 }
163
164 free(matvar->data);
165 matvar->data = new_data;
166
167 return 0;
168 }
169
170 /** @brief Returns the number of fields in a structure variable
171 *
172 * Returns the number of fields in the given structure.
173 * @ingroup MAT
174 * @param matvar Structure matlab variable
175 * @returns Number of fields
176 */
177 unsigned
Mat_VarGetNumberOfFields(matvar_t * matvar)178 Mat_VarGetNumberOfFields(matvar_t *matvar)
179 {
180 int nfields;
181 if ( matvar == NULL || matvar->class_type != MAT_C_STRUCT || NULL == matvar->internal ) {
182 nfields = 0;
183 } else {
184 nfields = matvar->internal->num_fields;
185 }
186 return nfields;
187 }
188
189 /** @brief Returns the fieldnames of a structure variable
190 *
191 * Returns the fieldnames for the given structure. The returned pointers are
192 * internal to the structure and should not be free'd.
193 * @ingroup MAT
194 * @param matvar Structure matlab variable
195 * @returns Array of fieldnames
196 */
197 char *const *
Mat_VarGetStructFieldnames(const matvar_t * matvar)198 Mat_VarGetStructFieldnames(const matvar_t *matvar)
199 {
200 if ( matvar == NULL || matvar->class_type != MAT_C_STRUCT || NULL == matvar->internal ) {
201 return NULL;
202 } else {
203 return matvar->internal->fieldnames;
204 }
205 }
206
207 /** @brief Finds a field of a structure by the field's index
208 *
209 * Returns a pointer to the structure field at the given 0-relative index.
210 * @ingroup MAT
211 * @param matvar Pointer to the Structure MAT variable
212 * @param field_index 0-relative index of the field.
213 * @param index linear index of the structure array
214 * @return Pointer to the structure field on success, NULL on error
215 */
216 matvar_t *
Mat_VarGetStructFieldByIndex(matvar_t * matvar,size_t field_index,size_t index)217 Mat_VarGetStructFieldByIndex(matvar_t *matvar, size_t field_index, size_t index)
218 {
219 int err;
220 matvar_t *field = NULL;
221 size_t nelems = 1, nfields;
222
223 if ( matvar == NULL || matvar->class_type != MAT_C_STRUCT || matvar->data_size == 0 )
224 return NULL;
225
226 err = Mat_MulDims(matvar, &nelems);
227 if ( err )
228 return NULL;
229
230 nfields = matvar->internal->num_fields;
231
232 if ( nelems > 0 && index >= nelems ) {
233 Mat_Critical("Mat_VarGetStructField: structure index out of bounds");
234 } else if ( nfields > 0 ) {
235 if ( field_index > nfields ) {
236 Mat_Critical("Mat_VarGetStructField: field index out of bounds");
237 } else {
238 field = *((matvar_t **)matvar->data + index * nfields + field_index);
239 }
240 }
241
242 return field;
243 }
244
245 /** @brief Finds a field of a structure by the field's name
246 *
247 * Returns a pointer to the structure field at the given 0-relative index.
248 * @ingroup MAT
249 * @param matvar Pointer to the Structure MAT variable
250 * @param field_name Name of the structure field
251 * @param index linear index of the structure array
252 * @return Pointer to the structure field on success, NULL on error
253 */
254 matvar_t *
Mat_VarGetStructFieldByName(matvar_t * matvar,const char * field_name,size_t index)255 Mat_VarGetStructFieldByName(matvar_t *matvar, const char *field_name, size_t index)
256 {
257 int i, nfields, field_index, err;
258 matvar_t *field = NULL;
259 size_t nelems = 1;
260
261 if ( matvar == NULL || matvar->class_type != MAT_C_STRUCT || matvar->data_size == 0 )
262 return NULL;
263
264 err = Mat_MulDims(matvar, &nelems);
265 if ( err )
266 return NULL;
267
268 nfields = matvar->internal->num_fields;
269 field_index = -1;
270 for ( i = 0; i < nfields; i++ ) {
271 if ( !strcmp(matvar->internal->fieldnames[i], field_name) ) {
272 field_index = i;
273 break;
274 }
275 }
276
277 if ( index >= nelems ) {
278 Mat_Critical("Mat_VarGetStructField: structure index out of bounds");
279 } else if ( field_index >= 0 ) {
280 field = *((matvar_t **)matvar->data + index * nfields + field_index);
281 }
282
283 return field;
284 }
285
286 /** @brief Finds a field of a structure
287 *
288 * Returns a pointer to the structure field at the given 0-relative index.
289 * @ingroup MAT
290 * @param matvar Pointer to the Structure MAT variable
291 * @param name_or_index Name of the field, or the 1-relative index of the field
292 * If the index is used, it should be the address of an integer variable whose
293 * value is the index number.
294 * @param opt MAT_BY_NAME if the name_or_index is the name or MAT_BY_INDEX if
295 * the index was passed.
296 * @param index linear index of the structure to find the field of
297 * @return Pointer to the Structure Field on success, NULL on error
298 */
299 matvar_t *
Mat_VarGetStructField(matvar_t * matvar,void * name_or_index,int opt,int index)300 Mat_VarGetStructField(matvar_t *matvar, void *name_or_index, int opt, int index)
301 {
302 int err, nfields;
303 matvar_t *field = NULL;
304 size_t nelems = 1;
305
306 err = Mat_MulDims(matvar, &nelems);
307 nfields = matvar->internal->num_fields;
308 if ( index < 0 || (nelems > 0 && (size_t)index >= nelems) )
309 err = 1;
310 else if ( nfields < 1 )
311 err = 1;
312
313 if ( !err && (opt == MAT_BY_INDEX) ) {
314 size_t field_index = *(int *)name_or_index;
315 if ( field_index > 0 )
316 field = Mat_VarGetStructFieldByIndex(matvar, field_index - 1, index);
317 } else if ( !err && (opt == MAT_BY_NAME) ) {
318 field = Mat_VarGetStructFieldByName(matvar, (const char *)name_or_index, index);
319 }
320
321 return field;
322 }
323
324 /** @brief Indexes a structure
325 *
326 * Finds structures of a structure array given a start, stride, and edge for
327 * each dimension. The structures are placed in a new structure array. If
328 * copy_fields is non-zero, the indexed structures are copied and should be
329 * freed, but if copy_fields is zero, the indexed structures are pointers to
330 * the original, but should still be freed. The structures have a flag set
331 * so that the structure fields are not freed.
332 *
333 * Note that this function is limited to structure arrays with a rank less than
334 * 10.
335 *
336 * @ingroup MAT
337 * @param matvar Structure matlab variable
338 * @param start vector of length rank with 0-relative starting coordinates for
339 * each dimension.
340 * @param stride vector of length rank with strides for each dimension.
341 * @param edge vector of length rank with the number of elements to read in
342 * each dimension.
343 * @param copy_fields 1 to copy the fields, 0 to just set pointers to them.
344 * @returns A new structure array with fields indexed from @c matvar.
345 */
346 matvar_t *
Mat_VarGetStructs(matvar_t * matvar,int * start,int * stride,int * edge,int copy_fields)347 Mat_VarGetStructs(matvar_t *matvar, int *start, int *stride, int *edge, int copy_fields)
348 {
349 size_t i, N, I, nfields, field,
350 idx[10] =
351 {
352 0,
353 },
354 cnt[10] =
355 {
356 0,
357 },
358 dimp[10] = {
359 0,
360 };
361 matvar_t **fields, *struct_slab;
362 int j;
363
364 if ( matvar == NULL || start == NULL || stride == NULL || edge == NULL ) {
365 return NULL;
366 } else if ( matvar->rank > 9 ) {
367 return NULL;
368 } else if ( matvar->class_type != MAT_C_STRUCT ) {
369 return NULL;
370 }
371
372 struct_slab = Mat_VarDuplicate(matvar, 0);
373 if ( !copy_fields )
374 struct_slab->mem_conserve = 1;
375
376 nfields = matvar->internal->num_fields;
377
378 dimp[0] = matvar->dims[0];
379 N = edge[0];
380 I = start[0];
381 struct_slab->dims[0] = edge[0];
382 idx[0] = start[0];
383 for ( j = 1; j < matvar->rank; j++ ) {
384 idx[j] = start[j];
385 dimp[j] = dimp[j - 1] * matvar->dims[j];
386 N *= edge[j];
387 I += start[j] * dimp[j - 1];
388 struct_slab->dims[j] = edge[j];
389 }
390 I *= nfields;
391 struct_slab->nbytes = N * nfields * sizeof(matvar_t *);
392 struct_slab->data = malloc(struct_slab->nbytes);
393 if ( struct_slab->data == NULL ) {
394 Mat_VarFree(struct_slab);
395 return NULL;
396 }
397 fields = (matvar_t **)struct_slab->data;
398 for ( i = 0; i < N; i += edge[0] ) {
399 for ( j = 0; j < edge[0]; j++ ) {
400 for ( field = 0; field < nfields; field++ ) {
401 if ( copy_fields )
402 fields[(i + j) * nfields + field] =
403 Mat_VarDuplicate(*((matvar_t **)matvar->data + I), 1);
404 else
405 fields[(i + j) * nfields + field] = *((matvar_t **)matvar->data + I);
406 I++;
407 }
408 I += (stride[0] - 1) * nfields;
409 }
410 idx[0] = start[0];
411 I = idx[0];
412 cnt[1]++;
413 idx[1] += stride[1];
414 for ( j = 1; j < matvar->rank; j++ ) {
415 if ( cnt[j] == (size_t)edge[j] ) {
416 cnt[j] = 0;
417 idx[j] = start[j];
418 if ( j < matvar->rank - 1 ) {
419 cnt[j + 1]++;
420 idx[j + 1] += stride[j + 1];
421 }
422 }
423 I += idx[j] * dimp[j - 1];
424 }
425 I *= nfields;
426 }
427 return struct_slab;
428 }
429
430 /** @brief Indexes a structure
431 *
432 * Finds structures of a structure array given a single (linear)start, stride,
433 * and edge. The structures are placed in a new structure array. If
434 * copy_fields is non-zero, the indexed structures are copied and should be
435 * freed, but if copy_fields is zero, the indexed structures are pointers to
436 * the original, but should still be freed since the mem_conserve flag is set
437 * so that the structures are not freed.
438 * MAT file version must be 5.
439 * @ingroup MAT
440 * @param matvar Structure matlab variable
441 * @param start starting index (0-relative)
442 * @param stride stride (1 reads consecutive elements)
443 * @param edge Number of elements to read
444 * @param copy_fields 1 to copy the fields, 0 to just set pointers to them.
445 * @returns A new structure with fields indexed from matvar
446 */
447 matvar_t *
Mat_VarGetStructsLinear(matvar_t * matvar,int start,int stride,int edge,int copy_fields)448 Mat_VarGetStructsLinear(matvar_t *matvar, int start, int stride, int edge, int copy_fields)
449 {
450 matvar_t *struct_slab;
451
452 if ( matvar == NULL || matvar->rank > 10 ) {
453 struct_slab = NULL;
454 } else {
455 int i, I, field, nfields;
456 matvar_t **fields;
457
458 struct_slab = Mat_VarDuplicate(matvar, 0);
459 if ( !copy_fields )
460 struct_slab->mem_conserve = 1;
461
462 nfields = matvar->internal->num_fields;
463
464 struct_slab->nbytes = (size_t)edge * nfields * sizeof(matvar_t *);
465 struct_slab->data = malloc(struct_slab->nbytes);
466 if ( struct_slab->data == NULL ) {
467 Mat_VarFree(struct_slab);
468 return NULL;
469 }
470 struct_slab->dims[0] = edge;
471 struct_slab->dims[1] = 1;
472 fields = (matvar_t **)struct_slab->data;
473 I = start * nfields;
474 for ( i = 0; i < edge; i++ ) {
475 if ( copy_fields ) {
476 for ( field = 0; field < nfields; field++ ) {
477 fields[i * nfields + field] =
478 Mat_VarDuplicate(*((matvar_t **)matvar->data + I), 1);
479 I++;
480 }
481 } else {
482 for ( field = 0; field < nfields; field++ ) {
483 fields[i * nfields + field] = *((matvar_t **)matvar->data + I);
484 I++;
485 }
486 }
487 I += (stride - 1) * nfields;
488 }
489 }
490 return struct_slab;
491 }
492
493 /** @brief Sets the structure field to the given variable
494 *
495 * Sets the structure field specified by the 0-relative field index
496 * @c field_index for the given 0-relative structure index @c index to
497 * @c field.
498 * @ingroup MAT
499 * @param matvar Pointer to the structure MAT variable
500 * @param field_index 0-relative index of the field.
501 * @param index linear index of the structure array
502 * @param field New field variable
503 * @return Pointer to the previous field (NULL if no previous field)
504 */
505 matvar_t *
Mat_VarSetStructFieldByIndex(matvar_t * matvar,size_t field_index,size_t index,matvar_t * field)506 Mat_VarSetStructFieldByIndex(matvar_t *matvar, size_t field_index, size_t index, matvar_t *field)
507 {
508 int err;
509 matvar_t *old_field = NULL;
510 size_t nelems = 1, nfields;
511
512 if ( matvar == NULL || matvar->class_type != MAT_C_STRUCT || matvar->data == NULL )
513 return NULL;
514
515 err = Mat_MulDims(matvar, &nelems);
516 if ( err )
517 return NULL;
518
519 nfields = matvar->internal->num_fields;
520
521 if ( index < nelems && field_index < nfields ) {
522 matvar_t **fields = (matvar_t **)matvar->data;
523 old_field = fields[index * nfields + field_index];
524 fields[index * nfields + field_index] = field;
525 if ( NULL != field->name ) {
526 free(field->name);
527 }
528 field->name = strdup(matvar->internal->fieldnames[field_index]);
529 }
530
531 return old_field;
532 }
533
534 /** @brief Sets the structure field to the given variable
535 *
536 * Sets the specified structure fieldname at the given 0-relative @c index to
537 * @c field.
538 * @ingroup MAT
539 * @param matvar Pointer to the Structure MAT variable
540 * @param field_name Name of the structure field
541 * @param index linear index of the structure array
542 * @param field New field variable
543 * @return Pointer to the previous field (NULL if no previous field)
544 */
545 matvar_t *
Mat_VarSetStructFieldByName(matvar_t * matvar,const char * field_name,size_t index,matvar_t * field)546 Mat_VarSetStructFieldByName(matvar_t *matvar, const char *field_name, size_t index, matvar_t *field)
547 {
548 int err, i, nfields, field_index;
549 matvar_t *old_field = NULL;
550 size_t nelems = 1;
551
552 if ( matvar == NULL || matvar->class_type != MAT_C_STRUCT || matvar->data == NULL )
553 return NULL;
554
555 err = Mat_MulDims(matvar, &nelems);
556 if ( err )
557 return NULL;
558
559 nfields = matvar->internal->num_fields;
560 field_index = -1;
561 for ( i = 0; i < nfields; i++ ) {
562 if ( !strcmp(matvar->internal->fieldnames[i], field_name) ) {
563 field_index = i;
564 break;
565 }
566 }
567
568 if ( index < nelems && field_index >= 0 ) {
569 matvar_t **fields = (matvar_t **)matvar->data;
570 old_field = fields[index * nfields + field_index];
571 fields[index * nfields + field_index] = field;
572 if ( NULL != field->name ) {
573 free(field->name);
574 }
575 field->name = strdup(matvar->internal->fieldnames[field_index]);
576 }
577
578 return old_field;
579 }
580