Function parse

Source
pub fn parse<T: Clone, E: Into<Box<dyn Error>>>(
    input: &str,
    padding: usize,
    default_value: T,
    hot_map: impl Fn(u8) -> T,
    hot_valid: impl Fn(u8) -> bool,
    slow_map: impl FnMut(usize, u8) -> Result<T, E>,
) -> Result<Grid<T>, InputError>
Expand description

Parse a 2D grid.

This function assumes that each byte represents one item in the grid. Using 1 byte wide output types is recommended to enable more efficient vectorization.

Parsing is done in two passes per line:

  1. A “hot” pass, where each byte is mapped to an output value and checked for validity. This uses the hot_map and hot_valid functions, which should be pure and total mappings from u8 to their respective outputs to enable vectorization. Additionally, hot_valid must return false for both '\r' and '\n'.

  2. A “slow” pass, where any invalid bytes from the first pass are re-processed with their index in the final grid. This uses the slow_map function which returns a Result, containing either a mapped value or an error. slow_map can be used to perform more complex, non-pure, mappings such as storing positions and will never be called for newlines or bytes that were valid in the first pass. If all the bytes were valid in the first pass, this pass is skipped.

default_value is used to initialize the grid before parsing and for any padding. A value with an all-zero bit pattern will usually faster to initialize and is recommended when padding is not used.

Returns (number of rows, number of columns, data) on success.

§Examples

assert_eq!(
    grid::parse(
        /* input         */ "##.#\n#..#\n#.##",
        /* padding       */ 0,
        /* default_value */ false,
        /* hot_map       */ |b| b == b'#',
        /* hot_valid     */ |b| matches!(b, b'.' | b'#'),
        /* slow_map      */ |_, _| Err("expected '.' or '#'"),
    ).unwrap(),
    (3, 4, vec![
        true, true, false, true,
        true, false, false, true,
        true, false, true, true,
    ]),
);
assert_eq!(
    grid::parse(
        /* input         */"##.#\n#..#\n#.##",
        /* padding       */ 2,
        /* default_value */ false,
        /* hot_map       */ |b| b == b'#',
        /* hot_valid     */ |b| matches!(b, b'.' | b'#'),
        /* slow_map      */ |_, _| Err("expected '.' or '#'"),
    ).unwrap(),
    (7, 8, vec![
        false, false, false, false, false, false, false, false,
        false, false, false, false, false, false, false, false,
        false, false, true, true, false, true, false, false,
        false, false, true, false, false, true, false, false,
        false, false, true, false, true, true, false, false,
        false, false, false, false, false, false, false, false,
        false, false, false, false, false, false, false, false,
    ]),
);
let mut start = None;
assert_eq!(
    grid::parse(
        /* input         */ ".0.#S\r\n..1..\r\n.###2\r\n.3...",
        /* padding       */ 1,
        /* default_value */ b'#',
        /* hot_map       */ |b| b,
        /* hot_valid     */ |b| matches!(b, b'.' | b'#' | b'0'..=b'9'),
        /* slow_map      */ |i, b| {
            match b {
                b'S' if start.is_none() => {
                    start = Some(i);
                    Ok(b'.')
                },
                b'S' => Err("expected only one 'S'"),
                _ => Err("expected '.', '#', 'S' or a digit")
            }
        },
    ).unwrap(),
    (6, 7, vec![
        b'#', b'#', b'#', b'#', b'#', b'#', b'#',
        b'#', b'.', b'0', b'.', b'#', b'.', b'#',
        b'#', b'.', b'.', b'1', b'.', b'.', b'#',
        b'#', b'.', b'#', b'#', b'#', b'2', b'#',
        b'#', b'.', b'3', b'.', b'.', b'.', b'#',
        b'#', b'#', b'#', b'#', b'#', b'#', b'#',
    ]),
);
assert_eq!(start, Some(12));