Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 46 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,62 @@ jobs:
strategy:
matrix:
include:
- cc: gcc-12
- id: gcc-12
cc: gcc-12
cxx: g++-12
- cc: clang
- id: clang
cc: clang
cxx: clang++
name: ${{ matrix.cc }}
name: Build (${{ matrix.cc }})
env:
CC: ${{ matrix.cc }}
CXX: ${{ matrix.cxx }}
steps:
- uses: actions/checkout@v4
- name: Install Julia
run: curl -fsSL https://install.julialang.org | sh -s -- -y
- name: CMake
run: |
sudo apt-get update
sudo apt-get install libhdf5-dev gcc-12 g++-12 clang
cmake -B build
- name: Build
run: VERBOSE=true make -C build -j `nproc`
- name: Archive Build
run: tar -czf build.tar.gz build
- uses: actions/upload-artifact@v4
with:
name: compliance-build-${{ matrix.id }}
path: build.tar.gz
retention-days: 1

test:
name: Test (${{ matrix.cc }})
needs: build
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
matrix:
include:
- id: gcc-12
cc: gcc-12
- id: clang
cc: clang
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: compliance-build-${{ matrix.id }}
- name: Restore Build
run: tar -xzf build.tar.gz
- name: Install Runtime Dependencies
run: |
sudo apt-get update
sudo apt-get install libhdf5-dev
- uses: prefix-dev/setup-pixi@v0.10.0
with:
run-install: false
cache: false
- name: Matrix Tests
run: ctest --test-dir ./build/test/bash
- name: Install Julia
Expand All @@ -56,3 +95,7 @@ jobs:
run: julia -e 'using Pkg; Pkg.add(["Finch", "HDF5"])'
- name: Tensor Tests
run: ctest --test-dir ./build/test/julia
- name: Compliance Tests
env:
BINSPARSE_BUILD_DIR: ${{ github.workspace }}/build
run: ./test/compliance/run-binsparse-tests.sh -q
18 changes: 18 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,22 @@ FetchContent_Declare(
)
FetchContent_MakeAvailable(cJSON)

FetchContent_Declare(
semver_c
GIT_REPOSITORY https://github.com/h2non/semver.c.git
GIT_TAG v1.0.0
)
FetchContent_MakeAvailable(semver_c)
add_library(binsparse_semver OBJECT ${semver_c_SOURCE_DIR}/semver.c)
set_target_properties(binsparse_semver PROPERTIES
POSITION_INDEPENDENT_CODE ON)
target_include_directories(binsparse_semver PRIVATE
$<BUILD_INTERFACE:${semver_c_SOURCE_DIR}>)
target_sources(binsparse PRIVATE $<TARGET_OBJECTS:binsparse_semver>)
target_sources(binsparse_dynamic PRIVATE $<TARGET_OBJECTS:binsparse_semver>)
target_include_directories(binsparse PRIVATE ${semver_c_SOURCE_DIR})
target_include_directories(binsparse_dynamic PRIVATE ${semver_c_SOURCE_DIR})

# Restore original BUILD_SHARED_LIBS setting
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_BACKUP})

