mfbt/PodOperations.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/mfbt/PodOperations.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,184 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/*
    1.11 + * Operations for zeroing POD types, arrays, and so on.
    1.12 + *
    1.13 + * These operations are preferable to memset, memcmp, and the like because they
    1.14 + * don't require remembering to multiply by sizeof(T), array lengths, and so on
    1.15 + * everywhere.
    1.16 + */
    1.17 +
    1.18 +#ifndef mozilla_PodOperations_h
    1.19 +#define mozilla_PodOperations_h
    1.20 +
    1.21 +#include "mozilla/Array.h"
    1.22 +#include "mozilla/ArrayUtils.h"
    1.23 +#include "mozilla/Attributes.h"
    1.24 +
    1.25 +#include <stdint.h>
    1.26 +#include <string.h>
    1.27 +
    1.28 +namespace mozilla {
    1.29 +
    1.30 +/** Set the contents of |t| to 0. */
    1.31 +template<typename T>
    1.32 +static MOZ_ALWAYS_INLINE void
    1.33 +PodZero(T* t)
    1.34 +{
    1.35 +  memset(t, 0, sizeof(T));
    1.36 +}
    1.37 +
    1.38 +/** Set the contents of |nelem| elements starting at |t| to 0. */
    1.39 +template<typename T>
    1.40 +static MOZ_ALWAYS_INLINE void
    1.41 +PodZero(T* t, size_t nelem)
    1.42 +{
    1.43 +  /*
    1.44 +   * This function is often called with 'nelem' small; we use an inline loop
    1.45 +   * instead of calling 'memset' with a non-constant length.  The compiler
    1.46 +   * should inline the memset call with constant size, though.
    1.47 +   */
    1.48 +  for (T* end = t + nelem; t < end; t++)
    1.49 +    memset(t, 0, sizeof(T));
    1.50 +}
    1.51 +
    1.52 +/*
    1.53 + * Arrays implicitly convert to pointers to their first element, which is
    1.54 + * dangerous when combined with the above PodZero definitions.  Adding an
    1.55 + * overload for arrays is ambiguous, so we need another identifier.  The
    1.56 + * ambiguous overload is left to catch mistaken uses of PodZero; if you get a
    1.57 + * compile error involving PodZero and array types, use PodArrayZero instead.
    1.58 + */
    1.59 +template<typename T, size_t N>
    1.60 +static void PodZero(T (&t)[N]) MOZ_DELETE;
    1.61 +template<typename T, size_t N>
    1.62 +static void PodZero(T (&t)[N], size_t nelem) MOZ_DELETE;
    1.63 +
    1.64 +/** Set the contents of the array |t| to zero. */
    1.65 +template <class T, size_t N>
    1.66 +static MOZ_ALWAYS_INLINE void
    1.67 +PodArrayZero(T (&t)[N])
    1.68 +{
    1.69 +  memset(t, 0, N * sizeof(T));
    1.70 +}
    1.71 +
    1.72 +template <typename T, size_t N>
    1.73 +static MOZ_ALWAYS_INLINE void
    1.74 +PodArrayZero(Array<T, N>& arr)
    1.75 +{
    1.76 +  memset(&arr[0], 0, N * sizeof(T));
    1.77 +}
    1.78 +
    1.79 +/**
    1.80 + * Assign |*src| to |*dst|.  The locations must not be the same and must not
    1.81 + * overlap.
    1.82 + */
    1.83 +template<typename T>
    1.84 +static MOZ_ALWAYS_INLINE void
    1.85 +PodAssign(T* dst, const T* src)
    1.86 +{
    1.87 +  MOZ_ASSERT(dst != src);
    1.88 +  MOZ_ASSERT_IF(src < dst, PointerRangeSize(src, static_cast<const T*>(dst)) >= 1);
    1.89 +  MOZ_ASSERT_IF(dst < src, PointerRangeSize(static_cast<const T*>(dst), src) >= 1);
    1.90 +  memcpy(reinterpret_cast<char*>(dst), reinterpret_cast<const char*>(src), sizeof(T));
    1.91 +}
    1.92 +
    1.93 +/**
    1.94 + * Copy |nelem| T elements from |src| to |dst|.  The two memory ranges must not
    1.95 + * overlap!
    1.96 + */
    1.97 +template<typename T>
    1.98 +static MOZ_ALWAYS_INLINE void
    1.99 +PodCopy(T* dst, const T* src, size_t nelem)
   1.100 +{
   1.101 +  MOZ_ASSERT(dst != src);
   1.102 +  MOZ_ASSERT_IF(src < dst, PointerRangeSize(src, static_cast<const T*>(dst)) >= nelem);
   1.103 +  MOZ_ASSERT_IF(dst < src, PointerRangeSize(static_cast<const T*>(dst), src) >= nelem);
   1.104 +
   1.105 +  if (nelem < 128) {
   1.106 +    /*
   1.107 +     * Avoid using operator= in this loop, as it may have been
   1.108 +     * intentionally deleted by the POD type.
   1.109 +     */
   1.110 +    for (const T* srcend = src + nelem; src < srcend; src++, dst++)
   1.111 +      PodAssign(dst, src);
   1.112 +  } else {
   1.113 +    memcpy(dst, src, nelem * sizeof(T));
   1.114 +  }
   1.115 +}
   1.116 +
   1.117 +template<typename T>
   1.118 +static MOZ_ALWAYS_INLINE void
   1.119 +PodCopy(volatile T* dst, const volatile T* src, size_t nelem)
   1.120 +{
   1.121 +  MOZ_ASSERT(dst != src);
   1.122 +  MOZ_ASSERT_IF(src < dst,
   1.123 +                PointerRangeSize(src, static_cast<const volatile T*>(dst)) >= nelem);
   1.124 +  MOZ_ASSERT_IF(dst < src,
   1.125 +                PointerRangeSize(static_cast<const volatile T*>(dst), src) >= nelem);
   1.126 +
   1.127 +  /*
   1.128 +   * Volatile |dst| requires extra work, because it's undefined behavior to
   1.129 +   * modify volatile objects using the mem* functions.  Just write out the
   1.130 +   * loops manually, using operator= rather than memcpy for the same reason,
   1.131 +   * and let the compiler optimize to the extent it can.
   1.132 +   */
   1.133 +  for (const volatile T* srcend = src + nelem; src < srcend; src++, dst++)
   1.134 +    *dst = *src;
   1.135 +}
   1.136 +
   1.137 +/*
   1.138 + * Copy the contents of the array |src| into the array |dst|, both of size N.
   1.139 + * The arrays must not overlap!
   1.140 + */
   1.141 +template <class T, size_t N>
   1.142 +static MOZ_ALWAYS_INLINE void
   1.143 +PodArrayCopy(T (&dst)[N], const T (&src)[N])
   1.144 +{
   1.145 +  PodCopy(dst, src, N);
   1.146 +}
   1.147 +
   1.148 +/**
   1.149 + * Copy the memory for |nelem| T elements from |src| to |dst|.  If the two
   1.150 + * memory ranges overlap, then the effect is as if the |nelem| elements are
   1.151 + * first copied from |src| to a temporary array, and then from the temporary
   1.152 + * array to |dst|.
   1.153 + */
   1.154 +template<typename T>
   1.155 +static MOZ_ALWAYS_INLINE void
   1.156 +PodMove(T* dst, const T* src, size_t nelem)
   1.157 +{
   1.158 +  MOZ_ASSERT(nelem <= SIZE_MAX / sizeof(T),
   1.159 +             "trying to move an impossible number of elements");
   1.160 +  memmove(dst, src, nelem * sizeof(T));
   1.161 +}
   1.162 +
   1.163 +/**
   1.164 + * Determine whether the |len| elements at |one| are memory-identical to the
   1.165 + * |len| elements at |two|.
   1.166 + */
   1.167 +template<typename T>
   1.168 +static MOZ_ALWAYS_INLINE bool
   1.169 +PodEqual(const T* one, const T* two, size_t len)
   1.170 +{
   1.171 +  if (len < 128) {
   1.172 +    const T* p1end = one + len;
   1.173 +    const T* p1 = one;
   1.174 +    const T* p2 = two;
   1.175 +    for (; p1 < p1end; p1++, p2++) {
   1.176 +      if (*p1 != *p2)
   1.177 +        return false;
   1.178 +    }
   1.179 +    return true;
   1.180 +  }
   1.181 +
   1.182 +  return !memcmp(one, two, len * sizeof(T));
   1.183 +}
   1.184 +
   1.185 +} // namespace mozilla
   1.186 +
   1.187 +#endif /* mozilla_PodOperations_h */

mercurial