Back to index

enigmail  1.4.3
RangedPtr.h
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * vim: set ts=8 sw=4 et tw=99 ft=cpp:
00003  *
00004  * ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at:
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is Mozilla Code.
00018  *
00019  * The Initial Developer of the Original Code is
00020  *   The Mozilla Foundation
00021  * Portions created by the Initial Developer are Copyright (C) 2011
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *   Jeff Walden <jwalden+code@mit.edu> (original author)
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 /*
00042  * Implements a smart pointer asserted to remain within a range specified at
00043  * construction.
00044  */
00045 
00046 #ifndef mozilla_RangedPtr_h_
00047 #define mozilla_RangedPtr_h_
00048 
00049 #include "mozilla/Assertions.h"
00050 #include "mozilla/Attributes.h"
00051 #include "mozilla/Util.h"
00052 
00053 namespace mozilla {
00054 
00055 /*
00056  * RangedPtr is a smart pointer restricted to an address range specified at
00057  * creation.  The pointer (and any smart pointers derived from it) must remain
00058  * within the range [start, end] (inclusive of end to facilitate use as
00059  * sentinels).  Dereferencing or indexing into the pointer (or pointers derived
00060  * from it) must remain within the range [start, end).  All the standard pointer
00061  * operators are defined on it; in debug builds these operations assert that the
00062  * range specified at construction is respected.
00063  *
00064  * In theory passing a smart pointer instance as an argument can be slightly
00065  * slower than passing a T* (due to ABI requirements for passing structs versus
00066  * passing pointers), if the method being called isn't inlined.  If you are in
00067  * extremely performance-critical code, you may want to be careful using this
00068  * smart pointer as an argument type.
00069  *
00070  * RangedPtr<T> intentionally does not implicitly convert to T*.  Use get() to
00071  * explicitly convert to T*.  Keep in mind that the raw pointer of course won't
00072  * implement bounds checking in debug builds.
00073  */
00074 template <typename T>
00075 class RangedPtr
00076 {
00077     T* ptr;
00078 
00079 #ifdef DEBUG
00080     T* const rangeStart;
00081     T* const rangeEnd;
00082 #endif
00083 
00084     void checkSanity() {
00085         MOZ_ASSERT(rangeStart <= ptr);
00086         MOZ_ASSERT(ptr <= rangeEnd);
00087     }
00088 
00089     /* Creates a new pointer for |ptr|, restricted to this pointer's range. */
00090     RangedPtr<T> create(T *ptr) const {
00091 #ifdef DEBUG
00092         return RangedPtr<T>(ptr, rangeStart, rangeEnd);
00093 #else
00094         return RangedPtr<T>(ptr, NULL, size_t(0));
00095 #endif
00096     }
00097 
00098   public:
00099     RangedPtr(T* p, T* start, T* end)
00100       : ptr(p)
00101 #ifdef DEBUG
00102       , rangeStart(start), rangeEnd(end)
00103 #endif
00104     {
00105         MOZ_ASSERT(rangeStart <= rangeEnd);
00106         checkSanity();
00107     }
00108     RangedPtr(T* p, T* start, size_t length)
00109       : ptr(p)
00110 #ifdef DEBUG
00111       , rangeStart(start), rangeEnd(start + length)
00112 #endif
00113     {
00114         MOZ_ASSERT(length <= size_t(-1) / sizeof(T));
00115         MOZ_ASSERT(uintptr_t(rangeStart) + length * sizeof(T) >= uintptr_t(rangeStart));
00116         checkSanity();
00117     }
00118 
00119     /* Equivalent to RangedPtr(p, p, length). */
00120     RangedPtr(T* p, size_t length)
00121       : ptr(p)
00122 #ifdef DEBUG
00123       , rangeStart(p), rangeEnd(p + length)
00124 #endif
00125     {
00126         MOZ_ASSERT(length <= size_t(-1) / sizeof(T));
00127         MOZ_ASSERT(uintptr_t(rangeStart) + length * sizeof(T) >= uintptr_t(rangeStart));
00128         checkSanity();
00129     }
00130 
00131     /* Equivalent to RangedPtr(arr, arr, N). */
00132     template<size_t N>
00133     RangedPtr(T arr[N])
00134       : ptr(arr)
00135 #ifdef DEBUG
00136       , rangeStart(arr), rangeEnd(arr + N)
00137 #endif
00138     {
00139       checkSanity();
00140     }
00141 
00142     T* get() const {
00143         return ptr;
00144     }
00145 
00146     /*
00147      * You can only assign one RangedPtr into another if the two pointers have
00148      * the same valid range:
00149      *
00150      *   char arr1[] = "hi";
00151      *   char arr2[] = "bye";
00152      *   RangedPtr<char> p1(arr1, 2);
00153      *   p1 = RangedPtr<char>(arr1 + 1, arr1, arr1 + 2); // works
00154      *   p1 = RangedPtr<char>(arr2, 3);                  // asserts
00155      */
00156     RangedPtr<T>& operator=(const RangedPtr<T>& other) {
00157         MOZ_ASSERT(rangeStart == other.rangeStart);
00158         MOZ_ASSERT(rangeEnd == other.rangeEnd);
00159         ptr = other.ptr;
00160         checkSanity();
00161         return *this;
00162     }
00163 
00164     RangedPtr<T> operator+(size_t inc) {
00165         MOZ_ASSERT(inc <= size_t(-1) / sizeof(T));
00166         MOZ_ASSERT(ptr + inc > ptr);
00167         return create(ptr + inc);
00168     }
00169 
00170     RangedPtr<T> operator-(size_t dec) {
00171         MOZ_ASSERT(dec <= size_t(-1) / sizeof(T));
00172         MOZ_ASSERT(ptr - dec < ptr);
00173         return create(ptr - dec);
00174     }
00175 
00176     /*
00177      * You can assign a raw pointer into a RangedPtr if the raw pointer is
00178      * within the range specified at creation.
00179      */
00180     template <typename U>
00181     RangedPtr<T>& operator=(U* p) {
00182         *this = create(p);
00183         return *this;
00184     }
00185 
00186     template <typename U>
00187     RangedPtr<T>& operator=(const RangedPtr<U>& p) {
00188         MOZ_ASSERT(rangeStart <= p.ptr);
00189         MOZ_ASSERT(p.ptr <= rangeEnd);
00190         ptr = p.ptr;
00191         checkSanity();
00192         return *this;
00193     }
00194 
00195     RangedPtr<T>& operator++() {
00196         return (*this += 1);
00197     }
00198 
00199     RangedPtr<T> operator++(int) {
00200         RangedPtr<T> rcp = *this;
00201         ++*this;
00202         return rcp;
00203     }
00204 
00205     RangedPtr<T>& operator--() {
00206         return (*this -= 1);
00207     }
00208 
00209     RangedPtr<T> operator--(int) {
00210         RangedPtr<T> rcp = *this;
00211         --*this;
00212         return rcp;
00213     }
00214 
00215     RangedPtr<T>& operator+=(size_t inc) {
00216         this->operator=<T>(*this + inc);
00217         return *this;
00218     }
00219 
00220     RangedPtr<T>& operator-=(size_t dec) {
00221         this->operator=<T>(*this - dec);
00222         return *this;
00223     }
00224 
00225     T& operator[](int index) const {
00226         MOZ_ASSERT(size_t(index > 0 ? index : -index) <= size_t(-1) / sizeof(T));
00227         return *create(ptr + index);
00228     }
00229 
00230     T& operator*() const {
00231         return *ptr;
00232     }
00233 
00234     template <typename U>
00235     bool operator==(const RangedPtr<U>& other) const {
00236         return ptr == other.ptr;
00237     }
00238     template <typename U>
00239     bool operator!=(const RangedPtr<U>& other) const {
00240         return !(*this == other);
00241     }
00242 
00243     template<typename U>
00244     bool operator==(const U* u) const {
00245         return ptr == u;
00246     }
00247     template<typename U>
00248     bool operator!=(const U* u) const {
00249         return !(*this == u);
00250     }
00251 
00252     template <typename U>
00253     bool operator<(const RangedPtr<U>& other) const {
00254         return ptr < other.ptr;
00255     }
00256     template <typename U>
00257     bool operator<=(const RangedPtr<U>& other) const {
00258         return ptr <= other.ptr;
00259     }
00260 
00261     template <typename U>
00262     bool operator>(const RangedPtr<U>& other) const {
00263         return ptr > other.ptr;
00264     }
00265     template <typename U>
00266     bool operator>=(const RangedPtr<U>& other) const {
00267         return ptr >= other.ptr;
00268     }
00269 
00270     size_t operator-(const RangedPtr<T>& other) const {
00271         MOZ_ASSERT(ptr >= other.ptr);
00272         return PointerRangeSize(other.ptr, ptr);
00273     }
00274 
00275   private:
00276     RangedPtr() MOZ_DELETE;
00277     T* operator&() MOZ_DELETE;
00278     operator T*() const MOZ_DELETE;
00279 };
00280 
00281 } /* namespace mozilla */
00282 
00283 #endif  /* mozilla_RangedPtr_h_ */