PodOperations.h (6271B)
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 * Operations for zeroing POD types, arrays, and so on. 9 * 10 * These operations are preferable to memset, memcmp, and the like because they 11 * don't require remembering to multiply by sizeof(T), array lengths, and so on 12 * everywhere. 13 */ 14 15 #ifndef mozilla_PodOperations_h 16 #define mozilla_PodOperations_h 17 18 #include "mozilla/Assertions.h" 19 #include "mozilla/Attributes.h" 20 21 #include <cstring> 22 #include <limits> 23 #include <type_traits> 24 25 namespace mozilla { 26 27 template <typename T, size_t Length> 28 class Array; 29 30 template <typename T> 31 class NotNull; 32 33 /** Set the contents of |aT| to 0. */ 34 template <typename T> 35 static MOZ_ALWAYS_INLINE void PodZero(T* aT) { 36 static_assert(std::is_trivially_copyable_v<T>, 37 "PodZero requires trivially copyable types"); 38 memset(aT, 0, sizeof(T)); 39 } 40 41 /** Set the contents of |aNElem| elements starting at |aT| to 0. */ 42 template <typename T> 43 static MOZ_ALWAYS_INLINE void PodZero(T* aT, size_t aNElem) { 44 static_assert(std::is_trivially_copyable_v<T>, 45 "PodZero requires trivially copyable types"); 46 /* 47 * NB: If the caller uses a constant size, both GCC and Clang inline the 48 * memset call if they find it profitable. 49 * 50 * If the value is dynamic, some might think that it's more profitable to 51 * perform an explicit loop over the aNElem. It turns out Clang rolls back the 52 * loop anyway, so even if GCC doesn't, keep the codebase simple and clearly 53 * convey the intent instead of trying to outsmart the compiler. 54 */ 55 MOZ_ASSERT(aNElem <= std::numeric_limits<size_t>::max() / sizeof(T), 56 "trying to zero an impossible number of elements"); 57 memset(aT, 0, sizeof(T) * aNElem); 58 } 59 60 /** Set the contents of |aNElem| elements starting at |aT| to 0. */ 61 template <typename T> 62 static MOZ_ALWAYS_INLINE void PodZero(NotNull<T*> aT, size_t aNElem) { 63 PodZero(aT.get(), aNElem); 64 } 65 66 /* 67 * Arrays implicitly convert to pointers to their first element, which is 68 * dangerous when combined with the above PodZero definitions. Adding an 69 * overload for arrays is ambiguous, so we need another identifier. The 70 * ambiguous overload is left to catch mistaken uses of PodZero; if you get a 71 * compile error involving PodZero and array types, use PodArrayZero instead. 72 */ 73 template <typename T, size_t N> 74 static void PodZero(T (&aT)[N]) = delete; 75 template <typename T, size_t N> 76 static void PodZero(T (&aT)[N], size_t aNElem) = delete; 77 78 /** Set the contents of the array |aT| to zero. */ 79 template <class T, size_t N> 80 static MOZ_ALWAYS_INLINE void PodArrayZero(T (&aT)[N]) { 81 static_assert(std::is_trivially_copyable_v<T>, 82 "PodArrayZero requires trivially copyable types"); 83 static_assert(N < std::numeric_limits<size_t>::max() / sizeof(T)); 84 memset(aT, 0, N * sizeof(T)); 85 } 86 87 template <typename T, size_t N> 88 static MOZ_ALWAYS_INLINE void PodArrayZero(Array<T, N>& aArr) { 89 static_assert(std::is_trivially_copyable_v<T>, 90 "PodArrayZero requires trivially copyable types"); 91 static_assert(N < std::numeric_limits<size_t>::max() / sizeof(T)); 92 memset(&aArr[0], 0, N * sizeof(T)); 93 } 94 95 /** 96 * Copy |aNElem| T elements from |aSrc| to |aDst|. The two memory ranges must 97 * not overlap! 98 */ 99 template <typename T> 100 static MOZ_ALWAYS_INLINE void PodCopy(T* aDst, const T* aSrc, size_t aNElem) { 101 static_assert(std::is_trivially_copyable_v<T>, 102 "PodCopy requires trivially copyable types"); 103 MOZ_ASSERT(aDst + aNElem <= aSrc || aSrc + aNElem <= aDst, 104 "destination and source must not overlap"); 105 MOZ_ASSERT(aNElem <= std::numeric_limits<size_t>::max() / sizeof(T), 106 "trying to copy an impossible number of elements"); 107 108 // Linux memcpy for small sizes seems slower than on other 109 // platforms. So we use a loop for small sizes there only. 110 // 111 // See Bug 1967062 for details. 112 #if defined(XP_LINUX) 113 if (aNElem < 128) { 114 for (const T* srcend = aSrc + aNElem; aSrc < srcend; aSrc++, aDst++) { 115 *aDst = *aSrc; 116 } 117 return; 118 } 119 #endif 120 121 memcpy(aDst, aSrc, aNElem * sizeof(T)); 122 } 123 124 template <typename T> 125 static MOZ_ALWAYS_INLINE void PodCopy(volatile T* aDst, const volatile T* aSrc, 126 size_t aNElem) { 127 static_assert(std::is_trivially_copyable_v<T>, 128 "PodCopy requires trivially copyable types"); 129 MOZ_ASSERT(aDst + aNElem <= aSrc || aSrc + aNElem <= aDst, 130 "destination and source must not overlap"); 131 132 /* 133 * Volatile |aDst| requires extra work, because it's undefined behavior to 134 * modify volatile objects using the mem* functions. Just write out the 135 * loops manually, using operator= rather than memcpy for the same reason, 136 * and let the compiler optimize to the extent it can. 137 */ 138 for (const volatile T* srcend = aSrc + aNElem; aSrc < srcend; 139 aSrc++, aDst++) { 140 *aDst = *aSrc; 141 } 142 } 143 144 /* 145 * Copy the contents of the array |aSrc| into the array |aDst|, both of size N. 146 * The arrays must not overlap! 147 */ 148 template <class T, size_t N> 149 static MOZ_ALWAYS_INLINE void PodArrayCopy(T (&aDst)[N], const T (&aSrc)[N]) { 150 PodCopy(aDst, aSrc, N); 151 } 152 153 /** 154 * Copy the memory for |aNElem| T elements from |aSrc| to |aDst|. If the two 155 * memory ranges overlap, then the effect is as if the |aNElem| elements are 156 * first copied from |aSrc| to a temporary array, and then from the temporary 157 * array to |aDst|. 158 */ 159 template <typename T> 160 static MOZ_ALWAYS_INLINE void PodMove(T* aDst, const T* aSrc, size_t aNElem) { 161 static_assert(std::is_trivially_copyable_v<T>, 162 "PodMove requires trivially copyable types"); 163 MOZ_ASSERT(aNElem <= std::numeric_limits<size_t>::max() / sizeof(T), 164 "trying to move an impossible number of elements"); 165 memmove(aDst, aSrc, aNElem * sizeof(T)); 166 } 167 168 /** 169 * Looking for a PodEqual? Use ArrayEqual from ArrayUtils.h. 170 * Note that we *cannot* use memcmp for this, due to padding bytes, etc.. 171 */ 172 173 } // namespace mozilla 174 175 #endif /* mozilla_PodOperations_h */