1use crate::Device;
18use rlx_ir::{DType, Shape};
19
20#[derive(Debug, Clone)]
22pub struct Buffer {
23 bytes: Vec<u8>,
24 shape: Shape,
25 device: Device,
26}
27
28impl Buffer {
29 pub fn new_host(shape: Shape, data: Vec<u8>) -> Self {
34 Self {
35 bytes: data,
36 shape,
37 device: Device::Cpu,
38 }
39 }
40
41 pub fn zeros(shape: Shape) -> Self {
43 let n = shape.size_bytes().unwrap_or(0);
44 Self {
45 bytes: vec![0u8; n],
46 shape,
47 device: Device::Cpu,
48 }
49 }
50
51 pub fn shape(&self) -> &Shape {
52 &self.shape
53 }
54 pub fn device(&self) -> Device {
55 self.device
56 }
57 pub fn dtype(&self) -> DType {
58 self.shape.dtype()
59 }
60 pub fn num_elements(&self) -> usize {
61 self.shape.num_elements().unwrap_or(0)
62 }
63 pub fn byte_size(&self) -> usize {
64 self.bytes.len()
65 }
66
67 pub fn as_f32(&self) -> &[f32] {
70 assert_eq!(self.dtype(), DType::F32, "as_f32 on non-F32 buffer");
71 let n = self.num_elements();
72 unsafe { std::slice::from_raw_parts(self.bytes.as_ptr() as *const f32, n) }
73 }
74
75 pub fn as_f32_mut(&mut self) -> &mut [f32] {
76 assert_eq!(self.dtype(), DType::F32, "as_f32_mut on non-F32 buffer");
77 let n = self.num_elements();
78 unsafe { std::slice::from_raw_parts_mut(self.bytes.as_mut_ptr() as *mut f32, n) }
79 }
80
81 pub fn to_device(self, device: Device) -> Self {
86 match (self.device, device) {
87 (a, b) if a == b => self,
88 (Device::Cpu, Device::Metal) => Self {
89 device: Device::Metal,
90 ..self
91 },
92 (Device::Metal, Device::Cpu) => Self {
93 device: Device::Cpu,
94 ..self
95 },
96 _ => self,
97 }
98 }
99
100 pub fn to_host(self) -> Self {
103 self.to_device(Device::Cpu)
104 }
105
106 pub fn host_bytes(&self) -> &[u8] {
110 assert_eq!(
111 self.device,
112 Device::Cpu,
113 "host_bytes on non-host buffer; call .to_host() first"
114 );
115 &self.bytes
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn zeros_initializes() {
125 let b = Buffer::zeros(Shape::new(&[2, 3], DType::F32));
126 assert_eq!(b.num_elements(), 6);
127 assert_eq!(b.byte_size(), 24);
128 for v in b.as_f32() {
129 assert_eq!(*v, 0.0);
130 }
131 }
132
133 #[test]
134 fn dtype_mismatch_panics() {
135 let b = Buffer::zeros(Shape::new(&[4], DType::I32));
136 let result = std::panic::catch_unwind(|| b.as_f32());
137 assert!(result.is_err());
138 }
139
140 #[test]
141 fn to_device_round_trip() {
142 let b = Buffer::zeros(Shape::new(&[4], DType::F32));
143 let on_metal = b.to_device(Device::Metal);
144 assert_eq!(on_metal.device(), Device::Metal);
145 let back = on_metal.to_host();
146 assert_eq!(back.device(), Device::Cpu);
147 }
148}