Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#ifndef mozilla_interceptor_RangeMap_h
#define mozilla_interceptor_RangeMap_h
#include "mozilla/Attributes.h"
#include "mozilla/Maybe.h"
#include "mozilla/mozalloc.h"
#include "mozilla/Span.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Vector.h"
#include <algorithm>
namespace mozilla {
namespace interceptor {
/**
* This class maintains a vector of VMSharingPolicyUnique objects, sorted on
* the memory range that is used for reserving each object.
*
* This is used by VMSharingPolicyShared for creating and looking up VM regions
* that are within proximity of the applicable range.
*
* VMSharingPolicyUnique objects managed by this class are reused whenever
* possible. If no range is required, we just return the first available
* policy.
*
* If no range is required and no policies have yet been allocated, we create
* a new one with a null range as a default.
*/
template <typename MMPolicyT>
class RangeMap final {
private:
/**
* This class is used as the comparison key for sorting and insertion.
*/
class Range {
public:
constexpr Range() : mBase(0), mLimit(0) {}
explicit Range(const Maybe<Span<const uint8_t>>& aBounds)
: mBase(aBounds ? reinterpret_cast<const uintptr_t>(
MMPolicyT::GetLowerBound(aBounds.ref()))
: 0),
mLimit(aBounds ? reinterpret_cast<const uintptr_t>(
MMPolicyT::GetUpperBoundIncl(aBounds.ref()))
: 0) {}
Range& operator=(const Range&) = default;
Range(const Range&) = default;
Range(Range&&) = default;
Range& operator=(Range&&) = default;
bool operator<(const Range& aOther) const {
return mBase < aOther.mBase ||
(mBase == aOther.mBase && mLimit < aOther.mLimit);
}
bool Contains(const Range& aOther) const {
return mBase <= aOther.mBase && mLimit >= aOther.mLimit;
}
private:
uintptr_t mBase;
uintptr_t mLimit;
};
class PolicyInfo final : public Range {
public:
explicit PolicyInfo(const Range& aRange)
: Range(aRange),
mPolicy(MakeUnique<VMSharingPolicyUnique<MMPolicyT>>()) {}
PolicyInfo(const PolicyInfo&) = delete;
PolicyInfo& operator=(const PolicyInfo&) = delete;
PolicyInfo(PolicyInfo&& aOther) = default;
PolicyInfo& operator=(PolicyInfo&& aOther) = default;
VMSharingPolicyUnique<MMPolicyT>* GetPolicy() { return mPolicy.get(); }
private:
UniquePtr<VMSharingPolicyUnique<MMPolicyT>> mPolicy;
};
using VectorType = Vector<PolicyInfo, 0, InfallibleAllocPolicy>;
public:
constexpr RangeMap() : mPolicies(nullptr) {}
VMSharingPolicyUnique<MMPolicyT>* GetPolicy(
const Maybe<Span<const uint8_t>>& aBounds) {
Range testRange(aBounds);
if (!mPolicies) {
mPolicies = new VectorType();
}
// If no bounds are specified, we just use the first available policy
if (!aBounds) {
if (mPolicies->empty()) {
if (!mPolicies->append(PolicyInfo(testRange))) {
return nullptr;
}
}
return GetFirstPolicy();
}
// mPolicies is sorted, so we search
auto itr =
std::lower_bound(mPolicies->begin(), mPolicies->end(), testRange);
if (itr != mPolicies->end() && itr->Contains(testRange)) {
return itr->GetPolicy();
}
itr = mPolicies->insert(itr, PolicyInfo(testRange));
MOZ_ASSERT(std::is_sorted(mPolicies->begin(), mPolicies->end()));
return itr->GetPolicy();
}
private:
VMSharingPolicyUnique<MMPolicyT>* GetFirstPolicy() {
MOZ_RELEASE_ASSERT(mPolicies && !mPolicies->empty());
return mPolicies->begin()->GetPolicy();
}
private:
VectorType* mPolicies;
};
} // namespace interceptor
} // namespace mozilla
#endif // mozilla_interceptor_RangeMap_h