Skip to content

Commit

Permalink
Split the bit reader functionalities from the byte stream provider.
Browse files Browse the repository at this point in the history
BUG=None

Review URL: https://codereview.chromium.org/112343011

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@244959 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
damienv@chromium.org committed Jan 15, 2014
1 parent d0ae42e commit c8bb080
Show file tree
Hide file tree
Showing 6 changed files with 349 additions and 111 deletions.
83 changes: 17 additions & 66 deletions media/base/bit_reader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,80 +4,31 @@

#include "media/base/bit_reader.h"

#include <algorithm>

namespace media {

BitReader::BitReader(const uint8* data, off_t size)
: data_(data), bytes_left_(size), num_remaining_bits_in_curr_byte_(0) {
DCHECK(data_ != NULL && bytes_left_ > 0);

UpdateCurrByte();
BitReader::BitReader(const uint8* data, int size)
: initial_size_(size),
data_(data),
bytes_left_(size),
bit_reader_core_(this) {
DCHECK(data != NULL);
DCHECK_GE(size, 0);
}

BitReader::~BitReader() {}

bool BitReader::SkipBits(int num_bits) {
DCHECK_GE(num_bits, 0);
DVLOG_IF(0, num_bits > 100)
<< "BitReader::SkipBits inefficient for large skips";

// Skip any bits in the current byte waiting to be processed, then
// process full bytes until less than 8 bits remaining.
while (num_bits > 0 && num_bits > num_remaining_bits_in_curr_byte_) {
num_bits -= num_remaining_bits_in_curr_byte_;
num_remaining_bits_in_curr_byte_ = 0;
UpdateCurrByte();

// If there is no more data remaining, only return true if we
// skipped all that were requested.
if (num_remaining_bits_in_curr_byte_ == 0)
return (num_bits == 0);
}

// Less than 8 bits remaining to skip. Use ReadBitsInternal to verify
// that the remaining bits we need exist, and adjust them as necessary
// for subsequent operations.
uint64 not_needed;
return ReadBitsInternal(num_bits, &not_needed);
}

int BitReader::bits_available() const {
return 8 * bytes_left_ + num_remaining_bits_in_curr_byte_;
}

bool BitReader::ReadBitsInternal(int num_bits, uint64* out) {
DCHECK_LE(num_bits, 64);

*out = 0;

while (num_remaining_bits_in_curr_byte_ != 0 && num_bits != 0) {
int bits_to_take = std::min(num_remaining_bits_in_curr_byte_, num_bits);

*out <<= bits_to_take;
*out += curr_byte_ >> (num_remaining_bits_in_curr_byte_ - bits_to_take);
num_bits -= bits_to_take;
num_remaining_bits_in_curr_byte_ -= bits_to_take;
curr_byte_ &= (1 << num_remaining_bits_in_curr_byte_) - 1;

if (num_remaining_bits_in_curr_byte_ == 0)
UpdateCurrByte();
}

return num_bits == 0;
}

void BitReader::UpdateCurrByte() {
DCHECK_EQ(num_remaining_bits_in_curr_byte_, 0);
int BitReader::GetBytes(int max_nbytes, const uint8** out) {
DCHECK_GE(max_nbytes, 0);
DCHECK(out);

if (bytes_left_ == 0)
return;
int nbytes = max_nbytes;
if (nbytes > bytes_left_)
nbytes = bytes_left_;

// Load a new byte and advance pointers.
curr_byte_ = *data_;
++data_;
--bytes_left_;
num_remaining_bits_in_curr_byte_ = 8;
*out = data_;
data_ += nbytes;
bytes_left_ -= nbytes;
return nbytes;
}

} // namespace media
77 changes: 32 additions & 45 deletions media/base/bit_reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,70 +5,57 @@
#ifndef MEDIA_BASE_BIT_READER_H_
#define MEDIA_BASE_BIT_READER_H_

#include <sys/types.h>

#include "base/basictypes.h"
#include "base/logging.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "media/base/bit_reader_core.h"
#include "media/base/media_export.h"

namespace media {

// A class to read bit streams.
class MEDIA_EXPORT BitReader {
class MEDIA_EXPORT BitReader
: NON_EXPORTED_BASE(private BitReaderCore::ByteStreamProvider) {
public:
// Initialize the reader to start reading at |data|, |size| being size
// of |data| in bytes.
BitReader(const uint8* data, off_t size);
~BitReader();

// Read |num_bits| next bits from stream and return in |*out|, first bit
// from the stream starting at |num_bits| position in |*out|.
// |num_bits| cannot be larger than the bits the type can hold.
// Return false if the given number of bits cannot be read (not enough
// bits in the stream), true otherwise. When return false, the stream will
// enter a state where further ReadBits/SkipBits operations will always
// return false unless |num_bits| is 0. The type |T| has to be a primitive
// integer type.
template<typename T> bool ReadBits(int num_bits, T *out) {
DCHECK_LE(num_bits, static_cast<int>(sizeof(T) * 8));
uint64 temp;
bool ret = ReadBitsInternal(num_bits, &temp);
*out = static_cast<T>(temp);
return ret;
BitReader(const uint8* data, int size);
virtual ~BitReader();

template<typename T> bool ReadBits(int num_bits, T* out) {
return bit_reader_core_.ReadBits(num_bits, out);
}

bool ReadFlag(bool* flag) {
return bit_reader_core_.ReadFlag(flag);
}

bool SkipBits(int num_bits) {
return bit_reader_core_.SkipBits(num_bits);
}

// Skip |num_bits| next bits from stream. Return false if the given number of
// bits cannot be skipped (not enough bits in the stream), true otherwise.
// When return false, the stream will enter a state where further ReadBits/
// SkipBits operations will always return false unless |num_bits| is 0.
bool SkipBits(int num_bits);
int bits_available() const {
return initial_size_ * 8 - bits_read();
}

// Returns the number of bits available for reading.
int bits_available() const;
int bits_read() const {
return bit_reader_core_.bits_read();
}

private:
// Help function used by ReadBits to avoid inlining the bit reading logic.
bool ReadBitsInternal(int num_bits, uint64* out);
// BitReaderCore::ByteStreamProvider implementation.
virtual int GetBytes(int max_n, const uint8** out) OVERRIDE;

// Advance to the next byte, loading it into curr_byte_.
// If the num_remaining_bits_in_curr_byte_ is 0 after this function returns,
// the stream has reached the end.
void UpdateCurrByte();
// Total number of bytes that was initially passed to BitReader.
const int initial_size_;

// Pointer to the next unread (not in curr_byte_) byte in the stream.
// Pointer to the next unread byte in the stream.
const uint8* data_;

// Bytes left in the stream (without the curr_byte_).
off_t bytes_left_;
// Bytes left in the stream.
int bytes_left_;

// Contents of the current byte; first unread bit starting at position
// 8 - num_remaining_bits_in_curr_byte_ from MSB.
uint8 curr_byte_;
BitReaderCore bit_reader_core_;

// Number of bits remaining in curr_byte_
int num_remaining_bits_in_curr_byte_;

private:
DISALLOW_COPY_AND_ASSIGN(BitReader);
};

Expand Down
159 changes: 159 additions & 0 deletions media/base/bit_reader_core.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/base/bit_reader_core.h"

#include <base/port.h>
#include <base/sys_byteorder.h>

namespace {
const int kRegWidthInBits = sizeof(uint64) * 8;
}

namespace media {

BitReaderCore::ByteStreamProvider::ByteStreamProvider() {
}

BitReaderCore::ByteStreamProvider::~ByteStreamProvider() {
}

BitReaderCore::BitReaderCore(ByteStreamProvider* byte_stream_provider)
: byte_stream_provider_(byte_stream_provider),
bits_read_(0),
nbits_(0),
reg_(0),
nbits_next_(0),
reg_next_(0) {
}

BitReaderCore::~BitReaderCore() {
}

bool BitReaderCore::ReadFlag(bool* flag) {
if (nbits_ == 0 && !Refill(1))
return false;

*flag = (reg_ & (GG_UINT64_C(1) << (kRegWidthInBits - 1))) != 0;
reg_ <<= 1;
nbits_--;
bits_read_++;
return true;
}

int BitReaderCore::PeekBitsMsbAligned(int num_bits, uint64* out) {
// Try to have at least |num_bits| in the bit register.
if (nbits_ < num_bits)
Refill(num_bits);

*out = reg_;
return nbits_;
}

bool BitReaderCore::SkipBits(int num_bits) {
DCHECK_GE(num_bits, 0);
DVLOG_IF(0, num_bits > 100)
<< "BitReader::SkipBits inefficient for large skips";

uint64 dummy;
while (num_bits >= kRegWidthInBits) {
if (!ReadBitsInternal(kRegWidthInBits, &dummy))
return false;
num_bits -= kRegWidthInBits;
}
return ReadBitsInternal(num_bits, &dummy);
}

int BitReaderCore::bits_read() const {
return bits_read_;
}

bool BitReaderCore::ReadBitsInternal(int num_bits, uint64* out) {
DCHECK_GE(num_bits, 0);

if (num_bits == 0) {
*out = 0;
return true;
}

if (num_bits > nbits_ && !Refill(num_bits)) {
// Any subsequent ReadBits should fail:
// empty the current bit register for that purpose.
nbits_ = 0;
reg_ = 0;
return false;
}

bits_read_ += num_bits;

if (num_bits == kRegWidthInBits) {
// Special case needed since for example for a 64 bit integer "a"
// "a << 64" is not defined by the C/C++ standard.
*out = reg_;
reg_ = 0;
nbits_ = 0;
return true;
}

*out = reg_ >> (kRegWidthInBits - num_bits);
reg_ <<= num_bits;
nbits_ -= num_bits;
return true;
}

bool BitReaderCore::Refill(int min_nbits) {
DCHECK_LE(min_nbits, kRegWidthInBits);

// Transfer from the next to the current register.
RefillCurrentRegister();
if (min_nbits <= nbits_)
return true;
DCHECK_EQ(nbits_next_, 0);
DCHECK_EQ(reg_next_, 0u);

// Max number of bytes to refill.
int max_nbytes = sizeof(reg_next_);

// Refill.
const uint8* byte_stream_window;
int window_size =
byte_stream_provider_->GetBytes(max_nbytes, &byte_stream_window);
DCHECK_GE(window_size, 0);
DCHECK_LE(window_size, max_nbytes);
if (window_size == 0)
return false;

reg_next_ = 0;
memcpy(&reg_next_, byte_stream_window, window_size);
reg_next_ = base::NetToHost64(reg_next_);
nbits_next_ = window_size * 8;

// Transfer from the next to the current register.
RefillCurrentRegister();

return (nbits_ >= min_nbits);
}

void BitReaderCore::RefillCurrentRegister() {
// No refill possible if the destination register is full
// or the source register is empty.
if (nbits_ == kRegWidthInBits || nbits_next_ == 0)
return;

reg_ |= (reg_next_ >> nbits_);

int free_nbits = kRegWidthInBits - nbits_;
if (free_nbits >= nbits_next_) {
nbits_ += nbits_next_;
reg_next_ = 0;
nbits_next_ = 0;
return;
}

nbits_ += free_nbits;
reg_next_ <<= free_nbits;
nbits_next_ -= free_nbits;
}

} // namespace media
Loading

0 comments on commit c8bb080

Please sign in to comment.