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 Words 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 Words 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 Words 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 Words before you wrap the cursor in Reverse, and you should have initialitze the Cursor position at the beginning of the reversed sequence of Words rather than the end.

Shortcut

The method Cursor::into_reversed does everything at once:

  • it reverses the underlying sequence of Words;
  • 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 Words); and
  • it wraps the thus modified Cursor in a Reverse.
// ... 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

Returns the number of Words that are left for reading. Read more

Whether or not there is no data left to read. Read more

Returns the number of Words that are left for reading. Read more

Whether or not there is no data left to read. Read more

Returns the number of Words that one can expect to still be able to write to the data sink. Read more

Whether or not there is expected to still be some space left to write. Read more

Formats the value using the given formatter. Read more

Delegates the call to the wrapped backend and returns its result without doing any conversion. This is consistent with the implementaiton of Seek::sek for Reverse.

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

Passes pos through to the wrapped backend, i.e., doesn’t do any conversion. This is consistent with the implementation of Pos::pos for Reverse.

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

Writes a sequence of Words to the data sink, short-circuiting on error. Read more

Returns true if the data sink could be full Read more

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more

Immutably borrows from an owned value. Read more

Mutably borrows from an owned value. Read more

Performs the conversion.

Performs the conversion.

The type returned in the event of a conversion error.

Performs the conversion.

The type returned in the event of a conversion error.

Performs the conversion.