/* 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 <pthread.h>
#include <stdint.h>
#include <stddef.h>
#include <sys/mman.h>
#include <unistd.h>
#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 <
int s>
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 <
typename T>
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<
unsigned char> le_uint16;
typedef le_to_cpu<le_uint16> 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<size_t, mozilla::ReleaseAcquire> 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 <
typename T>
static inline T* AlignedPtr(T* ptr, size_t alignment) {
return reinterpret_cast<T*>(
AlignedPtr(
reinterpret_cast<uintptr_t>(ptr), alignment));
}
template <
typename T>
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 <
typename T>
static inline T* AlignedEndPtr(T* ptr, size_t alignment) {
return reinterpret_cast<T*>(
AlignedEndPtr(
reinterpret_cast<uintptr_t>(ptr), alignment));
}
template <
typename T>
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 <
typename T>
static inline bool IsAlignedPtr(T* ptr, size_t alignment) {
return IsAlignedPtr(
reinterpret_cast<uintptr_t>(ptr), alignment);
}
template <
typename T>
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<
unsigned char*>(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<
char*>(buf) + offset;
}
/**
* Returns whether the given address is within the mapped range
*/
bool Contains(
void* ptr)
const {
return (ptr >= buf) && (ptr <
reinterpret_cast<
char*>(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 <
typename T>
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<T*>(
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<T*>(
this)->munmap(get(), GetLength());
}
void release() { MemoryRange::Assign(MAP_FAILED, 0); }
};
struct MappedPtr :
public GenericMappedPtr<MappedPtr> {
MappedPtr(
void* buf, size_t length)
: GenericMappedPtr<MappedPtr>(buf, length) {}
MOZ_IMPLICIT MappedPtr(
const MemoryRange& other)
: GenericMappedPtr<MappedPtr>(other) {}
MappedPtr() : GenericMappedPtr<MappedPtr>() {}
private:
friend class GenericMappedPtr<MappedPtr>;
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<S> a(buf);
* UnsizedArray<S> b; b.Init(buf);
*
* This is roughly equivalent to
* const S *a = reinterpret_cast<const S *>(buf);
* const S *b = nullptr; b = reinterpret_cast<const S *>(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 <
typename T>
class UnsizedArray {
public:
typedef size_t idx_t;
/**
* Constructors and Initializers
*/
UnsizedArray() : contents(nullptr) {}
explicit UnsizedArray(
const void* buf)
: contents(
reinterpret_cast<
const T*>(buf)) {}
void Init(
const void* buf) {
MOZ_ASSERT(contents == nullptr);
contents =
reinterpret_cast<
const T*>(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<S> a(buf, len);
* UnsizedArray<S> 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<S> c; c.InitSize(buf, size);
*
* It is also possible to initialize an Array in two steps, only providing
* one data at a time:
*
* Array<S> d;
* d.Init(buf);
* d.Init(len); // or d.InitSize(size);
*
*/
template <
typename T>
class Array :
public UnsizedArray<T> {
public:
typedef typename UnsizedArray<T>::idx_t idx_t;
/**
* Constructors and Initializers
*/
Array() : UnsizedArray<T>(), length(0) {}
Array(
const void* buf,
const idx_t length)
: UnsizedArray<T>(buf), length(length) {}
void Init(
const void* buf) { UnsizedArray<T>::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<T>::Init(buf);
Init(len);
}
void InitSize(
const void* buf,
const idx_t size) {
UnsizedArray<T>::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<T>::
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<T>::
operator bool();
}
/**
* Iterator for an Array. Use is similar to that of STL const_iterators:
*
* struct S { ... };
* Array<S> a(buf, len);
* for (Array<S>::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<T>;
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<T>::
operator[](0));
return iterator();
}
/**
* Returns an iterator pointing past the end of the Array
*/
iterator end()
const {
if (length)
return iterator(UnsizedArray<T>::
operator[](length));
return iterator();
}
/**
* Reverse iterator for an Array. Use is similar to that of STL
* const_reverse_iterators:
*
* struct S { ... };
* Array<S> a(buf, len);
* for (Array<S>::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<T>;
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<T>::
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<T>::
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 <
typename T>
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 */