|  | /* | 
|  | * Copyright (c) 2016, Alliance for Open Media. All rights reserved | 
|  | * | 
|  | * This source code is subject to the terms of the BSD 2 Clause License and | 
|  | * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License | 
|  | * was not distributed with this source code in the LICENSE file, you can | 
|  | * obtain it at www.aomedia.org/license/software. If the Alliance for Open | 
|  | * Media Patent License 1.0 was not distributed with this source code in the | 
|  | * PATENTS file, you can obtain it at www.aomedia.org/license/patent. | 
|  | */ | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <math.h> | 
|  | #include <stdio.h> | 
|  | #include <ctime> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "third_party/googletest/src/googletest/include/gtest/gtest.h" | 
|  |  | 
|  | #include "test/acm_random.h" | 
|  | #include "aom_dsp/ansreader.h" | 
|  | #include "aom_dsp/buf_ans.h" | 
|  |  | 
|  | namespace { | 
|  | typedef std::vector<std::pair<uint8_t, bool> > PvVec; | 
|  |  | 
|  | const int kPrintStats = 0; | 
|  | // Use a small buffer size to exercise ANS window spills or buffer growth | 
|  | const int kBufAnsSize = 1 << 8; | 
|  |  | 
|  | PvVec abs_encode_build_vals(int iters) { | 
|  | PvVec ret; | 
|  | libaom_test::ACMRandom gen(0x30317076); | 
|  | double entropy = 0; | 
|  | for (int i = 0; i < iters; ++i) { | 
|  | uint8_t p; | 
|  | do { | 
|  | p = gen.Rand8(); | 
|  | } while (p == 0);  // zero is not a valid coding probability | 
|  | bool b = gen.Rand8() < p; | 
|  | ret.push_back(std::make_pair(static_cast<uint8_t>(p), b)); | 
|  | if (kPrintStats) { | 
|  | double d = p / 256.; | 
|  | entropy += -d * log2(d) - (1 - d) * log2(1 - d); | 
|  | } | 
|  | } | 
|  | if (kPrintStats) printf("entropy %f\n", entropy); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | bool check_rabs(const PvVec &pv_vec, uint8_t *buf) { | 
|  | BufAnsCoder a; | 
|  | aom_buf_ans_alloc(&a, NULL, kBufAnsSize); | 
|  | buf_ans_write_init(&a, buf); | 
|  |  | 
|  | std::clock_t start = std::clock(); | 
|  | for (PvVec::const_iterator it = pv_vec.begin(); it != pv_vec.end(); ++it) { | 
|  | buf_rabs_write(&a, it->second, 256 - it->first); | 
|  | } | 
|  | aom_buf_ans_flush(&a); | 
|  | std::clock_t enc_time = std::clock() - start; | 
|  | int offset = buf_ans_write_end(&a); | 
|  | aom_buf_ans_free(&a); | 
|  | bool okay = true; | 
|  | AnsDecoder d; | 
|  | #if ANS_MAX_SYMBOLS | 
|  | d.window_size = kBufAnsSize; | 
|  | #endif | 
|  | if (ans_read_init(&d, buf, offset)) return false; | 
|  | start = std::clock(); | 
|  | for (PvVec::const_iterator it = pv_vec.begin(); it != pv_vec.end(); ++it) { | 
|  | okay = okay && (rabs_read(&d, 256 - it->first) != 0) == it->second; | 
|  | } | 
|  | std::clock_t dec_time = std::clock() - start; | 
|  | if (!okay) return false; | 
|  | if (kPrintStats) | 
|  | printf("uABS size %d enc_time %f dec_time %f\n", offset, | 
|  | static_cast<float>(enc_time) / CLOCKS_PER_SEC, | 
|  | static_cast<float>(dec_time) / CLOCKS_PER_SEC); | 
|  | return ans_read_end(&d) != 0; | 
|  | } | 
|  |  | 
|  | const aom_cdf_prob spareto65[] = { 8320, 6018, 4402, 3254, 4259, | 
|  | 3919, 2057, 492,  45,   2 }; | 
|  |  | 
|  | const int kRansSymbols = | 
|  | static_cast<int>(sizeof(spareto65) / sizeof(spareto65[0])); | 
|  |  | 
|  | struct rans_sym { | 
|  | aom_cdf_prob prob; | 
|  | aom_cdf_prob cum_prob;  // not-inclusive | 
|  | }; | 
|  |  | 
|  | std::vector<int> ans_encode_build_vals(rans_sym *const tab, int iters) { | 
|  | aom_cdf_prob sum = 0; | 
|  | for (int i = 0; i < kRansSymbols; ++i) { | 
|  | tab[i].cum_prob = sum; | 
|  | tab[i].prob = spareto65[i]; | 
|  | sum += spareto65[i]; | 
|  | } | 
|  | std::vector<int> p_to_sym; | 
|  | for (int i = 0; i < kRansSymbols; ++i) { | 
|  | p_to_sym.insert(p_to_sym.end(), tab[i].prob, i); | 
|  | } | 
|  | assert(p_to_sym.size() == RANS_PRECISION); | 
|  | std::vector<int> ret; | 
|  | libaom_test::ACMRandom gen(18543637); | 
|  | for (int i = 0; i < iters; ++i) { | 
|  | int sym = | 
|  | p_to_sym[((gen.Rand8() << 8) + gen.Rand8()) & (RANS_PRECISION - 1)]; | 
|  | ret.push_back(sym); | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | void rans_build_dec_tab(const struct rans_sym sym_tab[], | 
|  | aom_cdf_prob *dec_tab) { | 
|  | unsigned int sum = 0; | 
|  | for (int i = 0; sum < RANS_PRECISION; ++i) { | 
|  | dec_tab[i] = sum += sym_tab[i].prob; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool check_rans(const std::vector<int> &sym_vec, const rans_sym *const tab, | 
|  | uint8_t *buf) { | 
|  | BufAnsCoder a; | 
|  | aom_buf_ans_alloc(&a, NULL, kBufAnsSize); | 
|  | buf_ans_write_init(&a, buf); | 
|  | aom_cdf_prob dec_tab[kRansSymbols]; | 
|  | rans_build_dec_tab(tab, dec_tab); | 
|  |  | 
|  | std::clock_t start = std::clock(); | 
|  | for (std::vector<int>::const_iterator it = sym_vec.begin(); | 
|  | it != sym_vec.end(); ++it) { | 
|  | buf_rans_write(&a, tab[*it].cum_prob, tab[*it].prob); | 
|  | } | 
|  | aom_buf_ans_flush(&a); | 
|  | std::clock_t enc_time = std::clock() - start; | 
|  | int offset = buf_ans_write_end(&a); | 
|  | aom_buf_ans_free(&a); | 
|  | bool okay = true; | 
|  | AnsDecoder d; | 
|  | #if ANS_MAX_SYMBOLS | 
|  | d.window_size = kBufAnsSize; | 
|  | #endif | 
|  | if (ans_read_init(&d, buf, offset)) return false; | 
|  | start = std::clock(); | 
|  | for (std::vector<int>::const_iterator it = sym_vec.begin(); | 
|  | it != sym_vec.end(); ++it) { | 
|  | okay &= rans_read(&d, dec_tab) == *it; | 
|  | } | 
|  | std::clock_t dec_time = std::clock() - start; | 
|  | if (!okay) return false; | 
|  | if (kPrintStats) | 
|  | printf("rANS size %d enc_time %f dec_time %f\n", offset, | 
|  | static_cast<float>(enc_time) / CLOCKS_PER_SEC, | 
|  | static_cast<float>(dec_time) / CLOCKS_PER_SEC); | 
|  | return ans_read_end(&d) != 0; | 
|  | } | 
|  |  | 
|  | class AbsTestFix : public ::testing::Test { | 
|  | protected: | 
|  | static void SetUpTestCase() { pv_vec_ = abs_encode_build_vals(kNumBools); } | 
|  | virtual void SetUp() { buf_ = new uint8_t[kNumBools / 8]; } | 
|  | virtual void TearDown() { delete[] buf_; } | 
|  | static const int kNumBools = 100000000; | 
|  | static PvVec pv_vec_; | 
|  | uint8_t *buf_; | 
|  | }; | 
|  | PvVec AbsTestFix::pv_vec_; | 
|  |  | 
|  | class AnsTestFix : public ::testing::Test { | 
|  | protected: | 
|  | static void SetUpTestCase() { | 
|  | sym_vec_ = ans_encode_build_vals(rans_sym_tab_, kNumSyms); | 
|  | } | 
|  | virtual void SetUp() { buf_ = new uint8_t[kNumSyms / 2]; } | 
|  | virtual void TearDown() { delete[] buf_; } | 
|  | static const int kNumSyms = 25000000; | 
|  | static std::vector<int> sym_vec_; | 
|  | static rans_sym rans_sym_tab_[kRansSymbols]; | 
|  | uint8_t *buf_; | 
|  | }; | 
|  | std::vector<int> AnsTestFix::sym_vec_; | 
|  | rans_sym AnsTestFix::rans_sym_tab_[kRansSymbols]; | 
|  |  | 
|  | TEST_F(AbsTestFix, Rabs) { EXPECT_TRUE(check_rabs(pv_vec_, buf_)); } | 
|  | TEST_F(AnsTestFix, Rans) { | 
|  | EXPECT_TRUE(check_rans(sym_vec_, rans_sym_tab_, buf_)); | 
|  | } | 
|  | TEST(AnsTest, FinalStateSerialization) { | 
|  | for (unsigned i = L_BASE; i < L_BASE * IO_BASE; ++i) { | 
|  | uint8_t buf[8]; | 
|  | AnsCoder c; | 
|  | ans_write_init(&c, buf); | 
|  | c.state = i; | 
|  | const int written_size = ans_write_end(&c); | 
|  | ASSERT_LT(static_cast<size_t>(written_size), sizeof(buf)); | 
|  | AnsDecoder d; | 
|  | #if ANS_MAX_SYMBOLS | 
|  | // There is no real data window here because no symbols are sent through | 
|  | // ans (only synthetic states), so use a dummy value | 
|  | d.window_size = 1024; | 
|  | #endif | 
|  | const int read_init_status = ans_read_init(&d, buf, written_size); | 
|  | EXPECT_EQ(read_init_status, 0); | 
|  | EXPECT_EQ(d.state, i); | 
|  | } | 
|  | } | 
|  | }  // namespace |