1use crate::EngineError;
4
5pub const HOST_ALIGN: usize = 64;
7
8pub const DEVICE_ALIGN: usize = 128;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub struct BufferLayout {
14 len: usize,
15 align: usize,
16}
17
18impl BufferLayout {
19 pub fn new(len: usize, align: usize) -> Result<Self, EngineError> {
21 if align == 0 || !align.is_power_of_two() {
22 return Err(EngineError::allocation(
23 len,
24 align,
25 "alignment must be a non-zero power of two",
26 ));
27 }
28 Ok(Self { len, align })
29 }
30
31 pub const fn host(len: usize) -> Self {
33 Self {
34 len,
35 align: HOST_ALIGN,
36 }
37 }
38
39 pub const fn device(len: usize) -> Self {
41 Self {
42 len,
43 align: DEVICE_ALIGN,
44 }
45 }
46
47 pub const fn len(&self) -> usize {
49 self.len
50 }
51
52 pub const fn is_empty(&self) -> bool {
54 self.len == 0
55 }
56
57 pub const fn align(&self) -> usize {
59 self.align
60 }
61
62 pub const fn padded_len(&self) -> usize {
64 let rem = self.len % self.align;
65 if rem == 0 {
66 self.len
67 } else {
68 self.len + (self.align - rem)
69 }
70 }
71
72 pub const fn is_aligned(&self, addr: usize) -> bool {
74 addr.is_multiple_of(self.align)
75 }
76}
77
78#[cfg(test)]
79#[allow(clippy::unwrap_used, clippy::expect_used)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn rejects_bad_alignment() {
85 assert!(BufferLayout::new(8, 0).is_err());
86 assert!(BufferLayout::new(8, 48).is_err());
87 assert!(BufferLayout::new(8, 64).is_ok());
88 }
89
90 #[test]
91 fn pads_to_alignment() {
92 assert_eq!(BufferLayout::host(0).padded_len(), 0);
93 assert_eq!(BufferLayout::host(1).padded_len(), 64);
94 assert_eq!(BufferLayout::host(64).padded_len(), 64);
95 assert_eq!(BufferLayout::device(129).padded_len(), 256);
96 }
97
98 #[test]
99 fn checks_addresses() {
100 assert!(BufferLayout::device(1).is_aligned(0x1000));
101 assert!(!BufferLayout::device(1).is_aligned(0x1040));
102 assert!(BufferLayout::host(1).is_aligned(0x1040));
103 }
104}