ArrayUtils.h (3777B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* 8 * Implements various helper functions related to arrays. 9 */ 10 11 #ifndef mozilla_ArrayUtils_h 12 #define mozilla_ArrayUtils_h 13 14 #include "mozilla/Assertions.h" 15 #include "mozilla/Attributes.h" 16 17 #include <stddef.h> 18 #include <stdint.h> 19 20 #ifdef __cplusplus 21 # include <algorithm> 22 # include <type_traits> 23 24 namespace mozilla { 25 26 /* 27 * Safely subtract two pointers when it is known that aEnd >= aBegin, yielding a 28 * size_t result. 29 * 30 * Ordinary pointer subtraction yields a ptrdiff_t result, which, being signed, 31 * has insufficient range to express the distance between pointers at opposite 32 * ends of the address space. Furthermore, most compilers use ptrdiff_t to 33 * represent the intermediate byte address distance, before dividing by 34 * sizeof(T); if that intermediate result overflows, they'll produce results 35 * with the wrong sign even when the correct scaled distance would fit in a 36 * ptrdiff_t. 37 */ 38 template <class T> 39 MOZ_ALWAYS_INLINE size_t PointerRangeSize(T* aBegin, T* aEnd) { 40 MOZ_ASSERT(aEnd >= aBegin); 41 return (size_t(aEnd) - size_t(aBegin)) / sizeof(T); 42 } 43 44 /** 45 * std::equal has subpar ergonomics. 46 */ 47 48 template <typename T, typename U, size_t N> 49 bool ArrayEqual(const T (&a)[N], const U (&b)[N]) { 50 return std::equal(a, a + N, b); 51 } 52 53 template <typename T, typename U> 54 bool ArrayEqual(const T* const a, const U* const b, const size_t n) { 55 return std::equal(a, a + n, b); 56 } 57 58 namespace detail { 59 60 template <typename AlignType, typename Pointee, typename = void> 61 struct AlignedChecker { 62 static void test(const Pointee* aPtr) { 63 MOZ_ASSERT((uintptr_t(aPtr) % alignof(AlignType)) == 0, 64 "performing a range-check with a misaligned pointer"); 65 } 66 }; 67 68 template <typename AlignType, typename Pointee> 69 struct AlignedChecker<AlignType, Pointee, 70 std::enable_if_t<std::is_void_v<AlignType>>> { 71 static void test(const Pointee* aPtr) {} 72 }; 73 74 } // namespace detail 75 76 /** 77 * Determines whether |aPtr| points at an object in the range [aBegin, aEnd). 78 * 79 * |aPtr| must have the same alignment as |aBegin| and |aEnd|. This usually 80 * should be achieved by ensuring |aPtr| points at a |U|, not just that it 81 * points at a |T|. 82 * 83 * It is a usage error for any argument to be misaligned. 84 * 85 * It's okay for T* to be void*, and if so U* may also be void*. In the latter 86 * case no argument is required to be aligned (obviously, as void* implies no 87 * particular alignment). 88 */ 89 template <typename T, typename U> 90 inline std::enable_if_t<std::is_same_v<T, U> || std::is_base_of<T, U>::value || 91 std::is_void_v<T>, 92 bool> 93 IsInRange(const T* aPtr, const U* aBegin, const U* aEnd) { 94 MOZ_ASSERT(aBegin <= aEnd); 95 detail::AlignedChecker<U, T>::test(aPtr); 96 detail::AlignedChecker<U, U>::test(aBegin); 97 detail::AlignedChecker<U, U>::test(aEnd); 98 return aBegin <= reinterpret_cast<const U*>(aPtr) && 99 reinterpret_cast<const U*>(aPtr) < aEnd; 100 } 101 102 /** 103 * Convenience version of the above method when the valid range is specified as 104 * uintptr_t values. As above, |aPtr| must be aligned, and |aBegin| and |aEnd| 105 * must be aligned with respect to |T|. 106 */ 107 template <typename T> 108 inline bool IsInRange(const T* aPtr, uintptr_t aBegin, uintptr_t aEnd) { 109 return IsInRange(aPtr, reinterpret_cast<const T*>(aBegin), 110 reinterpret_cast<const T*>(aEnd)); 111 } 112 113 } /* namespace mozilla */ 114 115 #endif /* __cplusplus */ 116 117 #endif /* mozilla_ArrayUtils_h */