diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2013-12-11 22:24:18 +0000 |
---|---|---|
committer | <> | 2014-07-24 09:30:59 +0000 |
commit | 59e2936f588aa945e8dcd6c737523c299067e9d0 (patch) | |
tree | 97e74980cc54baa19de5faa11f5a50a0121a48ea /mfbt/RangedPtr.h | |
download | mozjs24-59e2936f588aa945e8dcd6c737523c299067e9d0.tar.gz |
Imported from /home/lorry/working-area/delta_mozilla_mozjs24/mozjs-24.2.0.tar.bz2.HEADmozjs-24.2.0master
Diffstat (limited to 'mfbt/RangedPtr.h')
-rw-r--r-- | mfbt/RangedPtr.h | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/mfbt/RangedPtr.h b/mfbt/RangedPtr.h new file mode 100644 index 0000000..7ce19d0 --- /dev/null +++ b/mfbt/RangedPtr.h @@ -0,0 +1,254 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/Assertions.h" +#include "mozilla/Attributes.h" +#include "mozilla/Util.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* ptr; + +#ifdef DEBUG + T* const rangeStart; + T* const rangeEnd; +#endif + + typedef void (RangedPtr::* ConvertibleToBool)(); + void nonNull() {} + + void checkSanity() { + MOZ_ASSERT(rangeStart <= ptr); + MOZ_ASSERT(ptr <= rangeEnd); + } + + /* Creates a new pointer for |p|, restricted to this pointer's range. */ + RangedPtr<T> create(T *p) const { +#ifdef DEBUG + return RangedPtr<T>(p, rangeStart, rangeEnd); +#else + return RangedPtr<T>(p, NULL, size_t(0)); +#endif + } + + uintptr_t asUintptr() const { return uintptr_t(ptr); } + + public: + RangedPtr(T* p, T* start, T* end) + : ptr(p) +#ifdef DEBUG + , rangeStart(start), rangeEnd(end) +#endif + { + MOZ_ASSERT(rangeStart <= rangeEnd); + checkSanity(); + } + RangedPtr(T* p, T* start, size_t length) + : ptr(p) +#ifdef DEBUG + , rangeStart(start), rangeEnd(start + length) +#endif + { + MOZ_ASSERT(length <= size_t(-1) / sizeof(T)); + MOZ_ASSERT(uintptr_t(rangeStart) + length * sizeof(T) >= uintptr_t(rangeStart)); + checkSanity(); + } + + /* Equivalent to RangedPtr(p, p, length). */ + RangedPtr(T* p, size_t length) + : ptr(p) +#ifdef DEBUG + , rangeStart(p), rangeEnd(p + length) +#endif + { + MOZ_ASSERT(length <= size_t(-1) / sizeof(T)); + MOZ_ASSERT(uintptr_t(rangeStart) + length * sizeof(T) >= uintptr_t(rangeStart)); + checkSanity(); + } + + /* Equivalent to RangedPtr(arr, arr, N). */ + template<size_t N> + RangedPtr(T (&arr)[N]) + : ptr(arr) +#ifdef DEBUG + , rangeStart(arr), rangeEnd(arr + N) +#endif + { + checkSanity(); + } + + T* get() const { + return ptr; + } + + operator ConvertibleToBool() const { return ptr ? &RangedPtr::nonNull : 0; } + + /* + * 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>& other) { + MOZ_ASSERT(rangeStart == other.rangeStart); + MOZ_ASSERT(rangeEnd == other.rangeEnd); + ptr = other.ptr; + checkSanity(); + return *this; + } + + RangedPtr<T> operator+(size_t inc) { + MOZ_ASSERT(inc <= size_t(-1) / sizeof(T)); + MOZ_ASSERT(asUintptr() + inc * sizeof(T) >= asUintptr()); + return create(ptr + inc); + } + + RangedPtr<T> operator-(size_t dec) { + MOZ_ASSERT(dec <= size_t(-1) / sizeof(T)); + MOZ_ASSERT(asUintptr() - dec * sizeof(T) <= asUintptr()); + return create(ptr - dec); + } + + /* + * 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* p) { + *this = create(p); + return *this; + } + + template <typename U> + RangedPtr<T>& operator=(const RangedPtr<U>& p) { + MOZ_ASSERT(rangeStart <= p.ptr); + MOZ_ASSERT(p.ptr <= rangeEnd); + ptr = p.ptr; + 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 inc) { + *this = *this + inc; + return *this; + } + + RangedPtr<T>& operator-=(size_t dec) { + *this = *this - dec; + return *this; + } + + T& operator[](int index) const { + MOZ_ASSERT(size_t(index > 0 ? index : -index) <= size_t(-1) / sizeof(T)); + return *create(ptr + index); + } + + T& operator*() const { + return *ptr; + } + + template <typename U> + bool operator==(const RangedPtr<U>& other) const { + return ptr == other.ptr; + } + template <typename U> + bool operator!=(const RangedPtr<U>& other) const { + return !(*this == other); + } + + template<typename U> + bool operator==(const U* u) const { + return ptr == u; + } + template<typename U> + bool operator!=(const U* u) const { + return !(*this == u); + } + + template <typename U> + bool operator<(const RangedPtr<U>& other) const { + return ptr < other.ptr; + } + template <typename U> + bool operator<=(const RangedPtr<U>& other) const { + return ptr <= other.ptr; + } + + template <typename U> + bool operator>(const RangedPtr<U>& other) const { + return ptr > other.ptr; + } + template <typename U> + bool operator>=(const RangedPtr<U>& other) const { + return ptr >= other.ptr; + } + + size_t operator-(const RangedPtr<T>& other) const { + MOZ_ASSERT(ptr >= other.ptr); + return PointerRangeSize(other.ptr, ptr); + } + + private: + RangedPtr() MOZ_DELETE; + T* operator&() MOZ_DELETE; +}; + +} /* namespace mozilla */ + +#endif /* mozilla_RangedPtr_h_ */ |