1use anyhow::{Context, Result, bail};
20use serde_json::Value;
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub struct TensorSpan {
26 pub abs_offset: u64,
28 pub len: u64,
30}
31
32pub fn tensor_span(
42 tensor: &str,
43 offsets: &Value,
44 data_start: u64,
45 file_len: u64,
46) -> Result<TensorSpan> {
47 let arr = offsets
48 .as_array()
49 .with_context(|| format!("tensor {tensor}: data_offsets is not an array"))?;
50 if arr.len() != 2 {
51 bail!(
52 "tensor {tensor}: data_offsets has {} entries, expected 2",
53 arr.len()
54 );
55 }
56 let rel_start = arr[0]
57 .as_u64()
58 .with_context(|| format!("tensor {tensor}: bad data_offsets[0]"))?;
59 let rel_end = arr[1]
60 .as_u64()
61 .with_context(|| format!("tensor {tensor}: bad data_offsets[1]"))?;
62 if rel_end < rel_start {
65 bail!("tensor {tensor}: data_offsets [{rel_start}, {rel_end}] end precedes start");
66 }
67 let len = rel_end - rel_start;
68 let abs_offset = data_start
71 .checked_add(rel_start)
72 .with_context(|| format!("tensor {tensor}: data_offsets[0] {rel_start} overflows"))?;
73 let abs_end = abs_offset
74 .checked_add(len)
75 .with_context(|| format!("tensor {tensor}: span {abs_offset}+{len} overflows"))?;
76 if abs_end > file_len {
77 bail!(
78 "tensor {tensor}: spans bytes {abs_offset}..{abs_end} but the shard is only \
79 {file_len} bytes (truncated or corrupt checkpoint)"
80 );
81 }
82 Ok(TensorSpan { abs_offset, len })
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88 use serde_json::json;
89
90 #[test]
92 fn accepts_well_formed_span() {
93 let s = tensor_span("w", &json!([0, 128]), 72, 4096).unwrap();
94 assert_eq!(
95 s,
96 TensorSpan {
97 abs_offset: 72,
98 len: 128
99 }
100 );
101 let s = tensor_span("w", &json!([128, 256]), 72, 4096).unwrap();
102 assert_eq!(
103 s,
104 TensorSpan {
105 abs_offset: 200,
106 len: 128
107 }
108 );
109 }
110
111 #[test]
113 fn accepts_empty_tensor() {
114 let s = tensor_span("w", &json!([64, 64]), 8, 4096).unwrap();
115 assert_eq!(s.len, 0);
116 }
117
118 #[test]
120 fn accepts_span_ending_exactly_at_eof() {
121 let s = tensor_span("w", &json!([0, 4024]), 72, 4096).unwrap();
122 assert_eq!(s.len, 4024);
123 }
124
125 #[test]
128 fn rejects_reversed_offsets_instead_of_underflowing() {
129 let err = tensor_span("evil", &json!([4096, 0]), 8, 65536).unwrap_err();
130 let msg = err.to_string();
131 assert!(msg.contains("evil"), "{msg}");
132 assert!(msg.contains("end precedes start"), "{msg}");
133 assert_eq!(0u64.wrapping_sub(4096), u64::MAX - 4095);
136 }
137
138 #[test]
140 fn rejects_span_past_end_of_file() {
141 let err = tensor_span("w", &json!([0, 1_000_000]), 72, 4096).unwrap_err();
142 assert!(err.to_string().contains("truncated or corrupt"), "{err}");
143 }
144
145 #[test]
147 fn rejects_offset_that_overflows_u64() {
148 let err = tensor_span("w", &json!([u64::MAX, u64::MAX]), 72, 4096).unwrap_err();
149 assert!(
150 err.to_string().contains("data_offsets[0]") && err.to_string().contains("overflows"),
151 "{err}"
152 );
153 }
154
155 #[test]
157 fn rejects_span_end_that_overflows_u64() {
158 let err = tensor_span("w", &json!([u64::MAX - 1, u64::MAX]), 1, u64::MAX).unwrap_err();
159 assert!(
160 err.to_string().contains("span") && err.to_string().contains("overflows"),
161 "{err}"
162 );
163 }
164
165 #[test]
167 fn rejects_malformed_offsets() {
168 let cases = [
169 (json!("nope"), "not an array"),
170 (json!([0]), "1 entries"),
171 (json!([0, 1, 2]), "3 entries"),
172 (json!([-1, 16]), "data_offsets[0]"),
173 (json!([0.5, 16]), "data_offsets[0]"),
174 (json!([0, -1]), "data_offsets[1]"),
175 (json!([0, 16.5]), "data_offsets[1]"),
176 (json!([0, "16"]), "data_offsets[1]"),
177 ];
178 for (offsets, cause) in cases {
179 let err = tensor_span("w", &offsets, 8, 4096).unwrap_err();
180 let msg = err.to_string();
181 assert!(msg.contains("tensor w"), "{msg}");
182 assert!(msg.contains(cause), "expected {cause} in: {msg}");
183 }
184 }
185}