| // 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. |
| |
| #ifndef BITMAP_RLE_BITMAP_H_ |
| #define BITMAP_RLE_BITMAP_H_ |
| |
| #include <stddef.h> |
| #include <zircon/types.h> |
| |
| #include <memory> |
| |
| #include <bitmap/bitmap.h> |
| #include <fbl/intrusive_double_list.h> |
| #include <fbl/macros.h> |
| |
| namespace bitmap { |
| |
| // A run-length encoded bitmap. |
| template <typename T> |
| class RleBitmapBase final : public Bitmap<T> { |
| public: |
| // Elements of the bitmap list |
| struct Element : public fbl::DoublyLinkedListable<std::unique_ptr<Element>> { |
| // The start of this run of 1-bits. |
| T bitoff; |
| // The number of 1-bits in this run. |
| T bitlen; |
| |
| // The (inclusive) start of this run of 1-bits. |
| T start() const { return bitoff; } |
| |
| // The (exclusive) end of this run of 1-bits. |
| T end() const { return bitoff + bitlen; } |
| }; |
| |
| private: |
| using ElementPtr = std::unique_ptr<Element>; |
| // Private forward-declaration to share the type between the iterator type |
| // and the internal list. |
| using ListType = fbl::DoublyLinkedList<ElementPtr>; |
| |
| public: |
| using const_iterator = typename ListType::const_iterator; |
| using FreeList = ListType; |
| |
| constexpr RleBitmapBase() : num_elems_(0), num_bits_(0) {} |
| |
| RleBitmapBase(RleBitmapBase&& rhs) noexcept = default; |
| RleBitmapBase& operator=(RleBitmapBase&& rhs) noexcept = default; |
| |
| // Returns the current number of ranges. |
| size_t num_ranges() const { return num_elems_; } |
| |
| // Returns the current number of bits. |
| T num_bits() const { return num_bits_; } |
| |
| zx_status_t Find(bool is_set, T bitoff, T bitmax, T run_len, T* out) const override; |
| |
| // Returns true if all the bits in [*bitoff*, *bitmax*) are set. Afterwards, |
| // *first_unset* will be set to the lesser of bitmax and the index of the |
| // first unset bit after *bitoff*. |
| bool Get(T bitoff, T bitmax, T* first_unset) const override; |
| using Bitmap<T>::Get; |
| |
| // Sets all bits in the range [*bitoff*, *bitmax*). Only fails on allocation |
| // error or if bitmax < bitoff. |
| zx_status_t Set(T bitoff, T bitmax) override; |
| |
| // Sets all bits in the range [*bitoff*, *bitmax*). Only fails if |
| // *bitmax* < *bitoff* or if an allocation is needed and *free_list* |
| // does not contain one. |
| // |
| // *free_list* is a list of usable allocations. If an allocation is needed, |
| // it will be drawn from it. This function is guaranteed to need at most |
| // one allocation. If any nodes need to be deleted, they will be appended |
| // to *free_list*. |
| zx_status_t SetNoAlloc(T bitoff, T bitmax, FreeList* free_list); |
| |
| // Clears all bits in the range [*bitoff*, *bitmax*). Only fails on allocation |
| // error or if bitmax < bitoff. |
| zx_status_t Clear(T bitoff, T bitmax) override; |
| |
| // Clear all bits in the range [*bitoff*, *bitmax*). Only fails if |
| // *bitmax* < *bitoff* or if an allocation is needed and *free_list* |
| // does not contain one. |
| // |
| // *free_list* is a list of usable allocations. If an allocation is needed, |
| // it will be drawn from it. This function is guaranteed to need at most |
| // one allocation. If any nodes need to be deleted, they will be appended |
| // to *free_list*. |
| zx_status_t ClearNoAlloc(T bitoff, T bitmax, FreeList* free_list); |
| |
| // Clear all bits in the bitmap. |
| void ClearAll() override; |
| |
| // Iterate over the ranges in the bitmap. Modifying the list while |
| // iterating over it may yield undefined results. |
| const_iterator cbegin() const { return elems_.cbegin(); } |
| const_iterator begin() const { return elems_.cbegin(); } |
| const_iterator end() const { return elems_.cend(); } |
| const_iterator cend() const { return elems_.cend(); } |
| |
| private: |
| static ElementPtr AllocateElement(RleBitmapBase::FreeList* free_list); |
| static void ReleaseElement(RleBitmapBase::FreeList* free_list, ElementPtr&& elem); |
| |
| zx_status_t SetInternal(T bitoff, T bitmax, FreeList* free_list); |
| zx_status_t ClearInternal(T bitoff, T bitmax, FreeList* free_list); |
| |
| // The ranges of the bitmap. Ranges are ordered by ascending |bitoff| value. |
| // When no "Set" operation is in progress, there should not be any overlapping ranges. |
| ListType elems_; |
| |
| // The number of ranges in elems_. |
| size_t num_elems_; |
| |
| // The number of total bits in elems_; i.e. the sum of the bitlen field of all stored |
| // Elements. |
| T num_bits_; |
| }; |
| |
| // -- Implementation -- |
| |
| // 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*. |
| template <typename T> |
| typename RleBitmapBase<T>::ElementPtr RleBitmapBase<T>::AllocateElement( |
| RleBitmapBase::FreeList* free_list) { |
| if (free_list) |
| return free_list->pop_front(); |
| |
| fbl::AllocChecker ac; |
| ElementPtr new_elem(new (&ac) Element); |
| if (!ac.check()) { |
| return ElementPtr(); |
| } |
| return new_elem; |
| } |
| |
| // 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*. |
| template <typename T> |
| void RleBitmapBase<T>::ReleaseElement(RleBitmapBase::FreeList* free_list, ElementPtr&& elem) { |
| if (free_list) { |
| free_list->push_back(std::move(elem)); |
| } |
| } |
| |
| template <typename T> |
| zx_status_t RleBitmapBase<T>::Find(bool is_set, T bitoff, T bitmax, T run_len, 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; |
| } |
| if (bitmax - bitoff < run_len) { |
| return ZX_ERR_NO_RESOURCES; |
| } |
| |
| T elem_min = std::max(bitoff, elem.bitoff); // Minimum valid bit within elem. |
| T elem_max = std::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; |
| } |
| |
| template <typename T> |
| bool RleBitmapBase<T>::Get(T bitoff, T bitmax, 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; |
| } |
| |
| template <typename T> |
| void RleBitmapBase<T>::ClearAll() { |
| elems_.clear(); |
| num_elems_ = 0; |
| num_bits_ = 0; |
| } |
| |
| template <typename T> |
| zx_status_t RleBitmapBase<T>::Set(T bitoff, T bitmax) { |
| return SetInternal(bitoff, bitmax, nullptr); |
| } |
| |
| template <typename T> |
| zx_status_t RleBitmapBase<T>::SetNoAlloc(T bitoff, T bitmax, FreeList* free_list) { |
| if (free_list == nullptr) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| return SetInternal(bitoff, bitmax, free_list); |
| } |
| |
| template <typename T> |
| zx_status_t RleBitmapBase<T>::Clear(T bitoff, T bitmax) { |
| return ClearInternal(bitoff, bitmax, nullptr); |
| } |
| |
| template <typename T> |
| zx_status_t RleBitmapBase<T>::ClearNoAlloc(T bitoff, T bitmax, FreeList* free_list) { |
| if (free_list == nullptr) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| return ClearInternal(bitoff, bitmax, free_list); |
| } |
| |
| template <typename T> |
| zx_status_t RleBitmapBase<T>::SetInternal(T bitoff, T bitmax, FreeList* free_list) { |
| if (bitmax < bitoff) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| const T bitlen = bitmax - bitoff; |
| if (bitlen == 0) { |
| return ZX_OK; |
| } |
| |
| ElementPtr 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 Element& 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; |
| Element& 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 |
| T max = elem.bitoff + elem.bitlen; |
| while (itr != elems_.end()) { |
| if (itr->bitoff > max) { |
| break; |
| } |
| |
| max = std::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; |
| } |
| |
| template <typename T> |
| zx_status_t RleBitmapBase<T>::ClearInternal(T bitoff, 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; |
| } |
| // '*itr' contains [bitoff, bitmax), and we need to split it. |
| ElementPtr 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; |
| } |
| 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; |
| } |
| // [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; |
| } |
| |
| using RleBitmap = RleBitmapBase<size_t>; |
| using RleBitmapElement = RleBitmap::Element; |
| |
| } // namespace bitmap |
| |
| #endif // BITMAP_RLE_BITMAP_H_ |