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