Breaking fix: In packed fixed fields, replace Cow<'a, T> with a similar custom struct PackedFixed<'a, T>, which exposes alignment-safe reading methods.
Other fields will still use Cow<'a, T>.
PackedFixed
Like Cow, PackedFixed is an enum. It has the following variants:
PackedFixed::Borrowed: Contains a reference to a (possibly) unaligned byte slice, and reads or iterates on the fly using read_unaligned(). This is the heart of this enum's purpose.
PackedFixed::Owned: Contains an owned vector of the target encoded type. This is mainly for users to create their own owned PackedFixed instances without having to create a vector and reference it separately.
PackedFixed::NoDataYet: Default state for initialization.
Optimization?
There is also the fact that PackedFixed::Owned will always contain an aligned vector while PackedFixed::Borrowed contains a reference to bytes that might be unaligned, and so every read must go through read_unaligned().
In fact, the original names for the variants were PackedFixed::Aligned and PackedFixed::MaybeUnaligned instead of PackedFixed::Owned and PackedFixed::Borrowed.
From my own testing attempts, I can't really tell whether there are speed/compiler optimizations when reading from PackedFixed::Owned instead of PackedFixed::Borrowed. There does appear to be some rather big slowdowns (about 3x) of reads from PackedFixed::Borrowed depending on where that code is placed (a rather arbitrary factor), but I can't tell if this is an artifact of my own testing or not. However, methods to convert PackedFixed::Borrowed to PackedFixed::Owned are provided.
Iterators
T refers to the data type that is encoded by the raw bytes.
There are two iterators provided:
PackedFixedIntoIter: Regular iterator that will move self, and returns T at each iteration.
PackedFixedRefIter: Exactly the same as PackedFixedIntoIter (returns T as well), but works on &PackedFixed and therefore does not move self. It does not return &T despite iterating over &PackedFixed (see section Returning Referencesbelow); this is why we do not follow the convention of naming it PackedFixedIter.
Returning References
Because read_unaligned() does a bitwise copy and returns a new value, the PackedFixed::Borrowed variant cannot return references to the data that is encoded by its referenced bytes. It might technically be possible to implement methods that do for the enum in general (either panic when called on PackedFixed::Borrowed variant or convert to PackedFixed::Owned first) but I thought this would be rather confusing.
Problem: UB in
read_packed_fixed()
Closes #215 (Thanks @saethlin for finding this)
Solution
Breaking fix: In packed fixed fields, replace
Cow<'a, T>
with a similar custom structPackedFixed<'a, T>
, which exposes alignment-safe reading methods.Other fields will still use
Cow<'a, T>
.PackedFixed
Like
Cow
,PackedFixed
is an enum. It has the following variants:PackedFixed::Borrowed
: Contains a reference to a (possibly) unaligned byte slice, and reads or iterates on the fly usingread_unaligned()
. This is the heart of this enum's purpose.PackedFixed::Owned
: Contains an owned vector of the target encoded type. This is mainly for users to create their own ownedPackedFixed
instances without having to create a vector and reference it separately.PackedFixed::NoDataYet
: Default state for initialization.Optimization?
There is also the fact that
PackedFixed::Owned
will always contain an aligned vector whilePackedFixed::Borrowed
contains a reference to bytes that might be unaligned, and so every read must go throughread_unaligned()
.From my own testing attempts, I can't really tell whether there are speed/compiler optimizations when reading from
PackedFixed::Owned
instead ofPackedFixed::Borrowed
. There does appear to be some rather big slowdowns (about 3x) of reads fromPackedFixed::Borrowed
depending on where that code is placed (a rather arbitrary factor), but I can't tell if this is an artifact of my own testing or not. However, methods to convertPackedFixed::Borrowed
toPackedFixed::Owned
are provided.Iterators
There are two iterators provided:
PackedFixedIntoIter
: Regular iterator that will moveself
, and returnsT
at each iteration.PackedFixedRefIter
: Exactly the same asPackedFixedIntoIter
(returnsT
as well), but works on&PackedFixed
and therefore does not moveself
. It does not return&T
despite iterating over&PackedFixed
(see section Returning Referencesbelow); this is why we do not follow the convention of naming itPackedFixedIter
.Returning References
Because
read_unaligned()
does a bitwise copy and returns a new value, thePackedFixed::Borrowed
variant cannot return references to the data that is encoded by its referenced bytes. It might technically be possible to implement methods that do for the enum in general (either panic when called onPackedFixed::Borrowed
variant or convert toPackedFixed::Owned
first) but I thought this would be rather confusing.