blob: 510d3d49b7290b02446e0df2b977facf305cd3eb [file] [log] [blame]
//
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// HandleRangeAllocator.cpp : Implementation for HandleRangeAllocator.h
#include "libANGLE/HandleRangeAllocator.h"
#include <algorithm>
#include <limits>
#include <utility>
#include "common/angleutils.h"
#include "common/debug.h"
namespace gl
{
const GLuint HandleRangeAllocator::kInvalidHandle = 0;
HandleRangeAllocator::HandleRangeAllocator()
{
// Simplify the code by making sure that lower_bound(id) never
// returns the beginning of the map, if id is valid (eg != kInvalidHandle).
mUsed.insert(std::make_pair(0u, 0u));
}
HandleRangeAllocator::~HandleRangeAllocator() {}
GLuint HandleRangeAllocator::allocate()
{
return allocateRange(1u);
}
GLuint HandleRangeAllocator::allocateAtOrAbove(GLuint wanted)
{
if (wanted == 0u || wanted == 1u)
return allocateRange(1u);
auto current = mUsed.lower_bound(wanted);
auto next = current;
if (current == mUsed.end() || current->first > wanted)
{
current--;
}
else
{
next++;
}
GLuint firstId = current->first;
GLuint lastId = current->second;
ASSERT(wanted >= firstId);
if (wanted - 1u <= lastId)
{
// Append to current range.
lastId++;
if (lastId == 0)
{
// The increment overflowed.
return allocateRange(1u);
}
current->second = lastId;
if (next != mUsed.end() && next->first - 1u == lastId)
{
// Merge with next range.
current->second = next->second;
mUsed.erase(next);
}
return lastId;
}
else if (next != mUsed.end() && next->first - 1u == wanted)
{
// Prepend to next range.
GLuint lastExisting = next->second;
mUsed.erase(next);
mUsed.insert(std::make_pair(wanted, lastExisting));
return wanted;
}
mUsed.insert(std::make_pair(wanted, wanted));
return wanted;
}
GLuint HandleRangeAllocator::allocateRange(GLuint range)
{
ASSERT(range != 0);
auto current = mUsed.begin();
auto next = current;
while (++next != mUsed.end())
{
if (next->first - current->second > range)
break;
current = next;
}
const GLuint firstId = current->second + 1u;
const GLuint lastId = firstId + range - 1u;
// deal with wraparound
if (firstId == 0u || lastId < firstId)
return kInvalidHandle;
current->second = lastId;
if (next != mUsed.end() && next->first - 1u == lastId)
{
// merge with next range
current->second = next->second;
mUsed.erase(next);
}
return firstId;
}
bool HandleRangeAllocator::markAsUsed(GLuint handle)
{
ASSERT(handle);
auto current = mUsed.lower_bound(handle);
if (current != mUsed.end() && current->first == handle)
return false;
auto next = current;
--current;
if (current->second >= handle)
return false;
ASSERT(current->first < handle && current->second < handle);
if (current->second + 1u == handle)
{
// Append to current range.
current->second = handle;
if (next != mUsed.end() && next->first - 1u == handle)
{
// Merge with next range.
current->second = next->second;
mUsed.erase(next);
}
return true;
}
else if (next != mUsed.end() && next->first - 1u == handle)
{
// Prepend to next range.
GLuint lastExisting = next->second;
mUsed.erase(next);
mUsed.insert(std::make_pair(handle, lastExisting));
return true;
}
mUsed.insert(std::make_pair(handle, handle));
return true;
}
void HandleRangeAllocator::release(GLuint handle)
{
releaseRange(handle, 1u);
}
void HandleRangeAllocator::releaseRange(GLuint first, GLuint range)
{
if (range == 0u || (first == 0u && range == 1u))
return;
if (first == 0u)
{
first++;
range--;
}
GLuint last = first + range - 1u;
if (last < first)
last = std::numeric_limits<GLuint>::max();
while (true)
{
auto current = mUsed.lower_bound(last);
if (current == mUsed.end() || current->first > last)
--current;
if (current->second < first)
return;
if (current->first >= first)
{
const GLuint lastExisting = current->second;
mUsed.erase(current);
if (last < lastExisting)
{
mUsed.insert(std::make_pair(last + 1u, lastExisting));
}
}
else if (current->second <= last)
{
current->second = first - 1u;
}
else
{
ASSERT(current->first < first && current->second > last);
const GLuint lastExisting = current->second;
current->second = first - 1u;
mUsed.insert(std::make_pair(last + 1u, lastExisting));
}
}
}
bool HandleRangeAllocator::isUsed(GLuint handle) const
{
if (handle == kInvalidHandle)
return false;
auto current = mUsed.lower_bound(handle);
if (current != mUsed.end())
{
if (current->first == handle)
return true;
}
--current;
return current->second >= handle;
}
} // namespace gl