singe-kernel 0.1.0-alpha.4

Reusable CPU and GPU kernels.
Documentation
#![allow(dead_code)]

use crate::error::{Error, Result};

pub fn checked_i32_value(value: usize) -> Result<i32> {
    i32::try_from(value).map_err(|_| Error::LengthExceedsI32)
}

pub fn checked_element_count(rows: usize, cols: usize) -> Result<usize> {
    rows.checked_mul(cols).ok_or(Error::SizeOverflow)
}

pub fn ensure_len(actual: usize, expected: usize) -> Result<()> {
    ensure_named_len("length", actual, expected)
}

pub fn ensure_named_len(name: &'static str, actual: usize, expected: usize) -> Result<()> {
    if actual == expected {
        Ok(())
    } else {
        Err(Error::LengthMismatchFor {
            name: name.into(),
            expected,
            actual,
        })
    }
}

pub fn checked_rank4_len(dimensions: [usize; 4]) -> Result<usize> {
    let len = checked_element_count(dimensions[0], dimensions[1])?;
    let len = checked_element_count(len, dimensions[2])?;
    checked_element_count(len, dimensions[3])
}

pub fn ensure_rank4_reach(
    input_len: usize,
    dimensions: [usize; 4],
    strides: [usize; 4],
) -> Result<()> {
    ensure_rank4_reach_named("input", input_len, dimensions, strides)
}

pub fn ensure_rank4_reach_named(
    name: &'static str,
    input_len: usize,
    dimensions: [usize; 4],
    strides: [usize; 4],
) -> Result<()> {
    ensure_rank_reach(name, input_len, dimensions, strides)
}

pub fn ensure_rank3_reach(
    input_len: usize,
    dimensions: [usize; 3],
    strides: [usize; 3],
) -> Result<()> {
    ensure_rank3_reach_named("input", input_len, dimensions, strides)
}

pub fn ensure_rank3_reach_named(
    name: &'static str,
    input_len: usize,
    dimensions: [usize; 3],
    strides: [usize; 3],
) -> Result<()> {
    ensure_rank_reach(name, input_len, dimensions, strides)
}

pub fn ensure_rank2_reach(
    input_len: usize,
    dimensions: [usize; 2],
    strides: [usize; 2],
) -> Result<()> {
    ensure_rank2_reach_named("input", input_len, dimensions, strides)
}

pub fn ensure_rank2_reach_named(
    name: &'static str,
    input_len: usize,
    dimensions: [usize; 2],
    strides: [usize; 2],
) -> Result<()> {
    ensure_rank_reach(name, input_len, dimensions, strides)
}

fn ensure_rank_reach<const R: usize>(
    name: &'static str,
    input_len: usize,
    dimensions: [usize; R],
    strides: [usize; R],
) -> Result<()> {
    if dimensions.contains(&0) {
        return Ok(());
    }
    let mut max_offset = 0usize;
    for axis in 0..R {
        let span = dimensions[axis]
            .checked_sub(1)
            .ok_or(Error::SizeOverflow)?
            .checked_mul(strides[axis])
            .ok_or(Error::SizeOverflow)?;
        max_offset = max_offset.checked_add(span).ok_or(Error::SizeOverflow)?;
    }
    let required = max_offset.checked_add(1).ok_or(Error::SizeOverflow)?;
    if required <= input_len {
        Ok(())
    } else {
        Err(Error::RankReachMismatch {
            name: name.into(),
            rank: R,
            required,
            actual: input_len,
        })
    }
}

pub fn checked_rank4_i32(values: [usize; 4]) -> Result<[i32; 4]> {
    Ok([
        checked_i32_value(values[0])?,
        checked_i32_value(values[1])?,
        checked_i32_value(values[2])?,
        checked_i32_value(values[3])?,
    ])
}

pub fn checked_rank3_i32(values: [usize; 3]) -> Result<[i32; 3]> {
    Ok([
        checked_i32_value(values[0])?,
        checked_i32_value(values[1])?,
        checked_i32_value(values[2])?,
    ])
}

pub fn checked_rank2_i32(values: [usize; 2]) -> Result<[i32; 2]> {
    Ok([checked_i32_value(values[0])?, checked_i32_value(values[1])?])
}

pub fn ensure_len_at_least(actual: usize, minimum: usize) -> Result<()> {
    ensure_named_len_at_least("length", actual, minimum)
}

pub fn ensure_named_len_at_least(name: &'static str, actual: usize, minimum: usize) -> Result<()> {
    if actual < minimum {
        Err(Error::LengthTooShort {
            name: name.into(),
            minimum,
            actual,
        })
    } else {
        Ok(())
    }
}