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/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 c1ef342..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; @@ -79,8 +80,9 @@ 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_matrix_t mat; + BSP_CHECK(bsp_read_matrix(&mat, file_name, NULL)); + bsp_destroy_matrix_t(&mat); } for (size_t i = 0; i < num_trials; i++) { @@ -88,11 +90,12 @@ 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); - 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..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; @@ -82,8 +83,9 @@ 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_matrix_t mat; + BSP_CHECK(bsp_read_matrix_parallel(&mat, file_name, NULL, num_threads)); + bsp_destroy_matrix_t(&mat); } for (size_t i = 0; i < num_trials; i++) { @@ -92,12 +94,13 @@ 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); - 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_write.c b/examples/benchmark_write.c index 8af5881..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; @@ -97,7 +98,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 +126,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/bsp-ls.c b/examples/bsp-ls.c index 8ecdd94..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); @@ -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/bsp2mtx.c b/examples/bsp2mtx.c index 6e3f483..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); } @@ -29,7 +30,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..641ec8c 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 && @@ -128,13 +129,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; } } @@ -202,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/check_equivalence_parallel.c b/examples/check_equivalence_parallel.c index b773cf8..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 && @@ -136,13 +137,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..9b35c3f 100644 --- a/examples/mtx2bsp.c +++ b/examples/mtx2bsp.c @@ -4,7 +4,7 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#include +#include #include #include @@ -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; @@ -163,14 +163,17 @@ 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_cjson(output_fname, matrix, group_name, user_json, + compression_level)); end = gettime(); duration = end - begin; printf("%lf seconds writing Binsparse file...\n", duration); printf(" === Done writing. ===\n"); - bsp_destroy_matrix_t(matrix); + 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/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 a4ba9bb..18d2eac 100644 --- a/examples/simple_read.c +++ b/examples/simple_read.c @@ -4,14 +4,15 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#include +#include int main(int argc, char** argv) { char* file_name = (char*) "test.hdf5"; 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_CHECK(bsp_read_array(&array, f, "test")); int* values = (int*) array.data; @@ -19,7 +20,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..0a51b49 100644 --- a/examples/simple_write.c +++ b/examples/simple_write.c @@ -4,14 +4,15 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#include +#include int main(int argc, char** argv) { const char* file_name = "test.hdf5"; 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_construct_array_t(&array, 1000, BSP_INT32); int* values = (int*) array.data; @@ -22,7 +23,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..b3b7db2 100644 --- a/include/binsparse/array.h +++ b/include/binsparse/array.h @@ -6,9 +6,9 @@ #pragma once -#include #include #include +#include #include #include #include @@ -20,71 +20,90 @@ 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_allocator(bsp_array_t* array, + bsp_allocator_t allocator) { + array->data = NULL; + array->size = 0; + array->allocator = allocator; + return BSP_SUCCESS; } -static inline bsp_array_t bsp_construct_array_t(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); - 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 = allocator; + array->data = array->allocator.malloc(byte_size); + + if (array->data == NULL) { + return BSP_ERROR_MEMORY; + } - return array; + 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_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); +} - return array; +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 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..189aa7c 100644 --- a/include/binsparse/binsparse.h +++ b/include/binsparse/binsparse.h @@ -11,8 +11,8 @@ #include #include #include +#include #include -#include #include #include #include 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..2aef4a5 --- /dev/null +++ b/include/binsparse/binsparse_hdf5.h @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2024 Binsparse Developers + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#pragma once + +#include +#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/convert_matrix.h b/include/binsparse/convert_matrix.h index 2f6c332..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; @@ -37,17 +38,36 @@ 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) { + 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 // 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); + bsp_matrix_t empty_result; + bsp_construct_default_matrix_t(&empty_result); + return empty_result; + } } 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); + 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++) { size_t index; bsp_array_read(matrix.indices_1, i, index); @@ -55,7 +75,14 @@ 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); + bsp_matrix_t empty_result; + bsp_construct_default_matrix_t(&empty_result); + return empty_result; + } for (size_t i = 0; i < matrix.nrows; i++) { size_t row_begin, row_end; @@ -77,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; @@ -109,12 +137,32 @@ 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) { + bsp_matrix_t empty_result; + bsp_construct_default_matrix_t(&empty_result); + return empty_result; + } 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); + bsp_matrix_t empty_result; + bsp_construct_default_matrix_t(&empty_result); + return empty_result; + } } 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); + bsp_matrix_t empty_result; + bsp_construct_default_matrix_t(&empty_result); + return empty_result; + } for (size_t i = 0; i < matrix.nnz; i++) { size_t index; @@ -123,8 +171,15 @@ 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); + 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/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/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/error.h b/include/binsparse/error.h new file mode 100644 index 0000000..73b3f92 --- /dev/null +++ b/include/binsparse/error.h @@ -0,0 +1,83 @@ +/* + * SPDX-FileCopyrightText: 2024 Binsparse Developers + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#pragma once + +#include +#include + +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"; + } +} + +/** + * 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/generate.h b/include/binsparse/generate.h index 93205a1..b625669 100644 --- a/include/binsparse/generate.h +++ b/include/binsparse/generate.h @@ -70,13 +70,31 @@ 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; - 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 = 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..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 @@ -21,10 +22,14 @@ // 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) { - array = bsp_complex_array_to_fp(array); + bsp_error_t error = bsp_complex_array_to_fp(&array); + if (error != BSP_SUCCESS) { + return BSP_ERROR_TYPE; + } } hsize_t hsize[1]; @@ -56,7 +61,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); @@ -64,30 +72,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) { - return bsp_construct_default_array_t(); + H5Dclose(dset); + bsp_construct_default_array_t(array); + return BSP_ERROR_IO; } hid_t fspace = H5Dget_space(dset); if (fspace == H5I_INVALID_HID) { - return bsp_construct_default_array_t(); + H5Sclose(fspace); + H5Dclose(dset); + bsp_construct_default_array_t(array); + return BSP_ERROR_IO; } hsize_t dims[3]; @@ -95,7 +114,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) { - return bsp_construct_default_array_t(); + H5Sclose(fspace); + H5Dclose(dset); + bsp_construct_default_array_t(array); + return BSP_ERROR_IO; } hid_t hdf5_type = H5Dget_type(dset); @@ -104,10 +126,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)); @@ -131,17 +152,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); @@ -149,17 +170,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); } @@ -169,21 +190,27 @@ 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_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) { - return bsp_construct_default_array_t(); + bsp_construct_default_array_t_allocator(array, allocator); + return BSP_ERROR_IO; } hid_t fspace = H5Dget_space(dset); if (fspace == H5I_INVALID_HID) { - return bsp_construct_default_array_t(); + H5Sclose(fspace); + H5Dclose(dset); + bsp_construct_default_array_t_allocator(array, allocator); + return BSP_ERROR_IO; } hsize_t dims[3]; @@ -191,29 +218,48 @@ 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(); + H5Dclose(dset); + H5Sclose(fspace); + bsp_construct_default_array_t_allocator(array, allocator); + 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_array_t(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_allocator(array, allocator); + 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) { - return bsp_construct_default_array_t(); + bsp_destroy_array_t(array); + H5Sclose(fspace); + H5Dclose(dset); + bsp_construct_default_array_t_allocator(array, allocator); + 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_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); H5Tset_size(strtype, strlen(string)); H5Tset_cset(strtype, H5T_CSET_UTF8); @@ -227,23 +273,34 @@ 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_allocator(char** string, hid_t f, const char* label, + bsp_allocator_t allocator) { 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*) allocator.malloc(size + 1); - H5Aread(attribute, strtype, string); + H5Aread(attribute, strtype, *string); H5Aclose(attribute); H5Tclose(strtype); - return string; + 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 db858c7..bedb3ed 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,27 @@ 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_allocator(bsp_matrix_t* matrix, + bsp_allocator_t allocator) { + bsp_construct_default_array_t(&matrix->values); + 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_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_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); + 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_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 c823a40..2c64ef8 100644 --- a/include/binsparse/matrix_market/matrix_market_read.h +++ b/include/binsparse/matrix_market/matrix_market_read.h @@ -32,13 +32,18 @@ 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; 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; @@ -104,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; } @@ -125,7 +132,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; @@ -135,14 +143,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 +262,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,19 +295,21 @@ 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; } free(indices); fclose(f); + bsp_destroy_mm_metadata(&metadata); + return matrix; } @@ -274,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); @@ -304,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/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/read_matrix.h b/include/binsparse/read_matrix.h index 948ffba..523f34c 100644 --- a/include/binsparse/read_matrix.h +++ b/include/binsparse/read_matrix.h @@ -6,26 +6,24 @@ #pragma once +#include +#include + #ifdef __cplusplus extern "C" { #endif -#ifdef BSP_USE_HDF5 -#include - -#if __STDC_VERSION__ >= 201112L -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); -#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_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/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/include/binsparse/types.h b/include/binsparse/types.h index e3c9e53..b487444 100644 --- a/include/binsparse/types.h +++ b/include/binsparse/types.h @@ -7,7 +7,8 @@ #pragma once #include -#include +#include +#include typedef enum bsp_type_t { BSP_UINT8 = 0, @@ -90,204 +91,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) { diff --git a/include/binsparse/write_matrix.h b/include/binsparse/write_matrix.h index 53b89f5..e535569 100644 --- a/include/binsparse/write_matrix.h +++ b/include/binsparse/write_matrix.h @@ -13,17 +13,10 @@ extern "C" { // TODO: make cJSON optional. #include -#include -#ifdef BSP_USE_HDF5 -#include - -int 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, const char* user_json, + int compression_level); #ifdef __cplusplus } diff --git a/src/read_matrix.c b/src/read_matrix.c index 464210c..e06a82b 100644 --- a/src/read_matrix.c +++ b/src/read_matrix.c @@ -5,21 +5,30 @@ */ #include +#include +#include +#include #include #include #include #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(); - - char* json_string = bsp_read_attribute(f, (char*) "binsparse"); +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 error; + } 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)); @@ -40,7 +49,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"); @@ -62,64 +71,97 @@ 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")) { - 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 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) { - matrix.values = bsp_fp_array_to_complex(matrix.values); + bsp_error_t error = bsp_fp_array_to_complex(&matrix->values); + if (error != BSP_SUCCESS) { + // TODO: handle error + return error; + } } } 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 error; + } } 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 error; + } } 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 error; + } } if (cJSON_HasObjectItem(binsparse, "structure")) { 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(hid_t f) { - bsp_matrix_t matrix = bsp_construct_default_matrix_t(); +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_read_attribute(f, (char*) "binsparse"); + char* json_string; + bsp_error_t error = bsp_read_attribute_allocator( + &json_string, f, (char*) "binsparse", allocator); + if (error != BSP_SUCCESS) { + return error; + } cJSON* j = cJSON_Parse(json_string); @@ -145,7 +187,7 @@ bsp_matrix_t bsp_read_matrix_from_group(hid_t f) { assert(format != 0); - matrix.format = format; + matrix->format = format; cJSON* nnz_ = cJSON_GetObjectItemCaseSensitive(binsparse, "number_of_stored_values"); @@ -167,54 +209,88 @@ bsp_matrix_t bsp_read_matrix_from_group(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")) { - matrix.values = bsp_read_array(f, (char*) "values"); + error = bsp_read_array_allocator(&matrix->values, f, (char*) "values", + allocator); + if (error != BSP_SUCCESS) { + free(json_string); + 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) { - matrix.values = bsp_fp_array_to_complex(matrix.values); + bsp_error_t error = bsp_fp_array_to_complex(&matrix->values); + if (error != BSP_SUCCESS) { + // TODO: handle error + return error; + } } } if (cJSON_HasObjectItem(data_types_, "indices_0")) { - matrix.indices_0 = bsp_read_array(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 error; + } } if (cJSON_HasObjectItem(data_types_, "indices_1")) { - matrix.indices_1 = bsp_read_array(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 error; + } } if (cJSON_HasObjectItem(data_types_, "pointers_to_1")) { - matrix.pointers_to_1 = bsp_read_array(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); + bsp_destroy_array_t(&matrix->indices_0); + bsp_destroy_array_t(&matrix->indices_1); + return error; + } } if (cJSON_HasObjectItem(binsparse, "structure")) { 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_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) { @@ -228,52 +304,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(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) { 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_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(g); + bsp_error_t error = + bsp_read_matrix_from_group_allocator(matrix, g, allocator); H5Gclose(g); H5Fclose(f); - return matrix; + return error; } } + +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); +} 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..d87244a 100644 --- a/src/write_matrix.c +++ b/src/write_matrix.c @@ -5,7 +5,8 @@ */ #include -#include +#include +#include #include #include #include @@ -22,7 +23,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); @@ -86,51 +88,75 @@ 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_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 (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; +} + +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; } -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_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_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; + } H5Fclose(f); } else { hid_t f; @@ -140,9 +166,28 @@ 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_cjson(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; +} + +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; } diff --git a/src/write_tensor.c b/src/write_tensor.c index 11a84ef..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(); @@ -53,8 +55,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 +87,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 +110,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 +122,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 +156,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 +191,5 @@ int bsp_write_tensor(const char* fname, bsp_tensor_t tensor, const char* group, H5Gclose(g); H5Fclose(f); } - return 0; + return BSP_SUCCESS; }