combs_formats/
litertlm.rs1use std::path::Path;
23
24use crate::flatbuf::FlatBuffer;
25use crate::source::ModelSource;
26use crate::tflite::TfliteSource;
27use crate::{FormatError, Result};
28
29fn bad(what: impl Into<String>) -> FormatError {
30 FormatError::Safetensors(format!("litertlm: {}", what.into()))
31}
32
33const SECTION_TFLITE_MODEL: u8 = 3;
34const SECTION_SP_TOKENIZER: u8 = 4;
35const SECTION_HF_TOKENIZER_ZLIB: u8 = 6;
36
37#[derive(Debug)]
39pub struct SectionInfo {
40 pub begin: usize,
41 pub end: usize,
42 pub data_type: u8,
43}
44
45pub fn read_sections(header: &[u8]) -> Result<Vec<SectionInfo>> {
47 if header.len() < 0x20 || &header[0..8] != b"LITERTLM" {
48 return Err(bad("bad magic"));
49 }
50 let major = u32_le(header, 0x08)?;
51 if major != 1 {
52 return Err(bad(format!("unsupported major version {major}")));
53 }
54 let header_end = u64_le(header, 0x18)? as usize;
55 if header_end > header.len() || header_end < 0x20 {
56 return Err(bad("header end out of range"));
57 }
58 let fb = FlatBuffer::new(&header[0x20..header_end], None)?;
59 let root = fb.root();
60 let sm = fb
62 .uoffset(root, 1)
63 .ok_or_else(|| bad("no section_metadata"))?;
64 let objects = fb.table_vector(sm, 0)?;
65 let mut out = Vec::with_capacity(objects.len());
66 for obj in objects {
67 let begin = fb.scalar_u64(obj, 1).ok_or_else(|| bad("section missing begin"))? as usize;
68 let end = fb.scalar_u64(obj, 2).ok_or_else(|| bad("section missing end"))? as usize;
69 let data_type = fb.scalar_u8(obj, 3).unwrap_or(0);
70 out.push(SectionInfo { begin, end, data_type });
71 }
72 Ok(out)
73}
74
75pub fn open_litertlm(path: &Path) -> Result<Box<dyn ModelSource>> {
81 let head = {
82 use std::io::Read;
83 let mut f = std::fs::File::open(path)?;
84 let mut buf = vec![0u8; 1024 * 1024]; let n = f.read(&mut buf)?;
86 buf.truncate(n);
87 buf
88 };
89 let sections = read_sections(&head)?;
90 let tflite = sections
91 .iter()
92 .find(|s| s.data_type == SECTION_TFLITE_MODEL)
93 .ok_or_else(|| bad("no TFLiteModel section"))?;
94 let spm: Option<Vec<u8>> = sections
97 .iter()
98 .find(|s| s.data_type == SECTION_SP_TOKENIZER)
99 .map(|s| {
100 use std::io::{Read, Seek, SeekFrom};
101 let mut f = std::fs::File::open(path)?;
102 f.seek(SeekFrom::Start(s.begin as u64))?;
103 let mut buf = vec![0u8; s.end - s.begin];
104 f.read_exact(&mut buf)?;
105 Ok::<Vec<u8>, FormatError>(buf)
106 })
107 .transpose()?;
108 Ok(Box::new(TfliteSource::load_at_with_spm(
109 path,
110 tflite.begin,
111 spm.as_deref(),
112 )?))
113}
114
115fn u32_le(d: &[u8], pos: usize) -> Result<u32> {
116 let b = d.get(pos..pos + 4).ok_or_else(|| bad("u32 out of bounds"))?;
117 Ok(u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
118}
119
120fn u64_le(d: &[u8], pos: usize) -> Result<u64> {
121 let b = d.get(pos..pos + 8).ok_or_else(|| bad("u64 out of bounds"))?;
122 Ok(u64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]))
123}