| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- /* -*- 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; }
- void checkIdenticalRange(const RangedPtr<T>& aOther) const
- {
- MOZ_ASSERT(mRangeStart == aOther.mRangeStart);
- MOZ_ASSERT(mRangeEnd == aOther.mRangeEnd);
- }
- /*
- * 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)
- {
- checkIdenticalRange(aOther);
- mPtr = aOther.mPtr;
- checkSanity();
- return *this;
- }
- RangedPtr<T> operator+(size_t aInc) const
- {
- 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) const
- {
- 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;
- }
- 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 */
|