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 => self.secondary(n, body, depth)?.0,
1056 ObjectType::Algebraic => self.symbolic(n, body, depth)?.0,
1057 ObjectType::XlibName => element_object(self.xlib(n, body)?),
1058 _ => unknown(prolog, &n[at..at + size]),
1059 };
1060 Ok((obj, size))
1061 }
1062
1063 fn symbolic(&self, n: &[u8], body: usize, depth: usize) -> Result<(Object, Vec<Element>)> {
1065 let elements = self.elements(n, body, depth)?;
1066 let object = Object::Algebraic {
1067 source: self
1068 .names
1069 .and_then(|_| decompile::algebraic(&elements, &self.settings)),
1070 };
1071 Ok((object, elements))
1072 }
1073
1074 fn secondary(&self, n: &[u8], body: usize, depth: usize) -> Result<(Object, Vec<Element>)> {
1076 let elements = self.elements(n, body, depth)?;
1077 let object = Object::Program {
1078 source: self
1079 .names
1080 .map(|_| decompile::program(&elements, &self.settings)),
1081 };
1082 Ok((object, elements))
1083 }
1084
1085 fn elements(&self, n: &[u8], mut at: usize, depth: usize) -> Result<Vec<Element>> {
1087 let mut items = Vec::new();
1088 while field(n, at, 5)? != SEMI {
1089 let (obj, size) = self.element(n, at, depth)?;
1090 items.push(obj);
1091 at += size;
1092 }
1093 Ok(items)
1094 }
1095
1096 fn element(&self, n: &[u8], at: usize, depth: usize) -> Result<(Element, usize)> {
1098 let p = field(n, at, 5)?;
1099 match ObjectType::from_prolog(p) {
1100 Some(ObjectType::XlibName) => {
1101 self.charge(1)?;
1102 Ok((self.xlib(n, at + 5)?, 11))
1103 }
1104 Some(ObjectType::SystemBinary) => {
1105 self.charge(1)?;
1106 Ok((Element::SystemBinary(field(n, at + 5, 5)?), 10))
1107 }
1108 Some(ObjectType::Algebraic) if depth < MAX_DEPTH => {
1109 self.charge(1)?;
1110 let size = object_size(n, at)?;
1111 let (object, elements) = self.symbolic(n, at + 5, depth + 1)?;
1112 Ok((Element::Symbolic { object, elements }, size))
1113 }
1114 Some(ObjectType::Program) if depth < MAX_DEPTH => {
1115 self.charge(1)?;
1116 let size = object_size(n, at)?;
1117 let (object, elements) = self.secondary(n, at + 5, depth + 1)?;
1118 Ok((Element::Secondary { object, elements }, size))
1119 }
1120 Some(_) => {
1121 let (obj, size) = self.object(n, at, depth + 1)?;
1122 Ok((Element::Object(obj), size))
1123 }
1124 None => Ok((self.pointer(p, depth + 1)?, 5)),
1125 }
1126 }
1127
1128 fn xlib(&self, n: &[u8], at: usize) -> Result<Element> {
1130 let lib = field(n, at, 3)?;
1131 let cmd = field(n, at + 3, 3)?;
1132 let (lib, cmd) = (lib as u16, cmd as u16);
1133 let info = self.names.and_then(|t| t.xlib(lib, cmd));
1134 Ok(Element::Command {
1135 name: info.and_then(|i| i.name).map(str::to_string),
1136 address: None,
1137 xlib: Some((lib, cmd)),
1138 arity: info.and_then(|i| i.arity),
1139 silent: false,
1140 syntax: None,
1141 })
1142 }
1143
1144 fn pointer(&self, p: u32, depth: usize) -> Result<Element> {
1148 if let Some(t) = self.names {
1149 if let Some(info) = t.command_at(p, self.mem) {
1150 self.charge(1)?;
1151 return Ok(Element::Command {
1152 name: info.name.map(str::to_string),
1153 address: Some(p),
1154 xlib: Some((info.library, info.number)),
1155 arity: info.arity,
1156 silent: info.silent,
1157 syntax: info.syntax,
1158 });
1159 }
1160 if let Some(m) = t.unit_markers() {
1161 let op = [
1162 (m.times, UnitOp::Times),
1163 (m.divide, UnitOp::Divide),
1164 (m.power, UnitOp::Power),
1165 (m.prefix, UnitOp::Prefix),
1166 (m.end, UnitOp::End),
1167 ]
1168 .into_iter()
1169 .find_map(|(a, op)| (a == p).then_some(op));
1170 if let Some(op) = op {
1171 self.charge(1)?;
1172 return Ok(Element::Unit(op));
1173 }
1174 }
1175 }
1176 let read = |at: u32, width: u32| -> Option<u32> {
1177 (0..width).rev().try_fold(0u32, |v, i| {
1178 Some((v << 4) | u32::from(self.mem.nibble(at.wrapping_add(i))?))
1179 })
1180 };
1181 let command = Element::Command {
1182 name: None,
1183 address: Some(p),
1184 xlib: None,
1185 arity: None,
1186 silent: false,
1187 syntax: None,
1188 };
1189 match read(p, 5).and_then(ObjectType::from_prolog) {
1190 None | Some(ObjectType::Program | ObjectType::Code) => {
1193 self.charge(1)?;
1194 Ok(command)
1195 }
1196 Some(ObjectType::SystemBinary) => {
1197 self.charge(1)?;
1198 Ok(read(p + 5, 5).map_or(command, Element::SystemBinary))
1199 }
1200 Some(ObjectType::XlibName) => {
1201 self.charge(1)?;
1202 let body: Option<Vec<u8>> = (0..6).map(|i| self.mem.nibble(p + 5 + i)).collect();
1203 match body {
1204 Some(b) => self.xlib(&b, 0),
1205 None => Ok(command),
1206 }
1207 }
1208 Some(_) => Ok(Element::Object(self.rom_object(p, depth)?)),
1209 }
1210 }
1211
1212 fn rom_object(&self, addr: u32, depth: usize) -> Result<Object> {
1215 if let Some((obj, cost)) = self.seen.borrow().get(&(addr, depth)) {
1216 self.charge(cost.objects)?;
1217 self.charge_cloned(cost.nibbles)?;
1218 return Ok(obj.clone());
1219 }
1220 let before = self.left();
1221 let obj = self.read_object(addr, depth)?;
1222 let after = self.left();
1223 let cost = Cost {
1224 objects: before.objects - after.objects,
1225 nibbles: before.nibbles - after.nibbles,
1226 };
1227 self.seen
1228 .borrow_mut()
1229 .insert((addr, depth), (obj.clone(), cost));
1230 Ok(obj)
1231 }
1232
1233 fn read_object(&self, addr: u32, depth: usize) -> Result<Object> {
1235 let read = |len: usize| -> Vec<u8> {
1237 (0..len)
1238 .map_while(|i| {
1239 u32::try_from(i)
1240 .ok()
1241 .and_then(|i| self.mem.nibble(addr.wrapping_add(i)))
1242 })
1243 .collect()
1244 };
1245 let head = read(5);
1246 if read_field(&head, 0, 5)
1247 .and_then(ObjectType::from_prolog)
1248 .is_none()
1249 {
1250 self.charge(1)?;
1253 return Ok(Object::Command {
1254 name: None,
1255 address: Some(addr),
1256 library: None,
1257 command: None,
1258 source: None,
1259 });
1260 }
1261 let mut len = 64;
1262 loop {
1263 let nib = read(len);
1264 match object_size(&nib, 0) {
1265 Ok(size) => {
1266 self.charge_nibbles(size)?;
1267 return match self.object(&nib, 0, depth) {
1268 Ok((obj, _)) => Ok(obj),
1269 Err(e) if e.is_budget() => Err(e),
1270 Err(e) => Err(e.in_memory(addr)),
1271 };
1272 }
1273 Err(_) if nib.len() == len && len < MAX_MEMORY_OBJECT => len *= 8,
1274 Err(e) => return Err(e.in_memory(addr)),
1275 }
1276 }
1277 }
1278
1279 fn symbolic_matrix(&self, n: &[u8], body: usize, depth: usize) -> Result<Object> {
1282 let items: Vec<Object> = self
1283 .elements(n, body, depth)?
1284 .into_iter()
1285 .map(element_object)
1286 .collect();
1287 let rows = !items.is_empty()
1288 && items
1289 .iter()
1290 .all(|o| matches!(o, Object::Array { dims, .. } if dims.len() == 1));
1291 if !rows {
1292 return Ok(Object::Array {
1293 dims: vec![items.len()],
1294 items: items
1295 .into_iter()
1296 .map(|o| ArrayItem::Item(Box::new(o)))
1297 .collect(),
1298 });
1299 }
1300 let cols = match items.first() {
1301 Some(Object::Array { dims, .. }) => dims[0],
1302 _ => 0,
1303 };
1304 Ok(Object::Array {
1305 dims: vec![items.len(), cols],
1306 items: items
1307 .into_iter()
1308 .map(|o| match o {
1309 Object::Array { items, .. } => ArrayItem::Row(items),
1310 o => ArrayItem::Item(Box::new(o)),
1311 })
1312 .collect(),
1313 })
1314 }
1315
1316 fn array(&self, n: &[u8], at: usize, size: usize) -> Result<Object> {
1318 let body = at + 5;
1319 let elem = field(n, body + 5, 5)?;
1320 let ndims = usize_field(n, body + 10, 5)?;
1321 if ndims > MAX_ARRAY_DIMS {
1322 return Err(DecodeError::new(body + 10, Reason::ArrayDimensions(ndims)));
1323 }
1324 let bad = || DecodeError::new(at, Reason::ArrayLength);
1325 let start = ndims
1328 .checked_mul(5)
1329 .and_then(|d| d.checked_add(body + 15))
1330 .filter(|&s| s <= at + size)
1331 .ok_or_else(bad)?;
1332 let dims = (0..ndims)
1333 .map(|i| usize_field(n, body + 15 + 5 * i, 5))
1334 .collect::<Result<Vec<usize>>>()?;
1335 let width = match ObjectType::from_prolog(elem) {
1336 Some(ObjectType::Real) => 16,
1337 Some(ObjectType::Complex) => 32,
1338 _ => return Ok(unknown(field(n, at, 5)?, &n[at..at + size])),
1339 };
1340 let mut count: usize = 1;
1343 for d in &dims {
1344 count = count
1345 .checked_mul(*d)
1346 .filter(|&c| c <= size)
1347 .ok_or_else(bad)?;
1348 }
1349 let end = count
1350 .checked_mul(width)
1351 .and_then(|b| b.checked_add(start))
1352 .ok_or_else(bad)?;
1353 if ndims == 0 || end > at + size {
1354 return Err(bad());
1355 }
1356 self.charge(count)?;
1357 let mut flat = Vec::with_capacity(count);
1358 for i in 0..count {
1359 let p = start + i * width;
1360 flat.push(if width == 16 {
1361 Object::Real { value: real(n, p)? }
1362 } else {
1363 Object::Complex {
1364 re: real(n, p)?,
1365 im: real(n, p + 16)?,
1366 }
1367 });
1368 }
1369 Ok(Object::Array {
1370 items: nest(&dims, &mut flat.into_iter()),
1371 dims,
1372 })
1373 }
1374}
1375
1376#[derive(Clone, Copy, Debug)]
1378struct Cost {
1379 objects: usize,
1380 nibbles: usize,
1381}
1382
1383fn element_object(e: Element) -> Object {
1385 match e {
1386 Element::Object(o)
1387 | Element::Symbolic { object: o, .. }
1388 | Element::Secondary { object: o, .. } => o,
1389 Element::Command {
1390 name,
1391 address,
1392 xlib,
1393 ..
1394 } => Object::Command {
1395 name,
1396 address,
1397 library: xlib.map(|x| x.0),
1398 command: xlib.map(|x| x.1),
1399 source: None,
1400 },
1401 Element::SystemBinary(v) => Object::Unknown {
1402 prolog: format!("{:05X}", ObjectType::SystemBinary.prolog()),
1403 kind: Some(ObjectType::SystemBinary.name().to_string()),
1404 nibbles: 10,
1405 hex: format!("{:05X}", ObjectType::SystemBinary.prolog())
1406 .chars()
1407 .rev()
1408 .chain(format!("{v:05X}").chars().rev())
1409 .collect(),
1410 truncated: false,
1411 source: None,
1412 graphic: None,
1413 },
1414 Element::Unit(_) => Object::List { items: Vec::new() },
1416 }
1417}
1418
1419fn nest(dims: &[usize], flat: &mut impl Iterator<Item = Object>) -> Vec<ArrayItem> {
1422 match dims {
1423 [] => Vec::new(),
1424 [n] => flat
1425 .take(*n)
1426 .map(|o| ArrayItem::Item(Box::new(o)))
1427 .collect(),
1428 [n, rest @ ..] => (0..*n).map(|_| ArrayItem::Row(nest(rest, flat))).collect(),
1429 }
1430}
1431
1432impl Object {
1433 pub fn needs_source(&self) -> bool {
1436 match self {
1437 Object::Program { source } | Object::Algebraic { source } => source.is_none(),
1438 Object::Command { name, source, .. } => name.is_none() && source.is_none(),
1439 Object::Unit { unit, .. } => unit.is_none(),
1440 Object::Unknown { source, .. } => source.is_none(),
1441 Object::List { items } => items.iter().any(Object::needs_source),
1442 Object::Tagged { object, .. } => object.needs_source(),
1443 _ => false,
1444 }
1445 }
1446
1447 pub fn set_base(&mut self, b: Base) {
1449 match self {
1450 Object::Binary { value, base, text } => {
1451 *base = Some(b);
1452 *text = Some(b.format(*value));
1453 }
1454 Object::List { items } => items.iter_mut().for_each(|o| o.set_base(b)),
1455 Object::Tagged { object, .. } => object.set_base(b),
1456 _ => {}
1457 }
1458 }
1459
1460 pub fn has_binary(&self) -> bool {
1462 match self {
1463 Object::Binary { .. } => true,
1464 Object::List { items } => items.iter().any(Object::has_binary),
1465 Object::Tagged { object, .. } => object.has_binary(),
1466 _ => false,
1467 }
1468 }
1469}
1470#[cfg(test)]
1471mod tests {
1472 use super::*;
1473 use serde_json::json;
1474
1475 fn nib(s: &str) -> Vec<u8> {
1477 s.chars().map(|c| c.to_digit(16).unwrap() as u8).collect()
1478 }
1479
1480 fn dec(s: &str) -> Object {
1481 decode(&nib(s), &NoMemory).unwrap()
1482 }
1483
1484 fn real(s: &str) -> Real {
1485 Real::parse(s).unwrap()
1486 }
1487
1488 fn cmd(address: u32) -> Object {
1490 Object::Command {
1491 name: None,
1492 address: Some(address),
1493 library: None,
1494 command: None,
1495 source: None,
1496 }
1497 }
1498
1499 fn put_field(out: &mut Vec<u8>, value: u64, width: usize) {
1500 out.extend((0..width).map(|i| ((value >> (4 * i)) & 0xF) as u8));
1501 }
1502
1503 #[test]
1507 fn reals_decode_exactly() {
1508 let o = dec("339207997389453562780");
1510 assert_eq!(
1511 o,
1512 Object::Real {
1513 value: real("8.72653549837E-3")
1514 }
1515 );
1516 assert_eq!(
1517 serde_json::to_value(&o).unwrap(),
1518 json!({"type": "real", "value": 0.00872653549837})
1519 );
1520 let o = dec("339200070000000000519");
1522 let Object::Real { value } = o else { panic!() };
1523 assert_eq!(value.to_source(), "-1.5E-300");
1524 assert!(value.negative);
1525 assert_eq!(
1526 dec("339200000000000000000"),
1527 Object::Real { value: Real::ZERO }
1528 );
1529 }
1530
1531 #[test]
1532 fn real_text_round_trips_and_rounds() {
1533 for s in [
1534 "4.79425538604E-1",
1535 "5.",
1536 "-1.5E-300",
1537 "9.99999999999E499",
1538 "1.E-499",
1539 "0.",
1540 ] {
1541 let r = real(s);
1542 assert_eq!(r.to_source(), s);
1543 assert_eq!(Real::from_body(&r.to_body()).unwrap(), r);
1544 }
1545 assert_eq!(real("0.479425538604").to_source(), "4.79425538604E-1");
1546 assert_eq!(real(".5").to_source(), "5.E-1");
1547 assert_eq!(real("123").to_source(), "1.23E2");
1548 assert_eq!(real("1.234567890125").to_source(), "1.23456789013");
1550 assert_eq!(real("9.999999999995").to_source(), "1.E1");
1551 assert_eq!(real("-0.000").to_source(), "0.");
1552 assert!(Real::parse("1E500").is_err());
1553 assert!(Real::parse("1.2.3").is_err());
1554 assert!(Real::parse("").is_err());
1555 assert!(Real::parse("e5").is_err());
1556 assert_eq!(
1557 Real::from_f64(0.479425538604).unwrap(),
1558 real("4.79425538604E-1")
1559 );
1560 }
1561
1562 #[test]
1563 fn reals_beyond_f64_serialize_as_text() {
1564 let r = Object::Real {
1565 value: real("1.5E-400"),
1566 };
1567 assert_eq!(
1568 serde_json::to_value(&r).unwrap(),
1569 json!({"type": "real", "value": "1.5E-400"})
1570 );
1571 let back: Object =
1572 serde_json::from_value(json!({"type": "real", "value": "1.5E-400"})).unwrap();
1573 assert_eq!(back, r);
1574 let five: Object = serde_json::from_value(json!({"type": "real", "value": 5})).unwrap();
1575 assert_eq!(five, Object::Real { value: real("5") });
1576 }
1577
1578 #[test]
1579 fn complex_string_binary() {
1580 assert_eq!(
1581 dec("7792000000000000000100000000000000029"),
1582 Object::Complex {
1583 re: real("1"),
1584 im: real("-2")
1585 }
1586 );
1587 assert_eq!(
1589 dec("C2A2011000849602BA87BB"),
1590 Object::String {
1591 value: "Hi «x»".into()
1592 }
1593 );
1594 let mut b = dec("E4A2051000FF00000000000000");
1595 assert_eq!(
1596 b,
1597 Object::Binary {
1598 value: 255,
1599 base: None,
1600 text: None
1601 }
1602 );
1603 b.set_base(Base::Hex);
1604 assert_eq!(
1605 serde_json::to_value(&b).unwrap(),
1606 json!({"type": "binary", "value": 255, "base": "hex", "text": "# FFh"})
1607 );
1608 assert_eq!(Base::from_display("# 255d"), Some(Base::Dec));
1609 assert_eq!(Base::from_display("42."), None);
1610 }
1611
1612 #[test]
1613 fn integers_49g() {
1614 assert_eq!(
1615 dec("416207000050"),
1616 Object::Integer {
1617 value: Integer::parse("5").unwrap()
1618 }
1619 );
1620 let big = dec("416209100098765432109876543219");
1621 assert_eq!(
1622 big,
1623 Object::Integer {
1624 value: Integer::parse("-1234567890123456789").unwrap()
1625 }
1626 );
1627 assert_eq!(
1628 serde_json::to_value(&big).unwrap(),
1629 json!({"type": "integer", "value": "-1234567890123456789"})
1630 );
1631 assert_eq!(
1632 dec("416206000000"),
1633 Object::Integer {
1634 value: Integer::parse("0").unwrap()
1635 }
1636 );
1637 assert_eq!(
1638 serde_json::to_value(dec("416207000050")).unwrap(),
1639 json!({"type": "integer", "value": 5})
1640 );
1641 }
1642
1643 #[test]
1644 fn names_tagged_and_lists_with_rom_pointers() {
1645 let list = dec(
1647 "47A209C2A2ED2A2C2A20700003784E20108547A20D13A2B2130E4A2051000A200000000000000B2130",
1648 );
1649 let Object::List { items } = &list else {
1650 panic!("{list:?}")
1651 };
1652 assert_eq!(items.len(), 6);
1653 assert_eq!(items[0], cmd(0x2A2C9));
1654 assert_eq!(items[2], Object::String { value: "s".into() });
1655 assert_eq!(items[3], Object::Name { value: "X".into() });
1656 assert_eq!(
1657 items[4],
1658 Object::List {
1659 items: vec![cmd(0x2A31D)]
1660 }
1661 );
1662 assert_eq!(
1663 items[5],
1664 Object::Binary {
1665 value: 42,
1666 base: None,
1667 text: None
1668 }
1669 );
1670 let t = dec("47A20CFA201045D13A2B2130");
1672 assert_eq!(
1673 t,
1674 Object::List {
1675 items: vec![Object::Tagged {
1676 tag: "T".into(),
1677 object: Box::new(cmd(0x2A31D))
1678 }]
1679 }
1680 );
1681 }
1682
1683 struct Rom;
1685 impl Memory for Rom {
1686 fn nibble(&self, addr: u32) -> Option<u8> {
1687 let (base, obj) = match addr {
1688 0x2A2C9..=0x2A2DD => (0x2A2C9, "339200000000000000010"),
1689 0x2A31D..=0x2A331 => (0x2A31D, "339200000000000000050"),
1690 0x10000..=0x10004 => (0x10000, "50001"),
1692 _ => return None,
1693 };
1694 nib(obj).get((addr - base) as usize).copied()
1695 }
1696 }
1697
1698 #[test]
1699 fn rom_pointers_resolve_through_memory() {
1700 let list = decode(&nib("47A209C2A2D13A200001B2130"), &Rom).unwrap();
1701 assert_eq!(
1702 list,
1703 Object::List {
1704 items: vec![
1705 Object::Real { value: real("1") },
1706 Object::Real { value: real("5") },
1707 cmd(0x10000),
1708 ]
1709 }
1710 );
1711 }
1712
1713 #[test]
1714 fn arrays() {
1715 let m = dec(
1716 "8E920950003392020000200002000000000000000000100000000000000020000000000000003000000000000000400",
1717 );
1718 let item = |s: &str| ArrayItem::Item(Box::new(Object::Real { value: real(s) }));
1719 assert_eq!(
1720 m,
1721 Object::Array {
1722 dims: vec![2, 2],
1723 items: vec![
1724 ArrayItem::Row(vec![item("1"), item("2")]),
1725 ArrayItem::Row(vec![item("3"), item("4")])
1726 ],
1727 }
1728 );
1729 assert_eq!(
1730 serde_json::to_value(&m).unwrap(),
1731 json!({"type": "array", "dims": [2, 2], "items": [
1732 [{"type": "real", "value": 1.0}, {"type": "real", "value": 2.0}],
1733 [{"type": "real", "value": 3.0}, {"type": "real", "value": 4.0}]]})
1734 );
1735 let v = dec(
1736 "8E9204500077920100002000000000000000000100000000000000020000000000000003000000000000000400",
1737 );
1738 let Object::Array { dims, items } = v else {
1739 panic!()
1740 };
1741 assert_eq!(dims, vec![2]);
1742 assert_eq!(
1743 items[1],
1744 ArrayItem::Item(Box::new(Object::Complex {
1745 re: real("3"),
1746 im: real("4")
1747 }))
1748 );
1749 }
1750
1751 #[test]
1752 fn array_dimensions_cannot_overflow() {
1753 let mut o = nib("8E920");
1756 put_field(&mut o, 0x23, 5);
1757 put_field(&mut o, 0x02933, 5);
1758 put_field(&mut o, 4, 5);
1759 for d in [0x80000u64, 0x80000, 0x80000, 8] {
1760 put_field(&mut o, d, 5);
1761 }
1762 assert_eq!(o.len(), 40);
1763 let Ok(Object::Unknown { prolog, .. }) = decode(&o, &NoMemory) else {
1764 panic!("decoded");
1765 };
1766 assert_eq!(prolog, "029E8");
1767 let mut e = nib("8E920");
1769 put_field(&mut e, 25, 5);
1770 put_field(&mut e, 0x02933, 5);
1771 put_field(&mut e, 2, 5);
1772 put_field(&mut e, 3, 5);
1773 put_field(&mut e, 0, 5);
1774 assert_eq!(
1775 decode(&e, &NoMemory).unwrap(),
1776 Object::Array {
1777 dims: vec![3, 0],
1778 items: vec![
1779 ArrayItem::Row(vec![]),
1780 ArrayItem::Row(vec![]),
1781 ArrayItem::Row(vec![])
1782 ],
1783 }
1784 );
1785 let mut z = nib("8E920");
1786 put_field(&mut z, 30, 5);
1787 put_field(&mut z, 0x02933, 5);
1788 put_field(&mut z, 3, 5);
1789 for d in [0x80000u64, 0x80000, 0] {
1790 put_field(&mut z, d, 5);
1791 }
1792 assert!(matches!(
1793 decode(&z, &NoMemory).unwrap(),
1794 Object::Unknown { .. }
1795 ));
1796 }
1797
1798 fn dims_array(ndims: usize) -> Vec<u8> {
1800 let mut o = nib("8E920");
1801 put_field(&mut o, (15 + 5 * ndims + 16) as u64, 5);
1802 put_field(&mut o, 0x02933, 5);
1803 put_field(&mut o, ndims as u64, 5);
1804 for _ in 0..ndims {
1805 put_field(&mut o, 1, 5);
1806 }
1807 o.extend(nib("0000000000000010"));
1808 o
1809 }
1810
1811 #[test]
1812 fn arrays_have_at_most_two_dimensions() {
1813 let item = ArrayItem::Item(Box::new(Object::Real { value: real("1") }));
1814 assert_eq!(
1815 dec(&hex(&dims_array(2))),
1816 Object::Array {
1817 dims: vec![1, 1],
1818 items: vec![ArrayItem::Row(vec![item])],
1819 }
1820 );
1821 for ndims in [3, 12_000] {
1824 let Ok(Object::Unknown { prolog, .. }) = decode(&dims_array(ndims), &NoMemory) else {
1825 panic!("{ndims} dimensions decoded");
1826 };
1827 assert_eq!(prolog, "029E8");
1828 let mut list = nib("47A20");
1829 list.extend(dims_array(ndims));
1830 list.extend(nib("B2130"));
1831 let e = Reader::new(&NoMemory)
1832 .decoder
1833 .object(&list, 0, 0)
1834 .unwrap_err();
1835 assert!(e.to_string().contains("dimensions"), "{e:#}");
1836 }
1837 }
1838
1839 fn grob(rows: &[&str]) -> Vec<u8> {
1844 let height = rows.len();
1845 let width = rows.first().map_or(0, |r| r.len());
1846 let row_nibbles = width.div_ceil(8) * 2;
1847 let mut n = Vec::new();
1848 let put = |v: usize, n: &mut Vec<u8>| {
1849 for i in 0..5 {
1850 n.push(((v >> (4 * i)) & 0xF) as u8);
1851 }
1852 };
1853 put(0x02B1E, &mut n);
1854 put(15 + row_nibbles * height, &mut n);
1855 put(height, &mut n);
1856 put(width, &mut n);
1857 for r in rows {
1858 let mut row = vec![0u8; row_nibbles];
1859 for (x, c) in r.chars().enumerate() {
1860 if c == '#' {
1861 row[x / 4] |= 1 << (x % 4);
1862 }
1863 }
1864 n.extend(row);
1865 }
1866 n
1867 }
1868
1869 #[test]
1870 fn a_grob_is_pictured_row_by_row_leftmost_pixel_first() {
1871 let g = graphic(&grob(&["#"])).unwrap();
1872 assert_eq!((g.width, g.height, g.rows.as_str()), (1, 1, "80"));
1873 let g = graphic(&grob(&["#.#.#", ".#.#.", "....#"])).unwrap();
1875 assert_eq!((g.width, g.height, g.rows.as_str()), (5, 3, "A85008"));
1876 let g = graphic(&grob(&["........#", "#........"])).unwrap();
1878 assert_eq!(g.rows, "00808000");
1879 let mut row = ".".repeat(131);
1881 row.replace_range(130..131, "#");
1882 let screen: Vec<&str> = std::iter::repeat_n(row.as_str(), 64).collect();
1883 let n = grob(&screen);
1884 assert_eq!(n.len(), 2196);
1885 assert_eq!(field(&n, 5, 5).unwrap(), 0x88F);
1886 let g = graphic(&n).unwrap();
1887 assert_eq!((g.width, g.height), (131, 64));
1888 assert_eq!(&g.rows[..34], "0000000000000000000000000000000020");
1889 let Object::Unknown {
1891 kind,
1892 graphic: Some(p),
1893 ..
1894 } = decode(&grob(&["##"]), &NoMemory).unwrap()
1895 else {
1896 panic!("not a pictured unknown");
1897 };
1898 assert_eq!((kind.as_deref(), p.rows.as_str()), (Some("Graphic"), "C0"));
1899 }
1900
1901 #[test]
1902 fn a_picture_encodes_as_the_grob_it_came_from() {
1903 for rows in [
1904 vec!["#"],
1905 vec!["#.#.#", ".#.#.", "....#"],
1906 vec!["........#", "#........"],
1907 vec!["#.......", "........"],
1908 ] {
1909 let n = grob(&rows);
1910 let g = graphic(&n).unwrap();
1911 assert_eq!(g.to_grob().unwrap(), n, "{rows:?}");
1912 assert_eq!(graphic(&g.to_grob().unwrap()), Some(g));
1913 }
1914 let mut packed = vec![0u8; 17 * 64];
1916 packed[16] = 0x20;
1917 packed[17 * 63] = 0x80;
1918 let g = Graphic::from_packed(131, 64, &packed).unwrap();
1919 let n = g.to_grob().unwrap();
1920 assert_eq!(n.len(), 2196);
1921 assert_eq!(field(&n, 5, 5).unwrap(), 0x88F);
1922 assert_eq!(
1923 (field(&n, 10, 5).unwrap(), field(&n, 15, 5).unwrap()),
1924 (64, 131)
1925 );
1926 assert_eq!(n[20 + 32], 0x4, "pixel 130 of row 0");
1927 assert_eq!(n[20 + 34 * 63], 0x1, "pixel 0 of row 63");
1928 assert_eq!(graphic(&n), Some(g));
1929 let g = Graphic::from_packed(131, 16, &[0xFF; 17 * 16]).unwrap();
1931 let n = g.to_grob().unwrap();
1932 assert_eq!(n.len(), 20 + 34 * 16);
1933 assert_eq!(graphic(&n), Some(g));
1934 }
1935
1936 #[test]
1937 fn packed_rows_lose_the_bits_beyond_the_width() {
1938 let g = Graphic::from_packed(5, 2, &[0xFF, 0x07]).unwrap();
1939 assert_eq!(g.rows, "F800");
1940 assert_eq!(graphic(&g.to_grob().unwrap()), Some(g));
1941 let g = Graphic::from_packed(16, 1, &[0xAB, 0xCD]).unwrap();
1942 assert_eq!(g.rows, "ABCD");
1943 }
1944
1945 #[test]
1946 fn a_picture_of_no_or_the_wrong_size_is_refused() {
1947 assert!(Graphic::from_packed(0, 1, &[]).is_err());
1948 assert!(Graphic::from_packed(1, 0, &[]).is_err());
1949 assert!(Graphic::from_packed(131, 64, &[0; 17 * 63]).is_err());
1950 assert!(Graphic::from_packed(4096, 4097, &[]).is_err());
1951 let bad = |rows: &str| {
1952 Graphic {
1953 width: 4,
1954 height: 1,
1955 rows: rows.into(),
1956 }
1957 .to_grob()
1958 .is_err()
1959 };
1960 assert!(bad("F"), "short");
1961 assert!(bad("F00"), "long");
1962 assert!(bad("G0"), "not hex");
1963 assert!(bad("é"), "not ASCII");
1964 }
1965
1966 #[test]
1967 fn a_grob_that_does_not_add_up_has_no_picture() {
1968 let mut n = grob(&["#.", ".#"]);
1969 n[5] ^= 1;
1970 assert_eq!(graphic(&n), None, "a wrong length");
1971 let n = grob(&["#.", ".#"]);
1972 assert_eq!(graphic(&n[..n.len() - 1]), None, "cut short");
1973 assert_eq!(graphic(&[0xE, 0x1, 0xB, 0x2, 0x1]), None, "no fields");
1974 assert_eq!(graphic(&grob(&[])), None, "0 by 0");
1977 assert_eq!(graphic(&grob(&[""; 5])), None, "0 wide, 5 high");
1978 assert!(matches!(
1979 decode(&grob(&[]), &NoMemory),
1980 Ok(Object::Unknown { graphic: None, .. })
1981 ));
1982 let mut big = vec![0xE, 0x1, 0xB, 0x2, 0x0];
1984 for v in [0u32, 0x1000, 0x1001] {
1985 big.extend((0..5).map(|i| ((v >> (4 * i)) & 0xF) as u8));
1986 }
1987 assert_eq!(graphic(&big), None, "4096 by 4097 pixels");
1988 let mut trailing = grob(&["#"]);
1989 trailing.push(0);
1990 assert_eq!(
1991 graphic(&trailing).map(|g| g.rows),
1992 Some("80".into()),
1993 "a trailing nibble is not the GROB's"
1994 );
1995 }
1996}