libheif/bitstream.h (194 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/>.
*/
#ifndef LIBHEIF_BITSTREAM_H
#define LIBHEIF_BITSTREAM_H
#include <cinttypes>
#include <cstddef>
#include <vector>
#include <string>
#include <memory>
#include <limits>
#include <istream>
#include <string>
#include "error.h"
class StreamReader
{
public:
virtual ~StreamReader() = default;
virtual int64_t get_position() const = 0;
enum grow_status
{
size_reached, // requested size has been reached
timeout, // size has not been reached yet, but it may still grow further
size_beyond_eof // size has not been reached and never will. The file has grown to its full size
};
// a StreamReader can maintain a timeout for waiting for new data
virtual grow_status wait_for_file_size(int64_t target_size) = 0;
// returns 'false' when we read out of the available file size
virtual bool read(void* data, size_t size) = 0;
virtual bool seek(int64_t position) = 0;
bool seek_cur(int64_t position_offset)
{
return seek(get_position() + position_offset);
}
};
class StreamReader_istream : public StreamReader
{
public:
StreamReader_istream(std::unique_ptr<std::istream>&& istr);
int64_t get_position() const override;
grow_status wait_for_file_size(int64_t target_size) override;
bool read(void* data, size_t size) override;
bool seek(int64_t position) override;
private:
std::unique_ptr<std::istream> m_istr;
int64_t m_length;
};
class StreamReader_memory : public StreamReader
{
public:
StreamReader_memory(const uint8_t* data, size_t size, bool copy);
~StreamReader_memory() override;
int64_t get_position() const override;
grow_status wait_for_file_size(int64_t target_size) override;
bool read(void* data, size_t size) override;
bool seek(int64_t position) override;
private:
const uint8_t* m_data;
int64_t m_length;
int64_t m_position;
// if we made a copy of the data, we store a pointer to the owned memory area here
uint8_t* m_owned_data = nullptr;
};
class StreamReader_CApi : public StreamReader
{
public:
StreamReader_CApi(const heif_reader* func_table, void* userdata);
int64_t get_position() const override { return m_func_table->get_position(m_userdata); }
StreamReader::grow_status wait_for_file_size(int64_t target_size) override;
bool read(void* data, size_t size) override { return !m_func_table->read(data, size, m_userdata); }
bool seek(int64_t position) override { return !m_func_table->seek(position, m_userdata); }
private:
const heif_reader* m_func_table;
void* m_userdata;
};
// This class simplifies safely reading part of a file (e.g. a box).
// It makes sure that we do not read past the boundaries of a box.
class BitstreamRange
{
public:
BitstreamRange(std::shared_ptr<StreamReader> istr,
size_t length,
BitstreamRange* parent = nullptr);
// This function tries to make sure that the full data of this range is
// available. You should call this before starting reading the range.
// If you don't, you have to make sure that you do not read past the available data.
StreamReader::grow_status wait_until_range_is_available();
uint8_t read8();
uint16_t read16();
int16_t read16s();
uint32_t read32();
int32_t read32s();
uint64_t read64();
std::string read_string();
bool read(uint8_t* data, size_t n);
bool prepare_read(size_t nBytes);
StreamReader::grow_status wait_for_available_bytes(size_t nBytes);
void skip_to_end_of_file()
{
// we do not actually move the file position here (because the stream may still be incomplete),
// but we set all m_remaining to zero
m_remaining = 0;
if (m_parent_range) {
m_parent_range->skip_to_end_of_file();
}
}
void skip_to_end_of_box()
{
if (m_remaining > 0) {
if (m_parent_range) {
// also advance position in parent range
m_parent_range->skip_without_advancing_file_pos(m_remaining);
}
m_istr->seek_cur(m_remaining);
m_remaining = 0;
}
}
void set_eof_while_reading()
{
m_remaining = 0;
if (m_parent_range) {
m_parent_range->set_eof_while_reading();
}
m_error = true;
}
bool eof() const
{
return m_remaining == 0;
}
bool error() const
{
return m_error;
}
Error get_error() const
{
if (m_error) {
return Error(heif_error_Invalid_input,
heif_suberror_End_of_data);
}
else {
return Error::Ok;
}
}
std::shared_ptr<StreamReader> get_istream() { return m_istr; }
int get_nesting_level() const { return m_nesting_level; }
size_t get_remaining_bytes() const { return m_remaining; }
private:
std::shared_ptr<StreamReader> m_istr;
BitstreamRange* m_parent_range = nullptr;
int m_nesting_level = 0;
size_t m_remaining;
bool m_error = false;
// Note: 'nBytes' may not be larger than the number of remaining bytes
void skip_without_advancing_file_pos(size_t nBytes);
};
class BitReader
{
public:
BitReader(const uint8_t* buffer, int len);
int get_bits(int n);
int get_bits_fast(int n);
int peek_bits(int n);
void skip_bytes(int nBytes);
void skip_bits(int n);
void skip_bits_fast(int n);
void skip_to_byte_boundary();
bool get_uvlc(int* value);
bool get_svlc(int* value);
int get_current_byte_index() const
{
return data_length - bytes_remaining - nextbits_cnt / 8;
}
int64_t get_bits_remaining() const
{
return ((int64_t) bytes_remaining) * 8 + nextbits_cnt;
}
private:
const uint8_t* data;
int data_length;
int bytes_remaining;
uint64_t nextbits; // left-aligned bits
int nextbits_cnt;
void refill(); // refill to at least 56+1 bits
};
class StreamWriter
{
public:
void write8(uint8_t);
void write16(uint16_t);
void write16s(int16_t);
void write32(uint32_t);
void write32s(int32_t);
void write64(uint64_t);
void write(int size, uint64_t value);
void write(const std::string&);
void write(const std::vector<uint8_t>&);
void write(const StreamWriter&);
void skip(int n);
void insert(int nBytes);
size_t data_size() const { return m_data.size(); }
size_t get_position() const { return m_position; }
void set_position(size_t pos) { m_position = pos; }
void set_position_to_end() { m_position = m_data.size(); }
const std::vector<uint8_t> get_data() const { return m_data; }
private:
std::vector<uint8_t> m_data;
size_t m_position = 0;
};
#endif