/* -*- 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"namespacemozilla{/* * 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<typenameT>classRangedPtr{T*ptr;#ifdef DEBUGT*constrangeStart;T*constrangeEnd;#endiftypedefvoid(RangedPtr::*ConvertibleToBool)();voidnonNull(){}voidcheckSanity(){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 DEBUGreturnRangedPtr<T>(p,rangeStart,rangeEnd);#elsereturnRangedPtr<T>(p,NULL,size_t(0));#endif}uintptr_tasUintptr()const{returnuintptr_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_tlength):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_tlength):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_tN>RangedPtr(T(&arr)[N]):ptr(arr)#ifdef DEBUG,rangeStart(arr),rangeEnd(arr+N)#endif{checkSanity();}T*get()const{returnptr;}operatorConvertibleToBool()const{returnptr?&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=(constRangedPtr<T>&other){MOZ_ASSERT(rangeStart==other.rangeStart);MOZ_ASSERT(rangeEnd==other.rangeEnd);ptr=other.ptr;checkSanity();return*this;}RangedPtr<T>operator+(size_tinc){MOZ_ASSERT(inc<=size_t(-1)/sizeof(T));MOZ_ASSERT(asUintptr()+inc*sizeof(T)>=asUintptr());returncreate(ptr+inc);}RangedPtr<T>operator-(size_tdec){MOZ_ASSERT(dec<=size_t(-1)/sizeof(T));MOZ_ASSERT(asUintptr()-dec*sizeof(T)<=asUintptr());returncreate(ptr-dec);}/* * You can assign a raw pointer into a RangedPtr if the raw pointer is * within the range specified at creation. */template<typenameU>RangedPtr<T>&operator=(U*p){*this=create(p);return*this;}template<typenameU>RangedPtr<T>&operator=(constRangedPtr<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;returnrcp;}RangedPtr<T>&operator--(){return(*this-=1);}RangedPtr<T>operator--(int){RangedPtr<T>rcp=*this;--*this;returnrcp;}RangedPtr<T>&operator+=(size_tinc){*this=*this+inc;return*this;}RangedPtr<T>&operator-=(size_tdec){*this=*this-dec;return*this;}T&operator[](intindex)const{MOZ_ASSERT(size_t(index>0?index:-index)<=size_t(-1)/sizeof(T));return*create(ptr+index);}T&operator*()const{return*ptr;}template<typenameU>booloperator==(constRangedPtr<U>&other)const{returnptr==other.ptr;}template<typenameU>booloperator!=(constRangedPtr<U>&other)const{return!(*this==other);}template<typenameU>booloperator==(constU*u)const{returnptr==u;}template<typenameU>booloperator!=(constU*u)const{return!(*this==u);}template<typenameU>booloperator<(constRangedPtr<U>&other)const{returnptr<other.ptr;}template<typenameU>booloperator<=(constRangedPtr<U>&other)const{returnptr<=other.ptr;}template<typenameU>booloperator>(constRangedPtr<U>&other)const{returnptr>other.ptr;}template<typenameU>booloperator>=(constRangedPtr<U>&other)const{returnptr>=other.ptr;}size_toperator-(constRangedPtr<T>&other)const{MOZ_ASSERT(ptr>=other.ptr);returnPointerRangeSize(other.ptr,ptr);}private:RangedPtr()MOZ_DELETE;T*operator&()MOZ_DELETE;};}/* namespace mozilla */#endif /* mozilla_RangedPtr_h_ */