From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- mfbt/RangedPtr.h | 292 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 292 insertions(+) create mode 100644 mfbt/RangedPtr.h (limited to 'mfbt/RangedPtr.h') diff --git a/mfbt/RangedPtr.h b/mfbt/RangedPtr.h new file mode 100644 index 000000000..a07c1f4f8 --- /dev/null +++ b/mfbt/RangedPtr.h @@ -0,0 +1,292 @@ +/* -*- 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 + +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 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 +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 create(T* aPtr) const + { +#ifdef DEBUG + return RangedPtr(aPtr, mRangeStart, mRangeEnd); +#else + return RangedPtr(aPtr, nullptr, size_t(0)); +#endif + } + + uintptr_t asUintptr() const { return reinterpret_cast(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(mRangeStart) + aLength * sizeof(T) >= + reinterpret_cast(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(mRangeStart) + aLength * sizeof(T) >= + reinterpret_cast(mRangeStart)); + checkSanity(); + } + + /* Equivalent to RangedPtr(aArr, aArr, N). */ + template + 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& 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 p1(arr1, 2); + * p1 = RangedPtr(arr1 + 1, arr1, arr1 + 2); // works + * p1 = RangedPtr(arr2, 3); // asserts + */ + RangedPtr& operator=(const RangedPtr& aOther) + { + checkIdenticalRange(aOther); + mPtr = aOther.mPtr; + checkSanity(); + return *this; + } + + RangedPtr 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 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 + RangedPtr& operator=(U* aPtr) + { + *this = create(aPtr); + return *this; + } + + template + RangedPtr& operator=(const RangedPtr& aPtr) + { + MOZ_ASSERT(mRangeStart <= aPtr.mPtr); + MOZ_ASSERT(aPtr.mPtr <= mRangeEnd); + mPtr = aPtr.mPtr; + checkSanity(); + return *this; + } + + RangedPtr& operator++() + { + return (*this += 1); + } + + RangedPtr operator++(int) + { + RangedPtr rcp = *this; + ++*this; + return rcp; + } + + RangedPtr& operator--() + { + return (*this -= 1); + } + + RangedPtr operator--(int) + { + RangedPtr rcp = *this; + --*this; + return rcp; + } + + RangedPtr& operator+=(size_t aInc) + { + *this = *this + aInc; + return *this; + } + + RangedPtr& 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 + bool operator==(const RangedPtr& aOther) const + { + return mPtr == aOther.mPtr; + } + template + bool operator!=(const RangedPtr& aOther) const + { + return !(*this == aOther); + } + + template + bool operator==(const U* u) const + { + return mPtr == u; + } + template + bool operator!=(const U* u) const + { + return !(*this == u); + } + + template + bool operator<(const RangedPtr& aOther) const + { + return mPtr < aOther.mPtr; + } + template + bool operator<=(const RangedPtr& aOther) const + { + return mPtr <= aOther.mPtr; + } + + template + bool operator>(const RangedPtr& aOther) const + { + return mPtr > aOther.mPtr; + } + template + bool operator>=(const RangedPtr& aOther) const + { + return mPtr >= aOther.mPtr; + } + + size_t operator-(const RangedPtr& aOther) const + { + MOZ_ASSERT(mPtr >= aOther.mPtr); + return PointerRangeSize(aOther.mPtr, mPtr); + } + +private: + RangedPtr() = delete; + T* operator&() = delete; +}; + +} /* namespace mozilla */ + +#endif /* mozilla_RangedPtr_h */ -- cgit v1.2.3