use crate::EngineError;
pub const HOST_ALIGN: usize = 64;
pub const DEVICE_ALIGN: usize = 128;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BufferLayout {
len: usize,
align: usize,
}
impl BufferLayout {
pub fn new(len: usize, align: usize) -> Result<Self, EngineError> {
if align == 0 || !align.is_power_of_two() {
return Err(EngineError::allocation(
len,
align,
"alignment must be a non-zero power of two",
));
}
Ok(Self { len, align })
}
pub const fn host(len: usize) -> Self {
Self {
len,
align: HOST_ALIGN,
}
}
pub const fn device(len: usize) -> Self {
Self {
len,
align: DEVICE_ALIGN,
}
}
pub const fn len(&self) -> usize {
self.len
}
pub const fn is_empty(&self) -> bool {
self.len == 0
}
pub const fn align(&self) -> usize {
self.align
}
pub const fn padded_len(&self) -> usize {
let rem = self.len % self.align;
if rem == 0 {
self.len
} else {
self.len + (self.align - rem)
}
}
pub const fn is_aligned(&self, addr: usize) -> bool {
addr.is_multiple_of(self.align)
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn rejects_bad_alignment() {
assert!(BufferLayout::new(8, 0).is_err());
assert!(BufferLayout::new(8, 48).is_err());
assert!(BufferLayout::new(8, 64).is_ok());
}
#[test]
fn pads_to_alignment() {
assert_eq!(BufferLayout::host(0).padded_len(), 0);
assert_eq!(BufferLayout::host(1).padded_len(), 64);
assert_eq!(BufferLayout::host(64).padded_len(), 64);
assert_eq!(BufferLayout::device(129).padded_len(), 256);
}
#[test]
fn checks_addresses() {
assert!(BufferLayout::device(1).is_aligned(0x1000));
assert!(!BufferLayout::device(1).is_aligned(0x1040));
assert!(BufferLayout::host(1).is_aligned(0x1040));
}
}