Trait constriction::Seek

source ·
pub trait Seek: PosSeek {
    // Required method
    fn seek(&mut self, pos: Self::Position) -> Result<(), ()>;
}
Expand description

A trait for entropy coders that support random access.

This is the counterpart of Pos. While Pos::pos can be used to record “snapshots” of an entropy coder, Seek::seek can be used to jump to these recorded snapshots.

Not all entropy coders that implement Pos also implement Seek. For example, DefaultAnsCoder implements Pos but it doesn’t implement Seek because it supports both encoding and decoding and therefore always operates at the head. In such a case one can usually obtain a seekable entropy coder in return for surrendering some other property. For example, DefaultAnsCoder provides the methods as_seekable_decoder and into_seekable_decoder that return a decoder which implements Seek but which can no longer be used for encoding (i.e., it doesn’t implement Encode).

Example

use constriction::stream::{
    model::DefaultContiguousCategoricalEntropyModel, stack::DefaultAnsCoder, Decode
};
use constriction::{Pos, Seek};

// Create a `AnsCoder` encoder and an entropy model:
let mut ans = DefaultAnsCoder::new();
let probabilities = vec![0.03, 0.07, 0.1, 0.1, 0.2, 0.2, 0.1, 0.15, 0.05];
let entropy_model = DefaultContiguousCategoricalEntropyModel
    ::from_floating_point_probabilities(&probabilities).unwrap();

// Encode some symbols in two chunks and take a snapshot after each chunk.
let symbols1 = vec![8, 2, 0, 7];
ans.encode_iid_symbols_reverse(&symbols1, &entropy_model).unwrap();
let snapshot1 = ans.pos();

let symbols2 = vec![3, 1, 5];
ans.encode_iid_symbols_reverse(&symbols2, &entropy_model).unwrap();
let snapshot2 = ans.pos();

// As discussed above, `DefaultAnsCoder` doesn't impl `Seek` but we can get a decoder that does:
let mut seekable_decoder = ans.as_seekable_decoder();

// `seekable_decoder` is still a `AnsCoder`, so decoding would start with the items we encoded
// last. But since it implements `Seek` we can jump ahead to our first snapshot:
seekable_decoder.seek(snapshot1);
let decoded1 = seekable_decoder
    .decode_iid_symbols(4, &entropy_model)
    .collect::<Result<Vec<_>, _>>()
    .unwrap();
assert_eq!(decoded1, symbols1);

// We've reached the end of the compressed data ...
assert!(seekable_decoder.is_empty());

// ... but we can still jump to somewhere else and continue decoding from there:
seekable_decoder.seek(snapshot2);

// Creating snapshots didn't mutate the coder, so we can just decode through `snapshot1`:
let decoded_both = seekable_decoder.decode_iid_symbols(7, &entropy_model).map(Result::unwrap);
assert!(decoded_both.eq(symbols2.into_iter().chain(symbols1)));
assert!(seekable_decoder.is_empty()); // <-- We've reached the end again.

Required Methods§

source

fn seek(&mut self, pos: Self::Position) -> Result<(), ()>

Jumps to a given position in the compressed data.

The argument pos is the same pair of values returned by Pos::pos, i.e., it is a tuple of the position in the compressed data and the State to which the entropy coder should be restored. Both values are absolute (i.e., seeking happens independently of the current state or position of the entropy coder). The position is measured in units of Words (see second example below where we manipulate a position obtained from Pos::pos in order to reflect a manual reordering of the Words in the compressed data).

Examples

The method takes the position and state as a tuple rather than as independent method arguments so that one can simply pass in the tuple obtained from Pos::pos as sketched below:

// Step 1: Obtain an encoder and encode some data (omitted for brevity) ...

// Step 2: Take a snapshot by calling `Pos::pos`:
let snapshot = encoder.pos(); // <-- Returns a tuple `(pos, state)`.

// Step 3: Encode some more data and then obtain a decoder (omitted for brevity) ...

// Step 4: Jump to snapshot by calling `Seek::seek`:
decoder.seek(snapshot); // <-- No need to deconstruct `snapshot` into `(pos, state)`.

