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