Trait ord_lmdb_zero::traits::LmdbRaw
source · [−]pub unsafe trait LmdbRaw: Copy + Sized {
fn reported_type() -> String { ... }
}
Expand description
Marker trait indicating a value is to be stored in LMDB by simply copying it in.
This trait implies that one wants integers and such in native byte order and doesn’t care about inter-ABI portability of the values. There are a lot of safety implications as well.
Implementing this trait provides blanket implementations of
AsLmdbBytes
, FromLmdbBytes
, and FromReservedLmdbBytes
.
See also LmdbRawIfUnaligned
for types
that become LmdbRaw
when wrapped with
Unaligned
. In particular, all integer and
floating-point types are LmdbRawIfUnaligned
, except for u8
and i8
which are also LmdbRaw
.
Alignment
The FromLmdbBytes
conversion fails if the alignment of the input data
does not satisfy the alignment of the type. This means that behaviour will
be unpredictable if the required alignment of the struct is greater than 1,
as conversions will pass or fail depending on where LMDB decides to place
the value.
If you run into this issue, there are several ways to work around it.
Use Unaligned
Instead of directly reading and writing the bare type, wrap it in
lmdb_zero::Unaligned
. This adds no overhead in and of itself and removes
the alignment restriction, but heavily restricts what can be done with a
reference without copying it.
This is almost always the best option if your type fits in a register or two.
Make your structure #[repr(C, packed)]
If this is a problem, you can make your structure #[repr(packed)]
to give
it an alignment of 1 (but see also below about padding).
Note that it is possible to produce unsafe code using this approach even
without the use of unsafe
. See this rust
bug.
Do it yourself
If you have unusual requirements, your best bet is to implement
FromLmdbBytes
and friends manually as needed.
Unsafety
If the tagged type contains pointers of any kind, they will be stored in
and retrieved from the database, which has serious ramifications,
especially when the FromReservedLmdbBytes
implementation is used. If the
type contains Rust references, this will almost certainly lead to undefined
behaviour.
Behaviour is undefined if there exist bit patterns of the same size of the
type which are not valid instances of that type unless the client code can
somehow guarantee that such bit patterns do not occur. If this is a
problem, implement AsLmdbBytes
and FromLmdbBytes
manually and check for
validity. Of particular note, bool
and essentially all enum
types are
not sensible for use with LmdbRaw
(directly or within composites) because
they are only valid for particular bit patterns.
Warnings about inner padding
Use of this trait on a struct that is not #[repr(packed)]
makes it
possible to observe the normally unobservable padding bytes inserted into
the structure to satisfy type alignment.
When simply using these structures as values in a non-DUPSORT
database,
this simply means some arbitrary extra bytes get written with the values.
This is not going to be a problem unless you plan on sharing the databases
with untrusted third parties (which could leak sensitive information) or do
unusual things with type punning.
However, in any context where values need to be compared (ie, keys, and
values in DUPSORT
databases), these padding bytes now count towards the
comparison by default. Since the padding contains unpredictable values, you
can easily end up with multiple “identical” keys that differ in their
padding bytes, fail to find values you know are in the database because of
differences in padding values, etc.
One way to deal with both problems is to use #[repr(packed)]
(in addition
to #[repr(C)]
which keeps the field order defined across Rust versions),
which simply eliminates the padding bytes altogether. Note that due to a
bug in the Rust compiler, packed structures can lead to undefined
behaviour in “safe Rust”.
Until that issue is fixed, you should be careful about using
#[repr(packed)]
for this purpose unless all fields in the struct have the
same size or you understand the ABI(s) you care about well enough to know
whether misalignment will cause issues.
You can alternatively opt to live with the padding bytes, but additionally
implement LmdbOrdKey
on the type, and then use
DatabaseOptions::sort_keys_as
or DatabaseOptions::sort_values_as
appropriately to use the generated comparison function. As a result, the
padding bytes will be ignored for comparison purposes, but will nontheless
be written into the database and thus remain visible to puns and could leak
information.
Example
use lmdb_zero::traits::*;
#[repr(C)]
#[derive(Clone,Copy,Debug)]
struct MyStruct {
foo: i16,
// See warning about alignment/padding above!
// On AMD64, for example, we get 6 padding bytes here.
bar: u64,
}
unsafe impl LmdbRaw for MyStruct { }
Provided methods
fn reported_type() -> String
fn reported_type() -> String
Returns the name of this type to report in error messages.
If not implemented, defaults to "?"
.