libheif/bitstream.cc (521 lines of code) (raw):
/*
* HEIF codec.
* Copyright (c) 2017 Dirk Farin <dirk.farin@gmail.com>
*
* This file is part of libheif.
*
* libheif is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libheif is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libheif. If not, see <http://www.gnu.org/licenses/>.
*/
#include "bitstream.h"
#include <utility>
#include <cstring>
#include <cassert>
#define MAX_UVLC_LEADING_ZEROS 20
StreamReader_istream::StreamReader_istream(std::unique_ptr<std::istream>&& istr)
: m_istr(std::move(istr))
{
m_istr->seekg(0, std::ios_base::end);
m_length = m_istr->tellg();
m_istr->seekg(0, std::ios_base::beg);
}
int64_t StreamReader_istream::get_position() const
{
return m_istr->tellg();
}
StreamReader::grow_status StreamReader_istream::wait_for_file_size(int64_t target_size)
{
return (target_size > m_length) ? size_beyond_eof : size_reached;
}
bool StreamReader_istream::read(void* data, size_t size)
{
int64_t end_pos = get_position() + size;
if (end_pos > m_length) {
return false;
}
m_istr->read((char*) data, size);
return true;
}
bool StreamReader_istream::seek(int64_t position)
{
if (position > m_length)
return false;
m_istr->seekg(position, std::ios_base::beg);
return true;
}
StreamReader_memory::StreamReader_memory(const uint8_t* data, size_t size, bool copy)
: m_length(size),
m_position(0)
{
if (copy) {
m_owned_data = new uint8_t[m_length];
memcpy(m_owned_data, data, m_length);
m_data = m_owned_data;
}
else {
m_data = data;
}
}
StreamReader_memory::~StreamReader_memory()
{
if (m_owned_data) {
delete[] m_owned_data;
}
}
int64_t StreamReader_memory::get_position() const
{
return m_position;
}
StreamReader::grow_status StreamReader_memory::wait_for_file_size(int64_t target_size)
{
return (target_size > m_length) ? size_beyond_eof : size_reached;
}
bool StreamReader_memory::read(void* data, size_t size)
{
int64_t end_pos = m_position + size;
if (end_pos > m_length) {
return false;
}
memcpy(data, &m_data[m_position], size);
m_position += size;
return true;
}
bool StreamReader_memory::seek(int64_t position)
{
if (position > m_length || position < 0)
return false;
m_position = position;
return true;
}
StreamReader_CApi::StreamReader_CApi(const heif_reader* func_table, void* userdata)
: m_func_table(func_table), m_userdata(userdata)
{
}
StreamReader::grow_status StreamReader_CApi::wait_for_file_size(int64_t target_size)
{
heif_reader_grow_status status = m_func_table->wait_for_file_size(target_size, m_userdata);
switch (status) {
case heif_reader_grow_status_size_reached:
return size_reached;
case heif_reader_grow_status_timeout:
return timeout;
case heif_reader_grow_status_size_beyond_eof:
return size_beyond_eof;
default:
assert(0);
return size_beyond_eof;
}
}
BitstreamRange::BitstreamRange(std::shared_ptr<StreamReader> istr,
size_t length,
BitstreamRange* parent)
: m_istr(std::move(istr)), m_parent_range(parent), m_remaining(length)
{
if (parent) {
m_nesting_level = parent->m_nesting_level + 1;
}
}
StreamReader::grow_status BitstreamRange::wait_until_range_is_available()
{
return m_istr->wait_for_file_size(m_istr->get_position() + m_remaining);
}
uint8_t BitstreamRange::read8()
{
if (!prepare_read(1)) {
return 0;
}
uint8_t buf;
auto istr = get_istream();
bool success = istr->read((char*) &buf, 1);
if (!success) {
set_eof_while_reading();
return 0;
}
return buf;
}
uint16_t BitstreamRange::read16()
{
if (!prepare_read(2)) {
return 0;
}
uint8_t buf[2];
auto istr = get_istream();
bool success = istr->read((char*) buf, 2);
if (!success) {
set_eof_while_reading();
return 0;
}
return static_cast<uint16_t>((buf[0] << 8) | (buf[1]));
}
int16_t BitstreamRange::read16s()
{
uint16_t v = read16();
if (v & 0x8000) {
return -static_cast<int16_t>((~v) & 0x7fff) -1;
}
else {
return static_cast<int16_t>(v);
}
}
uint32_t BitstreamRange::read32()
{
if (!prepare_read(4)) {
return 0;
}
uint8_t buf[4];
auto istr = get_istream();
bool success = istr->read((char*) buf, 4);
if (!success) {
set_eof_while_reading();
return 0;
}
return (uint32_t) ((buf[0] << 24) |
(buf[1] << 16) |
(buf[2] << 8) |
(buf[3]));
}
uint64_t BitstreamRange::read64()
{
if (!prepare_read(8)) {
return 0;
}
uint8_t buf[8];
auto istr = get_istream();
bool success = istr->read((char*) buf, 8);
if (!success) {
set_eof_while_reading();
return 0;
}
return (uint64_t) (((uint64_t)buf[0] << 56) |
((uint64_t)buf[1] << 48) |
((uint64_t)buf[2] << 40) |
((uint64_t)buf[3] << 32) |
((uint64_t)buf[4] << 24) |
((uint64_t)buf[5] << 16) |
((uint64_t)buf[6] << 8) |
((uint64_t)buf[7]));
}
int32_t BitstreamRange::read32s()
{
uint32_t v = read32();
if (v & 0x80000000) {
return -static_cast<int32_t>((~v) & 0x7fffffff) -1;
}
else {
return static_cast<int32_t>(v);
}
}
std::string BitstreamRange::read_string()
{
std::string str;
// Reading a string when no more data is available, returns an empty string.
// Such a case happens, for example, when reading a 'url' box without content.
if (eof()) {
return std::string();
}
for (;;) {
if (!prepare_read(1)) {
return std::string();
}
auto istr = get_istream();
char c;
bool success = istr->read(&c, 1);
if (!success) {
set_eof_while_reading();
return std::string();
}
if (c == 0) {
break;
}
else {
str += (char) c;
}
}
return str;
}
bool BitstreamRange::read(uint8_t* data, size_t n)
{
if (!prepare_read(n)) {
return false;
}
auto istr = get_istream();
bool success = istr->read(data, n);
if (!success) {
set_eof_while_reading();
}
return success;
}
bool BitstreamRange::prepare_read(size_t nBytes)
{
// Note: we do not test for negative nBytes anymore because we now use the unsigned size_t
if (m_remaining < nBytes) {
// --- not enough data left in box -> move to end of box and set error flag
skip_to_end_of_box();
m_error = true;
return false;
}
else {
// --- this is the normal case (m_remaining >= nBytes)
if (m_parent_range) {
if (!m_parent_range->prepare_read(nBytes)) {
return false;
}
}
m_remaining -= nBytes;
return true;
}
}
StreamReader::grow_status BitstreamRange::wait_for_available_bytes(size_t nBytes)
{
int64_t target_size = m_istr->get_position() + nBytes;
return m_istr->wait_for_file_size(target_size);
}
void BitstreamRange::skip_without_advancing_file_pos(size_t n)
{
assert(n <= m_remaining);
m_remaining -= n;
if (m_parent_range) {
m_parent_range->skip_without_advancing_file_pos(n);
}
}
BitReader::BitReader(const uint8_t* buffer, int len)
{
data = buffer;
data_length = len;
bytes_remaining = len;
nextbits = 0;
nextbits_cnt = 0;
refill();
}
int BitReader::get_bits(int n)
{
if (nextbits_cnt < n) {
refill();
}
uint64_t val = nextbits;
val >>= 64 - n;
nextbits <<= n;
nextbits_cnt -= n;
return (int) val;
}
int BitReader::get_bits_fast(int n)
{
assert(nextbits_cnt >= n);
uint64_t val = nextbits;
val >>= 64 - n;
nextbits <<= n;
nextbits_cnt -= n;
return (int) val;
}
int BitReader::peek_bits(int n)
{
if (nextbits_cnt < n) {
refill();
}
uint64_t val = nextbits;
val >>= 64 - n;
return (int) val;
}
void BitReader::skip_bytes(int nBytes)
{
// TODO: this is slow
while (nBytes--) {
skip_bits(8);
}
}
void BitReader::skip_bits(int n)
{
if (nextbits_cnt < n) {
refill();
}
nextbits <<= n;
nextbits_cnt -= n;
}
void BitReader::skip_bits_fast(int n)
{
nextbits <<= n;
nextbits_cnt -= n;
}
void BitReader::skip_to_byte_boundary()
{
int nskip = (nextbits_cnt & 7);
nextbits <<= nskip;
nextbits_cnt -= nskip;
}
bool BitReader::get_uvlc(int* value)
{
int num_zeros = 0;
while (get_bits(1) == 0) {
num_zeros++;
if (num_zeros > MAX_UVLC_LEADING_ZEROS) { return false; }
}
int offset = 0;
if (num_zeros != 0) {
offset = (int) get_bits(num_zeros);
*value = offset + (1 << num_zeros) - 1;
assert(*value > 0);
return true;
}
else {
*value = 0;
return true;
}
}
bool BitReader::get_svlc(int* value)
{
int v;
if (!get_uvlc(&v)) {
return false;
}
else if (v == 0) {
*value = v;
return true;
}
bool negative = ((v & 1) == 0);
*value = negative ? -v / 2 : (v + 1) / 2;
return true;
}
void BitReader::refill()
{
#if 0
// TODO: activate me once I'm sure this works
while (nextbits_cnt <= 64-8 && bytes_remaining) {
uint64_t newval = *data++;
bytes_remaining--;
nextbits_cnt += 8;
newval <<= 64-nextbits_cnt;
nextbits |= newval;
}
#else
int shift = 64 - nextbits_cnt;
while (shift >= 8 && bytes_remaining) {
uint64_t newval = *data++;
bytes_remaining--;
shift -= 8;
newval <<= shift;
nextbits |= newval;
}
nextbits_cnt = 64 - shift;
#endif
}
void StreamWriter::write8(uint8_t v)
{
if (m_position == m_data.size()) {
m_data.push_back(v);
m_position++;
}
else {
m_data[m_position++] = v;
}
}
void StreamWriter::write16(uint16_t v)
{
size_t required_size = m_position + 2;
if (required_size > m_data.size()) {
m_data.resize(required_size);
}
m_data[m_position++] = uint8_t((v >> 8) & 0xFF);
m_data[m_position++] = uint8_t(v & 0xFF);
}
void StreamWriter::write16s(int16_t v16s)
{
uint16_t v;
if (v16s >= 0) {
v = static_cast<uint16_t>(v16s);
}
else {
v = ~static_cast<uint16_t>((-v16s-1));
}
write16(v);
}
void StreamWriter::write32(uint32_t v)
{
size_t required_size = m_position + 4;
if (required_size > m_data.size()) {
m_data.resize(required_size);
}
m_data[m_position++] = uint8_t((v >> 24) & 0xFF);
m_data[m_position++] = uint8_t((v >> 16) & 0xFF);
m_data[m_position++] = uint8_t((v >> 8) & 0xFF);
m_data[m_position++] = uint8_t(v & 0xFF);
}
void StreamWriter::write32s(int32_t v32s)
{
uint32_t v;
if (v32s >= 0) {
v = static_cast<uint32_t>(v32s);
}
else {
v = ~static_cast<uint32_t>((-v32s-1));
}
write32(v);
}
void StreamWriter::write64(uint64_t v)
{
size_t required_size = m_position + 8;
if (required_size > m_data.size()) {
m_data.resize(required_size);
}
m_data[m_position++] = uint8_t((v >> 56) & 0xFF);
m_data[m_position++] = uint8_t((v >> 48) & 0xFF);
m_data[m_position++] = uint8_t((v >> 40) & 0xFF);
m_data[m_position++] = uint8_t((v >> 32) & 0xFF);
m_data[m_position++] = uint8_t((v >> 24) & 0xFF);
m_data[m_position++] = uint8_t((v >> 16) & 0xFF);
m_data[m_position++] = uint8_t((v >> 8) & 0xFF);
m_data[m_position++] = uint8_t(v & 0xFF);
}
void StreamWriter::write(int size, uint64_t value)
{
if (size == 1) {
assert(value <= 0xFF);
write8((uint8_t) value);
}
else if (size == 2) {
assert(value <= 0xFFFF);
write16((uint16_t) value);
}
else if (size == 4) {
assert(value <= 0xFFFFFFFF);
write32((uint32_t) value);
}
else if (size == 8) {
write64((uint64_t) value);
}
else {
assert(false); // unimplemented size
}
}
void StreamWriter::write(const std::string& str)
{
size_t required_size = m_position + str.size() + 1;
if (required_size > m_data.size()) {
m_data.resize(required_size);
}
for (size_t i = 0; i < str.size(); i++) {
m_data[m_position++] = str[i];
}
m_data[m_position++] = 0;
}
void StreamWriter::write(const std::vector<uint8_t>& vec)
{
size_t required_size = m_position + vec.size();
if (required_size > m_data.size()) {
m_data.resize(required_size);
}
memcpy(m_data.data() + m_position, vec.data(), vec.size());
m_position += vec.size();
}
void StreamWriter::write(const StreamWriter& writer)
{
size_t required_size = m_position + writer.get_data().size();
if (required_size > m_data.size()) {
m_data.resize(required_size);
}
const auto& data = writer.get_data();
memcpy(m_data.data() + m_position, data.data(), data.size());
m_position += data.size();
}
void StreamWriter::skip(int n)
{
assert(m_position == m_data.size());
m_data.resize(m_data.size() + n);
m_position += n;
}
void StreamWriter::insert(int nBytes)
{
assert(nBytes >= 0);
if (nBytes == 0) {
return;
}
m_data.resize(m_data.size() + nBytes);
if (m_position < m_data.size() - nBytes) {
memmove(m_data.data() + m_position + nBytes,
m_data.data() + m_position,
m_data.size() - nBytes - m_position);
}
}