Remove most allocations

This commit is contained in:
2022-12-07 15:27:30 +01:00
parent e80b5bde68
commit 45a6c78d77

View File

@@ -1,5 +1,3 @@
use std::fmt::Debug;
use anyhow::Context; use anyhow::Context;
use anyhow::Result; use anyhow::Result;
use nom::branch::alt; use nom::branch::alt;
@@ -8,7 +6,7 @@ use nom::bytes::complete::take_until;
use nom::character::complete::newline; use nom::character::complete::newline;
use nom::combinator::map; use nom::combinator::map;
use nom::combinator::opt; use nom::combinator::opt;
use nom::multi::many0; use nom::multi::fold_many0;
use nom::sequence::delimited; use nom::sequence::delimited;
use nom::sequence::preceded; use nom::sequence::preceded;
use nom::sequence::separated_pair; use nom::sequence::separated_pair;
@@ -18,21 +16,22 @@ use nom::IResult;
use crate::common::parse_input; use crate::common::parse_input;
type Listing<'a> = Vec<(&'a [u8], ListEntry<'a>)>; fn parse_dir<'a>(
type Slicing<'a> = &'a [(&'a [u8], ListEntry<'a>)]; input: &'a [u8],
dirs: &mut Vec<u32>,
#[derive(Debug)] dir_stack: &mut Vec<&'a [u8]>,
enum ListEntry<'a> { ) -> IResult<&'a [u8], u32> {
File(u32),
Dir(Listing<'a>),
}
fn parse_dir(input: &[u8]) -> IResult<&[u8], Listing> {
use nom::character::complete::u32; use nom::character::complete::u32;
let (mut input, mut entries) = preceded( enum Entry<'a> {
File(u32),
Dir(&'a [u8]),
}
let initial_len = dir_stack.len();
let (mut input, mut size) = preceded(
tag("$ ls\n"), tag("$ ls\n"),
many0( fold_many0(
// Map many newline-terminated entries // Map many newline-terminated entries
terminated( terminated(
// of either // of either
@@ -40,67 +39,57 @@ fn parse_dir(input: &[u8]) -> IResult<&[u8], Listing> {
// A size followed by a name // A size followed by a name
map( map(
separated_pair(u32, tag(" "), take_until("\n")), separated_pair(u32, tag(" "), take_until("\n")),
|(size, name)| (name, ListEntry::File(size)), |(size, _)| Entry::File(size),
), ),
// Or the word "dir" followed by a name // Or the word "dir" followed by a name
map(preceded(tag("dir "), take_until("\n")), |name| { map(preceded(tag("dir "), take_until("\n")), |name| {
(name, ListEntry::Dir(Vec::new())) Entry::Dir(name)
}), }),
)), )),
newline, newline,
), ),
|| 0u32,
|files_sum, entry| match entry {
Entry::File(size) => files_sum + size,
Entry::Dir(name) => {
dir_stack.push(name);
files_sum
}
},
), ),
)(input)?; )(input)?;
// Assumption: directory entries are queried in the order they are listed for i in initial_len..dir_stack.len() {
// let (new_input, content_size) = delimited(
// This assumption appears to hold for my input so it's fine tuple((tag("$ cd "), tag(dir_stack[i]), newline)),
for (name, entry) in &mut entries { |input| parse_dir(input, dirs, dir_stack),
if let ListEntry::Dir(placeholder) = entry { // Optional cd'ing out because the last directory is never exited.
let (new_input, contents) = delimited( opt(tag("$ cd ..\n")),
tuple((tag("$ cd "), tag(*name), newline)), )(input)?;
parse_dir,
// Optional cd'ing out because the last directory is never exited.
opt(tag("$ cd ..\n")),
)(input)?;
input = new_input; input = new_input;
*placeholder = contents; size += content_size;
}
} }
Ok((input, entries)) dirs.push(size);
Ok((input, size))
} }
fn parse_program(input: &[u8]) -> IResult<&[u8], Listing> { fn parse_program(input: &[u8]) -> IResult<&[u8], (u32, Vec<u32>)> {
preceded(tag("$ cd /\n"), parse_dir)(input) let mut dirs = Vec::new();
} let mut dirstack = Vec::new();
let (input, size) = preceded(tag("$ cd /\n"), |input| {
parse_dir(input, &mut dirs, &mut dirstack)
})(input)?;
fn sum_sizes(listing: Slicing<'_>, acc: &mut impl FnMut(u32)) -> u32 { Ok((input, (size, dirs)))
let mut total_size = 0;
for (_, entry) in listing {
match entry {
ListEntry::File(size) => total_size += size,
ListEntry::Dir(listing) => total_size += sum_sizes(listing, acc),
}
}
acc(total_size);
total_size
} }
pub fn part1(input: &[u8]) -> Result<String> { pub fn part1(input: &[u8]) -> Result<String> {
let root = parse_input(input, parse_program)?; let (_, sizes) = parse_input(input, parse_program)?;
let mut searched_size = 0; let searched_size: u32 = sizes.into_iter().filter(|&size| size <= 100000).sum();
sum_sizes(&root, &mut |size| {
if size <= 100000 {
searched_size += size
}
});
Ok(searched_size.to_string()) Ok(searched_size.to_string())
} }
@@ -109,11 +98,7 @@ pub fn part2(input: &[u8]) -> Result<String> {
const TARGET: u32 = 30000000; const TARGET: u32 = 30000000;
const TOTAL: u32 = 70000000; const TOTAL: u32 = 70000000;
let root = parse_input(input, parse_program)?; let (used, sizes) = parse_input(input, parse_program)?;
let mut sizes = Vec::new();
let used = sum_sizes(&root, &mut |size| sizes.push(size));
let required = TARGET - (TOTAL - used); let required = TARGET - (TOTAL - used);