1use half::{bf16, f16};
7use std::collections::HashMap;
8
9pub struct STensor {
11 pub data: Vec<f32>,
12 pub shape: Vec<usize>,
13}
14
15pub fn safetensors(bytes: &[u8]) -> Result<HashMap<String, STensor>, String> {
17 if bytes.len() < 8 {
18 return Err("safetensors: too short".into());
19 }
20 let hlen = u64::from_le_bytes(bytes[0..8].try_into().unwrap()) as usize;
21 let base = 8 + hlen;
22 if bytes.len() < base {
23 return Err("safetensors: header exceeds buffer".into());
24 }
25 let header: serde_json::Value =
26 serde_json::from_slice(&bytes[8..base]).map_err(|e| format!("safetensors header json: {e}"))?;
27 let obj = header.as_object().ok_or("safetensors: header not an object")?;
28
29 let mut out = HashMap::new();
30 for (name, v) in obj {
31 if name == "__metadata__" {
32 continue;
33 }
34 let dtype = v["dtype"].as_str().ok_or("missing dtype")?;
35 let shape: Vec<usize> = v["shape"]
36 .as_array()
37 .ok_or("missing shape")?
38 .iter()
39 .map(|d| d.as_u64().unwrap() as usize)
40 .collect();
41 let off = v["data_offsets"].as_array().ok_or("missing data_offsets")?;
42 let (s, e) = (off[0].as_u64().unwrap() as usize, off[1].as_u64().unwrap() as usize);
43 let raw = &bytes[base + s..base + e];
44 let data = dequant(dtype, raw)?;
45 let n: usize = shape.iter().product();
46 if data.len() != n {
47 return Err(format!("{name}: {} elems for shape {shape:?} ({n})", data.len()));
48 }
49 out.insert(name.clone(), STensor { data, shape });
50 }
51 Ok(out)
52}
53
54fn dequant(dtype: &str, raw: &[u8]) -> Result<Vec<f32>, String> {
56 Ok(match dtype {
57 "F32" => raw.chunks_exact(4).map(|b| f32::from_le_bytes([b[0], b[1], b[2], b[3]])).collect(),
58 "F16" => raw.chunks_exact(2).map(|b| f16::from_le_bytes([b[0], b[1]]).to_f32()).collect(),
59 "BF16" => raw.chunks_exact(2).map(|b| bf16::from_le_bytes([b[0], b[1]]).to_f32()).collect(),
60 "F64" => raw.chunks_exact(8).map(|b| f64::from_le_bytes(b.try_into().unwrap()) as f32).collect(),
61 other => return Err(format!("unsupported safetensors dtype '{other}'")),
62 })
63}