Struct constriction::backends::Reverse [−][src]
pub struct Reverse<Backend>(pub Backend);
Expand description
Wrapper that inverts the read/write directions of a data source and/or data sink.
Motivation
This wrapper is usually used for a Cursor
. The Cursor
type implements
ReadWords<Word, S>
for both semantics S = Queue
and S = Stack
, so you can read
Word
s from a cursor in either forward or backward direction.
Reading from a Cursor
with Queue
semantics reads from the underlying slice [Word]
in the normal direction (from index 0
to index .len() - 1
), which is useful for
decoding data from an entropy coder that has queue semantics (like RangeEncoder
). By
contrast, reading from a Cursor
with Stack
semantics reads in the reverse direction.
This is usually a good thing: it is consistent with how Vec<Word>
(necessarily)
implements reading with Stack
semantics, so if you have a Vec<Word>
of data that was
compressed with a stack entropy coder (like AnsCoder
) then you are free to choose
whether you want to decode the data either directly from the Vec
or from a Cursor
that wraps the Vec
(if you don’t want to consume the compressed data). Both approaches
will achieve the same result, as you can see in the following example:
use constriction::{
backends::Cursor, stream::{model::DefaultLeakyQuantizer, stack::DefaultAnsCoder, Decode},
UnwrapInfallible,
};
// Some simple entropy model, just for demonstration purpose.
let quantizer = DefaultLeakyQuantizer::new(-100..=100);
let model = quantizer.quantize(probability::distribution::Gaussian::new(25.0, 10.0));
// Encode the symbols `0..50` using a stack entropy coder and get the compressed data.
let mut coder = DefaultAnsCoder::new();
coder.encode_iid_symbols_reverse(0..50, &model).unwrap();
let compressed = coder.into_compressed().unwrap_infallible(); // `compressed` is a `Vec<u32>`.
dbg!(compressed.len()); // Prints "compressed.len() = 11".
// You can either decode directly from the `Vec` (could also just have used `coder` for that).
let mut c2 = DefaultAnsCoder::from_compressed(compressed.clone()).unwrap();
assert!(c2.decode_iid_symbols(50, &model).map(UnwrapInfallible::unwrap_infallible).eq(0..50));
// Or you can wrap the slice `[u32]` in a `Cursor` (could have used `coder.as_decoder()`).
let borrowing_cursor = Cursor::new_at_write_end(&compressed[..]);
let mut c3 = DefaultAnsCoder::from_compressed(borrowing_cursor).unwrap();
assert!(c3.decode_iid_symbols(50, &model).map(UnwrapInfallible::unwrap_infallible).eq(0..50));
// You can also wrap the `Vec<u32>` in a `Cursor` (could have used `coder.into_decoder()`).
let owning_cursor = Cursor::new_at_write_end(compressed);
let mut c4 = DefaultAnsCoder::from_compressed(owning_cursor).unwrap();
assert!(c4.decode_iid_symbols(50, &model).map(UnwrapInfallible::unwrap_infallible).eq(0..50));
However, it can sometimes be awkward to read data in reverse direction, e.g., if you’re
reading it from a file or socket. In these situations you may prefer to reverse the
entire sequence of Word
s after encoding so that the decoder can read them in forward
direction. You can then still wrap the reversed sequence of Words
in a Cursor
, but
if you pass this cursor to an AnsCoder
then the AnsCoder
has to know somehow that
the top of this stack is at the beginning of the slice, i.e., that reading with Stack
semantics means reading in the forward direction. To express this, wrap the Cursor
in
a Reverse
, otherwise you’ll get garbled data:
use constriction::backends::Reverse;
// ... obtain same `model` and `compressed` as in the above example ...
compressed.reverse(); // Reverses the sequence of `u32` words in place (mutates `compressed`).
// Naively decoding the reversed compressed data leads to garbled symbols.
let wrong_cursor = Cursor::new_at_write_end(&compressed[..]);
let mut c5 = DefaultAnsCoder::from_compressed(wrong_cursor).unwrap();
let bug = c5.decode_iid_symbols(50, &model).collect::<Result<Vec<_>, _>>().unwrap_infallible();
dbg!(bug); // Prints "bug = [39, 47, 40, ...]", not what we had encoded.
// We must set the initial cursor position "at_write_beginning" and wrap it in a `Reverse`.
let reversed_cursor = Reverse(Cursor::new_at_write_beginning(&compressed[..]));
let mut c6 = DefaultAnsCoder::from_compressed(reversed_cursor).unwrap();
assert!(c6.decode_iid_symbols(50, &model).map(UnwrapInfallible::unwrap_infallible).eq(0..50));
Wrapping a Cursor
(or any ReadWords
) in a Reverse
with the statement let reversed_cursor = Reverse(original_cursor);
is a no-op. It changes neither the order of
the underlying sequence of Word
s nor the current cursor position, but it will reverse
the direction in which the cursor position moves when you read data from
reversed_cursor
. Therefore you should already have reversed the the underlying
sequence of Word
s before you wrap the cursor in Reverse
, and you should have
initialitze the Cursor
position at the beginning of the reversed sequence of Word
s
rather than the end.
Shortcut
The method Cursor::into_reversed
does everything at once:
- it reverses the underlying sequence of
Word
s; - it moves the current read position to its mirrored position (thus following where the
value that it originally pointed to moved upon reversing the sequence of
Word
s); and - it wraps the thus modified
Cursor
in aReverse
.
// ... obtain same `model` and `owning_cursor` as in the `c4` example above ...
// (could also use a mutably borrowing cursor instead)
let mut c7 = DefaultAnsCoder::from_compressed(owning_cursor.into_reversed()).unwrap();
assert!(c7.decode_iid_symbols(50, &model).map(UnwrapInfallible::unwrap_infallible).eq(0..50));
Tuple Fields
0: Backend
Implementations
Reverses both the data and the reading direction.
This is essentially the same as Cursor::into_reversed
, except that, rather than
wrapping yet another Reverse
around the Cursor
, the last step of this method
just removes the existing Reverse
wrapper, which has the same effect.
See documentation of Reverse
for more information and a usage example.
Trait Implementations
The error type that can occur when reading from the data source, or Infallible
. Read more
Reads a single Word
from the data source and advances the state of the data source
accordingly (i.e., so that the next read
won’t read the same Word
again). Read more
Returns true
if the data source could be out of data. Read more
The error type that can occur when reading from the data source, or Infallible
. Read more
Reads a single Word
from the data source and advances the state of the data source
accordingly (i.e., so that the next read
won’t read the same Word
again). Read more
Returns true
if the data source could be out of data. Read more
type WriteError = BoundedWriteError
type WriteError = BoundedWriteError
The error type that can occur when writing to the data sink, or Infallible
. Read more
Writes a single Word
to the data sink and advances the state of the data sink
accordingly (i.e., so that the next write
won’t overwrite the current Word
). Read more
fn extend_from_iter(
&mut self,
iter: impl Iterator<Item = Word>
) -> Result<(), Self::WriteError>
fn extend_from_iter(
&mut self,
iter: impl Iterator<Item = Word>
) -> Result<(), Self::WriteError>
Writes a sequence of Word
s to the data sink, short-circuiting on error. Read more
Returns true
if the data sink could be full Read more