Rather than truncating the undo stack when making an edit after undoing,
the undo steps themselves are pushed onto the undo stack before the
edit. This means that the undo stack never loses any previous states
of the document, and those states are always accessible by just undoing
enough.
Also use random constants for the initial hash state. Changed my
mind about using zero initialization. It doesn't make a practical
difference, but helps esnure that the mixing is randomized.
When incorporating a data block into the hash, it now does enough
mixing rounds to flip on average about 110 bits for any bit flipped
by the data block.
This reduces performance again, but not all the way to what they
were before. It's still reasonably fast, hashing at around 6-7 GB/s.
Originally I was using just `(usize, usize)` for marks, and `Vec`s
of those for sets. But that was already becoming unwieldy even for
really basic code. This abstracts just a handful of common
operations away to make this easier to reason about. However,
the internals are (intentionally) left exposed to allow other fiddly
things to be done.
This is very WIP. The intent is for these to be used for e.g.
cursors, screen positions, etc. The intent is for the buffer
itself to keep them synced properly with the actual text on edits,
although this is not yet implemented.