Expand Down Expand Up @@ -122,6 +138,8 @@ install(FILES
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
enable_testing()

add_subdirectory(test/compliance)

if (ENABLE_SANITIZERS)
set(SANITIZER_FLAGS "-fsanitize=address,undefined")
target_compile_options(binsparse INTERFACE ${SANITIZER_FLAGS} -g -O1 -fno-omit-frame-pointer)
Expand Down
9 changes: 8 additions & 1 deletion examples/bsp-ls.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,14 @@ void print_group_info(hid_t g, const char* name) {
assert(cJSON_IsString(version_));
char* version_string = cJSON_GetStringValue(version_);

// TODO: check version.
error = bsp_check_version_compatible(version_string);
if (error != BSP_SUCCESS) {
printf("Unsupported Binsparse version %s: %s\n", version_string,
bsp_get_error_string(error));
cJSON_Delete(j);
free(json_string);
return;
}

cJSON* format_ = cJSON_GetObjectItemCaseSensitive(binsparse, "format");
assert(format_ != NULL);
Expand Down
11 changes: 7 additions & 4 deletions include/binsparse/array.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ bsp_construct_array_t_allocator(bsp_array_t* array, size_t size,
size_t byte_size = size * bsp_type_size(type);

array->allocator = allocator;
array->data = array->allocator.malloc(byte_size);
array->data = byte_size == 0 ? NULL : array->allocator.malloc(byte_size);

if (array->data == NULL) {
if (byte_size != 0 && array->data == NULL) {
return BSP_ERROR_MEMORY;
}

Expand All @@ -65,7 +65,8 @@ static inline bsp_error_t bsp_copy_construct_array_t(bsp_array_t* array,
return error;
}

memcpy(array->data, other.data, other.size * bsp_type_size(other.type));
if (other.size != 0)
memcpy(array->data, other.data, other.size * bsp_type_size(other.type));

return BSP_SUCCESS;
}
Expand Down Expand Up @@ -104,7 +105,9 @@ static inline bsp_error_t bsp_fp_array_to_complex(bsp_array_t* array) {
}

static inline void bsp_destroy_array_t(bsp_array_t* array) {
array->allocator.free(array->data);
if (array->data != NULL) {
array->allocator.free(array->data);
}
}

static inline bool bsp_array_equal(bsp_array_t x, bsp_array_t y) {
Expand Down
3 changes: 1 addition & 2 deletions include/binsparse/binsparse.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@

#pragma once

#define BINSPARSE_VERSION "0.1"

#include <binsparse/array.h>
#include <binsparse/convert_matrix.h>
#include <binsparse/detail/detail.h>
Expand All @@ -19,4 +17,5 @@
#include <binsparse/matrix_market/matrix_market_write.h>
#include <binsparse/minimize_values.h>
#include <binsparse/read_matrix.h>
#include <binsparse/version.h>
#include <binsparse/write_matrix.h>
13 changes: 13 additions & 0 deletions include/binsparse/binsparse_cJSON.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <binsparse/detail/allocator.h>
#include <binsparse/matrix.h>
#include <binsparse/tensor.h>

#include <cJSON/cJSON.h>

Expand All @@ -25,12 +26,24 @@ bsp_error_t bsp_write_matrix_cjson(const char* fname, bsp_matrix_t matrix,
const char* group, cJSON* user_json,
int compression_level);

bsp_error_t bsp_write_tensor_cjson(const char* fname, bsp_tensor_t tensor,
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 BSP_USE_HDF5
#include <hdf5.h>

bsp_error_t bsp_write_tensor_to_group_cjson(hid_t f, bsp_tensor_t tensor,
cJSON* user_json,
int compression_level);
#endif

#ifdef __cplusplus
}
#endif
3 changes: 1 addition & 2 deletions include/binsparse/detail/allocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,4 @@ typedef struct bsp_allocator_t {
void (*free)(void*);
} bsp_allocator_t;

const static bsp_allocator_t bsp_default_allocator = {.malloc = malloc,
.free = free};
const static bsp_allocator_t bsp_default_allocator = {malloc, free};
6 changes: 3 additions & 3 deletions include/binsparse/detail/hdf5_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ static inline hid_t bsp_get_hdf5_standard_type(bsp_type_t type) {
} else if (type == BSP_FLOAT64) {
return H5T_IEEE_F64LE;
} else if (type == BSP_BINT8) {
return H5T_STD_I8LE;
return H5T_STD_U8LE;
} else {
return H5I_INVALID_HID;
}
Expand Down Expand Up @@ -86,7 +86,7 @@ static inline bsp_type_t bsp_get_bsp_type(hid_t type) {
// 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 (type == BSP_INT8) {
if (sizeof(int8_t) == sizeof(char)) {
return H5T_NATIVE_CHAR;
} else if (sizeof(int8_t) == sizeof(short)) {
Expand Down Expand Up @@ -142,7 +142,7 @@ static inline hid_t bsp_get_hdf5_native_type(bsp_type_t type) {
} else {
assert(false);
}
} else if (type == BSP_UINT8) {
} else if (type == BSP_UINT8 || type == BSP_BINT8) {
if (sizeof(uint8_t) == sizeof(unsigned char)) {
return H5T_NATIVE_UCHAR;
} else if (sizeof(uint8_t) == sizeof(unsigned short)) {
Expand Down
73 changes: 46 additions & 27 deletions include/binsparse/hdf5_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,21 @@ static inline bsp_error_t bsp_write_array(hid_t f, const char* label,

hid_t dcpl = H5Pcreate(H5P_DATASET_CREATE);

// Choose 1 MiB, the default chunk cache size, as our chunk size.
size_t chunk_size = 1024 * 1024 / bsp_type_size(array.type);
if (array.size > 0) {
// Choose 1 MiB, the default chunk cache size, as our chunk size.
size_t chunk_size = 1024 * 1024 / bsp_type_size(array.type);

// If the dataset is smaller than the chunk size, cap the chunk size.
if (array.size < chunk_size) {
chunk_size = array.size;
}
// If the dataset is smaller than the chunk size, cap the chunk size.
if (array.size < chunk_size) {
chunk_size = array.size;
}

hsize[0] = chunk_size;
H5Pset_chunk(dcpl, 1, hsize);
hsize[0] = chunk_size;
H5Pset_chunk(dcpl, 1, hsize);

if (compression_level > 0) {
H5Pset_deflate(dcpl, compression_level);
if (compression_level > 0) {
H5Pset_deflate(dcpl, compression_level);
}
}

hid_t dset =
Expand All @@ -67,17 +69,18 @@ static inline bsp_error_t bsp_write_array(hid_t f, const char* label,
return BSP_ERROR_IO;
}

hid_t hdf5_native_type = bsp_get_hdf5_native_type(array.type);

hid_t r = H5Dwrite(dset, hdf5_native_type, H5S_ALL, fspace, H5P_DEFAULT,
array.data);
if (array.size > 0) {
hid_t hdf5_native_type = bsp_get_hdf5_native_type(array.type);
hid_t r = H5Dwrite(dset, hdf5_native_type, H5S_ALL, fspace, H5P_DEFAULT,
array.data);

if (r < 0) {
H5Dclose(dset);
H5Sclose(fspace);
H5Pclose(lcpl);
H5Pclose(dcpl);
return BSP_ERROR_IO;
if (r < 0) {
H5Dclose(dset);
H5Sclose(fspace);
H5Pclose(lcpl);
H5Pclose(dcpl);
return BSP_ERROR_IO;
}
}

H5Sclose(fspace);
Expand Down Expand Up @@ -123,12 +126,24 @@ static inline bsp_error_t bsp_read_array_parallel(bsp_array_t* array, hid_t f,
hid_t hdf5_type = H5Dget_type(dset);

bsp_type_t type = bsp_get_bsp_type(hdf5_type);
if (dims[0] == 0) {
array->type = type;
array->size = 0;
array->data = NULL;
array->allocator = bsp_shm_allocator;
H5Tclose(hdf5_type);
H5Sclose(fspace);
H5Dclose(dset);
return BSP_SUCCESS;
}

// Array will be written into a POSIX shared memory.
bsp_shm_t array_shm = bsp_shm_new(dims[0] * bsp_type_size(type));
array->type = type;
array->size = dims[0];
array->allocator = bsp_shm_allocator;
array->data = bsp_shm_attach(array_shm);
bsp_shm_delete(array_shm);

bsp_shm_t active_children_shm = bsp_shm_new(sizeof(_Atomic int));

Expand All @@ -152,11 +167,6 @@ static inline bsp_error_t bsp_read_array_parallel(bsp_array_t* array, hid_t f,
}
}

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)
? chunk_size * thread_num
Expand Down Expand Up @@ -237,8 +247,17 @@ static inline bsp_error_t bsp_read_array_allocator(bsp_array_t* array, hid_t f,
return BSP_ERROR_MEMORY;
}

herr_t status = H5Dread(dset, bsp_get_hdf5_native_type(type), H5S_ALL,
H5S_ALL, H5P_DEFAULT, array->data);
if (array->size == 0) {
H5Tclose(hdf5_type);
H5Sclose(fspace);
H5Dclose(dset);
return BSP_SUCCESS;
}

hid_t native_type = bsp_get_hdf5_native_type(type);
herr_t status =
H5Dread(dset, native_type, H5S_ALL, H5S_ALL, H5P_DEFAULT, array->data);
H5Tclose(hdf5_type);

if (status < 0) {
bsp_destroy_array_t(array);
Expand Down
21 changes: 21 additions & 0 deletions include/binsparse/version.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* SPDX-FileCopyrightText: 2026 Binsparse Developers
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#pragma once

#include <binsparse/error.h>

#define BINSPARSE_VERSION "0.1"

#ifdef __cplusplus
extern "C" {
#endif

bsp_error_t bsp_check_version_compatible(const char* version);

#ifdef __cplusplus
}
#endif
Loading
Loading