Skip to content

Instantly share code, notes, and snippets.

@tanoxyz
Created February 4, 2022 18:37
Show Gist options
  • Save tanoxyz/16eb1e3bf4c1730893788ba846605d0e to your computer and use it in GitHub Desktop.
Save tanoxyz/16eb1e3bf4c1730893788ba846605d0e to your computer and use it in GitHub Desktop.
Save matrices using matlab's file format from C
/* --------------------------------------------------------------------------------------
*
* 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