Add a `create_snapshot` function, that creates a RocksDB database
snapshot from the given database handle and returns it.
There's a pretty fantastic amount of horribly unsafe Rust going on here
- primarily caused by the fact that the rust-rocksdb snapshot type is,
unlike Iterator or ColumnFamily or any of the other types that really
*should* be, parametrized by the lifetime of a borrowed reference to its
owner database. The abstraction provided by Rustler doesn't allow
non-static lifetime-parametrized struct types to be returned back to
Erlang as resources, for some technical type-mismatch reasons but also
practically because relying on the Erlang garbage collector to free our
stuff doesn't fit at *all* with rust's lifetime model.
The workaround here is to use `mem::transmute` (I told you it was
crazy...) to turn the snapshot reference from a `Snapshot<'a>` to a
`Snapshot<'static>`, and pass *that*, along with a held ARC reference to
the owning DB, to Erlang, and then when the Erlang GC decides it's time
to free the snapshot, go *back* to a first unbounded, then elided
lifetime to force the snapshot to be dropped.
I've done some cursory checking and regular operation (creating a
snapshot from a process then letting that process die) appears to not
leak the snapshots, but we should keep an eye on this to make sure we
don't have any leaks (or worse) in the future.
As a further note, the bit that parses out the argument for the NIFs
that now accept *either* a DB or a snapshot is a little mucky - that
should be revisited at a later date to see if we can clean it up a
little.
In addition to Rox.stream/2, add a Rox.stream_keys/2 function, which
optimizes over `stream` by avoiding decoding the values of the stream
from binary at each step of the iteration. There's a later optimization
to be had here by avoiding allocating the Erlang term *entirely*.
RocksDB supports any binary to be a key, not just UTF-8 encoded
strings. In the case of things like UUIDs, this difference can be a big
one (ie. 36 bytes vs 16 bytes).