Created
February 4, 2022 18:37
-
-
Save tanoxyz/16eb1e3bf4c1730893788ba846605d0e to your computer and use it in GitHub Desktop.
Save matrices using matlab's file format from C
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* -------------------------------------------------------------------------------------- | |
* | |
* Example/library to save matrices to disk using matlab's file format and being able to | |
* load them from matlab. | |
* | |
* Only stores matrices of reals and double precision, but with minor changes could work | |
* with single precision or complex numbers. | |
* | |
* This was made following the matfile format documentation | |
* https://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf on revision "september 2021" | |
* | |
* ------------------------------------------------------------------------------------- */ | |
#include <stdio.h> | |
#include <stdint.h> | |
int | |
MATFILE_Write_header(FILE *file); | |
int | |
MATFILE_Write_matrix(FILE *file, double *mat, int32_t *dimensions, int32_t n_dims, const char *name); | |
/* | |
// EXAMPLE OF THE USAGE | |
int | |
main() { | |
double my_mat1[3*2] = { | |
1, 2, 3, | |
4, 5, 6 | |
}; | |
double my_mat2[2*3*4] = { | |
1, 2, | |
3, 4, | |
5, 6, | |
7, 8, | |
9, 10, | |
11, 12, | |
13, 14, | |
15, 16, | |
17, 18, | |
19, 20, | |
21, 22, | |
23, 24 | |
}; | |
FILE *out_file = NULL; | |
if (0 != fopen_s(&out_file, "my_mat.mat", "wb")) { | |
printf("Error opening file\n"); | |
return -1; | |
} | |
// Note that the header is mandatory | |
if (0 != MATFILE_Write_header(out_file)) { | |
printf("Error writting header\n"); | |
fclose(out_file); | |
return -1; | |
} | |
if (0 != MATFILE_Write_matrix(out_file, my_mat1, (int32_t[]){3, 2}, 2, "my_mat1")) { | |
printf("Error writting matrix\n"); | |
fclose(out_file); | |
return -1; | |
} | |
if (0 != MATFILE_Write_matrix(out_file, my_mat2, (int32_t[]){2, 3, 4}, 3, "my_mat2")) { | |
printf("Error writting matrix\n"); | |
fclose(out_file); | |
return -1; | |
} | |
fclose(out_file); | |
return 0; | |
} | |
*/ | |
typedef struct { | |
uint8_t descriptive_data[116]; | |
uint32_t subsys_data_offset_l; | |
uint32_t subsys_data_offset_h; | |
uint16_t version; | |
uint16_t endian_indicator; | |
} MATFILE_Header; | |
const MATFILE_Header MATFILE_header_default = { | |
.descriptive_data = "MATLAB 5.0 MAT-file", | |
.subsys_data_offset_l = 0, | |
.subsys_data_offset_h = 0, | |
.version = 0x100, | |
.endian_indicator = ('M'<<8)|'I' // Should put "IM" on little endian and "MI" on big endian | |
}; | |
typedef struct { | |
uint32_t data_type; | |
uint32_t number_of_bytes; | |
} MATFILE_DataTag; | |
typedef struct { | |
uint8_t class; | |
uint8_t flags; | |
uint16_t undefined0; | |
uint32_t undefined1; | |
} MATFILE_ArrayFlags; | |
enum { | |
MATFILE_DATA_TYPE_INT8 = 1, | |
MATFILE_DATA_TYPE_UINT8 = 2, | |
MATFILE_DATA_TYPE_INT16 = 3, | |
MATFILE_DATA_TYPE_UINT16 = 4, | |
MATFILE_DATA_TYPE_INT32 = 5, | |
MATFILE_DATA_TYPE_UINT32 = 6, | |
MATFILE_DATA_TYPE_SINGLE = 7, // IEEE 754 single format, (A float32) | |
// Reserved | |
MATFILE_DATA_TYPE_DOUBLE = 9, // IEEE 754 double format, (A float64) | |
// Reserved | |
// Reserved | |
MATFILE_DATA_TYPE_INT64 = 12, | |
MATFILE_DATA_TYPE_UINT64 = 13, | |
MATFILE_DATA_TYPE_MATRIX = 14, | |
MATFILE_DATA_TYPE_COMPRESSED = 15, | |
MATFILE_DATA_TYPE_UTF8 = 16, | |
MATFILE_DATA_TYPE_UTF16 = 17, | |
MATFILE_DATA_TYPE_UTF32 = 18, | |
}; | |
enum { | |
MATFILE_ARRAY_FLAG_COMPLEX = 0b00010000, | |
MATFILE_ARRAY_FLAG_GLOBAL = 0b00100000, | |
MATFILE_ARRAY_FLAG_LOGICAL = 0b01000000, | |
}; | |
enum { | |
MATFILE_ARRAY_CLASS_CELL = 1, | |
MATFILE_ARRAY_CLASS_STRUCTURE = 2, | |
MATFILE_ARRAY_CLASS_OBJECT = 3, | |
MATFILE_ARRAY_CLASS_CHARACTER = 4, | |
MATFILE_ARRAY_CLASS_SPARSE = 5, | |
MATFILE_ARRAY_CLASS_DOUBLE = 6, | |
MATFILE_ARRAY_CLASS_SINGLE = 7, | |
MATFILE_ARRAY_CLASS_INT8 = 8, | |
MATFILE_ARRAY_CLASS_UINT8 = 9, | |
MATFILE_ARRAY_CLASS_INT16 = 10, | |
MATFILE_ARRAY_CLASS_UINT16 = 11, | |
MATFILE_ARRAY_CLASS_INT32 = 12, | |
MATFILE_ARRAY_CLASS_UINT32 = 13, | |
MATFILE_ARRAY_CLASS_INT64 = 14, | |
MATFILE_ARRAY_CLASS_UINT64 = 15, | |
}; | |
int | |
MATFILE_Write_header(FILE *file) { | |
if (1 > fwrite(&MATFILE_header_default, sizeof(MATFILE_Header), 1, file)) return -1; | |
return 0; | |
} | |
int | |
MATFILE_Write_matrix(FILE *file, double *mat, int32_t *dimensions, int32_t n_dims, const char *name) { | |
int bytes_of_the_name = 0; | |
for (int i = 0; name[i] != 0; i+=1) bytes_of_the_name += 1; | |
int bytes_of_the_dimensions = sizeof(int32_t)*n_dims; | |
int bytes_of_the_matrix = sizeof(double); | |
for (int i = 0; i < n_dims; i+=1) bytes_of_the_matrix *= dimensions[i]; | |
#define PAD8(val) ((val+7)&(~7)) | |
#define GET_PAD(val) (((val+7)&(~7))-val) | |
{ // Header for the matrix | |
int whole_array_data_size = 0; | |
whole_array_data_size += PAD8(sizeof(MATFILE_DataTag)+sizeof(MATFILE_ArrayFlags)); // Flags | |
whole_array_data_size += PAD8(sizeof(MATFILE_DataTag)+bytes_of_the_dimensions); // Dimensions | |
whole_array_data_size += PAD8(sizeof(MATFILE_DataTag)+bytes_of_the_name); // Name | |
// Note that the whole size doesn't have into account the padding of the last element | |
whole_array_data_size += PAD8(sizeof(MATFILE_DataTag))+bytes_of_the_matrix; // Array data size | |
MATFILE_DataTag matrix_tag = { | |
.data_type = MATFILE_DATA_TYPE_MATRIX, | |
.number_of_bytes = whole_array_data_size, | |
}; | |
if (1 > fwrite(&matrix_tag, sizeof(MATFILE_DataTag), 1, file)) return -1; | |
} | |
{ // Array flags | |
MATFILE_DataTag tag = { | |
.data_type = MATFILE_DATA_TYPE_UINT32, | |
.number_of_bytes = 8, | |
}; | |
if (1 > fwrite(&tag, sizeof(MATFILE_DataTag), 1, file)) return -1; | |
MATFILE_ArrayFlags array_flags = {.class = MATFILE_ARRAY_CLASS_DOUBLE}; | |
if (1 > fwrite(&array_flags, sizeof(MATFILE_ArrayFlags), 1, file)) return -1; | |
} | |
{ // Dimensions of the array | |
MATFILE_DataTag tag = { | |
.data_type = MATFILE_DATA_TYPE_INT32, | |
.number_of_bytes = bytes_of_the_dimensions, | |
}; | |
if (1 > fwrite(&tag, sizeof(MATFILE_DataTag), 1, file)) return -1; | |
if (1 > fwrite(dimensions, bytes_of_the_dimensions, 1, file)) return -1; | |
uint32_t pad = GET_PAD(bytes_of_the_dimensions); | |
uint64_t zeros = 0; | |
if (pad) { | |
if (1 > fwrite(&zeros, pad, 1, file)) return -1; | |
} | |
} | |
{ // Array name | |
MATFILE_DataTag tag = { | |
.data_type = MATFILE_DATA_TYPE_INT8, | |
.number_of_bytes = bytes_of_the_name, | |
}; | |
if (1 > fwrite(&tag, sizeof(MATFILE_DataTag), 1, file)) return -1; | |
if (1 > fwrite(name, bytes_of_the_name, 1, file)) return -1; | |
uint32_t pad = GET_PAD(bytes_of_the_name); | |
uint64_t zeros = 0; | |
if (pad) { | |
if (pad > fwrite(&zeros, 1, pad, file)) return -1; | |
} | |
} | |
{ // Array data | |
MATFILE_DataTag tag = { | |
.data_type = MATFILE_DATA_TYPE_DOUBLE, | |
.number_of_bytes = bytes_of_the_matrix, | |
}; | |
if (1 > fwrite(&tag, sizeof(MATFILE_DataTag), 1, file)) return -1; | |
if (1 > fwrite(mat, bytes_of_the_matrix, 1, file)) return -1; | |
uint32_t pad = GET_PAD(bytes_of_the_matrix); | |
uint64_t zeros = 0; | |
if (pad) { | |
if (1 > fwrite(&zeros, pad, 1, file)) return -1; | |
} | |
} | |
#undef PAD8 | |
#undef GET_PAD | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment