michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * Operations for zeroing POD types, arrays, and so on. michael@0: * michael@0: * These operations are preferable to memset, memcmp, and the like because they michael@0: * don't require remembering to multiply by sizeof(T), array lengths, and so on michael@0: * everywhere. michael@0: */ michael@0: michael@0: #ifndef mozilla_PodOperations_h michael@0: #define mozilla_PodOperations_h michael@0: michael@0: #include "mozilla/Array.h" michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: namespace mozilla { michael@0: michael@0: /** Set the contents of |t| to 0. */ michael@0: template michael@0: static MOZ_ALWAYS_INLINE void michael@0: PodZero(T* t) michael@0: { michael@0: memset(t, 0, sizeof(T)); michael@0: } michael@0: michael@0: /** Set the contents of |nelem| elements starting at |t| to 0. */ michael@0: template michael@0: static MOZ_ALWAYS_INLINE void michael@0: PodZero(T* t, size_t nelem) michael@0: { michael@0: /* michael@0: * This function is often called with 'nelem' small; we use an inline loop michael@0: * instead of calling 'memset' with a non-constant length. The compiler michael@0: * should inline the memset call with constant size, though. michael@0: */ michael@0: for (T* end = t + nelem; t < end; t++) michael@0: memset(t, 0, sizeof(T)); michael@0: } michael@0: michael@0: /* michael@0: * Arrays implicitly convert to pointers to their first element, which is michael@0: * dangerous when combined with the above PodZero definitions. Adding an michael@0: * overload for arrays is ambiguous, so we need another identifier. The michael@0: * ambiguous overload is left to catch mistaken uses of PodZero; if you get a michael@0: * compile error involving PodZero and array types, use PodArrayZero instead. michael@0: */ michael@0: template michael@0: static void PodZero(T (&t)[N]) MOZ_DELETE; michael@0: template michael@0: static void PodZero(T (&t)[N], size_t nelem) MOZ_DELETE; michael@0: michael@0: /** Set the contents of the array |t| to zero. */ michael@0: template michael@0: static MOZ_ALWAYS_INLINE void michael@0: PodArrayZero(T (&t)[N]) michael@0: { michael@0: memset(t, 0, N * sizeof(T)); michael@0: } michael@0: michael@0: template michael@0: static MOZ_ALWAYS_INLINE void michael@0: PodArrayZero(Array& arr) michael@0: { michael@0: memset(&arr[0], 0, N * sizeof(T)); michael@0: } michael@0: michael@0: /** michael@0: * Assign |*src| to |*dst|. The locations must not be the same and must not michael@0: * overlap. michael@0: */ michael@0: template michael@0: static MOZ_ALWAYS_INLINE void michael@0: PodAssign(T* dst, const T* src) michael@0: { michael@0: MOZ_ASSERT(dst != src); michael@0: MOZ_ASSERT_IF(src < dst, PointerRangeSize(src, static_cast(dst)) >= 1); michael@0: MOZ_ASSERT_IF(dst < src, PointerRangeSize(static_cast(dst), src) >= 1); michael@0: memcpy(reinterpret_cast(dst), reinterpret_cast(src), sizeof(T)); michael@0: } michael@0: michael@0: /** michael@0: * Copy |nelem| T elements from |src| to |dst|. The two memory ranges must not michael@0: * overlap! michael@0: */ michael@0: template michael@0: static MOZ_ALWAYS_INLINE void michael@0: PodCopy(T* dst, const T* src, size_t nelem) michael@0: { michael@0: MOZ_ASSERT(dst != src); michael@0: MOZ_ASSERT_IF(src < dst, PointerRangeSize(src, static_cast(dst)) >= nelem); michael@0: MOZ_ASSERT_IF(dst < src, PointerRangeSize(static_cast(dst), src) >= nelem); michael@0: michael@0: if (nelem < 128) { michael@0: /* michael@0: * Avoid using operator= in this loop, as it may have been michael@0: * intentionally deleted by the POD type. michael@0: */ michael@0: for (const T* srcend = src + nelem; src < srcend; src++, dst++) michael@0: PodAssign(dst, src); michael@0: } else { michael@0: memcpy(dst, src, nelem * sizeof(T)); michael@0: } michael@0: } michael@0: michael@0: template michael@0: static MOZ_ALWAYS_INLINE void michael@0: PodCopy(volatile T* dst, const volatile T* src, size_t nelem) michael@0: { michael@0: MOZ_ASSERT(dst != src); michael@0: MOZ_ASSERT_IF(src < dst, michael@0: PointerRangeSize(src, static_cast(dst)) >= nelem); michael@0: MOZ_ASSERT_IF(dst < src, michael@0: PointerRangeSize(static_cast(dst), src) >= nelem); michael@0: michael@0: /* michael@0: * Volatile |dst| requires extra work, because it's undefined behavior to michael@0: * modify volatile objects using the mem* functions. Just write out the michael@0: * loops manually, using operator= rather than memcpy for the same reason, michael@0: * and let the compiler optimize to the extent it can. michael@0: */ michael@0: for (const volatile T* srcend = src + nelem; src < srcend; src++, dst++) michael@0: *dst = *src; michael@0: } michael@0: michael@0: /* michael@0: * Copy the contents of the array |src| into the array |dst|, both of size N. michael@0: * The arrays must not overlap! michael@0: */ michael@0: template michael@0: static MOZ_ALWAYS_INLINE void michael@0: PodArrayCopy(T (&dst)[N], const T (&src)[N]) michael@0: { michael@0: PodCopy(dst, src, N); michael@0: } michael@0: michael@0: /** michael@0: * Copy the memory for |nelem| T elements from |src| to |dst|. If the two michael@0: * memory ranges overlap, then the effect is as if the |nelem| elements are michael@0: * first copied from |src| to a temporary array, and then from the temporary michael@0: * array to |dst|. michael@0: */ michael@0: template michael@0: static MOZ_ALWAYS_INLINE void michael@0: PodMove(T* dst, const T* src, size_t nelem) michael@0: { michael@0: MOZ_ASSERT(nelem <= SIZE_MAX / sizeof(T), michael@0: "trying to move an impossible number of elements"); michael@0: memmove(dst, src, nelem * sizeof(T)); michael@0: } michael@0: michael@0: /** michael@0: * Determine whether the |len| elements at |one| are memory-identical to the michael@0: * |len| elements at |two|. michael@0: */ michael@0: template michael@0: static MOZ_ALWAYS_INLINE bool michael@0: PodEqual(const T* one, const T* two, size_t len) michael@0: { michael@0: if (len < 128) { michael@0: const T* p1end = one + len; michael@0: const T* p1 = one; michael@0: const T* p2 = two; michael@0: for (; p1 < p1end; p1++, p2++) { michael@0: if (*p1 != *p2) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: return !memcmp(one, two, len * sizeof(T)); michael@0: } michael@0: michael@0: } // namespace mozilla michael@0: michael@0: #endif /* mozilla_PodOperations_h */