blob: fbdeaf75846bfa2e172928f6431a35e8d67ad31c [file] [log] [blame]
// Copyright 2016 The Fuchsia 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 <bitmap/rle-bitmap.h>
#include <stddef.h>
#include <zircon/errors.h>
#include <zircon/types.h>
#include <fbl/algorithm.h>
#include <fbl/alloc_checker.h>
#include <utility>
namespace bitmap {
namespace {
// Allocate a new bitmap element. If *free_list* is null, allocate one using
// new. If *free_list* is not null, take one from *free_list*.
RleBitmapElementPtr AllocateElement(RleBitmap::FreeList* free_list) {
if (!free_list) {
fbl::AllocChecker ac;
RleBitmapElementPtr new_elem(new (&ac) RleBitmapElement());
if (!ac.check()) {
return RleBitmapElementPtr();
}
return new_elem;
} else {
return free_list->pop_front();
}
}
// Release the element *elem*. If *free_list* is null, release the element
// with delete. If *free_list* is not null, append it to *free_list*.
void ReleaseElement(RleBitmap::FreeList* free_list, RleBitmapElementPtr&& elem) {
if (free_list) {
free_list->push_back(std::move(elem));
}
}
} // namespace
zx_status_t RleBitmap::Find(bool is_set, size_t bitoff, size_t bitmax, size_t run_len, size_t* out)
const {
*out = bitmax;
// Loop through all existing elems to try to find a |run_len| length range of |is_set| bits.
// On each loop, |bitoff| is guaranteed to be either within the current elem, or in the range
// of unset bits leading up to it.
// Therefore, we can check whether |run_len| bits between |bitmax| and |bitoff| exist before
// the start of the elem (for unset runs), or within the current elem (for set runs).
for (const auto& elem : elems_) {
if (bitoff >= elem.end()) {
continue;
} else if (bitmax - bitoff < run_len) {
return ZX_ERR_NO_RESOURCES;
}
size_t elem_min = fbl::max(bitoff, elem.bitoff); // Minimum valid bit within elem.
size_t elem_max = fbl::min(bitmax, elem.end()); // Maximum valid bit within elem.
if (is_set && elem_max > elem_min && elem_max - elem_min >= run_len) {
// This element contains at least |run_len| bits
// which are between |bitoff| and |bitmax|.
*out = elem_min;
return ZX_OK;
}
if (!is_set && bitoff < elem.bitoff && elem.bitoff - bitoff >= run_len) {
// There are at least |run_len| bits between |bitoff| and the beginning of this element.
*out = bitoff;
return ZX_OK;
}
if (bitmax < elem.end()) {
// We have not found a valid run, and the specified range
// does not extend past this element.
return ZX_ERR_NO_RESOURCES;
}
// Update bitoff to the next value we want to check within the range.
bitoff = elem.end();
}
if (!is_set && bitmax - bitoff >= run_len) {
// We have not found an element with bits > bitoff, which means there is an infinite unset
// range starting at bitoff.
*out = bitoff;
return ZX_OK;
}
return ZX_ERR_NO_RESOURCES;
}
bool RleBitmap::Get(size_t bitoff, size_t bitmax, size_t* first_unset) const {
for (const auto& elem : elems_) {
if (bitoff < elem.bitoff) {
break;
}
if (bitoff < elem.bitoff + elem.bitlen) {
bitoff = elem.bitoff + elem.bitlen;
break;
}
}
if (bitoff > bitmax) {
bitoff = bitmax;
}
if (first_unset) {
*first_unset = bitoff;
}
return bitoff == bitmax;
}
void RleBitmap::ClearAll() {
elems_.clear();
num_elems_ = 0;
num_bits_ = 0;
}
zx_status_t RleBitmap::Set(size_t bitoff, size_t bitmax) {
return SetInternal(bitoff, bitmax, nullptr);
}
zx_status_t RleBitmap::SetNoAlloc(size_t bitoff, size_t bitmax, FreeList* free_list) {
if (free_list == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
return SetInternal(bitoff, bitmax, free_list);
}
zx_status_t RleBitmap::Clear(size_t bitoff, size_t bitmax) {
return ClearInternal(bitoff, bitmax, nullptr);
}
zx_status_t RleBitmap::ClearNoAlloc(size_t bitoff, size_t bitmax, FreeList* free_list) {
if (free_list == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
return ClearInternal(bitoff, bitmax, free_list);
}
zx_status_t RleBitmap::SetInternal(size_t bitoff, size_t bitmax, FreeList* free_list) {
if (bitmax < bitoff) {
return ZX_ERR_INVALID_ARGS;
}
const size_t bitlen = bitmax - bitoff;
if (bitlen == 0) {
return ZX_OK;
}
RleBitmapElementPtr new_elem = AllocateElement(free_list);
if (!new_elem) {
return ZX_ERR_NO_MEMORY;
}
++num_elems_;
new_elem->bitoff = bitoff;
new_elem->bitlen = bitlen;
auto ends_after = elems_.find_if([bitoff](const RleBitmapElement& elem) -> bool {
return elem.bitoff + elem.bitlen >= bitoff;
});
// Insert the new element before the first node that ends at a point >=
// when we begin.
elems_.insert(ends_after, std::move(new_elem));
num_bits_ += bitlen;
// If ends_after was the end of the list, there is no merging to do.
if (ends_after == elems_.end()) {
return ZX_OK;
}
auto itr = ends_after;
RleBitmapElement& elem = *--ends_after;
if (elem.bitoff >= itr->bitoff) {
// Our range either starts before or in the middle/end of *elem*.
// Adjust it so it starts at the same place as *elem*, to allow
// the merge logic to not consider this overlap case.
elem.bitlen += elem.bitoff - itr->bitoff;
num_bits_ += elem.bitoff - itr->bitoff;
elem.bitoff = itr->bitoff;
}
// Walk forwards and remove/merge any overlaps
size_t max = elem.bitoff + elem.bitlen;
while (itr != elems_.end()) {
if (itr->bitoff > max) {
break;
}
max = fbl::max(max, itr->bitoff + itr->bitlen);
num_bits_ += max - elem.bitoff - itr->bitlen - elem.bitlen;
elem.bitlen = max - elem.bitoff;
auto to_erase = itr;
++itr;
ReleaseElement(free_list, elems_.erase(to_erase));
--num_elems_;
}
return ZX_OK;
}
zx_status_t RleBitmap::ClearInternal(size_t bitoff, size_t bitmax, FreeList* free_list) {
if (bitmax < bitoff) {
return ZX_ERR_INVALID_ARGS;
}
if (bitmax - bitoff == 0) {
return ZX_OK;
}
auto itr = elems_.begin();
while (itr != elems_.end()) {
if (itr->bitoff + itr->bitlen < bitoff) {
++itr;
continue;
}
if (bitmax < itr->bitoff) {
break;
}
if (itr->bitoff < bitoff) {
if (itr->bitoff + itr->bitlen <= bitmax) {
// '*itr' contains 'bitoff'.
num_bits_ -= (itr->bitlen - (bitoff - itr->bitoff));
itr->bitlen = bitoff - itr->bitoff;
++itr;
continue;
} else {
// '*itr' contains [bitoff, bitmax), and we need to split it.
RleBitmapElementPtr new_elem = AllocateElement(free_list);
if (!new_elem) {
return ZX_ERR_NO_MEMORY;
}
++num_elems_;
new_elem->bitoff = bitmax;
new_elem->bitlen = itr->bitoff + itr->bitlen - bitmax;
elems_.insert_after(itr, std::move(new_elem));
itr->bitlen = bitoff - itr->bitoff;
num_bits_ -= (bitmax - bitoff);
break;
}
} else {
if (bitmax < itr->bitoff + itr->bitlen) {
// 'elem' contains 'bitmax'
num_bits_ -= (bitmax - itr->bitoff);
itr->bitlen = itr->bitoff + itr->bitlen - bitmax;
itr->bitoff = bitmax;
break;
} else {
// [bitoff, bitmax) fully contains '*itr'.
num_bits_ -= itr->bitlen;
auto to_erase = itr++;
ReleaseElement(free_list, elems_.erase(to_erase));
--num_elems_;
}
}
}
return ZX_OK;
}
} // namespace bitmap