blob: 5a18c5a07919361909df21f252863c1ab3d5f843 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#ifndef ZIRCON_KERNEL_PHYS_LIB_MEMALLOC_ALGORITHM_H_
#define ZIRCON_KERNEL_PHYS_LIB_MEMALLOC_ALGORITHM_H_
#include <lib/fit/function.h>
#include <lib/fitx/result.h>
#include <lib/memalloc/range.h>
#include <lib/stdcompat/span.h>
#include <zircon/assert.h>
#include <cstddef>
#include <string_view>
namespace memalloc {
// Inlined for phys-compatibility, where there is no ambient heap.
using MemRangeCallback = fit::inline_function<bool(const MemRange&)>;
// Serializes ranges in lexicographic order from a variable number of MemRange arrays.
class MemRangeStream {
public:
// Assumes that each associated array is already in lexicographic ordered.
explicit MemRangeStream(cpp20::span<internal::MemRangeIterationContext> state) : state_(state) {
ZX_DEBUG_ASSERT(std::all_of(state.begin(), state.end(), [](const auto& ctx) {
return std::is_sorted(ctx.ranges_.begin(), ctx.ranges_.end());
}));
}
// Returns the next range in the stream, returning nullptr when all ranges
// have been streamed (until the stream itself has been reset).
const MemRange* operator()();
size_t size() const {
size_t size = 0;
for (const auto& ctx : state_) {
size += ctx.ranges_.size();
}
return size;
}
bool empty() const { return size() == 0; }
// Reset the head of the stream back to the beginning.
void reset() {
for (auto& ctx : state_) {
ctx.it_ = ctx.ranges_.begin();
}
}
private:
cpp20::span<internal::MemRangeIterationContext> state_;
};
// Say a range among a set is "normalized" if it does not intersect with any of
// them and it is maximally contiguous. This routine finds the normalized RAM
// ranges among a provided set with a degree of arbitrary intersection with one
// another. The range is emitted by passing it to a callback for processing.
// If the callback returns false, then the routine will exit early.
//
// The span of ranges will be reordered, but otherwise will be preserved.
//
// This function runs in O(n*log(n)) time, where n is the total number of given
// ranges.
//
void FindNormalizedRamRanges(MemRangeStream ranges, MemRangeCallback cb);
// Used for streamlined testing.
inline void FindNormalizedRamRanges(cpp20::span<MemRange> ranges, MemRangeCallback cb) {
internal::MemRangeIterationContext ctx(ranges);
FindNormalizedRamRanges(MemRangeStream({&ctx, 1}), std::move(cb));
}
// The size of tne void* scratch space needed for FindNormalizedRanges() below,
// where `n` is the size of the input MemRangeStream.
constexpr size_t FindNormalizedRangesScratchSize(size_t n) { return 4 * n; }
// A variant of the above algorithm that finds all of the normalized ranges in
// order. It also runs in O(n*log(n)) time but with O(n) space. In particular,
// a void* buffer of scratch of size `FindNormalizedRangesScratchSize()` must
// be.
//
// Ranges may overlap only if they are of the same type or one type is
// kFreeRam; otherwise fitx::failed is returned.
fitx::result<fitx::failed> FindNormalizedRanges(MemRangeStream ranges, cpp20::span<void*> scratch,
MemRangeCallback cb);
// Used for streamlined testing.
inline fitx::result<fitx::failed> FindNormalizedRanges(cpp20::span<MemRange> ranges,
cpp20::span<void*> scratch,
MemRangeCallback cb) {
internal::MemRangeIterationContext ctx(ranges);
return FindNormalizedRanges(MemRangeStream({&ctx, 1}), scratch, std::move(cb));
}
} // namespace memalloc
#endif // ZIRCON_KERNEL_PHYS_LIB_MEMALLOC_ALGORITHM_H_