blob: ed3d67ebca39ee12d7ede47961d31b64efed5926 [file] [log] [blame]
// Copyright (c) 2011 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/byte_queue.h"
#include <algorithm>
#include <cstring>
#include "base/check_op.h"
#include "base/numerics/checked_math.h"
namespace media {
ByteQueue::ByteQueue() : buffer_(new uint8_t[size_]) {}
ByteQueue::~ByteQueue() = default;
void ByteQueue::Reset() {
offset_ = 0;
used_ = 0;
}
void ByteQueue::Push(const uint8_t* data, int size) {
DCHECK(data);
DCHECK_GT(size, 0);
// This can never overflow since used and size are both ints.
const size_t size_needed = static_cast<size_t>(used_) + size;
// Check to see if we need a bigger buffer.
if (size_needed > size_) {
// Growth is based on base::circular_deque which grows at 25%.
const size_t safe_size =
(base::CheckedNumeric<size_t>(size_) + size_ / 4).ValueOrDie();
const size_t new_size = std::max(size_needed, safe_size);
// Copy the data from the old buffer to the start of the new one.
if (used_ > 0) {
// Note: We could use realloc() here, but would need an additional move to
// pack data at offset_ = 0 after a potential internal new allocation +
// copy by realloc().
//
// In local tests on a few top video sites that ends up being the common
// case, so just prefer to copy and pack ourselves.
std::unique_ptr<uint8_t[]> new_buffer(new uint8_t[new_size]);
memcpy(new_buffer.get(), Front(), used_);
buffer_ = std::move(new_buffer);
} else {
// Free the existing |data| first so that the memory can be reused, if
// possible. Note that the new array is purposely not initialized.
buffer_.reset();
buffer_.reset(new uint8_t[new_size]);
}
size_ = new_size;
offset_ = 0;
} else if ((offset_ + used_ + size) > size_) {
// The buffer is big enough, but we need to move the data in the queue.
memmove(buffer_.get(), Front(), used_);
offset_ = 0;
}
memcpy(Front() + used_, data, size);
used_ += size;
}
void ByteQueue::Peek(const uint8_t** data, int* size) const {
DCHECK(data);
DCHECK(size);
*data = Front();
*size = used_;
}
void ByteQueue::Pop(int count) {
DCHECK_LE(count, used_);
offset_ += count;
used_ -= count;
// Move the offset back to 0 if we have reached the end of the buffer.
if (offset_ == size_) {
DCHECK_EQ(used_, 0);
offset_ = 0;
}
}
uint8_t* ByteQueue::Front() const {
return buffer_.get() + offset_;
}
} // namespace media