blob: b0a1d33c9877e4f9c0ead1a1115b6a91090d29f9 [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>
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*.
fbl::unique_ptr<RleBitmapElement> AllocateElement(RleBitmap::FreeList* free_list) {
if (!free_list) {
fbl::AllocChecker ac;
fbl::unique_ptr<RleBitmapElement> new_elem(new (&ac) RleBitmapElement());
if (!ac.check()) {
return fbl::unique_ptr<RleBitmapElement>();
}
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, fbl::unique_ptr<RleBitmapElement>&& elem) {
if (free_list) {
free_list->push_back(fbl::move(elem));
}
}
} // namespace
bool RleBitmap::Get(size_t bitoff, size_t bitmax, size_t* first_unset) const {
for (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;
}
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;
}
fbl::unique_ptr<RleBitmapElement> 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, fbl::move(new_elem));
// 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.bitlen + 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;
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);
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'.
itr->bitlen = bitoff - itr->bitoff;
++itr;
continue;
} else {
// '*itr' contains [bitoff, bitmax), and we need to split it.
fbl::unique_ptr<RleBitmapElement> 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, fbl::move(new_elem));
itr->bitlen = bitoff - itr->bitoff;
break;
}
} else {
if (bitmax < itr->bitoff + itr->bitlen) {
// 'elem' contains 'bitmax'
itr->bitlen = itr->bitoff + itr->bitlen - bitmax;
itr->bitoff = bitmax;
break;
} else {
// [bitoff, bitmax) fully contains '*itr'.
auto to_erase = itr++;
ReleaseElement(free_list, elems_.erase(to_erase));
--num_elems_;
}
}
}
return ZX_OK;
}
} // namespace bitmap