DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (e7438140bb20)

VCS Links

nsStringBuffer

nsStringBufferCanary

Macros

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef nsStringBuffer_h__
#define nsStringBuffer_h__

#include <atomic>
#include "mozilla/MemoryReporting.h"

template<class T> struct already_AddRefed;

/*
 * Add a canary field to protect against double-frees of nsStringBuffer and
 * other potential heap corruptions.  We intend to back this out before 58 hits
 * beta.
 */
#if (defined(DEBUG) || defined(NIGHTLY_BUILD)) && !defined(MOZ_ASAN)
# define STRING_BUFFER_CANARY 1
#endif

#ifdef STRING_BUFFER_CANARY
enum nsStringBufferCanary : uint32_t {
  CANARY_OK = 0xaf57c8fa,
  CANARY_POISON = 0x534dc0f5
};
#endif

/**
 * This structure precedes the string buffers "we" allocate.  It may be the
 * case that nsTAString::mData does not point to one of these special
 * buffers.  The mDataFlags member variable distinguishes the buffer type.
 *
 * When this header is in use, it enables reference counting, and capacity
 * tracking.  NOTE: A string buffer can be modified only if its reference
 * count is 1.
 */
class nsStringBuffer
{
private:
  friend class CheckStaticAtomSizes;

  std::atomic<uint32_t> mRefCount;
  uint32_t mStorageSize;

#ifdef STRING_BUFFER_CANARY
  uint32_t mCanary;
#endif

public:

  /**
   * Allocates a new string buffer, with given size in bytes and a
   * reference count of one.  When the string buffer is no longer needed,
   * it should be released via Release.
   *
   * It is up to the caller to set the bytes corresponding to the string
   * buffer by calling the Data method to fetch the raw data pointer.  Care
   * must be taken to properly null terminate the character array.  The
   * storage size can be greater than the length of the actual string
   * (i.e., it is not required that the null terminator appear in the last
   * storage unit of the string buffer's data).
   *
   * @return new string buffer or null if out of memory.
   */
  static already_AddRefed<nsStringBuffer> Alloc(size_t aStorageSize);

  /**
   * Resizes the given string buffer to the specified storage size.  This
   * method must not be called on a readonly string buffer.  Use this API
   * carefully!!
   *
   * This method behaves like the ANSI-C realloc function.  (i.e., If the
   * allocation fails, null will be returned and the given string buffer
   * will remain unmodified.)
   *
   * @see IsReadonly
   */
  static nsStringBuffer* Realloc(nsStringBuffer* aBuf, size_t aStorageSize);

  /**
   * Increment the reference count on this string buffer.
   */
  void NS_FASTCALL AddRef();

  /**
   * Decrement the reference count on this string buffer.  The string
   * buffer will be destroyed when its reference count reaches zero.
   */
  void NS_FASTCALL Release();

  /**
   * This method returns the string buffer corresponding to the given data
   * pointer.  The data pointer must have been returned previously by a
   * call to the nsStringBuffer::Data method.
   */
  static nsStringBuffer* FromData(void* aData)
  {
    nsStringBuffer* sb = reinterpret_cast<nsStringBuffer*>(aData) - 1;
#ifdef STRING_BUFFER_CANARY
    if (MOZ_UNLIKELY(sb->mCanary != CANARY_OK))
      sb->FromDataCanaryCheckFailed();
#endif
    return sb;
  }

  /**
   * This method returns the data pointer for this string buffer.
   */
  void* Data() const
  {
    return const_cast<char*>(reinterpret_cast<const char*>(this + 1));
  }

  /**
   * This function returns the storage size of a string buffer in bytes.
   * This value is the same value that was originally passed to Alloc (or
   * Realloc).
   */
  uint32_t StorageSize() const
  {
    return mStorageSize;
  }

  /**
   * If this method returns false, then the caller can be sure that their
   * reference to the string buffer is the only reference to the string
   * buffer, and therefore it has exclusive access to the string buffer and
   * associated data.  However, if this function returns true, then other
   * consumers may rely on the data in this buffer being immutable and
   * other threads may access this buffer simultaneously.
   */
  bool IsReadonly() const
  {
    // This doesn't lead to the destruction of the buffer, so we don't
    // need to perform acquire memory synchronization for the normal
    // reason that a reference count needs acquire synchronization
    // (ensuring that all writes to the object made on other threads are
    // visible to the thread destroying the object).
    //
    // We then need to consider the possibility that there were prior
    // writes to the buffer on a different thread:  one that has either
    // since released its reference count, or one that also has access
    // to this buffer through the same reference.  There are two ways
    // for that to happen: either the buffer pointer or a data structure
    // (e.g., string object) pointing to the buffer was transferred from
    // one thread to another, or the data structure pointing to the
    // buffer was already visible on both threads.  In the first case
    // (transfer), the transfer of data from one thread to another would
    // have handled the memory synchronization.  In the latter case
    // (data structure visible on both threads), the caller needed some
    // sort of higher level memory synchronization to protect against
    // the string object being mutated at the same time on multiple
    // threads.
    return mRefCount.load(std::memory_order_relaxed) > 1;
  }

  /**
   * The FromString methods return a string buffer for the given string
   * object or null if the string object does not have a string buffer.
   * The reference count of the string buffer is NOT incremented by these
   * methods.  If the caller wishes to hold onto the returned value, then
   * the returned string buffer must have its reference count incremented
   * via a call to the AddRef method.
   */
  static nsStringBuffer* FromString(const nsAString& aStr);
  static nsStringBuffer* FromString(const nsACString& aStr);

  /**
   * The ToString methods assign this string buffer to a given string
   * object.  If the string object does not support sharable string
   * buffers, then its value will be set to a copy of the given string
   * buffer.  Otherwise, these methods increment the reference count of the
   * given string buffer.  It is important to specify the length (in
   * storage units) of the string contained in the string buffer since the
   * length of the string may be less than its storage size.  The string
   * must have a null terminator at the offset specified by |len|.
   *
   * NOTE: storage size is measured in bytes even for wide strings;
   *       however, string length is always measured in storage units
   *       (2-byte units for wide strings).
   */
  void ToString(uint32_t aLen, nsAString& aStr, bool aMoveOwnership = false);
  void ToString(uint32_t aLen, nsACString& aStr, bool aMoveOwnership = false);

  /**
   * This measures the size only if the StringBuffer is unshared.
   */
  size_t SizeOfIncludingThisIfUnshared(mozilla::MallocSizeOf aMallocSizeOf) const;

  /**
   * This measures the size regardless of whether the StringBuffer is
   * unshared.
   *
   * WARNING: Only use this if you really know what you are doing, because
   * it can easily lead to double-counting strings.  If you do use them,
   * please explain clearly in a comment why it's safe and won't lead to
   * double-counting.
   */
  size_t SizeOfIncludingThisEvenIfShared(mozilla::MallocSizeOf aMallocSizeOf) const;

#ifdef STRING_BUFFER_CANARY
  /*
   * Called by FromData if the canary check failed.  This is out-of-line in
   * nsSubstring.cpp so that MOZ_CRASH_UNSAFE_PRINTF is available via #includes.
   * It is not available in FromData due to #include-order.
   */
  void FromDataCanaryCheckFailed() const;
#endif
};

#endif /* !defined(nsStringBuffer_h__ */