From 761c7e38da4c7645ab82685d14344748e1f81df0 Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Mon, 18 Aug 2025 13:03:15 -0700 Subject: [PATCH 1/9] Replace `assert` with `bsp_error_t` for `bsp_array_t`. --- examples/simple_read.c | 2 +- examples/simple_write.c | 10 ++- include/binsparse/array.h | 81 ++++++++++--------- include/binsparse/binsparse.h | 1 + include/binsparse/convert_matrix.h | 57 ++++++++++--- include/binsparse/error.h | 62 ++++++++++++++ include/binsparse/generate.h | 24 +++++- include/binsparse/hdf5_wrapper.h | 16 +++- include/binsparse/matrix.h | 8 +- .../matrix_market/matrix_market_read.h | 61 +++++++++++--- include/binsparse/minimize_values.h | 30 ++++--- include/binsparse/tensor.h | 6 +- src/read_matrix.c | 10 ++- 13 files changed, 284 insertions(+), 84 deletions(-) create mode 100644 include/binsparse/error.h diff --git a/examples/simple_read.c b/examples/simple_read.c index a4ba9bb..3af4921 100644 --- a/examples/simple_read.c +++ b/examples/simple_read.c @@ -19,7 +19,7 @@ int main(int argc, char** argv) { printf("%lu: %d\n", i, values[i]); } - bsp_destroy_array_t(array); + bsp_destroy_array_t(&array); H5Fclose(f); return 0; diff --git a/examples/simple_write.c b/examples/simple_write.c index deb47d8..cdb5024 100644 --- a/examples/simple_write.c +++ b/examples/simple_write.c @@ -11,7 +11,13 @@ int main(int argc, char** argv) { hid_t f = H5Fcreate(file_name, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); - bsp_array_t array = bsp_construct_array_t(1000, BSP_INT32); + bsp_array_t array; + bsp_error_t error = bsp_construct_array_t(&array, 1000, BSP_INT32); + if (error != BSP_SUCCESS) { + printf("Error: Failed to allocate array\n"); + H5Fclose(f); + return 1; + } int* values = (int*) array.data; @@ -22,7 +28,7 @@ int main(int argc, char** argv) { bsp_write_array(f, "test", array, 0); H5Fclose(f); - bsp_destroy_array_t(array); + bsp_destroy_array_t(&array); return 0; } diff --git a/include/binsparse/array.h b/include/binsparse/array.h index 8ac69af..60b392e 100644 --- a/include/binsparse/array.h +++ b/include/binsparse/array.h @@ -6,9 +6,9 @@ #pragma once -#include #include #include +#include #include #include #include @@ -28,63 +28,70 @@ static inline bsp_array_t bsp_construct_default_array_t() { return array; } -static inline bsp_array_t bsp_construct_array_t(size_t size, bsp_type_t type) { +static inline bsp_error_t bsp_construct_array_t(bsp_array_t* array, size_t size, + bsp_type_t type) { size_t byte_size = size * bsp_type_size(type); - bsp_array_t array; - array.allocator = bsp_default_allocator; - array.data = array.allocator.malloc(byte_size); - assert(array.data != NULL); - array.size = size; - array.type = type; + array->allocator = bsp_default_allocator; + array->data = array->allocator.malloc(byte_size); - return array; + if (array->data == NULL) { + return BSP_ERROR_MEMORY; + } + + array->size = size; + array->type = type; + + return BSP_SUCCESS; } -static inline bsp_array_t bsp_copy_construct_array_t(bsp_array_t other) { - bsp_array_t array = bsp_construct_array_t(other.size, other.type); - memcpy(array.data, other.data, other.size * bsp_type_size(other.type)); +static inline bsp_error_t bsp_copy_construct_array_t(bsp_array_t* array, + bsp_array_t other) { + bsp_error_t error = bsp_construct_array_t(array, other.size, other.type); + if (error != BSP_SUCCESS) { + return error; + } + + memcpy(array->data, other.data, other.size * bsp_type_size(other.type)); - return array; + return BSP_SUCCESS; } -static inline bsp_array_t bsp_complex_array_to_fp(bsp_array_t other) { - assert(other.type == BSP_COMPLEX_FLOAT32 || - other.type == BSP_COMPLEX_FLOAT64); +static inline bsp_error_t bsp_complex_array_to_fp(bsp_array_t* array) { + if (array->type != BSP_COMPLEX_FLOAT32 && + array->type != BSP_COMPLEX_FLOAT64) { + return BSP_ERROR_TYPE; + } - bsp_array_t array; - array.data = other.data; - array.size = other.size * 2; - array.allocator = other.allocator; + array->size = array->size * 2; - if (other.type == BSP_COMPLEX_FLOAT32) { - array.type = BSP_FLOAT32; + if (array->type == BSP_COMPLEX_FLOAT32) { + array->type = BSP_FLOAT32; } else { - array.type = BSP_FLOAT64; + array->type = BSP_FLOAT64; } - return array; + return BSP_SUCCESS; } -static inline bsp_array_t bsp_fp_array_to_complex(bsp_array_t other) { - assert(other.type == BSP_FLOAT32 || other.type == BSP_FLOAT64); - - bsp_array_t array; - array.data = other.data; - array.size = other.size / 2; - array.allocator = other.allocator; +static inline bsp_error_t bsp_fp_array_to_complex(bsp_array_t* array) { + if (array->type != BSP_FLOAT32 && array->type != BSP_FLOAT64) { + return BSP_ERROR_TYPE; + } - if (other.type == BSP_FLOAT32) { - array.type = BSP_COMPLEX_FLOAT32; + if (array->type == BSP_FLOAT32) { + array->type = BSP_COMPLEX_FLOAT32; } else { - array.type = BSP_COMPLEX_FLOAT64; + array->type = BSP_COMPLEX_FLOAT64; } - return array; + array->size = array->size / 2; + + return BSP_SUCCESS; } -static inline void bsp_destroy_array_t(bsp_array_t array) { - array.allocator.free(array.data); +static inline void bsp_destroy_array_t(bsp_array_t* array) { + array->allocator.free(array->data); } static inline bool bsp_array_equal(bsp_array_t x, bsp_array_t y) { diff --git a/include/binsparse/binsparse.h b/include/binsparse/binsparse.h index bb6eecd..b77735a 100644 --- a/include/binsparse/binsparse.h +++ b/include/binsparse/binsparse.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/include/binsparse/convert_matrix.h b/include/binsparse/convert_matrix.h index 2f6c332..a8b8038 100644 --- a/include/binsparse/convert_matrix.h +++ b/include/binsparse/convert_matrix.h @@ -37,17 +37,30 @@ static inline bsp_matrix_t bsp_convert_matrix(bsp_matrix_t matrix, bsp_type_t index_type = bsp_pick_integer_type(max_dim); - result.values = bsp_copy_construct_array_t(matrix.values); + bsp_error_t error = + bsp_copy_construct_array_t(&result.values, matrix.values); + if (error != BSP_SUCCESS) { + return bsp_construct_default_matrix_t(); + } // There is a corner case with tall and skinny matrices where we need a // higher width for rowind. In order to keep rowind/colind the same type, // we might upcast. if (index_type == matrix.indices_1.type) { - result.indices_1 = bsp_copy_construct_array_t(matrix.indices_1); + error = bsp_copy_construct_array_t(&result.indices_1, matrix.indices_1); + if (error != BSP_SUCCESS) { + bsp_destroy_array_t(&result.values); + return bsp_construct_default_matrix_t(); + } } else { - result.indices_1 = - bsp_construct_array_t(matrix.indices_1.size, index_type); + error = bsp_construct_array_t(&result.indices_1, matrix.indices_1.size, + index_type); + if (error != BSP_SUCCESS) { + bsp_destroy_array_t(&result.values); + return bsp_construct_default_matrix_t(); + } + for (size_t i = 0; i < matrix.indices_1.size; i++) { size_t index; bsp_array_read(matrix.indices_1, i, index); @@ -55,7 +68,12 @@ static inline bsp_matrix_t bsp_convert_matrix(bsp_matrix_t matrix, } } - result.indices_0 = bsp_construct_array_t(matrix.nnz, index_type); + error = bsp_construct_array_t(&result.indices_0, matrix.nnz, index_type); + if (error != BSP_SUCCESS) { + bsp_destroy_array_t(&result.values); + bsp_destroy_array_t(&result.indices_1); + return bsp_construct_default_matrix_t(); + } for (size_t i = 0; i < matrix.nrows; i++) { size_t row_begin, row_end; @@ -109,12 +127,26 @@ static inline bsp_matrix_t bsp_convert_matrix(bsp_matrix_t matrix, // indices can be copied exactly. Values' type will not change, but // column indices might, thus the extra branch. - result.values = bsp_copy_construct_array_t(matrix.values); + bsp_error_t error = + bsp_copy_construct_array_t(&result.values, matrix.values); + if (error != BSP_SUCCESS) { + return bsp_construct_default_matrix_t(); + } if (index_type == matrix.indices_1.type) { - result.indices_1 = bsp_copy_construct_array_t(matrix.indices_1); + error = + bsp_copy_construct_array_t(&result.indices_1, matrix.indices_1); + if (error != BSP_SUCCESS) { + bsp_destroy_array_t(&result.values); + return bsp_construct_default_matrix_t(); + } } else { - result.indices_1 = bsp_construct_array_t(matrix.nnz, index_type); + error = + bsp_construct_array_t(&result.indices_1, matrix.nnz, index_type); + if (error != BSP_SUCCESS) { + bsp_destroy_array_t(&result.values); + return bsp_construct_default_matrix_t(); + } for (size_t i = 0; i < matrix.nnz; i++) { size_t index; @@ -123,8 +155,13 @@ static inline bsp_matrix_t bsp_convert_matrix(bsp_matrix_t matrix, } } - result.pointers_to_1 = - bsp_construct_array_t(matrix.nrows + 1, index_type); + error = bsp_construct_array_t(&result.pointers_to_1, matrix.nrows + 1, + index_type); + if (error != BSP_SUCCESS) { + bsp_destroy_array_t(&result.values); + bsp_destroy_array_t(&result.indices_1); + return bsp_construct_default_matrix_t(); + } bsp_array_t rowptr = result.pointers_to_1; diff --git a/include/binsparse/error.h b/include/binsparse/error.h new file mode 100644 index 0000000..2988c16 --- /dev/null +++ b/include/binsparse/error.h @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: 2024 Binsparse Developers + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#pragma once + +typedef enum bsp_error_t { + BSP_SUCCESS = 0, + + // Memory-related failures (malloc, array allocation, etc.) + BSP_ERROR_MEMORY = 1, + + // File I/O failures (file not found, read/write errors, HDF5 operations) + BSP_ERROR_IO = 2, + + // JSON parsing and metadata errors + BSP_ERROR_FORMAT = 3, + + // Invalid input parameters, dimensions, types, etc. + BSP_ERROR_INVALID_INPUT = 4, + + // Unsupported operations or not-yet-implemented features + BSP_ERROR_UNSUPPORTED = 5, + + // Type-related errors (invalid types, type mismatches) + BSP_ERROR_TYPE = 6, + + // Generic internal errors (should-never-happen cases) + BSP_ERROR_INTERNAL = 7 + +} bsp_error_t; + +/** + * Get a human-readable error message for a bsp_error_t code. + * + * @param error The error code + * @return A constant string describing the error + */ +static inline const char* bsp_get_error_string(bsp_error_t error) { + switch (error) { + case BSP_SUCCESS: + return "Success"; + case BSP_ERROR_MEMORY: + return "Memory allocation or management error"; + case BSP_ERROR_IO: + return "File I/O or HDF5 operation error"; + case BSP_ERROR_FORMAT: + return "JSON parsing or metadata format error"; + case BSP_ERROR_INVALID_INPUT: + return "Invalid input parameters or data"; + case BSP_ERROR_UNSUPPORTED: + return "Unsupported operation or feature"; + case BSP_ERROR_TYPE: + return "Data type error or type mismatch"; + case BSP_ERROR_INTERNAL: + return "Internal library error"; + default: + return "Unknown error"; + } +} diff --git a/include/binsparse/generate.h b/include/binsparse/generate.h index 93205a1..3b3043d 100644 --- a/include/binsparse/generate.h +++ b/include/binsparse/generate.h @@ -74,9 +74,27 @@ static inline bsp_matrix_t bsp_generate_coo(size_t m, size_t n, size_t nnz, matrix.nrows = m; matrix.ncols = n; matrix.nnz = nnz; - matrix.values = bsp_construct_array_t(nnz, value_type); - matrix.indices_0 = bsp_construct_array_t(nnz, index_type); - matrix.indices_1 = bsp_construct_array_t(nnz, index_type); + + bsp_error_t error; + error = bsp_construct_array_t(&matrix.values, nnz, value_type); + if (error != BSP_SUCCESS) { + // Return empty matrix on error + return matrix; + } + + error = bsp_construct_array_t(&matrix.indices_0, nnz, index_type); + if (error != BSP_SUCCESS) { + bsp_destroy_array_t(&matrix.values); + return matrix; + } + + error = bsp_construct_array_t(&matrix.indices_1, nnz, index_type); + if (error != BSP_SUCCESS) { + bsp_destroy_array_t(&matrix.values); + bsp_destroy_array_t(&matrix.indices_0); + return matrix; + } + matrix.format = BSP_COO; bsp_array_fill_random(matrix.values, 100); diff --git a/include/binsparse/hdf5_wrapper.h b/include/binsparse/hdf5_wrapper.h index 5aded28..c8f07ea 100644 --- a/include/binsparse/hdf5_wrapper.h +++ b/include/binsparse/hdf5_wrapper.h @@ -24,7 +24,10 @@ static inline int bsp_write_array(hid_t f, const char* label, bsp_array_t array, int compression_level) { if (array.type == BSP_COMPLEX_FLOAT32 || array.type == BSP_COMPLEX_FLOAT64) { - array = bsp_complex_array_to_fp(array); + bsp_error_t error = bsp_complex_array_to_fp(&array); + if (error != BSP_SUCCESS) { + return -3; // Type conversion error + } } hsize_t hsize[1]; @@ -198,12 +201,21 @@ static inline bsp_array_t bsp_read_array(hid_t f, const char* label) { bsp_type_t type = bsp_get_bsp_type(hdf5_type); - bsp_array_t array = bsp_construct_array_t(dims[0], type); + bsp_array_t array = bsp_construct_default_array_t(); + bsp_error_t error = bsp_construct_array_t(&array, dims[0], type); + if (error != BSP_SUCCESS) { + H5Dclose(dset); + H5Sclose(fspace); + return bsp_construct_default_array_t(); + } herr_t status = H5Dread(dset, bsp_get_hdf5_native_type(type), H5S_ALL, H5S_ALL, H5P_DEFAULT, array.data); if (status < 0) { + bsp_destroy_array_t(&array); + H5Dclose(dset); + H5Sclose(fspace); return bsp_construct_default_array_t(); } diff --git a/include/binsparse/matrix.h b/include/binsparse/matrix.h index db858c7..c4563f1 100644 --- a/include/binsparse/matrix.h +++ b/include/binsparse/matrix.h @@ -39,10 +39,10 @@ static inline bsp_matrix_t bsp_construct_default_matrix_t() { } static inline void bsp_destroy_matrix_t(bsp_matrix_t matrix) { - bsp_destroy_array_t(matrix.values); - bsp_destroy_array_t(matrix.indices_0); - bsp_destroy_array_t(matrix.indices_1); - bsp_destroy_array_t(matrix.pointers_to_1); + bsp_destroy_array_t(&matrix.values); + bsp_destroy_array_t(&matrix.indices_0); + bsp_destroy_array_t(&matrix.indices_1); + bsp_destroy_array_t(&matrix.pointers_to_1); } static inline size_t bsp_matrix_nbytes(bsp_matrix_t mat) { diff --git a/include/binsparse/matrix_market/matrix_market_read.h b/include/binsparse/matrix_market/matrix_market_read.h index c823a40..383acad 100644 --- a/include/binsparse/matrix_market/matrix_market_read.h +++ b/include/binsparse/matrix_market/matrix_market_read.h @@ -38,7 +38,11 @@ static inline bsp_matrix_t bsp_mmread_explicit_array(const char* file_path, matrix.ncols = metadata.ncols; matrix.nnz = matrix.nrows * matrix.ncols; - matrix.values = bsp_construct_array_t(matrix.nnz, value_type); + bsp_error_t error = + bsp_construct_array_t(&matrix.values, matrix.nnz, value_type); + if (error != BSP_SUCCESS) { + return matrix; // Return empty matrix on error + } matrix.format = BSP_DMAT; @@ -135,14 +139,33 @@ bsp_mmread_explicit_coordinate(const char* file_path, bsp_type_t value_type, matrix.ncols = metadata.ncols; matrix.nnz = metadata.nnz; - matrix.indices_0 = bsp_construct_array_t(matrix.nnz, index_type); - matrix.indices_1 = bsp_construct_array_t(matrix.nnz, index_type); + bsp_error_t error = + bsp_construct_array_t(&matrix.indices_0, matrix.nnz, index_type); + if (error != BSP_SUCCESS) { + return matrix; + } + + error = bsp_construct_array_t(&matrix.indices_1, matrix.nnz, index_type); + if (error != BSP_SUCCESS) { + bsp_destroy_array_t(&matrix.indices_0); + return matrix; + } if (mm_type == BSP_MM_PATTERN) { - matrix.values = bsp_construct_array_t(1, value_type); + error = bsp_construct_array_t(&matrix.values, 1, value_type); + if (error != BSP_SUCCESS) { + bsp_destroy_array_t(&matrix.indices_0); + bsp_destroy_array_t(&matrix.indices_1); + return matrix; + } bsp_array_write(matrix.values, 0, true); } else { - matrix.values = bsp_construct_array_t(matrix.nnz, value_type); + error = bsp_construct_array_t(&matrix.values, matrix.nnz, value_type); + if (error != BSP_SUCCESS) { + bsp_destroy_array_t(&matrix.indices_0); + bsp_destroy_array_t(&matrix.indices_1); + return matrix; + } } matrix.format = BSP_COO; @@ -235,13 +258,29 @@ bsp_mmread_explicit_coordinate(const char* file_path, bsp_type_t value_type, qsort(indices, matrix.nnz, sizeof(size_t), bsp_coo_comparison_row_sort_operator_impl_); - bsp_array_t rowind = bsp_copy_construct_array_t(matrix.indices_0); - bsp_array_t colind = bsp_copy_construct_array_t(matrix.indices_1); + bsp_array_t rowind; + bsp_array_t colind; + + error = bsp_copy_construct_array_t(&rowind, matrix.indices_0); + if (error != BSP_SUCCESS) { + return matrix; + } + + error = bsp_copy_construct_array_t(&colind, matrix.indices_1); + if (error != BSP_SUCCESS) { + bsp_destroy_array_t(&rowind); + return matrix; + } bsp_array_t values; if (!matrix.is_iso) { - values = bsp_copy_construct_array_t(matrix.values); + error = bsp_copy_construct_array_t(&values, matrix.values); + if (error != BSP_SUCCESS) { + bsp_destroy_array_t(&rowind); + bsp_destroy_array_t(&colind); + return matrix; + } } for (size_t i = 0; i < matrix.nnz; i++) { @@ -252,13 +291,13 @@ bsp_mmread_explicit_coordinate(const char* file_path, bsp_type_t value_type, } } - bsp_destroy_array_t(matrix.indices_0); - bsp_destroy_array_t(matrix.indices_1); + bsp_destroy_array_t(&matrix.indices_0); + bsp_destroy_array_t(&matrix.indices_1); matrix.indices_0 = rowind; matrix.indices_1 = colind; if (!matrix.is_iso) { - bsp_destroy_array_t(matrix.values); + bsp_destroy_array_t(&matrix.values); matrix.values = values; } diff --git a/include/binsparse/minimize_values.h b/include/binsparse/minimize_values.h index 4969000..7ef18f8 100644 --- a/include/binsparse/minimize_values.h +++ b/include/binsparse/minimize_values.h @@ -24,8 +24,12 @@ static inline bsp_matrix_t bsp_matrix_minimize_values(bsp_matrix_t matrix) { } if (float32_representable) { - bsp_array_t new_values = - bsp_construct_array_t(matrix.values.size, BSP_FLOAT32); + bsp_array_t new_values; + bsp_error_t error = + bsp_construct_array_t(&new_values, matrix.values.size, BSP_FLOAT32); + if (error != BSP_SUCCESS) { + return matrix; // Return original matrix on error + } float* n_values = (float*) new_values.data; @@ -33,7 +37,7 @@ static inline bsp_matrix_t bsp_matrix_minimize_values(bsp_matrix_t matrix) { n_values[i] = values[i]; } - bsp_destroy_array_t(matrix.values); + bsp_destroy_array_t(&matrix.values); matrix.values = new_values; } } else if (matrix.values.type == BSP_INT64) { @@ -78,8 +82,12 @@ static inline bsp_matrix_t bsp_matrix_minimize_values(bsp_matrix_t matrix) { value_type = BSP_INT64; } } - bsp_array_t new_values = - bsp_construct_array_t(matrix.values.size, value_type); + bsp_array_t new_values; + bsp_error_t error = + bsp_construct_array_t(&new_values, matrix.values.size, value_type); + if (error != BSP_SUCCESS) { + return matrix; // Return original matrix on error + } for (size_t i = 0; i < matrix.values.size; i++) { int64_t value; @@ -87,7 +95,7 @@ static inline bsp_matrix_t bsp_matrix_minimize_values(bsp_matrix_t matrix) { bsp_array_write(new_values, i, value); } - bsp_destroy_array_t(matrix.values); + bsp_destroy_array_t(&matrix.values); matrix.values = new_values; } else if (matrix.values.type == BSP_COMPLEX_FLOAT64) { bool float32_representable = true; @@ -101,8 +109,12 @@ static inline bsp_matrix_t bsp_matrix_minimize_values(bsp_matrix_t matrix) { } if (float32_representable) { - bsp_array_t new_values = - bsp_construct_array_t(matrix.values.size, BSP_COMPLEX_FLOAT32); + bsp_array_t new_values; + bsp_error_t error = bsp_construct_array_t(&new_values, matrix.values.size, + BSP_COMPLEX_FLOAT32); + if (error != BSP_SUCCESS) { + return matrix; // Return original matrix on error + } float _Complex* n_values = (float _Complex*) new_values.data; @@ -110,7 +122,7 @@ static inline bsp_matrix_t bsp_matrix_minimize_values(bsp_matrix_t matrix) { n_values[i] = values[i]; } - bsp_destroy_array_t(matrix.values); + bsp_destroy_array_t(&matrix.values); matrix.values = new_values; } } diff --git a/include/binsparse/tensor.h b/include/binsparse/tensor.h index 4d63512..31d6b89 100644 --- a/include/binsparse/tensor.h +++ b/include/binsparse/tensor.h @@ -74,7 +74,7 @@ static void bsp_destroy_level_t(bsp_level_t* level) { switch (level->kind) { case BSP_TENSOR_ELEMENT: { bsp_element_t* element = (bsp_element_t*) level->data; - bsp_destroy_array_t(element->values); + bsp_destroy_array_t(&element->values); free(element); break; } @@ -88,10 +88,10 @@ static void bsp_destroy_level_t(bsp_level_t* level) { bsp_sparse_t* sparse = (bsp_sparse_t*) level->data; if (sparse->pointers_to != NULL) - bsp_destroy_array_t(*sparse->pointers_to); + bsp_destroy_array_t(sparse->pointers_to); if (sparse->indices != NULL) { for (int i = 0; i < sparse->rank; i++) { - bsp_destroy_array_t(sparse->indices[i]); + bsp_destroy_array_t(&sparse->indices[i]); } } bsp_destroy_level_t(sparse->child); diff --git a/src/read_matrix.c b/src/read_matrix.c index 464210c..e3f233e 100644 --- a/src/read_matrix.c +++ b/src/read_matrix.c @@ -83,7 +83,10 @@ bsp_matrix_t bsp_read_matrix_from_group_parallel(hid_t f, int num_threads) { } if (strlen(type_string) >= 8 && strncmp(type_string, "complex[", 8) == 0) { - matrix.values = bsp_fp_array_to_complex(matrix.values); + bsp_error_t error = bsp_fp_array_to_complex(&matrix.values); + if (error != BSP_SUCCESS) { + // Handle error - for now just continue with original array + } } } @@ -188,7 +191,10 @@ bsp_matrix_t bsp_read_matrix_from_group(hid_t f) { } if (strlen(type_string) >= 8 && strncmp(type_string, "complex[", 8) == 0) { - matrix.values = bsp_fp_array_to_complex(matrix.values); + bsp_error_t error = bsp_fp_array_to_complex(&matrix.values); + if (error != BSP_SUCCESS) { + // Handle error - for now just continue with original array + } } } From d1801b96a21def08733631815137996f2c5b57a6 Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Mon, 18 Aug 2025 20:51:10 +0000 Subject: [PATCH 2/9] Add error handling to `bsp_matrix_t` functions. --- examples/benchmark_read.c | 4 +- examples/benchmark_read_parallel.c | 4 +- examples/bsp2mtx.c | 2 +- examples/check_equivalence.c | 4 +- examples/check_equivalence_parallel.c | 4 +- examples/mtx2bsp.c | 4 +- include/binsparse/array.h | 11 +++-- include/binsparse/convert_matrix.h | 40 ++++++++++++++----- include/binsparse/generate.h | 6 +-- include/binsparse/hdf5_wrapper.h | 37 ++++++++++++----- include/binsparse/matrix.h | 29 +++++++------- .../matrix_market/matrix_market_read.h | 6 ++- src/read_matrix.c | 6 ++- 13 files changed, 98 insertions(+), 59 deletions(-) diff --git a/examples/benchmark_read.c b/examples/benchmark_read.c index c1ef342..9d72d89 100644 --- a/examples/benchmark_read.c +++ b/examples/benchmark_read.c @@ -80,7 +80,7 @@ int main(int argc, char** argv) { // If running warm cache experiments, read once to warm cache. if (!cold_cache) { bsp_matrix_t mat = bsp_read_matrix(file_name, NULL); - bsp_destroy_matrix_t(mat); + bsp_destroy_matrix_t(&mat); } for (size_t i = 0; i < num_trials; i++) { @@ -92,7 +92,7 @@ int main(int argc, char** argv) { double end = gettime(); durations[i] = end - begin; nbytes = bsp_matrix_nbytes(mat); - bsp_destroy_matrix_t(mat); + bsp_destroy_matrix_t(&mat); double gbytes = ((double) nbytes) / 1024 / 1024 / 1024; double gbytes_s = gbytes / durations[i]; diff --git a/examples/benchmark_read_parallel.c b/examples/benchmark_read_parallel.c index b485938..c83ece9 100644 --- a/examples/benchmark_read_parallel.c +++ b/examples/benchmark_read_parallel.c @@ -83,7 +83,7 @@ int main(int argc, char** argv) { // If running warm cache experiments, read once to warm cache. if (!cold_cache) { bsp_matrix_t mat = bsp_read_matrix_parallel(file_name, NULL, num_threads); - bsp_destroy_matrix_t(mat); + bsp_destroy_matrix_t(&mat); } for (size_t i = 0; i < num_trials; i++) { @@ -97,7 +97,7 @@ int main(int argc, char** argv) { durations[i] = end - begin; nbytes = bsp_matrix_nbytes(mat); - bsp_destroy_matrix_t(mat); + bsp_destroy_matrix_t(&mat); double gbytes = ((double) nbytes) / 1024 / 1024 / 1024; double gbytes_s = gbytes / durations[i]; diff --git a/examples/bsp2mtx.c b/examples/bsp2mtx.c index 6e3f483..ac2cbb4 100644 --- a/examples/bsp2mtx.c +++ b/examples/bsp2mtx.c @@ -29,7 +29,7 @@ int main(int argc, char** argv) { bsp_mmwrite(output_fname, matrix); printf(" === Done writing. ===\n"); - bsp_destroy_matrix_t(matrix); + bsp_destroy_matrix_t(&matrix); return 0; } diff --git a/examples/check_equivalence.c b/examples/check_equivalence.c index 0acea42..c15558d 100644 --- a/examples/check_equivalence.c +++ b/examples/check_equivalence.c @@ -128,13 +128,13 @@ int main(int argc, char** argv) { if (matrix1.format != matrix2.format) { if (matrix1.format != BSP_COOR) { bsp_matrix_t intermediate = bsp_convert_matrix(matrix1, BSP_COOR); - bsp_destroy_matrix_t(matrix1); + bsp_destroy_matrix_t(&matrix1); matrix1 = intermediate; } if (matrix2.format != BSP_COOR) { bsp_matrix_t intermediate = bsp_convert_matrix(matrix2, BSP_COOR); - bsp_destroy_matrix_t(matrix2); + bsp_destroy_matrix_t(&matrix2); matrix2 = intermediate; } } diff --git a/examples/check_equivalence_parallel.c b/examples/check_equivalence_parallel.c index b773cf8..399c4c4 100644 --- a/examples/check_equivalence_parallel.c +++ b/examples/check_equivalence_parallel.c @@ -136,13 +136,13 @@ int main(int argc, char** argv) { if (matrix1.format != matrix2.format) { if (matrix1.format != BSP_COOR) { bsp_matrix_t intermediate = bsp_convert_matrix(matrix1, BSP_COOR); - bsp_destroy_matrix_t(matrix1); + bsp_destroy_matrix_t(&matrix1); matrix1 = intermediate; } if (matrix2.format != BSP_COOR) { bsp_matrix_t intermediate = bsp_convert_matrix(matrix2, BSP_COOR); - bsp_destroy_matrix_t(matrix2); + bsp_destroy_matrix_t(&matrix2); matrix2 = intermediate; } } diff --git a/examples/mtx2bsp.c b/examples/mtx2bsp.c index 07db200..621a17d 100644 --- a/examples/mtx2bsp.c +++ b/examples/mtx2bsp.c @@ -151,7 +151,7 @@ int main(int argc, char** argv) { if (format != BSP_COOR) { begin = gettime(); bsp_matrix_t converted_matrix = bsp_convert_matrix(matrix, format); - bsp_destroy_matrix_t(matrix); + bsp_destroy_matrix_t(&matrix); matrix = converted_matrix; end = gettime(); duration = end - begin; @@ -170,7 +170,7 @@ int main(int argc, char** argv) { printf("%lf seconds writing Binsparse file...\n", duration); printf(" === Done writing. ===\n"); - bsp_destroy_matrix_t(matrix); + bsp_destroy_matrix_t(&matrix); return 0; } diff --git a/include/binsparse/array.h b/include/binsparse/array.h index 60b392e..8c1be72 100644 --- a/include/binsparse/array.h +++ b/include/binsparse/array.h @@ -20,12 +20,11 @@ typedef struct bsp_array_t { bsp_allocator_t allocator; } bsp_array_t; -static inline bsp_array_t bsp_construct_default_array_t() { - bsp_array_t array; - array.data = NULL; - array.size = 0; - array.allocator = bsp_default_allocator; - return array; +static inline bsp_error_t bsp_construct_default_array_t(bsp_array_t* array) { + array->data = NULL; + array->size = 0; + array->allocator = bsp_default_allocator; + return BSP_SUCCESS; } static inline bsp_error_t bsp_construct_array_t(bsp_array_t* array, size_t size, diff --git a/include/binsparse/convert_matrix.h b/include/binsparse/convert_matrix.h index a8b8038..93c96ef 100644 --- a/include/binsparse/convert_matrix.h +++ b/include/binsparse/convert_matrix.h @@ -20,7 +20,8 @@ static inline bsp_matrix_t bsp_convert_matrix(bsp_matrix_t matrix, // *Convert to COO* from another format. if (matrix.format == BSP_CSR) { // Convert CSR -> COOR - bsp_matrix_t result = bsp_construct_default_matrix_t(); + bsp_matrix_t result; + bsp_construct_default_matrix_t(&result); result.format = BSP_COOR; @@ -40,7 +41,9 @@ static inline bsp_matrix_t bsp_convert_matrix(bsp_matrix_t matrix, bsp_error_t error = bsp_copy_construct_array_t(&result.values, matrix.values); if (error != BSP_SUCCESS) { - return bsp_construct_default_matrix_t(); + bsp_matrix_t empty_result; + bsp_construct_default_matrix_t(&empty_result); + return empty_result; } // There is a corner case with tall and skinny matrices where we need a @@ -51,14 +54,18 @@ static inline bsp_matrix_t bsp_convert_matrix(bsp_matrix_t matrix, error = bsp_copy_construct_array_t(&result.indices_1, matrix.indices_1); if (error != BSP_SUCCESS) { bsp_destroy_array_t(&result.values); - return bsp_construct_default_matrix_t(); + bsp_matrix_t empty_result; + bsp_construct_default_matrix_t(&empty_result); + return empty_result; } } else { error = bsp_construct_array_t(&result.indices_1, matrix.indices_1.size, index_type); if (error != BSP_SUCCESS) { bsp_destroy_array_t(&result.values); - return bsp_construct_default_matrix_t(); + bsp_matrix_t empty_result; + bsp_construct_default_matrix_t(&empty_result); + return empty_result; } for (size_t i = 0; i < matrix.indices_1.size; i++) { @@ -72,7 +79,9 @@ static inline bsp_matrix_t bsp_convert_matrix(bsp_matrix_t matrix, if (error != BSP_SUCCESS) { bsp_destroy_array_t(&result.values); bsp_destroy_array_t(&result.indices_1); - return bsp_construct_default_matrix_t(); + bsp_matrix_t empty_result; + bsp_construct_default_matrix_t(&empty_result); + return empty_result; } for (size_t i = 0; i < matrix.nrows; i++) { @@ -95,13 +104,14 @@ static inline bsp_matrix_t bsp_convert_matrix(bsp_matrix_t matrix, if (matrix.format != BSP_COOR) { bsp_matrix_t intermediate = bsp_convert_matrix(matrix, BSP_COOR); bsp_matrix_t result = bsp_convert_matrix(intermediate, format); - bsp_destroy_matrix_t(intermediate); + bsp_destroy_matrix_t(&intermediate); return result; } else { if (format == BSP_CSR) { // Convert COOR -> CSR - bsp_matrix_t result = bsp_construct_default_matrix_t(); + bsp_matrix_t result; + bsp_construct_default_matrix_t(&result); result.format = BSP_CSR; @@ -130,7 +140,9 @@ static inline bsp_matrix_t bsp_convert_matrix(bsp_matrix_t matrix, bsp_error_t error = bsp_copy_construct_array_t(&result.values, matrix.values); if (error != BSP_SUCCESS) { - return bsp_construct_default_matrix_t(); + bsp_matrix_t empty_result; + bsp_construct_default_matrix_t(&empty_result); + return empty_result; } if (index_type == matrix.indices_1.type) { @@ -138,14 +150,18 @@ static inline bsp_matrix_t bsp_convert_matrix(bsp_matrix_t matrix, bsp_copy_construct_array_t(&result.indices_1, matrix.indices_1); if (error != BSP_SUCCESS) { bsp_destroy_array_t(&result.values); - return bsp_construct_default_matrix_t(); + bsp_matrix_t empty_result; + bsp_construct_default_matrix_t(&empty_result); + return empty_result; } } else { error = bsp_construct_array_t(&result.indices_1, matrix.nnz, index_type); if (error != BSP_SUCCESS) { bsp_destroy_array_t(&result.values); - return bsp_construct_default_matrix_t(); + bsp_matrix_t empty_result; + bsp_construct_default_matrix_t(&empty_result); + return empty_result; } for (size_t i = 0; i < matrix.nnz; i++) { @@ -160,7 +176,9 @@ static inline bsp_matrix_t bsp_convert_matrix(bsp_matrix_t matrix, if (error != BSP_SUCCESS) { bsp_destroy_array_t(&result.values); bsp_destroy_array_t(&result.indices_1); - return bsp_construct_default_matrix_t(); + bsp_matrix_t empty_result; + bsp_construct_default_matrix_t(&empty_result); + return empty_result; } bsp_array_t rowptr = result.pointers_to_1; diff --git a/include/binsparse/generate.h b/include/binsparse/generate.h index 3b3043d..b625669 100644 --- a/include/binsparse/generate.h +++ b/include/binsparse/generate.h @@ -70,13 +70,13 @@ static inline void bsp_array_fill_random(bsp_array_t array, size_t bound) { static inline bsp_matrix_t bsp_generate_coo(size_t m, size_t n, size_t nnz, bsp_type_t value_type, bsp_type_t index_type) { - bsp_matrix_t matrix = bsp_construct_default_matrix_t(); + bsp_matrix_t matrix; + bsp_construct_default_matrix_t(&matrix); matrix.nrows = m; matrix.ncols = n; matrix.nnz = nnz; - bsp_error_t error; - error = bsp_construct_array_t(&matrix.values, nnz, value_type); + bsp_error_t error = bsp_construct_array_t(&matrix.values, nnz, value_type); if (error != BSP_SUCCESS) { // Return empty matrix on error return matrix; diff --git a/include/binsparse/hdf5_wrapper.h b/include/binsparse/hdf5_wrapper.h index c8f07ea..eb2bda9 100644 --- a/include/binsparse/hdf5_wrapper.h +++ b/include/binsparse/hdf5_wrapper.h @@ -84,13 +84,17 @@ static inline bsp_array_t bsp_read_array_parallel(hid_t f, const char* label, hid_t dset = H5Dopen2(f, label, H5P_DEFAULT); if (dset == H5I_INVALID_HID) { - return bsp_construct_default_array_t(); + bsp_array_t empty_array; + bsp_construct_default_array_t(&empty_array); + return empty_array; } hid_t fspace = H5Dget_space(dset); if (fspace == H5I_INVALID_HID) { - return bsp_construct_default_array_t(); + bsp_array_t empty_array; + bsp_construct_default_array_t(&empty_array); + return empty_array; } hsize_t dims[3]; @@ -98,7 +102,9 @@ static inline bsp_array_t bsp_read_array_parallel(hid_t f, const char* label, int r = H5Sget_simple_extent_dims(fspace, dims, NULL); if (r < 0) { - return bsp_construct_default_array_t(); + bsp_array_t empty_array; + bsp_construct_default_array_t(&empty_array); + return empty_array; } hid_t hdf5_type = H5Dget_type(dset); @@ -180,13 +186,17 @@ static inline bsp_array_t bsp_read_array(hid_t f, const char* label) { hid_t dset = H5Dopen2(f, label, H5P_DEFAULT); if (dset == H5I_INVALID_HID) { - return bsp_construct_default_array_t(); + bsp_array_t empty_array; + bsp_construct_default_array_t(&empty_array); + return empty_array; } hid_t fspace = H5Dget_space(dset); if (fspace == H5I_INVALID_HID) { - return bsp_construct_default_array_t(); + bsp_array_t empty_array; + bsp_construct_default_array_t(&empty_array); + return empty_array; } hsize_t dims[3]; @@ -194,19 +204,26 @@ static inline bsp_array_t bsp_read_array(hid_t f, const char* label) { int r = H5Sget_simple_extent_dims(fspace, dims, NULL); if (r < 0) { - return bsp_construct_default_array_t(); + bsp_array_t empty_array; + bsp_construct_default_array_t(&empty_array); + return empty_array; } hid_t hdf5_type = H5Dget_type(dset); bsp_type_t type = bsp_get_bsp_type(hdf5_type); - bsp_array_t array = bsp_construct_default_array_t(); + bsp_array_t array; + bsp_construct_default_array_t(&array); + bsp_error_t error = bsp_construct_array_t(&array, dims[0], type); if (error != BSP_SUCCESS) { + bsp_destroy_array_t(&array); H5Dclose(dset); H5Sclose(fspace); - return bsp_construct_default_array_t(); + bsp_array_t empty_array; + bsp_construct_default_array_t(&empty_array); + return empty_array; } herr_t status = H5Dread(dset, bsp_get_hdf5_native_type(type), H5S_ALL, @@ -216,7 +233,9 @@ static inline bsp_array_t bsp_read_array(hid_t f, const char* label) { bsp_destroy_array_t(&array); H5Dclose(dset); H5Sclose(fspace); - return bsp_construct_default_array_t(); + bsp_array_t empty_array; + bsp_construct_default_array_t(&empty_array); + return empty_array; } H5Dclose(dset); diff --git a/include/binsparse/matrix.h b/include/binsparse/matrix.h index c4563f1..04ad858 100644 --- a/include/binsparse/matrix.h +++ b/include/binsparse/matrix.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include #include @@ -26,23 +27,21 @@ typedef struct bsp_matrix_t { bsp_structure_t structure; } bsp_matrix_t; -static inline bsp_matrix_t bsp_construct_default_matrix_t() { - bsp_matrix_t mat; - mat.values = bsp_construct_default_array_t(); - mat.indices_0 = bsp_construct_default_array_t(); - mat.indices_1 = bsp_construct_default_array_t(); - mat.pointers_to_1 = bsp_construct_default_array_t(); - mat.nrows = mat.ncols = mat.nnz = 0; - mat.is_iso = false; - mat.structure = BSP_GENERAL; - return mat; +static inline void bsp_construct_default_matrix_t(bsp_matrix_t* matrix) { + bsp_construct_default_array_t(&matrix->values); + bsp_construct_default_array_t(&matrix->indices_0); + bsp_construct_default_array_t(&matrix->indices_1); + bsp_construct_default_array_t(&matrix->pointers_to_1); + matrix->nrows = matrix->ncols = matrix->nnz = 0; + matrix->is_iso = false; + matrix->structure = BSP_GENERAL; } -static inline void bsp_destroy_matrix_t(bsp_matrix_t matrix) { - bsp_destroy_array_t(&matrix.values); - bsp_destroy_array_t(&matrix.indices_0); - bsp_destroy_array_t(&matrix.indices_1); - bsp_destroy_array_t(&matrix.pointers_to_1); +static inline void bsp_destroy_matrix_t(bsp_matrix_t* matrix) { + bsp_destroy_array_t(&matrix->values); + bsp_destroy_array_t(&matrix->indices_0); + bsp_destroy_array_t(&matrix->indices_1); + bsp_destroy_array_t(&matrix->pointers_to_1); } static inline size_t bsp_matrix_nbytes(bsp_matrix_t mat) { diff --git a/include/binsparse/matrix_market/matrix_market_read.h b/include/binsparse/matrix_market/matrix_market_read.h index 383acad..4db0947 100644 --- a/include/binsparse/matrix_market/matrix_market_read.h +++ b/include/binsparse/matrix_market/matrix_market_read.h @@ -32,7 +32,8 @@ static inline bsp_matrix_t bsp_mmread_explicit_array(const char* file_path, assert(false); } - bsp_matrix_t matrix = bsp_construct_default_matrix_t(); + bsp_matrix_t matrix; + bsp_construct_default_matrix_t(&matrix); matrix.nrows = metadata.nrows; matrix.ncols = metadata.ncols; @@ -129,7 +130,8 @@ bsp_mmread_explicit_coordinate(const char* file_path, bsp_type_t value_type, assert(false); } - bsp_matrix_t matrix = bsp_construct_default_matrix_t(); + bsp_matrix_t matrix; + bsp_construct_default_matrix_t(&matrix); if (mm_type == BSP_MM_PATTERN) { matrix.is_iso = true; diff --git a/src/read_matrix.c b/src/read_matrix.c index e3f233e..18e4d6a 100644 --- a/src/read_matrix.c +++ b/src/read_matrix.c @@ -12,7 +12,8 @@ #if __STDC_VERSION__ >= 201112L bsp_matrix_t bsp_read_matrix_from_group_parallel(hid_t f, int num_threads) { - bsp_matrix_t matrix = bsp_construct_default_matrix_t(); + bsp_matrix_t matrix; + bsp_construct_default_matrix_t(&matrix); char* json_string = bsp_read_attribute(f, (char*) "binsparse"); @@ -120,7 +121,8 @@ bsp_matrix_t bsp_read_matrix_from_group_parallel(hid_t f, int num_threads) { #endif bsp_matrix_t bsp_read_matrix_from_group(hid_t f) { - bsp_matrix_t matrix = bsp_construct_default_matrix_t(); + bsp_matrix_t matrix; + bsp_construct_default_matrix_t(&matrix); char* json_string = bsp_read_attribute(f, (char*) "binsparse"); From 8405a288e37c10d03c9e100f3a0404cbafd1b479 Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Mon, 18 Aug 2025 14:30:22 -0700 Subject: [PATCH 3/9] Update `bsp_matrix_t` to have proper error handling, update parsing functions. --- examples/bsp-ls.c | 8 +- examples/simple_read.c | 8 +- examples/simple_write.c | 7 +- include/binsparse/hdf5_wrapper.h | 142 +++++++++++++++++-------------- include/binsparse/write_matrix.h | 9 +- src/read_matrix.c | 79 ++++++++++++++--- src/read_tensor.c | 27 +++++- src/write_matrix.c | 66 ++++++++------ src/write_tensor.c | 50 ++++++----- 9 files changed, 258 insertions(+), 138 deletions(-) diff --git a/examples/bsp-ls.c b/examples/bsp-ls.c index 8ecdd94..9207224 100644 --- a/examples/bsp-ls.c +++ b/examples/bsp-ls.c @@ -18,7 +18,13 @@ void print_group_info(hid_t g, const char* name) { H5E_END_TRY; if (bsp_json != H5I_INVALID_HID) { - char* json_string = bsp_read_attribute(g, "binsparse"); + char* json_string; + bsp_error_t error = bsp_read_attribute(&json_string, g, "binsparse"); + if (error != BSP_SUCCESS) { + printf("Error reading binsparse attribute: %s\n", + bsp_get_error_string(error)); + return; + } cJSON* j = cJSON_Parse(json_string); diff --git a/examples/simple_read.c b/examples/simple_read.c index 3af4921..0edfa08 100644 --- a/examples/simple_read.c +++ b/examples/simple_read.c @@ -11,7 +11,13 @@ int main(int argc, char** argv) { hid_t f = H5Fopen(file_name, H5F_ACC_RDWR, H5P_DEFAULT); - bsp_array_t array = bsp_read_array(f, "test"); + bsp_array_t array; + bsp_error_t error = bsp_read_array(&array, f, "test"); + if (error != BSP_SUCCESS) { + printf("Error reading array: %s\n", bsp_get_error_string(error)); + H5Fclose(f); + return 1; + } int* values = (int*) array.data; diff --git a/examples/simple_write.c b/examples/simple_write.c index cdb5024..261f228 100644 --- a/examples/simple_write.c +++ b/examples/simple_write.c @@ -12,12 +12,7 @@ int main(int argc, char** argv) { hid_t f = H5Fcreate(file_name, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); bsp_array_t array; - bsp_error_t error = bsp_construct_array_t(&array, 1000, BSP_INT32); - if (error != BSP_SUCCESS) { - printf("Error: Failed to allocate array\n"); - H5Fclose(f); - return 1; - } + bsp_construct_array_t(&array, 1000, BSP_INT32); int* values = (int*) array.data; diff --git a/include/binsparse/hdf5_wrapper.h b/include/binsparse/hdf5_wrapper.h index eb2bda9..c82a695 100644 --- a/include/binsparse/hdf5_wrapper.h +++ b/include/binsparse/hdf5_wrapper.h @@ -21,12 +21,13 @@ // Write an array to a dataset / file // Returns 0 on success, nonzero on error. -static inline int bsp_write_array(hid_t f, const char* label, bsp_array_t array, - int compression_level) { +static inline bsp_error_t bsp_write_array(hid_t f, const char* label, + bsp_array_t array, + int compression_level) { if (array.type == BSP_COMPLEX_FLOAT32 || array.type == BSP_COMPLEX_FLOAT64) { bsp_error_t error = bsp_complex_array_to_fp(&array); if (error != BSP_SUCCESS) { - return -3; // Type conversion error + return BSP_ERROR_TYPE; } } @@ -59,7 +60,10 @@ static inline int bsp_write_array(hid_t f, const char* label, bsp_array_t array, H5Dcreate2(f, label, hdf5_standard_type, fspace, lcpl, dcpl, H5P_DEFAULT); if (dset == H5I_INVALID_HID) { - return -1; + H5Sclose(fspace); + H5Pclose(lcpl); + H5Pclose(dcpl); + return BSP_ERROR_IO; } hid_t hdf5_native_type = bsp_get_hdf5_native_type(array.type); @@ -67,34 +71,41 @@ static inline int bsp_write_array(hid_t f, const char* label, bsp_array_t array, hid_t r = H5Dwrite(dset, hdf5_native_type, H5S_ALL, fspace, H5P_DEFAULT, array.data); - if (r == H5I_INVALID_HID) { - return -2; + if (r < 0) { + H5Dclose(dset); + H5Sclose(fspace); + H5Pclose(lcpl); + H5Pclose(dcpl); + return BSP_ERROR_IO; } H5Sclose(fspace); + H5Dclose(dset); H5Pclose(lcpl); H5Pclose(dcpl); - return 0; + return BSP_SUCCESS; } #if __STDC_VERSION__ >= 201112L -static inline bsp_array_t bsp_read_array_parallel(hid_t f, const char* label, +static inline bsp_error_t bsp_read_array_parallel(bsp_array_t* array, hid_t f, + const char* label, int num_threads) { hid_t dset = H5Dopen2(f, label, H5P_DEFAULT); if (dset == H5I_INVALID_HID) { - bsp_array_t empty_array; - bsp_construct_default_array_t(&empty_array); - return empty_array; + H5Dclose(dset); + bsp_construct_default_array_t(array); + return BSP_ERROR_IO; } hid_t fspace = H5Dget_space(dset); if (fspace == H5I_INVALID_HID) { - bsp_array_t empty_array; - bsp_construct_default_array_t(&empty_array); - return empty_array; + H5Sclose(fspace); + H5Dclose(dset); + bsp_construct_default_array_t(array); + return BSP_ERROR_IO; } hsize_t dims[3]; @@ -102,9 +113,10 @@ static inline bsp_array_t bsp_read_array_parallel(hid_t f, const char* label, int r = H5Sget_simple_extent_dims(fspace, dims, NULL); if (r < 0) { - bsp_array_t empty_array; - bsp_construct_default_array_t(&empty_array); - return empty_array; + H5Sclose(fspace); + H5Dclose(dset); + bsp_construct_default_array_t(array); + return BSP_ERROR_IO; } hid_t hdf5_type = H5Dget_type(dset); @@ -113,10 +125,9 @@ static inline bsp_array_t bsp_read_array_parallel(hid_t f, const char* label, // Array will be written into a POSIX shared memory. bsp_shm_t array_shm = bsp_shm_new(dims[0] * bsp_type_size(type)); - bsp_array_t array; - array.type = type; - array.size = dims[0]; - array.allocator = bsp_shm_allocator; + array->type = type; + array->size = dims[0]; + array->allocator = bsp_shm_allocator; bsp_shm_t active_children_shm = bsp_shm_new(sizeof(_Atomic int)); @@ -140,17 +151,17 @@ static inline bsp_array_t bsp_read_array_parallel(hid_t f, const char* label, } } - array.data = bsp_shm_attach(array_shm); + array->data = bsp_shm_attach(array_shm); if (thread_num == 0) { bsp_shm_delete(array_shm); } - hsize_t chunk_size = (array.size + num_threads - 1) / num_threads; - hsize_t start = (chunk_size * thread_num < array.size) + hsize_t chunk_size = (array->size + num_threads - 1) / num_threads; + hsize_t start = (chunk_size * thread_num < array->size) ? chunk_size * thread_num - : array.size; + : array->size; hsize_t count = - (start + chunk_size <= array.size) ? chunk_size : array.size - start; + (start + chunk_size <= array->size) ? chunk_size : array->size - start; if (count > 0) { H5Sselect_hyperslab(fspace, H5S_SELECT_SET, &start, NULL, &count, NULL); @@ -158,17 +169,17 @@ static inline bsp_array_t bsp_read_array_parallel(hid_t f, const char* label, hid_t memspace_id = H5Screate_simple(1, &count, NULL); H5Dread(dset, bsp_get_hdf5_native_type(type), memspace_id, fspace, - H5P_DEFAULT, ((char*) array.data) + start * bsp_type_size(type)); + H5P_DEFAULT, ((char*) array->data) + start * bsp_type_size(type)); H5Sclose(memspace_id); } - H5Dclose(dset); H5Sclose(fspace); + H5Dclose(dset); if (thread_num > 0) { atomic_fetch_add_explicit(active_children, -1, memory_order_relaxed); bsp_shm_detach(active_children); - bsp_shm_detach(array.data); + bsp_shm_detach(array->data); exit(0); } @@ -178,25 +189,26 @@ static inline bsp_array_t bsp_read_array_parallel(hid_t f, const char* label, } bsp_shm_detach(active_children); - return array; + return BSP_SUCCESS; } #endif -static inline bsp_array_t bsp_read_array(hid_t f, const char* label) { +static inline bsp_error_t bsp_read_array(bsp_array_t* array, hid_t f, + const char* label) { hid_t dset = H5Dopen2(f, label, H5P_DEFAULT); if (dset == H5I_INVALID_HID) { - bsp_array_t empty_array; - bsp_construct_default_array_t(&empty_array); - return empty_array; + bsp_construct_default_array_t(array); + return BSP_ERROR_IO; } hid_t fspace = H5Dget_space(dset); if (fspace == H5I_INVALID_HID) { - bsp_array_t empty_array; - bsp_construct_default_array_t(&empty_array); - return empty_array; + H5Sclose(fspace); + H5Dclose(dset); + bsp_construct_default_array_t(array); + return BSP_ERROR_IO; } hsize_t dims[3]; @@ -204,47 +216,42 @@ static inline bsp_array_t bsp_read_array(hid_t f, const char* label) { int r = H5Sget_simple_extent_dims(fspace, dims, NULL); if (r < 0) { - bsp_array_t empty_array; - bsp_construct_default_array_t(&empty_array); - return empty_array; + H5Dclose(dset); + H5Sclose(fspace); + bsp_construct_default_array_t(array); + return BSP_ERROR_IO; } hid_t hdf5_type = H5Dget_type(dset); bsp_type_t type = bsp_get_bsp_type(hdf5_type); - bsp_array_t array; - bsp_construct_default_array_t(&array); - - bsp_error_t error = bsp_construct_array_t(&array, dims[0], type); + bsp_error_t error = bsp_construct_array_t(array, dims[0], type); if (error != BSP_SUCCESS) { - bsp_destroy_array_t(&array); H5Dclose(dset); H5Sclose(fspace); - bsp_array_t empty_array; - bsp_construct_default_array_t(&empty_array); - return empty_array; + bsp_construct_default_array_t(array); + return BSP_ERROR_MEMORY; } herr_t status = H5Dread(dset, bsp_get_hdf5_native_type(type), H5S_ALL, - H5S_ALL, H5P_DEFAULT, array.data); + H5S_ALL, H5P_DEFAULT, array->data); if (status < 0) { - bsp_destroy_array_t(&array); - H5Dclose(dset); + bsp_destroy_array_t(array); H5Sclose(fspace); - bsp_array_t empty_array; - bsp_construct_default_array_t(&empty_array); - return empty_array; + H5Dclose(dset); + bsp_construct_default_array_t(array); + return BSP_ERROR_IO; } - H5Dclose(dset); H5Sclose(fspace); - return array; + H5Dclose(dset); + return BSP_SUCCESS; } -static inline void bsp_write_attribute(hid_t f, const char* label, - const char* string) { +static inline bsp_error_t bsp_write_attribute(hid_t f, const char* label, + const char* string) { hid_t strtype = H5Tcopy(H5T_C_S1); H5Tset_size(strtype, strlen(string)); H5Tset_cset(strtype, H5T_CSET_UTF8); @@ -258,23 +265,28 @@ static inline void bsp_write_attribute(hid_t f, const char* label, H5Tclose(strtype); H5Aclose(attribute); H5Sclose(dataspace); + + return BSP_SUCCESS; } -static inline char* bsp_read_attribute(hid_t f, const char* label) { +static inline bsp_error_t bsp_read_attribute(char** string, hid_t f, + const char* label) { hid_t attribute = H5Aopen(f, label, H5P_DEFAULT); - hid_t strtype = H5Aget_type(attribute); - hid_t type_class = H5Tget_class(strtype); - assert(type_class == H5T_STRING); + if (attribute == H5I_INVALID_HID) { + return BSP_ERROR_FORMAT; + } + + hid_t strtype = H5Aget_type(attribute); size_t size = H5Tget_size(strtype); - char* string = (char*) malloc(size + 1); + *string = (char*) malloc(size + 1); - H5Aread(attribute, strtype, string); + H5Aread(attribute, strtype, *string); H5Aclose(attribute); H5Tclose(strtype); - return string; + return BSP_SUCCESS; } diff --git a/include/binsparse/write_matrix.h b/include/binsparse/write_matrix.h index 53b89f5..2b067d2 100644 --- a/include/binsparse/write_matrix.h +++ b/include/binsparse/write_matrix.h @@ -18,12 +18,13 @@ extern "C" { #ifdef BSP_USE_HDF5 #include -int bsp_write_matrix_to_group(hid_t f, bsp_matrix_t matrix, cJSON* user_json, - int compression_level); +bsp_error_t bsp_write_matrix_to_group(hid_t f, bsp_matrix_t matrix, + cJSON* user_json, int compression_level); #endif -int bsp_write_matrix(const char* fname, bsp_matrix_t matrix, const char* group, - cJSON* user_json, int compression_level); +bsp_error_t bsp_write_matrix(const char* fname, bsp_matrix_t matrix, + const char* group, cJSON* user_json, + int compression_level); #ifdef __cplusplus } diff --git a/src/read_matrix.c b/src/read_matrix.c index 18e4d6a..bc9a75a 100644 --- a/src/read_matrix.c +++ b/src/read_matrix.c @@ -15,7 +15,11 @@ bsp_matrix_t bsp_read_matrix_from_group_parallel(hid_t f, int num_threads) { bsp_matrix_t matrix; bsp_construct_default_matrix_t(&matrix); - char* json_string = bsp_read_attribute(f, (char*) "binsparse"); + char* json_string; + bsp_error_t error = bsp_read_attribute(&json_string, f, (char*) "binsparse"); + if (error != BSP_SUCCESS) { + return matrix; + } cJSON* j = cJSON_Parse(json_string); @@ -73,7 +77,12 @@ bsp_matrix_t bsp_read_matrix_from_group_parallel(hid_t f, int num_threads) { assert(data_types_ != NULL); if (cJSON_HasObjectItem(data_types_, "values")) { - matrix.values = bsp_read_array_parallel(f, (char*) "values", num_threads); + error = bsp_read_array_parallel(&matrix.values, f, (char*) "values", + num_threads); + if (error != BSP_SUCCESS) { + free(json_string); + return matrix; + } cJSON* value_type = cJSON_GetObjectItemCaseSensitive(data_types_, "values"); char* type_string = cJSON_GetStringValue(value_type); @@ -92,18 +101,36 @@ bsp_matrix_t bsp_read_matrix_from_group_parallel(hid_t f, int num_threads) { } if (cJSON_HasObjectItem(data_types_, "indices_0")) { - matrix.indices_0 = - bsp_read_array_parallel(f, (char*) "indices_0", num_threads); + error = bsp_read_array_parallel(&matrix.indices_0, f, (char*) "indices_0", + num_threads); + if (error != BSP_SUCCESS) { + free(json_string); + bsp_destroy_array_t(&matrix.values); + return matrix; + } } if (cJSON_HasObjectItem(data_types_, "indices_1")) { - matrix.indices_1 = - bsp_read_array_parallel(f, (char*) "indices_1", num_threads); + error = bsp_read_array_parallel(&matrix.indices_1, f, (char*) "indices_1", + num_threads); + if (error != BSP_SUCCESS) { + free(json_string); + bsp_destroy_array_t(&matrix.values); + bsp_destroy_array_t(&matrix.indices_0); + return matrix; + } } if (cJSON_HasObjectItem(data_types_, "pointers_to_1")) { - matrix.pointers_to_1 = - bsp_read_array_parallel(f, (char*) "pointers_to_1", num_threads); + error = bsp_read_array_parallel(&matrix.pointers_to_1, f, + (char*) "pointers_to_1", num_threads); + if (error != BSP_SUCCESS) { + free(json_string); + bsp_destroy_array_t(&matrix.values); + bsp_destroy_array_t(&matrix.indices_0); + bsp_destroy_array_t(&matrix.indices_1); + return matrix; + } } if (cJSON_HasObjectItem(binsparse, "structure")) { @@ -124,7 +151,11 @@ bsp_matrix_t bsp_read_matrix_from_group(hid_t f) { bsp_matrix_t matrix; bsp_construct_default_matrix_t(&matrix); - char* json_string = bsp_read_attribute(f, (char*) "binsparse"); + char* json_string; + bsp_error_t error = bsp_read_attribute(&json_string, f, (char*) "binsparse"); + if (error != BSP_SUCCESS) { + return matrix; + } cJSON* j = cJSON_Parse(json_string); @@ -182,7 +213,11 @@ bsp_matrix_t bsp_read_matrix_from_group(hid_t f) { assert(data_types_ != NULL); if (cJSON_HasObjectItem(data_types_, "values")) { - matrix.values = bsp_read_array(f, (char*) "values"); + error = bsp_read_array(&matrix.values, f, (char*) "values"); + if (error != BSP_SUCCESS) { + free(json_string); + return matrix; + } cJSON* value_type = cJSON_GetObjectItemCaseSensitive(data_types_, "values"); char* type_string = cJSON_GetStringValue(value_type); @@ -201,15 +236,33 @@ bsp_matrix_t bsp_read_matrix_from_group(hid_t f) { } if (cJSON_HasObjectItem(data_types_, "indices_0")) { - matrix.indices_0 = bsp_read_array(f, (char*) "indices_0"); + error = bsp_read_array(&matrix.indices_0, f, (char*) "indices_0"); + if (error != BSP_SUCCESS) { + free(json_string); + bsp_destroy_array_t(&matrix.values); + return matrix; + } } if (cJSON_HasObjectItem(data_types_, "indices_1")) { - matrix.indices_1 = bsp_read_array(f, (char*) "indices_1"); + error = bsp_read_array(&matrix.indices_1, f, (char*) "indices_1"); + if (error != BSP_SUCCESS) { + free(json_string); + bsp_destroy_array_t(&matrix.values); + bsp_destroy_array_t(&matrix.indices_0); + return matrix; + } } if (cJSON_HasObjectItem(data_types_, "pointers_to_1")) { - matrix.pointers_to_1 = bsp_read_array(f, (char*) "pointers_to_1"); + error = bsp_read_array(&matrix.pointers_to_1, f, (char*) "pointers_to_1"); + if (error != BSP_SUCCESS) { + free(json_string); + bsp_destroy_array_t(&matrix.values); + bsp_destroy_array_t(&matrix.indices_0); + bsp_destroy_array_t(&matrix.indices_1); + return matrix; + } } if (cJSON_HasObjectItem(binsparse, "structure")) { diff --git a/src/read_tensor.c b/src/read_tensor.c index 01453ac..cce6004 100644 --- a/src/read_tensor.c +++ b/src/read_tensor.c @@ -32,7 +32,11 @@ char* key_with_index(const char* key, size_t index) { bsp_tensor_t bsp_read_tensor_from_group(hid_t f) { bsp_tensor_t tensor = bsp_construct_default_tensor_t(); - char* json_string = bsp_read_attribute(f, (char*) "binsparse"); + char* json_string; + bsp_error_t error = bsp_read_attribute(&json_string, f, (char*) "binsparse"); + if (error != BSP_SUCCESS) { + return tensor; + } cJSON* j = cJSON_Parse(json_string); @@ -103,7 +107,12 @@ bsp_tensor_t bsp_read_tensor_from_group(hid_t f) { // base case: working with an element. if (strcmp(type, "element") == 0) { - bsp_array_t values = bsp_read_array(f, (char*) "values"); + bsp_array_t values; + bsp_error_t error = bsp_read_array(&values, f, (char*) "values"); + if (error != BSP_SUCCESS) { + free(json_string); + return tensor; + } cur_level->kind = BSP_TENSOR_ELEMENT; bsp_element_t* data = malloc(sizeof(bsp_element_t)); data->values = values; @@ -136,7 +145,12 @@ bsp_tensor_t bsp_read_tensor_from_group(hid_t f) { if (depth != 0) { char* pointers_key = key_with_index("pointers_to_", depth); data->pointers_to = malloc(sizeof(bsp_array_t)); - *data->pointers_to = bsp_read_array(f, pointers_key); + error = bsp_read_array(data->pointers_to, f, pointers_key); + if (error != BSP_SUCCESS) { + free(pointers_key); + free(json_string); + return tensor; + } free(pointers_key); } else { data->pointers_to = NULL; @@ -146,7 +160,12 @@ bsp_tensor_t bsp_read_tensor_from_group(hid_t f) { data->indices = malloc(rank * sizeof(bsp_array_t)); for (int idx = 0; idx < rank; idx++) { char* indices_key = key_with_index("indices_", depth + idx); - data->indices[idx] = bsp_read_array(f, indices_key); + error = bsp_read_array(&data->indices[idx], f, indices_key); + if (error != BSP_SUCCESS) { + free(indices_key); + free(json_string); + return tensor; + } free(indices_key); } diff --git a/src/write_matrix.c b/src/write_matrix.c index 7c012b0..0b202bb 100644 --- a/src/write_matrix.c +++ b/src/write_matrix.c @@ -86,51 +86,61 @@ char* bsp_generate_json(bsp_matrix_t matrix, cJSON* user_json) { return string; } -int bsp_write_matrix_to_group(hid_t f, bsp_matrix_t matrix, cJSON* user_json, - int compression_level) { - int result = +bsp_error_t bsp_write_matrix_to_group(hid_t f, bsp_matrix_t matrix, + cJSON* user_json, int compression_level) { + bsp_error_t error = bsp_write_array(f, (char*) "values", matrix.values, compression_level); - - if (result != 0) - return result; + if (error != BSP_SUCCESS) { + return error; + } if (matrix.indices_0.size > 0) { - result = bsp_write_array(f, (char*) "indices_0", matrix.indices_0, - compression_level); - if (result != 0) { - return result; + error = bsp_write_array(f, (char*) "indices_0", matrix.indices_0, + compression_level); + if (error != BSP_SUCCESS) { + return error; } } if (matrix.indices_1.size > 0) { - result = bsp_write_array(f, (char*) "indices_1", matrix.indices_1, - compression_level); - if (result != 0) { - return result; + error = bsp_write_array(f, (char*) "indices_1", matrix.indices_1, + compression_level); + if (error != BSP_SUCCESS) { + return error; } } if (matrix.pointers_to_1.size > 0) { - result = bsp_write_array(f, (char*) "pointers_to_1", matrix.pointers_to_1, - compression_level); - if (result != 0) { - return result; + error = bsp_write_array(f, (char*) "pointers_to_1", matrix.pointers_to_1, + compression_level); + if (error != BSP_SUCCESS) { + return error; } } char* json_string = bsp_generate_json(matrix, user_json); - bsp_write_attribute(f, (char*) "binsparse", json_string); + error = bsp_write_attribute(f, (char*) "binsparse", json_string); + if (error != BSP_SUCCESS) { + free(json_string); + return error; + } free(json_string); - return 0; + return BSP_SUCCESS; } -int bsp_write_matrix(const char* fname, bsp_matrix_t matrix, const char* group, - cJSON* user_json, int compression_level) { +bsp_error_t bsp_write_matrix(const char* fname, bsp_matrix_t matrix, + const char* group, cJSON* user_json, + int compression_level) { if (group == NULL) { hid_t f = H5Fcreate(fname, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); - bsp_write_matrix_to_group(f, matrix, user_json, compression_level); + bsp_error_t error = + bsp_write_matrix_to_group(f, matrix, user_json, compression_level); + if (error != BSP_SUCCESS) { + H5Fclose(f); + return error; + } H5Fclose(f); } else { hid_t f; @@ -140,9 +150,15 @@ int bsp_write_matrix(const char* fname, bsp_matrix_t matrix, const char* group, f = H5Fcreate(fname, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); } hid_t g = H5Gcreate1(f, group, H5P_DEFAULT); - bsp_write_matrix_to_group(g, matrix, user_json, compression_level); + bsp_error_t error = + bsp_write_matrix_to_group(g, matrix, user_json, compression_level); + if (error != BSP_SUCCESS) { + H5Gclose(g); + H5Fclose(f); + return error; + } H5Gclose(g); H5Fclose(f); } - return 0; + return BSP_SUCCESS; } diff --git a/src/write_tensor.c b/src/write_tensor.c index 11a84ef..f212aeb 100644 --- a/src/write_tensor.c +++ b/src/write_tensor.c @@ -53,8 +53,8 @@ static cJSON* init_tensor_json(bsp_tensor_t tensor, cJSON* user_json) { return j; } -int bsp_write_tensor_to_group(hid_t f, bsp_tensor_t tensor, cJSON* user_json, - int compression_level) { +bsp_error_t bsp_write_tensor_to_group(hid_t f, bsp_tensor_t tensor, + cJSON* user_json, int compression_level) { // bsp_matrix_t matrix; cJSON* j = init_tensor_json(tensor, user_json); // tensor: @@ -85,10 +85,11 @@ int bsp_write_tensor_to_group(hid_t f, bsp_tensor_t tensor, cJSON* user_json, } // attempt to write an array. - int result = bsp_write_array(f, (char*) "values", values, compression_level); - if (result != 0) { + bsp_error_t error = + bsp_write_array(f, (char*) "values", values, compression_level); + if (error != BSP_SUCCESS) { cJSON_Delete(j); - return result; + return error; } int rank = 0; @@ -107,11 +108,11 @@ int bsp_write_tensor_to_group(hid_t f, bsp_tensor_t tensor, cJSON* user_json, cJSON_AddStringToObject(data_types, key_with_index("pointers_to_", rank), bsp_get_type_string(sparse->pointers_to->type)); - result = bsp_write_array(f, key_with_index("pointers_to_", rank), - *sparse->pointers_to, compression_level); - if (result != 0) { + error = bsp_write_array(f, key_with_index("pointers_to_", rank), + *sparse->pointers_to, compression_level); + if (error != BSP_SUCCESS) { cJSON_Delete(j); - return result; + return error; } } @@ -119,11 +120,11 @@ int bsp_write_tensor_to_group(hid_t f, bsp_tensor_t tensor, cJSON* user_json, cJSON_AddStringToObject(data_types, key_with_index("indices_", rank + i), bsp_get_type_string(sparse->indices[i].type)); - result = bsp_write_array(f, key_with_index("indices_", rank + i), - sparse->indices[i], compression_level); - if (result != 0) { + error = bsp_write_array(f, key_with_index("indices_", rank + i), + sparse->indices[i], compression_level); + if (error != BSP_SUCCESS) { cJSON_Delete(j); - return result; + return error; } } @@ -153,17 +154,28 @@ int bsp_write_tensor_to_group(hid_t f, bsp_tensor_t tensor, cJSON* user_json, } char* json_string = cJSON_Print(j); - bsp_write_attribute(f, (char*) "binsparse", json_string); + error = bsp_write_attribute(f, (char*) "binsparse", json_string); + if (error != BSP_SUCCESS) { + free(json_string); + cJSON_Delete(j); + return error; + } free(json_string); - return 0; + return BSP_SUCCESS; } -int bsp_write_tensor(const char* fname, bsp_tensor_t tensor, const char* group, - cJSON* user_json, int compression_level) { +bsp_error_t bsp_write_tensor(const char* fname, bsp_tensor_t tensor, + const char* group, cJSON* user_json, + int compression_level) { if (group == NULL) { hid_t f = H5Fcreate(fname, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); - bsp_write_tensor_to_group(f, tensor, user_json, compression_level); + bsp_error_t error = + bsp_write_tensor_to_group(f, tensor, user_json, compression_level); + if (error != BSP_SUCCESS) { + H5Fclose(f); + return error; + } H5Fclose(f); } else { hid_t f; @@ -177,5 +189,5 @@ int bsp_write_tensor(const char* fname, bsp_tensor_t tensor, const char* group, H5Gclose(g); H5Fclose(f); } - return 0; + return BSP_SUCCESS; } From 13a54719c3f52cf9bee8527fd9394cea0d63627e Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Mon, 18 Aug 2025 21:02:46 -0700 Subject: [PATCH 4/9] Add option to use a specific allocator. --- include/binsparse/array.h | 23 +++++++++++++++----- include/binsparse/hdf5_wrapper.h | 35 +++++++++++++++++++++---------- include/binsparse/matrix.h | 14 +++++++++---- include/binsparse/read_matrix.h | 5 +++++ src/read_matrix.c | 36 +++++++++++++++++++++++--------- 5 files changed, 83 insertions(+), 30 deletions(-) diff --git a/include/binsparse/array.h b/include/binsparse/array.h index 8c1be72..b3b7db2 100644 --- a/include/binsparse/array.h +++ b/include/binsparse/array.h @@ -20,18 +20,25 @@ typedef struct bsp_array_t { bsp_allocator_t allocator; } bsp_array_t; -static inline bsp_error_t bsp_construct_default_array_t(bsp_array_t* array) { +static inline bsp_error_t +bsp_construct_default_array_t_allocator(bsp_array_t* array, + bsp_allocator_t allocator) { array->data = NULL; array->size = 0; - array->allocator = bsp_default_allocator; + array->allocator = allocator; return BSP_SUCCESS; } -static inline bsp_error_t bsp_construct_array_t(bsp_array_t* array, size_t size, - bsp_type_t type) { +static inline bsp_error_t bsp_construct_default_array_t(bsp_array_t* array) { + return bsp_construct_default_array_t_allocator(array, bsp_default_allocator); +} + +static inline bsp_error_t +bsp_construct_array_t_allocator(bsp_array_t* array, size_t size, + bsp_type_t type, bsp_allocator_t allocator) { size_t byte_size = size * bsp_type_size(type); - array->allocator = bsp_default_allocator; + array->allocator = allocator; array->data = array->allocator.malloc(byte_size); if (array->data == NULL) { @@ -44,6 +51,12 @@ static inline bsp_error_t bsp_construct_array_t(bsp_array_t* array, size_t size, return BSP_SUCCESS; } +static inline bsp_error_t bsp_construct_array_t(bsp_array_t* array, size_t size, + bsp_type_t type) { + return bsp_construct_array_t_allocator(array, size, type, + bsp_default_allocator); +} + static inline bsp_error_t bsp_copy_construct_array_t(bsp_array_t* array, bsp_array_t other) { bsp_error_t error = bsp_construct_array_t(array, other.size, other.type); diff --git a/include/binsparse/hdf5_wrapper.h b/include/binsparse/hdf5_wrapper.h index c82a695..3c871dd 100644 --- a/include/binsparse/hdf5_wrapper.h +++ b/include/binsparse/hdf5_wrapper.h @@ -193,12 +193,13 @@ static inline bsp_error_t bsp_read_array_parallel(bsp_array_t* array, hid_t f, } #endif -static inline bsp_error_t bsp_read_array(bsp_array_t* array, hid_t f, - const char* label) { +static inline bsp_error_t bsp_read_array_allocator(bsp_array_t* array, hid_t f, + const char* label, + bsp_allocator_t allocator) { hid_t dset = H5Dopen2(f, label, H5P_DEFAULT); if (dset == H5I_INVALID_HID) { - bsp_construct_default_array_t(array); + bsp_construct_default_array_t_allocator(array, allocator); return BSP_ERROR_IO; } @@ -207,7 +208,7 @@ static inline bsp_error_t bsp_read_array(bsp_array_t* array, hid_t f, if (fspace == H5I_INVALID_HID) { H5Sclose(fspace); H5Dclose(dset); - bsp_construct_default_array_t(array); + bsp_construct_default_array_t_allocator(array, allocator); return BSP_ERROR_IO; } @@ -218,7 +219,7 @@ static inline bsp_error_t bsp_read_array(bsp_array_t* array, hid_t f, if (r < 0) { H5Dclose(dset); H5Sclose(fspace); - bsp_construct_default_array_t(array); + bsp_construct_default_array_t_allocator(array, allocator); return BSP_ERROR_IO; } @@ -226,11 +227,12 @@ static inline bsp_error_t bsp_read_array(bsp_array_t* array, hid_t f, bsp_type_t type = bsp_get_bsp_type(hdf5_type); - bsp_error_t error = bsp_construct_array_t(array, dims[0], type); + bsp_error_t error = + bsp_construct_array_t_allocator(array, dims[0], type, allocator); if (error != BSP_SUCCESS) { H5Dclose(dset); H5Sclose(fspace); - bsp_construct_default_array_t(array); + bsp_construct_default_array_t_allocator(array, allocator); return BSP_ERROR_MEMORY; } @@ -241,7 +243,7 @@ static inline bsp_error_t bsp_read_array(bsp_array_t* array, hid_t f, bsp_destroy_array_t(array); H5Sclose(fspace); H5Dclose(dset); - bsp_construct_default_array_t(array); + bsp_construct_default_array_t_allocator(array, allocator); return BSP_ERROR_IO; } @@ -250,6 +252,11 @@ static inline bsp_error_t bsp_read_array(bsp_array_t* array, hid_t f, return BSP_SUCCESS; } +static inline bsp_error_t bsp_read_array(bsp_array_t* array, hid_t f, + const char* label) { + return bsp_read_array_allocator(array, f, label, bsp_default_allocator); +} + static inline bsp_error_t bsp_write_attribute(hid_t f, const char* label, const char* string) { hid_t strtype = H5Tcopy(H5T_C_S1); @@ -269,8 +276,9 @@ static inline bsp_error_t bsp_write_attribute(hid_t f, const char* label, return BSP_SUCCESS; } -static inline bsp_error_t bsp_read_attribute(char** string, hid_t f, - const char* label) { +static inline bsp_error_t +bsp_read_attribute_allocator(char** string, hid_t f, const char* label, + bsp_allocator_t allocator) { hid_t attribute = H5Aopen(f, label, H5P_DEFAULT); if (attribute == H5I_INVALID_HID) { @@ -281,7 +289,7 @@ static inline bsp_error_t bsp_read_attribute(char** string, hid_t f, size_t size = H5Tget_size(strtype); - *string = (char*) malloc(size + 1); + *string = (char*) allocator.malloc(size + 1); H5Aread(attribute, strtype, *string); @@ -290,3 +298,8 @@ static inline bsp_error_t bsp_read_attribute(char** string, hid_t f, return BSP_SUCCESS; } + +static inline bsp_error_t bsp_read_attribute(char** string, hid_t f, + const char* label) { + return bsp_read_attribute_allocator(string, f, label, bsp_default_allocator); +} diff --git a/include/binsparse/matrix.h b/include/binsparse/matrix.h index 04ad858..bedb3ed 100644 --- a/include/binsparse/matrix.h +++ b/include/binsparse/matrix.h @@ -27,16 +27,22 @@ typedef struct bsp_matrix_t { bsp_structure_t structure; } bsp_matrix_t; -static inline void bsp_construct_default_matrix_t(bsp_matrix_t* matrix) { +static inline void +bsp_construct_default_matrix_t_allocator(bsp_matrix_t* matrix, + bsp_allocator_t allocator) { bsp_construct_default_array_t(&matrix->values); - bsp_construct_default_array_t(&matrix->indices_0); - bsp_construct_default_array_t(&matrix->indices_1); - bsp_construct_default_array_t(&matrix->pointers_to_1); + bsp_construct_default_array_t_allocator(&matrix->indices_0, allocator); + bsp_construct_default_array_t_allocator(&matrix->indices_1, allocator); + bsp_construct_default_array_t_allocator(&matrix->pointers_to_1, allocator); matrix->nrows = matrix->ncols = matrix->nnz = 0; matrix->is_iso = false; matrix->structure = BSP_GENERAL; } +static inline void bsp_construct_default_matrix_t(bsp_matrix_t* matrix) { + bsp_construct_default_matrix_t_allocator(matrix, bsp_default_allocator); +} + static inline void bsp_destroy_matrix_t(bsp_matrix_t* matrix) { bsp_destroy_array_t(&matrix->values); bsp_destroy_array_t(&matrix->indices_0); diff --git a/include/binsparse/read_matrix.h b/include/binsparse/read_matrix.h index 948ffba..699d9a8 100644 --- a/include/binsparse/read_matrix.h +++ b/include/binsparse/read_matrix.h @@ -11,6 +11,7 @@ extern "C" { #endif #ifdef BSP_USE_HDF5 +#include #include #if __STDC_VERSION__ >= 201112L @@ -18,6 +19,8 @@ bsp_matrix_t bsp_read_matrix_from_group_parallel(hid_t f, int num_threads); #endif bsp_matrix_t bsp_read_matrix_from_group(hid_t f); +bsp_matrix_t bsp_read_matrix_from_group_allocator(hid_t f, + bsp_allocator_t allocator); #endif #if __STDC_VERSION__ >= 201112L @@ -26,6 +29,8 @@ bsp_matrix_t bsp_read_matrix_parallel(const char* file_name, const char* group, #endif bsp_matrix_t bsp_read_matrix(const char* file_name, const char* group); +bsp_matrix_t bsp_read_matrix_allocator(const char* file_name, const char* group, + bsp_allocator_t allocator); #ifdef __cplusplus } diff --git a/src/read_matrix.c b/src/read_matrix.c index bc9a75a..9f9ced9 100644 --- a/src/read_matrix.c +++ b/src/read_matrix.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -147,12 +148,14 @@ bsp_matrix_t bsp_read_matrix_from_group_parallel(hid_t f, int num_threads) { } #endif -bsp_matrix_t bsp_read_matrix_from_group(hid_t f) { +bsp_matrix_t bsp_read_matrix_from_group_allocator(hid_t f, + bsp_allocator_t allocator) { bsp_matrix_t matrix; - bsp_construct_default_matrix_t(&matrix); + bsp_construct_default_matrix_t_allocator(&matrix, allocator); char* json_string; - bsp_error_t error = bsp_read_attribute(&json_string, f, (char*) "binsparse"); + bsp_error_t error = bsp_read_attribute_allocator( + &json_string, f, (char*) "binsparse", allocator); if (error != BSP_SUCCESS) { return matrix; } @@ -213,7 +216,8 @@ bsp_matrix_t bsp_read_matrix_from_group(hid_t f) { assert(data_types_ != NULL); if (cJSON_HasObjectItem(data_types_, "values")) { - error = bsp_read_array(&matrix.values, f, (char*) "values"); + error = bsp_read_array_allocator(&matrix.values, f, (char*) "values", + allocator); if (error != BSP_SUCCESS) { free(json_string); return matrix; @@ -236,7 +240,8 @@ bsp_matrix_t bsp_read_matrix_from_group(hid_t f) { } if (cJSON_HasObjectItem(data_types_, "indices_0")) { - error = bsp_read_array(&matrix.indices_0, f, (char*) "indices_0"); + error = bsp_read_array_allocator(&matrix.indices_0, f, (char*) "indices_0", + allocator); if (error != BSP_SUCCESS) { free(json_string); bsp_destroy_array_t(&matrix.values); @@ -245,7 +250,8 @@ bsp_matrix_t bsp_read_matrix_from_group(hid_t f) { } if (cJSON_HasObjectItem(data_types_, "indices_1")) { - error = bsp_read_array(&matrix.indices_1, f, (char*) "indices_1"); + error = bsp_read_array_allocator(&matrix.indices_1, f, (char*) "indices_1", + allocator); if (error != BSP_SUCCESS) { free(json_string); bsp_destroy_array_t(&matrix.values); @@ -255,7 +261,8 @@ bsp_matrix_t bsp_read_matrix_from_group(hid_t f) { } if (cJSON_HasObjectItem(data_types_, "pointers_to_1")) { - error = bsp_read_array(&matrix.pointers_to_1, f, (char*) "pointers_to_1"); + error = bsp_read_array_allocator(&matrix.pointers_to_1, f, + (char*) "pointers_to_1", allocator); if (error != BSP_SUCCESS) { free(json_string); bsp_destroy_array_t(&matrix.values); @@ -278,6 +285,10 @@ bsp_matrix_t bsp_read_matrix_from_group(hid_t f) { return matrix; } +bsp_matrix_t bsp_read_matrix_from_group(hid_t f) { + return bsp_read_matrix_from_group_allocator(f, bsp_default_allocator); +} + static inline size_t bsp_final_dot(const char* str) { size_t dot_idx = 0; for (size_t i = 0; str[i] != '\0'; i++) { @@ -315,13 +326,14 @@ bsp_matrix_t bsp_read_matrix_parallel(const char* file_name, const char* group, } #endif -bsp_matrix_t bsp_read_matrix(const char* file_name, const char* group) { +bsp_matrix_t bsp_read_matrix_allocator(const char* file_name, const char* group, + bsp_allocator_t allocator) { if (group == NULL) { size_t idx = bsp_final_dot(file_name); if (strcmp(file_name + idx, ".hdf5") == 0 || strcmp(file_name + idx, ".h5") == 0) { hid_t f = H5Fopen(file_name, H5F_ACC_RDONLY, H5P_DEFAULT); - bsp_matrix_t matrix = bsp_read_matrix_from_group(f); + bsp_matrix_t matrix = bsp_read_matrix_from_group_allocator(f, allocator); H5Fclose(f); return matrix; } else if (strcmp(file_name + idx, ".mtx") == 0) { @@ -332,9 +344,13 @@ bsp_matrix_t bsp_read_matrix(const char* file_name, const char* group) { } else { hid_t f = H5Fopen(file_name, H5F_ACC_RDONLY, H5P_DEFAULT); hid_t g = H5Gopen1(f, group); - bsp_matrix_t matrix = bsp_read_matrix_from_group(g); + bsp_matrix_t matrix = bsp_read_matrix_from_group_allocator(g, allocator); H5Gclose(g); H5Fclose(f); return matrix; } } + +bsp_matrix_t bsp_read_matrix(const char* file_name, const char* group) { + return bsp_read_matrix_allocator(file_name, group, bsp_default_allocator); +} From b37c626c86ef0c969e2d8ecf8fcc1c902084aa20 Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Mon, 18 Aug 2025 21:33:33 -0700 Subject: [PATCH 5/9] Add error handling for Binsparse matrix IO functions. --- README.md | 4 +- examples/benchmark_read.c | 6 +- examples/benchmark_read_parallel.c | 6 +- examples/benchmark_write.c | 6 +- examples/bsp2mtx.c | 5 +- examples/check_equivalence.c | 5 +- examples/check_equivalence_parallel.c | 7 +- examples/mtx2bsp.c | 4 +- examples/simple_matrix_read.c | 3 +- examples/simple_matrix_write.c | 2 +- examples/simple_read.c | 7 +- include/binsparse/error.h | 21 ++++ include/binsparse/read_matrix.h | 22 ++-- src/read_matrix.c | 167 ++++++++++++++------------ 14 files changed, 155 insertions(+), 110 deletions(-) diff --git a/README.md b/README.md index a806025..29a5a82 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,8 @@ This library provides a C interface for reading and writing binsparse matrices. #include int main(int argc, char** argv) { - bsp_matrix_t mat = bsp_read_matrix("chesapeake.bsp.hdf5"); + bsp_matrix_t mat; + bsp_read_matrix(&mat, "chesapeake.bsp.hdf5", NULL); if (mat.format == BSP_COO) { float* values = mat.values.data; @@ -33,6 +34,7 @@ int main(int argc, char** argv) { bsp_get_matrix_format_string(mat.format)); } + bsp_destroy_matrix_t(&mat); return 0; } ``` diff --git a/examples/benchmark_read.c b/examples/benchmark_read.c index 9d72d89..6162fd5 100644 --- a/examples/benchmark_read.c +++ b/examples/benchmark_read.c @@ -79,7 +79,8 @@ int main(int argc, char** argv) { // If running warm cache experiments, read once to warm cache. if (!cold_cache) { - bsp_matrix_t mat = bsp_read_matrix(file_name, NULL); + bsp_matrix_t mat; + BSP_CHECK(bsp_read_matrix(&mat, file_name, NULL)); bsp_destroy_matrix_t(&mat); } @@ -88,7 +89,8 @@ int main(int argc, char** argv) { flush_cache(); } double begin = gettime(); - bsp_matrix_t mat = bsp_read_matrix(file_name, NULL); + bsp_matrix_t mat; + BSP_CHECK(bsp_read_matrix(&mat, file_name, NULL)); double end = gettime(); durations[i] = end - begin; nbytes = bsp_matrix_nbytes(mat); diff --git a/examples/benchmark_read_parallel.c b/examples/benchmark_read_parallel.c index c83ece9..33af3fa 100644 --- a/examples/benchmark_read_parallel.c +++ b/examples/benchmark_read_parallel.c @@ -82,7 +82,8 @@ int main(int argc, char** argv) { // If running warm cache experiments, read once to warm cache. if (!cold_cache) { - bsp_matrix_t mat = bsp_read_matrix_parallel(file_name, NULL, num_threads); + bsp_matrix_t mat; + BSP_CHECK(bsp_read_matrix_parallel(&mat, file_name, NULL, num_threads)); bsp_destroy_matrix_t(&mat); } @@ -92,7 +93,8 @@ int main(int argc, char** argv) { } fflush(stdout); double begin = gettime(); - bsp_matrix_t mat = bsp_read_matrix_parallel(file_name, NULL, num_threads); + bsp_matrix_t mat; + BSP_CHECK(bsp_read_matrix_parallel(&mat, file_name, NULL, num_threads)); double end = gettime(); durations[i] = end - begin; nbytes = bsp_matrix_nbytes(mat); diff --git a/examples/benchmark_write.c b/examples/benchmark_write.c index 8af5881..7b459cc 100644 --- a/examples/benchmark_write.c +++ b/examples/benchmark_write.c @@ -97,7 +97,8 @@ int main(int argc, char** argv) { double durations[num_trials]; - bsp_matrix_t mat = bsp_read_matrix(file_name, NULL); + bsp_matrix_t mat; + BSP_CHECK(bsp_read_matrix(&mat, file_name, NULL)); size_t nbytes = bsp_matrix_nbytes(mat); char output_filename[2048]; @@ -124,7 +125,8 @@ int main(int argc, char** argv) { printf("Writing to file %s\n", output_filename); double begin = gettime(); - bsp_write_matrix(output_filename, mat, NULL, NULL, compression_level); + BSP_CHECK( + bsp_write_matrix(output_filename, mat, NULL, NULL, compression_level)); if (flush_each_write) { flush_writes(); diff --git a/examples/bsp2mtx.c b/examples/bsp2mtx.c index ac2cbb4..8b40c32 100644 --- a/examples/bsp2mtx.c +++ b/examples/bsp2mtx.c @@ -19,8 +19,9 @@ int main(int argc, char** argv) { char* output_fname = argv[2]; printf(" === Reading file... ===\n"); - bsp_matrix_t matrix = bsp_read_matrix(input_fname, NULL); - printf(" === Done writing. ===\n"); + bsp_matrix_t matrix; + BSP_CHECK(bsp_read_matrix(&matrix, input_fname, NULL)); + printf(" === Done reading. ===\n"); if (matrix.format != BSP_COO) { matrix = bsp_convert_matrix(matrix, BSP_COO); } diff --git a/examples/check_equivalence.c b/examples/check_equivalence.c index c15558d..2fba86b 100644 --- a/examples/check_equivalence.c +++ b/examples/check_equivalence.c @@ -110,8 +110,9 @@ int main(int argc, char** argv) { printf("Matrix 2: %s and %s\n", info2.fname, (info2.dataset == NULL) ? "root" : info2.dataset); - bsp_matrix_t matrix1 = bsp_read_matrix(info1.fname, info1.dataset); - bsp_matrix_t matrix2 = bsp_read_matrix(info2.fname, info2.dataset); + bsp_matrix_t matrix1, matrix2; + BSP_CHECK(bsp_read_matrix(&matrix1, info1.fname, info1.dataset)); + BSP_CHECK(bsp_read_matrix(&matrix2, info2.fname, info2.dataset)); bool perform_suitesparse_declamping = true; if (perform_suitesparse_declamping && diff --git a/examples/check_equivalence_parallel.c b/examples/check_equivalence_parallel.c index 399c4c4..ecdcaf0 100644 --- a/examples/check_equivalence_parallel.c +++ b/examples/check_equivalence_parallel.c @@ -117,9 +117,10 @@ int main(int argc, char** argv) { fflush(stdout); - bsp_matrix_t matrix1 = bsp_read_matrix(info1.fname, info1.dataset); - bsp_matrix_t matrix2 = - bsp_read_matrix_parallel(info2.fname, info2.dataset, num_threads); + bsp_matrix_t matrix1, matrix2; + BSP_CHECK(bsp_read_matrix(&matrix1, info1.fname, info1.dataset)); + BSP_CHECK(bsp_read_matrix_parallel(&matrix2, info2.fname, info2.dataset, + num_threads)); bool perform_suitesparse_declamping = true; if (perform_suitesparse_declamping && diff --git a/examples/mtx2bsp.c b/examples/mtx2bsp.c index 621a17d..6e1c7d1 100644 --- a/examples/mtx2bsp.c +++ b/examples/mtx2bsp.c @@ -163,8 +163,8 @@ int main(int argc, char** argv) { printf(" === Writing to %s... ===\n", output_fname); begin = gettime(); - bsp_write_matrix(output_fname, matrix, group_name, user_json, - compression_level); + BSP_CHECK(bsp_write_matrix(output_fname, matrix, group_name, user_json, + compression_level)); end = gettime(); duration = end - begin; printf("%lf seconds writing Binsparse file...\n", duration); diff --git a/examples/simple_matrix_read.c b/examples/simple_matrix_read.c index acbdabc..8ca1411 100644 --- a/examples/simple_matrix_read.c +++ b/examples/simple_matrix_read.c @@ -9,7 +9,8 @@ int main(int argc, char** argv) { const char* file_name = "test.hdf5"; - bsp_matrix_t mat = bsp_read_matrix(file_name, NULL); + bsp_matrix_t mat; + BSP_CHECK(bsp_read_matrix(&mat, file_name, NULL)); if (mat.format == BSP_COO) { float* values = (float*) mat.values.data; diff --git a/examples/simple_matrix_write.c b/examples/simple_matrix_write.c index 5ee6f3f..852c7e1 100644 --- a/examples/simple_matrix_write.c +++ b/examples/simple_matrix_write.c @@ -21,7 +21,7 @@ int main(int argc, char** argv) { printf("%d, %d: %f\n", rowind[i], colind[i], values[i]); } - bsp_write_matrix("test.hdf5", mat, NULL, NULL, 9); + BSP_CHECK(bsp_write_matrix("test.hdf5", mat, NULL, NULL, 9)); return 0; } diff --git a/examples/simple_read.c b/examples/simple_read.c index 0edfa08..23722a8 100644 --- a/examples/simple_read.c +++ b/examples/simple_read.c @@ -12,12 +12,7 @@ int main(int argc, char** argv) { hid_t f = H5Fopen(file_name, H5F_ACC_RDWR, H5P_DEFAULT); bsp_array_t array; - bsp_error_t error = bsp_read_array(&array, f, "test"); - if (error != BSP_SUCCESS) { - printf("Error reading array: %s\n", bsp_get_error_string(error)); - H5Fclose(f); - return 1; - } + BSP_CHECK(bsp_read_array(&array, f, "test")); int* values = (int*) array.data; diff --git a/include/binsparse/error.h b/include/binsparse/error.h index 2988c16..73b3f92 100644 --- a/include/binsparse/error.h +++ b/include/binsparse/error.h @@ -6,6 +6,9 @@ #pragma once +#include +#include + typedef enum bsp_error_t { BSP_SUCCESS = 0, @@ -60,3 +63,21 @@ static inline const char* bsp_get_error_string(bsp_error_t error) { return "Unknown error"; } } + +/** + * BSP_CHECK macro - checks if a bsp_error_t is BSP_SUCCESS. + * If not, prints file, line number, and error message, then aborts. + * + * Usage: BSP_CHECK(bsp_read_matrix(&matrix, "file.hdf5", NULL)); + * + * @param call The function call that returns a bsp_error_t + */ +#define BSP_CHECK(call) \ + do { \ + bsp_error_t _bsp_error = (call); \ + if (_bsp_error != BSP_SUCCESS) { \ + fprintf(stderr, "BSP Error at %s:%d: %s\n", __FILE__, __LINE__, \ + bsp_get_error_string(_bsp_error)); \ + abort(); \ + } \ + } while (0) diff --git a/include/binsparse/read_matrix.h b/include/binsparse/read_matrix.h index 699d9a8..875e2d3 100644 --- a/include/binsparse/read_matrix.h +++ b/include/binsparse/read_matrix.h @@ -15,22 +15,26 @@ extern "C" { #include #if __STDC_VERSION__ >= 201112L -bsp_matrix_t bsp_read_matrix_from_group_parallel(hid_t f, int num_threads); +bsp_error_t bsp_read_matrix_from_group_parallel(bsp_matrix_t* matrix, hid_t f, + int num_threads); #endif -bsp_matrix_t bsp_read_matrix_from_group(hid_t f); -bsp_matrix_t bsp_read_matrix_from_group_allocator(hid_t f, - bsp_allocator_t allocator); +bsp_error_t bsp_read_matrix_from_group(bsp_matrix_t* matrix, hid_t f); +bsp_error_t bsp_read_matrix_from_group_allocator(bsp_matrix_t* matrix, hid_t f, + bsp_allocator_t allocator); #endif #if __STDC_VERSION__ >= 201112L -bsp_matrix_t bsp_read_matrix_parallel(const char* file_name, const char* group, - int num_threads); +bsp_error_t bsp_read_matrix_parallel(bsp_matrix_t* matrix, + const char* file_name, const char* group, + int num_threads); #endif -bsp_matrix_t bsp_read_matrix(const char* file_name, const char* group); -bsp_matrix_t bsp_read_matrix_allocator(const char* file_name, const char* group, - bsp_allocator_t allocator); +bsp_error_t bsp_read_matrix(bsp_matrix_t* matrix, const char* file_name, + const char* group); +bsp_error_t bsp_read_matrix_allocator(bsp_matrix_t* matrix, + const char* file_name, const char* group, + bsp_allocator_t allocator); #ifdef __cplusplus } diff --git a/src/read_matrix.c b/src/read_matrix.c index 9f9ced9..32d8438 100644 --- a/src/read_matrix.c +++ b/src/read_matrix.c @@ -12,14 +12,14 @@ #include #if __STDC_VERSION__ >= 201112L -bsp_matrix_t bsp_read_matrix_from_group_parallel(hid_t f, int num_threads) { - bsp_matrix_t matrix; - bsp_construct_default_matrix_t(&matrix); +bsp_error_t bsp_read_matrix_from_group_parallel(bsp_matrix_t* matrix, hid_t f, + int num_threads) { + bsp_construct_default_matrix_t(matrix); char* json_string; bsp_error_t error = bsp_read_attribute(&json_string, f, (char*) "binsparse"); if (error != BSP_SUCCESS) { - return matrix; + return error; } cJSON* j = cJSON_Parse(json_string); @@ -46,7 +46,7 @@ bsp_matrix_t bsp_read_matrix_from_group_parallel(hid_t f, int num_threads) { assert(format != 0); - matrix.format = format; + matrix->format = format; cJSON* nnz_ = cJSON_GetObjectItemCaseSensitive(binsparse, "number_of_stored_values"); @@ -68,69 +68,70 @@ bsp_matrix_t bsp_read_matrix_from_group_parallel(hid_t f, int num_threads) { size_t ncols = cJSON_GetNumberValue(ncols_); - matrix.nrows = nrows; - matrix.ncols = ncols; - matrix.nnz = nnz; - matrix.format = format; + matrix->nrows = nrows; + matrix->ncols = ncols; + matrix->nnz = nnz; + matrix->format = format; cJSON* data_types_ = cJSON_GetObjectItemCaseSensitive(binsparse, "data_types"); assert(data_types_ != NULL); if (cJSON_HasObjectItem(data_types_, "values")) { - error = bsp_read_array_parallel(&matrix.values, f, (char*) "values", + error = bsp_read_array_parallel(&matrix->values, f, (char*) "values", num_threads); if (error != BSP_SUCCESS) { free(json_string); - return matrix; + return error; } cJSON* value_type = cJSON_GetObjectItemCaseSensitive(data_types_, "values"); char* type_string = cJSON_GetStringValue(value_type); if (strlen(type_string) >= 4 && strncmp(type_string, "iso[", 4) == 0) { - matrix.is_iso = true; + matrix->is_iso = true; type_string += 4; } if (strlen(type_string) >= 8 && strncmp(type_string, "complex[", 8) == 0) { - bsp_error_t error = bsp_fp_array_to_complex(&matrix.values); + bsp_error_t error = bsp_fp_array_to_complex(&matrix->values); if (error != BSP_SUCCESS) { - // Handle error - for now just continue with original array + // TODO: handle error + return error; } } } if (cJSON_HasObjectItem(data_types_, "indices_0")) { - error = bsp_read_array_parallel(&matrix.indices_0, f, (char*) "indices_0", + error = bsp_read_array_parallel(&matrix->indices_0, f, (char*) "indices_0", num_threads); if (error != BSP_SUCCESS) { free(json_string); - bsp_destroy_array_t(&matrix.values); - return matrix; + bsp_destroy_array_t(&matrix->values); + return error; } } if (cJSON_HasObjectItem(data_types_, "indices_1")) { - error = bsp_read_array_parallel(&matrix.indices_1, f, (char*) "indices_1", + error = bsp_read_array_parallel(&matrix->indices_1, f, (char*) "indices_1", num_threads); if (error != BSP_SUCCESS) { free(json_string); - bsp_destroy_array_t(&matrix.values); - bsp_destroy_array_t(&matrix.indices_0); - return matrix; + bsp_destroy_array_t(&matrix->values); + bsp_destroy_array_t(&matrix->indices_0); + return error; } } if (cJSON_HasObjectItem(data_types_, "pointers_to_1")) { - error = bsp_read_array_parallel(&matrix.pointers_to_1, f, + error = bsp_read_array_parallel(&matrix->pointers_to_1, f, (char*) "pointers_to_1", num_threads); if (error != BSP_SUCCESS) { free(json_string); - bsp_destroy_array_t(&matrix.values); - bsp_destroy_array_t(&matrix.indices_0); - bsp_destroy_array_t(&matrix.indices_1); - return matrix; + bsp_destroy_array_t(&matrix->values); + bsp_destroy_array_t(&matrix->indices_0); + bsp_destroy_array_t(&matrix->indices_1); + return error; } } @@ -138,26 +139,25 @@ bsp_matrix_t bsp_read_matrix_from_group_parallel(hid_t f, int num_threads) { cJSON* structure_ = cJSON_GetObjectItemCaseSensitive(binsparse, "structure"); char* structure = cJSON_GetStringValue(structure_); - matrix.structure = bsp_get_structure(structure); + matrix->structure = bsp_get_structure(structure); } cJSON_Delete(j); free(json_string); - return matrix; + return BSP_SUCCESS; } #endif -bsp_matrix_t bsp_read_matrix_from_group_allocator(hid_t f, - bsp_allocator_t allocator) { - bsp_matrix_t matrix; - bsp_construct_default_matrix_t_allocator(&matrix, allocator); +bsp_error_t bsp_read_matrix_from_group_allocator(bsp_matrix_t* matrix, hid_t f, + bsp_allocator_t allocator) { + bsp_construct_default_matrix_t_allocator(matrix, allocator); char* json_string; bsp_error_t error = bsp_read_attribute_allocator( &json_string, f, (char*) "binsparse", allocator); if (error != BSP_SUCCESS) { - return matrix; + return error; } cJSON* j = cJSON_Parse(json_string); @@ -184,7 +184,7 @@ bsp_matrix_t bsp_read_matrix_from_group_allocator(hid_t f, assert(format != 0); - matrix.format = format; + matrix->format = format; cJSON* nnz_ = cJSON_GetObjectItemCaseSensitive(binsparse, "number_of_stored_values"); @@ -206,69 +206,70 @@ bsp_matrix_t bsp_read_matrix_from_group_allocator(hid_t f, size_t ncols = cJSON_GetNumberValue(ncols_); - matrix.nrows = nrows; - matrix.ncols = ncols; - matrix.nnz = nnz; - matrix.format = format; + matrix->nrows = nrows; + matrix->ncols = ncols; + matrix->nnz = nnz; + matrix->format = format; cJSON* data_types_ = cJSON_GetObjectItemCaseSensitive(binsparse, "data_types"); assert(data_types_ != NULL); if (cJSON_HasObjectItem(data_types_, "values")) { - error = bsp_read_array_allocator(&matrix.values, f, (char*) "values", + error = bsp_read_array_allocator(&matrix->values, f, (char*) "values", allocator); if (error != BSP_SUCCESS) { free(json_string); - return matrix; + return error; } cJSON* value_type = cJSON_GetObjectItemCaseSensitive(data_types_, "values"); char* type_string = cJSON_GetStringValue(value_type); if (strlen(type_string) >= 4 && strncmp(type_string, "iso[", 4) == 0) { - matrix.is_iso = true; + matrix->is_iso = true; type_string += 4; } if (strlen(type_string) >= 8 && strncmp(type_string, "complex[", 8) == 0) { - bsp_error_t error = bsp_fp_array_to_complex(&matrix.values); + bsp_error_t error = bsp_fp_array_to_complex(&matrix->values); if (error != BSP_SUCCESS) { - // Handle error - for now just continue with original array + // TODO: handle error + return error; } } } if (cJSON_HasObjectItem(data_types_, "indices_0")) { - error = bsp_read_array_allocator(&matrix.indices_0, f, (char*) "indices_0", + error = bsp_read_array_allocator(&matrix->indices_0, f, (char*) "indices_0", allocator); if (error != BSP_SUCCESS) { free(json_string); - bsp_destroy_array_t(&matrix.values); - return matrix; + bsp_destroy_array_t(&matrix->values); + return error; } } if (cJSON_HasObjectItem(data_types_, "indices_1")) { - error = bsp_read_array_allocator(&matrix.indices_1, f, (char*) "indices_1", + error = bsp_read_array_allocator(&matrix->indices_1, f, (char*) "indices_1", allocator); if (error != BSP_SUCCESS) { free(json_string); - bsp_destroy_array_t(&matrix.values); - bsp_destroy_array_t(&matrix.indices_0); - return matrix; + bsp_destroy_array_t(&matrix->values); + bsp_destroy_array_t(&matrix->indices_0); + return error; } } if (cJSON_HasObjectItem(data_types_, "pointers_to_1")) { - error = bsp_read_array_allocator(&matrix.pointers_to_1, f, + error = bsp_read_array_allocator(&matrix->pointers_to_1, f, (char*) "pointers_to_1", allocator); if (error != BSP_SUCCESS) { free(json_string); - bsp_destroy_array_t(&matrix.values); - bsp_destroy_array_t(&matrix.indices_0); - bsp_destroy_array_t(&matrix.indices_1); - return matrix; + bsp_destroy_array_t(&matrix->values); + bsp_destroy_array_t(&matrix->indices_0); + bsp_destroy_array_t(&matrix->indices_1); + return error; } } @@ -276,17 +277,17 @@ bsp_matrix_t bsp_read_matrix_from_group_allocator(hid_t f, cJSON* structure_ = cJSON_GetObjectItemCaseSensitive(binsparse, "structure"); char* structure = cJSON_GetStringValue(structure_); - matrix.structure = bsp_get_structure(structure); + matrix->structure = bsp_get_structure(structure); } cJSON_Delete(j); free(json_string); - return matrix; + return BSP_SUCCESS; } -bsp_matrix_t bsp_read_matrix_from_group(hid_t f) { - return bsp_read_matrix_from_group_allocator(f, bsp_default_allocator); +bsp_error_t bsp_read_matrix_from_group(bsp_matrix_t* matrix, hid_t f) { + return bsp_read_matrix_from_group_allocator(matrix, f, bsp_default_allocator); } static inline size_t bsp_final_dot(const char* str) { @@ -300,57 +301,69 @@ static inline size_t bsp_final_dot(const char* str) { } #if __STDC_VERSION__ >= 201112L -bsp_matrix_t bsp_read_matrix_parallel(const char* file_name, const char* group, - int num_threads) { +bsp_error_t bsp_read_matrix_parallel(bsp_matrix_t* matrix, + const char* file_name, const char* group, + int num_threads) { if (group == NULL) { size_t idx = bsp_final_dot(file_name); if (strcmp(file_name + idx, ".hdf5") == 0 || strcmp(file_name + idx, ".h5") == 0) { hid_t f = H5Fopen(file_name, H5F_ACC_RDONLY, H5P_DEFAULT); - bsp_matrix_t matrix = bsp_read_matrix_from_group_parallel(f, num_threads); + bsp_error_t error = + bsp_read_matrix_from_group_parallel(matrix, f, num_threads); H5Fclose(f); - return matrix; + return error; } else if (strcmp(file_name + idx, ".mtx") == 0) { - return bsp_mmread(file_name); + // TODO: implement error-handling for Matrix Market. + *matrix = bsp_mmread(file_name); + return BSP_SUCCESS; } else { - assert(false); + return BSP_ERROR_IO; } } else { hid_t f = H5Fopen(file_name, H5F_ACC_RDONLY, H5P_DEFAULT); hid_t g = H5Gopen1(f, group); - bsp_matrix_t matrix = bsp_read_matrix_from_group_parallel(g, num_threads); + bsp_error_t error = + bsp_read_matrix_from_group_parallel(matrix, g, num_threads); H5Gclose(g); H5Fclose(f); - return matrix; + return error; } } #endif -bsp_matrix_t bsp_read_matrix_allocator(const char* file_name, const char* group, - bsp_allocator_t allocator) { +bsp_error_t bsp_read_matrix_allocator(bsp_matrix_t* matrix, + const char* file_name, const char* group, + bsp_allocator_t allocator) { if (group == NULL) { size_t idx = bsp_final_dot(file_name); if (strcmp(file_name + idx, ".hdf5") == 0 || strcmp(file_name + idx, ".h5") == 0) { hid_t f = H5Fopen(file_name, H5F_ACC_RDONLY, H5P_DEFAULT); - bsp_matrix_t matrix = bsp_read_matrix_from_group_allocator(f, allocator); + bsp_error_t error = + bsp_read_matrix_from_group_allocator(matrix, f, allocator); H5Fclose(f); - return matrix; + return error; } else if (strcmp(file_name + idx, ".mtx") == 0) { - return bsp_mmread(file_name); + // TODO: implement error-handling for Matrix Market. + *matrix = bsp_mmread(file_name); + return BSP_SUCCESS; } else { - assert(false); + return BSP_ERROR_IO; } } else { hid_t f = H5Fopen(file_name, H5F_ACC_RDONLY, H5P_DEFAULT); hid_t g = H5Gopen1(f, group); - bsp_matrix_t matrix = bsp_read_matrix_from_group_allocator(g, allocator); + bsp_error_t error = + bsp_read_matrix_from_group_allocator(matrix, g, allocator); H5Gclose(g); H5Fclose(f); - return matrix; + return error; } } -bsp_matrix_t bsp_read_matrix(const char* file_name, const char* group) { - return bsp_read_matrix_allocator(file_name, group, bsp_default_allocator); +bsp_error_t bsp_read_matrix(bsp_matrix_t* matrix, const char* file_name, + const char* group) { + return bsp_read_matrix_allocator(matrix, file_name, group, + bsp_default_allocator); } From 355caa3e8c886070d865139f4034b0901c19fb40 Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Tue, 19 Aug 2025 11:03:57 -0700 Subject: [PATCH 6/9] Add sanitizers build option, fix a few memory leaks. --- CMakeLists.txt | 8 ++++++++ examples/check_equivalence.c | 5 +++++ examples/mtx2bsp.c | 3 +++ include/binsparse/detail/parse_dataset.h | 5 +++++ include/binsparse/matrix_market/matrix_market_inspector.h | 4 ++++ include/binsparse/matrix_market/matrix_market_read.h | 8 ++++++++ src/write_matrix.c | 3 ++- 7 files changed, 35 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ea7531..76cf717 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,8 @@ set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 20) set(CMAKE_C_FLAGS "-O3 -march=native") +option(ENABLE_SANITIZERS "Enable Clang sanitizers" OFF) + include(GNUInstallDirs) add_library(binsparse STATIC) @@ -84,6 +86,12 @@ install(FILES DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/binsparse) if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + if (ENABLE_SANITIZERS) + set(SANITIZER_FLAGS "-fsanitize=address,undefined") + target_compile_options(binsparse INTERFACE ${SANITIZER_FLAGS} -g -O1 -fno-omit-frame-pointer) + target_link_options(binsparse INTERFACE ${SANITIZER_FLAGS}) + endif() + add_subdirectory(examples) add_subdirectory(test) endif() diff --git a/examples/check_equivalence.c b/examples/check_equivalence.c index 2fba86b..641ec8c 100644 --- a/examples/check_equivalence.c +++ b/examples/check_equivalence.c @@ -203,6 +203,11 @@ int main(int argc, char** argv) { return 9; } + bsp_destroy_matrix_t(&matrix1); + bsp_destroy_matrix_t(&matrix2); + bsp_destroy_fdataset_info_t(&info1); + bsp_destroy_fdataset_info_t(&info2); + printf("The files are equivalent.\n"); printf("OK!\n"); diff --git a/examples/mtx2bsp.c b/examples/mtx2bsp.c index 6e1c7d1..1d88613 100644 --- a/examples/mtx2bsp.c +++ b/examples/mtx2bsp.c @@ -171,6 +171,9 @@ int main(int argc, char** argv) { printf(" === Done writing. ===\n"); bsp_destroy_matrix_t(&matrix); + bsp_destroy_mm_metadata(&m); + bsp_destroy_fdataset_info_t(&info2); + cJSON_Delete(user_json); return 0; } diff --git a/include/binsparse/detail/parse_dataset.h b/include/binsparse/detail/parse_dataset.h index 4facff7..489be88 100644 --- a/include/binsparse/detail/parse_dataset.h +++ b/include/binsparse/detail/parse_dataset.h @@ -13,6 +13,11 @@ typedef struct { char* dataset; } bsp_fdataset_info_t; +static inline void bsp_destroy_fdataset_info_t(bsp_fdataset_info_t* info) { + free(info->fname); + free(info->dataset); +} + static inline bsp_fdataset_info_t bsp_parse_fdataset_string(char* str) { size_t len = strlen(str); diff --git a/include/binsparse/matrix_market/matrix_market_inspector.h b/include/binsparse/matrix_market/matrix_market_inspector.h index 8811a89..0e35fff 100644 --- a/include/binsparse/matrix_market/matrix_market_inspector.h +++ b/include/binsparse/matrix_market/matrix_market_inspector.h @@ -32,6 +32,10 @@ typedef struct bsp_mm_metadata { char* comments; } bsp_mm_metadata; +static inline void bsp_destroy_mm_metadata(bsp_mm_metadata* metadata) { + free(metadata->comments); +} + static inline bsp_mm_metadata bsp_mmread_metadata(const char* file_path) { FILE* f = fopen(file_path, "r"); diff --git a/include/binsparse/matrix_market/matrix_market_read.h b/include/binsparse/matrix_market/matrix_market_read.h index 4db0947..2c64ef8 100644 --- a/include/binsparse/matrix_market/matrix_market_read.h +++ b/include/binsparse/matrix_market/matrix_market_read.h @@ -109,6 +109,8 @@ static inline bsp_matrix_t bsp_mmread_explicit_array(const char* file_path, fclose(f); + bsp_destroy_mm_metadata(&metadata); + return matrix; } @@ -306,6 +308,8 @@ bsp_mmread_explicit_coordinate(const char* file_path, bsp_type_t value_type, free(indices); fclose(f); + bsp_destroy_mm_metadata(&metadata); + return matrix; } @@ -315,8 +319,10 @@ static inline bsp_matrix_t bsp_mmread_explicit(const char* file_path, bsp_mm_metadata metadata = bsp_mmread_metadata(file_path); if (strcmp(metadata.format, "array") == 0) { + bsp_destroy_mm_metadata(&metadata); return bsp_mmread_explicit_array(file_path, value_type, index_type); } else if (strcmp(metadata.format, "coordinate") == 0) { + bsp_destroy_mm_metadata(&metadata); return bsp_mmread_explicit_coordinate(file_path, value_type, index_type); } else { assert(false); @@ -345,5 +351,7 @@ static inline bsp_matrix_t bsp_mmread(const char* file_path) { bsp_type_t index_type = bsp_pick_integer_type(max_dim); + bsp_destroy_mm_metadata(&metadata); + return bsp_mmread_explicit(file_path, value_type, index_type); } diff --git a/src/write_matrix.c b/src/write_matrix.c index 0b202bb..322318c 100644 --- a/src/write_matrix.c +++ b/src/write_matrix.c @@ -22,7 +22,8 @@ char* bsp_generate_json(bsp_matrix_t matrix, cJSON* user_json) { cJSON* item; cJSON_ArrayForEach(item, user_json) { - cJSON_AddItemToObject(j, item->string, item); + cJSON* item_copy = cJSON_Duplicate(item, 1); // 1 = deep copy + cJSON_AddItemToObject(j, item->string, item_copy); } cJSON_AddStringToObject(binsparse, "version", BINSPARSE_VERSION); From 61f288f3b69f896a752ac3355d5e3c148b401e18 Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Tue, 19 Aug 2025 19:33:05 -0700 Subject: [PATCH 7/9] Separate functions that depend on `cJSON` or `hdf5` into separate headers. `binsparse_all.h` includes everything. --- examples/bsp-ls.c | 2 +- examples/mtx2bsp.c | 6 ++-- include/binsparse/binsparse_all.h | 11 +++++++ include/binsparse/binsparse_cJSON.h | 34 +++++++++++++++++++++ include/binsparse/binsparse_hdf5.h | 43 ++++++++++++++++++++++++++ include/binsparse/read_matrix.h | 17 ++--------- include/binsparse/write_matrix.h | 10 +----- src/read_matrix.c | 7 +++-- src/write_matrix.c | 47 +++++++++++++++++++++++------ 9 files changed, 138 insertions(+), 39 deletions(-) create mode 100644 include/binsparse/binsparse_all.h create mode 100644 include/binsparse/binsparse_cJSON.h create mode 100644 include/binsparse/binsparse_hdf5.h diff --git a/examples/bsp-ls.c b/examples/bsp-ls.c index 9207224..7fb0870 100644 --- a/examples/bsp-ls.c +++ b/examples/bsp-ls.c @@ -4,7 +4,7 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#include +#include herr_t visit_group(hid_t loc_id, const char* name, const H5L_info_t* linfo, void* opdata); diff --git a/examples/mtx2bsp.c b/examples/mtx2bsp.c index 1d88613..9b35c3f 100644 --- a/examples/mtx2bsp.c +++ b/examples/mtx2bsp.c @@ -4,7 +4,7 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#include +#include #include #include @@ -163,8 +163,8 @@ int main(int argc, char** argv) { printf(" === Writing to %s... ===\n", output_fname); begin = gettime(); - BSP_CHECK(bsp_write_matrix(output_fname, matrix, group_name, user_json, - compression_level)); + BSP_CHECK(bsp_write_matrix_cjson(output_fname, matrix, group_name, user_json, + compression_level)); end = gettime(); duration = end - begin; printf("%lf seconds writing Binsparse file...\n", duration); diff --git a/include/binsparse/binsparse_all.h b/include/binsparse/binsparse_all.h new file mode 100644 index 0000000..34f3463 --- /dev/null +++ b/include/binsparse/binsparse_all.h @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2024 Binsparse Developers + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#pragma once + +#include +#include +#include diff --git a/include/binsparse/binsparse_cJSON.h b/include/binsparse/binsparse_cJSON.h new file mode 100644 index 0000000..c74b9c4 --- /dev/null +++ b/include/binsparse/binsparse_cJSON.h @@ -0,0 +1,34 @@ +/* + * SPDX-FileCopyrightText: 2024 Binsparse Developers + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#pragma once + +#include +#include + +#include + +#ifndef BSP_BINSPARSE_CJSON_H +#define BSP_BINSPARSE_CJSON_H +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +bsp_error_t bsp_write_matrix_cjson(const char* fname, bsp_matrix_t matrix, + const char* group, cJSON* user_json, + int compression_level); + +#ifdef BSP_BINSPARSE_HDF5_H +bsp_error_t bsp_write_matrix_to_group_cjson(hid_t f, bsp_matrix_t matrix, + cJSON* user_json, + int compression_level); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/include/binsparse/binsparse_hdf5.h b/include/binsparse/binsparse_hdf5.h new file mode 100644 index 0000000..c97bea1 --- /dev/null +++ b/include/binsparse/binsparse_hdf5.h @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2024 Binsparse Developers + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#pragma once + +#include +#include + +#include + +#ifndef BSP_BINSPARSE_HDF5_H +#define BSP_BINSPARSE_HDF5_H +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if __STDC_VERSION__ >= 201112L +bsp_error_t bsp_read_matrix_from_group_parallel(bsp_matrix_t* matrix, hid_t f, + int num_threads); +#endif + +bsp_error_t bsp_read_matrix_from_group(bsp_matrix_t* matrix, hid_t f); +bsp_error_t bsp_read_matrix_from_group_allocator(bsp_matrix_t* matrix, hid_t f, + bsp_allocator_t allocator); + +bsp_error_t bsp_write_matrix_to_group(hid_t f, bsp_matrix_t matrix, + const char* user_json, + int compression_level); + +#ifdef BSP_BINSPARSE_CJSON_H +bsp_error_t bsp_write_matrix_to_group_cjson(hid_t f, bsp_matrix_t matrix, + cJSON* user_json, + int compression_level); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/include/binsparse/read_matrix.h b/include/binsparse/read_matrix.h index 875e2d3..523f34c 100644 --- a/include/binsparse/read_matrix.h +++ b/include/binsparse/read_matrix.h @@ -6,22 +6,11 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef BSP_USE_HDF5 #include -#include +#include -#if __STDC_VERSION__ >= 201112L -bsp_error_t bsp_read_matrix_from_group_parallel(bsp_matrix_t* matrix, hid_t f, - int num_threads); -#endif - -bsp_error_t bsp_read_matrix_from_group(bsp_matrix_t* matrix, hid_t f); -bsp_error_t bsp_read_matrix_from_group_allocator(bsp_matrix_t* matrix, hid_t f, - bsp_allocator_t allocator); +#ifdef __cplusplus +extern "C" { #endif #if __STDC_VERSION__ >= 201112L diff --git a/include/binsparse/write_matrix.h b/include/binsparse/write_matrix.h index 2b067d2..e535569 100644 --- a/include/binsparse/write_matrix.h +++ b/include/binsparse/write_matrix.h @@ -13,17 +13,9 @@ extern "C" { // TODO: make cJSON optional. #include -#include - -#ifdef BSP_USE_HDF5 -#include - -bsp_error_t bsp_write_matrix_to_group(hid_t f, bsp_matrix_t matrix, - cJSON* user_json, int compression_level); -#endif bsp_error_t bsp_write_matrix(const char* fname, bsp_matrix_t matrix, - const char* group, cJSON* user_json, + const char* group, const char* user_json, int compression_level); #ifdef __cplusplus diff --git a/src/read_matrix.c b/src/read_matrix.c index 32d8438..e06a82b 100644 --- a/src/read_matrix.c +++ b/src/read_matrix.c @@ -5,6 +5,8 @@ */ #include +#include +#include #include #include #include @@ -24,8 +26,9 @@ bsp_error_t bsp_read_matrix_from_group_parallel(bsp_matrix_t* matrix, hid_t f, cJSON* j = cJSON_Parse(json_string); - assert(j != NULL); - assert(cJSON_IsObject(j)); + if (j == NULL || !cJSON_IsObject(j)) { + return BSP_ERROR_FORMAT; + } cJSON* binsparse = cJSON_GetObjectItemCaseSensitive(j, "binsparse"); assert(cJSON_IsObject(binsparse)); diff --git a/src/write_matrix.c b/src/write_matrix.c index 322318c..2345ae3 100644 --- a/src/write_matrix.c +++ b/src/write_matrix.c @@ -5,7 +5,7 @@ */ #include -#include +#include #include #include #include @@ -87,8 +87,9 @@ char* bsp_generate_json(bsp_matrix_t matrix, cJSON* user_json) { return string; } -bsp_error_t bsp_write_matrix_to_group(hid_t f, bsp_matrix_t matrix, - cJSON* user_json, int compression_level) { +bsp_error_t bsp_write_matrix_to_group_cjson(hid_t f, bsp_matrix_t matrix, + cJSON* user_json, + int compression_level) { bsp_error_t error = bsp_write_array(f, (char*) "values", matrix.values, compression_level); if (error != BSP_SUCCESS) { @@ -131,13 +132,26 @@ bsp_error_t bsp_write_matrix_to_group(hid_t f, bsp_matrix_t matrix, return BSP_SUCCESS; } -bsp_error_t bsp_write_matrix(const char* fname, bsp_matrix_t matrix, - const char* group, cJSON* user_json, - int compression_level) { +bsp_error_t bsp_write_matrix_to_group(hid_t f, bsp_matrix_t matrix, + const char* user_json, + int compression_level) { + cJSON* user_json_cjson = cJSON_Parse(user_json); + if (user_json_cjson == NULL) { + return BSP_ERROR_FORMAT; + } + bsp_error_t error = bsp_write_matrix_to_group_cjson( + f, matrix, user_json_cjson, compression_level); + cJSON_Delete(user_json_cjson); + return error; +} + +bsp_error_t bsp_write_matrix_cjson(const char* fname, bsp_matrix_t matrix, + const char* group, cJSON* user_json, + int compression_level) { if (group == NULL) { hid_t f = H5Fcreate(fname, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); - bsp_error_t error = - bsp_write_matrix_to_group(f, matrix, user_json, compression_level); + bsp_error_t error = bsp_write_matrix_to_group_cjson(f, matrix, user_json, + compression_level); if (error != BSP_SUCCESS) { H5Fclose(f); return error; @@ -151,8 +165,8 @@ bsp_error_t bsp_write_matrix(const char* fname, bsp_matrix_t matrix, f = H5Fcreate(fname, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); } hid_t g = H5Gcreate1(f, group, H5P_DEFAULT); - bsp_error_t error = - bsp_write_matrix_to_group(g, matrix, user_json, compression_level); + bsp_error_t error = bsp_write_matrix_to_group_cjson(g, matrix, user_json, + compression_level); if (error != BSP_SUCCESS) { H5Gclose(g); H5Fclose(f); @@ -163,3 +177,16 @@ bsp_error_t bsp_write_matrix(const char* fname, bsp_matrix_t matrix, } return BSP_SUCCESS; } + +bsp_error_t bsp_write_matrix(const char* fname, bsp_matrix_t matrix, + const char* group, const char* user_json, + int compression_level) { + cJSON* user_json_cjson = cJSON_Parse(user_json); + if (user_json_cjson == NULL) { + return BSP_ERROR_FORMAT; + } + bsp_error_t error = bsp_write_matrix_cjson( + fname, matrix, group, user_json_cjson, compression_level); + cJSON_Delete(user_json_cjson); + return error; +} From 4c406faf625ba20aac9102289ba26b09c8a774f7 Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Tue, 19 Aug 2025 20:10:33 -0700 Subject: [PATCH 8/9] Move hdf5 types into another file. --- include/binsparse/binsparse.h | 1 - include/binsparse/detail/hdf5_types.h | 208 ++++++++++++++++++++++++++ include/binsparse/hdf5_wrapper.h | 1 + include/binsparse/types.h | 199 ------------------------ 4 files changed, 209 insertions(+), 200 deletions(-) create mode 100644 include/binsparse/detail/hdf5_types.h diff --git a/include/binsparse/binsparse.h b/include/binsparse/binsparse.h index b77735a..189aa7c 100644 --- a/include/binsparse/binsparse.h +++ b/include/binsparse/binsparse.h @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/include/binsparse/detail/hdf5_types.h b/include/binsparse/detail/hdf5_types.h new file mode 100644 index 0000000..02f0315 --- /dev/null +++ b/include/binsparse/detail/hdf5_types.h @@ -0,0 +1,208 @@ +/* + * SPDX-FileCopyrightText: 2024 Binsparse Developers + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#pragma once + +#include +#include + +static inline hid_t bsp_get_hdf5_standard_type(bsp_type_t type) { + if (type == BSP_UINT8) { + return H5T_STD_U8LE; + } else if (type == BSP_UINT16) { + return H5T_STD_U16LE; + } else if (type == BSP_UINT32) { + return H5T_STD_U32LE; + } else if (type == BSP_UINT64) { + return H5T_STD_U64LE; + } else if (type == BSP_INT8) { + return H5T_STD_I8LE; + } else if (type == BSP_INT16) { + return H5T_STD_I16LE; + } else if (type == BSP_INT32) { + return H5T_STD_I32LE; + } else if (type == BSP_INT64) { + return H5T_STD_I64LE; + } else if (type == BSP_FLOAT32) { + return H5T_IEEE_F32LE; + } else if (type == BSP_FLOAT64) { + return H5T_IEEE_F64LE; + } else if (type == BSP_BINT8) { + return H5T_STD_I8LE; + } else { + return H5I_INVALID_HID; + } +} + +static inline bsp_type_t bsp_get_bsp_type(hid_t type) { + H5T_class_t cl = H5Tget_class(type); + H5T_order_t order = H5Tget_order(type); + H5T_sign_t sign = H5Tget_sign(type); + size_t size = H5Tget_size(type); + + if (cl == H5T_INTEGER) { + if (sign == H5T_SGN_NONE) { + if (size == 1) { + return BSP_UINT8; + } else if (size == 2) { + return BSP_UINT16; + } else if (size == 4) { + return BSP_UINT32; + } else if (size == 8) { + return BSP_UINT64; + } else { + return BSP_INVALID_TYPE; + } + } else /* if (sign == H5T_SGN_2) */ { + if (size == 1) { + return BSP_INT8; + } else if (size == 2) { + return BSP_INT16; + } else if (size == 4) { + return BSP_INT32; + } else if (size == 8) { + return BSP_INT64; + } else { + return BSP_INVALID_TYPE; + } + } + } else if (cl == H5T_FLOAT) { + if (size == 4) { + return BSP_FLOAT32; + } else if (size == 8) { + return BSP_FLOAT64; + } else { + return BSP_INVALID_TYPE; + } + } else { + return BSP_INVALID_TYPE; + } +} + +// NOTE: This code is a bit silly, but it seems to be the only +// way to generically determine the HDF5 native types for +// stdint's fixed width integer types. +static inline hid_t bsp_get_hdf5_native_type(bsp_type_t type) { + if (type == BSP_INT8 || type == BSP_BINT8) { + if (sizeof(int8_t) == sizeof(char)) { + return H5T_NATIVE_CHAR; + } else if (sizeof(int8_t) == sizeof(short)) { + return H5T_NATIVE_SHORT; + } else if (sizeof(int8_t) == sizeof(int)) { + return H5T_NATIVE_INT; + } else if (sizeof(int8_t) == sizeof(long)) { + return H5T_NATIVE_LONG; + } else if (sizeof(int8_t) == sizeof(long long)) { + return H5T_NATIVE_LLONG; + } else { + assert(false); + } + } else if (type == BSP_INT16) { + if (sizeof(int16_t) == sizeof(char)) { + return H5T_NATIVE_CHAR; + } else if (sizeof(int16_t) == sizeof(short)) { + return H5T_NATIVE_SHORT; + } else if (sizeof(int16_t) == sizeof(int)) { + return H5T_NATIVE_INT; + } else if (sizeof(int32_t) == sizeof(long)) { + return H5T_NATIVE_LONG; + } else if (sizeof(int64_t) == sizeof(long long)) { + return H5T_NATIVE_LLONG; + } else { + assert(false); + } + } else if (type == BSP_INT32) { + if (sizeof(int32_t) == sizeof(char)) { + return H5T_NATIVE_CHAR; + } else if (sizeof(int32_t) == sizeof(short)) { + return H5T_NATIVE_SHORT; + } else if (sizeof(int32_t) == sizeof(int)) { + return H5T_NATIVE_INT; + } else if (sizeof(int32_t) == sizeof(long)) { + return H5T_NATIVE_LONG; + } else if (sizeof(int32_t) == sizeof(long long)) { + return H5T_NATIVE_LLONG; + } else { + assert(false); + } + } else if (type == BSP_INT64) { + if (sizeof(int64_t) == sizeof(char)) { + return H5T_NATIVE_CHAR; + } else if (sizeof(int64_t) == sizeof(short)) { + return H5T_NATIVE_SHORT; + } else if (sizeof(int64_t) == sizeof(int)) { + return H5T_NATIVE_INT; + } else if (sizeof(int64_t) == sizeof(long)) { + return H5T_NATIVE_LONG; + } else if (sizeof(int64_t) == sizeof(long long)) { + return H5T_NATIVE_LLONG; + } else { + assert(false); + } + } else if (type == BSP_UINT8) { + if (sizeof(uint8_t) == sizeof(unsigned char)) { + return H5T_NATIVE_UCHAR; + } else if (sizeof(uint8_t) == sizeof(unsigned short)) { + return H5T_NATIVE_USHORT; + } else if (sizeof(uint8_t) == sizeof(unsigned int)) { + return H5T_NATIVE_UINT; + } else if (sizeof(uint8_t) == sizeof(unsigned long)) { + return H5T_NATIVE_ULONG; + } else if (sizeof(uint8_t) == sizeof(unsigned long long)) { + return H5T_NATIVE_ULLONG; + } else { + assert(false); + } + } else if (type == BSP_UINT16) { + if (sizeof(uint16_t) == sizeof(unsigned char)) { + return H5T_NATIVE_UCHAR; + } else if (sizeof(uint16_t) == sizeof(unsigned short)) { + return H5T_NATIVE_USHORT; + } else if (sizeof(uint16_t) == sizeof(unsigned int)) { + return H5T_NATIVE_UINT; + } else if (sizeof(uint16_t) == sizeof(unsigned long)) { + return H5T_NATIVE_ULONG; + } else if (sizeof(uint16_t) == sizeof(unsigned long long)) { + return H5T_NATIVE_ULLONG; + } else { + assert(false); + } + } else if (type == BSP_UINT32) { + if (sizeof(uint32_t) == sizeof(unsigned char)) { + return H5T_NATIVE_UCHAR; + } else if (sizeof(uint32_t) == sizeof(unsigned short)) { + return H5T_NATIVE_USHORT; + } else if (sizeof(uint32_t) == sizeof(unsigned int)) { + return H5T_NATIVE_UINT; + } else if (sizeof(uint32_t) == sizeof(unsigned long)) { + return H5T_NATIVE_ULONG; + } else if (sizeof(uint32_t) == sizeof(unsigned long long)) { + return H5T_NATIVE_ULLONG; + } else { + assert(false); + } + } else if (type == BSP_UINT64) { + if (sizeof(uint64_t) == sizeof(unsigned char)) { + return H5T_NATIVE_UCHAR; + } else if (sizeof(uint64_t) == sizeof(unsigned short)) { + return H5T_NATIVE_USHORT; + } else if (sizeof(uint64_t) == sizeof(unsigned int)) { + return H5T_NATIVE_UINT; + } else if (sizeof(uint64_t) == sizeof(unsigned long)) { + return H5T_NATIVE_ULONG; + } else if (sizeof(uint64_t) == sizeof(unsigned long long)) { + return H5T_NATIVE_ULLONG; + } else { + assert(false); + } + } else if (type == BSP_FLOAT32) { + return H5T_NATIVE_FLOAT; + } else if (type == BSP_FLOAT64) { + return H5T_NATIVE_DOUBLE; + } else { + return H5I_INVALID_HID; + } +} diff --git a/include/binsparse/hdf5_wrapper.h b/include/binsparse/hdf5_wrapper.h index 3c871dd..16ff7a7 100644 --- a/include/binsparse/hdf5_wrapper.h +++ b/include/binsparse/hdf5_wrapper.h @@ -13,6 +13,7 @@ #include +#include #include #if __STDC_VERSION__ >= 201112L diff --git a/include/binsparse/types.h b/include/binsparse/types.h index e3c9e53..e38c7e8 100644 --- a/include/binsparse/types.h +++ b/include/binsparse/types.h @@ -7,7 +7,6 @@ #pragma once #include -#include typedef enum bsp_type_t { BSP_UINT8 = 0, @@ -90,204 +89,6 @@ static inline size_t bsp_type_size(bsp_type_t type) { } } -static inline hid_t bsp_get_hdf5_standard_type(bsp_type_t type) { - if (type == BSP_UINT8) { - return H5T_STD_U8LE; - } else if (type == BSP_UINT16) { - return H5T_STD_U16LE; - } else if (type == BSP_UINT32) { - return H5T_STD_U32LE; - } else if (type == BSP_UINT64) { - return H5T_STD_U64LE; - } else if (type == BSP_INT8) { - return H5T_STD_I8LE; - } else if (type == BSP_INT16) { - return H5T_STD_I16LE; - } else if (type == BSP_INT32) { - return H5T_STD_I32LE; - } else if (type == BSP_INT64) { - return H5T_STD_I64LE; - } else if (type == BSP_FLOAT32) { - return H5T_IEEE_F32LE; - } else if (type == BSP_FLOAT64) { - return H5T_IEEE_F64LE; - } else if (type == BSP_BINT8) { - return H5T_STD_I8LE; - } else { - return H5I_INVALID_HID; - } -} - -static inline bsp_type_t bsp_get_bsp_type(hid_t type) { - H5T_class_t cl = H5Tget_class(type); - H5T_order_t order = H5Tget_order(type); - H5T_sign_t sign = H5Tget_sign(type); - size_t size = H5Tget_size(type); - - if (cl == H5T_INTEGER) { - if (sign == H5T_SGN_NONE) { - if (size == 1) { - return BSP_UINT8; - } else if (size == 2) { - return BSP_UINT16; - } else if (size == 4) { - return BSP_UINT32; - } else if (size == 8) { - return BSP_UINT64; - } else { - return BSP_INVALID_TYPE; - } - } else /* if (sign == H5T_SGN_2) */ { - if (size == 1) { - return BSP_INT8; - } else if (size == 2) { - return BSP_INT16; - } else if (size == 4) { - return BSP_INT32; - } else if (size == 8) { - return BSP_INT64; - } else { - return BSP_INVALID_TYPE; - } - } - } else if (cl == H5T_FLOAT) { - if (size == 4) { - return BSP_FLOAT32; - } else if (size == 8) { - return BSP_FLOAT64; - } else { - return BSP_INVALID_TYPE; - } - } else { - return BSP_INVALID_TYPE; - } -} - -// NOTE: This code is a bit silly, but it seems to be the only -// way to generically determine the HDF5 native types for -// stdint's fixed width integer types. -static inline hid_t bsp_get_hdf5_native_type(bsp_type_t type) { - if (type == BSP_INT8 || type == BSP_BINT8) { - if (sizeof(int8_t) == sizeof(char)) { - return H5T_NATIVE_CHAR; - } else if (sizeof(int8_t) == sizeof(short)) { - return H5T_NATIVE_SHORT; - } else if (sizeof(int8_t) == sizeof(int)) { - return H5T_NATIVE_INT; - } else if (sizeof(int8_t) == sizeof(long)) { - return H5T_NATIVE_LONG; - } else if (sizeof(int8_t) == sizeof(long long)) { - return H5T_NATIVE_LLONG; - } else { - assert(false); - } - } else if (type == BSP_INT16) { - if (sizeof(int16_t) == sizeof(char)) { - return H5T_NATIVE_CHAR; - } else if (sizeof(int16_t) == sizeof(short)) { - return H5T_NATIVE_SHORT; - } else if (sizeof(int16_t) == sizeof(int)) { - return H5T_NATIVE_INT; - } else if (sizeof(int32_t) == sizeof(long)) { - return H5T_NATIVE_LONG; - } else if (sizeof(int64_t) == sizeof(long long)) { - return H5T_NATIVE_LLONG; - } else { - assert(false); - } - } else if (type == BSP_INT32) { - if (sizeof(int32_t) == sizeof(char)) { - return H5T_NATIVE_CHAR; - } else if (sizeof(int32_t) == sizeof(short)) { - return H5T_NATIVE_SHORT; - } else if (sizeof(int32_t) == sizeof(int)) { - return H5T_NATIVE_INT; - } else if (sizeof(int32_t) == sizeof(long)) { - return H5T_NATIVE_LONG; - } else if (sizeof(int32_t) == sizeof(long long)) { - return H5T_NATIVE_LLONG; - } else { - assert(false); - } - } else if (type == BSP_INT64) { - if (sizeof(int64_t) == sizeof(char)) { - return H5T_NATIVE_CHAR; - } else if (sizeof(int64_t) == sizeof(short)) { - return H5T_NATIVE_SHORT; - } else if (sizeof(int64_t) == sizeof(int)) { - return H5T_NATIVE_INT; - } else if (sizeof(int64_t) == sizeof(long)) { - return H5T_NATIVE_LONG; - } else if (sizeof(int64_t) == sizeof(long long)) { - return H5T_NATIVE_LLONG; - } else { - assert(false); - } - } else if (type == BSP_UINT8) { - if (sizeof(uint8_t) == sizeof(unsigned char)) { - return H5T_NATIVE_UCHAR; - } else if (sizeof(uint8_t) == sizeof(unsigned short)) { - return H5T_NATIVE_USHORT; - } else if (sizeof(uint8_t) == sizeof(unsigned int)) { - return H5T_NATIVE_UINT; - } else if (sizeof(uint8_t) == sizeof(unsigned long)) { - return H5T_NATIVE_ULONG; - } else if (sizeof(uint8_t) == sizeof(unsigned long long)) { - return H5T_NATIVE_ULLONG; - } else { - assert(false); - } - } else if (type == BSP_UINT16) { - if (sizeof(uint16_t) == sizeof(unsigned char)) { - return H5T_NATIVE_UCHAR; - } else if (sizeof(uint16_t) == sizeof(unsigned short)) { - return H5T_NATIVE_USHORT; - } else if (sizeof(uint16_t) == sizeof(unsigned int)) { - return H5T_NATIVE_UINT; - } else if (sizeof(uint16_t) == sizeof(unsigned long)) { - return H5T_NATIVE_ULONG; - } else if (sizeof(uint16_t) == sizeof(unsigned long long)) { - return H5T_NATIVE_ULLONG; - } else { - assert(false); - } - } else if (type == BSP_UINT32) { - if (sizeof(uint32_t) == sizeof(unsigned char)) { - return H5T_NATIVE_UCHAR; - } else if (sizeof(uint32_t) == sizeof(unsigned short)) { - return H5T_NATIVE_USHORT; - } else if (sizeof(uint32_t) == sizeof(unsigned int)) { - return H5T_NATIVE_UINT; - } else if (sizeof(uint32_t) == sizeof(unsigned long)) { - return H5T_NATIVE_ULONG; - } else if (sizeof(uint32_t) == sizeof(unsigned long long)) { - return H5T_NATIVE_ULLONG; - } else { - assert(false); - } - } else if (type == BSP_UINT64) { - if (sizeof(uint64_t) == sizeof(unsigned char)) { - return H5T_NATIVE_UCHAR; - } else if (sizeof(uint64_t) == sizeof(unsigned short)) { - return H5T_NATIVE_USHORT; - } else if (sizeof(uint64_t) == sizeof(unsigned int)) { - return H5T_NATIVE_UINT; - } else if (sizeof(uint64_t) == sizeof(unsigned long)) { - return H5T_NATIVE_ULONG; - } else if (sizeof(uint64_t) == sizeof(unsigned long long)) { - return H5T_NATIVE_ULLONG; - } else { - assert(false); - } - } else if (type == BSP_FLOAT32) { - return H5T_NATIVE_FLOAT; - } else if (type == BSP_FLOAT64) { - return H5T_NATIVE_DOUBLE; - } else { - return H5I_INVALID_HID; - } -} - // Given the maximum value `max_value` that must be stored, // pick an unsigned integer type for indices. static inline bsp_type_t bsp_pick_integer_type(size_t max_value) { From 23ac92d3b1acea6998fb90b693b6e46e307876c0 Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Tue, 19 Aug 2025 20:33:10 -0700 Subject: [PATCH 9/9] Fix HDF5 inclusion. --- examples/benchmark_read.c | 1 + examples/benchmark_read_parallel.c | 1 + examples/benchmark_write.c | 1 + examples/simple_read.c | 2 +- examples/simple_write.c | 2 +- include/binsparse/binsparse_hdf5.h | 2 ++ include/binsparse/types.h | 2 ++ src/write_matrix.c | 1 + src/write_tensor.c | 8 +++++--- 9 files changed, 15 insertions(+), 5 deletions(-) diff --git a/examples/benchmark_read.c b/examples/benchmark_read.c index 6162fd5..0a1894a 100644 --- a/examples/benchmark_read.c +++ b/examples/benchmark_read.c @@ -7,6 +7,7 @@ #include #include #include +#include double gettime() { struct timespec time; diff --git a/examples/benchmark_read_parallel.c b/examples/benchmark_read_parallel.c index 33af3fa..24a5597 100644 --- a/examples/benchmark_read_parallel.c +++ b/examples/benchmark_read_parallel.c @@ -7,6 +7,7 @@ #include #include #include +#include double gettime() { struct timespec time; diff --git a/examples/benchmark_write.c b/examples/benchmark_write.c index 7b459cc..d2e5a60 100644 --- a/examples/benchmark_write.c +++ b/examples/benchmark_write.c @@ -7,6 +7,7 @@ #include #include #include +#include double gettime() { struct timespec time; diff --git a/examples/simple_read.c b/examples/simple_read.c index 23722a8..18d2eac 100644 --- a/examples/simple_read.c +++ b/examples/simple_read.c @@ -4,7 +4,7 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#include +#include int main(int argc, char** argv) { char* file_name = (char*) "test.hdf5"; diff --git a/examples/simple_write.c b/examples/simple_write.c index 261f228..0a51b49 100644 --- a/examples/simple_write.c +++ b/examples/simple_write.c @@ -4,7 +4,7 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#include +#include int main(int argc, char** argv) { const char* file_name = "test.hdf5"; diff --git a/include/binsparse/binsparse_hdf5.h b/include/binsparse/binsparse_hdf5.h index c97bea1..2aef4a5 100644 --- a/include/binsparse/binsparse_hdf5.h +++ b/include/binsparse/binsparse_hdf5.h @@ -9,6 +9,8 @@ #include #include +#include + #include #ifndef BSP_BINSPARSE_HDF5_H diff --git a/include/binsparse/types.h b/include/binsparse/types.h index e38c7e8..b487444 100644 --- a/include/binsparse/types.h +++ b/include/binsparse/types.h @@ -7,6 +7,8 @@ #pragma once #include +#include +#include typedef enum bsp_type_t { BSP_UINT8 = 0, diff --git a/src/write_matrix.c b/src/write_matrix.c index 2345ae3..d87244a 100644 --- a/src/write_matrix.c +++ b/src/write_matrix.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include diff --git a/src/write_tensor.c b/src/write_tensor.c index f212aeb..1b71f17 100644 --- a/src/write_tensor.c +++ b/src/write_tensor.c @@ -4,13 +4,15 @@ * SPDX-License-Identifier: BSD-3-Clause */ +#include +#include +#include + #include -#include #include -#include -#include #include +#include static cJSON* init_tensor_json(bsp_tensor_t tensor, cJSON* user_json) { cJSON* j = cJSON_CreateObject();