diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..aec7249 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,141 @@ +name: HMLL Build Workflow + +on: + workflow_call: + inputs: + os: + description: 'Operating system to run on' + required: true + type: string + compiler-cc: + description: 'C compiler' + required: true + type: string + compiler-cxx: + description: 'C++ compiler' + required: true + type: string + build-type: + description: 'CMake build type (Release, Debug, RelWithDebInfo)' + required: false + type: string + default: 'Debug' + static-build: + description: 'Build static library' + required: false + type: boolean + default: false + enable-cuda: + description: 'Enable CUDA support' + required: false + type: boolean + default: false + cuda-version: + description: 'CUDA version to use' + required: false + type: string + default: '12.2.0' + enable-python: + description: 'Enable Python bindings' + required: false + type: boolean + default: false + python-version: + description: 'Python version to use' + required: false + type: string + default: '3.11' + enable-rust: + description: 'Enable Rust bindings' + required: false + type: boolean + default: false + rust-toolchain: + description: 'Rust toolchain version' + required: false + type: string + default: 'stable' + enable-sanitizers: + description: 'Enable sanitizers' + required: false + type: boolean + default: true + enable-safetensors: + description: 'Enable safetensors support' + required: false + type: boolean + default: true + run-tests: + description: 'Run tests after build' + required: false + type: boolean + default: true + +jobs: + build: + runs-on: ${{ inputs.os }} + + steps: + - uses: actions/checkout@v6 + + - name: Setup CUDA + if: inputs.enable-cuda + uses: Jimver/cuda-toolkit@v0.2.30 + with: + cuda: ${{ inputs.cuda-version }} + method: 'network' + sub-packages: '["nvcc"]' + + - name: Setup Python + if: inputs.enable-python + uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python-version }} + + - name: Setup Rust + if: inputs.enable-rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: ${{ inputs.rust-toolchain }} + + - name: Install Python dependencies + if: inputs.enable-python + run: | + python -m pip install --upgrade pip + pip install pytest numpy + + - name: Configure CMake + env: + CC: ${{ inputs.compiler-cc }} + CXX: ${{ inputs.compiler-cxx }} + run: | + cmake -B ${{ github.workspace }}/build \ + -DCMAKE_BUILD_TYPE=${{ inputs.build-type }} \ + -DHMLL_BUILD_STATIC=${{ inputs.static-build && 'ON' || 'OFF' }} \ + -DHMLL_BUILD_TESTS=${{ inputs.run-tests && 'ON' || 'OFF' }} \ + -DHMLL_ENABLE_SANITIZERS=${{ inputs.enable-sanitizers && 'ON' || 'OFF' }} \ + -DHMLL_ENABLE_SAFETENSORS=${{ inputs.enable-safetensors && 'ON' || 'OFF' }} \ + ${{ inputs.enable-cuda && '-DHMLL_ENABLE_CUDA=ON' || '' }} + + - name: Build C++ Library + run: cmake --build ${{ github.workspace }}/build --config ${{ inputs.build-type }} + + - name: Build Rust Bindings + if: inputs.enable-rust + working-directory: lib/rust/hmll + run: cargo build --release + + - name: Test C++ Library + if: inputs.run-tests + working-directory: ${{ github.workspace }}/build + run: ctest -C ${{ inputs.build-type }} --output-on-failure + + - name: Test Rust Bindings + if: inputs.enable-rust && inputs.run-tests + working-directory: lib/rust/hmll + run: cargo test + + - name: Test Python Bindings + if: inputs.enable-python && inputs.run-tests + run: | + python examples/read_with_hmll.py || echo "Python example test - define proper test suite" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..998348f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,85 @@ +name: CI + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + cpu-build-matrix: + name: CPU Build (${{ matrix.os }}, ${{ matrix.compiler.cc }}, static=${{ matrix.static }}) + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest, ubuntu-24.04-arm ] + compiler: [ + { cc: gcc, cxx: g++ }, + { cc: clang, cxx: clang++ } + ] + static: [ false, true ] + uses: ./.github/workflows/build.yml + with: + os: ${{ matrix.os }} + compiler-cc: ${{ matrix.compiler.cc }} + compiler-cxx: ${{ matrix.compiler.cxx }} + build-type: Debug + static-build: ${{ matrix.static }} + enable-sanitizers: true + enable-safetensors: true + run-tests: true + + cuda-build: + name: CUDA Build (${{ matrix.os }}, ${{ matrix.cuda-version }}) + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest ] + cuda-version: [ '12.6.0', '12.8.0', '13.0.0'] + uses: ./.github/workflows/build.yml + with: + os: ${{ matrix.os }} + compiler-cc: clang + compiler-cxx: clang++ + build-type: Debug + enable-cuda: true + cuda-version: ${{ matrix.cuda-version }} + enable-safetensors: true + enable-sanitizers: true + run-tests: false + + rust-bindings: + name: Rust Bindings (${{ matrix.os }}) + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest, ubuntu-24.04-arm ] + rust-toolchain: [ stable, nightly ] + uses: ./.github/workflows/build.yml + with: + os: ${{ matrix.os }} + compiler-cc: gcc + compiler-cxx: g++ + build-type: Debug + enable-rust: true + rust-toolchain: ${{ matrix.rust-toolchain }} + enable-safetensors: true + run-tests: true + + python-bindings: + name: Python Bindings (${{ matrix.os }}, Python ${{ matrix.python-version }}) + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest, ubuntu-24.04-arm ] + python-version: [ '3.11', '3.12', '3.13', '3.13t' ] + uses: ./.github/workflows/build.yml + with: + os: ${{ matrix.os }} + compiler-cc: gcc + compiler-cxx: g++ + build-type: Debug + enable-python: true + python-version: ${{ matrix.python-version }} + enable-safetensors: true + run-tests: true diff --git a/.github/workflows/cmake.yaml b/.github/workflows/cmake.yaml deleted file mode 100644 index 45cdc99..0000000 --- a/.github/workflows/cmake.yaml +++ /dev/null @@ -1,43 +0,0 @@ -name: Build hmll library - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -env: - # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) - BUILD_TYPE: Debug - -jobs: - build: - strategy: - matrix: - os: [ ubuntu-latest, ubuntu-24.04-arm ] - compiler: [ - { cc: gcc, cxx: g++ }, - { cc: clang, cxx: clang++ } - ] - static: [OFF, ON] - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v6 - - name: Configure CMake - env: - CC: ${{ matrix.compiler.cc }} - CXX: ${{ matrix.compiler.cxx }} - run: cmake -B ${{ github.workspace }}/build \ - -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ - -DHMLL_BUILD_STATIC=${{ matrix.static }} \ - -DHMLL_BUILD_TESTS=ON \ - -DHMLL_ENABLE_SANITIZERS=ON \ - -DHMLL_ENABLE_SAFETENSORS=ON - - - name: Build - run: cmake --build ${{ github.workspace }}/build --config ${{env.BUILD_TYPE}} - - - name: Test - working-directory: ${{ github.workspace }}/build - run: ctest -C ${{ env.BUILD_TYPE }} diff --git a/.github/workflows/python.yaml b/.github/workflows/python.yaml deleted file mode 100644 index f387b10..0000000 --- a/.github/workflows/python.yaml +++ /dev/null @@ -1,42 +0,0 @@ -name: Build pyhmll library - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -env: - # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) - BUILD_TYPE: Debug - -jobs: - build: - strategy: - matrix: - os: [ ubuntu-latest, ubuntu-24.04-arm ] - compiler: [ - { cc: gcc, cxx: g++ }, - { cc: clang, cxx: clang++ } - ] - python: [ 3.12, 3.13, 3.13t ] - - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v6 - - uses: actions/setup-python@v6 - with: - python-version: ${{ matrix.python }} - - name: Configure CMake - env: - CC: ${{ matrix.compiler.cc }} - CXX: ${{ matrix.compiler.cxx }} - run: cmake -B ${{ github.workspace }}/build \ - -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ - -DHMLL_ENABLE_PYTHON=ON \ - -DHMLL_BUILD_TESTS=ON \ - -DHMLL_ENABLE_SANITIZERS=ON - - - name: Build - # Build your program with the given configuration - run: cmake --build ${{ github.workspace }}/build --config ${{env.BUILD_TYPE}} diff --git a/lib/loader.c b/lib/loader.c index 9ad7988..8c88bfe 100644 --- a/lib/loader.c +++ b/lib/loader.c @@ -52,7 +52,7 @@ struct hmll_range hmll_fetch_tensor(struct hmll *ctx, const struct hmll_registry return (struct hmll_range){0}; const struct hmll_lookup_result lookup = hmll_lookup_tensor(ctx, registry, name); - if (lookup.specs == HMLL_FALSE) { + if (lookup.specs == NULL) { ctx->error = HMLL_ERR(HMLL_ERR_TENSOR_NOT_FOUND); return (struct hmll_range){0}; } diff --git a/lib/rust/hmll-sys/build.rs b/lib/rust/hmll-sys/build.rs index a9f56ac..1f17924 100644 --- a/lib/rust/hmll-sys/build.rs +++ b/lib/rust/hmll-sys/build.rs @@ -113,7 +113,7 @@ fn main() { .derive_default(true) .derive_copy(true) .derive_eq(true) - .derive_hash(true) + .no_partialeq("hmll_loader") .impl_debug(true) .prepend_enum_name(false) .size_t_is_usize(true) diff --git a/lib/rust/hmll-sys/src/lib.rs b/lib/rust/hmll-sys/src/lib.rs new file mode 100644 index 0000000..6f5a1b1 --- /dev/null +++ b/lib/rust/hmll-sys/src/lib.rs @@ -0,0 +1,142 @@ +//! Low-level FFI bindings to the hmll library. +//! +//! This crate provides direct FFI bindings to the C library, generated using bindgen. +//! For a safe, idiomatic Rust API, use the `hmll` crate instead. + +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(dead_code)] + +// Include the generated bindings +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +// Optimized helper functions for zero-cost error checking + +/// Check if an error represents success (hot path - inline always) +#[inline(always)] +pub const fn hmll_is_success(error: hmll_error) -> bool { + error.code == HMLL_ERR_SUCCESS && error.sys_err == 0 +} + +/// Check if an error represents failure (hot path - inline always) +#[inline(always)] +pub const fn hmll_check_error(res: hmll_error) -> bool { + res.code != HMLL_ERR_SUCCESS || res.sys_err != 0 +} + +/// Get error code as u32 (hot path - inline always) +#[inline(always)] +pub const fn hmll_error_code(error: hmll_error) -> u32 { + error.code +} + +/// Get system error as i32 (hot path - inline always) +#[inline(always)] +pub const fn hmll_sys_error(error: hmll_error) -> i32 { + error.sys_err +} + +/// Check if device is CPU (hot path - inline always) +#[inline(always)] +pub const fn hmll_is_cpu(device: hmll_device) -> bool { + device == HMLL_DEVICE_CPU +} + +/// Check if device is CUDA (hot path - inline always) +#[inline(always)] +pub const fn hmll_is_cuda(device: hmll_device) -> bool { + device == HMLL_DEVICE_CUDA +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_error_success() { + let success_err = hmll_error { + code: HMLL_ERR_SUCCESS, + sys_err: 0, + }; + assert!(hmll_is_success(success_err)); + assert!(!hmll_check_error(success_err)); + } + + #[test] + fn test_error_failure() { + let fail_err = hmll_error { + code: HMLL_ERR_IO_ERROR, + sys_err: 0, + }; + assert!(!hmll_is_success(fail_err)); + assert!(hmll_check_error(fail_err)); + } + + #[test] + fn test_error_system() { + let sys_err = hmll_error { + code: HMLL_ERR_SUCCESS, + sys_err: -1, + }; + assert!(!hmll_is_success(sys_err)); + assert!(hmll_check_error(sys_err)); + } + + #[test] + fn test_device_checks() { + assert!(hmll_is_cpu(HMLL_DEVICE_CPU)); + assert!(!hmll_is_cuda(HMLL_DEVICE_CPU)); + assert!(hmll_is_cuda(HMLL_DEVICE_CUDA)); + assert!(!hmll_is_cpu(HMLL_DEVICE_CUDA)); + } + + #[test] + fn test_error_getters() { + let err = hmll_error { + code: HMLL_ERR_IO_ERROR, + sys_err: 42, + }; + assert_eq!(hmll_error_code(err), HMLL_ERR_IO_ERROR); + assert_eq!(hmll_sys_error(err), 42); + } + + #[test] + fn test_constants() { + assert_eq!(HMLL_ERR_SUCCESS, 0); + assert_eq!(HMLL_DEVICE_CPU, 0); + assert_eq!(HMLL_DEVICE_CUDA, 1); + } + + #[test] + fn test_source_size() { + let source = hmll_source { + fd: -1, + size: 1024, + }; + assert_eq!(source.size, 1024); + assert_eq!(source.fd, -1); + } + + #[test] + fn test_range_creation() { + let range = hmll_range { + start: 0, + end: 100, + }; + assert_eq!(range.start, 0); + assert_eq!(range.end, 100); + } + + #[test] + fn test_iobuf_creation() { + let iobuf = hmll_iobuf { + size: 4096, + ptr: std::ptr::null_mut(), + device: HMLL_DEVICE_CPU, + }; + assert_eq!(iobuf.size, 4096); + assert!(iobuf.ptr.is_null()); + assert_eq!(iobuf.device, HMLL_DEVICE_CPU); + } +} diff --git a/lib/safetensors.c b/lib/safetensors.c index 9d3de31..6ef4a87 100644 --- a/lib/safetensors.c +++ b/lib/safetensors.c @@ -322,4 +322,4 @@ struct hmll_error hmll_safetensors_populate_registry( if (hmll_check(ctx->error)) return ctx->error; if (reg->num_tensors == 0) reg->num_tensors = tidx; return HMLL_OK; -} \ No newline at end of file +}