1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mfbt/RangedPtr.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,260 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* 1.11 + * Implements a smart pointer asserted to remain within a range specified at 1.12 + * construction. 1.13 + */ 1.14 + 1.15 +#ifndef mozilla_RangedPtr_h 1.16 +#define mozilla_RangedPtr_h 1.17 + 1.18 +#include "mozilla/ArrayUtils.h" 1.19 +#include "mozilla/Assertions.h" 1.20 +#include "mozilla/Attributes.h" 1.21 +#include "mozilla/NullPtr.h" 1.22 + 1.23 +#include <stdint.h> 1.24 + 1.25 +namespace mozilla { 1.26 + 1.27 +/* 1.28 + * RangedPtr is a smart pointer restricted to an address range specified at 1.29 + * creation. The pointer (and any smart pointers derived from it) must remain 1.30 + * within the range [start, end] (inclusive of end to facilitate use as 1.31 + * sentinels). Dereferencing or indexing into the pointer (or pointers derived 1.32 + * from it) must remain within the range [start, end). All the standard pointer 1.33 + * operators are defined on it; in debug builds these operations assert that the 1.34 + * range specified at construction is respected. 1.35 + * 1.36 + * In theory passing a smart pointer instance as an argument can be slightly 1.37 + * slower than passing a T* (due to ABI requirements for passing structs versus 1.38 + * passing pointers), if the method being called isn't inlined. If you are in 1.39 + * extremely performance-critical code, you may want to be careful using this 1.40 + * smart pointer as an argument type. 1.41 + * 1.42 + * RangedPtr<T> intentionally does not implicitly convert to T*. Use get() to 1.43 + * explicitly convert to T*. Keep in mind that the raw pointer of course won't 1.44 + * implement bounds checking in debug builds. 1.45 + */ 1.46 +template<typename T> 1.47 +class RangedPtr 1.48 +{ 1.49 + T* ptr; 1.50 + 1.51 +#ifdef DEBUG 1.52 + T* const rangeStart; 1.53 + T* const rangeEnd; 1.54 +#endif 1.55 + 1.56 + typedef void (RangedPtr::* ConvertibleToBool)(); 1.57 + void nonNull() {} 1.58 + 1.59 + void checkSanity() { 1.60 + MOZ_ASSERT(rangeStart <= ptr); 1.61 + MOZ_ASSERT(ptr <= rangeEnd); 1.62 + } 1.63 + 1.64 + /* Creates a new pointer for |p|, restricted to this pointer's range. */ 1.65 + RangedPtr<T> create(T *p) const { 1.66 +#ifdef DEBUG 1.67 + return RangedPtr<T>(p, rangeStart, rangeEnd); 1.68 +#else 1.69 + return RangedPtr<T>(p, nullptr, size_t(0)); 1.70 +#endif 1.71 + } 1.72 + 1.73 + uintptr_t asUintptr() const { return uintptr_t(ptr); } 1.74 + 1.75 + public: 1.76 + RangedPtr(T* p, T* start, T* end) 1.77 + : ptr(p) 1.78 +#ifdef DEBUG 1.79 + , rangeStart(start), rangeEnd(end) 1.80 +#endif 1.81 + { 1.82 + MOZ_ASSERT(rangeStart <= rangeEnd); 1.83 + checkSanity(); 1.84 + } 1.85 + RangedPtr(T* p, T* start, size_t length) 1.86 + : ptr(p) 1.87 +#ifdef DEBUG 1.88 + , rangeStart(start), rangeEnd(start + length) 1.89 +#endif 1.90 + { 1.91 + MOZ_ASSERT(length <= size_t(-1) / sizeof(T)); 1.92 + MOZ_ASSERT(uintptr_t(rangeStart) + length * sizeof(T) >= uintptr_t(rangeStart)); 1.93 + checkSanity(); 1.94 + } 1.95 + 1.96 + /* Equivalent to RangedPtr(p, p, length). */ 1.97 + RangedPtr(T* p, size_t length) 1.98 + : ptr(p) 1.99 +#ifdef DEBUG 1.100 + , rangeStart(p), rangeEnd(p + length) 1.101 +#endif 1.102 + { 1.103 + MOZ_ASSERT(length <= size_t(-1) / sizeof(T)); 1.104 + MOZ_ASSERT(uintptr_t(rangeStart) + length * sizeof(T) >= uintptr_t(rangeStart)); 1.105 + checkSanity(); 1.106 + } 1.107 + 1.108 + /* Equivalent to RangedPtr(arr, arr, N). */ 1.109 + template<size_t N> 1.110 + RangedPtr(T (&arr)[N]) 1.111 + : ptr(arr) 1.112 +#ifdef DEBUG 1.113 + , rangeStart(arr), rangeEnd(arr + N) 1.114 +#endif 1.115 + { 1.116 + checkSanity(); 1.117 + } 1.118 + 1.119 + T* get() const { 1.120 + return ptr; 1.121 + } 1.122 + 1.123 + operator ConvertibleToBool() const { return ptr ? &RangedPtr::nonNull : 0; } 1.124 + 1.125 + /* 1.126 + * You can only assign one RangedPtr into another if the two pointers have 1.127 + * the same valid range: 1.128 + * 1.129 + * char arr1[] = "hi"; 1.130 + * char arr2[] = "bye"; 1.131 + * RangedPtr<char> p1(arr1, 2); 1.132 + * p1 = RangedPtr<char>(arr1 + 1, arr1, arr1 + 2); // works 1.133 + * p1 = RangedPtr<char>(arr2, 3); // asserts 1.134 + */ 1.135 + RangedPtr<T>& operator=(const RangedPtr<T>& other) { 1.136 + MOZ_ASSERT(rangeStart == other.rangeStart); 1.137 + MOZ_ASSERT(rangeEnd == other.rangeEnd); 1.138 + ptr = other.ptr; 1.139 + checkSanity(); 1.140 + return *this; 1.141 + } 1.142 + 1.143 + RangedPtr<T> operator+(size_t inc) { 1.144 + MOZ_ASSERT(inc <= size_t(-1) / sizeof(T)); 1.145 + MOZ_ASSERT(asUintptr() + inc * sizeof(T) >= asUintptr()); 1.146 + return create(ptr + inc); 1.147 + } 1.148 + 1.149 + RangedPtr<T> operator-(size_t dec) { 1.150 + MOZ_ASSERT(dec <= size_t(-1) / sizeof(T)); 1.151 + MOZ_ASSERT(asUintptr() - dec * sizeof(T) <= asUintptr()); 1.152 + return create(ptr - dec); 1.153 + } 1.154 + 1.155 + /* 1.156 + * You can assign a raw pointer into a RangedPtr if the raw pointer is 1.157 + * within the range specified at creation. 1.158 + */ 1.159 + template <typename U> 1.160 + RangedPtr<T>& operator=(U* p) { 1.161 + *this = create(p); 1.162 + return *this; 1.163 + } 1.164 + 1.165 + template <typename U> 1.166 + RangedPtr<T>& operator=(const RangedPtr<U>& p) { 1.167 + MOZ_ASSERT(rangeStart <= p.ptr); 1.168 + MOZ_ASSERT(p.ptr <= rangeEnd); 1.169 + ptr = p.ptr; 1.170 + checkSanity(); 1.171 + return *this; 1.172 + } 1.173 + 1.174 + RangedPtr<T>& operator++() { 1.175 + return (*this += 1); 1.176 + } 1.177 + 1.178 + RangedPtr<T> operator++(int) { 1.179 + RangedPtr<T> rcp = *this; 1.180 + ++*this; 1.181 + return rcp; 1.182 + } 1.183 + 1.184 + RangedPtr<T>& operator--() { 1.185 + return (*this -= 1); 1.186 + } 1.187 + 1.188 + RangedPtr<T> operator--(int) { 1.189 + RangedPtr<T> rcp = *this; 1.190 + --*this; 1.191 + return rcp; 1.192 + } 1.193 + 1.194 + RangedPtr<T>& operator+=(size_t inc) { 1.195 + *this = *this + inc; 1.196 + return *this; 1.197 + } 1.198 + 1.199 + RangedPtr<T>& operator-=(size_t dec) { 1.200 + *this = *this - dec; 1.201 + return *this; 1.202 + } 1.203 + 1.204 + T& operator[](int index) const { 1.205 + MOZ_ASSERT(size_t(index > 0 ? index : -index) <= size_t(-1) / sizeof(T)); 1.206 + return *create(ptr + index); 1.207 + } 1.208 + 1.209 + T& operator*() const { 1.210 + MOZ_ASSERT(ptr >= rangeStart); 1.211 + MOZ_ASSERT(ptr < rangeEnd); 1.212 + return *ptr; 1.213 + } 1.214 + 1.215 + template <typename U> 1.216 + bool operator==(const RangedPtr<U>& other) const { 1.217 + return ptr == other.ptr; 1.218 + } 1.219 + template <typename U> 1.220 + bool operator!=(const RangedPtr<U>& other) const { 1.221 + return !(*this == other); 1.222 + } 1.223 + 1.224 + template<typename U> 1.225 + bool operator==(const U* u) const { 1.226 + return ptr == u; 1.227 + } 1.228 + template<typename U> 1.229 + bool operator!=(const U* u) const { 1.230 + return !(*this == u); 1.231 + } 1.232 + 1.233 + template <typename U> 1.234 + bool operator<(const RangedPtr<U>& other) const { 1.235 + return ptr < other.ptr; 1.236 + } 1.237 + template <typename U> 1.238 + bool operator<=(const RangedPtr<U>& other) const { 1.239 + return ptr <= other.ptr; 1.240 + } 1.241 + 1.242 + template <typename U> 1.243 + bool operator>(const RangedPtr<U>& other) const { 1.244 + return ptr > other.ptr; 1.245 + } 1.246 + template <typename U> 1.247 + bool operator>=(const RangedPtr<U>& other) const { 1.248 + return ptr >= other.ptr; 1.249 + } 1.250 + 1.251 + size_t operator-(const RangedPtr<T>& other) const { 1.252 + MOZ_ASSERT(ptr >= other.ptr); 1.253 + return PointerRangeSize(other.ptr, ptr); 1.254 + } 1.255 + 1.256 + private: 1.257 + RangedPtr() MOZ_DELETE; 1.258 + T* operator&() MOZ_DELETE; 1.259 +}; 1.260 + 1.261 +} /* namespace mozilla */ 1.262 + 1.263 +#endif /* mozilla_RangedPtr_h */