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 file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef Utils_h michael@0: #define Utils_h michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/Scoped.h" michael@0: michael@0: /** michael@0: * On architectures that are little endian and that support unaligned reads, michael@0: * we can use direct type, but on others, we want to have a special class michael@0: * to handle conversion and alignment issues. michael@0: */ michael@0: #if !defined(DEBUG) && (defined(__i386__) || defined(__x86_64__)) michael@0: typedef uint16_t le_uint16; michael@0: typedef uint32_t le_uint32; michael@0: #else michael@0: michael@0: /** michael@0: * Template that allows to find an unsigned int type from a (computed) bit size michael@0: */ michael@0: template struct UInt { }; michael@0: template <> struct UInt<16> { typedef uint16_t Type; }; michael@0: template <> struct UInt<32> { typedef uint32_t Type; }; michael@0: michael@0: /** michael@0: * Template to access 2 n-bit sized words as a 2*n-bit sized word, doing michael@0: * conversion from little endian and avoiding alignment issues. michael@0: */ michael@0: template michael@0: class le_to_cpu michael@0: { michael@0: public: michael@0: typedef typename UInt<16 * sizeof(T)>::Type Type; michael@0: michael@0: operator Type() const michael@0: { michael@0: return (b << (sizeof(T) * 8)) | a; michael@0: } michael@0: michael@0: const le_to_cpu& operator =(const Type &v) michael@0: { michael@0: a = v & ((1 << (sizeof(T) * 8)) - 1); michael@0: b = v >> (sizeof(T) * 8); michael@0: return *this; michael@0: } michael@0: michael@0: le_to_cpu() { } michael@0: le_to_cpu(const Type &v) michael@0: { michael@0: operator =(v); michael@0: } michael@0: michael@0: const le_to_cpu& operator +=(const Type &v) michael@0: { michael@0: return operator =(operator Type() + v); michael@0: } michael@0: michael@0: const le_to_cpu& operator ++(int) michael@0: { michael@0: return operator =(operator Type() + 1); michael@0: } michael@0: michael@0: private: michael@0: T a, b; michael@0: }; michael@0: michael@0: /** michael@0: * Type definitions michael@0: */ michael@0: typedef le_to_cpu le_uint16; michael@0: typedef le_to_cpu le_uint32; michael@0: #endif michael@0: michael@0: michael@0: /** michael@0: * AutoCloseFD is a RAII wrapper for POSIX file descriptors michael@0: */ michael@0: struct AutoCloseFDTraits michael@0: { michael@0: typedef int type; michael@0: static int empty() { return -1; } michael@0: static void release(int fd) { if (fd != -1) close(fd); } michael@0: }; michael@0: typedef mozilla::Scoped AutoCloseFD; michael@0: michael@0: /** michael@0: * AutoCloseFILE is a RAII wrapper for POSIX streams michael@0: */ michael@0: struct AutoCloseFILETraits michael@0: { michael@0: typedef FILE *type; michael@0: static FILE *empty() { return nullptr; } michael@0: static void release(FILE *f) { if (f) fclose(f); } michael@0: }; michael@0: typedef mozilla::Scoped AutoCloseFILE; michael@0: michael@0: /** michael@0: * Page alignment helpers michael@0: */ michael@0: static inline size_t PageSize() michael@0: { michael@0: return 4096; michael@0: } michael@0: michael@0: static inline uintptr_t AlignedPtr(uintptr_t ptr, size_t alignment) michael@0: { michael@0: return ptr & ~(alignment - 1); michael@0: } michael@0: michael@0: template michael@0: static inline T *AlignedPtr(T *ptr, size_t alignment) michael@0: { michael@0: return reinterpret_cast( michael@0: AlignedPtr(reinterpret_cast(ptr), alignment)); michael@0: } michael@0: michael@0: template michael@0: static inline T PageAlignedPtr(T ptr) michael@0: { michael@0: return AlignedPtr(ptr, PageSize()); michael@0: } michael@0: michael@0: static inline uintptr_t AlignedEndPtr(uintptr_t ptr, size_t alignment) michael@0: { michael@0: return AlignedPtr(ptr + alignment - 1, alignment); michael@0: } michael@0: michael@0: template michael@0: static inline T *AlignedEndPtr(T *ptr, size_t alignment) michael@0: { michael@0: return reinterpret_cast( michael@0: AlignedEndPtr(reinterpret_cast(ptr), alignment)); michael@0: } michael@0: michael@0: template michael@0: static inline T PageAlignedEndPtr(T ptr) michael@0: { michael@0: return AlignedEndPtr(ptr, PageSize()); michael@0: } michael@0: michael@0: static inline size_t AlignedSize(size_t size, size_t alignment) michael@0: { michael@0: return (size + alignment - 1) & ~(alignment - 1); michael@0: } michael@0: michael@0: static inline size_t PageAlignedSize(size_t size) michael@0: { michael@0: return AlignedSize(size, PageSize()); michael@0: } michael@0: michael@0: static inline bool IsAlignedPtr(uintptr_t ptr, size_t alignment) michael@0: { michael@0: return ptr % alignment == 0; michael@0: } michael@0: michael@0: template michael@0: static inline bool IsAlignedPtr(T *ptr, size_t alignment) michael@0: { michael@0: return IsAlignedPtr(reinterpret_cast(ptr), alignment); michael@0: } michael@0: michael@0: template michael@0: static inline bool IsPageAlignedPtr(T ptr) michael@0: { michael@0: return IsAlignedPtr(ptr, PageSize()); michael@0: } michael@0: michael@0: static inline bool IsAlignedSize(size_t size, size_t alignment) michael@0: { michael@0: return size % alignment == 0; michael@0: } michael@0: michael@0: static inline bool IsPageAlignedSize(size_t size) michael@0: { michael@0: return IsAlignedSize(size, PageSize()); michael@0: } michael@0: michael@0: static inline size_t PageNumber(size_t size) michael@0: { michael@0: return (size + PageSize() - 1) / PageSize(); michael@0: } michael@0: michael@0: /** michael@0: * MemoryRange stores a pointer, size pair. michael@0: */ michael@0: class MemoryRange michael@0: { michael@0: public: michael@0: MemoryRange(void *buf, size_t length): buf(buf), length(length) { } michael@0: michael@0: void Assign(void *b, size_t len) { michael@0: buf = b; michael@0: length = len; michael@0: } michael@0: michael@0: void Assign(const MemoryRange& other) { michael@0: buf = other.buf; michael@0: length = other.length; michael@0: } michael@0: michael@0: void *get() const michael@0: { michael@0: return buf; michael@0: } michael@0: michael@0: operator void *() const michael@0: { michael@0: return buf; michael@0: } michael@0: michael@0: operator unsigned char *() const michael@0: { michael@0: return reinterpret_cast(buf); michael@0: } michael@0: michael@0: bool operator ==(void *ptr) const { michael@0: return buf == ptr; michael@0: } michael@0: michael@0: bool operator ==(unsigned char *ptr) const { michael@0: return buf == ptr; michael@0: } michael@0: michael@0: void *operator +(off_t offset) const michael@0: { michael@0: return reinterpret_cast(buf) + offset; michael@0: } michael@0: michael@0: /** michael@0: * Returns whether the given address is within the mapped range michael@0: */ michael@0: bool Contains(void *ptr) const michael@0: { michael@0: return (ptr >= buf) && (ptr < reinterpret_cast(buf) + length); michael@0: } michael@0: michael@0: /** michael@0: * Returns the length of the mapped range michael@0: */ michael@0: size_t GetLength() const michael@0: { michael@0: return length; michael@0: } michael@0: michael@0: static MemoryRange mmap(void *addr, size_t length, int prot, int flags, michael@0: int fd, off_t offset) { michael@0: return MemoryRange(::mmap(addr, length, prot, flags, fd, offset), length); michael@0: } michael@0: michael@0: private: michael@0: void *buf; michael@0: size_t length; michael@0: }; michael@0: michael@0: /** michael@0: * MappedPtr is a RAII wrapper for mmap()ed memory. It can be used as michael@0: * a simple void * or unsigned char *. michael@0: * michael@0: * It is defined as a derivative of a template that allows to use a michael@0: * different unmapping strategy. michael@0: */ michael@0: template michael@0: class GenericMappedPtr: public MemoryRange michael@0: { michael@0: public: michael@0: GenericMappedPtr(void *buf, size_t length): MemoryRange(buf, length) { } michael@0: GenericMappedPtr(const MemoryRange& other): MemoryRange(other) { } michael@0: GenericMappedPtr(): MemoryRange(MAP_FAILED, 0) { } michael@0: michael@0: void Assign(void *b, size_t len) { michael@0: if (get() != MAP_FAILED) michael@0: static_cast(this)->munmap(get(), GetLength()); michael@0: MemoryRange::Assign(b, len); michael@0: } michael@0: michael@0: void Assign(const MemoryRange& other) { michael@0: Assign(other.get(), other.GetLength()); michael@0: } michael@0: michael@0: ~GenericMappedPtr() michael@0: { michael@0: if (get() != MAP_FAILED) michael@0: static_cast(this)->munmap(get(), GetLength()); michael@0: } michael@0: michael@0: }; michael@0: michael@0: struct MappedPtr: public GenericMappedPtr michael@0: { michael@0: MappedPtr(void *buf, size_t length) michael@0: : GenericMappedPtr(buf, length) { } michael@0: MappedPtr(const MemoryRange& other) michael@0: : GenericMappedPtr(other) { } michael@0: MappedPtr(): GenericMappedPtr() { } michael@0: michael@0: private: michael@0: friend class GenericMappedPtr; michael@0: void munmap(void *buf, size_t length) michael@0: { michael@0: ::munmap(buf, length); michael@0: } michael@0: }; michael@0: michael@0: /** michael@0: * UnsizedArray is a way to access raw arrays of data in memory. michael@0: * michael@0: * struct S { ... }; michael@0: * UnsizedArray a(buf); michael@0: * UnsizedArray b; b.Init(buf); michael@0: * michael@0: * This is roughly equivalent to michael@0: * const S *a = reinterpret_cast(buf); michael@0: * const S *b = nullptr; b = reinterpret_cast(buf); michael@0: * michael@0: * An UnsizedArray has no known length, and it's up to the caller to make michael@0: * sure the accessed memory is mapped and makes sense. michael@0: */ michael@0: template michael@0: class UnsizedArray michael@0: { michael@0: public: michael@0: typedef size_t idx_t; michael@0: michael@0: /** michael@0: * Constructors and Initializers michael@0: */ michael@0: UnsizedArray(): contents(nullptr) { } michael@0: UnsizedArray(const void *buf): contents(reinterpret_cast(buf)) { } michael@0: michael@0: void Init(const void *buf) michael@0: { michael@0: MOZ_ASSERT(contents == nullptr); michael@0: contents = reinterpret_cast(buf); michael@0: } michael@0: michael@0: /** michael@0: * Returns the nth element of the array michael@0: */ michael@0: const T &operator[](const idx_t index) const michael@0: { michael@0: MOZ_ASSERT(contents); michael@0: return contents[index]; michael@0: } michael@0: michael@0: operator const T *() const michael@0: { michael@0: return contents; michael@0: } michael@0: /** michael@0: * Returns whether the array points somewhere michael@0: */ michael@0: operator bool() const michael@0: { michael@0: return contents != nullptr; michael@0: } michael@0: private: michael@0: const T *contents; michael@0: }; michael@0: michael@0: /** michael@0: * Array, like UnsizedArray, is a way to access raw arrays of data in memory. michael@0: * Unlike UnsizedArray, it has a known length, and is enumerable with an michael@0: * iterator. michael@0: * michael@0: * struct S { ... }; michael@0: * Array a(buf, len); michael@0: * UnsizedArray b; b.Init(buf, len); michael@0: * michael@0: * In the above examples, len is the number of elements in the array. It is michael@0: * also possible to initialize an Array with the buffer size: michael@0: * michael@0: * Array c; c.InitSize(buf, size); michael@0: * michael@0: * It is also possible to initialize an Array in two steps, only providing michael@0: * one data at a time: michael@0: * michael@0: * Array d; michael@0: * d.Init(buf); michael@0: * d.Init(len); // or d.InitSize(size); michael@0: * michael@0: */ michael@0: template michael@0: class Array: public UnsizedArray michael@0: { michael@0: public: michael@0: typedef typename UnsizedArray::idx_t idx_t; michael@0: michael@0: /** michael@0: * Constructors and Initializers michael@0: */ michael@0: Array(): UnsizedArray(), length(0) { } michael@0: Array(const void *buf, const idx_t length) michael@0: : UnsizedArray(buf), length(length) { } michael@0: michael@0: void Init(const void *buf) michael@0: { michael@0: UnsizedArray::Init(buf); michael@0: } michael@0: michael@0: void Init(const idx_t len) michael@0: { michael@0: MOZ_ASSERT(length == 0); michael@0: length = len; michael@0: } michael@0: michael@0: void InitSize(const idx_t size) michael@0: { michael@0: Init(size / sizeof(T)); michael@0: } michael@0: michael@0: void Init(const void *buf, const idx_t len) michael@0: { michael@0: UnsizedArray::Init(buf); michael@0: Init(len); michael@0: } michael@0: michael@0: void InitSize(const void *buf, const idx_t size) michael@0: { michael@0: UnsizedArray::Init(buf); michael@0: InitSize(size); michael@0: } michael@0: michael@0: /** michael@0: * Returns the nth element of the array michael@0: */ michael@0: const T &operator[](const idx_t index) const michael@0: { michael@0: MOZ_ASSERT(index < length); michael@0: MOZ_ASSERT(operator bool()); michael@0: return UnsizedArray::operator[](index); michael@0: } michael@0: michael@0: /** michael@0: * Returns the number of elements in the array michael@0: */ michael@0: idx_t numElements() const michael@0: { michael@0: return length; michael@0: } michael@0: michael@0: /** michael@0: * Returns whether the array points somewhere and has at least one element. michael@0: */ michael@0: operator bool() const michael@0: { michael@0: return (length > 0) && UnsizedArray::operator bool(); michael@0: } michael@0: michael@0: /** michael@0: * Iterator for an Array. Use is similar to that of STL const_iterators: michael@0: * michael@0: * struct S { ... }; michael@0: * Array a(buf, len); michael@0: * for (Array::iterator it = a.begin(); it < a.end(); ++it) { michael@0: * // Do something with *it. michael@0: * } michael@0: */ michael@0: class iterator michael@0: { michael@0: public: michael@0: iterator(): item(nullptr) { } michael@0: michael@0: const T &operator *() const michael@0: { michael@0: return *item; michael@0: } michael@0: michael@0: const T *operator ->() const michael@0: { michael@0: return item; michael@0: } michael@0: michael@0: iterator &operator ++() michael@0: { michael@0: ++item; michael@0: return *this; michael@0: } michael@0: michael@0: bool operator<(const iterator &other) const michael@0: { michael@0: return item < other.item; michael@0: } michael@0: protected: michael@0: friend class Array; michael@0: iterator(const T &item): item(&item) { } michael@0: michael@0: private: michael@0: const T *item; michael@0: }; michael@0: michael@0: /** michael@0: * Returns an iterator pointing at the beginning of the Array michael@0: */ michael@0: iterator begin() const { michael@0: if (length) michael@0: return iterator(UnsizedArray::operator[](0)); michael@0: return iterator(); michael@0: } michael@0: michael@0: /** michael@0: * Returns an iterator pointing past the end of the Array michael@0: */ michael@0: iterator end() const { michael@0: if (length) michael@0: return iterator(UnsizedArray::operator[](length)); michael@0: return iterator(); michael@0: } michael@0: michael@0: /** michael@0: * Reverse iterator for an Array. Use is similar to that of STL michael@0: * const_reverse_iterators: michael@0: * michael@0: * struct S { ... }; michael@0: * Array a(buf, len); michael@0: * for (Array::reverse_iterator it = a.rbegin(); it < a.rend(); ++it) { michael@0: * // Do something with *it. michael@0: * } michael@0: */ michael@0: class reverse_iterator michael@0: { michael@0: public: michael@0: reverse_iterator(): item(nullptr) { } michael@0: michael@0: const T &operator *() const michael@0: { michael@0: const T *tmp = item; michael@0: return *--tmp; michael@0: } michael@0: michael@0: const T *operator ->() const michael@0: { michael@0: return &operator*(); michael@0: } michael@0: michael@0: reverse_iterator &operator ++() michael@0: { michael@0: --item; michael@0: return *this; michael@0: } michael@0: michael@0: bool operator<(const reverse_iterator &other) const michael@0: { michael@0: return item > other.item; michael@0: } michael@0: protected: michael@0: friend class Array; michael@0: reverse_iterator(const T &item): item(&item) { } michael@0: michael@0: private: michael@0: const T *item; michael@0: }; michael@0: michael@0: /** michael@0: * Returns a reverse iterator pointing at the end of the Array michael@0: */ michael@0: reverse_iterator rbegin() const { michael@0: if (length) michael@0: return reverse_iterator(UnsizedArray::operator[](length)); michael@0: return reverse_iterator(); michael@0: } michael@0: michael@0: /** michael@0: * Returns a reverse iterator pointing past the beginning of the Array michael@0: */ michael@0: reverse_iterator rend() const { michael@0: if (length) michael@0: return reverse_iterator(UnsizedArray::operator[](0)); michael@0: return reverse_iterator(); michael@0: } michael@0: private: michael@0: idx_t length; michael@0: }; michael@0: michael@0: /** michael@0: * Transforms a pointer-to-function to a pointer-to-object pointing at the michael@0: * same address. michael@0: */ michael@0: template michael@0: void *FunctionPtr(T func) michael@0: { michael@0: union { michael@0: void *ptr; michael@0: T func; michael@0: } f; michael@0: f.func = func; michael@0: return f.ptr; michael@0: } michael@0: michael@0: #endif /* Utils_h */ michael@0: