1use std::cell::{Cell, RefCell};
22use std::collections::HashMap;
23use std::fmt;
24
25use serde::de::{self, Deserializer, Visitor};
26use serde::ser::Serializer;
27use serde::{Deserialize, Serialize};
28
29use crate::charset;
30use crate::decompile::{self, Element, Settings, UnitOp};
31use crate::error::{DecodeError, Error, Reason};
32use crate::names::NameTable;
33use crate::prolog::{MAX_DEPTH, ObjectType, SEMI, object_size, read_field};
34
35const MAX_HEX_NIBBLES: usize = 4096;
37const MAX_MEMORY_OBJECT: usize = 1 << 20;
39pub const MAX_DECODED_OBJECTS: usize = 1 << 18;
49pub const MAX_DECODED_NIBBLES: usize = 1 << 21;
53pub const MAX_CLONED_NIBBLES: usize = 1 << 24;
60const MAX_ARRAY_DIMS: usize = 2;
64const F64_EXPONENTS: std::ops::RangeInclusive<i32> = -307..=307;
66
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
70pub struct Real {
71 pub negative: bool,
73 pub digits: [u8; 12],
76 pub exponent: i32,
78}
79
80impl Real {
81 pub const ZERO: Real = Real {
83 negative: false,
84 digits: [0; 12],
85 exponent: 0,
86 };
87
88 pub fn from_body(body: &[u8]) -> Result<Real, DecodeError> {
92 let body = body
93 .get(..16)
94 .ok_or(DecodeError::new(0, Reason::RealTooShort))?;
95 if let Some(at) = body.iter().position(|&d| d > 9) {
96 return Err(DecodeError::new(
97 at,
98 Reason::NotBcd {
99 kind: ObjectType::Real,
100 hex: hex(body),
101 },
102 ));
103 }
104 let e = i32::from(body[0]) + 10 * i32::from(body[1]) + 100 * i32::from(body[2]);
105 let exponent = if e >= 500 { e - 1000 } else { e };
106 let mut digits = [0u8; 12];
107 for (i, d) in digits.iter_mut().enumerate() {
108 *d = body[14 - i];
109 }
110 let negative = match body[15] {
111 0 => false,
112 9 => true,
113 s => return Err(DecodeError::new(15, Reason::BadSign(s))),
114 };
115 Ok(Real {
116 negative,
117 digits,
118 exponent,
119 })
120 }
121
122 pub fn to_body(&self) -> [u8; 16] {
124 let mut body = [0u8; 16];
125 let e = u32::try_from(self.exponent.rem_euclid(1000)).unwrap_or(0);
126 body[0] = (e % 10) as u8;
127 body[1] = (e / 10 % 10) as u8;
128 body[2] = (e / 100) as u8;
129 for (i, d) in self.digits.iter().enumerate() {
130 body[14 - i] = *d;
131 }
132 body[15] = if self.negative { 9 } else { 0 };
133 body
134 }
135
136 fn is_zero(&self) -> bool {
137 self.digits.iter().all(|&d| d == 0)
138 }
139
140 pub fn to_f64(&self) -> f64 {
142 format!("{}e{}", self.mantissa_text(), self.exponent)
143 .parse()
144 .unwrap_or(f64::NAN)
145 }
146
147 fn mantissa_text(&self) -> String {
149 let mut s = String::new();
150 if self.negative {
151 s.push('-');
152 }
153 s.push(char::from(b'0' + self.digits[0]));
154 s.push('.');
155 let last = self.digits.iter().rposition(|&d| d != 0).unwrap_or(0);
156 for d in &self.digits[1..=last] {
157 s.push(char::from(b'0' + d));
158 }
159 s
160 }
161
162 pub fn to_source(&self) -> String {
165 if self.exponent == 0 {
166 self.mantissa_text()
167 } else {
168 format!("{}E{}", self.mantissa_text(), self.exponent)
169 }
170 }
171
172 pub fn parse(text: &str) -> Result<Real, Error> {
175 let bad = || Error::Parse(format!("not a real number: {text:?}"));
176 let t = text.trim();
177 let (negative, t) = match t.strip_prefix('-') {
178 Some(rest) => (true, rest),
179 None => (false, t.strip_prefix('+').unwrap_or(t)),
180 };
181 let (mant, exp) = match t.find(['e', 'E']) {
182 Some(i) => (&t[..i], t[i + 1..].parse::<i32>().map_err(|_| bad())?),
183 None => (t, 0),
184 };
185 let (int, frac) = mant.split_once('.').unwrap_or((mant, ""));
186 if (int.is_empty() && frac.is_empty())
187 || !int.bytes().chain(frac.bytes()).all(|b| b.is_ascii_digit())
188 {
189 return Err(bad());
190 }
191 let all: Vec<u8> = int.bytes().chain(frac.bytes()).map(|b| b - b'0').collect();
192 let Some(first) = all.iter().position(|&d| d != 0) else {
193 return Ok(Real::ZERO);
194 };
195 let int_len = i32::try_from(int.len()).map_err(|_| bad())?;
198 let first_i = i32::try_from(first).map_err(|_| bad())?;
199 let mut exponent = int_len - 1 - first_i + exp;
200 let sig = &all[first..];
201 let mut digits = [0u8; 12];
202 for (d, s) in digits.iter_mut().zip(sig) {
203 *d = *s;
204 }
205 if sig.get(12).is_some_and(|&d| d >= 5) {
206 let mut i = 12;
208 loop {
209 if i == 0 {
210 digits = [0; 12];
211 digits[0] = 1;
212 exponent += 1;
213 break;
214 }
215 i -= 1;
216 if digits[i] == 9 {
217 digits[i] = 0;
218 } else {
219 digits[i] += 1;
220 break;
221 }
222 }
223 }
224 if !(-499..=499).contains(&exponent) {
225 return Err(Error::Parse(format!(
226 "{text:?} is outside the calculator's range (exponents -499 to 499)"
227 )));
228 }
229 Ok(Real {
230 negative,
231 digits,
232 exponent,
233 })
234 }
235
236 pub fn from_f64(v: f64) -> Result<Real, Error> {
238 if !v.is_finite() {
239 return Err(Error::Parse(format!("{v} is not a finite number")));
240 }
241 Real::parse(&format!("{v:e}"))
242 }
243}
244
245impl fmt::Display for Real {
246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247 f.write_str(&self.to_source())
248 }
249}
250
251impl Serialize for Real {
252 fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
253 if self.is_zero() || F64_EXPONENTS.contains(&self.exponent) {
254 s.serialize_f64(self.to_f64())
255 } else {
256 s.serialize_str(&self.to_source())
257 }
258 }
259}
260
261impl<'de> Deserialize<'de> for Real {
262 fn deserialize<D: Deserializer<'de>>(d: D) -> std::result::Result<Real, D::Error> {
263 struct V;
264 impl Visitor<'_> for V {
265 type Value = Real;
266 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
267 f.write_str("a number or decimal text such as \"1.5E-400\"")
268 }
269 fn visit_f64<E: de::Error>(self, v: f64) -> std::result::Result<Real, E> {
270 Real::from_f64(v).map_err(E::custom)
271 }
272 fn visit_i64<E: de::Error>(self, v: i64) -> std::result::Result<Real, E> {
273 Real::parse(&v.to_string()).map_err(E::custom)
274 }
275 fn visit_u64<E: de::Error>(self, v: u64) -> std::result::Result<Real, E> {
276 Real::parse(&v.to_string()).map_err(E::custom)
277 }
278 fn visit_str<E: de::Error>(self, v: &str) -> std::result::Result<Real, E> {
279 Real::parse(v).map_err(E::custom)
280 }
281 }
282 d.deserialize_any(V)
283 }
284}
285
286#[derive(Clone, Debug, PartialEq, Eq)]
288pub struct Integer {
289 pub negative: bool,
291 pub digits: String,
294}
295
296impl Integer {
297 pub fn parse(text: &str) -> Result<Integer, Error> {
299 let t = text.trim();
300 let (negative, d) = match t.strip_prefix('-') {
301 Some(rest) => (true, rest),
302 None => (false, t.strip_prefix('+').unwrap_or(t)),
303 };
304 if d.is_empty() || !d.bytes().all(|b| b.is_ascii_digit()) {
305 return Err(Error::Parse(format!("not an integer: {text:?}")));
306 }
307 let digits = d.trim_start_matches('0');
308 let digits = if digits.is_empty() { "0" } else { digits };
309 Ok(Integer {
310 negative: negative && digits != "0",
311 digits: digits.to_string(),
312 })
313 }
314
315 pub fn from_nibbles(n: &[u8]) -> Result<Integer, DecodeError> {
319 let Some((&sign, digits)) = n.split_last() else {
320 return Err(DecodeError::new(0, Reason::EmptyInteger));
321 };
322 if let Some(at) = n.iter().position(|&d| d > 9) {
323 return Err(DecodeError::new(
324 at,
325 Reason::NotBcd {
326 kind: ObjectType::Integer,
327 hex: hex(n),
328 },
329 ));
330 }
331 let text: String = digits.iter().rev().map(|d| char::from(b'0' + d)).collect();
332 let text = text.trim_start_matches('0');
333 let digits = if text.is_empty() { "0" } else { text };
334 Ok(Integer {
335 negative: sign == 9 && digits != "0",
336 digits: digits.to_string(),
337 })
338 }
339
340 pub fn to_nibbles(&self) -> Vec<u8> {
342 if self.digits == "0" {
343 return vec![0];
344 }
345 let mut n: Vec<u8> = self.digits.bytes().rev().map(|b| b - b'0').collect();
346 n.push(if self.negative { 9 } else { 0 });
347 n
348 }
349
350 pub fn to_source(&self) -> String {
352 if self.negative {
353 format!("-{}", self.digits)
354 } else {
355 self.digits.clone()
356 }
357 }
358}
359
360impl Serialize for Integer {
361 fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
362 match self.to_source().parse::<i64>() {
364 Ok(v) if self.digits.len() <= 15 => s.serialize_i64(v),
365 _ => s.serialize_str(&self.to_source()),
366 }
367 }
368}
369
370impl<'de> Deserialize<'de> for Integer {
371 fn deserialize<D: Deserializer<'de>>(d: D) -> std::result::Result<Integer, D::Error> {
372 struct V;
373 impl Visitor<'_> for V {
374 type Value = Integer;
375 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
376 f.write_str("an integer or a string of decimal digits")
377 }
378 fn visit_i64<E: de::Error>(self, v: i64) -> std::result::Result<Integer, E> {
379 Integer::parse(&v.to_string()).map_err(E::custom)
380 }
381 fn visit_u64<E: de::Error>(self, v: u64) -> std::result::Result<Integer, E> {
382 Integer::parse(&v.to_string()).map_err(E::custom)
383 }
384 fn visit_f64<E: de::Error>(self, v: f64) -> std::result::Result<Integer, E> {
385 if v.fract() != 0.0 || !v.is_finite() || v.abs() > 9.0e15 {
386 return Err(E::custom(format!("{v} is not an exact integer")));
387 }
388 Integer::parse(&format!("{v:.0}")).map_err(E::custom)
389 }
390 fn visit_str<E: de::Error>(self, v: &str) -> std::result::Result<Integer, E> {
391 Integer::parse(v).map_err(E::custom)
392 }
393 }
394 d.deserialize_any(V)
395 }
396}
397
398#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
400#[serde(rename_all = "lowercase")]
401pub enum Base {
402 Hex,
404 Dec,
406 Oct,
408 Bin,
410}
411
412impl Base {
413 pub fn from_display(text: &str) -> Option<Base> {
415 let t = text.trim();
416 if !t.starts_with('#') {
417 return None;
418 }
419 match t.chars().last()? {
420 'h' => Some(Base::Hex),
421 'd' => Some(Base::Dec),
422 'o' => Some(Base::Oct),
423 'b' => Some(Base::Bin),
424 _ => None,
425 }
426 }
427
428 pub fn format(self, value: u64) -> String {
430 match self {
431 Base::Hex => format!("# {value:X}h"),
432 Base::Dec => format!("# {value}d"),
433 Base::Oct => format!("# {value:o}o"),
434 Base::Bin => format!("# {value:b}b"),
435 }
436 }
437}
438
439#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
441#[serde(untagged)]
442pub enum ArrayItem {
443 Row(Vec<ArrayItem>),
445 Item(Box<Object>),
447}
448
449#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
451#[serde(tag = "type", rename_all = "snake_case")]
452pub enum Object {
453 Real {
455 value: Real,
457 },
458 Integer {
460 value: Integer,
462 },
463 Complex {
465 re: Real,
467 im: Real,
469 },
470 String {
472 value: String,
474 },
475 Name {
477 value: String,
479 },
480 LocalName {
482 value: String,
484 },
485 Character {
487 value: String,
489 },
490 Binary {
492 value: u64,
494 #[serde(default, skip_serializing_if = "Option::is_none")]
496 base: Option<Base>,
497 #[serde(default, skip_serializing_if = "Option::is_none")]
499 text: Option<String>,
500 },
501 List {
503 items: Vec<Object>,
505 },
506 Tagged {
508 tag: String,
510 object: Box<Object>,
512 },
513 Unit {
515 value: Real,
517 #[serde(default, skip_serializing_if = "Option::is_none")]
519 unit: Option<String>,
520 },
521 Array {
523 dims: Vec<usize>,
525 items: Vec<ArrayItem>,
527 },
528 Program {
530 #[serde(default, skip_serializing_if = "Option::is_none")]
532 source: Option<String>,
533 },
534 Algebraic {
536 #[serde(default, skip_serializing_if = "Option::is_none")]
538 source: Option<String>,
539 },
540 Command {
543 #[serde(default, skip_serializing_if = "Option::is_none")]
546 name: Option<String>,
547 #[serde(default, skip_serializing_if = "Option::is_none")]
549 address: Option<u32>,
550 #[serde(default, skip_serializing_if = "Option::is_none")]
554 library: Option<u16>,
555 #[serde(default, skip_serializing_if = "Option::is_none")]
557 command: Option<u16>,
558 #[serde(default, skip_serializing_if = "Option::is_none")]
560 source: Option<String>,
561 },
562 Unknown {
564 prolog: String,
566 #[serde(default, skip_serializing_if = "Option::is_none")]
568 kind: Option<String>,
569 nibbles: usize,
571 hex: String,
573 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
575 truncated: bool,
576 #[serde(default, skip_serializing_if = "Option::is_none")]
578 source: Option<String>,
579 #[serde(default, skip_serializing_if = "Option::is_none")]
581 graphic: Option<Graphic>,
582 },
583}
584
585#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
590pub struct Graphic {
591 pub width: u32,
593 pub height: u32,
595 pub rows: String,
597}
598
599const MAX_GRAPHIC_PIXELS: u64 = 2048 * 2048;
601
602impl Graphic {
603 pub fn from_packed(width: u32, height: u32, packed: &[u8]) -> Result<Graphic, Error> {
609 let (_, row_bytes, h) = picture_size(width, height)?;
610 if packed.len() != row_bytes * h {
611 return Err(Error::Invalid(format!(
612 "{} bytes are not {width} by {height} pixels ({} bytes)",
613 packed.len(),
614 row_bytes * h
615 )));
616 }
617 let tail = match width % 8 {
619 0 => 0xFF,
620 n => 0xFFu8 << (8 - n),
621 };
622 let mut rows = String::with_capacity(packed.len() * 2);
623 for row in packed.chunks(row_bytes) {
624 for (i, &b) in row.iter().enumerate() {
625 let b = if i + 1 == row_bytes { b & tail } else { b };
626 rows.push_str(&format!("{b:02X}"));
627 }
628 }
629 Ok(Graphic {
630 width,
631 height,
632 rows,
633 })
634 }
635
636 pub fn to_grob(&self) -> Result<Vec<u8>, Error> {
640 let (width, height) = (self.width, self.height);
641 let (w, row_bytes, h) = picture_size(width, height)?;
642 let row_nibbles = row_bytes * 2;
643 let body = row_nibbles * h;
644 if self.rows.len() != body || !self.rows.is_ascii() {
645 return Err(Error::Invalid(format!(
646 "the rows of a {width} by {height} picture are {body} hex digits"
647 )));
648 }
649 let put =
650 |out: &mut Vec<u8>, v: u64| out.extend((0..5).map(|i| ((v >> (4 * i)) & 0xF) as u8));
651 let mut n = Vec::with_capacity(20 + body);
652 put(&mut n, u64::from(ObjectType::Graphic.prolog()));
653 put(&mut n, 15 + body as u64);
655 put(&mut n, u64::from(height));
656 put(&mut n, u64::from(width));
657 for y in 0..h {
658 let hex = &self.rows[y * row_nibbles..(y + 1) * row_nibbles];
659 let mut row = vec![0u8; row_nibbles];
660 for b in 0..row_nibbles / 2 {
661 let byte = u8::from_str_radix(&hex[b * 2..b * 2 + 2], 16)
662 .map_err(|_| Error::Invalid(format!("row {y} is not hex")))?;
663 for bit in 0..8 {
664 let x = b * 8 + bit;
665 if x < w && byte & (0x80 >> bit) != 0 {
666 row[x / 4] |= 1 << (x % 4);
667 }
668 }
669 }
670 n.extend(row);
671 }
672 Ok(n)
673 }
674}
675
676fn picture_size(width: u32, height: u32) -> Result<(usize, usize, usize), Error> {
680 let too_big = u64::from(width) * u64::from(height) > MAX_GRAPHIC_PIXELS;
681 match (usize::try_from(width), usize::try_from(height)) {
682 (Ok(w), Ok(h)) if w != 0 && h != 0 && !too_big => Ok((w, w.div_ceil(8), h)),
683 _ => Err(Error::Invalid(format!(
684 "a picture of {width} by {height} pixels cannot be a GROB here"
685 ))),
686 }
687}
688
689pub fn graphic(n: &[u8]) -> Option<Graphic> {
697 if field(n, 0, 5).ok()? != ObjectType::Graphic.prolog() {
698 return None;
699 }
700 let length = usize::try_from(field(n, 5, 5).ok()?).ok()?;
701 let height = field(n, 10, 5).ok()?;
702 let width = field(n, 15, 5).ok()?;
703 if width == 0 || height == 0 || u64::from(width) * u64::from(height) > MAX_GRAPHIC_PIXELS {
704 return None;
705 }
706 let row_nibbles = usize::try_from(width.div_ceil(8) * 2).ok()?;
707 let body = row_nibbles * usize::try_from(height).ok()?;
708 if length != 15 + body || n.len() < 20 + body {
710 return None;
711 }
712 let row_bytes = row_nibbles / 2;
713 let w = usize::try_from(width).ok()?;
714 let mut rows = String::with_capacity(body);
715 for y in 0..usize::try_from(height).ok()? {
716 let row = &n[20 + y * row_nibbles..20 + (y + 1) * row_nibbles];
717 for b in 0..row_bytes {
718 let mut byte = 0u8;
719 for bit in 0..8 {
720 let x = b * 8 + bit;
721 let on = (row[x / 4] >> (x % 4)) & 1 == 1;
722 if on && x < w {
723 byte |= 0x80 >> bit;
724 }
725 }
726 rows.push_str(&format!("{byte:02X}"));
727 }
728 }
729 Some(Graphic {
730 width,
731 height,
732 rows,
733 })
734}
735
736pub trait Memory {
738 fn nibble(&self, addr: u32) -> Option<u8>;
740}
741
742#[derive(Clone, Copy, Debug, Default)]
744pub struct NoMemory;
745
746impl Memory for NoMemory {
747 fn nibble(&self, _addr: u32) -> Option<u8> {
748 None
749 }
750}
751
752fn hex(nibbles: &[u8]) -> String {
753 nibbles
754 .iter()
755 .map(|&n| char::from_digit(u32::from(n & 0xF), 16).unwrap_or('?'))
756 .collect::<String>()
757 .to_uppercase()
758}
759
760type Result<T, E = DecodeError> = std::result::Result<T, E>;
762
763fn field(n: &[u8], at: usize, width: usize) -> Result<u32> {
764 read_field(n, at, width).ok_or(DecodeError::new(at, Reason::Truncated))
765}
766
767fn usize_field(n: &[u8], at: usize, width: usize) -> Result<usize> {
768 usize::try_from(field(n, at, width)?).map_err(|_| DecodeError::new(at, Reason::SizeOverflow))
769}
770
771fn chars(n: &[u8], at: usize, count: usize) -> Result<String> {
773 let bytes = (0..count)
774 .map(|i| field(n, at + 2 * i, 2).map(|b| b as u8))
775 .collect::<Result<Vec<u8>>>()?;
776 Ok(charset::decode(&bytes))
777}
778
779pub fn decode(nibbles: &[u8], mem: &dyn Memory) -> Result<Object> {
784 Reader::new(mem).decode(nibbles)
785}
786
787pub fn decode_at(addr: u32, mem: &dyn Memory) -> Result<Object> {
792 Reader::new(mem).decode_at(addr)
793}
794
795pub struct Reader<'a> {
800 decoder: Decoder<'a>,
801}
802
803impl fmt::Debug for Reader<'_> {
804 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
805 f.debug_struct("Reader")
806 .field("budget", &self.decoder.budget.get())
807 .field("nibbles", &self.decoder.nibbles.get())
808 .field("names", &self.decoder.names.is_some())
809 .finish_non_exhaustive()
810 }
811}
812
813impl<'a> Reader<'a> {
814 pub fn new(mem: &'a dyn Memory) -> Self {
817 Self {
818 decoder: Decoder::new(mem, None, Settings::default()),
819 }
820 }
821
822 pub fn with_names(mem: &'a dyn Memory, names: &'a NameTable, settings: Settings) -> Self {
825 Self {
826 decoder: Decoder::new(mem, Some(names), settings),
827 }
828 }
829
830 pub fn decode_at(&self, addr: u32) -> Result<Object> {
834 let d = &self.decoder;
835 if let Some(info) = d.names.and_then(|t| t.command_at(addr, d.mem)) {
836 d.charge(1)?;
837 return Ok(Object::Command {
838 name: info.name.map(str::to_string),
839 address: Some(addr),
840 library: Some(info.library),
841 command: Some(info.number),
842 source: None,
843 });
844 }
845 d.rom_object(addr, 0)
846 }
847
848 pub fn decode(&self, nibbles: &[u8]) -> Result<Object> {
850 match self.decoder.object(nibbles, 0, 0) {
851 Ok((obj, _)) => Ok(obj),
852 Err(e) if self.decoder.exhausted.get() || e.is_budget() => Err(e),
854 Err(_) => {
855 let prolog = field(nibbles, 0, 5)?;
856 Ok(unknown(prolog, nibbles))
857 }
858 }
859 }
860}
861
862fn real(n: &[u8], at: usize) -> Result<Real> {
864 Real::from_body(n.get(at..).unwrap_or_default()).map_err(|e| e.shifted(at))
865}
866
867fn unknown(prolog: u32, nibbles: &[u8]) -> Object {
868 let shown = nibbles.len().min(MAX_HEX_NIBBLES);
869 Object::Unknown {
870 prolog: format!("{prolog:05X}"),
871 kind: ObjectType::from_prolog(prolog).map(|t| t.name().to_string()),
872 nibbles: nibbles.len(),
873 hex: hex(&nibbles[..shown]),
874 truncated: shown < nibbles.len(),
875 source: None,
876 graphic: graphic(nibbles),
877 }
878}
879
880struct Decoder<'a> {
881 mem: &'a dyn Memory,
882 names: Option<&'a NameTable>,
885 settings: Settings,
887 budget: Cell<usize>,
889 nibbles: Cell<usize>,
891 cloned: Cell<usize>,
893 seen: RefCell<HashMap<(u32, usize), (Object, Cost)>>,
898 exhausted: Cell<bool>,
900}
901
902impl<'a> Decoder<'a> {
903 fn new(mem: &'a dyn Memory, names: Option<&'a NameTable>, settings: Settings) -> Self {
904 Self {
905 mem,
906 names,
907 settings,
908 budget: Cell::new(MAX_DECODED_OBJECTS),
909 nibbles: Cell::new(MAX_DECODED_NIBBLES),
910 cloned: Cell::new(MAX_CLONED_NIBBLES),
911 seen: RefCell::new(HashMap::new()),
912 exhausted: Cell::new(false),
913 }
914 }
915
916 fn charge(&self, count: usize) -> Result<()> {
918 let Some(left) = self.budget.get().checked_sub(count) else {
919 self.exhausted.set(true);
920 return Err(DecodeError::new(0, Reason::TooManyObjects));
921 };
922 self.budget.set(left);
923 Ok(())
924 }
925
926 fn charge_nibbles(&self, count: usize) -> Result<()> {
928 let Some(left) = self.nibbles.get().checked_sub(count) else {
929 self.exhausted.set(true);
930 return Err(DecodeError::new(0, Reason::TooManyNibbles));
931 };
932 self.nibbles.set(left);
933 Ok(())
934 }
935
936 fn charge_cloned(&self, count: usize) -> Result<()> {
938 let Some(left) = self.cloned.get().checked_sub(count) else {
939 self.exhausted.set(true);
940 return Err(DecodeError::new(0, Reason::TooManyCloned));
941 };
942 self.cloned.set(left);
943 Ok(())
944 }
945
946 fn left(&self) -> Cost {
949 Cost {
950 objects: self.budget.get(),
951 nibbles: self.nibbles.get() + self.cloned.get(),
952 }
953 }
954
955 fn object(&self, n: &[u8], at: usize, depth: usize) -> Result<(Object, usize)> {
957 if depth > MAX_DEPTH {
958 return Err(DecodeError::new(at, Reason::TooDeep));
959 }
960 self.charge(1)?;
961 let prolog = field(n, at, 5)?;
962 let ty = ObjectType::from_prolog(prolog)
963 .ok_or(DecodeError::new(at, Reason::UnknownProlog(prolog)))?;
964 let size = object_size(n, at)?;
965 let body = at + 5;
966 let obj = match ty {
967 ObjectType::Real => Object::Real {
968 value: real(n, body)?,
969 },
970 ObjectType::Complex => Object::Complex {
971 re: real(n, body)?,
972 im: real(n, body + 16)?,
973 },
974 ObjectType::String => {
975 let len = usize_field(n, body, 5)?;
976 let count = len
977 .checked_sub(5)
978 .ok_or(DecodeError::new(body, Reason::LengthTooSmall(ty)))?;
979 if count % 2 != 0 {
980 return Err(DecodeError::new(body, Reason::OddString));
981 }
982 Object::String {
983 value: chars(n, body + 5, count / 2)?,
984 }
985 }
986 ObjectType::BinaryInteger => {
987 let len = usize_field(n, body, 5)?;
988 let count = len
989 .checked_sub(5)
990 .ok_or(DecodeError::new(body, Reason::LengthTooSmall(ty)))?;
991 if count > 16 {
992 return Ok((unknown(prolog, &n[at..at + size]), size));
993 }
994 let value = n[body + 5..body + 5 + count]
995 .iter()
996 .rev()
997 .fold(0u64, |acc, &d| (acc << 4) | u64::from(d));
998 Object::Binary {
999 value,
1000 base: None,
1001 text: None,
1002 }
1003 }
1004 ObjectType::Integer => {
1005 let len = usize_field(n, body, 5)?;
1006 let count = len
1007 .checked_sub(5)
1008 .ok_or(DecodeError::new(body, Reason::LengthTooSmall(ty)))?;
1009 Object::Integer {
1010 value: Integer::from_nibbles(&n[body + 5..body + 5 + count])
1011 .map_err(|e| e.shifted(body + 5))?,
1012 }
1013 }
1014 ObjectType::GlobalName | ObjectType::LocalName => {
1015 let count = usize_field(n, body, 2)?;
1016 let value = chars(n, body + 2, count)?;
1017 if ty == ObjectType::GlobalName {
1018 Object::Name { value }
1019 } else {
1020 Object::LocalName { value }
1021 }
1022 }
1023 ObjectType::Character => Object::Character {
1024 value: chars(n, body, 1)?,
1025 },
1026 ObjectType::List => Object::List {
1027 items: self
1028 .elements(n, body, depth)?
1029 .into_iter()
1030 .map(element_object)
1031 .collect(),
1032 },
1033 ObjectType::Tagged => {
1034 let count = usize_field(n, body, 2)?;
1035 let tag = chars(n, body + 2, count)?;
1036 let (object, _) = self.element(n, body + 2 + 2 * count, depth)?;
1037 Object::Tagged {
1038 tag,
1039 object: Box::new(element_object(object)),
1040 }
1041 }
1042 ObjectType::Unit => {
1043 let mut elements = self.elements(n, body, depth)?.into_iter();
1044 let Some(Element::Object(Object::Real { value })) = elements.next() else {
1045 return Err(DecodeError::new(body, Reason::UnitWithoutReal));
1046 };
1047 let rest: Vec<Element> = elements.collect();
1048 let unit = self
1049 .names
1050 .and_then(|_| decompile::unit(&rest, &self.settings));
1051 Object::Unit { value, unit }
1052 }
1053 ObjectType::Array => self.array(n, at, size)?,
1054 ObjectType::SymbolicMatrix => self.symbolic_matrix(n, body, depth)?,
1055 ObjectType::Program => {
1056 let elements = self.elements(n, body, depth)?;
1057 Object::Program {
1058 source: self
1059 .names
1060 .map(|_| decompile::program(&elements, &self.settings)),
1061 }
1062 }
1063 ObjectType::Algebraic => self.symbolic(n, body, depth)?.0,
1064 ObjectType::XlibName => element_object(self.xlib(n, body)?),
1065 _ => unknown(prolog, &n[at..at + size]),
1066 };
1067 Ok((obj, size))
1068 }
1069
1070 fn symbolic(&self, n: &[u8], body: usize, depth: usize) -> Result<(Object, Vec<Element>)> {
1072 let elements = self.elements(n, body, depth)?;
1073 let object = Object::Algebraic {
1074 source: self
1075 .names
1076 .and_then(|_| decompile::algebraic(&elements, &self.settings)),
1077 };
1078 Ok((object, elements))
1079 }
1080
1081 fn elements(&self, n: &[u8], mut at: usize, depth: usize) -> Result<Vec<Element>> {
1083 let mut items = Vec::new();
1084 while field(n, at, 5)? != SEMI {
1085 let (obj, size) = self.element(n, at, depth)?;
1086 items.push(obj);
1087 at += size;
1088 }
1089 Ok(items)
1090 }
1091
1092 fn element(&self, n: &[u8], at: usize, depth: usize) -> Result<(Element, usize)> {
1094 let p = field(n, at, 5)?;
1095 match ObjectType::from_prolog(p) {
1096 Some(ObjectType::XlibName) => {
1097 self.charge(1)?;
1098 Ok((self.xlib(n, at + 5)?, 11))
1099 }
1100 Some(ObjectType::SystemBinary) => {
1101 self.charge(1)?;
1102 Ok((Element::SystemBinary(field(n, at + 5, 5)?), 10))
1103 }
1104 Some(ObjectType::Algebraic) if depth < MAX_DEPTH => {
1105 self.charge(1)?;
1106 let size = object_size(n, at)?;
1107 let (object, elements) = self.symbolic(n, at + 5, depth + 1)?;
1108 Ok((Element::Symbolic { object, elements }, size))
1109 }
1110 Some(_) => {
1111 let (obj, size) = self.object(n, at, depth + 1)?;
1112 Ok((Element::Object(obj), size))
1113 }
1114 None => Ok((self.pointer(p, depth + 1)?, 5)),
1115 }
1116 }
1117
1118 fn xlib(&self, n: &[u8], at: usize) -> Result<Element> {
1120 let lib = field(n, at, 3)?;
1121 let cmd = field(n, at + 3, 3)?;
1122 let (lib, cmd) = (lib as u16, cmd as u16);
1123 let info = self.names.and_then(|t| t.xlib(lib, cmd));
1124 Ok(Element::Command {
1125 name: info.and_then(|i| i.name).map(str::to_string),
1126 address: None,
1127 xlib: Some((lib, cmd)),
1128 arity: info.and_then(|i| i.arity),
1129 silent: false,
1130 })
1131 }
1132
1133 fn pointer(&self, p: u32, depth: usize) -> Result<Element> {
1137 if let Some(t) = self.names {
1138 if let Some(info) = t.command_at(p, self.mem) {
1139 self.charge(1)?;
1140 return Ok(Element::Command {
1141 name: info.name.map(str::to_string),
1142 address: Some(p),
1143 xlib: Some((info.library, info.number)),
1144 arity: info.arity,
1145 silent: info.silent,
1146 });
1147 }
1148 if let Some(m) = t.unit_markers() {
1149 let op = [
1150 (m.times, UnitOp::Times),
1151 (m.divide, UnitOp::Divide),
1152 (m.power, UnitOp::Power),
1153 (m.prefix, UnitOp::Prefix),
1154 (m.end, UnitOp::End),
1155 ]
1156 .into_iter()
1157 .find_map(|(a, op)| (a == p).then_some(op));
1158 if let Some(op) = op {
1159 self.charge(1)?;
1160 return Ok(Element::Unit(op));
1161 }
1162 }
1163 }
1164 let read = |at: u32, width: u32| -> Option<u32> {
1165 (0..width).rev().try_fold(0u32, |v, i| {
1166 Some((v << 4) | u32::from(self.mem.nibble(at.wrapping_add(i))?))
1167 })
1168 };
1169 let command = Element::Command {
1170 name: None,
1171 address: Some(p),
1172 xlib: None,
1173 arity: None,
1174 silent: false,
1175 };
1176 match read(p, 5).and_then(ObjectType::from_prolog) {
1177 None | Some(ObjectType::Program | ObjectType::Code) => {
1180 self.charge(1)?;
1181 Ok(command)
1182 }
1183 Some(ObjectType::SystemBinary) => {
1184 self.charge(1)?;
1185 Ok(read(p + 5, 5).map_or(command, Element::SystemBinary))
1186 }
1187 Some(ObjectType::XlibName) => {
1188 self.charge(1)?;
1189 let body: Option<Vec<u8>> = (0..6).map(|i| self.mem.nibble(p + 5 + i)).collect();
1190 match body {
1191 Some(b) => self.xlib(&b, 0),
1192 None => Ok(command),
1193 }
1194 }
1195 Some(_) => Ok(Element::Object(self.rom_object(p, depth)?)),
1196 }
1197 }
1198
1199 fn rom_object(&self, addr: u32, depth: usize) -> Result<Object> {
1202 if let Some((obj, cost)) = self.seen.borrow().get(&(addr, depth)) {
1203 self.charge(cost.objects)?;
1204 self.charge_cloned(cost.nibbles)?;
1205 return Ok(obj.clone());
1206 }
1207 let before = self.left();
1208 let obj = self.read_object(addr, depth)?;
1209 let after = self.left();
1210 let cost = Cost {
1211 objects: before.objects - after.objects,
1212 nibbles: before.nibbles - after.nibbles,
1213 };
1214 self.seen
1215 .borrow_mut()
1216 .insert((addr, depth), (obj.clone(), cost));
1217 Ok(obj)
1218 }
1219
1220 fn read_object(&self, addr: u32, depth: usize) -> Result<Object> {
1222 let read = |len: usize| -> Vec<u8> {
1224 (0..len)
1225 .map_while(|i| {
1226 u32::try_from(i)
1227 .ok()
1228 .and_then(|i| self.mem.nibble(addr.wrapping_add(i)))
1229 })
1230 .collect()
1231 };
1232 let head = read(5);
1233 if read_field(&head, 0, 5)
1234 .and_then(ObjectType::from_prolog)
1235 .is_none()
1236 {
1237 self.charge(1)?;
1240 return Ok(Object::Command {
1241 name: None,
1242 address: Some(addr),
1243 library: None,
1244 command: None,
1245 source: None,
1246 });
1247 }
1248 let mut len = 64;
1249 loop {
1250 let nib = read(len);
1251 match object_size(&nib, 0) {
1252 Ok(size) => {
1253 self.charge_nibbles(size)?;
1254 return match self.object(&nib, 0, depth) {
1255 Ok((obj, _)) => Ok(obj),
1256 Err(e) if e.is_budget() => Err(e),
1257 Err(e) => Err(e.in_memory(addr)),
1258 };
1259 }
1260 Err(_) if nib.len() == len && len < MAX_MEMORY_OBJECT => len *= 8,
1261 Err(e) => return Err(e.in_memory(addr)),
1262 }
1263 }
1264 }
1265
1266 fn symbolic_matrix(&self, n: &[u8], body: usize, depth: usize) -> Result<Object> {
1269 let items: Vec<Object> = self
1270 .elements(n, body, depth)?
1271 .into_iter()
1272 .map(element_object)
1273 .collect();
1274 let rows = !items.is_empty()
1275 && items
1276 .iter()
1277 .all(|o| matches!(o, Object::Array { dims, .. } if dims.len() == 1));
1278 if !rows {
1279 return Ok(Object::Array {
1280 dims: vec![items.len()],
1281 items: items
1282 .into_iter()
1283 .map(|o| ArrayItem::Item(Box::new(o)))
1284 .collect(),
1285 });
1286 }
1287 let cols = match items.first() {
1288 Some(Object::Array { dims, .. }) => dims[0],
1289 _ => 0,
1290 };
1291 Ok(Object::Array {
1292 dims: vec![items.len(), cols],
1293 items: items
1294 .into_iter()
1295 .map(|o| match o {
1296 Object::Array { items, .. } => ArrayItem::Row(items),
1297 o => ArrayItem::Item(Box::new(o)),
1298 })
1299 .collect(),
1300 })
1301 }
1302
1303 fn array(&self, n: &[u8], at: usize, size: usize) -> Result<Object> {
1305 let body = at + 5;
1306 let elem = field(n, body + 5, 5)?;
1307 let ndims = usize_field(n, body + 10, 5)?;
1308 if ndims > MAX_ARRAY_DIMS {
1309 return Err(DecodeError::new(body + 10, Reason::ArrayDimensions(ndims)));
1310 }
1311 let bad = || DecodeError::new(at, Reason::ArrayLength);
1312 let start = ndims
1315 .checked_mul(5)
1316 .and_then(|d| d.checked_add(body + 15))
1317 .filter(|&s| s <= at + size)
1318 .ok_or_else(bad)?;
1319 let dims = (0..ndims)
1320 .map(|i| usize_field(n, body + 15 + 5 * i, 5))
1321 .collect::<Result<Vec<usize>>>()?;
1322 let width = match ObjectType::from_prolog(elem) {
1323 Some(ObjectType::Real) => 16,
1324 Some(ObjectType::Complex) => 32,
1325 _ => return Ok(unknown(field(n, at, 5)?, &n[at..at + size])),
1326 };
1327 let mut count: usize = 1;
1330 for d in &dims {
1331 count = count
1332 .checked_mul(*d)
1333 .filter(|&c| c <= size)
1334 .ok_or_else(bad)?;
1335 }
1336 let end = count
1337 .checked_mul(width)
1338 .and_then(|b| b.checked_add(start))
1339 .ok_or_else(bad)?;
1340 if ndims == 0 || end > at + size {
1341 return Err(bad());
1342 }
1343 self.charge(count)?;
1344 let mut flat = Vec::with_capacity(count);
1345 for i in 0..count {
1346 let p = start + i * width;
1347 flat.push(if width == 16 {
1348 Object::Real { value: real(n, p)? }
1349 } else {
1350 Object::Complex {
1351 re: real(n, p)?,
1352 im: real(n, p + 16)?,
1353 }
1354 });
1355 }
1356 Ok(Object::Array {
1357 items: nest(&dims, &mut flat.into_iter()),
1358 dims,
1359 })
1360 }
1361}
1362
1363#[derive(Clone, Copy, Debug)]
1365struct Cost {
1366 objects: usize,
1367 nibbles: usize,
1368}
1369
1370fn element_object(e: Element) -> Object {
1372 match e {
1373 Element::Object(o) | Element::Symbolic { object: o, .. } => o,
1374 Element::Command {
1375 name,
1376 address,
1377 xlib,
1378 ..
1379 } => Object::Command {
1380 name,
1381 address,
1382 library: xlib.map(|x| x.0),
1383 command: xlib.map(|x| x.1),
1384 source: None,
1385 },
1386 Element::SystemBinary(v) => Object::Unknown {
1387 prolog: format!("{:05X}", ObjectType::SystemBinary.prolog()),
1388 kind: Some(ObjectType::SystemBinary.name().to_string()),
1389 nibbles: 10,
1390 hex: format!("{:05X}", ObjectType::SystemBinary.prolog())
1391 .chars()
1392 .rev()
1393 .chain(format!("{v:05X}").chars().rev())
1394 .collect(),
1395 truncated: false,
1396 source: None,
1397 graphic: None,
1398 },
1399 Element::Unit(_) => Object::List { items: Vec::new() },
1401 }
1402}
1403
1404fn nest(dims: &[usize], flat: &mut impl Iterator<Item = Object>) -> Vec<ArrayItem> {
1407 match dims {
1408 [] => Vec::new(),
1409 [n] => flat
1410 .take(*n)
1411 .map(|o| ArrayItem::Item(Box::new(o)))
1412 .collect(),
1413 [n, rest @ ..] => (0..*n).map(|_| ArrayItem::Row(nest(rest, flat))).collect(),
1414 }
1415}
1416
1417impl Object {
1418 pub fn needs_source(&self) -> bool {
1421 match self {
1422 Object::Program { source } | Object::Algebraic { source } => source.is_none(),
1423 Object::Command { name, source, .. } => name.is_none() && source.is_none(),
1424 Object::Unit { unit, .. } => unit.is_none(),
1425 Object::Unknown { source, .. } => source.is_none(),
1426 Object::List { items } => items.iter().any(Object::needs_source),
1427 Object::Tagged { object, .. } => object.needs_source(),
1428 _ => false,
1429 }
1430 }
1431
1432 pub fn set_base(&mut self, b: Base) {
1434 match self {
1435 Object::Binary { value, base, text } => {
1436 *base = Some(b);
1437 *text = Some(b.format(*value));
1438 }
1439 Object::List { items } => items.iter_mut().for_each(|o| o.set_base(b)),
1440 Object::Tagged { object, .. } => object.set_base(b),
1441 _ => {}
1442 }
1443 }
1444
1445 pub fn has_binary(&self) -> bool {
1447 match self {
1448 Object::Binary { .. } => true,
1449 Object::List { items } => items.iter().any(Object::has_binary),
1450 Object::Tagged { object, .. } => object.has_binary(),
1451 _ => false,
1452 }
1453 }
1454}
1455#[cfg(test)]
1456mod tests {
1457 use super::*;
1458 use serde_json::json;
1459
1460 fn nib(s: &str) -> Vec<u8> {
1462 s.chars().map(|c| c.to_digit(16).unwrap() as u8).collect()
1463 }
1464
1465 fn dec(s: &str) -> Object {
1466 decode(&nib(s), &NoMemory).unwrap()
1467 }
1468
1469 fn real(s: &str) -> Real {
1470 Real::parse(s).unwrap()
1471 }
1472
1473 fn cmd(address: u32) -> Object {
1475 Object::Command {
1476 name: None,
1477 address: Some(address),
1478 library: None,
1479 command: None,
1480 source: None,
1481 }
1482 }
1483
1484 fn put_field(out: &mut Vec<u8>, value: u64, width: usize) {
1485 out.extend((0..width).map(|i| ((value >> (4 * i)) & 0xF) as u8));
1486 }
1487
1488 #[test]
1492 fn reals_decode_exactly() {
1493 let o = dec("339207997389453562780");
1495 assert_eq!(
1496 o,
1497 Object::Real {
1498 value: real("8.72653549837E-3")
1499 }
1500 );
1501 assert_eq!(
1502 serde_json::to_value(&o).unwrap(),
1503 json!({"type": "real", "value": 0.00872653549837})
1504 );
1505 let o = dec("339200070000000000519");
1507 let Object::Real { value } = o else { panic!() };
1508 assert_eq!(value.to_source(), "-1.5E-300");
1509 assert!(value.negative);
1510 assert_eq!(
1511 dec("339200000000000000000"),
1512 Object::Real { value: Real::ZERO }
1513 );
1514 }
1515
1516 #[test]
1517 fn real_text_round_trips_and_rounds() {
1518 for s in [
1519 "4.79425538604E-1",
1520 "5.",
1521 "-1.5E-300",
1522 "9.99999999999E499",
1523 "1.E-499",
1524 "0.",
1525 ] {
1526 let r = real(s);
1527 assert_eq!(r.to_source(), s);
1528 assert_eq!(Real::from_body(&r.to_body()).unwrap(), r);
1529 }
1530 assert_eq!(real("0.479425538604").to_source(), "4.79425538604E-1");
1531 assert_eq!(real(".5").to_source(), "5.E-1");
1532 assert_eq!(real("123").to_source(), "1.23E2");
1533 assert_eq!(real("1.234567890125").to_source(), "1.23456789013");
1535 assert_eq!(real("9.999999999995").to_source(), "1.E1");
1536 assert_eq!(real("-0.000").to_source(), "0.");
1537 assert!(Real::parse("1E500").is_err());
1538 assert!(Real::parse("1.2.3").is_err());
1539 assert!(Real::parse("").is_err());
1540 assert!(Real::parse("e5").is_err());
1541 assert_eq!(
1542 Real::from_f64(0.479425538604).unwrap(),
1543 real("4.79425538604E-1")
1544 );
1545 }
1546
1547 #[test]
1548 fn reals_beyond_f64_serialize_as_text() {
1549 let r = Object::Real {
1550 value: real("1.5E-400"),
1551 };
1552 assert_eq!(
1553 serde_json::to_value(&r).unwrap(),
1554 json!({"type": "real", "value": "1.5E-400"})
1555 );
1556 let back: Object =
1557 serde_json::from_value(json!({"type": "real", "value": "1.5E-400"})).unwrap();
1558 assert_eq!(back, r);
1559 let five: Object = serde_json::from_value(json!({"type": "real", "value": 5})).unwrap();
1560 assert_eq!(five, Object::Real { value: real("5") });
1561 }
1562
1563 #[test]
1564 fn complex_string_binary() {
1565 assert_eq!(
1566 dec("7792000000000000000100000000000000029"),
1567 Object::Complex {
1568 re: real("1"),
1569 im: real("-2")
1570 }
1571 );
1572 assert_eq!(
1574 dec("C2A2011000849602BA87BB"),
1575 Object::String {
1576 value: "Hi «x»".into()
1577 }
1578 );
1579 let mut b = dec("E4A2051000FF00000000000000");
1580 assert_eq!(
1581 b,
1582 Object::Binary {
1583 value: 255,
1584 base: None,
1585 text: None
1586 }
1587 );
1588 b.set_base(Base::Hex);
1589 assert_eq!(
1590 serde_json::to_value(&b).unwrap(),
1591 json!({"type": "binary", "value": 255, "base": "hex", "text": "# FFh"})
1592 );
1593 assert_eq!(Base::from_display("# 255d"), Some(Base::Dec));
1594 assert_eq!(Base::from_display("42."), None);
1595 }
1596
1597 #[test]
1598 fn integers_49g() {
1599 assert_eq!(
1600 dec("416207000050"),
1601 Object::Integer {
1602 value: Integer::parse("5").unwrap()
1603 }
1604 );
1605 let big = dec("416209100098765432109876543219");
1606 assert_eq!(
1607 big,
1608 Object::Integer {
1609 value: Integer::parse("-1234567890123456789").unwrap()
1610 }
1611 );
1612 assert_eq!(
1613 serde_json::to_value(&big).unwrap(),
1614 json!({"type": "integer", "value": "-1234567890123456789"})
1615 );
1616 assert_eq!(
1617 dec("416206000000"),
1618 Object::Integer {
1619 value: Integer::parse("0").unwrap()
1620 }
1621 );
1622 assert_eq!(
1623 serde_json::to_value(dec("416207000050")).unwrap(),
1624 json!({"type": "integer", "value": 5})
1625 );
1626 }
1627
1628 #[test]
1629 fn names_tagged_and_lists_with_rom_pointers() {
1630 let list = dec(
1632 "47A209C2A2ED2A2C2A20700003784E20108547A20D13A2B2130E4A2051000A200000000000000B2130",
1633 );
1634 let Object::List { items } = &list else {
1635 panic!("{list:?}")
1636 };
1637 assert_eq!(items.len(), 6);
1638 assert_eq!(items[0], cmd(0x2A2C9));
1639 assert_eq!(items[2], Object::String { value: "s".into() });
1640 assert_eq!(items[3], Object::Name { value: "X".into() });
1641 assert_eq!(
1642 items[4],
1643 Object::List {
1644 items: vec![cmd(0x2A31D)]
1645 }
1646 );
1647 assert_eq!(
1648 items[5],
1649 Object::Binary {
1650 value: 42,
1651 base: None,
1652 text: None
1653 }
1654 );
1655 let t = dec("47A20CFA201045D13A2B2130");
1657 assert_eq!(
1658 t,
1659 Object::List {
1660 items: vec![Object::Tagged {
1661 tag: "T".into(),
1662 object: Box::new(cmd(0x2A31D))
1663 }]
1664 }
1665 );
1666 }
1667
1668 struct Rom;
1670 impl Memory for Rom {
1671 fn nibble(&self, addr: u32) -> Option<u8> {
1672 let (base, obj) = match addr {
1673 0x2A2C9..=0x2A2DD => (0x2A2C9, "339200000000000000010"),
1674 0x2A31D..=0x2A331 => (0x2A31D, "339200000000000000050"),
1675 0x10000..=0x10004 => (0x10000, "50001"),
1677 _ => return None,
1678 };
1679 nib(obj).get((addr - base) as usize).copied()
1680 }
1681 }
1682
1683 #[test]
1684 fn rom_pointers_resolve_through_memory() {
1685 let list = decode(&nib("47A209C2A2D13A200001B2130"), &Rom).unwrap();
1686 assert_eq!(
1687 list,
1688 Object::List {
1689 items: vec![
1690 Object::Real { value: real("1") },
1691 Object::Real { value: real("5") },
1692 cmd(0x10000),
1693 ]
1694 }
1695 );
1696 }
1697
1698 #[test]
1699 fn arrays() {
1700 let m = dec(
1701 "8E920950003392020000200002000000000000000000100000000000000020000000000000003000000000000000400",
1702 );
1703 let item = |s: &str| ArrayItem::Item(Box::new(Object::Real { value: real(s) }));
1704 assert_eq!(
1705 m,
1706 Object::Array {
1707 dims: vec![2, 2],
1708 items: vec![
1709 ArrayItem::Row(vec![item("1"), item("2")]),
1710 ArrayItem::Row(vec![item("3"), item("4")])
1711 ],
1712 }
1713 );
1714 assert_eq!(
1715 serde_json::to_value(&m).unwrap(),
1716 json!({"type": "array", "dims": [2, 2], "items": [
1717 [{"type": "real", "value": 1.0}, {"type": "real", "value": 2.0}],
1718 [{"type": "real", "value": 3.0}, {"type": "real", "value": 4.0}]]})
1719 );
1720 let v = dec(
1721 "8E9204500077920100002000000000000000000100000000000000020000000000000003000000000000000400",
1722 );
1723 let Object::Array { dims, items } = v else {
1724 panic!()
1725 };
1726 assert_eq!(dims, vec![2]);
1727 assert_eq!(
1728 items[1],
1729 ArrayItem::Item(Box::new(Object::Complex {
1730 re: real("3"),
1731 im: real("4")
1732 }))
1733 );
1734 }
1735
1736 #[test]
1737 fn array_dimensions_cannot_overflow() {
1738 let mut o = nib("8E920");
1741 put_field(&mut o, 0x23, 5);
1742 put_field(&mut o, 0x02933, 5);
1743 put_field(&mut o, 4, 5);
1744 for d in [0x80000u64, 0x80000, 0x80000, 8] {
1745 put_field(&mut o, d, 5);
1746 }
1747 assert_eq!(o.len(), 40);
1748 let Ok(Object::Unknown { prolog, .. }) = decode(&o, &NoMemory) else {
1749 panic!("decoded");
1750 };
1751 assert_eq!(prolog, "029E8");
1752 let mut e = nib("8E920");
1754 put_field(&mut e, 25, 5);
1755 put_field(&mut e, 0x02933, 5);
1756 put_field(&mut e, 2, 5);
1757 put_field(&mut e, 3, 5);
1758 put_field(&mut e, 0, 5);
1759 assert_eq!(
1760 decode(&e, &NoMemory).unwrap(),
1761 Object::Array {
1762 dims: vec![3, 0],
1763 items: vec![
1764 ArrayItem::Row(vec![]),
1765 ArrayItem::Row(vec![]),
1766 ArrayItem::Row(vec![])
1767 ],
1768 }
1769 );
1770 let mut z = nib("8E920");
1771 put_field(&mut z, 30, 5);
1772 put_field(&mut z, 0x02933, 5);
1773 put_field(&mut z, 3, 5);
1774 for d in [0x80000u64, 0x80000, 0] {
1775 put_field(&mut z, d, 5);
1776 }
1777 assert!(matches!(
1778 decode(&z, &NoMemory).unwrap(),
1779 Object::Unknown { .. }
1780 ));
1781 }
1782
1783 fn dims_array(ndims: usize) -> Vec<u8> {
1785 let mut o = nib("8E920");
1786 put_field(&mut o, (15 + 5 * ndims + 16) as u64, 5);
1787 put_field(&mut o, 0x02933, 5);
1788 put_field(&mut o, ndims as u64, 5);
1789 for _ in 0..ndims {
1790 put_field(&mut o, 1, 5);
1791 }
1792 o.extend(nib("0000000000000010"));
1793 o
1794 }
1795
1796 #[test]
1797 fn arrays_have_at_most_two_dimensions() {
1798 let item = ArrayItem::Item(Box::new(Object::Real { value: real("1") }));
1799 assert_eq!(
1800 dec(&hex(&dims_array(2))),
1801 Object::Array {
1802 dims: vec![1, 1],
1803 items: vec![ArrayItem::Row(vec![item])],
1804 }
1805 );
1806 for ndims in [3, 12_000] {
1809 let Ok(Object::Unknown { prolog, .. }) = decode(&dims_array(ndims), &NoMemory) else {
1810 panic!("{ndims} dimensions decoded");
1811 };
1812 assert_eq!(prolog, "029E8");
1813 let mut list = nib("47A20");
1814 list.extend(dims_array(ndims));
1815 list.extend(nib("B2130"));
1816 let e = Reader::new(&NoMemory)
1817 .decoder
1818 .object(&list, 0, 0)
1819 .unwrap_err();
1820 assert!(e.to_string().contains("dimensions"), "{e:#}");
1821 }
1822 }
1823
1824 fn grob(rows: &[&str]) -> Vec<u8> {
1829 let height = rows.len();
1830 let width = rows.first().map_or(0, |r| r.len());
1831 let row_nibbles = width.div_ceil(8) * 2;
1832 let mut n = Vec::new();
1833 let put = |v: usize, n: &mut Vec<u8>| {
1834 for i in 0..5 {
1835 n.push(((v >> (4 * i)) & 0xF) as u8);
1836 }
1837 };
1838 put(0x02B1E, &mut n);
1839 put(15 + row_nibbles * height, &mut n);
1840 put(height, &mut n);
1841 put(width, &mut n);
1842 for r in rows {
1843 let mut row = vec![0u8; row_nibbles];
1844 for (x, c) in r.chars().enumerate() {
1845 if c == '#' {
1846 row[x / 4] |= 1 << (x % 4);
1847 }
1848 }
1849 n.extend(row);
1850 }
1851 n
1852 }
1853
1854 #[test]
1855 fn a_grob_is_pictured_row_by_row_leftmost_pixel_first() {
1856 let g = graphic(&grob(&["#"])).unwrap();
1857 assert_eq!((g.width, g.height, g.rows.as_str()), (1, 1, "80"));
1858 let g = graphic(&grob(&["#.#.#", ".#.#.", "....#"])).unwrap();
1860 assert_eq!((g.width, g.height, g.rows.as_str()), (5, 3, "A85008"));
1861 let g = graphic(&grob(&["........#", "#........"])).unwrap();
1863 assert_eq!(g.rows, "00808000");
1864 let mut row = ".".repeat(131);
1866 row.replace_range(130..131, "#");
1867 let screen: Vec<&str> = std::iter::repeat_n(row.as_str(), 64).collect();
1868 let n = grob(&screen);
1869 assert_eq!(n.len(), 2196);
1870 assert_eq!(field(&n, 5, 5).unwrap(), 0x88F);
1871 let g = graphic(&n).unwrap();
1872 assert_eq!((g.width, g.height), (131, 64));
1873 assert_eq!(&g.rows[..34], "0000000000000000000000000000000020");
1874 let Object::Unknown {
1876 kind,
1877 graphic: Some(p),
1878 ..
1879 } = decode(&grob(&["##"]), &NoMemory).unwrap()
1880 else {
1881 panic!("not a pictured unknown");
1882 };
1883 assert_eq!((kind.as_deref(), p.rows.as_str()), (Some("Graphic"), "C0"));
1884 }
1885
1886 #[test]
1887 fn a_picture_encodes_as_the_grob_it_came_from() {
1888 for rows in [
1889 vec!["#"],
1890 vec!["#.#.#", ".#.#.", "....#"],
1891 vec!["........#", "#........"],
1892 vec!["#.......", "........"],
1893 ] {
1894 let n = grob(&rows);
1895 let g = graphic(&n).unwrap();
1896 assert_eq!(g.to_grob().unwrap(), n, "{rows:?}");
1897 assert_eq!(graphic(&g.to_grob().unwrap()), Some(g));
1898 }
1899 let mut packed = vec![0u8; 17 * 64];
1901 packed[16] = 0x20;
1902 packed[17 * 63] = 0x80;
1903 let g = Graphic::from_packed(131, 64, &packed).unwrap();
1904 let n = g.to_grob().unwrap();
1905 assert_eq!(n.len(), 2196);
1906 assert_eq!(field(&n, 5, 5).unwrap(), 0x88F);
1907 assert_eq!(
1908 (field(&n, 10, 5).unwrap(), field(&n, 15, 5).unwrap()),
1909 (64, 131)
1910 );
1911 assert_eq!(n[20 + 32], 0x4, "pixel 130 of row 0");
1912 assert_eq!(n[20 + 34 * 63], 0x1, "pixel 0 of row 63");
1913 assert_eq!(graphic(&n), Some(g));
1914 let g = Graphic::from_packed(131, 16, &[0xFF; 17 * 16]).unwrap();
1916 let n = g.to_grob().unwrap();
1917 assert_eq!(n.len(), 20 + 34 * 16);
1918 assert_eq!(graphic(&n), Some(g));
1919 }
1920
1921 #[test]
1922 fn packed_rows_lose_the_bits_beyond_the_width() {
1923 let g = Graphic::from_packed(5, 2, &[0xFF, 0x07]).unwrap();
1924 assert_eq!(g.rows, "F800");
1925 assert_eq!(graphic(&g.to_grob().unwrap()), Some(g));
1926 let g = Graphic::from_packed(16, 1, &[0xAB, 0xCD]).unwrap();
1927 assert_eq!(g.rows, "ABCD");
1928 }
1929
1930 #[test]
1931 fn a_picture_of_no_or_the_wrong_size_is_refused() {
1932 assert!(Graphic::from_packed(0, 1, &[]).is_err());
1933 assert!(Graphic::from_packed(1, 0, &[]).is_err());
1934 assert!(Graphic::from_packed(131, 64, &[0; 17 * 63]).is_err());
1935 assert!(Graphic::from_packed(4096, 4097, &[]).is_err());
1936 let bad = |rows: &str| {
1937 Graphic {
1938 width: 4,
1939 height: 1,
1940 rows: rows.into(),
1941 }
1942 .to_grob()
1943 .is_err()
1944 };
1945 assert!(bad("F"), "short");
1946 assert!(bad("F00"), "long");
1947 assert!(bad("G0"), "not hex");
1948 assert!(bad("é"), "not ASCII");
1949 }
1950
1951 #[test]
1952 fn a_grob_that_does_not_add_up_has_no_picture() {
1953 let mut n = grob(&["#.", ".#"]);
1954 n[5] ^= 1;
1955 assert_eq!(graphic(&n), None, "a wrong length");
1956 let n = grob(&["#.", ".#"]);
1957 assert_eq!(graphic(&n[..n.len() - 1]), None, "cut short");
1958 assert_eq!(graphic(&[0xE, 0x1, 0xB, 0x2, 0x1]), None, "no fields");
1959 assert_eq!(graphic(&grob(&[])), None, "0 by 0");
1962 assert_eq!(graphic(&grob(&[""; 5])), None, "0 wide, 5 high");
1963 assert!(matches!(
1964 decode(&grob(&[]), &NoMemory),
1965 Ok(Object::Unknown { graphic: None, .. })
1966 ));
1967 let mut big = vec![0xE, 0x1, 0xB, 0x2, 0x0];
1969 for v in [0u32, 0x1000, 0x1001] {
1970 big.extend((0..5).map(|i| ((v >> (4 * i)) & 0xF) as u8));
1971 }
1972 assert_eq!(graphic(&big), None, "4096 by 4097 pixels");
1973 let mut trailing = grob(&["#"]);
1974 trailing.push(0);
1975 assert_eq!(
1976 graphic(&trailing).map(|g| g.rows),
1977 Some("80".into()),
1978 "a trailing nibble is not the GROB's"
1979 );
1980 }
1981}