For more fine-grained control, one may want to assemble the tuple pos manually. For example, a DefaultAnsCoder encodes data from front to back and then decodes the data in the reverse direction from back to front. Decoding from back to front may be inconvenient in some use cases, so one might prefer to instead reverse the order of the Words once encoding is finished, and then decode them in the more natural direction from front to back. Reversing the compressed data changes the position of each Word, and so any positions obtained from Pos need to be adjusted accordingly before they may be passed to seek, as in the following example:

use constriction::{
    stream::{model::LeakyQuantizer, stack::{DefaultAnsCoder, AnsCoder}, Decode},
    Pos, Seek
};

// Construct a `DefaultAnsCoder` for encoding and an entropy model:
let mut encoder = DefaultAnsCoder::new();
let quantizer = LeakyQuantizer::<_, _, u32, 24>::new(-100..=100);
let entropy_model = quantizer.quantize(probability::distribution::Gaussian::new(0.0, 10.0));

// Encode two chunks of symbols and take a snapshot in-between:
encoder.encode_iid_symbols_reverse(-100..40, &entropy_model).unwrap();
let (mut snapshot_pos, snapshot_state) = encoder.pos();
encoder.encode_iid_symbols_reverse(50..101, &entropy_model).unwrap();

// Obtain compressed data, reverse it, and create a decoder that reads it from front to back:
let mut compressed = encoder.into_compressed().unwrap();
compressed.reverse();
snapshot_pos = compressed.len() - snapshot_pos; // <-- Adjusts the snapshot position.
let mut decoder = AnsCoder::from_reversed_compressed(compressed).unwrap();

// Since we chose to encode onto a stack, decoding yields the last encoded chunk first:
assert_eq!(decoder.decode_symbol(entropy_model).unwrap(), 50);
assert_eq!(decoder.decode_symbol(entropy_model).unwrap(), 51);

// To jump to our snapshot, we have to use the adjusted `snapshot_pos`:
decoder.seek((snapshot_pos, snapshot_state));
assert!(decoder.decode_iid_symbols(140, &entropy_model).map(Result::unwrap).eq(-100..40));
assert!(decoder.is_empty()); // <-- We've reached the end of the compressed data.

Implementations on Foreign Types§

source§

impl<Array> Seek for SmallVec<Array>
where Array: Array,

source§

fn seek(&mut self, pos: usize) -> Result<(), ()>

Seeking in a SmallVec only succeeds if the provided position pos is smaller than or equal to the SmallVec’s current length. In this case, seeking will truncate the SmallVec to length pos. This is because SmallVecs, like Vecs, have Stack semantics, and the current read/write position (i.e., the head of the stack) is always at the end of the SmallVec.

If you have a Vec or SmallVec with name v and your intention is to read to or write from it at arbitrary positions rather than just at the end then you probably want to wrap either v or the slice &v[..] in a Cursor.

source§

impl<Word> Seek for Vec<Word>

source§

fn seek(&mut self, pos: usize) -> Result<(), ()>

Seeking in a Vec<Word> only succeeds if the provided position pos is smaller than or equal to the vector’s current length. In this case, seeking will truncate the vector to length pos. This is because vectors have Stack semantics, and the current read/write position (i.e., the head of the stack) is always at the end of the vector.

If you have a Vec with name v and your intention is to read to or write from it at arbitrary positions rather than just at the end then you probably want to wrap either v or the slice &v[..] in a Cursor.

Implementors§

source§

impl<B: Seek> Seek for Reverse<B>

source§

impl<Word, Buf: AsRef<[Word]>> Seek for Cursor<Word, Buf>

source§

impl<Word, State, Backend> Seek for RangeDecoder<Word, State, Backend>
where Word: BitArray + Into<State>, State: BitArray + AsPrimitive<Word>, Backend: ReadWords<Word, Queue> + Seek,

source§

impl<Word, State, Backend> Seek for AnsCoder<Word, State, Backend>
where Word: BitArray + Into<State>, State: BitArray + AsPrimitive<Word>, Backend: Seek,

source§

impl<Word, State, CompressedBackend, RemaindersBackend, const PRECISION: usize> Seek for ChainCoder<Word, State, CompressedBackend, RemaindersBackend, PRECISION>
where Word: BitArray + Into<State>, State: BitArray + AsPrimitive<Word>, CompressedBackend: Seek, RemaindersBackend: Seek,