Bug 1889091 - Part 3: Specialize calls to native functions with variadic parameters...
[gecko.git] / mozglue / linker / Utils.h
blob4aea454d6e65226a0a559ffbbda7ff2baeb315e0
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #ifndef Utils_h
6 #define Utils_h
8 #include <pthread.h>
9 #include <stdint.h>
10 #include <stddef.h>
11 #include <sys/mman.h>
12 #include <unistd.h>
13 #include "mozilla/Assertions.h"
14 #include "mozilla/Atomics.h"
16 /**
17 * On architectures that are little endian and that support unaligned reads,
18 * we can use direct type, but on others, we want to have a special class
19 * to handle conversion and alignment issues.
21 #if !defined(DEBUG) && (defined(__i386__) || defined(__x86_64__))
22 typedef uint16_t le_uint16;
23 typedef uint32_t le_uint32;
24 #else
26 /**
27 * Template that allows to find an unsigned int type from a (computed) bit size
29 template <int s>
30 struct UInt {};
31 template <>
32 struct UInt<16> {
33 typedef uint16_t Type;
35 template <>
36 struct UInt<32> {
37 typedef uint32_t Type;
40 /**
41 * Template to access 2 n-bit sized words as a 2*n-bit sized word, doing
42 * conversion from little endian and avoiding alignment issues.
44 template <typename T>
45 class le_to_cpu {
46 public:
47 typedef typename UInt<16 * sizeof(T)>::Type Type;
49 operator Type() const { return (b << (sizeof(T) * 8)) | a; }
51 const le_to_cpu& operator=(const Type& v) {
52 a = v & ((1 << (sizeof(T) * 8)) - 1);
53 b = v >> (sizeof(T) * 8);
54 return *this;
57 le_to_cpu() {}
58 explicit le_to_cpu(const Type& v) { operator=(v); }
60 const le_to_cpu& operator+=(const Type& v) {
61 return operator=(operator Type() + v);
64 const le_to_cpu& operator++(int) { return operator=(operator Type() + 1); }
66 private:
67 T a, b;
70 /**
71 * Type definitions
73 typedef le_to_cpu<unsigned char> le_uint16;
74 typedef le_to_cpu<le_uint16> le_uint32;
75 #endif
77 struct AutoCloseFD {
78 const int fd;
80 MOZ_IMPLICIT AutoCloseFD(int fd) : fd(fd) {}
81 ~AutoCloseFD() {
82 if (fd != -1) close(fd);
84 operator int() const { return fd; }
87 extern mozilla::Atomic<size_t, mozilla::ReleaseAcquire> gPageSize;
89 /**
90 * Page alignment helpers
92 static size_t PageSize() {
93 if (!gPageSize) {
94 gPageSize = sysconf(_SC_PAGESIZE);
97 return gPageSize;
100 static inline uintptr_t AlignedPtr(uintptr_t ptr, size_t alignment) {
101 return ptr & ~(alignment - 1);
104 template <typename T>
105 static inline T* AlignedPtr(T* ptr, size_t alignment) {
106 return reinterpret_cast<T*>(
107 AlignedPtr(reinterpret_cast<uintptr_t>(ptr), alignment));
110 template <typename T>
111 static inline T PageAlignedPtr(T ptr) {
112 return AlignedPtr(ptr, PageSize());
115 static inline uintptr_t AlignedEndPtr(uintptr_t ptr, size_t alignment) {
116 return AlignedPtr(ptr + alignment - 1, alignment);
119 template <typename T>
120 static inline T* AlignedEndPtr(T* ptr, size_t alignment) {
121 return reinterpret_cast<T*>(
122 AlignedEndPtr(reinterpret_cast<uintptr_t>(ptr), alignment));
125 template <typename T>
126 static inline T PageAlignedEndPtr(T ptr) {
127 return AlignedEndPtr(ptr, PageSize());
130 static inline size_t AlignedSize(size_t size, size_t alignment) {
131 return (size + alignment - 1) & ~(alignment - 1);
134 static inline size_t PageAlignedSize(size_t size) {
135 return AlignedSize(size, PageSize());
138 static inline bool IsAlignedPtr(uintptr_t ptr, size_t alignment) {
139 return ptr % alignment == 0;
142 template <typename T>
143 static inline bool IsAlignedPtr(T* ptr, size_t alignment) {
144 return IsAlignedPtr(reinterpret_cast<uintptr_t>(ptr), alignment);
147 template <typename T>
148 static inline bool IsPageAlignedPtr(T ptr) {
149 return IsAlignedPtr(ptr, PageSize());
152 static inline bool IsAlignedSize(size_t size, size_t alignment) {
153 return size % alignment == 0;
156 static inline bool IsPageAlignedSize(size_t size) {
157 return IsAlignedSize(size, PageSize());
160 static inline size_t PageNumber(size_t size) {
161 return (size + PageSize() - 1) / PageSize();
165 * MemoryRange stores a pointer, size pair.
167 class MemoryRange {
168 public:
169 MemoryRange(void* buf, size_t length) : buf(buf), length(length) {}
171 void Assign(void* b, size_t len) {
172 buf = b;
173 length = len;
176 void Assign(const MemoryRange& other) {
177 buf = other.buf;
178 length = other.length;
181 void* get() const { return buf; }
183 operator void*() const { return buf; }
185 operator unsigned char*() const {
186 return reinterpret_cast<unsigned char*>(buf);
189 bool operator==(void* ptr) const { return buf == ptr; }
191 bool operator==(unsigned char* ptr) const { return buf == ptr; }
193 void* operator+(off_t offset) const {
194 return reinterpret_cast<char*>(buf) + offset;
198 * Returns whether the given address is within the mapped range
200 bool Contains(void* ptr) const {
201 return (ptr >= buf) && (ptr < reinterpret_cast<char*>(buf) + length);
205 * Returns the length of the mapped range
207 size_t GetLength() const { return length; }
209 static MemoryRange mmap(void* addr, size_t length, int prot, int flags,
210 int fd, off_t offset) {
211 return MemoryRange(::mmap(addr, length, prot, flags, fd, offset), length);
214 private:
215 void* buf;
216 size_t length;
220 * MappedPtr is a RAII wrapper for mmap()ed memory. It can be used as
221 * a simple void * or unsigned char *.
223 * It is defined as a derivative of a template that allows to use a
224 * different unmapping strategy.
226 template <typename T>
227 class GenericMappedPtr : public MemoryRange {
228 public:
229 GenericMappedPtr(void* buf, size_t length) : MemoryRange(buf, length) {}
230 explicit GenericMappedPtr(const MemoryRange& other) : MemoryRange(other) {}
231 GenericMappedPtr() : MemoryRange(MAP_FAILED, 0) {}
233 void Assign(void* b, size_t len) {
234 if (get() != MAP_FAILED) static_cast<T*>(this)->munmap(get(), GetLength());
235 MemoryRange::Assign(b, len);
238 void Assign(const MemoryRange& other) {
239 Assign(other.get(), other.GetLength());
242 ~GenericMappedPtr() {
243 if (get() != MAP_FAILED) static_cast<T*>(this)->munmap(get(), GetLength());
246 void release() { MemoryRange::Assign(MAP_FAILED, 0); }
249 struct MappedPtr : public GenericMappedPtr<MappedPtr> {
250 MappedPtr(void* buf, size_t length)
251 : GenericMappedPtr<MappedPtr>(buf, length) {}
252 MOZ_IMPLICIT MappedPtr(const MemoryRange& other)
253 : GenericMappedPtr<MappedPtr>(other) {}
254 MappedPtr() : GenericMappedPtr<MappedPtr>() {}
256 private:
257 friend class GenericMappedPtr<MappedPtr>;
258 void munmap(void* buf, size_t length) { ::munmap(buf, length); }
262 * UnsizedArray is a way to access raw arrays of data in memory.
264 * struct S { ... };
265 * UnsizedArray<S> a(buf);
266 * UnsizedArray<S> b; b.Init(buf);
268 * This is roughly equivalent to
269 * const S *a = reinterpret_cast<const S *>(buf);
270 * const S *b = nullptr; b = reinterpret_cast<const S *>(buf);
272 * An UnsizedArray has no known length, and it's up to the caller to make
273 * sure the accessed memory is mapped and makes sense.
275 template <typename T>
276 class UnsizedArray {
277 public:
278 typedef size_t idx_t;
281 * Constructors and Initializers
283 UnsizedArray() : contents(nullptr) {}
284 explicit UnsizedArray(const void* buf)
285 : contents(reinterpret_cast<const T*>(buf)) {}
287 void Init(const void* buf) {
288 MOZ_ASSERT(contents == nullptr);
289 contents = reinterpret_cast<const T*>(buf);
293 * Returns the nth element of the array
295 const T& operator[](const idx_t index) const {
296 MOZ_ASSERT(contents);
297 return contents[index];
300 operator const T*() const { return contents; }
302 * Returns whether the array points somewhere
304 explicit operator bool() const { return contents != nullptr; }
306 private:
307 const T* contents;
311 * Array, like UnsizedArray, is a way to access raw arrays of data in memory.
312 * Unlike UnsizedArray, it has a known length, and is enumerable with an
313 * iterator.
315 * struct S { ... };
316 * Array<S> a(buf, len);
317 * UnsizedArray<S> b; b.Init(buf, len);
319 * In the above examples, len is the number of elements in the array. It is
320 * also possible to initialize an Array with the buffer size:
322 * Array<S> c; c.InitSize(buf, size);
324 * It is also possible to initialize an Array in two steps, only providing
325 * one data at a time:
327 * Array<S> d;
328 * d.Init(buf);
329 * d.Init(len); // or d.InitSize(size);
332 template <typename T>
333 class Array : public UnsizedArray<T> {
334 public:
335 typedef typename UnsizedArray<T>::idx_t idx_t;
338 * Constructors and Initializers
340 Array() : UnsizedArray<T>(), length(0) {}
341 Array(const void* buf, const idx_t length)
342 : UnsizedArray<T>(buf), length(length) {}
344 void Init(const void* buf) { UnsizedArray<T>::Init(buf); }
346 void Init(const idx_t len) {
347 MOZ_ASSERT(length == 0);
348 length = len;
351 void InitSize(const idx_t size) { Init(size / sizeof(T)); }
353 void Init(const void* buf, const idx_t len) {
354 UnsizedArray<T>::Init(buf);
355 Init(len);
358 void InitSize(const void* buf, const idx_t size) {
359 UnsizedArray<T>::Init(buf);
360 InitSize(size);
364 * Returns the nth element of the array
366 const T& operator[](const idx_t index) const {
367 MOZ_ASSERT(index < length);
368 MOZ_ASSERT(operator bool());
369 return UnsizedArray<T>::operator[](index);
373 * Returns the number of elements in the array
375 idx_t numElements() const { return length; }
378 * Returns whether the array points somewhere and has at least one element.
380 explicit operator bool() const {
381 return (length > 0) && UnsizedArray<T>::operator bool();
385 * Iterator for an Array. Use is similar to that of STL const_iterators:
387 * struct S { ... };
388 * Array<S> a(buf, len);
389 * for (Array<S>::iterator it = a.begin(); it < a.end(); ++it) {
390 * // Do something with *it.
393 class iterator {
394 public:
395 iterator() : item(nullptr) {}
397 const T& operator*() const { return *item; }
399 const T* operator->() const { return item; }
401 iterator& operator++() {
402 ++item;
403 return *this;
406 bool operator<(const iterator& other) const { return item < other.item; }
408 protected:
409 friend class Array<T>;
410 explicit iterator(const T& item) : item(&item) {}
412 private:
413 const T* item;
417 * Returns an iterator pointing at the beginning of the Array
419 iterator begin() const {
420 if (length) return iterator(UnsizedArray<T>::operator[](0));
421 return iterator();
425 * Returns an iterator pointing past the end of the Array
427 iterator end() const {
428 if (length) return iterator(UnsizedArray<T>::operator[](length));
429 return iterator();
433 * Reverse iterator for an Array. Use is similar to that of STL
434 * const_reverse_iterators:
436 * struct S { ... };
437 * Array<S> a(buf, len);
438 * for (Array<S>::reverse_iterator it = a.rbegin(); it < a.rend(); ++it) {
439 * // Do something with *it.
442 class reverse_iterator {
443 public:
444 reverse_iterator() : item(nullptr) {}
446 const T& operator*() const {
447 const T* tmp = item;
448 return *--tmp;
451 const T* operator->() const { return &operator*(); }
453 reverse_iterator& operator++() {
454 --item;
455 return *this;
458 bool operator<(const reverse_iterator& other) const {
459 return item > other.item;
462 protected:
463 friend class Array<T>;
464 explicit reverse_iterator(const T& item) : item(&item) {}
466 private:
467 const T* item;
471 * Returns a reverse iterator pointing at the end of the Array
473 reverse_iterator rbegin() const {
474 if (length) return reverse_iterator(UnsizedArray<T>::operator[](length));
475 return reverse_iterator();
479 * Returns a reverse iterator pointing past the beginning of the Array
481 reverse_iterator rend() const {
482 if (length) return reverse_iterator(UnsizedArray<T>::operator[](0));
483 return reverse_iterator();
486 private:
487 idx_t length;
491 * Transforms a pointer-to-function to a pointer-to-object pointing at the
492 * same address.
494 template <typename T>
495 void* FunctionPtr(T func) {
496 union {
497 void* ptr;
498 T func;
499 } f;
500 f.func = func;
501 return f.ptr;
504 class AutoLock {
505 public:
506 explicit AutoLock(pthread_mutex_t* mutex) : mutex(mutex) {
507 if (pthread_mutex_lock(mutex)) MOZ_CRASH("pthread_mutex_lock failed");
509 ~AutoLock() {
510 if (pthread_mutex_unlock(mutex)) MOZ_CRASH("pthread_mutex_unlock failed");
513 private:
514 pthread_mutex_t* mutex;
517 #endif /* Utils_h */