blob: 8b937764c271fb3382969ecbda91eb3630fb0186 [file] [log] [blame]
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// -----------------------------------------------------------------------------
//
// Asymmetric Numeral System helper functions.
//
// Author: Skal (pascal.massimino@gmail.com)
#ifndef WP2_UTILS_ANS_UTILS_H_
#define WP2_UTILS_ANS_UTILS_H_
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <limits>
#include "src/dsp/math.h"
#include "src/utils/ans.h"
#include "src/utils/ans_enc.h"
#include "src/wp2/base.h"
namespace WP2 {
//------------------------------------------------------------------------------
// ANS quantizations.
// Scale the counts to fit in max_bits and replace them with the nearest powers
// of 2. 'cost' is set to the cost of encoding the original counts using the
// quantized counts.
void ANSCountsQuantizeHuffman(uint32_t size, const uint32_t* counts,
uint32_t* out, uint32_t max_bits, float* cost);
// Quantize the counts to be at most max_freq. Non-null values stay non-null.
// Returns true if the array is modified
// If do_expand is set to true, the counts can increase if the current
// max is inferior to max_freq.
// 'cost' is the cost of storing the data with quantized probabilities. A
// nullptr can be passed.
// The function exists as inplace or with a pre-allocated 'out' buffer.
bool ANSCountsQuantize(bool do_expand, uint32_t max_freq, uint32_t size,
uint32_t* counts, float* cost);
bool ANSCountsQuantize(bool do_expand, uint32_t max_freq, uint32_t size,
const uint32_t* counts, uint32_t* out, float* cost);
// Outputs the bit cost of a histogram.
float ANSCountsCost(const uint32_t* counts, uint32_t size);
//------------------------------------------------------------------------------
// ANS vector storage.
// Helper function for the function below.
struct OptimizeArrayStorageStat {
uint32_t count;
uint8_t n_bits;
};
// define to have a fast, but slightly less efficient OptimizeArrayStorage.
#define OPTIMIZE_ARRAY_STORAGE 1
void OptimizeArrayStorage(OptimizeArrayStorageStat* stats, size_t* size,
float overhead);
// Return the index of the highest set bit.
inline uint8_t FindLastSet(uint32_t val) {
return (val == 0) ? 0 : (1 + WP2Log2Floor(val));
}
// Forward declaration.
template <typename T, bool USE_NNZ>
float StoreVectorImpl(const T* vals, uint32_t size, uint32_t nnz,
uint32_t val_upper, OptimizeArrayStorageStat* stats,
ANSEncBase* enc);
// This function efficiently stores a vector of values by splitting it into
// groups of numbers of similar bit size.
// val_upper is an upper bound (potentially the max) of the input values.
// The size of the array and this 'val_upper' must be stored independently.
// If no ANS encoder is provided (enc == nullptr), the function can still be
// used to estimate the resulting size in bits (and it is faster than calling
// the function with an ANSEncCounter). It stores the array with tuples (number
// of bits, number of numbers with that bit-size, a list of numbers).
// 'stats' is a pre-allocated buffer of the same size that will be used for
// temporary computation.
template <typename T>
float StoreVector(const T* const vals, uint32_t size, uint32_t val_upper,
OptimizeArrayStorageStat* stats, ANSEncBase* enc) {
return StoreVectorImpl<T, false>(vals, size,
/*nnz=*/std::numeric_limits<uint32_t>::max(),
val_upper, stats, enc);
}
// Same as StoreVector except that we know the number 'nnz' of
// non-zero values as well as the fact that the last element (if size>0) is
// non-zero.
template <typename T>
float StoreVectorNnz(const T* const vals, uint32_t size, uint32_t nnz,
uint32_t val_upper, OptimizeArrayStorageStat* stats,
ANSEncBase* enc) {
// Check the number of non-zero elements is correct.
assert(std::count(vals, vals + size, 0) + nnz == size);
assert(size == 0 || vals[size - 1] != 0);
return StoreVectorImpl<T, true>(vals, size, nnz, val_upper, stats, enc);
}
// Implementation of StoreVector and StoreVectorNnz.
// If USE_NNZ is false the number of non-zero elements is not used.
template <typename T, bool USE_NNZ>
float StoreVectorImpl(const T* const vals, uint32_t size, uint32_t nnz,
uint32_t val_upper, OptimizeArrayStorageStat* stats,
ANSEncBase* enc) {
if (size == 0 || val_upper == 0 || (USE_NNZ && nnz == 0)) return 0.f;
WP2MathInit(); // Initialization for WP2Log2.
const uint8_t n_bits_max = FindLastSet(val_upper);
const float val_upper_log2 = WP2Log2(val_upper);
const float val_upper_plus_one_log2 = WP2Log2(val_upper + 1);
auto add_value = [enc, val_upper, n_bits_max](T val, uint32_t min) {
if (val_upper <= kANSMaxRange) {
enc->PutRange(val, min, val_upper, "value");
} else {
enc->PutUValue(val, n_bits_max, "value");
}
};
auto add_value_cost = [val_upper, val_upper_log2, val_upper_plus_one_log2,
n_bits_max](uint32_t min) -> float {
if (val_upper <= kANSMaxRange) {
if (min == 0) return val_upper_plus_one_log2;
assert(min == 1);
return val_upper_log2;
} else {
return n_bits_max;
}
};
if (USE_NNZ && nnz == 1) {
// The last value is the non-zero one.
add_value(vals[size - 1], 1);
return add_value_cost(/*min=*/1);
}
if (size <= 2) {
// For 2 values, storing as ranges is usually more efficient because of the
// overhead that describes the data.
if (USE_NNZ) {
uint32_t nnz_left = nnz;
float cost = 0.f;
for (uint32_t i = 0; i < size && nnz_left > 0; ++i) {
const uint32_t min = i + nnz_left == size ? 1 : 0;
if (enc != nullptr) add_value(vals[i], min);
cost += add_value_cost(min);
if (vals[i] != 0) --nnz_left;
}
return cost;
} else {
if (enc != nullptr) {
for (uint32_t i = 0; i < size; ++i) add_value(vals[i], 0);
}
return size * add_value_cost(/*min=*/0);
}
}
// When USE_NNZ, there are two optimizations that can be done.
// - all the last values that are > 0 can be encoded -1. index_subtract_one is
// the index when such values start.
// - if the last value is preceded by a 0, the previous 0s do not need to be
// encoded and can be skipped starting at index index_skip_zeros.
uint32_t index_subtract_one, index_skip_zeros;
if (USE_NNZ) {
for (index_subtract_one = size - 1; index_subtract_one > 0;
--index_subtract_one) {
if (vals[index_subtract_one - 1] == 0) break;
}
if (index_subtract_one + 1 == size) {
// The only way for index_subtract_one to be 0 here is to have
// a single value, because otherwise either
// - the last value is not preceded by a 0, so index_subtract_one+1!=size,
// or
// - the last value is preceded by a 0, so index_subtract_one>0.
// And the single value case is handled above as an early exit.
assert(index_subtract_one > 0);
for (index_skip_zeros = index_subtract_one - 1; index_skip_zeros > 0;
--index_skip_zeros) {
if (vals[index_skip_zeros - 1] != 0) break;
}
} else {
index_skip_zeros = size;
}
} else {
index_subtract_one = index_skip_zeros = size;
}
// Stats contains a list of pairs: number of bits, numbers of consecutive
// values with that bit depth.
size_t stats_size = 0;
{
T val_max = 0, val_min = 0;
size_t i;
for (i = 0; i < index_subtract_one && i < index_skip_zeros; ++i) {
const T val = vals[i];
assert(val <= val_upper);
// Check if the highest set bit is the same (faster than calling
// FindLastSet).
if (val >= val_min && val < val_max) {
++stats[stats_size - 1].count;
} else {
stats[stats_size].count = 1;
stats[stats_size].n_bits = FindLastSet(val);
val_max = (1u << stats[stats_size].n_bits);
val_min = (val_max >> 1);
++stats_size;
}
}
if (USE_NNZ && i == index_skip_zeros) {
// In the case where the last non-zero value is by itself, merge the
// previous 0s in the previous stat.
stats[stats_size - 1].count += (size - 1 - index_skip_zeros);
i = size - 1;
}
// If only non-zero elements remain, store them with -1.
for (; i < size; ++i) {
const T val = vals[i] - 1;
assert(val <= val_upper);
// Check if the highest set bit is the same (faster than calling
// FindLastSet).
if (val >= val_min && val < val_max) {
++stats[stats_size - 1].count;
} else {
stats[stats_size].count = 1;
stats[stats_size].n_bits = FindLastSet(val);
val_max = (1u << stats[stats_size].n_bits);
val_min = (val_max >> 1);
++stats_size;
}
}
}
// Merge the pairs to give an optimal cost.
const float cost0 = WP2Log2Fast(n_bits_max);
const float cost1 = WP2Log2Fast(n_bits_max + 1);
OptimizeArrayStorage(stats, &stats_size, cost0 + WP2Log2(size + 1));
// Figure out the cost of this storage. This is a simplified version of the
// code below where 'enc' is replaced by 'cost'. Comments have been removed
// for simplification.
const T* v = vals;
float cost = cost1;
size_t size_left = size;
uint8_t n_bits_max_in_val = 0;
for (size_t i = 0; i < stats_size; ++i) {
if (USE_NNZ && v >= vals + index_skip_zeros) {
i = stats_size - 1;
v = vals + index_subtract_one;
size_left = 1;
}
const OptimizeArrayStorageStat& s = stats[i];
assert(s.count > 0);
if (i > 0) cost += cost0;
cost += WP2Log2(size_left);
const bool use_r_value =
(s.n_bits == n_bits_max) && (n_bits_max <= kANSMaxRangeBits);
if (s.n_bits == 0) {
v += s.count;
} else if (s.count == 1) {
if (s.n_bits > 1) {
if (use_r_value) {
const uint32_t high_order_bit = 1u << (s.n_bits - 1);
const bool subtract_one = (USE_NNZ && v >= vals + index_subtract_one);
cost += WP2Log2(subtract_one ? val_upper - high_order_bit
: val_upper + 1 - high_order_bit);
} else {
cost += s.n_bits - 1;
}
}
++v;
} else {
const T* const v_end_count = v + s.count;
if (USE_NNZ) {
const T* v_end = std::min(
{v_end_count, vals + index_subtract_one, vals + index_skip_zeros});
cost +=
(v_end - v) * (use_r_value ? val_upper_plus_one_log2 : s.n_bits);
v = v_end;
if (v == vals + index_skip_zeros) v = vals + index_subtract_one;
if (v < v_end_count) {
cost += (v_end_count - v) * (use_r_value ? val_upper_log2 : s.n_bits);
}
} else {
cost += s.count * (use_r_value ? val_upper_plus_one_log2 : s.n_bits);
}
v = v_end_count;
}
size_left -= s.count;
n_bits_max_in_val = std::max(n_bits_max_in_val, s.n_bits);
}
// Compute the cost if all values are set to the same bit depth and choose it
// accordingly. This is a simplified version of the code below where 'enc' is
// replaced by 'cost', with only one statistic with count == size and n_bits
// == n_bits_max_in_val.
float cost_same_depth = cost1;
{
// Whether to write the values as RValue or UValue.
const bool use_r_value =
(n_bits_max_in_val == n_bits_max) && (n_bits_max <= kANSMaxRangeBits);
if (USE_NNZ) {
cost_same_depth +=
std::min({size, index_subtract_one, index_skip_zeros}) *
(use_r_value ? val_upper_plus_one_log2 : n_bits_max_in_val);
cost_same_depth += (size - index_subtract_one) *
(use_r_value ? val_upper_log2 : n_bits_max_in_val);
} else {
cost_same_depth +=
size * (use_r_value ? val_upper_plus_one_log2 : n_bits_max_in_val);
}
}
const bool have_same_depth = (cost_same_depth < cost);
if (have_same_depth) {
cost = cost_same_depth;
stats_size = 1;
stats->count = size;
stats->n_bits = n_bits_max_in_val;
}
// Add the cost for the same depth bit.
cost += 1;
if (enc == nullptr) return cost;
enc->PutBool(have_same_depth, "has_same_depth");
// Store the optimized version of the array.
v = vals;
uint8_t n_bits_prev = 0;
size_left = size;
for (size_t i = 0; i < stats_size; ++i) {
if (USE_NNZ && v >= vals + index_skip_zeros) {
// Jump to encoding the last non-zero value.
i = stats_size - 1;
v = vals + index_subtract_one;
size_left = 1;
}
const OptimizeArrayStorageStat& s = stats[i];
// The number of bits is in [0:n_bits_max) for the first set.
if (v == vals) {
enc->PutRValue(s.n_bits, n_bits_max + 1, "n_bits");
} else {
// If we store n_bits0 for a set, the following n_bits1 will be stored as:
// n_bits1 if n_bits1 < n_bits0,
// n_bits1 - 1 otherwise.
if (s.n_bits < n_bits_prev) {
enc->PutRValue(s.n_bits, n_bits_max, "n_bits");
} else {
enc->PutRValue(s.n_bits - 1, n_bits_max, "n_bits");
}
}
n_bits_prev = s.n_bits;
// Store the number of values.
if (!have_same_depth) enc->PutRange(s.count, 1, size_left, "count");
// Whether to write the values as RValue or UValue.
const bool use_r_value =
(s.n_bits == n_bits_max) && (n_bits_max <= kANSMaxRangeBits);
size_left -= s.count;
if (s.n_bits == 0) {
v += s.count;
} else if (s.count == 1) {
// If we only have one number, we don't need to store the high order
// bit as it's always 1, otherwise it would fit in (n_bits_ - 1).
// And if that number is only on one bit to begin with, we don't need
// to store anything.
if (s.n_bits > 1) {
const uint32_t high_order_bit = 1u << (s.n_bits - 1);
const bool subtract_one = (USE_NNZ && v >= vals + index_subtract_one);
const uint32_t value_to_code =
(subtract_one ? *v - 1 : *v) ^ high_order_bit;
if (use_r_value) {
enc->PutRValue(value_to_code,
subtract_one ? val_upper - high_order_bit
: val_upper + 1 - high_order_bit,
"value");
} else {
enc->PutUValue(value_to_code, s.n_bits - 1, "value");
}
}
++v;
} else {
// Figure out the end of the segment.
const T* const v_end_count = v + s.count;
const T* v_end = v_end_count;
if (USE_NNZ) {
if (vals + index_subtract_one < v_end) {
v_end = vals + index_subtract_one;
}
if (vals + index_skip_zeros < v_end) {
v_end = vals + index_skip_zeros;
}
}
for (; v < v_end; ++v) {
if (use_r_value) {
enc->PutRValue(*v, val_upper + 1, "value");
} else {
enc->PutUValue(*v, s.n_bits, "value");
}
}
if (USE_NNZ) {
if (v == vals + index_skip_zeros) {
// Skip the last zeros.
v = vals + index_subtract_one;
assert(v <= v_end_count);
}
// The last elements can be encoded minus 1.
for (; v < v_end_count; ++v) {
if (use_r_value) {
enc->PutRValue(*v - 1, val_upper, "value");
} else {
enc->PutUValue(*v - 1, s.n_bits, "value");
}
}
}
}
}
assert(size_left == 0);
return cost;
}
// Forward declaration.
template <typename T, bool USE_NNZ>
void LoadVectorImpl(ANSDec* dec, size_t nnz, uint32_t val_upper, T& v);
// Reciprocal of the function above to read a stored vector.
// 'val_upper' is the *inclusive* upper bound expected for the vector's values.
// The vector must be pre-allocated (with length 'size').
template <typename T>
void LoadVector(ANSDec* dec, uint32_t val_upper, T& v) {
LoadVectorImpl<T, false>(dec, /*nnz=*/std::numeric_limits<uint32_t>::max(),
val_upper, v);
}
// Same as above except we are also given the maximum number of non-zero values
// and we know the last element is non-zero.
template <typename T>
void LoadVectorNnz(ANSDec* dec, uint32_t nnz, uint32_t val_upper, T& v) {
LoadVectorImpl<T, true>(dec, nnz, val_upper, v);
}
template <typename T, bool USE_NNZ>
void LoadVectorImpl(ANSDec* dec, size_t nnz, uint32_t val_upper, T& v) {
uint32_t size = v.size();
if (size == 0) return;
if (val_upper == 0 || (USE_NNZ && nnz == 0)) {
std::fill(&v[0], &v[size], 0);
return;
}
const uint8_t n_bits_max = FindLastSet(val_upper);
auto read_value = [dec, val_upper, n_bits_max](uint32_t min) -> uint32_t {
if (val_upper <= kANSMaxRange) {
return static_cast<uint32_t>(dec->ReadRange(min, val_upper, "value"));
} else {
return dec->ReadUValue(n_bits_max, "value");
}
};
if (USE_NNZ && nnz == 1) {
// The last value is the non-zero one.
std::fill(&v[0], &v[size - 1], 0);
v[size - 1] = read_value(1);
return;
}
if (size <= 2) {
uint32_t i = 0;
if (USE_NNZ) {
for (; i < size; ++i) {
v[i] = read_value(i + nnz == size ? 1 : 0);
if (v[i] > 0) --nnz;
}
} else {
for (; i < size; ++i) v[i] = read_value(0);
}
std::fill(&v[i], &v[size], 0);
return;
}
size_t k = 0;
int n_bits_prev = -1;
const bool have_same_depth = dec->ReadBool("has_same_depth");
while (size > 0) {
// Figure out the number of bits.
uint32_t n_bits;
if (n_bits_prev < 0) {
// The first time, we have to use the full range of bits.
n_bits = dec->ReadRValue(n_bits_max + 1, "n_bits");
} else {
// The following time, we save one unit as we know the value has to be
// different from before.
n_bits = dec->ReadRValue(n_bits_max, "n_bits");
if ((int)n_bits >= n_bits_prev) ++n_bits;
}
n_bits_prev = (int)n_bits;
// Figure out the number of elements with that bit depth.
if (USE_NNZ && nnz == 1) {
// We know we only have zeros until the last value.
for (; size > 1; ++k, --size) v[k] = 0;
}
const uint32_t n =
(have_same_depth) ? size : dec->ReadRange(1, size, "count");
// Whether to read the values as RValue or UValue.
const bool use_r_value =
(n_bits == n_bits_max) && (n_bits_max <= kANSMaxRangeBits);
if (n_bits == 0) {
// For a depth of 0 bits, we know it is a 0.
for (size_t i = 0; i < n; ++i) v[k++] = 0;
assert(size >= n);
size -= n;
// If we have more nnz_left than elements after this segment, the last 0s
// were actually elements reduced by 1.
if (USE_NNZ && nnz >= size) {
for (uint32_t i = k - (nnz - size); i < k; ++i) ++v[i];
nnz = size;
}
} else if (n == 1) {
// If we only have one number, we didn't store the high order
// bit as it's always 1, otherwise it would fit in (n_bits_ - 1).
uint32_t value;
const bool add_one = (USE_NNZ && size == nnz);
if (n_bits == 1) {
// And if that number is only on one bit to begin with, we didn't
// store anything.
value = 1u;
} else {
value = 1u << (n_bits - 1);
if (use_r_value) {
value |= dec->ReadRValue(
add_one ? val_upper - value : val_upper + 1 - value, "value");
} else {
value |= dec->ReadUValue(n_bits - 1, "value");
}
}
v[k] = add_one ? value + 1 : value;
if (USE_NNZ && v[k] != 0) --nnz;
++k;
assert(size >= 1);
size -= 1;
} else {
for (size_t i = 0; i < n; ++i, ++k, assert(size >= 1), --size) {
// Use the fact that the last element is non-zero.
if (USE_NNZ && nnz == 1 && size > 1) {
v[k] = 0;
continue;
}
const bool add_one = (USE_NNZ && size == nnz);
if (use_r_value) {
v[k] = dec->ReadRValue(add_one ? val_upper : val_upper + 1, "value");
} else {
v[k] = dec->ReadUValue(n_bits, "value");
}
if (add_one) ++v[k];
if (USE_NNZ && v[k] > 0) --nnz;
}
}
}
if (USE_NNZ) assert(nnz == 0);
if (k < v.size()) std::fill(&v[k], v.end(), 0);
}
//------------------------------------------------------------------------------
// ANSEncCounter
// ANSEncCounter behaves like an ANSEnc but does not store anything.
// Its only goal is to keep track of the cost of what its ANSEnc counterpart
// will store. It is useful to test the cost of an ANSEnc without dealing with
// all the token logic.
class ANSEncCounter : public ANSEncBase {
public:
ANSEncCounter() : cost_(0.), symbol_cost_(0.) {}
uint32_t PutBit(uint32_t bit, uint32_t num_zeros, uint32_t num_total,
WP2_OPT_LABEL) override;
uint32_t PutABit(uint32_t bit, ANSBinSymbol* stats, WP2_OPT_LABEL) override;
uint32_t PutSymbol(uint32_t symbol, const ANSDictionary& dict,
WP2_OPT_LABEL) override;
uint32_t PutSymbol(uint32_t symbol, const ANSAdaptiveSymbol& asym,
WP2_OPT_LABEL) override;
uint32_t PutSymbol(uint32_t symbol, uint32_t max_symbol,
const ANSDictionary& dict, WP2_OPT_LABEL) override;
uint32_t PutSymbol(uint32_t symbol, uint32_t max_symbol,
const ANSAdaptiveSymbol& asym, WP2_OPT_LABEL) override;
uint32_t PutUValue(uint32_t value, uint32_t bits, WP2_OPT_LABEL) override;
uint32_t PutRValue(uint32_t value, uint32_t range, WP2_OPT_LABEL) override;
// Adds a fixed cost to the total cost.
void AddCost(float cost) { cost_ += cost; }
// Returns the total cost in bits. Cost of symbols is the cost based on
// dictionary stats at time of writing.
float GetCost() const override;
// Returns the total cost. Cost of symbols is taken from the provided
// dictionaries, and thus the result might be different from GetCost().
float GetCost(const ANSDictionaries& dicts) const override;
// The number of tokens is useless for a counter.
uint32_t NumTokens() const override { return 0; };
WP2Status GetStatus() const override { return WP2_STATUS_OK; }
WP2Status Append(const ANSEncCounter& enc);
// Resets costs to 0.
void Reset();
protected:
float cost_;
// Cost of symbols at time of writing.
float symbol_cost_;
};
// ANS encoder that does nothing! Useful when a function expects an ANS encoder
// but we don't actually want to write to the bitstream.
class ANSEncNoop : public ANSEncBase {
public:
ANSEncNoop() = default;
uint32_t PutBit(uint32_t bit, uint32_t num_zeros, uint32_t num_total,
WP2_OPT_LABEL) override {
return bit;
}
uint32_t PutABit(uint32_t bit, ANSBinSymbol* const stats,
WP2_OPT_LABEL) override {
return bit;
}
uint32_t PutSymbol(uint32_t symbol, const ANSDictionary& dict,
WP2_OPT_LABEL) override {
return symbol;
}
uint32_t PutSymbol(uint32_t symbol, const ANSAdaptiveSymbol& asym,
WP2_OPT_LABEL) override {
return symbol;
}
uint32_t PutSymbol(uint32_t symbol, uint32_t max_symbol,
const ANSDictionary& dict, WP2_OPT_LABEL) override {
return symbol;
}
uint32_t PutSymbol(uint32_t symbol, uint32_t max_symbol,
const ANSAdaptiveSymbol& asym, WP2_OPT_LABEL) override {
return symbol;
}
uint32_t PutUValue(uint32_t value, uint32_t bits, WP2_OPT_LABEL) override {
return value;
}
uint32_t PutRValue(uint32_t value, uint32_t range, WP2_OPT_LABEL) override {
return value;
}
float GetCost() const override {
assert(false);
return 0;
}
float GetCost(const ANSDictionaries& dicts) const override {
assert(false);
return 0;
}
uint32_t NumTokens() const override {
assert(false);
return 0;
}
WP2Status GetStatus() const override { return WP2_STATUS_OK; }
};
//------------------------------------------------------------------------------
// Very simple utilities to store/load elements.
// Loads a mapping where all values are in the range [ 0, 'range' ).
// A mapping is such that value i is mapped to 'mapping'[i]. All values are
// strictly increasing and are in the range [ 0, 'range' ).
// 'mapping' must point to a memory buffer of size at least 'size'.
WP2Status LoadMapping(ANSDec* dec, uint32_t size, uint32_t range,
uint16_t* mapping);
// Stores a mapping.
// 'stats' is the same as in StoreVector.
// Returns the cost of storing the mapping. It is faster to get this value than
// to call the function with an ANSEncCounter.
float StoreMapping(const uint16_t* mapping, size_t size, uint32_t range,
int effort, OptimizeArrayStorageStat* stats,
ANSEncBase* enc);
//------------------------------------------------------------------------------
// Writes a value in [min, max] where the size of the interval can be larger
// than kANSMaxRange.
uint32_t PutLargeRange(uint32_t v, uint32_t min, uint32_t max, ANSEncBase* enc,
WP2_OPT_LABEL);
// Reads a value written with PutLargeRange.
uint32_t ReadLargeRange(uint32_t min, uint32_t max, ANSDec* dec, WP2_OPT_LABEL);
//------------------------------------------------------------------------------
// Class that adds a debug prefix when it's instantiated and pop it in the
// destructor.
class ANSDebugPrefix {
public:
#if defined(WP2_BITTRACE) || defined(WP2_TRACE) || defined(WP2_ENC_DEC_MATCH)
ANSDebugPrefix(ANSEncBase* const enc, const char prefix[])
: enc_(enc), dec_(nullptr) {
if (enc_ != nullptr) enc_->AddDebugPrefix(prefix);
}
ANSDebugPrefix(ANSDec* const dec, const char prefix[])
: enc_(nullptr), dec_(dec) {
if (dec_ != nullptr) dec_->AddDebugPrefix(prefix);
}
~ANSDebugPrefix() {
if (enc_ != nullptr) {
enc_->PopDebugPrefix();
} else if (dec_ != nullptr) {
dec_->PopDebugPrefix();
}
}
private:
ANSEncBase* enc_;
ANSDec* dec_;
#else
ANSDebugPrefix(ANSEncBase* const enc, const char prefix[]) {
(void)enc;
(void)prefix;
}
ANSDebugPrefix(ANSDec* const dec, const char prefix[]) {
(void)dec;
(void)prefix;
}
#endif
};
} // namespace WP2
#endif // WP2_UTILS_ANS_UTILS_H_