tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

RangedPtr.h (7832B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /*
      8 * Implements a smart pointer asserted to remain within a range specified at
      9 * construction.
     10 */
     11 
     12 #ifndef mozilla_RangedPtr_h
     13 #define mozilla_RangedPtr_h
     14 
     15 #include "mozilla/ArrayUtils.h"
     16 #include "mozilla/Assertions.h"
     17 #include "mozilla/Attributes.h"
     18 
     19 #include <cstddef>
     20 #include <cstdint>
     21 
     22 namespace mozilla {
     23 
     24 /*
     25 * RangedPtr is a smart pointer restricted to an address range specified at
     26 * creation.  The pointer (and any smart pointers derived from it) must remain
     27 * within the range [start, end] (inclusive of end to facilitate use as
     28 * sentinels).  Dereferencing or indexing into the pointer (or pointers derived
     29 * from it) must remain within the range [start, end).  All the standard pointer
     30 * operators are defined on it; in debug builds these operations assert that the
     31 * range specified at construction is respected.
     32 *
     33 * In theory passing a smart pointer instance as an argument can be slightly
     34 * slower than passing a T* (due to ABI requirements for passing structs versus
     35 * passing pointers), if the method being called isn't inlined.  If you are in
     36 * extremely performance-critical code, you may want to be careful using this
     37 * smart pointer as an argument type.
     38 *
     39 * RangedPtr<T> intentionally does not implicitly convert to T*.  Use get() to
     40 * explicitly convert to T*.  Keep in mind that the raw pointer of course won't
     41 * implement bounds checking in debug builds.
     42 */
     43 template <typename T>
     44 class RangedPtr {
     45  template <typename U>
     46  friend class RangedPtr;
     47 
     48  T* mPtr;
     49 
     50 #if defined(DEBUG) || defined(FUZZING)
     51  T* const mRangeStart;
     52  T* const mRangeEnd;
     53 #endif
     54 
     55  void checkSanity() {
     56    MOZ_ASSERT_DEBUG_OR_FUZZING(mRangeStart <= mPtr);
     57    MOZ_ASSERT_DEBUG_OR_FUZZING(mPtr <= mRangeEnd);
     58  }
     59 
     60  /* Creates a new pointer for |aPtr|, restricted to this pointer's range. */
     61  RangedPtr<T> create(T* aPtr) const {
     62 #if defined(DEBUG) || defined(FUZZING)
     63    return RangedPtr<T>(aPtr, mRangeStart, mRangeEnd);
     64 #else
     65    return RangedPtr<T>(aPtr, nullptr, nullptr);
     66 #endif
     67  }
     68 
     69  uintptr_t asUintptr() const { return reinterpret_cast<uintptr_t>(mPtr); }
     70 
     71 public:
     72  RangedPtr(T* aPtr, T* aStart, T* aEnd)
     73      : mPtr(aPtr)
     74 #if defined(DEBUG) || defined(FUZZING)
     75        ,
     76        mRangeStart(aStart),
     77        mRangeEnd(aEnd)
     78 #endif
     79  {
     80    MOZ_ASSERT_DEBUG_OR_FUZZING(mRangeStart <= mRangeEnd);
     81    checkSanity();
     82  }
     83 
     84  RangedPtr(T* aPtr, T* aStart, size_t aLength)
     85      : RangedPtr(aPtr, aStart, aStart + aLength) {
     86    /* Extra overflow check. */
     87    MOZ_ASSERT_DEBUG_OR_FUZZING(aLength <= size_t(-1) / sizeof(T));
     88  }
     89 
     90  RangedPtr(T* aPtr, size_t aLength) : RangedPtr(aPtr, aPtr, aLength) {}
     91 
     92  template <size_t N>
     93  explicit RangedPtr(T (&aArr)[N]) : RangedPtr(aArr, aArr, N) {}
     94 
     95  RangedPtr(const RangedPtr& aOther)
     96 #if defined(DEBUG) || defined(FUZZING)
     97      : RangedPtr(aOther.mPtr, aOther.mRangeStart, aOther.mRangeEnd)
     98 #else
     99      : RangedPtr(aOther.mPtr, nullptr, nullptr)
    100 #endif
    101  {
    102  }
    103 
    104  template <typename U>
    105  MOZ_IMPLICIT RangedPtr(const RangedPtr<U>& aOther)
    106 #if defined(DEBUG) || defined(FUZZING)
    107      : RangedPtr(aOther.mPtr, aOther.mRangeStart, aOther.mRangeEnd)
    108 #else
    109      : RangedPtr(aOther.mPtr, nullptr, nullptr)
    110 #endif
    111  {
    112  }
    113 
    114  T* get() const { return mPtr; }
    115 
    116  explicit operator bool() const { return mPtr != nullptr; }
    117 
    118  void checkIdenticalRange(const RangedPtr<T>& aOther) const {
    119    MOZ_ASSERT_DEBUG_OR_FUZZING(mRangeStart == aOther.mRangeStart);
    120    MOZ_ASSERT_DEBUG_OR_FUZZING(mRangeEnd == aOther.mRangeEnd);
    121  }
    122 
    123  template <typename U>
    124  RangedPtr<U> ReinterpretCast() const {
    125 #if defined(DEBUG) || defined(FUZZING)
    126    return {reinterpret_cast<U*>(mPtr), reinterpret_cast<U*>(mRangeStart),
    127            reinterpret_cast<U*>(mRangeEnd)};
    128 #else
    129    return {reinterpret_cast<U*>(mPtr), nullptr, nullptr};
    130 #endif
    131  }
    132 
    133  /*
    134   * You can only assign one RangedPtr into another if the two pointers have
    135   * the same valid range:
    136   *
    137   *   char arr1[] = "hi";
    138   *   char arr2[] = "bye";
    139   *   RangedPtr<char> p1(arr1, 2);
    140   *   p1 = RangedPtr<char>(arr1 + 1, arr1, arr1 + 2); // works
    141   *   p1 = RangedPtr<char>(arr2, 3);                  // asserts
    142   */
    143  RangedPtr<T>& operator=(const RangedPtr<T>& aOther) {
    144    checkIdenticalRange(aOther);
    145    mPtr = aOther.mPtr;
    146    checkSanity();
    147    return *this;
    148  }
    149 
    150  RangedPtr<T> operator+(size_t aInc) const {
    151    MOZ_ASSERT_DEBUG_OR_FUZZING(aInc <= size_t(-1) / sizeof(T));
    152    MOZ_ASSERT_DEBUG_OR_FUZZING(asUintptr() + aInc * sizeof(T) >= asUintptr());
    153    return create(mPtr + aInc);
    154  }
    155 
    156  RangedPtr<T> operator-(size_t aDec) const {
    157    MOZ_ASSERT_DEBUG_OR_FUZZING(aDec <= size_t(-1) / sizeof(T));
    158    MOZ_ASSERT_DEBUG_OR_FUZZING(asUintptr() - aDec * sizeof(T) <= asUintptr());
    159    return create(mPtr - aDec);
    160  }
    161 
    162  /*
    163   * You can assign a raw pointer into a RangedPtr if the raw pointer is
    164   * within the range specified at creation.
    165   */
    166  template <typename U>
    167  RangedPtr<T>& operator=(U* aPtr) {
    168    *this = create(aPtr);
    169    return *this;
    170  }
    171 
    172  template <typename U>
    173  RangedPtr<T>& operator=(const RangedPtr<U>& aPtr) {
    174    MOZ_ASSERT_DEBUG_OR_FUZZING(mRangeStart <= aPtr.mPtr);
    175    MOZ_ASSERT_DEBUG_OR_FUZZING(aPtr.mPtr <= mRangeEnd);
    176    mPtr = aPtr.mPtr;
    177    checkSanity();
    178    return *this;
    179  }
    180 
    181  RangedPtr<T>& operator++() { return (*this += 1); }
    182 
    183  RangedPtr<T> operator++(int) {
    184    RangedPtr<T> rcp = *this;
    185    ++*this;
    186    return rcp;
    187  }
    188 
    189  RangedPtr<T>& operator--() { return (*this -= 1); }
    190 
    191  RangedPtr<T> operator--(int) {
    192    RangedPtr<T> rcp = *this;
    193    --*this;
    194    return rcp;
    195  }
    196 
    197  RangedPtr<T>& operator+=(size_t aInc) {
    198    *this = *this + aInc;
    199    return *this;
    200  }
    201 
    202  RangedPtr<T>& operator-=(size_t aDec) {
    203    *this = *this - aDec;
    204    return *this;
    205  }
    206 
    207  T& operator[](ptrdiff_t aIndex) const {
    208    MOZ_ASSERT_DEBUG_OR_FUZZING(size_t(aIndex > 0 ? aIndex : -aIndex) <=
    209                                size_t(-1) / sizeof(T));
    210    return *create(mPtr + aIndex);
    211  }
    212 
    213  T& operator*() const {
    214    MOZ_ASSERT_DEBUG_OR_FUZZING(mPtr >= mRangeStart);
    215    MOZ_ASSERT_DEBUG_OR_FUZZING(mPtr < mRangeEnd);
    216    return *mPtr;
    217  }
    218 
    219  T* operator->() const {
    220    MOZ_ASSERT_DEBUG_OR_FUZZING(mPtr >= mRangeStart);
    221    MOZ_ASSERT_DEBUG_OR_FUZZING(mPtr < mRangeEnd);
    222    return mPtr;
    223  }
    224 
    225  template <typename U>
    226  bool operator==(const RangedPtr<U>& aOther) const {
    227    return mPtr == aOther.mPtr;
    228  }
    229  template <typename U>
    230  bool operator!=(const RangedPtr<U>& aOther) const {
    231    return !(*this == aOther);
    232  }
    233 
    234  template <typename U>
    235  bool operator==(const U* u) const {
    236    return mPtr == u;
    237  }
    238  template <typename U>
    239  bool operator!=(const U* u) const {
    240    return !(*this == u);
    241  }
    242 
    243  bool operator==(std::nullptr_t) const { return mPtr == nullptr; }
    244  bool operator!=(std::nullptr_t) const { return mPtr != nullptr; }
    245 
    246  template <typename U>
    247  bool operator<(const RangedPtr<U>& aOther) const {
    248    return mPtr < aOther.mPtr;
    249  }
    250  template <typename U>
    251  bool operator<=(const RangedPtr<U>& aOther) const {
    252    return mPtr <= aOther.mPtr;
    253  }
    254 
    255  template <typename U>
    256  bool operator>(const RangedPtr<U>& aOther) const {
    257    return mPtr > aOther.mPtr;
    258  }
    259  template <typename U>
    260  bool operator>=(const RangedPtr<U>& aOther) const {
    261    return mPtr >= aOther.mPtr;
    262  }
    263 
    264  size_t operator-(const RangedPtr<T>& aOther) const {
    265    MOZ_ASSERT_DEBUG_OR_FUZZING(mPtr >= aOther.mPtr);
    266    return PointerRangeSize(aOther.mPtr, mPtr);
    267  }
    268 
    269 private:
    270  RangedPtr() = delete;
    271 };
    272 
    273 } /* namespace mozilla */
    274 
    275 #endif /* mozilla_RangedPtr_h */