1use std::collections::HashMap;
17use std::io::BufRead;
18
19use byteorder::{LittleEndian, ReadBytesExt};
20use fuel_ir::{Context, DType, Error as E, Layout, Result, Shape, bail};
21
22const VERBOSE: bool = false;
23
24#[repr(u8)]
30#[derive(Debug, Eq, PartialEq, Clone)]
31pub enum OpCode {
32 Proto = 0x80,
33 Global = b'c',
34 BinPut = b'q',
35 LongBinPut = b'r',
36 EmptyTuple = b')',
37 Reduce = b'R',
38 Mark = b'(',
39 BinUnicode = b'X',
40 BinInt = b'J',
41 Tuple = b't',
42 BinPersId = b'Q',
43 BinInt1 = b'K',
44 BinInt2 = b'M',
45 Tuple1 = 0x85,
46 Tuple2 = 0x86,
47 Tuple3 = 0x87,
48 NewTrue = 0x88,
49 NewFalse = 0x89,
50 None = b'N',
51 BinGet = b'h',
52 LongBinGet = b'j',
53 SetItem = b's',
54 SetItems = b'u',
55 EmptyDict = b'}',
56 Dict = b'd',
57 Build = b'b',
58 Stop = b'.',
59 NewObj = 0x81,
60 EmptyList = b']',
61 BinFloat = b'G',
62 Append = b'a',
63 Appends = b'e',
64 Long1 = 0x8a,
65}
66
67impl TryFrom<u8> for OpCode {
68 type Error = u8;
69 fn try_from(value: u8) -> std::result::Result<Self, Self::Error> {
70 match value {
71 0x80 => Ok(Self::Proto),
72 b'c' => Ok(Self::Global),
73 b'q' => Ok(Self::BinPut),
74 b'r' => Ok(Self::LongBinPut),
75 b')' => Ok(Self::EmptyTuple),
76 b'R' => Ok(Self::Reduce),
77 b'(' => Ok(Self::Mark),
78 b'X' => Ok(Self::BinUnicode),
79 b'J' => Ok(Self::BinInt),
80 b't' => Ok(Self::Tuple),
81 b'Q' => Ok(Self::BinPersId),
82 b'K' => Ok(Self::BinInt1),
83 b'M' => Ok(Self::BinInt2),
84 b'N' => Ok(Self::None),
85 0x85 => Ok(Self::Tuple1),
86 0x86 => Ok(Self::Tuple2),
87 0x87 => Ok(Self::Tuple3),
88 0x88 => Ok(Self::NewTrue),
89 0x89 => Ok(Self::NewFalse),
90 b'h' => Ok(Self::BinGet),
91 b'j' => Ok(Self::LongBinGet),
92 b's' => Ok(Self::SetItem),
93 b'u' => Ok(Self::SetItems),
94 b'}' => Ok(Self::EmptyDict),
95 b'd' => Ok(Self::EmptyDict),
96 b'b' => Ok(Self::Build),
97 b'.' => Ok(Self::Stop),
98 0x81 => Ok(Self::NewObj),
99 b']' => Ok(Self::EmptyList),
100 b'G' => Ok(Self::BinFloat),
101 b'a' => Ok(Self::Append),
102 b'e' => Ok(Self::Appends),
103 0x8a => Ok(Self::Long1),
104 value => Err(value),
105 }
106 }
107}
108
109fn read_to_newline<R: BufRead>(r: &mut R) -> Result<Vec<u8>> {
110 let mut data: Vec<u8> = Vec::with_capacity(32);
111 r.read_until(b'\n', &mut data)?;
112 data.pop();
113 if data.last() == Some(&b'\r') {
114 data.pop();
115 }
116 Ok(data)
117}
118
119#[derive(Debug, Clone, PartialEq)]
121pub enum Object {
122 Class {
123 module_name: String,
124 class_name: String,
125 },
126 Int(i32),
127 Long(i64),
128 Float(f64),
129 Unicode(String),
130 Bool(bool),
131 None,
132 Tuple(Vec<Object>),
133 List(Vec<Object>),
134 Mark,
135 Dict(Vec<(Object, Object)>),
136 Reduce {
137 callable: Box<Object>,
138 args: Box<Object>,
139 },
140 Build {
141 callable: Box<Object>,
142 args: Box<Object>,
143 },
144 PersistentLoad(Box<Object>),
145}
146
147type OResult<T> = std::result::Result<T, Object>;
148
149impl Object {
150 pub fn unicode(self) -> OResult<String> {
151 match self {
152 Self::Unicode(t) => Ok(t),
153 _ => Err(self),
154 }
155 }
156
157 pub fn reduce(self) -> OResult<(Self, Self)> {
158 match self {
159 Self::Reduce { callable, args } => Ok((*callable, *args)),
160 _ => Err(self),
161 }
162 }
163
164 pub fn none(self) -> OResult<()> {
165 match self {
166 Self::None => Ok(()),
167 _ => Err(self),
168 }
169 }
170
171 pub fn persistent_load(self) -> OResult<Self> {
172 match self {
173 Self::PersistentLoad(t) => Ok(*t),
174 _ => Err(self),
175 }
176 }
177
178 pub fn bool(self) -> OResult<bool> {
179 match self {
180 Self::Bool(t) => Ok(t),
181 _ => Err(self),
182 }
183 }
184
185 pub fn int(self) -> OResult<i32> {
186 match self {
187 Self::Int(t) => Ok(t),
188 _ => Err(self),
189 }
190 }
191
192 pub fn int_or_long(self) -> OResult<i64> {
193 match self {
194 Self::Int(t) => Ok(t as i64),
195 Self::Long(t) => Ok(t),
196 _ => Err(self),
197 }
198 }
199
200 pub fn tuple(self) -> OResult<Vec<Self>> {
201 match self {
202 Self::Tuple(t) => Ok(t),
203 _ => Err(self),
204 }
205 }
206
207 pub fn dict(self) -> OResult<Vec<(Self, Self)>> {
208 match self {
209 Self::Dict(t) => Ok(t),
210 _ => Err(self),
211 }
212 }
213
214 pub fn class(self) -> OResult<(String, String)> {
215 match self {
216 Self::Class {
217 module_name,
218 class_name,
219 } => Ok((module_name, class_name)),
220 _ => Err(self),
221 }
222 }
223
224 pub fn into_tensor_info(
228 self,
229 name: Self,
230 dir_name: &std::path::Path,
231 ) -> Result<Option<TensorInfo>> {
232 let name = match name.unicode() {
233 Ok(name) => name,
234 Err(_) => return Ok(None),
235 };
236 let (callable, args) = match self.reduce() {
237 Ok(callable_args) => callable_args,
238 _ => return Ok(None),
239 };
240 let (callable, args) = match callable {
241 Object::Class {
242 module_name,
243 class_name,
244 } if module_name == "torch._tensor" && class_name == "_rebuild_from_type_v2" => {
245 let mut args = args.tuple()?;
246 let callable = args.remove(0);
247 let args = args.remove(1);
248 (callable, args)
249 }
250 Object::Class {
251 module_name,
252 class_name,
253 } if module_name == "torch._utils" && class_name == "_rebuild_parameter" => {
254 let mut args = args.tuple()?;
255 args.remove(0).reduce()?
256 }
257 _ => (callable, args),
258 };
259 match callable {
260 Object::Class {
261 module_name,
262 class_name,
263 } if module_name == "torch._utils" && class_name == "_rebuild_tensor_v2" => {}
264 _ => return Ok(None),
265 };
266 let (layout, dtype, file_path, storage_size) = rebuild_args(args)?;
267 Ok(Some(TensorInfo {
268 name,
269 dtype,
270 layout,
271 path: format!("{}/{}", dir_name.to_string_lossy(), file_path),
272 storage_size,
273 }))
274 }
275}
276
277impl TryFrom<Object> for String {
278 type Error = Object;
279 fn try_from(value: Object) -> std::result::Result<Self, Self::Error> {
280 match value {
281 Object::Unicode(s) => Ok(s),
282 other => Err(other),
283 }
284 }
285}
286
287impl TryFrom<Object> for usize {
288 type Error = Object;
289 fn try_from(value: Object) -> std::result::Result<Self, Self::Error> {
290 match value {
291 Object::Int(s) if s >= 0 => Ok(s as usize),
292 other => Err(other),
293 }
294 }
295}
296
297impl<T: TryFrom<Object, Error = Object>> TryFrom<Object> for Vec<T> {
298 type Error = Object;
299 fn try_from(value: Object) -> std::result::Result<Self, Self::Error> {
300 match value {
301 Object::Tuple(values) => values
302 .into_iter()
303 .map(|v| T::try_from(v))
304 .collect::<std::result::Result<Vec<T>, Self::Error>>(),
305 other => Err(other),
306 }
307 }
308}
309
310#[derive(Debug)]
312pub struct Stack {
313 stack: Vec<Object>,
314 memo: HashMap<u32, Object>,
315}
316
317impl Stack {
318 pub fn empty() -> Self {
319 Self {
320 stack: Vec::with_capacity(512),
321 memo: HashMap::new(),
322 }
323 }
324
325 pub fn stack(&self) -> &[Object] {
326 self.stack.as_slice()
327 }
328
329 pub fn read_loop<R: BufRead>(&mut self, r: &mut R) -> Result<()> {
331 loop {
332 if self.read(r)? {
333 break;
334 }
335 }
336 Ok(())
337 }
338
339 pub fn finalize(mut self) -> Result<Object> {
341 self.pop()
342 }
343
344 fn push(&mut self, obj: Object) {
345 self.stack.push(obj)
346 }
347
348 fn pop(&mut self) -> Result<Object> {
349 match self.stack.pop() {
350 None => bail!("unexpected empty stack"),
351 Some(obj) => Ok(obj),
352 }
353 }
354
355 fn build(&mut self) -> Result<()> {
357 let args = self.pop()?;
358 let obj = self.pop()?;
359 let obj = match (obj, args) {
360 (Object::Dict(mut obj), Object::Dict(mut args)) => {
361 obj.append(&mut args);
362 Object::Dict(obj)
363 }
364 (obj, args) => Object::Build {
365 callable: Box::new(obj),
366 args: Box::new(args),
367 },
368 };
369 self.push(obj);
370 Ok(())
371 }
372
373 fn reduce(&mut self) -> Result<()> {
374 let args = self.pop()?;
375 let callable = self.pop()?;
376 #[allow(clippy::single_match)]
377 let reduced = match &callable {
378 Object::Class {
379 module_name,
380 class_name,
381 } => {
382 if module_name == "collections"
383 && (class_name == "OrderedDict" || class_name == "defaultdict")
384 {
385 Some(Object::Dict(vec![]))
387 } else {
388 None
389 }
390 }
391 _ => None,
392 };
393 let reduced = reduced.unwrap_or_else(|| Object::Reduce {
394 callable: Box::new(callable),
395 args: Box::new(args),
396 });
397 self.push(reduced);
398 Ok(())
399 }
400
401 fn last(&mut self) -> Result<&mut Object> {
402 match self.stack.last_mut() {
403 None => bail!("unexpected empty stack"),
404 Some(obj) => Ok(obj),
405 }
406 }
407
408 fn memo_get(&self, id: u32) -> Result<Object> {
409 match self.memo.get(&id) {
410 None => bail!("missing object in memo {id}"),
411 Some(obj) => Ok(obj.clone()),
412 }
413 }
414
415 fn memo_put(&mut self, id: u32) -> Result<()> {
416 let obj = self.last()?.clone();
417 self.memo.insert(id, obj);
418 Ok(())
419 }
420
421 fn persistent_load(&self, id: Object) -> Result<Object> {
422 Ok(Object::PersistentLoad(Box::new(id)))
423 }
424
425 fn new_obj(&self, class: Object, args: Object) -> Result<Object> {
426 Ok(Object::Reduce {
427 callable: Box::new(class),
428 args: Box::new(args),
429 })
430 }
431
432 fn pop_to_marker(&mut self) -> Result<Vec<Object>> {
433 let mut mark_idx = None;
434 for (idx, obj) in self.stack.iter().enumerate().rev() {
435 if obj == &Object::Mark {
436 mark_idx = Some(idx);
437 break;
438 }
439 }
440 match mark_idx {
441 Some(mark_idx) => {
442 let objs = self.stack.split_off(mark_idx + 1);
443 self.stack.pop();
444 Ok(objs)
445 }
446 None => bail!("marker object not found"),
447 }
448 }
449
450 pub fn read<R: BufRead>(&mut self, r: &mut R) -> Result<bool> {
453 let op_code = match OpCode::try_from(r.read_u8()?) {
454 Ok(op_code) => op_code,
455 Err(op_code) => bail!("unknown op-code {op_code}"),
456 };
457 match op_code {
458 OpCode::Proto => {
459 let version = r.read_u8()?;
460 if VERBOSE {
461 println!("proto {version}");
462 }
463 }
464 OpCode::Global => {
465 let module_name = read_to_newline(r)?;
466 let class_name = read_to_newline(r)?;
467 let module_name = String::from_utf8_lossy(&module_name).to_string();
468 let class_name = String::from_utf8_lossy(&class_name).to_string();
469 self.push(Object::Class {
470 module_name,
471 class_name,
472 })
473 }
474 OpCode::BinInt1 => {
475 let arg = r.read_u8()?;
476 self.push(Object::Int(arg as i32))
477 }
478 OpCode::BinInt2 => {
479 let arg = r.read_u16::<LittleEndian>()?;
480 self.push(Object::Int(arg as i32))
481 }
482 OpCode::BinInt => {
483 let arg = r.read_i32::<LittleEndian>()?;
484 self.push(Object::Int(arg))
485 }
486 OpCode::BinFloat => {
487 let arg = r.read_f64::<byteorder::BigEndian>()?;
491 self.push(Object::Float(arg))
492 }
493 OpCode::BinUnicode => {
494 let len = r.read_u32::<LittleEndian>()?;
495 let mut data = vec![0u8; len as usize];
496 r.read_exact(&mut data)?;
497 let data = String::from_utf8(data).map_err(E::wrap)?;
498 self.push(Object::Unicode(data))
499 }
500 OpCode::BinPersId => {
501 let id = self.pop()?;
502 let obj = self.persistent_load(id)?;
503 self.push(obj)
504 }
505 OpCode::Tuple => {
506 let objs = self.pop_to_marker()?;
507 self.push(Object::Tuple(objs))
508 }
509 OpCode::Tuple1 => {
510 let obj = self.pop()?;
511 self.push(Object::Tuple(vec![obj]))
512 }
513 OpCode::Tuple2 => {
514 let obj2 = self.pop()?;
515 let obj1 = self.pop()?;
516 self.push(Object::Tuple(vec![obj1, obj2]))
517 }
518 OpCode::Tuple3 => {
519 let obj3 = self.pop()?;
520 let obj2 = self.pop()?;
521 let obj1 = self.pop()?;
522 self.push(Object::Tuple(vec![obj1, obj2, obj3]))
523 }
524 OpCode::NewTrue => self.push(Object::Bool(true)),
525 OpCode::NewFalse => self.push(Object::Bool(false)),
526 OpCode::Append => {
527 let value = self.pop()?;
528 let pylist = self.last()?;
529 if let Object::List(d) = pylist {
530 d.push(value)
531 } else {
532 bail!("expected a list, got {pylist:?}")
533 }
534 }
535 OpCode::Appends => {
536 let objs = self.pop_to_marker()?;
537 let pylist = self.last()?;
538 if let Object::List(d) = pylist {
539 d.extend(objs)
540 } else {
541 bail!("expected a list, got {pylist:?}")
542 }
543 }
544 OpCode::SetItem => {
545 let value = self.pop()?;
546 let key = self.pop()?;
547 let pydict = self.last()?;
548 if let Object::Dict(d) = pydict {
549 d.push((key, value))
550 } else {
551 bail!("expected a dict, got {pydict:?}")
552 }
553 }
554 OpCode::SetItems => {
555 let mut objs = self.pop_to_marker()?;
556 let pydict = self.last()?;
557 if let Object::Dict(d) = pydict {
558 if objs.len() % 2 != 0 {
559 bail!("setitems: not an even number of objects")
560 }
561 while let Some(value) = objs.pop() {
562 let key = objs.pop().context("empty objs")?;
563 d.push((key, value))
564 }
565 } else {
566 bail!("expected a dict, got {pydict:?}")
567 }
568 }
569 OpCode::None => self.push(Object::None),
570 OpCode::Stop => return Ok(true),
571 OpCode::Build => self.build()?,
572 OpCode::EmptyDict => self.push(Object::Dict(vec![])),
573 OpCode::Dict => {
574 let mut objs = self.pop_to_marker()?;
575 let mut pydict = vec![];
576 if objs.len() % 2 != 0 {
577 bail!("setitems: not an even number of objects")
578 }
579 while let Some(value) = objs.pop() {
580 let key = objs.pop().context("empty objs")?;
581 pydict.push((key, value))
582 }
583 self.push(Object::Dict(pydict))
584 }
585 OpCode::Mark => self.push(Object::Mark),
586 OpCode::Reduce => self.reduce()?,
587 OpCode::EmptyTuple => self.push(Object::Tuple(vec![])),
588 OpCode::EmptyList => self.push(Object::List(vec![])),
589 OpCode::BinGet => {
590 let arg = r.read_u8()?;
591 let obj = self.memo_get(arg as u32)?;
592 self.push(obj)
593 }
594 OpCode::LongBinGet => {
595 let arg = r.read_u32::<LittleEndian>()?;
596 let obj = self.memo_get(arg)?;
597 self.push(obj)
598 }
599 OpCode::BinPut => {
600 let arg = r.read_u8()?;
601 self.memo_put(arg as u32)?
602 }
603 OpCode::LongBinPut => {
604 let arg = r.read_u32::<LittleEndian>()?;
605 self.memo_put(arg)?
606 }
607 OpCode::NewObj => {
608 let args = self.pop()?;
609 let class = self.pop()?;
610 let obj = self.new_obj(class, args)?;
611 self.push(obj)
612 }
613 OpCode::Long1 => {
614 let n_bytes = r.read_u8()?;
615 let mut v = 0;
616 for i in 0..n_bytes {
618 v |= (r.read_u8()? as i64) << (i * 8);
619 }
620 self.push(Object::Long(v))
621 }
622 }
623 Ok(false)
624 }
625}
626
627impl From<Object> for E {
628 fn from(value: Object) -> Self {
629 E::Msg(format!("conversion error on {value:?}"))
630 }
631}
632
633fn rebuild_args(args: Object) -> Result<(Layout, DType, String, usize)> {
636 let mut args = args.tuple()?;
637 let stride_usize = Vec::<usize>::try_from(args.remove(3))?;
638 let stride: fuel_ir::StrideVec = stride_usize.iter().map(|&s| s as isize).collect();
639 let size = Vec::<usize>::try_from(args.remove(2))?;
640 let offset = args.remove(1).int_or_long()? as usize;
641 let storage = args.remove(0).persistent_load()?;
642 let mut storage = storage.tuple()?;
643 let storage_size = storage.remove(4).int_or_long()? as usize;
644 let path = storage.remove(2).unicode()?;
645 let (_module_name, class_name) = storage.remove(1).class()?;
646 let dtype = match class_name.as_str() {
647 "FloatStorage" => DType::F32,
648 "DoubleStorage" => DType::F64,
649 "HalfStorage" => DType::F16,
650 "BFloat16Storage" => DType::BF16,
651 "ByteStorage" => DType::U8,
652 "LongStorage" => DType::I64,
653 other => bail!("unsupported storage type {other}"),
654 };
655 let layout = Layout::new(Shape::from(size), stride, offset * dtype.size_in_bytes());
656 Ok((layout, dtype, path, storage_size))
657}
658
659#[derive(Debug, Clone)]
665pub struct TensorInfo {
666 pub name: String,
668 pub dtype: DType,
670 pub layout: Layout,
672 pub path: String,
674 pub storage_size: usize,
676}
677
678pub fn read_pth_tensor_info<P: AsRef<std::path::Path>>(
686 file: P,
687 verbose: bool,
688 key: Option<&str>,
689) -> Result<Vec<TensorInfo>> {
690 let file = std::fs::File::open(file)?;
691 let zip_reader = std::io::BufReader::new(file);
692 let mut zip = zip::ZipArchive::new(zip_reader)?;
693 let zip_file_names = zip
694 .file_names()
695 .map(|f| f.map(|name| name.to_string()))
696 .collect::<std::result::Result<Vec<String>, _>>()?;
697
698 let mut tensor_infos = vec![];
699 for file_name in zip_file_names.iter() {
700 if !file_name.ends_with("data.pkl") {
701 continue;
702 }
703 let dir_name = std::path::PathBuf::from(file_name.strip_suffix(".pkl").context("no .pkl")?);
704 let reader = zip.by_name(file_name)?;
705 let mut reader = std::io::BufReader::new(reader);
706 let mut stack = Stack::empty();
707 stack.read_loop(&mut reader)?;
708 let obj = stack.finalize()?;
709 if VERBOSE || verbose {
710 println!("{obj:#?}");
711 }
712
713 let obj = match obj {
714 Object::Build { callable, args } => match *callable {
715 Object::Reduce { callable, args: _ } => match *callable {
716 Object::Class {
717 module_name,
718 class_name,
719 } if module_name == "__torch__" && class_name == "Module" => *args,
720 _ => continue,
721 },
722 _ => continue,
723 },
724 obj => obj,
725 };
726
727 let obj = if let Some(key) = key {
729 if let Object::Dict(key_values) = obj {
730 key_values
731 .into_iter()
732 .find(|(k, _)| *k == Object::Unicode(key.to_owned()))
733 .map(|(_, v)| v)
734 .ok_or_else(|| E::Msg(format!("key {key} not found")))?
735 } else {
736 obj
737 }
738 } else {
739 obj
740 };
741
742 if let Object::Dict(key_values) = obj {
744 for (name, value) in key_values.into_iter() {
745 match value.into_tensor_info(name, &dir_name) {
746 Ok(Some(tensor_info)) => tensor_infos.push(tensor_info),
747 Ok(None) => {}
748 Err(err) => eprintln!("skipping: {err:?}"),
749 }
750 }
751 }
752 }
753 Ok(tensor_infos)
754}