| /* -*- 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 http://mozilla.org/MPL/2.0/. */ |
| |
| /* |
| * Implements a smart pointer asserted to remain within a range specified at |
| * construction. |
| */ |
| |
| #ifndef mozilla_RangedPtr_h |
| #define mozilla_RangedPtr_h |
| |
| #include "mozilla/ArrayUtils.h" |
| #include "mozilla/Assertions.h" |
| #include "mozilla/Attributes.h" |
| |
| #include <stdint.h> |
| |
| namespace mozilla { |
| |
| /* |
| * RangedPtr is a smart pointer restricted to an address range specified at |
| * creation. The pointer (and any smart pointers derived from it) must remain |
| * within the range [start, end] (inclusive of end to facilitate use as |
| * sentinels). Dereferencing or indexing into the pointer (or pointers derived |
| * from it) must remain within the range [start, end). All the standard pointer |
| * operators are defined on it; in debug builds these operations assert that the |
| * range specified at construction is respected. |
| * |
| * In theory passing a smart pointer instance as an argument can be slightly |
| * slower than passing a T* (due to ABI requirements for passing structs versus |
| * passing pointers), if the method being called isn't inlined. If you are in |
| * extremely performance-critical code, you may want to be careful using this |
| * smart pointer as an argument type. |
| * |
| * RangedPtr<T> intentionally does not implicitly convert to T*. Use get() to |
| * explicitly convert to T*. Keep in mind that the raw pointer of course won't |
| * implement bounds checking in debug builds. |
| */ |
| template<typename T> |
| class RangedPtr |
| { |
| T* mPtr; |
| |
| #ifdef DEBUG |
| T* const mRangeStart; |
| T* const mRangeEnd; |
| #endif |
| |
| void checkSanity() |
| { |
| MOZ_ASSERT(mRangeStart <= mPtr); |
| MOZ_ASSERT(mPtr <= mRangeEnd); |
| } |
| |
| /* Creates a new pointer for |aPtr|, restricted to this pointer's range. */ |
| RangedPtr<T> create(T* aPtr) const |
| { |
| #ifdef DEBUG |
| return RangedPtr<T>(aPtr, mRangeStart, mRangeEnd); |
| #else |
| return RangedPtr<T>(aPtr, nullptr, size_t(0)); |
| #endif |
| } |
| |
| uintptr_t asUintptr() const { return reinterpret_cast<uintptr_t>(mPtr); } |
| |
| public: |
| RangedPtr(T* aPtr, T* aStart, T* aEnd) |
| : mPtr(aPtr) |
| #ifdef DEBUG |
| , mRangeStart(aStart), mRangeEnd(aEnd) |
| #endif |
| { |
| MOZ_ASSERT(mRangeStart <= mRangeEnd); |
| checkSanity(); |
| } |
| RangedPtr(T* aPtr, T* aStart, size_t aLength) |
| : mPtr(aPtr) |
| #ifdef DEBUG |
| , mRangeStart(aStart), mRangeEnd(aStart + aLength) |
| #endif |
| { |
| MOZ_ASSERT(aLength <= size_t(-1) / sizeof(T)); |
| MOZ_ASSERT(reinterpret_cast<uintptr_t>(mRangeStart) + aLength * sizeof(T) >= |
| reinterpret_cast<uintptr_t>(mRangeStart)); |
| checkSanity(); |
| } |
| |
| /* Equivalent to RangedPtr(aPtr, aPtr, aLength). */ |
| RangedPtr(T* aPtr, size_t aLength) |
| : mPtr(aPtr) |
| #ifdef DEBUG |
| , mRangeStart(aPtr), mRangeEnd(aPtr + aLength) |
| #endif |
| { |
| MOZ_ASSERT(aLength <= size_t(-1) / sizeof(T)); |
| MOZ_ASSERT(reinterpret_cast<uintptr_t>(mRangeStart) + aLength * sizeof(T) >= |
| reinterpret_cast<uintptr_t>(mRangeStart)); |
| checkSanity(); |
| } |
| |
| /* Equivalent to RangedPtr(aArr, aArr, N). */ |
| template<size_t N> |
| explicit RangedPtr(T (&aArr)[N]) |
| : mPtr(aArr) |
| #ifdef DEBUG |
| , mRangeStart(aArr), mRangeEnd(aArr + N) |
| #endif |
| { |
| checkSanity(); |
| } |
| |
| T* get() const { return mPtr; } |
| |
| explicit operator bool() const { return mPtr != nullptr; } |
| |
| /* |
| * You can only assign one RangedPtr into another if the two pointers have |
| * the same valid range: |
| * |
| * char arr1[] = "hi"; |
| * char arr2[] = "bye"; |
| * RangedPtr<char> p1(arr1, 2); |
| * p1 = RangedPtr<char>(arr1 + 1, arr1, arr1 + 2); // works |
| * p1 = RangedPtr<char>(arr2, 3); // asserts |
| */ |
| RangedPtr<T>& operator=(const RangedPtr<T>& aOther) |
| { |
| MOZ_ASSERT(mRangeStart == aOther.mRangeStart); |
| MOZ_ASSERT(mRangeEnd == aOther.mRangeEnd); |
| mPtr = aOther.mPtr; |
| checkSanity(); |
| return *this; |
| } |
| |
| RangedPtr<T> operator+(size_t aInc) |
| { |
| MOZ_ASSERT(aInc <= size_t(-1) / sizeof(T)); |
| MOZ_ASSERT(asUintptr() + aInc * sizeof(T) >= asUintptr()); |
| return create(mPtr + aInc); |
| } |
| |
| RangedPtr<T> operator-(size_t aDec) |
| { |
| MOZ_ASSERT(aDec <= size_t(-1) / sizeof(T)); |
| MOZ_ASSERT(asUintptr() - aDec * sizeof(T) <= asUintptr()); |
| return create(mPtr - aDec); |
| } |
| |
| /* |
| * You can assign a raw pointer into a RangedPtr if the raw pointer is |
| * within the range specified at creation. |
| */ |
| template <typename U> |
| RangedPtr<T>& operator=(U* aPtr) |
| { |
| *this = create(aPtr); |
| return *this; |
| } |
| |
| template <typename U> |
| RangedPtr<T>& operator=(const RangedPtr<U>& aPtr) |
| { |
| MOZ_ASSERT(mRangeStart <= aPtr.mPtr); |
| MOZ_ASSERT(aPtr.mPtr <= mRangeEnd); |
| mPtr = aPtr.mPtr; |
| checkSanity(); |
| return *this; |
| } |
| |
| RangedPtr<T>& operator++() |
| { |
| return (*this += 1); |
| } |
| |
| RangedPtr<T> operator++(int) |
| { |
| RangedPtr<T> rcp = *this; |
| ++*this; |
| return rcp; |
| } |
| |
| RangedPtr<T>& operator--() |
| { |
| return (*this -= 1); |
| } |
| |
| RangedPtr<T> operator--(int) |
| { |
| RangedPtr<T> rcp = *this; |
| --*this; |
| return rcp; |
| } |
| |
| RangedPtr<T>& operator+=(size_t aInc) |
| { |
| *this = *this + aInc; |
| return *this; |
| } |
| |
| RangedPtr<T>& operator-=(size_t aDec) |
| { |
| *this = *this - aDec; |
| return *this; |
| } |
| |
| T& operator[](int aIndex) const |
| { |
| MOZ_ASSERT(size_t(aIndex > 0 ? aIndex : -aIndex) <= size_t(-1) / sizeof(T)); |
| return *create(mPtr + aIndex); |
| } |
| |
| T& operator*() const |
| { |
| MOZ_ASSERT(mPtr >= mRangeStart); |
| MOZ_ASSERT(mPtr < mRangeEnd); |
| return *mPtr; |
| } |
| |
| template <typename U> |
| bool operator==(const RangedPtr<U>& aOther) const |
| { |
| return mPtr == aOther.mPtr; |
| } |
| template <typename U> |
| bool operator!=(const RangedPtr<U>& aOther) const |
| { |
| return !(*this == aOther); |
| } |
| |
| template<typename U> |
| bool operator==(const U* u) const |
| { |
| return mPtr == u; |
| } |
| template<typename U> |
| bool operator!=(const U* u) const |
| { |
| return !(*this == u); |
| } |
| |
| template <typename U> |
| bool operator<(const RangedPtr<U>& aOther) const |
| { |
| return mPtr < aOther.mPtr; |
| } |
| template <typename U> |
| bool operator<=(const RangedPtr<U>& aOther) const |
| { |
| return mPtr <= aOther.mPtr; |
| } |
| |
| template <typename U> |
| bool operator>(const RangedPtr<U>& aOther) const |
| { |
| return mPtr > aOther.mPtr; |
| } |
| template <typename U> |
| bool operator>=(const RangedPtr<U>& aOther) const |
| { |
| return mPtr >= aOther.mPtr; |
| } |
| |
| size_t operator-(const RangedPtr<T>& aOther) const |
| { |
| MOZ_ASSERT(mPtr >= aOther.mPtr); |
| return PointerRangeSize(aOther.mPtr, mPtr); |
| } |
| |
| private: |
| RangedPtr() = delete; |
| T* operator&() = delete; |
| }; |
| |
| } /* namespace mozilla */ |
| |
| #endif /* mozilla_RangedPtr_h */ |