1use super::{Primitive, TensorBuffer, TensorDevice, TensorDeviceError, TensorElement};
2use ruda_tensor::{Backend, DType, FloatDType, IntDType, Slice, TensorData, read_sync};
3use std::ops::Range;
4
5pub(super) fn checked_length<T: TensorElement>(length: usize) -> Result<(), TensorDeviceError> {
6 let bytes = length.checked_mul(std::mem::size_of::<T>())
7 .ok_or(TensorDeviceError::InvalidBuffer("collective buffer byte length overflow"))?;
8 if bytes > isize::MAX as usize {
9 return Err(TensorDeviceError::InvalidBuffer("collective buffer exceeds addressable size"));
10 }
11 Ok(())
12}
13
14pub(super) fn checked_range(
15 total: usize,
16 offset: usize,
17 length: usize,
18) -> Result<Range<usize>, TensorDeviceError> {
19 let end = offset.checked_add(length)
20 .ok_or(TensorDeviceError::InvalidBuffer("collective element range overflow"))?;
21 if end > total {
22 return Err(TensorDeviceError::InvalidBuffer("collective element range out of bounds"));
23 }
24 Ok(offset..end)
25}
26
27impl<B: Backend> Primitive<B> {
28 pub(super) fn slice(self, range: Range<usize>) -> Self {
29 let slice = Slice::from(range);
30 match self {
31 Self::Float(value) => Self::Float(B::float_slice(value, &[slice])),
32 Self::Int(value) => Self::Int(B::int_slice(value, &[slice])),
33 }
34 }
35
36 pub(super) fn assign(self, range: Range<usize>, value: Self) -> Self {
37 let slice = Slice::from(range);
38 match (self, value) {
39 (Self::Float(tensor), Self::Float(value)) => {
40 Self::Float(B::float_slice_assign(tensor, &[slice], value))
41 }
42 (Self::Int(tensor), Self::Int(value)) => {
43 Self::Int(B::int_slice_assign(tensor, &[slice], value))
44 }
45 _ => unreachable!("typed collective buffer storage kind"),
46 }
47 }
48}
49
50impl<B: Backend> TensorDevice<B> {
51 pub fn allocate<T: TensorElement>(
52 &self,
53 length: usize,
54 ) -> Result<TensorBuffer<B, T>, TensorDeviceError> {
55 self.validate_type::<T>()?;
56 checked_length::<T>(length)?;
57 let shape = [length].into();
58 let value = match T::dtype() {
59 DType::F32 => Primitive::Float(B::float_empty(shape, &self.device, FloatDType::F32)),
60 DType::F16 => Primitive::Float(B::float_empty(shape, &self.device, FloatDType::F16)),
61 DType::BF16 => Primitive::Float(B::float_empty(shape, &self.device, FloatDType::BF16)),
62 DType::I32 => Primitive::Int(B::int_empty(shape, &self.device, IntDType::I32)),
63 DType::I64 => Primitive::Int(B::int_empty(shape, &self.device, IntDType::I64)),
64 DType::U8 => Primitive::Int(B::int_empty(shape, &self.device, IntDType::U8)),
65 DType::U32 => Primitive::Int(B::int_empty(shape, &self.device, IntDType::U32)),
66 _ => unreachable!("sealed collective element type"),
67 };
68 Ok(self.wrap(value, length))
69 }
70
71 pub(super) fn from_values<T: TensorElement>(&self, values: &[T]) -> Primitive<B> {
72 let data = TensorData::new(values.to_vec(), [values.len()]);
73 if matches!(T::dtype(), DType::U8 | DType::U32 | DType::I32 | DType::I64) {
74 Primitive::Int(B::int_from_data(data, &self.device))
75 } else {
76 Primitive::Float(B::float_from_data(data, &self.device))
77 }
78 }
79
80 pub fn write<T: TensorElement>(
81 &self,
82 buffer: &TensorBuffer<B, T>,
83 values: &[T],
84 ) -> Result<(), TensorDeviceError> {
85 if values.len() != buffer.length {
86 return Err(TensorDeviceError::InvalidBuffer("collective full write length mismatch"));
87 }
88 self.write_at(buffer, 0, values)
89 }
90
91 pub fn write_at<T: TensorElement>(
92 &self,
93 buffer: &TensorBuffer<B, T>,
94 offset: usize,
95 values: &[T],
96 ) -> Result<(), TensorDeviceError> {
97 self.validate_buffer(buffer)?;
98 let range = checked_range(buffer.length, offset, values.len())?;
99 if range.is_empty() {
100 return Ok(());
101 }
102 let incoming = self.from_values(values);
103 buffer.update(|current| {
104 if range.start == 0 && range.end == buffer.length {
105 incoming
106 } else {
107 current.assign(range, incoming)
108 }
109 })
110 }
111
112 pub fn download<T: TensorElement>(
113 &self,
114 buffer: &TensorBuffer<B, T>,
115 ) -> Result<Vec<T>, TensorDeviceError> {
116 self.download_at(buffer, 0, buffer.length)
117 }
118
119 pub fn download_at<T: TensorElement>(
120 &self,
121 buffer: &TensorBuffer<B, T>,
122 offset: usize,
123 length: usize,
124 ) -> Result<Vec<T>, TensorDeviceError> {
125 self.validate_buffer(buffer)?;
126 let range = checked_range(buffer.length, offset, length)?;
127 if range.is_empty() {
128 return Ok(Vec::new());
129 }
130 let mut value = buffer.snapshot()?;
131 if range.start != 0 || range.end != buffer.length {
132 value = value.slice(range);
133 }
134 let data = match value {
135 Primitive::Float(value) => read_sync(B::float_into_data(value))?,
136 Primitive::Int(value) => read_sync(B::int_into_data(value))?,
137 };
138 data.into_vec::<T>().map_err(|error| TensorDeviceError::Data(format!("{error:?}")))
139 }
140}