// Copyright 2015 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 "mojo/public/cpp/environment/logging.h" #include "mojo/services/media/common/cpp/fifo_allocator.h" namespace mojo { namespace media { FifoAllocator::FifoAllocator(uint64_t size) : front_(nullptr), free_(nullptr) { // front_ and free_ need to be set to nullptr before calling reset. Reset(size); } FifoAllocator::~FifoAllocator() { DeleteFrontToBack(front_); DeleteFrontToBack(free_); } void FifoAllocator::Reset(uint64_t size) { MOJO_DCHECK(size < kNullOffset); DeleteFrontToBack(front_); DeleteFrontToBack(free_); size_ = size; free_ = nullptr; front_ = back_ = active_ = get_free(false, size, 0); active_->prev = nullptr; active_->next = nullptr; } uint64_t FifoAllocator::AllocateRegion(uint64_t size) { MOJO_DCHECK(size != 0); if (active_->size < size) { // The active region is too small. Look for one that's large enough. if (!AdvanceActive(size)) { // No unallocated regions are large enough. Can't do the allocation. return kNullOffset; } } if (active_->size == size) { // The active region is exactly the right size. Use it for the allocation. uint64_t result = active_->offset; active_->allocated = true; if (active_ == back_ && !front_->allocated) { // active_ was the back region and the front region isn't allocated. Make // the front region the new active region. active_ = front_; } else { // The region after active_ is allocated, so make a zero-sized // placeholder. MakeActivePlaceholder(); } return result; } // The active region can accommodate this allocation with room left over. // Create a new region (allocated) of the requested size at the front of the // active region, and adjust the active region to reflect the deficit. MOJO_DCHECK(active_->size > size); Region *allocated = get_free(true, size, active_->offset); active_->size -= size; active_->offset += size; insert_before(allocated, active_); return allocated->offset; } void FifoAllocator::ReleaseRegion(uint64_t size, uint64_t offset) { // Start at active_->next. That's usually the region we're looking for. bool released = Release(size, offset, active_->next, nullptr) || Release(size, offset, front_, active_); MOJO_DCHECK(released); } bool FifoAllocator::Release( uint64_t size, uint64_t offset, Region* begin, Region* end) { MOJO_DCHECK(begin != nullptr || end == nullptr); for (Region* region = begin; region != end; region = region->next) { if (region->offset == offset) { MOJO_DCHECK(region->allocated); MOJO_DCHECK(region->size == size); region->allocated = false; Region *prev = region->prev; if (prev != nullptr && !prev->allocated) { // Coalesce wtih the previous region. prev->size += region->size; remove(region); put_free(region); region = prev; } Region *next = region->next; if (next != nullptr && !next->allocated) { // Coalesce wtih the next region. next->offset = region->offset; next->size += region->size; if (active_ == region) { // This can happen if we coalesced the previous region. active_ = next; } remove(region); put_free(region); } return true; } } return false; } bool FifoAllocator::AdvanceActive(uint64_t size) { MOJO_DCHECK(size != 0); return AdvanceActive(size, active_->next, nullptr) || AdvanceActive(size, front_, active_); } bool FifoAllocator::AdvanceActive(uint64_t size, Region* begin, Region* end) { for (Region* region = begin; region != end; region = region->next) { if (!region->allocated && region->size >= size) { if (active_->size == 0) { // The old active region is zero-sized. Get rid of it. MOJO_DCHECK(!active_->allocated); remove(active_); put_free(active_); } active_ = region; return true; } } return false; } void FifoAllocator::MakeActivePlaceholder() { // If the old active region was at the back of the list, we'll be inserting // at the front, so make the offset zero. We insert at the front, because it's // a bit more efficient and because we don't need to implement insert_after. Region *new_active = get_free( false, 0, active_ == back_ ? 0 : active_->offset + active_->size); MOJO_DCHECK((active_ == back_) == (active_->next == nullptr)); insert_before(new_active, active_ == back_ ? front_ : active_->next); active_ = new_active; } void FifoAllocator::remove(Region* region) { MOJO_DCHECK(region); if (front_ == region) { MOJO_DCHECK(region->prev == nullptr); front_ = region->next; } else { MOJO_DCHECK(region->prev); MOJO_DCHECK(region->prev->next ==region); region->prev->next = region->next; } if (back_ == region) { MOJO_DCHECK(region->next == nullptr); back_ = region->prev; } else { MOJO_DCHECK(region->next); MOJO_DCHECK(region->next->prev == region); region->next->prev = region->prev; } } void FifoAllocator::insert_before(Region* region, Region* before_this) { MOJO_DCHECK(region); MOJO_DCHECK(before_this); region->prev = before_this->prev; before_this->prev = region; region->next = before_this; if (front_ == before_this) { MOJO_DCHECK(region->prev == nullptr); front_ = region; } else { MOJO_DCHECK(region->prev); region->prev->next = region; } } FifoAllocator::Region* FifoAllocator::get_free( bool allocated, uint64_t size, uint64_t offset) { MOJO_DCHECK(size <= size_); MOJO_DCHECK(offset <= size_ - size); Region *result = free_; if (result == nullptr) { result = new Region(); } else { free_ = free_->next; } result->allocated = allocated; result->size = size; result->offset = offset; return result; } void FifoAllocator::DeleteFrontToBack(Region* region) { while (region != nullptr) { Region *to_delete = region; region = region->next; delete to_delete; } } } // namespace media } // namespace mojo