1use std::fmt;
6use std::io;
7use std::str::FromStr;
8
9use bitvec::prelude::*;
10use is_sorted::IsSorted;
11
12use oxidd_core::function::Function;
13use oxidd_core::util::AllocResult;
14use oxidd_core::util::Borrowed;
15use oxidd_core::util::EdgeDropGuard;
16use oxidd_core::util::EdgeHashMap;
17use oxidd_core::util::EdgeVecDropGuard;
18use oxidd_core::util::OutOfMemory;
19use oxidd_core::DiagramRules;
20use oxidd_core::Edge;
21use oxidd_core::HasLevel;
22use oxidd_core::InnerNode;
23use oxidd_core::LevelNo;
24use oxidd_core::Manager;
25use oxidd_core::Node;
26
27type FxBuildHasher = std::hash::BuildHasherDefault<rustc_hash::FxHasher>;
33
34#[derive(Clone, Copy, PartialEq, Eq, Debug)]
36#[repr(u8)]
37enum Code {
38 Terminal,
39 AbsoluteID,
40 RelativeID,
41 Relative1,
42}
43
44impl From<u8> for Code {
45 fn from(value: u8) -> Self {
46 match value {
47 0 => Code::Terminal,
48 1 => Code::AbsoluteID,
49 2 => Code::RelativeID,
50 3 => Code::Relative1,
51 _ => panic!(),
52 }
53 }
54}
55
56#[derive(Clone, Copy, PartialEq, Eq, Debug)]
57enum VarInfo {
58 VariableID,
59 PermutationID,
60 AuxiliaryID,
61 VariableName,
62 None,
63}
64
65#[derive(Debug)]
67pub struct DumpHeader {
68 ascii: bool, varinfo: VarInfo,
70 dd: String, nnodes: usize,
72 nvars: u32,
73 suppvarnames: Vec<String>, orderedvarnames: Vec<String>, ids: Vec<u32>,
76 permids: Vec<u32>,
77 auxids: Vec<u32>, rootids: Vec<isize>,
79 rootnames: Vec<String>, lines: usize, }
83
84impl DumpHeader {
85 pub fn load(mut input: impl io::BufRead) -> io::Result<Self> {
90 let mut header = DumpHeader {
91 ascii: true,
92 varinfo: VarInfo::None,
93 dd: String::new(),
94 nnodes: 0,
95 nvars: 0,
96 suppvarnames: Vec::new(),
97 orderedvarnames: Vec::new(),
98 ids: Vec::new(),
99 permids: Vec::new(),
100 auxids: Vec::new(),
101 rootids: Vec::new(),
102 rootnames: Vec::new(),
103 lines: 1,
104 };
105 let mut nsuppvars = 0;
106 let mut nroots = 0;
107
108 let mut line = Vec::new();
109 let mut line_no = 1usize;
110
111 loop {
112 match input.read_until(b'\n', &mut line) {
113 Ok(0) => return err("unexpected end of file"),
114 Ok(_) => {}
115 Err(e) => return Err(e),
116 }
117 while let Some(b'\n' | b'\r') = line.last() {
118 line.pop();
119 }
120
121 let (key, value) = match memchr::memchr2(b' ', b'\t', &line) {
122 Some(p) => (&line[..p], &line[p + 1..]),
123 None => (&line[..], [].as_slice()),
124 };
125 let value = trim(value);
126
127 match key {
129 b".ver" => {
130 if value != b"DDDMP-2.0" {
131 return err(format!(
132 "unsupported version '{}' (line {line_no})",
133 String::from_utf8_lossy(value)
134 ));
135 }
136 }
137 b".mode" => {
138 header.ascii = match value {
139 b"A" => true,
140 b"B" => false,
141 _ => {
142 return err(format!(
143 "unknown value '{}' for key '.mode' (line {line_no})",
144 String::from_utf8_lossy(value),
145 ))
146 }
147 };
148 }
149 b".varinfo" => {
150 header.varinfo = match value {
151 b"0" => VarInfo::VariableID,
152 b"1" => VarInfo::PermutationID,
153 b"2" => VarInfo::AuxiliaryID,
154 b"3" => VarInfo::VariableName,
155 b"4" => VarInfo::None,
156 _ => {
157 return err(format!(
158 "unknown value '{}' for key '.varinfo' (line {line_no})",
159 String::from_utf8_lossy(value),
160 ))
161 }
162 };
163 }
164 b".dd" => header.dd = String::from_utf8_lossy(value).to_string(),
165 b".nnodes" => header.nnodes = parse_single_usize(value, line_no)?,
166 b".nvars" => header.nvars = parse_single_u32(value, line_no)?,
167 b".nsuppvars" => nsuppvars = parse_single_u32(value, line_no)?,
168 b".suppvarnames" => {
169 header.suppvarnames = parse_str_list(value, header.nvars as usize);
170 }
171 b".orderedvarnames" => {
172 header.orderedvarnames = parse_str_list(value, header.nvars as usize);
173 }
174 b".ids" => {
175 header.ids = parse_u32_list(value, nsuppvars as usize, line_no)?;
176 }
177 b".permids" => {
178 header.permids = parse_u32_list(value, nsuppvars as usize, line_no)?;
179 }
180 b".auxids" => {
181 header.auxids = parse_u32_list(value, nsuppvars as usize, line_no)?;
182 }
183 b".nroots" => nroots = parse_single_usize(value, line_no)?,
184 b".rootids" => {
185 header.rootids.clear();
186 parse_edge_list(value, &mut header.rootids, line_no)?;
187 }
188 b".rootnames" => header.rootnames = parse_str_list(value, nroots),
189 b".nodes" => break,
190 _ => {
191 return err(format!(
192 "unknown key '{}' (line {line_no})",
193 String::from_utf8_lossy(key),
194 ))
195 }
196 }
197
198 line_no += 1;
199 line.clear();
200 }
201
202 header.lines = line_no;
203
204 if nsuppvars > header.nvars {
207 return err(format!(
208 ".nsuppvars ({nsuppvars}) must not be greater than .nvars ({})",
209 header.nvars,
210 ));
211 }
212
213 if header.ids.len() != nsuppvars as usize {
214 return err(format!(
215 "number of support variables in .ids entry ({}) does not match .nsuppvars ({nsuppvars})",
216 header.ids.len(),
217 ));
218 }
219 if header.permids.len() != nsuppvars as usize {
220 return err(format!(
221 "number of support variables in .permids entry ({}) does not match .nsuppvars ({nsuppvars})",
222 header.permids.len(),
223 ));
224 }
225 if !header.auxids.is_empty() && header.auxids.len() != nsuppvars as usize {
226 return err(format!(
227 "number of support variables in .auxids entry ({}) does not match .nsuppvars ({nsuppvars})",
228 header.auxids.len(),
229 ));
230 }
231
232 if !header.ids.is_empty() {
233 if !IsSorted::is_sorted_by(&mut header.ids.iter(), cmp_strict) {
234 return err("support variables in .ids must be ascending");
235 }
236 if *header.ids.last().unwrap() >= header.nvars {
237 return err(format!(
238 "support variables in .ids must be less than .nvars ({})",
239 header.nvars,
240 ));
241 }
242 }
243
244 let mut permids_present: BitVec = bitvec![0; header.nvars as usize];
245 for &id in &header.permids {
246 if id >= header.nvars {
247 return err(format!(
248 "support variables in .permids must be less than .nvars ({})",
249 header.nvars,
250 ));
251 }
252 if permids_present[id as usize] {
253 return err(format!("variable ({id}) occurs twice in .permids"));
254 }
255 permids_present.set(id as usize, true);
256 }
257
258 if !header.orderedvarnames.is_empty()
259 && header.orderedvarnames.len() != header.nvars as usize
260 {
261 return err(format!(
262 "number of variables in .orderedvarnames entry ({}) does not match .nvars ({})",
263 header.orderedvarnames.len(),
264 header.nvars
265 ));
266 }
267 if !header.suppvarnames.is_empty() {
268 if header.suppvarnames.len() != nsuppvars as usize {
269 return err(format!(
270 "number of variables in .suppvarnames entry ({}) does not match .nsuppvars ({nsuppvars})",
271 header.suppvarnames.len(),
272 ));
273 }
274
275 if !header.orderedvarnames.is_empty() {
276 for (i, suppvar) in header.suppvarnames.iter().enumerate() {
277 let permid = header.permids[i];
278 let ordervar = &header.orderedvarnames[permid as usize];
279 if suppvar != ordervar {
280 return err(format!(
281 ".suppvarnames and .orderedvarnames do not match \
282 (entry {i} of .suppvarnames is '{suppvar}' \
283 but the permuted ID is {permid} with name '{ordervar}')",
284 ));
285 }
286 }
287 }
288 }
289
290 if header.rootids.len() != nroots {
291 return err(format!(
292 "number of roots in .rootids entry ({}) does not match .nroots ({nroots})",
293 header.rootids.len(),
294 ));
295 }
296 for &id in &header.rootids {
297 if id == 0 {
298 return err(".rootids must not be 0");
299 }
300 if id.unsigned_abs() > header.nnodes {
301 return err(format!(
302 "entry in .rootids out of range ({id} > {})",
303 header.nnodes,
304 ));
305 }
306 }
307 if !header.rootnames.is_empty() && header.rootnames.len() != nroots {
308 return err(format!(
309 "number of roots in .rootnames entry ({}) does not match .nroots ({nroots})",
310 header.rootnames.len(),
311 ));
312 }
313
314 Ok(header)
315 }
316
317 pub fn diagram_name(&self) -> Option<&str> {
321 if self.dd.is_empty() {
322 None
323 } else {
324 Some(&self.dd)
325 }
326 }
327
328 pub fn num_nodes(&self) -> usize {
332 self.nnodes
333 }
334
335 pub fn num_vars(&self) -> u32 {
339 self.nvars
340 }
341
342 pub fn num_support_vars(&self) -> u32 {
346 self.ids.len() as _
347 }
348
349 pub fn support_vars(&self) -> &[u32] {
361 &self.ids
362 }
363
364 pub fn support_var_permutation(&self) -> &[u32] {
378 &self.permids
379 }
380
381 pub fn auxiliary_var_ids(&self) -> &[u32] {
386 &self.auxids
387 }
388
389 pub fn support_var_names(&self) -> Option<&[String]> {
400 if self.suppvarnames.is_empty() {
401 None
402 } else {
403 Some(&self.suppvarnames[..])
404 }
405 }
406
407 pub fn ordered_var_names(&self) -> Option<&[String]> {
420 if self.orderedvarnames.is_empty() {
421 None
422 } else {
423 Some(&self.orderedvarnames[..])
424 }
425 }
426
427 pub fn num_roots(&self) -> usize {
432 self.rootids.len()
433 }
434
435 pub fn root_names(&self) -> Option<&[String]> {
440 if self.rootnames.is_empty() {
441 None
442 } else {
443 Some(&self.rootnames[..])
444 }
445 }
446}
447
448fn err<T>(msg: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Result<T> {
450 Err(io::Error::new(io::ErrorKind::InvalidData, msg))
451}
452
453fn cmp_strict<T: Ord>(a: &T, b: &T) -> Option<std::cmp::Ordering> {
455 Some(a.cmp(b).then(std::cmp::Ordering::Greater))
456}
457
458pub trait AsciiDisplay {
460 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error>;
462}
463
464struct Ascii<T>(T);
465impl<T: AsciiDisplay> fmt::Display for Ascii<&T> {
466 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
467 self.0.fmt(f)
468 }
469}
470
471#[allow(clippy::too_many_arguments)] pub fn export<'id, F: Function>(
491 mut file: impl io::Write,
492 manager: &F::Manager<'id>,
493 ascii: bool,
494 dd_name: &str,
495 vars: &[&F],
496 var_names: Option<&[&str]>,
497 functions: &[&F],
498 function_names: Option<&[&str]>,
499 is_complemented: impl Fn(&<F::Manager<'id> as Manager>::Edge) -> bool,
500) -> io::Result<()>
501where
502 <F::Manager<'id> as Manager>::InnerNode: HasLevel,
503 <F::Manager<'id> as Manager>::Terminal: AsciiDisplay,
504{
505 assert!(var_names.is_none() || var_names.unwrap().len() == vars.len());
506 assert!(function_names.is_none() || function_names.unwrap().len() == functions.len());
507 assert!(
508 ascii || <F::Manager<'id> as Manager>::InnerNode::ARITY == 2,
509 "binary mode is (currently) only supported for binary nodes"
510 );
511
512 writeln!(file, ".ver DDDMP-2.0")?;
513 writeln!(file, ".mode {}", if ascii { 'A' } else { 'B' })?;
514
515 writeln!(file, ".varinfo {}", VarInfo::None as u32)?;
517
518 if !dd_name.is_empty() {
519 let invalid = |c: char| !c.is_ascii() || c.is_ascii_control();
520 if dd_name.chars().any(invalid) {
521 return Err(io::Error::new(
522 io::ErrorKind::InvalidInput,
523 "decision diagram name must be ASCII text without control characters",
524 ));
525 }
526 writeln!(file, ".dd {dd_name}")?;
527 }
528
529 let nvars = manager.num_levels();
530 assert!(nvars as usize == vars.len());
531
532 let mut node_map: Vec<(LevelNo, EdgeHashMap<F::Manager<'id>, usize, FxBuildHasher>)> =
537 (0..nvars).map(|_| (0, EdgeHashMap::new(manager))).collect();
538 let mut terminal_map: EdgeHashMap<F::Manager<'id>, usize, FxBuildHasher> =
539 EdgeHashMap::new(manager);
540
541 fn rec_add_map<M: Manager>(
542 manager: &M,
543 node_map: &mut Vec<(u32, EdgeHashMap<M, usize, FxBuildHasher>)>,
544 terminal_map: &mut EdgeHashMap<M, usize, FxBuildHasher>,
545 e: Borrowed<M::Edge>,
546 ) where
547 M::InnerNode: HasLevel,
548 {
549 match manager.get_node(&e) {
550 Node::Inner(node) => {
551 let (_, map) = &mut node_map[node.level() as usize];
552 let res = map.insert(&e.with_tag(Default::default()), 0);
554 if res.is_none() {
555 for e in node.children() {
556 rec_add_map::<M>(manager, node_map, terminal_map, e);
557 }
558 }
559 }
560 Node::Terminal(_) => {
561 terminal_map.insert(&e.with_tag(Default::default()), 0);
562 }
563 }
564 }
565
566 for &func in functions {
567 rec_add_map::<F::Manager<'id>>(
568 manager,
569 &mut node_map,
570 &mut terminal_map,
571 func.as_edge(manager).borrowed(),
572 );
573 }
574
575 let mut nnodes = 0;
576 for (_, idx) in &mut terminal_map {
577 nnodes += 1; *idx = nnodes;
579 }
580 let nsuppvars = node_map.iter().filter(|(_, m)| !m.is_empty()).count() as u32;
581 let mut suppvars: BitVec = bitvec![0; nvars as usize];
582 let mut suppvar_idx = nsuppvars;
583 for (i, (var_idx, level)) in node_map.iter_mut().enumerate().rev() {
585 if level.is_empty() {
586 continue;
587 }
588 suppvar_idx -= 1;
589 *var_idx = suppvar_idx;
590 suppvars.set(i, true);
591 for (_, idx) in level.iter_mut() {
592 nnodes += 1; *idx = nnodes;
594 }
595 }
596 writeln!(file, ".nnodes {nnodes}")?;
597 writeln!(file, ".nvars {nvars}")?;
598 writeln!(file, ".nsuppvars {nsuppvars}")?;
599
600 if let Some(var_names) = var_names {
601 let mut ordered_var_names = Vec::new();
602 ordered_var_names.resize(var_names.len(), "");
603
604 write!(file, ".suppvarnames")?;
605 for (&f, &name) in vars.iter().zip(var_names) {
606 let invalid =
607 |c: char| !c.is_ascii() || c.is_ascii_control() || c.is_ascii_whitespace();
608 if name.is_empty() || name.chars().any(invalid) {
609 return Err(io::Error::new(
610 io::ErrorKind::InvalidInput,
611 "variable name must be ASCII text without control characters and spaces",
612 ));
613 }
614 let node = manager
615 .get_node(f.as_edge(manager))
616 .expect_inner("variables must not be terminals");
617 let level = node.level() as usize;
618 assert!(
619 ordered_var_names[level].is_empty(),
620 "variables must not be given multiple times"
621 );
622 ordered_var_names[level] = name;
623 if suppvars[level] {
624 write!(file, " {name}")?;
625 }
626 }
627 writeln!(file)?;
628
629 write!(file, ".orderedvarnames")?;
630 for name in ordered_var_names {
631 write!(file, " {name}")?;
632 }
633 writeln!(file)?;
634 }
635
636 write!(file, ".ids")?;
637 for (id, &f) in vars.iter().enumerate() {
638 let node = manager
639 .get_node(f.as_edge(manager))
640 .expect_inner("variables must not be terminals");
641 if suppvars[node.level() as usize] {
642 write!(file, " {id}")?;
643 }
644 }
645 writeln!(file)?;
646
647 write!(file, ".permids")?;
648 for &f in vars {
649 let level = manager.get_node(f.as_edge(manager)).unwrap_inner().level();
650 if suppvars[level as usize] {
651 write!(file, " {level}")?;
652 }
653 }
654 writeln!(file)?;
655
656 let idx = |e: &<F::Manager<'id> as Manager>::Edge| {
659 let idx = match manager.get_node(e) {
660 Node::Inner(node) => {
661 let (_, map) = &node_map[node.level() as usize];
662 *map.get(&e.with_tag(Default::default())).unwrap() as isize
663 }
664 Node::Terminal(_) => {
665 *terminal_map.get(&e.with_tag(Default::default())).unwrap() as isize
666 }
667 };
668 if is_complemented(e) {
669 -idx
670 } else {
671 idx
672 }
673 };
674 let bin_idx = |e: &<F::Manager<'id> as Manager>::Edge, node_id: usize| {
675 let idx = match manager.get_node(e) {
676 Node::Inner(node) => {
677 let (_, map) = &node_map[node.level() as usize];
678 *map.get(&e.with_tag(Default::default())).unwrap()
679 }
680 Node::Terminal(_) => {
681 if manager.num_terminals() == 1 {
682 return (Code::Terminal, 0);
687 }
688 todo!();
689 }
690 };
691 if idx == node_id - 1 {
692 (Code::Relative1, 0)
693 } else if node_id - idx < idx {
694 (Code::RelativeID, node_id - idx)
695 } else {
696 (Code::AbsoluteID, idx)
697 }
698 };
699
700 writeln!(file, ".nroots {}", functions.len())?;
701 write!(file, ".rootids")?;
702 for &func in functions {
703 write!(file, " {}", idx(func.as_edge(manager)))?;
704 }
705 writeln!(file)?;
706 if let Some(function_names) = function_names {
707 write!(file, ".rootnames")?;
708 for &name in function_names {
709 let invalid =
710 |c: char| !c.is_ascii() || c.is_ascii_control() || c.is_ascii_whitespace();
711 if name.is_empty() || name.chars().any(invalid) {
712 return Err(io::Error::new(
713 io::ErrorKind::InvalidInput,
714 "function name must be ASCII text without control characters and spaces",
715 ));
716 }
717 write!(file, " {}", name)?;
718 }
719 writeln!(file)?;
720 }
721
722 writeln!(file, ".nodes")?;
723
724 #[inline]
725 const fn node_code(var: Code, t: Code, e_complement: bool, e: Code) -> u8 {
726 (var as u8) << 5 | (t as u8) << 3 | (e_complement as u8) << 2 | e as u8
727 }
728
729 let mut exported_nodes = 0;
730 for (edge, &node_id) in terminal_map.iter() {
731 assert_eq!(exported_nodes + 1, node_id);
735 if ascii {
736 let node = manager.get_node(edge);
739 let desc = Ascii(node.unwrap_terminal());
740 writeln!(file, "{node_id} {desc} 0 0")?;
741 } else {
742 let terminal = node_code(Code::Terminal, Code::Terminal, false, Code::Terminal);
743 write_escaped(&mut file, &[terminal])?;
744 }
745 exported_nodes += 1;
746 }
747 for &(var_idx, ref level) in node_map.iter().rev() {
749 for (e, &node_id) in level.iter() {
750 assert_eq!(exported_nodes + 1, node_id);
751 let node = manager.get_node(e).unwrap_inner();
752 if ascii {
753 write!(file, "{node_id} {var_idx}")?;
756 for child in node.children() {
757 write!(file, " {}", idx(&child))?;
758 }
759 writeln!(file)?;
760 } else {
761 let mut iter = node.children();
762 let t = iter.next().unwrap();
763 let t_lvl = manager.get_node(&t).level();
764 let e = iter.next().unwrap();
765 let e_lvl = manager.get_node(&e).level();
766 debug_assert!(iter.next().is_none());
767
768 let mut var_code = Code::AbsoluteID;
769 let mut var_idx = var_idx;
770 let min_lvl = std::cmp::min(t_lvl, e_lvl);
771 if min_lvl != LevelNo::MAX {
772 let (min_var_idx, _) = node_map[min_lvl as usize];
773 if var_idx == min_var_idx - 1 {
774 var_code = Code::Relative1;
775 } else if min_var_idx - var_idx < var_idx {
776 var_code = Code::RelativeID;
777 var_idx = min_var_idx - var_idx;
778 }
779 }
780
781 let (t_code, t_idx) = bin_idx(&t, node_id);
782 let (e_code, e_idx) = bin_idx(&e, node_id);
783
784 debug_assert!(!is_complemented(&t));
785 write_escaped(
786 &mut file,
787 &[node_code(var_code, t_code, is_complemented(&e), e_code)],
788 )?;
789 if var_code == Code::AbsoluteID || var_code == Code::RelativeID {
790 encode_7bit(&mut file, var_idx as usize)?;
791 }
792 if t_code == Code::AbsoluteID || t_code == Code::RelativeID {
793 encode_7bit(&mut file, t_idx)?;
794 }
795 if e_code == Code::AbsoluteID || e_code == Code::RelativeID {
796 encode_7bit(&mut file, e_idx)?;
797 }
798 assert!(u64::BITS >= usize::BITS);
799 }
800 exported_nodes += 1;
801 }
802 }
803
804 writeln!(file, ".end")?;
805
806 Ok(())
807}
808
809fn encode_7bit(writer: impl io::Write, mut value: usize) -> io::Result<()> {
811 let mut buf = [0u8; (usize::BITS as usize + 8 - 1) / 7];
812 let mut idx = buf.len() - 1;
813 buf[idx] = (value as u8).wrapping_shl(1);
814 value >>= 7;
815 while value != 0 {
816 idx -= 1;
817 buf[idx] = (value as u8).wrapping_shl(1) | 1;
818 value >>= 7;
819 }
820 write_escaped(writer, &buf[idx..])
821}
822
823fn write_escaped(mut writer: impl io::Write, buf: &[u8]) -> io::Result<()> {
825 for &c in buf {
826 match c {
827 0x00 => writer.write_all(&[0x00, 0x00])?,
828 0x0a => writer.write_all(&[0x00, 0x01])?,
829 0x0d => writer.write_all(&[0x00, 0x02])?,
830 0x1a => writer.write_all(&[0x00, 0x03])?,
831 _ => writer.write_all(&[c])?,
832 }
833 }
834 Ok(())
835}
836
837fn read_unescape(input: impl io::Read) -> io::Result<u8> {
839 let mut bytes = input.bytes();
840 let eof_msg = "unexpected end of file";
841 match bytes.next() {
842 None => return err(eof_msg),
843 Some(Ok(0)) => {}
844 Some(res) => return res,
845 };
846 match bytes.next() {
847 None => err(eof_msg),
848 Some(Ok(0x00)) => Ok(0x00),
849 Some(Ok(0x01)) => Ok(0x0a),
850 Some(Ok(0x02)) => Ok(0x0d),
851 Some(Ok(0x03)) => Ok(0x1a),
852 Some(Ok(b)) => Err(io::Error::new(
853 io::ErrorKind::InvalidData,
854 format!("invalid escape sequence 0x00 0x{b:x}"),
855 )),
856 Some(e) => e,
857 }
858}
859
860fn decode_7bit(mut input: impl io::Read) -> io::Result<usize> {
863 let mut res = 0usize;
864 loop {
865 let b = read_unescape(&mut input)?;
866 match res.checked_shl(7) {
867 Some(v) => res = v,
868 None => return err("integer too large"),
869 }
870 res |= (b >> 1) as usize;
871 if b & 1 == 0 {
872 return Ok(res);
873 }
874 }
875}
876
877pub fn import<'id, F: Function>(
890 mut input: impl io::BufRead,
891 header: &DumpHeader,
892 manager: &F::Manager<'id>,
893 support_vars: &[F],
894 complement: impl Fn(
895 &F::Manager<'id>,
896 <F::Manager<'id> as Manager>::Edge,
897 ) -> AllocResult<<F::Manager<'id> as Manager>::Edge>,
898) -> io::Result<Vec<F>>
899where
900 <F::Manager<'id> as Manager>::InnerNode: HasLevel,
901 <F::Manager<'id> as Manager>::Terminal: FromStr,
902{
903 assert_eq!(support_vars.len(), header.ids.len());
904 let suppvar_level_map: Vec<LevelNo> = support_vars
905 .iter()
906 .map(|f| {
907 let nw = manager
908 .get_node(f.as_edge(manager))
909 .expect_inner("variables must not be terminals");
910 nw.level()
911 })
912 .collect();
913 assert!(IsSorted::is_sorted_by(
914 &mut suppvar_level_map.iter(),
915 cmp_strict
916 ));
917
918 let nodes = if header.ascii {
919 import_ascii::<F::Manager<'id>>(
920 &mut input,
921 header,
922 manager,
923 &suppvar_level_map,
924 &complement,
925 )
926 } else {
927 let mut level_suppvar_map = vec![u32::MAX; manager.num_levels() as usize];
928 for (i, &level) in suppvar_level_map.iter().enumerate() {
929 level_suppvar_map[level as usize] = i as u32;
930 }
931 import_bin::<F::Manager<'id>>(
932 &mut input,
933 header,
934 manager,
935 &level_suppvar_map,
936 &suppvar_level_map,
937 &complement,
938 )
939 }?;
940 let nodes = EdgeVecDropGuard::new(manager, nodes);
941 debug_assert_eq!(nodes.len(), header.nnodes);
942
943 let mut buf = Vec::with_capacity(8);
944 input.read_to_end(&mut buf)?;
945 if &buf[..4] != b".end" || !buf[4..].iter().all(|b| b.is_ascii_whitespace()) {
946 return err("file must end with '.end'");
947 }
948
949 let mut roots = Vec::with_capacity(header.rootids.len());
950 for &root in &header.rootids {
951 debug_assert_ne!(root, 0);
952 let node_index = root.unsigned_abs() - 1;
953 let e = manager.clone_edge(&nodes[node_index]);
954 roots.push(F::from_edge(
955 manager,
956 if root > 0 {
957 e
958 } else {
959 match complement(manager, e) {
960 Ok(e) => e,
961 Err(OutOfMemory) => return Err(io::ErrorKind::OutOfMemory.into()),
962 }
963 },
964 ));
965 }
966
967 Ok(roots)
968}
969
970fn import_ascii<M: Manager>(
971 mut input: impl io::BufRead,
972 header: &DumpHeader,
973 manager: &M,
974 suppvar_level_map: &[LevelNo],
975 complement: impl Fn(&M, M::Edge) -> AllocResult<M::Edge>,
976) -> io::Result<Vec<M::Edge>>
977where
978 M::InnerNode: HasLevel,
979 M::Terminal: FromStr,
980{
981 let mut nodes: Vec<M::Edge> = Vec::with_capacity(header.nnodes);
982 let mut line = Vec::new();
983 let mut line_no = header.lines + 1;
984 let mut children = Vec::with_capacity(M::InnerNode::ARITY);
985 for node_id in 1..=header.nnodes {
986 match input.read_until(b'\n', &mut line) {
987 Ok(0) => return err("unexpected end of file"),
988 Ok(_) => {}
989 Err(e) => return Err(e),
990 }
991 while let Some(b'\n' | b'\r') = line.last() {
992 line.pop();
993 }
994
995 let (rest, node_id_lineno) = parse_usize(&line, line_no)?;
996 if node_id_lineno != node_id {
997 return err(format!("expected node ID {node_id} on line {line_no}"));
998 }
999
1000 let rest = trim_start(rest);
1001 let rest = match header.varinfo {
1002 VarInfo::VariableID
1003 | VarInfo::PermutationID
1004 | VarInfo::AuxiliaryID
1005 | VarInfo::VariableName => match memchr::memchr2(b' ', b'\t', rest) {
1006 Some(pos) => &rest[pos + 1..],
1007 None => {
1008 return err(format!(
1009 "expected a space after variable extra info (line {line_no})"
1010 ))
1011 }
1012 },
1013 VarInfo::None => rest,
1014 };
1015
1016 let rest = trim_start(rest);
1017 let (rest, var_id) = match memchr::memchr2(b' ', b'\t', rest) {
1018 Some(pos) => (&rest[pos + 1..], &rest[..pos]),
1019 None => {
1020 return err(format!(
1021 "expected a space after variable internal index (line {line_no})"
1022 ))
1023 }
1024 };
1025
1026 parse_edge_list(rest, &mut children, line_no)?;
1027 if children.len() != M::InnerNode::ARITY {
1028 return err(format!(
1029 "expected {} children, got {} (line {line_no})",
1030 M::InnerNode::ARITY,
1031 children.len()
1032 ));
1033 }
1034
1035 let res = if children.iter().any(|&c| c == 0) {
1036 let string = match std::str::from_utf8(var_id) {
1038 Ok(s) => s,
1039 Err(_) => {
1040 return err(format!(
1041 "terminal description must be valid UTF-8 (line {line_no})"
1042 ));
1043 }
1044 };
1045 let terminal: M::Terminal = match string.parse() {
1046 Ok(t) => t,
1047 Err(_) => return err(format!("invalid terminal description (line {line_no})")),
1048 };
1049 manager.get_terminal(terminal)
1050 } else {
1051 let (_, var_id) = parse_u32(var_id, line_no)?;
1052 let Some(&level) = suppvar_level_map.get(var_id as usize) else {
1053 return err(format!("variable out of range (line {line_no})"));
1054 };
1055
1056 for &child in &children {
1057 let child = child.unsigned_abs();
1058 if child >= node_id {
1059 return err(format!(
1060 "children ids must be less than node ({child} >= {node_id}, line {line_no})",
1061 ));
1062 }
1063 let child_level = manager.get_node(&nodes[child - 1]).level();
1064 if level >= child_level {
1065 return err(format!(
1066 "node level must be less than the children's levels ({level} >= {child_level}, line {line_no})",
1067 ));
1068 }
1069 }
1070
1071 <M::Rules as DiagramRules<_, _, _>>::reduce(
1072 manager,
1073 level,
1074 children.iter().map(|&child| {
1075 debug_assert_ne!(child, 0);
1076 let e = manager.clone_edge(&nodes[child.unsigned_abs() - 1]);
1077 if child < 0 {
1078 complement(manager, e).unwrap()
1079 } else {
1080 e
1081 }
1082 }),
1083 )
1084 .then_insert(manager, level)
1085 };
1086 let node = match res {
1087 Ok(e) => e,
1088 Err(OutOfMemory) => {
1089 for e in nodes {
1090 manager.drop_edge(e);
1091 }
1092 return Err(io::ErrorKind::OutOfMemory.into());
1093 }
1094 };
1095
1096 nodes.push(node);
1097
1098 children.clear();
1099 line.clear();
1100 line_no += 1;
1101 }
1102
1103 Ok(nodes)
1104}
1105
1106fn import_bin<M: Manager>(
1107 mut input: impl io::BufRead,
1108 header: &DumpHeader,
1109 manager: &M,
1110 level_suppvar_map: &[u32],
1111 suppvar_level_map: &[LevelNo],
1112 complement: impl Fn(&M, M::Edge) -> AllocResult<M::Edge>,
1113) -> io::Result<Vec<M::Edge>>
1114where
1115 M::InnerNode: HasLevel,
1116{
1117 assert_eq!(
1118 M::InnerNode::ARITY,
1119 2,
1120 "binary mode is (currently) only supported for binary nodes"
1121 );
1122 let terminal = {
1123 let msg = "binary mode is (currently) only supported for diagrams with a single terminal";
1124 let mut it = manager.terminals();
1125 let t = EdgeDropGuard::new(manager, it.next().expect(msg));
1126 assert!(it.next().is_none(), "{msg}");
1127 t
1128 };
1129
1130 fn idx(input: impl io::BufRead, node_id: usize, code: Code) -> io::Result<usize> {
1131 debug_assert!(node_id >= 1);
1132 let id = match code {
1133 Code::Terminal => 1,
1134 Code::AbsoluteID => decode_7bit(input)?,
1135 Code::RelativeID => node_id - decode_7bit(input)?,
1136 Code::Relative1 => node_id - 1,
1137 };
1138 if id == 0 {
1139 return err("then/else ID must not be 0");
1140 }
1141 if id >= node_id {
1142 return err("then/else ID too large");
1143 }
1144 Ok((id - 1) as usize)
1145 }
1146
1147 let mut nodes = EdgeVecDropGuard::new(manager, Vec::with_capacity(header.nnodes));
1148 for node_id in 1..=header.nnodes {
1149 let node_code = read_unescape(&mut input)?;
1150 let var_code = Code::from((node_code >> 5) & 0b11);
1151 let t_code = Code::from((node_code >> 3) & 0b11);
1152 let e_complement = ((node_code >> 2) & 0b1) != 0;
1153 let e_code = Code::from(node_code & 0b11);
1154
1155 let vid = match var_code {
1156 Code::Terminal => {
1157 nodes.push(manager.clone_edge(&terminal));
1158 continue;
1159 }
1160 Code::AbsoluteID | Code::RelativeID => decode_7bit(&mut input)?,
1161 Code::Relative1 => 1,
1162 };
1163
1164 let t = manager.clone_edge(&nodes[idx(&mut input, node_id, t_code)?]);
1165 let t_level = manager.get_node(&t).level();
1166 let e = manager.clone_edge(&nodes[idx(&mut input, node_id, e_code)?]);
1167 let e_level = manager.get_node(&e).level();
1168 let e = if e_complement {
1169 match complement(manager, e) {
1170 Ok(e) => e,
1171 Err(OutOfMemory) => {
1172 return Err(io::ErrorKind::OutOfMemory.into());
1173 }
1174 }
1175 } else {
1176 e
1177 };
1178
1179 let vid = match var_code {
1180 Code::Terminal => unreachable!(),
1181 Code::AbsoluteID if vid >= suppvar_level_map.len() => {
1182 return err("variable ID out of range")
1183 }
1184 Code::AbsoluteID => vid,
1185 Code::RelativeID | Code::Relative1 => {
1186 let min_level = std::cmp::min(t_level, e_level);
1187 let child_min_suppvar = if min_level == LevelNo::MAX {
1188 level_suppvar_map.len()
1189 } else {
1190 level_suppvar_map[min_level as usize] as usize
1191 };
1192 match child_min_suppvar.checked_sub(vid) {
1193 Some(v) => v,
1194 None => return err("variable ID out of range"),
1195 }
1196 }
1197 };
1198
1199 let level = suppvar_level_map[vid as usize];
1200 if level >= t_level || level >= e_level {
1201 return err("node level must be less than the children's levels");
1202 }
1203
1204 match <M::Rules as DiagramRules<_, _, _>>::reduce(manager, level, [t, e])
1205 .then_insert(manager, level)
1206 {
1207 Ok(e) => nodes.push(e),
1208 Err(OutOfMemory) => {
1209 return Err(io::ErrorKind::OutOfMemory.into());
1210 }
1211 }
1212 }
1213
1214 Ok(nodes.into_vec())
1215}
1216
1217const fn trim_start(mut s: &[u8]) -> &[u8] {
1219 while let [b' ' | b'\t', rest @ ..] = s {
1220 s = rest;
1221 }
1222 s
1223}
1224
1225const fn trim_end(mut s: &[u8]) -> &[u8] {
1227 while let [rest @ .., b' ' | b'\t'] = s {
1228 s = rest;
1229 }
1230 s
1231}
1232
1233const fn trim(s: &[u8]) -> &[u8] {
1235 trim_end(trim_start(s))
1236}
1237
1238fn parse_str_list(input: &[u8], capacity: usize) -> Vec<String> {
1240 let mut res = Vec::with_capacity(capacity);
1241 let mut start = 0;
1242 for pos in memchr::memchr2_iter(b' ', b'\t', input).chain([input.len()]) {
1243 if pos != start {
1245 res.push(String::from_utf8_lossy(&input[start..pos]).to_string())
1246 }
1247 start = pos + 1;
1248 }
1249 res
1250}
1251
1252macro_rules! parse_unsigned {
1253 ($f:ident, $t:ty) => {
1254 fn $f(input: &[u8], line_no: usize) -> io::Result<(&[u8], $t)> {
1261 let mut res: $t = 0;
1262 let mut num = false;
1263 let mut consumed = 0;
1264 for &c in input {
1265 match c {
1266 b'0'..=b'9' => {
1267 num = true;
1268 match res
1269 .checked_mul(10)
1270 .and_then(|v| v.checked_add((c - b'0') as _))
1271 {
1272 Some(v) => res = v,
1273 None => {
1274 return err(format!(
1275 "integer '{}' too large (line {line_no})",
1276 String::from_utf8_lossy(input),
1277 ));
1278 }
1279 }
1280 }
1281 b' ' | b'\t' if num => break,
1282 b' ' | b'\t' => {}
1283 _ => {
1284 return err(format!(
1285 "unexpected char '{}' in integer (line {line_no})",
1286 c as char,
1287 ));
1288 }
1289 }
1290 consumed += 1;
1291 }
1292 if !num {
1293 return err(format!(
1294 "expected an integer, got an empty string (line {line_no})"
1295 ));
1296 }
1297 Ok((&input[consumed..], res))
1298 }
1299 };
1300}
1301parse_unsigned!(parse_u32, u32);
1302parse_unsigned!(parse_usize, usize);
1303
1304macro_rules! parse_single_unsigned {
1305 ($f:ident, $t:ty) => {
1306 fn $f(input: &[u8], line_no: usize) -> io::Result<$t> {
1308 let mut res: $t = 0;
1309 let mut num = false;
1310 for &c in input {
1311 match c {
1312 b'0'..=b'9' => {
1313 num = true;
1314 match res
1315 .checked_mul(10)
1316 .and_then(|v| v.checked_add((c - b'0') as _))
1317 {
1318 Some(v) => res = v,
1319 None => {
1320 return err(format!(
1321 "integer '{}' too large (line {line_no})",
1322 String::from_utf8_lossy(input),
1323 ));
1324 }
1325 }
1326 }
1327 _ => {
1328 return err(format!(
1329 "unexpected char '{}' in integer (line {line_no})",
1330 c as char,
1331 ));
1332 }
1333 }
1334 }
1335 if !num {
1336 return err(format!("expected an integer (line {line_no})"));
1337 }
1338 Ok(res)
1339 }
1340 };
1341}
1342parse_single_unsigned!(parse_single_u32, u32);
1343parse_single_unsigned!(parse_single_usize, usize);
1344
1345fn parse_u32_list(input: &[u8], capacity: usize, line_no: usize) -> io::Result<Vec<u32>> {
1347 let mut res = Vec::with_capacity(capacity);
1348 let mut i = 0u32;
1349 let mut num = false;
1350
1351 for &c in input {
1352 match c {
1353 b'0'..=b'9' => {
1354 num = true;
1355 match i
1356 .checked_mul(10)
1357 .and_then(|v| v.checked_add((c - b'0') as _))
1358 {
1359 Some(v) => i = v,
1360 None => {
1361 return err(format!("integer too large (line {line_no})"));
1362 }
1363 }
1364 }
1365 b' ' | b'\t' => {
1366 if num {
1367 res.push(i);
1368 num = false;
1369 i = 0;
1370 }
1371 }
1372 _ => {
1373 return err(format!(
1374 "unexpected char '{}' in space-separated list of integers (line {line_no})",
1375 c as char,
1376 ));
1377 }
1378 }
1379 }
1380 if num {
1381 res.push(i);
1382 }
1383
1384 Ok(res)
1385}
1386
1387fn parse_edge_list(input: &[u8], dst: &mut Vec<isize>, line_no: usize) -> io::Result<()> {
1395 let mut i = 0isize;
1396 let mut neg = false;
1397 let mut num = false;
1398
1399 for &c in input {
1400 match c {
1401 b'0'..=b'9' => {
1402 num = true;
1403 match i
1404 .checked_mul(10)
1405 .and_then(|v| v.checked_add((c - b'0') as isize))
1406 {
1407 Some(v) => i = v,
1408 None => {
1409 return err(format!("integer too large (line {line_no})"));
1410 }
1411 }
1412 }
1413 b'-' => {
1414 if neg {
1415 return err(format!("expected a digit after '-' (line {line_no})"));
1416 }
1417 if num {
1418 return err(format!("expected a space before '-' (line {line_no})"));
1419 }
1420 neg = true;
1421 }
1422 b' ' | b'\t' => {
1423 if num {
1424 dst.push(if neg { -i } else { i });
1425 num = false;
1426 neg = false;
1427 i = 0;
1428 }
1429 }
1430 _ => {
1431 return err(format!(
1432 "unexpected char '{}' in space-separated list of integers (line {line_no})",
1433 c as char,
1434 ));
1435 }
1436 }
1437 }
1438 if num {
1439 dst.push(if neg { -i } else { i });
1440 }
1441
1442 Ok(())
1443}
1444
1445#[cfg(test)]
1446mod test {
1447 use super::*;
1448
1449 #[test]
1450 fn test_encode_7bit() -> io::Result<()> {
1451 let mut buf: Vec<u8> = Vec::new();
1452
1453 if usize::BITS == 64 {
1454 encode_7bit(&mut buf, usize::MAX)?;
1455 assert_eq!(
1456 &buf[..],
1457 &[
1458 0b0000_0011, 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1110, ]
1469 );
1470 }
1471
1472 buf.clear();
1473 encode_7bit(&mut buf, 0)?;
1474 assert_eq!(&buf[..], &[0, 0]); buf.clear();
1477 encode_7bit(&mut buf, 1)?;
1478 assert_eq!(&buf[..], &[0b10]);
1479
1480 Ok(())
1481 }
1482
1483 #[test]
1484 fn test_decode_7bit() -> io::Result<()> {
1485 if usize::BITS == 64 {
1486 let b = [
1487 0b0000_0011, 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1110, ];
1498 assert_eq!(decode_7bit(&b[..])?, usize::MAX);
1499
1500 let b = [
1501 0b0000_0101, 0b0000_0001, 0b0000_0001, 0b0000_0001, 0b0000_0001, 0b0000_0001, 0b0000_0001, 0b0000_0001, 0b0000_0001, 0b0000_0000, ];
1512 assert!(decode_7bit(&b[..]).is_err());
1513 }
1514
1515 assert_eq!(decode_7bit([0, 0].as_slice())?, 0);
1517
1518 assert_eq!(decode_7bit([0b10].as_slice())?, 1);
1519
1520 Ok(())
1521 }
1522
1523 #[test]
1526 fn test_trim_start() {
1527 assert_eq!(trim_start(b""), b"");
1528 assert_eq!(trim_start(b"abc"), b"abc");
1529 assert_eq!(trim_start(b"abc 123"), b"abc 123");
1530 assert_eq!(trim_start(b" abc 123"), b"abc 123");
1531 assert_eq!(trim_start(b"abc 123 "), b"abc 123 ");
1532 assert_eq!(trim_start(b"\t foo bar \t"), b"foo bar \t");
1533 }
1534
1535 #[test]
1536 fn test_trim_end() {
1537 assert_eq!(trim_end(b""), b"");
1538 assert_eq!(trim_end(b"abc"), b"abc");
1539 assert_eq!(trim_end(b"abc 123"), b"abc 123");
1540 assert_eq!(trim_end(b" abc 123"), b" abc 123");
1541 assert_eq!(trim_end(b"abc 123 "), b"abc 123");
1542 assert_eq!(trim_end(b"\t foo bar \t"), b"\t foo bar");
1543 }
1544
1545 #[test]
1546 fn test_trim() {
1547 assert_eq!(trim(b""), b"");
1548 assert_eq!(trim(b"abc"), b"abc");
1549 assert_eq!(trim(b"abc 123"), b"abc 123");
1550 assert_eq!(trim(b" abc 123"), b"abc 123");
1551 assert_eq!(trim(b"abc 123 "), b"abc 123");
1552 assert_eq!(trim(b"\t foo bar \t"), b"foo bar");
1553 }
1554
1555 #[test]
1556 fn test_parse_str_list() {
1557 assert_eq!(parse_str_list(b"", 0), Vec::<String>::new());
1558 assert_eq!(&parse_str_list(b"abc 123", 0)[..], ["abc", "123"]);
1559 assert_eq!(&parse_str_list(b" abc 123 ", 0)[..], ["abc", "123"]);
1560 assert_eq!(
1561 &parse_str_list(" a.-bc\tUniversität ".as_bytes(), 2)[..],
1562 ["a.-bc", "Universität"]
1563 );
1564 }
1565
1566 #[test]
1567 fn test_parse_single_u32() -> io::Result<()> {
1568 assert_eq!(parse_single_u32(b"0", 42)?, 0);
1569 assert_eq!(parse_single_u32(b"00", 42)?, 0);
1570 assert_eq!(parse_single_u32(b"123", 42)?, 123);
1571 assert_eq!(
1572 parse_single_u32(u32::MAX.to_string().as_bytes(), 42)?,
1573 u32::MAX
1574 );
1575
1576 assert!(parse_single_u32(b"", 42).is_err());
1577 assert!(parse_single_u32(b"42a", 42).is_err());
1578
1579 assert!(parse_single_u32((u32::MAX as u64 + 1).to_string().as_bytes(), 42).is_err());
1580
1581 Ok(())
1582 }
1583
1584 #[test]
1585 fn test_parse_single_usize() -> io::Result<()> {
1586 assert_eq!(parse_single_usize(b"0", 42)?, 0);
1587 assert_eq!(parse_single_usize(b"00", 42)?, 0);
1588 assert_eq!(parse_single_usize(b"123", 42)?, 123);
1589 assert_eq!(
1590 parse_single_usize(usize::MAX.to_string().as_bytes(), 42)?,
1591 usize::MAX
1592 );
1593
1594 assert!(parse_single_usize(b"", 42).is_err());
1595 assert!(parse_single_usize(b"42a", 42).is_err());
1596
1597 if u128::BITS > usize::BITS {
1598 assert!(
1599 parse_single_usize((usize::MAX as u128 + 1).to_string().as_bytes(), 42).is_err()
1600 );
1601 }
1602
1603 Ok(())
1604 }
1605
1606 #[test]
1607 fn test_parse_u32() -> io::Result<()> {
1608 assert_eq!(parse_u32(b"0", 42)?, (&b""[..], 0));
1609 assert_eq!(parse_u32(b"00", 42)?, (&b""[..], 0));
1610 assert_eq!(parse_u32(b"123", 42)?, (&b""[..], 123));
1611 assert_eq!(
1612 parse_u32(u32::MAX.to_string().as_bytes(), 42)?,
1613 (&b""[..], u32::MAX)
1614 );
1615
1616 let (rest, v) = parse_u32(b" 023 9 \t432\t", 42)?;
1617 assert_eq!(rest, b" 9 \t432\t");
1618 assert_eq!(v, 23);
1619 let (rest, v) = parse_u32(rest, 42)?;
1620 assert_eq!(rest, b" \t432\t");
1621 assert_eq!(v, 9);
1622 let (rest, v) = parse_u32(rest, 42)?;
1623 assert_eq!(rest, b"\t");
1624 assert_eq!(v, 432);
1625 assert!(parse_u32(rest, 42).is_err());
1626
1627 assert!(parse_u32(b"", 42).is_err());
1628 assert!(parse_u32(b"42a", 42).is_err());
1629
1630 assert!(parse_u32((u32::MAX as u64 + 1).to_string().as_bytes(), 42).is_err());
1631
1632 Ok(())
1633 }
1634
1635 #[test]
1636 fn test_parse_usize() -> io::Result<()> {
1637 assert_eq!(parse_usize(b"0", 42)?, (&b""[..], 0));
1638 assert_eq!(parse_usize(b"00", 42)?, (&b""[..], 0));
1639 assert_eq!(parse_usize(b"123", 42)?, (&b""[..], 123));
1640 assert_eq!(
1641 parse_usize(usize::MAX.to_string().as_bytes(), 42)?,
1642 (&b""[..], usize::MAX)
1643 );
1644
1645 let (rest, v) = parse_usize(b" 023 9 \t432\t", 42)?;
1646 assert_eq!(rest, b" 9 \t432\t");
1647 assert_eq!(v, 23);
1648 let (rest, v) = parse_usize(rest, 42)?;
1649 assert_eq!(rest, b" \t432\t");
1650 assert_eq!(v, 9);
1651 let (rest, v) = parse_usize(rest, 42)?;
1652 assert_eq!(rest, b"\t");
1653 assert_eq!(v, 432);
1654 assert!(parse_usize(rest, 42).is_err());
1655
1656 assert!(parse_usize(b"", 42).is_err());
1657 assert!(parse_usize(b"42a", 42).is_err());
1658
1659 if u128::BITS > usize::BITS {
1660 assert!(parse_usize((usize::MAX as u128 + 1).to_string().as_bytes(), 42).is_err());
1661 }
1662
1663 Ok(())
1664 }
1665
1666 #[test]
1667 fn test_parse_u32_list() -> io::Result<()> {
1668 assert_eq!(&parse_u32_list(b"", 0, 42)?[..], []);
1669 assert_eq!(&parse_u32_list(b" \t ", 1, 42)?[..], []);
1670 assert_eq!(&parse_u32_list(b" 123 \t 432 ", 2, 42)?[..], [123, 432]);
1671 assert_eq!(&parse_u32_list(b"0 00", 1, 42)?[..], [0, 0]);
1672 assert_eq!(
1673 &parse_u32_list(u32::MAX.to_string().as_bytes(), 1, 42)?[..],
1674 [u32::MAX]
1675 );
1676
1677 assert!(parse_u32_list(b"123 42a", 2, 42).is_err());
1678 assert!(parse_u32_list(b"123 -42", 2, 42).is_err());
1679
1680 assert!(parse_u32_list((u32::MAX as u64 + 1).to_string().as_bytes(), 1, 42).is_err());
1681
1682 Ok(())
1683 }
1684
1685 #[test]
1686 fn test_parse_edge_list() -> io::Result<()> {
1687 let mut res = Vec::with_capacity(32);
1688
1689 parse_edge_list(b"", &mut res, 42)?;
1690 assert_eq!(&res[..], []);
1691 parse_edge_list(b" \t ", &mut res, 42)?;
1692 assert_eq!(&res[..], []);
1693 parse_edge_list(b" 123 \t -432 ", &mut res, 42)?;
1694 assert_eq!(&res[..], [123, -432,]);
1695 res.clear();
1696 parse_edge_list(b"01 -02", &mut res, 42)?;
1697 assert_eq!(&res[..], [1, -2,]);
1698 res.clear();
1699 parse_edge_list(b"0 -0", &mut res, 42)?;
1700 assert_eq!(&res[..], [0, 0,]);
1701 res.clear();
1702 parse_edge_list(isize::MAX.to_string().as_bytes(), &mut res, 42)?;
1703 assert_eq!(&res[..], [isize::MAX]);
1704 res.clear();
1705 parse_edge_list(format!("-{}", isize::MAX).as_bytes(), &mut res, 42)?;
1706 assert_eq!(&res[..], [-isize::MAX]);
1707
1708 assert!(parse_edge_list(b"123 42a", &mut res, 42).is_err());
1709 assert!(parse_edge_list(b"123 4-2", &mut res, 42).is_err());
1710
1711 let isize_min = isize::MIN.to_string();
1712 assert!(parse_edge_list(isize_min.as_bytes(), &mut res, 42).is_err());
1713 assert!(parse_edge_list(isize_min[1..].as_bytes(), &mut res, 42).is_err());
1714
1715 Ok(())
1716 }
1717
1718 }