I realized that the C version of this thing does multiple things
in the same function: it loads the bigrams, it iterates through
the database, and it compares the things found in the database
to the prepared pattern. It seems to me, therefore, that we're
better off with an instance that loads the bigrams, then closes
the database immediately.
Later, the client can ask for one of two iterators: one that either
returns each entry in sequence, or one that returns each entry in sequence
that matches the pattern passed in.
This just removes the layer between `prepare_pattern` and
`prepare_pattern_raw`; the function now always returns the
allocated vector.
Oddly, it wasn't possible to encode this using an Option<> in the
`hunt()` function. The `usize` of the scan variable meant that we'd
never go below zero legally (and Rust wouldn't let that happen), so
the "if we're at zero we have some special cases to check" had to
remain here. The C version of this code could say "If this pointer
is below the allocated space" which is, to a Rust developer, hella
weird (you're literally pointed at memory you don't own!).
And despite the allocation, despite the special case checks, this code
is *still* twice as fast as its C implementation.
Added the squozen patprep function, added unit tests to the
patprep `c` code, and ensured that the rust version works the
same way. The only remaining code slowdown is that re-allocating
the Vec 50 million times turns out to be slower than re-using the
same slice of RAM over and over and over.