summaryrefslogtreecommitdiffstats
path: root/mozglue/misc/interceptor/RangeMap.h
blob: d45d031613614e0a610d747f210937661ac3f46a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/* -*- 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