// Copyright (c) the JPEG XL Project Authors. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // // Originally written for jxl-oxide.
let alphabet_size = Self::read_u8(br)? as usize + 1; if alphabet_size > table_size { return Err(Error::InvalidAnsHistogram);
}
let base = SUM_PROBS as usize / alphabet_size; let remainder = SUM_PROBS as usize % alphabet_size;
dist[0..remainder].fill(base as u16 + 1);
dist[remainder..alphabet_size].fill(base as u16);
let bucket_size = 1u16 << log_bucket_size; letmut buckets: Vec<_> = dist
.iter()
.enumerate()
.map(|(i, &dist)| WorkingBucket {
dist,
alias_symbol: if i < alphabet_size { i as u16 } else { 0 },
alias_offset: 0,
alias_cutoff: dist,
})
.collect();
letmut underfull = Vec::new(); letmut overfull = Vec::new(); for (idx, &WorkingBucket { dist, .. }) in buckets.iter().enumerate() { match dist.cmp(&bucket_size) {
std::cmp::Ordering::Less => underfull.push(idx),
std::cmp::Ordering::Equal => {}
std::cmp::Ordering::Greater => overfull.push(idx),
}
} whilelet (Some(o), Some(u)) = (overfull.pop(), underfull.pop()) { let by = bucket_size - buckets[u].alias_cutoff;
buckets[o].alias_cutoff -= by;
buckets[u].alias_symbol = o as u16;
buckets[u].alias_offset = buckets[o].alias_cutoff; match buckets[o].alias_cutoff.cmp(&bucket_size) {
std::cmp::Ordering::Less => underfull.push(o),
std::cmp::Ordering::Equal => {}
std::cmp::Ordering::Greater => overfull.push(o),
}
}
// Assertion failure happens only if `dist` doesn't sum to `SUM_PROB`, which is checked // before building alias map.
assert!(overfull.is_empty() && underfull.is_empty());
let index = br.peek(7); let (sym, bits) = TABLE[index as usize];
br.consume(bits as usize)?;
Ok(sym as u16)
}
}
impl AnsHistogram { #[inline] pubfn read(&self, br: &mut BitReader, state: &mut u32) -> u32 { let idx = *state & 0xfff; let i = (idx >> self.log_bucket_size) as usize; let pos = idx & self.bucket_mask;
debug_assert!(self.buckets.len().is_power_of_two());
debug_assert!(
i < self.buckets.len(), "bucket index {} out of bounds (len = {})",
i, self.buckets.len()
); // SAFETY: The struct-level safety invariant (see AnsHistogram::buckets) ensures that // buckets.len() = 2^(LOG_SUM_PROBS - log_bucket_size). Since idx = state & 0xfff // (12 bits) and i = idx >> log_bucket_size, we have i < buckets.len() always. #[allow(unsafe_code)] let bucket = unsafe { *self.buckets.get_unchecked(i) }; // Safe version: (~3% slower for e2 lossless decoding) // let bucket = self.buckets[i & (self.buckets.len() - 1)]; let alias_symbol = bucket.alias_symbol as u32; let alias_cutoff = bucket.alias_cutoff as u32; let dist = bucket.dist as u32;
let map_to_alias = (pos >= alias_cutoff) as u32; let offset = (bucket.alias_offset as u32) * map_to_alias; let dist_xor = (bucket.alias_dist_xor as u32) * map_to_alias;
let dist = dist ^ dist_xor; let symbol = (alias_symbol * map_to_alias) | (i as u32 * (1 - map_to_alias)); let offset = offset + pos;
let next_state = (*state >> LOG_SUM_PROBS) * dist + offset; let select_appended = (next_state < (1 << 16)) as u32; let appended_state = (next_state << 16) | (br.peek(16) as u32);
*state = (appended_state * select_appended) | (next_state * (1 - select_appended));
br.consume_optimistic((16 * select_appended) as usize);
symbol
}
#[test] fn single_symbol() { // Single symbol of 20 letmut br = BitReader::new(&[0b00100101, 0b01]); let histogram = AnsHistogram::decode(&mut br, 5).unwrap();
validate_buckets(&histogram.buckets);
assert_eq!(histogram.buckets[20].dist, SUM_PROBS);
assert_eq!(histogram.single_symbol, Some(20));
// Single symbol of 32 (invalid) letmut br = BitReader::new(&[0b00101101, 0b000]);
assert!(AnsHistogram::decode(&mut br, 5).is_err());
}
#[test] fn two_symbols() { // two symbols of 10 and 20, where the prob of symbol 10 is 256 letmut br = BitReader::new(&[0b10011111, 0b10010010, 0b00000000, 0b00010]); let histogram = AnsHistogram::decode(&mut br, 5).unwrap();
validate_buckets(&histogram.buckets);
assert_eq!(histogram.buckets[10].dist, 256);
assert_eq!(histogram.buckets[20].dist, SUM_PROBS - 256);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.