ArrayUtils.h 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. /*
  6. * Implements various helper functions related to arrays.
  7. */
  8. #ifndef mozilla_ArrayUtils_h
  9. #define mozilla_ArrayUtils_h
  10. #include "mozilla/Assertions.h"
  11. #include "mozilla/Attributes.h"
  12. #include <stddef.h>
  13. #ifdef __cplusplus
  14. #include "mozilla/Alignment.h"
  15. #include "mozilla/Array.h"
  16. #include "mozilla/EnumeratedArray.h"
  17. #include "mozilla/TypeTraits.h"
  18. namespace mozilla {
  19. /*
  20. * Safely subtract two pointers when it is known that aEnd >= aBegin, yielding a
  21. * size_t result.
  22. *
  23. * Ordinary pointer subtraction yields a ptrdiff_t result, which, being signed,
  24. * has insufficient range to express the distance between pointers at opposite
  25. * ends of the address space. Furthermore, most compilers use ptrdiff_t to
  26. * represent the intermediate byte address distance, before dividing by
  27. * sizeof(T); if that intermediate result overflows, they'll produce results
  28. * with the wrong sign even when the correct scaled distance would fit in a
  29. * ptrdiff_t.
  30. */
  31. template<class T>
  32. MOZ_ALWAYS_INLINE size_t
  33. PointerRangeSize(T* aBegin, T* aEnd)
  34. {
  35. MOZ_ASSERT(aEnd >= aBegin);
  36. return (size_t(aEnd) - size_t(aBegin)) / sizeof(T);
  37. }
  38. /*
  39. * Compute the length of an array with constant length. (Use of this method
  40. * with a non-array pointer will not compile.)
  41. *
  42. * Beware of the implicit trailing '\0' when using this with string constants.
  43. */
  44. template<typename T, size_t N>
  45. constexpr size_t
  46. ArrayLength(T (&aArr)[N])
  47. {
  48. return N;
  49. }
  50. template<typename T, size_t N>
  51. constexpr size_t
  52. ArrayLength(const Array<T, N>& aArr)
  53. {
  54. return N;
  55. }
  56. template<typename E, E N, typename T>
  57. constexpr size_t
  58. ArrayLength(const EnumeratedArray<E, N, T>& aArr)
  59. {
  60. return size_t(N);
  61. }
  62. /*
  63. * Compute the address one past the last element of a constant-length array.
  64. *
  65. * Beware of the implicit trailing '\0' when using this with string constants.
  66. */
  67. template<typename T, size_t N>
  68. constexpr T*
  69. ArrayEnd(T (&aArr)[N])
  70. {
  71. return aArr + ArrayLength(aArr);
  72. }
  73. template<typename T, size_t N>
  74. constexpr T*
  75. ArrayEnd(Array<T, N>& aArr)
  76. {
  77. return &aArr[0] + ArrayLength(aArr);
  78. }
  79. template<typename T, size_t N>
  80. constexpr const T*
  81. ArrayEnd(const Array<T, N>& aArr)
  82. {
  83. return &aArr[0] + ArrayLength(aArr);
  84. }
  85. namespace detail {
  86. template<typename AlignType, typename Pointee,
  87. typename = EnableIf<!IsVoid<AlignType>::value>>
  88. struct AlignedChecker
  89. {
  90. static void
  91. test(const Pointee* aPtr)
  92. {
  93. MOZ_ASSERT((uintptr_t(aPtr) % MOZ_ALIGNOF(AlignType)) == 0,
  94. "performing a range-check with a misaligned pointer");
  95. }
  96. };
  97. template<typename AlignType, typename Pointee>
  98. struct AlignedChecker<AlignType, Pointee>
  99. {
  100. static void
  101. test(const Pointee* aPtr)
  102. {
  103. }
  104. };
  105. } // namespace detail
  106. /**
  107. * Determines whether |aPtr| points at an object in the range [aBegin, aEnd).
  108. *
  109. * |aPtr| must have the same alignment as |aBegin| and |aEnd|. This usually
  110. * should be achieved by ensuring |aPtr| points at a |U|, not just that it
  111. * points at a |T|.
  112. *
  113. * It is a usage error for any argument to be misaligned.
  114. *
  115. * It's okay for T* to be void*, and if so U* may also be void*. In the latter
  116. * case no argument is required to be aligned (obviously, as void* implies no
  117. * particular alignment).
  118. */
  119. template<typename T, typename U>
  120. inline typename EnableIf<IsSame<T, U>::value ||
  121. IsBaseOf<T, U>::value ||
  122. IsVoid<T>::value,
  123. bool>::Type
  124. IsInRange(const T* aPtr, const U* aBegin, const U* aEnd)
  125. {
  126. MOZ_ASSERT(aBegin <= aEnd);
  127. detail::AlignedChecker<U, T>::test(aPtr);
  128. detail::AlignedChecker<U, U>::test(aBegin);
  129. detail::AlignedChecker<U, U>::test(aEnd);
  130. return aBegin <= reinterpret_cast<const U*>(aPtr) &&
  131. reinterpret_cast<const U*>(aPtr) < aEnd;
  132. }
  133. /**
  134. * Convenience version of the above method when the valid range is specified as
  135. * uintptr_t values. As above, |aPtr| must be aligned, and |aBegin| and |aEnd|
  136. * must be aligned with respect to |T|.
  137. */
  138. template<typename T>
  139. inline bool
  140. IsInRange(const T* aPtr, uintptr_t aBegin, uintptr_t aEnd)
  141. {
  142. return IsInRange(aPtr,
  143. reinterpret_cast<const T*>(aBegin),
  144. reinterpret_cast<const T*>(aEnd));
  145. }
  146. namespace detail {
  147. /*
  148. * Helper for the MOZ_ARRAY_LENGTH() macro to make the length a typesafe
  149. * compile-time constant even on compilers lacking constexpr support.
  150. */
  151. template <typename T, size_t N>
  152. char (&ArrayLengthHelper(T (&array)[N]))[N];
  153. } /* namespace detail */
  154. } /* namespace mozilla */
  155. #endif /* __cplusplus */
  156. /*
  157. * MOZ_ARRAY_LENGTH() is an alternative to mozilla::ArrayLength() for C files
  158. * that can't use C++ template functions and for static_assert() calls that
  159. * can't call ArrayLength() when it is not a C++11 constexpr function.
  160. */
  161. #ifdef __cplusplus
  162. # define MOZ_ARRAY_LENGTH(array) sizeof(mozilla::detail::ArrayLengthHelper(array))
  163. #else
  164. # define MOZ_ARRAY_LENGTH(array) (sizeof(array)/sizeof((array)[0]))
  165. #endif
  166. #endif /* mozilla_ArrayUtils_h */