michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * Implements a smart pointer asserted to remain within a range specified at michael@0: * construction. michael@0: */ michael@0: michael@0: #ifndef mozilla_RangedPtr_h michael@0: #define mozilla_RangedPtr_h michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/NullPtr.h" michael@0: michael@0: #include michael@0: michael@0: namespace mozilla { michael@0: michael@0: /* michael@0: * RangedPtr is a smart pointer restricted to an address range specified at michael@0: * creation. The pointer (and any smart pointers derived from it) must remain michael@0: * within the range [start, end] (inclusive of end to facilitate use as michael@0: * sentinels). Dereferencing or indexing into the pointer (or pointers derived michael@0: * from it) must remain within the range [start, end). All the standard pointer michael@0: * operators are defined on it; in debug builds these operations assert that the michael@0: * range specified at construction is respected. michael@0: * michael@0: * In theory passing a smart pointer instance as an argument can be slightly michael@0: * slower than passing a T* (due to ABI requirements for passing structs versus michael@0: * passing pointers), if the method being called isn't inlined. If you are in michael@0: * extremely performance-critical code, you may want to be careful using this michael@0: * smart pointer as an argument type. michael@0: * michael@0: * RangedPtr intentionally does not implicitly convert to T*. Use get() to michael@0: * explicitly convert to T*. Keep in mind that the raw pointer of course won't michael@0: * implement bounds checking in debug builds. michael@0: */ michael@0: template michael@0: class RangedPtr michael@0: { michael@0: T* ptr; michael@0: michael@0: #ifdef DEBUG michael@0: T* const rangeStart; michael@0: T* const rangeEnd; michael@0: #endif michael@0: michael@0: typedef void (RangedPtr::* ConvertibleToBool)(); michael@0: void nonNull() {} michael@0: michael@0: void checkSanity() { michael@0: MOZ_ASSERT(rangeStart <= ptr); michael@0: MOZ_ASSERT(ptr <= rangeEnd); michael@0: } michael@0: michael@0: /* Creates a new pointer for |p|, restricted to this pointer's range. */ michael@0: RangedPtr create(T *p) const { michael@0: #ifdef DEBUG michael@0: return RangedPtr(p, rangeStart, rangeEnd); michael@0: #else michael@0: return RangedPtr(p, nullptr, size_t(0)); michael@0: #endif michael@0: } michael@0: michael@0: uintptr_t asUintptr() const { return uintptr_t(ptr); } michael@0: michael@0: public: michael@0: RangedPtr(T* p, T* start, T* end) michael@0: : ptr(p) michael@0: #ifdef DEBUG michael@0: , rangeStart(start), rangeEnd(end) michael@0: #endif michael@0: { michael@0: MOZ_ASSERT(rangeStart <= rangeEnd); michael@0: checkSanity(); michael@0: } michael@0: RangedPtr(T* p, T* start, size_t length) michael@0: : ptr(p) michael@0: #ifdef DEBUG michael@0: , rangeStart(start), rangeEnd(start + length) michael@0: #endif michael@0: { michael@0: MOZ_ASSERT(length <= size_t(-1) / sizeof(T)); michael@0: MOZ_ASSERT(uintptr_t(rangeStart) + length * sizeof(T) >= uintptr_t(rangeStart)); michael@0: checkSanity(); michael@0: } michael@0: michael@0: /* Equivalent to RangedPtr(p, p, length). */ michael@0: RangedPtr(T* p, size_t length) michael@0: : ptr(p) michael@0: #ifdef DEBUG michael@0: , rangeStart(p), rangeEnd(p + length) michael@0: #endif michael@0: { michael@0: MOZ_ASSERT(length <= size_t(-1) / sizeof(T)); michael@0: MOZ_ASSERT(uintptr_t(rangeStart) + length * sizeof(T) >= uintptr_t(rangeStart)); michael@0: checkSanity(); michael@0: } michael@0: michael@0: /* Equivalent to RangedPtr(arr, arr, N). */ michael@0: template michael@0: RangedPtr(T (&arr)[N]) michael@0: : ptr(arr) michael@0: #ifdef DEBUG michael@0: , rangeStart(arr), rangeEnd(arr + N) michael@0: #endif michael@0: { michael@0: checkSanity(); michael@0: } michael@0: michael@0: T* get() const { michael@0: return ptr; michael@0: } michael@0: michael@0: operator ConvertibleToBool() const { return ptr ? &RangedPtr::nonNull : 0; } michael@0: michael@0: /* michael@0: * You can only assign one RangedPtr into another if the two pointers have michael@0: * the same valid range: michael@0: * michael@0: * char arr1[] = "hi"; michael@0: * char arr2[] = "bye"; michael@0: * RangedPtr p1(arr1, 2); michael@0: * p1 = RangedPtr(arr1 + 1, arr1, arr1 + 2); // works michael@0: * p1 = RangedPtr(arr2, 3); // asserts michael@0: */ michael@0: RangedPtr& operator=(const RangedPtr& other) { michael@0: MOZ_ASSERT(rangeStart == other.rangeStart); michael@0: MOZ_ASSERT(rangeEnd == other.rangeEnd); michael@0: ptr = other.ptr; michael@0: checkSanity(); michael@0: return *this; michael@0: } michael@0: michael@0: RangedPtr operator+(size_t inc) { michael@0: MOZ_ASSERT(inc <= size_t(-1) / sizeof(T)); michael@0: MOZ_ASSERT(asUintptr() + inc * sizeof(T) >= asUintptr()); michael@0: return create(ptr + inc); michael@0: } michael@0: michael@0: RangedPtr operator-(size_t dec) { michael@0: MOZ_ASSERT(dec <= size_t(-1) / sizeof(T)); michael@0: MOZ_ASSERT(asUintptr() - dec * sizeof(T) <= asUintptr()); michael@0: return create(ptr - dec); michael@0: } michael@0: michael@0: /* michael@0: * You can assign a raw pointer into a RangedPtr if the raw pointer is michael@0: * within the range specified at creation. michael@0: */ michael@0: template michael@0: RangedPtr& operator=(U* p) { michael@0: *this = create(p); michael@0: return *this; michael@0: } michael@0: michael@0: template michael@0: RangedPtr& operator=(const RangedPtr& p) { michael@0: MOZ_ASSERT(rangeStart <= p.ptr); michael@0: MOZ_ASSERT(p.ptr <= rangeEnd); michael@0: ptr = p.ptr; michael@0: checkSanity(); michael@0: return *this; michael@0: } michael@0: michael@0: RangedPtr& operator++() { michael@0: return (*this += 1); michael@0: } michael@0: michael@0: RangedPtr operator++(int) { michael@0: RangedPtr rcp = *this; michael@0: ++*this; michael@0: return rcp; michael@0: } michael@0: michael@0: RangedPtr& operator--() { michael@0: return (*this -= 1); michael@0: } michael@0: michael@0: RangedPtr operator--(int) { michael@0: RangedPtr rcp = *this; michael@0: --*this; michael@0: return rcp; michael@0: } michael@0: michael@0: RangedPtr& operator+=(size_t inc) { michael@0: *this = *this + inc; michael@0: return *this; michael@0: } michael@0: michael@0: RangedPtr& operator-=(size_t dec) { michael@0: *this = *this - dec; michael@0: return *this; michael@0: } michael@0: michael@0: T& operator[](int index) const { michael@0: MOZ_ASSERT(size_t(index > 0 ? index : -index) <= size_t(-1) / sizeof(T)); michael@0: return *create(ptr + index); michael@0: } michael@0: michael@0: T& operator*() const { michael@0: MOZ_ASSERT(ptr >= rangeStart); michael@0: MOZ_ASSERT(ptr < rangeEnd); michael@0: return *ptr; michael@0: } michael@0: michael@0: template michael@0: bool operator==(const RangedPtr& other) const { michael@0: return ptr == other.ptr; michael@0: } michael@0: template michael@0: bool operator!=(const RangedPtr& other) const { michael@0: return !(*this == other); michael@0: } michael@0: michael@0: template michael@0: bool operator==(const U* u) const { michael@0: return ptr == u; michael@0: } michael@0: template michael@0: bool operator!=(const U* u) const { michael@0: return !(*this == u); michael@0: } michael@0: michael@0: template michael@0: bool operator<(const RangedPtr& other) const { michael@0: return ptr < other.ptr; michael@0: } michael@0: template michael@0: bool operator<=(const RangedPtr& other) const { michael@0: return ptr <= other.ptr; michael@0: } michael@0: michael@0: template michael@0: bool operator>(const RangedPtr& other) const { michael@0: return ptr > other.ptr; michael@0: } michael@0: template michael@0: bool operator>=(const RangedPtr& other) const { michael@0: return ptr >= other.ptr; michael@0: } michael@0: michael@0: size_t operator-(const RangedPtr& other) const { michael@0: MOZ_ASSERT(ptr >= other.ptr); michael@0: return PointerRangeSize(other.ptr, ptr); michael@0: } michael@0: michael@0: private: michael@0: RangedPtr() MOZ_DELETE; michael@0: T* operator&() MOZ_DELETE; michael@0: }; michael@0: michael@0: } /* namespace mozilla */ michael@0: michael@0: #endif /* mozilla_RangedPtr_h */