1use std::borrow::Cow;
2use std::fmt;
3use std::sync::Arc;
4
5use super::{ElementType, VNextError, WeightComponentSpec, WeightId};
6
7pub unsafe trait StableHostMemory: Send + Sync + 'static {
18 fn stable_bytes(&self) -> &[u8];
19}
20
21#[derive(Clone)]
23pub struct RetainedHostMemoryRegion {
24 owner: Arc<dyn StableHostMemory>,
25 offset_bytes: usize,
26 length_bytes: usize,
27}
28
29impl RetainedHostMemoryRegion {
30 pub fn new<T>(
31 owner: Arc<T>,
32 offset_bytes: usize,
33 length_bytes: usize,
34 ) -> Result<Self, VNextError>
35 where
36 T: StableHostMemory,
37 {
38 let end = offset_bytes.checked_add(length_bytes).ok_or_else(|| {
39 VNextError::InvalidExecutionPlan {
40 reason: "retained host-memory range overflows the host address space".to_owned(),
41 }
42 })?;
43 if length_bytes == 0 || end > owner.stable_bytes().len() {
44 return Err(VNextError::InvalidExecutionPlan {
45 reason: "retained host-memory range is empty or exceeds its owner".to_owned(),
46 });
47 }
48 Ok(Self {
49 owner,
50 offset_bytes,
51 length_bytes,
52 })
53 }
54
55 pub fn bytes(&self) -> &[u8] {
56 &self.owner.stable_bytes()[self.offset_bytes..self.offset_bytes + self.length_bytes]
57 }
58
59 pub fn owner_bytes(&self) -> &[u8] {
63 self.owner.stable_bytes()
64 }
65
66 pub const fn offset_bytes(&self) -> usize {
67 self.offset_bytes
68 }
69
70 pub const fn length_bytes(&self) -> usize {
71 self.length_bytes
72 }
73}
74
75impl fmt::Debug for RetainedHostMemoryRegion {
76 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
77 formatter
78 .debug_struct("RetainedHostMemoryRegion")
79 .field("offset_bytes", &self.offset_bytes)
80 .field("length_bytes", &self.length_bytes)
81 .finish_non_exhaustive()
82 }
83}
84
85pub struct WeightComponentPayload<'source> {
90 component_id: WeightId,
91 external_names: Vec<String>,
92 source_files: Vec<String>,
93 dimensions: Vec<u64>,
94 element_type: ElementType,
95 bytes: Cow<'source, [u8]>,
96 retained_host_memory: Option<RetainedHostMemoryRegion>,
97}
98
99impl<'source> WeightComponentPayload<'source> {
100 pub fn new(
101 component: &WeightComponentSpec,
102 external_name: impl Into<String>,
103 source_file: impl Into<String>,
104 dimensions: Vec<u64>,
105 element_type: ElementType,
106 bytes: impl Into<Cow<'source, [u8]>>,
107 ) -> Result<Self, VNextError> {
108 Self::from_ordered_sources(
109 component,
110 vec![external_name.into()],
111 vec![source_file.into()],
112 dimensions,
113 element_type,
114 bytes,
115 )
116 }
117
118 pub fn from_ordered_sources(
122 component: &WeightComponentSpec,
123 external_names: Vec<String>,
124 source_files: Vec<String>,
125 dimensions: Vec<u64>,
126 element_type: ElementType,
127 bytes: impl Into<Cow<'source, [u8]>>,
128 ) -> Result<Self, VNextError> {
129 let bytes = bytes.into();
130 let valid_source_file = |source_file: &str| {
131 !source_file.is_empty()
132 && !source_file.starts_with('/')
133 && !source_file.contains('\\')
134 && source_file
135 .split('/')
136 .all(|component| !matches!(component, "" | "." | ".."))
137 };
138 let sources_match = !external_names.is_empty()
139 && external_names == component.external_names
140 && external_names.len() == source_files.len()
141 && source_files.iter().all(|file| valid_source_file(file));
142 let expected_bytes = component.physical_bytes()?;
143 if !sources_match
144 || dimensions != component.dimensions
145 || element_type != component.physical_element_type()
146 || u64::try_from(bytes.len()).ok() != Some(expected_bytes)
147 {
148 return Err(VNextError::InvalidExecutionPlan {
149 reason: format!(
150 "weight component `{}` payload differs from its schema identity, source, shape, type, or byte length",
151 component.id
152 ),
153 });
154 }
155 Ok(Self {
156 component_id: component.id.clone(),
157 external_names,
158 source_files,
159 dimensions,
160 element_type,
161 bytes,
162 retained_host_memory: None,
163 })
164 }
165
166 pub fn with_retained_host_memory(
170 mut self,
171 retained_host_memory: RetainedHostMemoryRegion,
172 ) -> Result<Self, VNextError> {
173 let retained_bytes = retained_host_memory.bytes();
174 if retained_bytes.len() != self.bytes.len()
175 || !std::ptr::eq(retained_bytes.as_ptr(), self.bytes.as_ptr())
176 {
177 return Err(VNextError::InvalidExecutionPlan {
178 reason: format!(
179 "weight component `{}` retained host-memory region differs from its validated payload",
180 self.component_id
181 ),
182 });
183 }
184 self.retained_host_memory = Some(retained_host_memory);
185 Ok(self)
186 }
187
188 pub fn component_id(&self) -> &WeightId {
189 &self.component_id
190 }
191
192 pub fn external_name(&self) -> &str {
193 &self.external_names[0]
194 }
195
196 pub fn source_file(&self) -> &str {
197 &self.source_files[0]
198 }
199
200 pub fn external_names(&self) -> &[String] {
201 &self.external_names
202 }
203
204 pub fn source_files(&self) -> &[String] {
205 &self.source_files
206 }
207
208 pub fn dimensions(&self) -> &[u64] {
209 &self.dimensions
210 }
211
212 pub const fn element_type(&self) -> ElementType {
213 self.element_type
214 }
215
216 pub fn bytes(&self) -> &[u8] {
217 &self.bytes
218 }
219
220 pub fn retained_host_memory(&self) -> Option<&RetainedHostMemoryRegion> {
221 self.retained_host_memory.as_ref()
222 }
223}
224
225pub trait WeightComponentSource: Send + Sync {
231 fn component<'source>(
232 &'source self,
233 component: &WeightComponentSpec,
234 ) -> Result<WeightComponentPayload<'source>, VNextError>;
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240 use crate::vnext::{BlockQuantizationSpec, WeightComponentRole, WeightEncoding};
241
242 struct StableBytes(Vec<u8>);
243
244 unsafe impl StableHostMemory for StableBytes {
247 fn stable_bytes(&self) -> &[u8] {
248 &self.0
249 }
250 }
251
252 fn packed_component() -> WeightComponentSpec {
253 WeightComponentSpec {
254 id: WeightId::new("component.test.gate_up").unwrap(),
255 role: WeightComponentRole::Values,
256 external_names: vec!["gate.weight".to_owned(), "up.weight".to_owned()],
257 dimensions: vec![2, 2, 2],
258 encoding: WeightEncoding::Dense {
259 element_type: ElementType::F16,
260 },
261 required: true,
262 }
263 }
264
265 #[test]
266 fn packed_payload_preserves_ordered_source_identity() {
267 let component = packed_component();
268 let payload = WeightComponentPayload::from_ordered_sources(
269 &component,
270 component.external_names.clone(),
271 vec![
272 "model-1.safetensors".to_owned(),
273 "model-2.safetensors".to_owned(),
274 ],
275 component.dimensions.clone(),
276 ElementType::F16,
277 vec![0_u8; 16],
278 )
279 .unwrap();
280 assert_eq!(payload.external_names(), component.external_names);
281 assert_eq!(payload.source_files().len(), 2);
282
283 let error = WeightComponentPayload::from_ordered_sources(
284 &component,
285 component.external_names.iter().rev().cloned().collect(),
286 vec![
287 "model-2.safetensors".to_owned(),
288 "model-1.safetensors".to_owned(),
289 ],
290 component.dimensions.clone(),
291 ElementType::F16,
292 vec![0_u8; 16],
293 )
294 .err()
295 .expect("source order is part of the packed component identity");
296 assert!(error.to_string().contains("differs from its schema"));
297 }
298
299 #[test]
300 fn block_quantized_payload_validates_block_abi_byte_size() {
301 let component = WeightComponentSpec {
302 id: WeightId::new("component.test.q4-k").unwrap(),
303 role: WeightComponentRole::PackedValues,
304 external_names: vec!["weight.q4_k".to_owned()],
305 dimensions: vec![2],
306 encoding: WeightEncoding::BlockQuantized(BlockQuantizationSpec {
307 format_id: "quantization.gguf.q4-k".to_owned().try_into().unwrap(),
308 logical_values_per_block: 256,
309 bytes_per_block: 144,
310 }),
311 required: true,
312 };
313 let payload = WeightComponentPayload::new(
314 &component,
315 "weight.q4_k",
316 "model.gguf",
317 vec![2],
318 ElementType::U8,
319 vec![0_u8; 288],
320 )
321 .unwrap();
322 assert_eq!(payload.bytes().len(), 288);
323
324 let error = WeightComponentPayload::new(
325 &component,
326 "weight.q4_k",
327 "model.gguf",
328 vec![2],
329 ElementType::U8,
330 vec![0_u8; 2],
331 )
332 .err()
333 .expect("block-grid element count must not be mistaken for byte length");
334 assert!(error.to_string().contains("differs from its schema"));
335 }
336
337 #[test]
338 fn retained_region_must_be_the_validated_payload_and_keeps_its_owner_alive() {
339 let component = packed_component();
340 let owner = Arc::new(StableBytes(vec![7_u8; 32]));
341 let retained = RetainedHostMemoryRegion::new(Arc::clone(&owner), 8, 16).unwrap();
342 let payload = WeightComponentPayload::from_ordered_sources(
343 &component,
344 component.external_names.clone(),
345 vec![
346 "model-1.safetensors".to_owned(),
347 "model-2.safetensors".to_owned(),
348 ],
349 component.dimensions.clone(),
350 ElementType::F16,
351 retained.bytes(),
352 )
353 .unwrap()
354 .with_retained_host_memory(retained.clone())
355 .unwrap();
356 let retained = payload.retained_host_memory().unwrap().clone();
357 drop(payload);
358 drop(owner);
359 assert_eq!(retained.bytes(), &[7_u8; 16]);
360
361 let other = Arc::new(StableBytes(vec![7_u8; 16]));
362 let wrong = RetainedHostMemoryRegion::new(other, 0, 16).unwrap();
363 let result = WeightComponentPayload::from_ordered_sources(
364 &component,
365 component.external_names.clone(),
366 vec![
367 "model-1.safetensors".to_owned(),
368 "model-2.safetensors".to_owned(),
369 ],
370 component.dimensions.clone(),
371 ElementType::F16,
372 vec![7_u8; 16],
373 )
374 .unwrap()
375 .with_retained_host_memory(wrong);
376 let error = match result {
377 Ok(_) => panic!("a different allocation must not satisfy retained payload identity"),
378 Err(error) => error,
379 };
380 assert!(error
381 .to_string()
382 .contains("retained host-memory region differs"));
383 }
384}