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