1use std::io;
2
3use is_sorted::IsSorted;
4
5use oxidd_core::error::OutOfMemory;
6use oxidd_core::function::{ETagOfFunc, EdgeOfFunc, Function, INodeOfFunc, TermOfFunc};
7use oxidd_core::util::{AllocResult, EdgeDropGuard, EdgeVecDropGuard};
8use oxidd_core::{DiagramRules, Edge, HasLevel, InnerNode, LevelNo, Manager, VarNo};
9
10use crate::ParseTagged;
11
12use super::{Code, VarInfo};
13
14fn cmp_strict<T: Ord>(a: &T, b: &T) -> Option<std::cmp::Ordering> {
18 Some(a.cmp(b).then(std::cmp::Ordering::Greater))
19}
20
21fn err<T>(msg: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Result<T> {
23 Err(io::Error::new(io::ErrorKind::InvalidData, msg))
24}
25
26#[derive(Debug)]
28pub struct DumpHeader {
29 ascii: bool,
31 varinfo: VarInfo,
32 dd: String,
34 nnodes: usize,
35 nvars: u32,
36 ids: Vec<VarNo>,
38 support_var_order: Vec<VarNo>,
40 permids: Vec<LevelNo>,
42 auxids: Vec<u32>, varnames: Vec<String>,
45 rootids: Vec<isize>,
46 rootnames: Vec<String>, lines: usize, }
50
51impl DumpHeader {
52 pub fn load(mut input: impl io::BufRead) -> io::Result<Self> {
57 let mut header = DumpHeader {
58 ascii: true,
59 varinfo: VarInfo::None,
60 dd: String::new(),
61 nnodes: 0,
62 nvars: 0,
63 ids: Vec::new(),
64 support_var_order: Vec::new(),
65 permids: Vec::new(),
66 auxids: Vec::new(),
67 varnames: Vec::new(),
68 rootids: Vec::new(),
69 rootnames: Vec::new(),
70 lines: 1,
71 };
72 let mut nsuppvars = 0;
73 let mut nroots = 0;
74 let mut suppvarnames = Vec::new();
75 let mut orderedvarnames = Vec::new();
76
77 let mut line = Vec::new();
78 let mut line_no = 1usize;
79
80 loop {
81 match input.read_until(b'\n', &mut line) {
82 Ok(0) => return err("unexpected end of file"),
83 Ok(_) => {}
84 Err(e) => return Err(e),
85 }
86 while let Some(b'\n' | b'\r') = line.last() {
87 line.pop();
88 }
89
90 let (key, value) = match memchr::memchr2(b' ', b'\t', &line) {
91 Some(p) => (&line[..p], &line[p + 1..]),
92 None => (&line[..], [].as_slice()),
93 };
94 let value = trim(value);
95
96 match key {
98 b".ver" => match value {
99 b"DDDMP-2.0" | b"DDDMP-3.0" => {}
100 _ => {
101 return err(format!(
102 "unsupported version '{}' (line {line_no})",
103 String::from_utf8_lossy(value)
104 ))
105 }
106 },
107 b".mode" => {
108 header.ascii = match value {
109 b"A" => true,
110 b"B" => false,
111 _ => {
112 return err(format!(
113 "unknown value '{}' for key '.mode' (line {line_no})",
114 String::from_utf8_lossy(value),
115 ))
116 }
117 };
118 }
119 b".varinfo" => {
120 header.varinfo = match value {
121 b"0" => VarInfo::VariableID,
122 b"1" => VarInfo::PermutationID,
123 b"2" => VarInfo::AuxiliaryID,
124 b"3" => VarInfo::VariableName,
125 b"4" => VarInfo::None,
126 _ => {
127 return err(format!(
128 "unknown value '{}' for key '.varinfo' (line {line_no})",
129 String::from_utf8_lossy(value),
130 ))
131 }
132 };
133 }
134 b".dd" => header.dd = String::from_utf8_lossy(value).to_string(),
135 b".nnodes" => header.nnodes = parse_single_usize(value, line_no)?,
136 b".nvars" => header.nvars = parse_single_u32(value, line_no)?,
137 b".nsuppvars" => nsuppvars = parse_single_u32(value, line_no)?,
138 b".varnames" => header.varnames = parse_str_list(value, header.nvars as usize),
139 b".suppvarnames" => suppvarnames = parse_str_list(value, nsuppvars as usize),
140 b".orderedvarnames" => {
141 orderedvarnames = parse_str_list(value, header.nvars as usize)
142 }
143 b".ids" => {
144 header.ids = parse_u32_list(value, nsuppvars as usize, line_no)?;
145 }
146 b".permids" => {
147 header.permids = parse_u32_list(value, nsuppvars as usize, line_no)?;
148 }
149 b".auxids" => {
150 header.auxids = parse_u32_list(value, nsuppvars as usize, line_no)?;
151 }
152 b".nroots" => nroots = parse_single_usize(value, line_no)?,
153 b".rootids" => {
154 header.rootids.clear();
155 header.rootids.reserve(nroots);
156 parse_edge_list(value, &mut header.rootids, line_no)?;
157 }
158 b".rootnames" => header.rootnames = parse_str_list(value, nroots),
159 b".nodes" => break,
160 _ => {
161 return err(format!(
162 "unknown key '{}' (line {line_no})",
163 String::from_utf8_lossy(key),
164 ))
165 }
166 }
167
168 line_no += 1;
169 line.clear();
170 }
171
172 header.lines = line_no;
173
174 if nsuppvars > header.nvars {
177 return err(format!(
178 ".nsuppvars ({nsuppvars}) must not be greater than .nvars ({})",
179 header.nvars,
180 ));
181 }
182
183 if header.ids.len() != nsuppvars as usize {
184 return err(format!(
185 "number of support variables in .ids entry ({}) does not match .nsuppvars ({nsuppvars})",
186 header.ids.len(),
187 ));
188 }
189 if header.permids.len() != nsuppvars as usize {
190 return err(format!(
191 "number of support variables in .permids entry ({}) does not match .nsuppvars ({nsuppvars})",
192 header.permids.len(),
193 ));
194 }
195 if !header.auxids.is_empty() && header.auxids.len() != nsuppvars as usize {
196 return err(format!(
197 "number of support variables in .auxids entry ({}) does not match .nsuppvars ({nsuppvars})",
198 header.auxids.len(),
199 ));
200 }
201
202 if !header.ids.is_empty() {
203 if !IsSorted::is_sorted_by(&mut header.ids.iter(), cmp_strict) {
204 return err("support variables in .ids must be ascending");
205 }
206 if *header.ids.last().unwrap() >= header.nvars {
207 return err(format!(
208 "support variables in .ids must be less than .nvars ({})",
209 header.nvars,
210 ));
211 }
212 }
213
214 let mut level_count = vec![0u32; header.nvars as usize];
215 for &level in &header.permids {
216 let Some(count) = level_count.get_mut(level as usize) else {
217 return err(format!(
218 "levels in .permids must be less than .nvars ({})",
219 header.nvars,
220 ));
221 };
222 if *count != 0 {
223 return err(format!("level ({level}) occurs twice in .permids"));
224 }
225 *count = 1;
226 }
227 let mut count = 0;
230 for i in level_count.iter_mut() {
231 let present = *i;
232 *i = count;
233 count += present;
234 }
235 header.support_var_order.resize(nsuppvars as usize, 0);
236 for (&var, &level) in header.ids.iter().zip(&header.permids) {
237 header.support_var_order[level_count[level as usize] as usize] = var;
238 }
239 drop(level_count);
240
241 if !orderedvarnames.is_empty() && orderedvarnames.len() != header.nvars as usize {
242 return err(format!(
243 "number of variables in .orderedvarnames entry ({}) does not match .nvars ({})",
244 orderedvarnames.len(),
245 header.nvars
246 ));
247 }
248 if !suppvarnames.is_empty() && suppvarnames.len() != nsuppvars as usize {
249 return err(format!(
250 "number of variables in .suppvarnames entry ({}) does not match .nsuppvars ({nsuppvars})",
251 suppvarnames.len(),
252 ));
253 }
254
255 'var_names: {
256 if header.varnames.is_empty() {
257 if orderedvarnames.is_empty() {
258 if suppvarnames.is_empty() {
259 break 'var_names;
260 }
261 debug_assert!(!suppvarnames.is_empty());
262 header.varnames = vec![String::new(); header.nvars as usize];
263 for (name, &target) in suppvarnames.into_iter().zip(&header.ids) {
264 header.varnames[target as usize] = name;
265 }
266 break 'var_names;
267 }
268
269 header.varnames = vec![String::new(); header.nvars as usize];
273 for (&id, &permid) in header.ids.iter().zip(&header.permids) {
274 header.varnames[id as usize] =
275 std::mem::take(&mut orderedvarnames[permid as usize]);
276 }
277
278 let mut non_suppvarnames = orderedvarnames.into_iter().filter(|s| !s.is_empty());
279 for name in &mut header.varnames {
280 if name.is_empty() {
281 *name = non_suppvarnames.next().unwrap();
282 }
283 }
284 } else {
285 if header.varnames.len() != header.nvars as usize {
286 return err(format!(
287 "number of variables in .varnames entry ({}) does not match .nvars ({})",
288 header.varnames.len(),
289 header.nvars
290 ));
291 }
292
293 if !orderedvarnames.is_empty() {
294 for (&id, &permid) in header.ids.iter().zip(&header.permids) {
301 let name = header.varnames[id as usize].as_str();
302 let order_name = orderedvarnames[permid as usize].as_str();
303 if name != order_name {
304 return err(format!(
305 ".varnames and .orderedvarnames do not match \
306 (variable {id} has name '{name}', but the entry at \
307 position {permid} of .orderedvarnames is \
308 '{order_name}'"
309 ));
310 }
311 }
312 }
313 }
314
315 for (i, (name, &id)) in suppvarnames.into_iter().zip(&header.ids).enumerate() {
317 let expected = header.varnames[id as usize].as_str();
318 if name != expected {
319 return err(format!(
320 ".suppvarnames and .varnames/.orderedvarnames do not \
321 match (entry {i} of .suppvarnames is '{name}' \
322 but the variable ID is {id} with name '{expected}')"
323 ));
324 }
325 }
326 }
327
328 if header.rootids.len() != nroots {
329 return err(format!(
330 "number of roots in .rootids entry ({}) does not match .nroots ({nroots})",
331 header.rootids.len(),
332 ));
333 }
334 for &id in &header.rootids {
335 if id == 0 {
336 return err(".rootids must not be 0");
337 }
338 if id.unsigned_abs() > header.nnodes {
339 return err(format!(
340 "entry in .rootids out of range ({id} > {})",
341 header.nnodes,
342 ));
343 }
344 }
345 if !header.rootnames.is_empty() && header.rootnames.len() != nroots {
346 return err(format!(
347 "number of roots in .rootnames entry ({}) does not match .nroots ({nroots})",
348 header.rootnames.len(),
349 ));
350 }
351
352 Ok(header)
353 }
354
355 pub fn diagram_name(&self) -> Option<&str> {
359 if self.dd.is_empty() {
360 None
361 } else {
362 Some(&self.dd)
363 }
364 }
365
366 pub fn num_nodes(&self) -> usize {
370 self.nnodes
371 }
372
373 pub fn num_vars(&self) -> u32 {
377 self.nvars
378 }
379
380 pub fn num_support_vars(&self) -> u32 {
384 self.ids.len() as _
385 }
386
387 pub fn support_vars(&self) -> &[VarNo] {
399 &self.ids
400 }
401
402 pub fn support_var_order(&self) -> &[VarNo] {
412 &self.support_var_order
413 }
414
415 pub fn support_var_to_level(&self) -> &[LevelNo] {
431 &self.permids
432 }
433 #[deprecated(since = "0.11.0", note = "use support_var_to_level instead")]
435 pub fn support_var_permutation(&self) -> &[LevelNo] {
436 &self.permids
437 }
438
439 pub fn auxiliary_var_ids(&self) -> &[u32] {
444 &self.auxids
445 }
446
447 pub fn var_names(&self) -> Option<&[String]> {
458 if self.varnames.is_empty() {
459 None
460 } else {
461 Some(&self.varnames[..])
462 }
463 }
464
465 pub fn num_roots(&self) -> usize {
470 self.rootids.len()
471 }
472
473 pub fn root_names(&self) -> Option<&[String]> {
478 if self.rootnames.is_empty() {
479 None
480 } else {
481 Some(&self.rootnames[..])
482 }
483 }
484}
485
486pub fn import<'id, F: Function>(
509 mut input: impl io::BufRead,
510 header: &DumpHeader,
511 manager: &F::Manager<'id>,
512 support_vars: impl IntoIterator<Item = VarNo>,
513 complement: impl Fn(&F::Manager<'id>, EdgeOfFunc<'id, F>) -> AllocResult<EdgeOfFunc<'id, F>>,
514) -> io::Result<Vec<F>>
515where
516 INodeOfFunc<'id, F>: HasLevel,
517 TermOfFunc<'id, F>: ParseTagged<ETagOfFunc<'id, F>>,
518{
519 let suppvar_level_map =
520 Vec::from_iter(support_vars.into_iter().map(|v| manager.var_to_level(v)));
521 assert_eq!(
522 suppvar_level_map.len(),
523 header.ids.len(),
524 "`support_vars` must provide one target variable per support variable in the DDDMP file",
525 );
526 assert!(
527 IsSorted::is_sorted_by(&mut suppvar_level_map.iter(), cmp_strict),
528 "`support_vars` must be sorted by the variables' current level",
529 );
530
531 let nodes = if header.ascii {
532 import_ascii::<F::Manager<'id>>(
533 &mut input,
534 header,
535 manager,
536 &suppvar_level_map,
537 &complement,
538 )
539 } else {
540 let mut level_suppvar_map = vec![u32::MAX; manager.num_levels() as usize];
541 for (i, &level) in suppvar_level_map.iter().enumerate() {
542 level_suppvar_map[level as usize] = i as u32;
543 }
544 import_bin::<F::Manager<'id>>(
545 &mut input,
546 header,
547 manager,
548 &level_suppvar_map,
549 &suppvar_level_map,
550 &complement,
551 )
552 }?;
553 let nodes = EdgeVecDropGuard::new(manager, nodes);
554 debug_assert_eq!(nodes.len(), header.nnodes);
555
556 if !reads_expected(input, b".end")? {
557 return err("file must end with '.end'");
558 }
559
560 let mut roots = Vec::with_capacity(header.rootids.len());
561 for &root in &header.rootids {
562 debug_assert_ne!(root, 0);
563 let node_index = root.unsigned_abs() - 1;
564 let e = manager.clone_edge(&nodes[node_index]);
565 roots.push(F::from_edge(
566 manager,
567 if root > 0 { e } else { complement(manager, e)? },
568 ));
569 }
570
571 Ok(roots)
572}
573
574fn reads_expected(mut file: impl io::Read, expected: &[u8]) -> io::Result<bool> {
577 let mut buf = Vec::with_capacity(expected.len() + 2);
578 file.read_to_end(&mut buf)?;
579 Ok(buf.starts_with(expected) && buf[expected.len()..].iter().all(u8::is_ascii_whitespace))
580}
581
582fn import_ascii<M: Manager>(
583 mut input: impl io::BufRead,
584 header: &DumpHeader,
585 manager: &M,
586 suppvar_level_map: &[LevelNo],
587 complement: impl Fn(&M, M::Edge) -> AllocResult<M::Edge>,
588) -> io::Result<Vec<M::Edge>>
589where
590 M::InnerNode: HasLevel,
591 M::Terminal: ParseTagged<M::EdgeTag>,
592{
593 let mut nodes = EdgeVecDropGuard::new(manager, Vec::with_capacity(header.nnodes));
594 let mut line = Vec::new();
595 let mut line_no = header.lines + 1;
596 let mut children = Vec::with_capacity(M::InnerNode::ARITY);
597 for node_id in 1..=header.nnodes {
598 match input.read_until(b'\n', &mut line) {
599 Ok(0) => return err("unexpected end of file"),
600 Ok(_) => {}
601 Err(e) => return Err(e),
602 }
603 while let Some(b'\n' | b'\r') = line.last() {
604 line.pop();
605 }
606
607 let (rest, node_id_lineno) = parse_usize(&line, line_no)?;
608 if node_id_lineno != node_id {
609 return err(format!("expected node ID {node_id} on line {line_no}"));
610 }
611
612 let rest = trim_start(rest);
613 let rest = match header.varinfo {
614 VarInfo::VariableID
615 | VarInfo::PermutationID
616 | VarInfo::AuxiliaryID
617 | VarInfo::VariableName => match memchr::memchr2(b' ', b'\t', rest) {
618 Some(pos) => &rest[pos + 1..],
619 None => {
620 return err(format!(
621 "expected a space after variable extra info (line {line_no})"
622 ))
623 }
624 },
625 VarInfo::None => rest,
626 };
627
628 let rest = trim_start(rest);
629 let (rest, var_id) = match memchr::memchr2(b' ', b'\t', rest) {
630 Some(pos) => (&rest[pos + 1..], &rest[..pos]),
631 None => {
632 return err(format!(
633 "expected a space after variable internal index (line {line_no})"
634 ))
635 }
636 };
637
638 parse_edge_list(rest, &mut children, line_no)?;
639 if children.len() != M::InnerNode::ARITY {
640 return err(format!(
641 "expected {} children, got {} (line {line_no})",
642 M::InnerNode::ARITY,
643 children.len()
644 ));
645 }
646
647 let node = if children.contains(&0) {
648 let string = match std::str::from_utf8(var_id) {
650 Ok(s) => s,
651 Err(_) => {
652 return err(format!(
653 "terminal description must be valid UTF-8 (line {line_no})"
654 ));
655 }
656 };
657 let Some((terminal, tag)) = M::Terminal::parse(string) else {
658 return err(format!(
659 "invalid terminal description '{string}' (line {line_no})"
660 ));
661 };
662 manager.get_terminal(terminal)?.with_tag_owned(tag)
663 } else {
664 let (_, var_id) = parse_u32(var_id, line_no)?;
665 let Some(&level) = suppvar_level_map.get(var_id as usize) else {
666 return err(format!("variable out of range (line {line_no})"));
667 };
668
669 for &child in &children {
670 let child = child.unsigned_abs();
671 if child >= node_id {
672 return err(format!(
673 "children ids must be less than node ({child} >= {node_id}, line {line_no})",
674 ));
675 }
676 let child_level = manager.get_node(&nodes[child - 1]).level();
677 if level >= child_level {
678 return err(format!(
679 "node level must be less than the children's levels ({level} >= {child_level}, line {line_no})",
680 ));
681 }
682 }
683
684 <M::Rules as DiagramRules<_, _, _>>::reduce(
685 manager,
686 level,
687 children.iter().map(|&child| {
688 debug_assert_ne!(child, 0);
689 let e = manager.clone_edge(&nodes[child.unsigned_abs() - 1]);
690 if child < 0 {
691 complement(manager, e).unwrap()
692 } else {
693 e
694 }
695 }),
696 )
697 .then_insert(manager, level)?
698 };
699 nodes.push(node);
700
701 children.clear();
702 line.clear();
703 line_no += 1;
704 }
705
706 Ok(nodes.into_vec())
707}
708
709fn import_bin<M: Manager>(
710 mut input: impl io::BufRead,
711 header: &DumpHeader,
712 manager: &M,
713 level_suppvar_map: &[u32],
714 suppvar_level_map: &[LevelNo],
715 complement: impl Fn(&M, M::Edge) -> AllocResult<M::Edge>,
716) -> io::Result<Vec<M::Edge>>
717where
718 M::InnerNode: HasLevel,
719{
720 assert_eq!(
721 M::InnerNode::ARITY,
722 2,
723 "binary mode is (currently) only supported for binary nodes"
724 );
725 let terminal = {
726 let msg = "binary mode is (currently) only supported for diagrams with a single terminal";
727 let mut it = manager.terminals();
728 let t = EdgeDropGuard::new(manager, it.next().expect(msg));
729 if let Some(t) = it.next() {
730 manager.drop_edge(t);
731 panic!("{msg}");
732 }
733 t
734 };
735
736 fn idx(input: impl io::BufRead, node_id: usize, code: Code) -> io::Result<usize> {
737 debug_assert!(node_id >= 1);
738 let id = match code {
739 Code::Terminal => 1,
740 Code::AbsoluteID => decode_7bit(input)?,
741 Code::RelativeID => node_id - decode_7bit(input)?,
742 Code::Relative1 => node_id - 1,
743 };
744 if id == 0 {
745 return err("then/else ID must not be 0");
746 }
747 if id >= node_id {
748 return err("then/else ID too large");
749 }
750 Ok((id - 1) as usize)
751 }
752
753 let mut nodes = EdgeVecDropGuard::new(manager, Vec::with_capacity(header.nnodes));
754 for node_id in 1..=header.nnodes {
755 let node_code = read_unescape(&mut input)?;
756 let var_code = Code::from((node_code >> 5) & 0b11);
757 let t_code = Code::from((node_code >> 3) & 0b11);
758 let e_complement = ((node_code >> 2) & 0b1) != 0;
759 let e_code = Code::from(node_code & 0b11);
760
761 let vid = match var_code {
762 Code::Terminal => {
763 nodes.push(manager.clone_edge(&terminal));
764 continue;
765 }
766 Code::AbsoluteID | Code::RelativeID => decode_7bit(&mut input)?,
767 Code::Relative1 => 1,
768 };
769
770 let t = EdgeDropGuard::new(
771 manager,
772 manager.clone_edge(&nodes[idx(&mut input, node_id, t_code)?]),
773 );
774 let t_level = manager.get_node(&t).level();
775 let e = EdgeDropGuard::new(
776 manager,
777 manager.clone_edge(&nodes[idx(&mut input, node_id, e_code)?]),
778 );
779 let e_level = manager.get_node(&e).level();
780 let e = if e_complement {
781 match complement(manager, e.into_edge()) {
782 Ok(e) => EdgeDropGuard::new(manager, e),
783 Err(OutOfMemory) => {
784 return Err(io::ErrorKind::OutOfMemory.into());
785 }
786 }
787 } else {
788 e
789 };
790
791 let vid = match var_code {
792 Code::Terminal => unreachable!(),
793 Code::AbsoluteID if vid >= suppvar_level_map.len() => {
794 return err("variable ID out of range")
795 }
796 Code::AbsoluteID => vid,
797 Code::RelativeID | Code::Relative1 => {
798 let min_level = std::cmp::min(t_level, e_level);
799 let child_min_suppvar = if min_level == LevelNo::MAX {
800 level_suppvar_map.len()
801 } else {
802 level_suppvar_map[min_level as usize] as usize
803 };
804 match child_min_suppvar.checked_sub(vid) {
805 Some(v) => v,
806 None => return err("variable ID out of range"),
807 }
808 }
809 };
810
811 let level = suppvar_level_map[vid as usize];
812 if level >= t_level || level >= e_level {
813 return err("node level must be less than the children's levels");
814 }
815
816 let children = [t.into_edge(), e.into_edge()];
817 match <M::Rules as DiagramRules<_, _, _>>::reduce(manager, level, children)
818 .then_insert(manager, level)
819 {
820 Ok(e) => nodes.push(e),
821 Err(OutOfMemory) => {
822 return Err(io::ErrorKind::OutOfMemory.into());
823 }
824 }
825 }
826
827 Ok(nodes.into_vec())
828}
829
830fn read_unescape(input: impl io::BufRead) -> io::Result<u8> {
832 let mut bytes = input.bytes();
835 let eof_msg = "unexpected end of file";
836 match bytes.next() {
837 None => return err(eof_msg),
838 Some(Ok(0)) => {}
839 Some(res) => return res,
840 };
841 match bytes.next() {
842 None => err(eof_msg),
843 Some(Ok(0x00)) => Ok(0x00),
844 Some(Ok(0x01)) => Ok(0x0a),
845 Some(Ok(0x02)) => Ok(0x0d),
846 Some(Ok(0x03)) => Ok(0x1a),
847 Some(Ok(b)) => Err(io::Error::new(
848 io::ErrorKind::InvalidData,
849 format!("invalid escape sequence 0x00 0x{b:x}"),
850 )),
851 Some(e) => e,
852 }
853}
854
855fn decode_7bit(mut input: impl io::BufRead) -> io::Result<usize> {
858 let mut res = 0usize;
859 loop {
860 let b = read_unescape(&mut input)?;
861 match res.checked_shl(7) {
862 Some(v) => res = v,
863 None => return err("integer too large"),
864 }
865 res |= (b >> 1) as usize;
866 if b & 1 == 0 {
867 return Ok(res);
868 }
869 }
870}
871
872const fn trim_start(mut s: &[u8]) -> &[u8] {
874 while let [b' ' | b'\t', rest @ ..] = s {
875 s = rest;
876 }
877 s
878}
879
880const fn trim_end(mut s: &[u8]) -> &[u8] {
882 while let [rest @ .., b' ' | b'\t'] = s {
883 s = rest;
884 }
885 s
886}
887
888const fn trim(s: &[u8]) -> &[u8] {
890 trim_end(trim_start(s))
891}
892
893fn parse_str_list(input: &[u8], capacity: usize) -> Vec<String> {
897 let mut res = Vec::with_capacity(capacity);
898 let mut start = 0;
899 for pos in memchr::memchr2_iter(b' ', b'\t', input).chain([input.len()]) {
900 if pos != start {
902 res.push(String::from_utf8_lossy(&input[start..pos]).to_string())
903 }
904 start = pos + 1;
905 }
906 res
907}
908
909macro_rules! parse_unsigned {
910 ($f:ident, $t:ty) => {
911 fn $f(input: &[u8], line_no: usize) -> io::Result<(&[u8], $t)> {
918 let mut res: $t = 0;
919 let mut num = false;
920 let mut consumed = 0;
921 for &c in input {
922 match c {
923 b'0'..=b'9' => {
924 num = true;
925 match res
926 .checked_mul(10)
927 .and_then(|v| v.checked_add((c - b'0') as _))
928 {
929 Some(v) => res = v,
930 None => {
931 return err(format!(
932 "integer '{}' too large (line {line_no})",
933 String::from_utf8_lossy(input),
934 ));
935 }
936 }
937 }
938 b' ' | b'\t' if num => break,
939 b' ' | b'\t' => {}
940 _ => {
941 return err(format!(
942 "unexpected char '{}' in integer (line {line_no})",
943 c as char,
944 ));
945 }
946 }
947 consumed += 1;
948 }
949 if !num {
950 return err(format!(
951 "expected an integer, got an empty string (line {line_no})"
952 ));
953 }
954 Ok((&input[consumed..], res))
955 }
956 };
957}
958parse_unsigned!(parse_u32, u32);
959parse_unsigned!(parse_usize, usize);
960
961macro_rules! parse_single_unsigned {
962 ($f:ident, $t:ty) => {
963 fn $f(input: &[u8], line_no: usize) -> io::Result<$t> {
965 let mut res: $t = 0;
966 let mut num = false;
967 for &c in input {
968 match c {
969 b'0'..=b'9' => {
970 num = true;
971 match res
972 .checked_mul(10)
973 .and_then(|v| v.checked_add((c - b'0') as _))
974 {
975 Some(v) => res = v,
976 None => {
977 return err(format!(
978 "integer '{}' too large (line {line_no})",
979 String::from_utf8_lossy(input),
980 ));
981 }
982 }
983 }
984 _ => {
985 return err(format!(
986 "unexpected char '{}' in integer (line {line_no})",
987 c as char,
988 ));
989 }
990 }
991 }
992 if !num {
993 return err(format!("expected an integer (line {line_no})"));
994 }
995 Ok(res)
996 }
997 };
998}
999parse_single_unsigned!(parse_single_u32, u32);
1000parse_single_unsigned!(parse_single_usize, usize);
1001
1002fn parse_u32_list(input: &[u8], capacity: usize, line_no: usize) -> io::Result<Vec<u32>> {
1004 let mut res = Vec::with_capacity(capacity);
1005 let mut i = 0u32;
1006 let mut num = false;
1007
1008 for &c in input {
1009 match c {
1010 b'0'..=b'9' => {
1011 num = true;
1012 match i
1013 .checked_mul(10)
1014 .and_then(|v| v.checked_add((c - b'0') as _))
1015 {
1016 Some(v) => i = v,
1017 None => {
1018 return err(format!("integer too large (line {line_no})"));
1019 }
1020 }
1021 }
1022 b' ' | b'\t' => {
1023 if num {
1024 res.push(i);
1025 num = false;
1026 i = 0;
1027 }
1028 }
1029 _ => {
1030 return err(format!(
1031 "unexpected char '{}' in space-separated list of integers (line {line_no})",
1032 c as char,
1033 ));
1034 }
1035 }
1036 }
1037 if num {
1038 res.push(i);
1039 }
1040
1041 Ok(res)
1042}
1043
1044fn parse_edge_list(input: &[u8], dst: &mut Vec<isize>, line_no: usize) -> io::Result<()> {
1052 let mut i = 0isize;
1053 let mut neg = false;
1054 let mut num = false;
1055
1056 for &c in input {
1057 match c {
1058 b'0'..=b'9' => {
1059 num = true;
1060 match i
1061 .checked_mul(10)
1062 .and_then(|v| v.checked_add((c - b'0') as isize))
1063 {
1064 Some(v) => i = v,
1065 None => {
1066 return err(format!("integer too large (line {line_no})"));
1067 }
1068 }
1069 }
1070 b'-' => {
1071 if neg {
1072 return err(format!("expected a digit after '-' (line {line_no})"));
1073 }
1074 if num {
1075 return err(format!("expected a space before '-' (line {line_no})"));
1076 }
1077 neg = true;
1078 }
1079 b' ' | b'\t' => {
1080 if num {
1081 dst.push(if neg { -i } else { i });
1082 num = false;
1083 neg = false;
1084 i = 0;
1085 }
1086 }
1087 _ => {
1088 return err(format!(
1089 "unexpected char '{}' in space-separated list of integers (line {line_no})",
1090 c as char,
1091 ));
1092 }
1093 }
1094 }
1095 if num {
1096 dst.push(if neg { -i } else { i });
1097 }
1098
1099 Ok(())
1100}
1101
1102#[cfg(test)]
1103mod test {
1104 use super::*;
1105
1106 #[test]
1107 fn test_decode_7bit() -> io::Result<()> {
1108 if usize::BITS == 64 {
1109 let b = [
1110 0b0000_0011, 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1110, ];
1121 assert_eq!(decode_7bit(&b[..])?, usize::MAX);
1122
1123 let b = [
1124 0b0000_0101, 0b0000_0001, 0b0000_0001, 0b0000_0001, 0b0000_0001, 0b0000_0001, 0b0000_0001, 0b0000_0001, 0b0000_0001, 0b0000_0000, ];
1135 assert!(decode_7bit(&b[..]).is_err());
1136 }
1137
1138 assert_eq!(decode_7bit([0, 0].as_slice())?, 0);
1140
1141 assert_eq!(decode_7bit([0b10].as_slice())?, 1);
1142
1143 Ok(())
1144 }
1145
1146 #[test]
1149 fn test_trim_start() {
1150 assert_eq!(trim_start(b""), b"");
1151 assert_eq!(trim_start(b"abc"), b"abc");
1152 assert_eq!(trim_start(b"abc 123"), b"abc 123");
1153 assert_eq!(trim_start(b" abc 123"), b"abc 123");
1154 assert_eq!(trim_start(b"abc 123 "), b"abc 123 ");
1155 assert_eq!(trim_start(b"\t foo bar \t"), b"foo bar \t");
1156 }
1157
1158 #[test]
1159 fn test_trim_end() {
1160 assert_eq!(trim_end(b""), b"");
1161 assert_eq!(trim_end(b"abc"), b"abc");
1162 assert_eq!(trim_end(b"abc 123"), b"abc 123");
1163 assert_eq!(trim_end(b" abc 123"), b" abc 123");
1164 assert_eq!(trim_end(b"abc 123 "), b"abc 123");
1165 assert_eq!(trim_end(b"\t foo bar \t"), b"\t foo bar");
1166 }
1167
1168 #[test]
1169 fn test_trim() {
1170 assert_eq!(trim(b""), b"");
1171 assert_eq!(trim(b"abc"), b"abc");
1172 assert_eq!(trim(b"abc 123"), b"abc 123");
1173 assert_eq!(trim(b" abc 123"), b"abc 123");
1174 assert_eq!(trim(b"abc 123 "), b"abc 123");
1175 assert_eq!(trim(b"\t foo bar \t"), b"foo bar");
1176 }
1177
1178 #[test]
1179 fn test_parse_str_list() {
1180 assert_eq!(parse_str_list(b"", 0), Vec::<String>::new());
1181 assert_eq!(&parse_str_list(b"abc 123", 0)[..], ["abc", "123"]);
1182 assert_eq!(&parse_str_list(b" abc 123 ", 0)[..], ["abc", "123"]);
1183 assert_eq!(
1184 &parse_str_list(" a.-bc\tUniversität ".as_bytes(), 2)[..],
1185 ["a.-bc", "Universität"]
1186 );
1187 }
1188
1189 #[test]
1190 fn test_parse_single_u32() -> io::Result<()> {
1191 assert_eq!(parse_single_u32(b"0", 42)?, 0);
1192 assert_eq!(parse_single_u32(b"00", 42)?, 0);
1193 assert_eq!(parse_single_u32(b"123", 42)?, 123);
1194 assert_eq!(
1195 parse_single_u32(u32::MAX.to_string().as_bytes(), 42)?,
1196 u32::MAX
1197 );
1198
1199 assert!(parse_single_u32(b"", 42).is_err());
1200 assert!(parse_single_u32(b"42a", 42).is_err());
1201
1202 assert!(parse_single_u32((u32::MAX as u64 + 1).to_string().as_bytes(), 42).is_err());
1203
1204 Ok(())
1205 }
1206
1207 #[test]
1208 fn test_parse_single_usize() -> io::Result<()> {
1209 assert_eq!(parse_single_usize(b"0", 42)?, 0);
1210 assert_eq!(parse_single_usize(b"00", 42)?, 0);
1211 assert_eq!(parse_single_usize(b"123", 42)?, 123);
1212 assert_eq!(
1213 parse_single_usize(usize::MAX.to_string().as_bytes(), 42)?,
1214 usize::MAX
1215 );
1216
1217 assert!(parse_single_usize(b"", 42).is_err());
1218 assert!(parse_single_usize(b"42a", 42).is_err());
1219
1220 if u128::BITS > usize::BITS {
1221 assert!(
1222 parse_single_usize((usize::MAX as u128 + 1).to_string().as_bytes(), 42).is_err()
1223 );
1224 }
1225
1226 Ok(())
1227 }
1228
1229 #[test]
1230 fn test_parse_u32() -> io::Result<()> {
1231 assert_eq!(parse_u32(b"0", 42)?, (&b""[..], 0));
1232 assert_eq!(parse_u32(b"00", 42)?, (&b""[..], 0));
1233 assert_eq!(parse_u32(b"123", 42)?, (&b""[..], 123));
1234 assert_eq!(
1235 parse_u32(u32::MAX.to_string().as_bytes(), 42)?,
1236 (&b""[..], u32::MAX)
1237 );
1238
1239 let (rest, v) = parse_u32(b" 023 9 \t432\t", 42)?;
1240 assert_eq!(rest, b" 9 \t432\t");
1241 assert_eq!(v, 23);
1242 let (rest, v) = parse_u32(rest, 42)?;
1243 assert_eq!(rest, b" \t432\t");
1244 assert_eq!(v, 9);
1245 let (rest, v) = parse_u32(rest, 42)?;
1246 assert_eq!(rest, b"\t");
1247 assert_eq!(v, 432);
1248 assert!(parse_u32(rest, 42).is_err());
1249
1250 assert!(parse_u32(b"", 42).is_err());
1251 assert!(parse_u32(b"42a", 42).is_err());
1252
1253 assert!(parse_u32((u32::MAX as u64 + 1).to_string().as_bytes(), 42).is_err());
1254
1255 Ok(())
1256 }
1257
1258 #[test]
1259 fn test_parse_usize() -> io::Result<()> {
1260 assert_eq!(parse_usize(b"0", 42)?, (&b""[..], 0));
1261 assert_eq!(parse_usize(b"00", 42)?, (&b""[..], 0));
1262 assert_eq!(parse_usize(b"123", 42)?, (&b""[..], 123));
1263 assert_eq!(
1264 parse_usize(usize::MAX.to_string().as_bytes(), 42)?,
1265 (&b""[..], usize::MAX)
1266 );
1267
1268 let (rest, v) = parse_usize(b" 023 9 \t432\t", 42)?;
1269 assert_eq!(rest, b" 9 \t432\t");
1270 assert_eq!(v, 23);
1271 let (rest, v) = parse_usize(rest, 42)?;
1272 assert_eq!(rest, b" \t432\t");
1273 assert_eq!(v, 9);
1274 let (rest, v) = parse_usize(rest, 42)?;
1275 assert_eq!(rest, b"\t");
1276 assert_eq!(v, 432);
1277 assert!(parse_usize(rest, 42).is_err());
1278
1279 assert!(parse_usize(b"", 42).is_err());
1280 assert!(parse_usize(b"42a", 42).is_err());
1281
1282 if u128::BITS > usize::BITS {
1283 assert!(parse_usize((usize::MAX as u128 + 1).to_string().as_bytes(), 42).is_err());
1284 }
1285
1286 Ok(())
1287 }
1288
1289 #[test]
1290 fn test_parse_u32_list() -> io::Result<()> {
1291 assert_eq!(&parse_u32_list(b"", 0, 42)?[..], []);
1292 assert_eq!(&parse_u32_list(b" \t ", 1, 42)?[..], []);
1293 assert_eq!(&parse_u32_list(b" 123 \t 432 ", 2, 42)?[..], [123, 432]);
1294 assert_eq!(&parse_u32_list(b"0 00", 1, 42)?[..], [0, 0]);
1295 assert_eq!(
1296 &parse_u32_list(u32::MAX.to_string().as_bytes(), 1, 42)?[..],
1297 [u32::MAX]
1298 );
1299
1300 assert!(parse_u32_list(b"123 42a", 2, 42).is_err());
1301 assert!(parse_u32_list(b"123 -42", 2, 42).is_err());
1302
1303 assert!(parse_u32_list((u32::MAX as u64 + 1).to_string().as_bytes(), 1, 42).is_err());
1304
1305 Ok(())
1306 }
1307
1308 #[test]
1309 fn test_parse_edge_list() -> io::Result<()> {
1310 let mut res = Vec::with_capacity(32);
1311
1312 parse_edge_list(b"", &mut res, 42)?;
1313 assert_eq!(&res[..], []);
1314 parse_edge_list(b" \t ", &mut res, 42)?;
1315 assert_eq!(&res[..], []);
1316 parse_edge_list(b" 123 \t -432 ", &mut res, 42)?;
1317 assert_eq!(&res[..], [123, -432,]);
1318 res.clear();
1319 parse_edge_list(b"01 -02", &mut res, 42)?;
1320 assert_eq!(&res[..], [1, -2,]);
1321 res.clear();
1322 parse_edge_list(b"0 -0", &mut res, 42)?;
1323 assert_eq!(&res[..], [0, 0,]);
1324 res.clear();
1325 parse_edge_list(isize::MAX.to_string().as_bytes(), &mut res, 42)?;
1326 assert_eq!(&res[..], [isize::MAX]);
1327 res.clear();
1328 parse_edge_list(format!("-{}", isize::MAX).as_bytes(), &mut res, 42)?;
1329 assert_eq!(&res[..], [-isize::MAX]);
1330
1331 assert!(parse_edge_list(b"123 42a", &mut res, 42).is_err());
1332 assert!(parse_edge_list(b"123 4-2", &mut res, 42).is_err());
1333
1334 let isize_min = isize::MIN.to_string();
1335 assert!(parse_edge_list(isize_min.as_bytes(), &mut res, 42).is_err());
1336 assert!(parse_edge_list(&isize_min.as_bytes()[1..], &mut res, 42).is_err());
1337
1338 Ok(())
1339 }
1340
1341 }