more parsing optimizations

This commit is contained in:
kageru 2022-12-07 15:51:46 +01:00
parent 8050b16034
commit 2d601b9bfb

View File

@ -1,4 +1,4 @@
#![feature(test, if_let_guard, iter_collect_into)] #![feature(test, if_let_guard)]
extern crate test; extern crate test;
use aoc2022::{boilerplate, common::*}; use aoc2022::{boilerplate, common::*};
@ -6,19 +6,15 @@ use aoc2022::{boilerplate, common::*};
const DAY: usize = 7; const DAY: usize = 7;
enum Node<'a> { enum Node<'a> {
File(&'a str, usize), File(usize),
Dir(&'a str, Vec<Node<'a>>, usize), Dir(&'a str, Vec<Node<'a>>, usize),
} }
impl<'a> Node<'a> { impl<'a> Node<'a> {
/// Implementing this rather than IndexMut because that requires a mostly redundant fn subdir_mut(&mut self, dir: &str) -> Option<&mut Self> {
/// implementation of Index which is more boilerplate.
fn subdir_mut(&mut self, dir: &str) -> &mut Self {
match self { match self {
Self::Dir(_, contents, _) => { Self::Dir(_, contents, _) => contents.iter_mut().find(|d| matches!(**d, Self::Dir(name, _, _) if name == dir)),
contents.iter_mut().find(|d| matches!(**d, Self::Dir(name, _, _) if name == dir)).expect("File not found") _ => None,
}
Self::File(name, _) => panic!("Can't index into a file ({name})"),
} }
} }
} }
@ -26,21 +22,18 @@ impl<'a> Node<'a> {
fn parse_input(raw: &str) -> Node<'_> { fn parse_input(raw: &str) -> Node<'_> {
let mut pwd = Vec::<&str>::new(); let mut pwd = Vec::<&str>::new();
let mut fs = Node::Dir("/", Vec::new(), 0); let mut fs = Node::Dir("/", Vec::new(), 0);
for cmd in raw.trim_start_matches("$ cd /\n$ ").split("$ ") { for cmd in raw.trim_start_matches("$ cd /\n$ ").split("\n$ ") {
let mut lines = cmd.lines(); match cmd.bytes().next() {
match lines.next().and_then(|s| s.split_once(' ')) { Some(b'c') => if cmd.ends_with(".") { drop(pwd.pop()) } else { pwd.push(&cmd[3..]) },
Some(("cd", "..")) => drop(pwd.pop()),
Some(("cd", dir)) => pwd.push(dir),
// ls // ls
_ if let Node::Dir(_, contents, _) = pwd.iter().fold(&mut fs, |cd, p| cd.subdir_mut(p)) => { _ if let Some(Node::Dir(_, contents, _)) = pwd.iter().try_fold(&mut fs, |cd, p| cd.subdir_mut(p)) => contents.extend(
lines cmd.lines()
.filter_map(|l| l.split_once(' ')) .filter_map(|l| l.split_once(' '))
.map(|line| match line { .map(|line| match line {
("dir", d) => Node::Dir(d, Vec::new(), 0), ("dir", d) => Node::Dir(d, Vec::new(), 0),
(size, name) => Node::File(name, size.parse().unwrap()) (size, _) => Node::File(parse_num(size))
}) })
.collect_into(contents); ),
}
_ => unreachable!() _ => unreachable!()
}; };
} }
@ -50,7 +43,7 @@ fn parse_input(raw: &str) -> Node<'_> {
fn compute_dir_sizes(node: &mut Node<'_>) -> usize { fn compute_dir_sizes(node: &mut Node<'_>) -> usize {
match node { match node {
Node::File(_, s) => *s, Node::File(s) => *s,
Node::Dir(_, c, size) => { Node::Dir(_, c, size) => {
*size = c.iter_mut().map(|d| compute_dir_sizes(d)).sum(); *size = c.iter_mut().map(|d| compute_dir_sizes(d)).sum();
*size *size
@ -110,13 +103,13 @@ $ ls
}, },
bench1 == 1667443, bench1 == 1667443,
bench2 == 8998590, bench2 == 8998590,
bench_parse: node_name => "/", bench_parse: dir_name => "/",
} }
#[cfg(test)] #[cfg(test)]
fn node_name<'a>(n: &Node<'a>) -> &'a str { fn dir_name<'a>(n: &Node<'a>) -> &'a str {
match n { match n {
Node::Dir(name, _, _) => name, Node::Dir(name, _, _) => name,
Node::File(name, _) => name, Node::File(_) => panic!("Not a directory"),
} }
} }