StringBuffer.h (12171B)
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 #ifndef StringBuffer_h__ 8 #define StringBuffer_h__ 9 10 #include <atomic> 11 #include <cstring> 12 #include "mozilla/CheckedInt.h" 13 #include "mozilla/MemoryReporting.h" 14 #include "mozilla/Assertions.h" 15 #include "mozilla/AlreadyAddRefed.h" 16 #include "mozilla/Maybe.h" 17 #include "mozilla/RefCounted.h" 18 #include "mozmemory.h" 19 20 namespace mozilla { 21 22 /** 23 * This structure precedes the string buffers "we" allocate. It may be the 24 * case that nsTAString::mData does not point to one of these special 25 * buffers. The mDataFlags member variable distinguishes the buffer type. 26 * 27 * When this header is in use, it enables reference counting, and capacity 28 * tracking. NOTE: A string buffer can be modified only if its reference 29 * count is 1. 30 */ 31 class StringBuffer { 32 private: 33 std::atomic<uint32_t> mRefCount; 34 uint32_t mStorageSize; 35 36 public: 37 MOZ_DECLARE_REFCOUNTED_TYPENAME(StringBuffer) 38 39 /** 40 * Allocates a new string buffer, with given size in bytes and a 41 * reference count of one. When the string buffer is no longer needed, 42 * it should be released via Release. 43 * 44 * It is up to the caller to set the bytes corresponding to the string 45 * buffer by calling the Data method to fetch the raw data pointer. Care 46 * must be taken to properly null terminate the character array. The 47 * storage size can be greater than the length of the actual string 48 * (i.e., it is not required that the null terminator appear in the last 49 * storage unit of the string buffer's data). 50 * 51 * This guarantees that StorageSize() returns aSize if the returned 52 * buffer is non-null. Some callers like nsAttrValue rely on it. 53 * 54 * @return new string buffer or null if out of memory. 55 */ 56 static already_AddRefed<StringBuffer> Alloc( 57 size_t aSize, mozilla::Maybe<arena_id_t> aArena = mozilla::Nothing()) { 58 MOZ_ASSERT(aSize != 0, "zero capacity allocation not allowed"); 59 MOZ_ASSERT(sizeof(StringBuffer) + aSize <= size_t(uint32_t(-1)) && 60 sizeof(StringBuffer) + aSize > aSize, 61 "mStorageSize will truncate"); 62 63 size_t bytes = sizeof(StringBuffer) + aSize; 64 void* hdr = aArena ? moz_arena_malloc(*aArena, bytes) : malloc(bytes); 65 if (!hdr) { 66 return nullptr; 67 } 68 return ConstructInPlace(hdr, aSize); 69 } 70 71 /** 72 * Like Alloc, but use aBuffer instead of allocating a new buffer. This can 73 * be used when the caller already has a malloced buffer of the right size and 74 * allocating a new one would be too expensive. 75 * 76 * aStorageSize must be the string's length in bytes (including the null 77 * terminator). The caller must initialize all of these bytes either before or 78 * after calling this function. 79 * 80 * @return the new StringBuffer header. 81 */ 82 static already_AddRefed<StringBuffer> ConstructInPlace(void* aBuffer, 83 size_t aStorageSize) { 84 MOZ_ASSERT(aBuffer, "must have a valid buffer"); 85 MOZ_ASSERT(aStorageSize != 0, "zero capacity StringBuffer not allowed"); 86 auto* hdr = new (aBuffer) StringBuffer(); 87 hdr->mRefCount = 1; 88 hdr->mStorageSize = aStorageSize; 89 detail::RefCountLogger::logAddRef(hdr, 1); 90 return already_AddRefed(hdr); 91 } 92 93 /** 94 * Returns true if (aLength + 1) * sizeof(CharT) is a valid allocation size 95 * for Alloc. Adds +1 to aLength for the null-terminator. 96 */ 97 template <typename CharT> 98 static constexpr bool IsValidLength(size_t aLength) { 99 auto checkedSize = 100 (CheckedUint32(aLength) + 1) * sizeof(CharT) + sizeof(StringBuffer); 101 return checkedSize.isValid(); 102 } 103 104 /** 105 * Returns a string buffer initialized with the given string on it, or null on 106 * OOM. 107 * Note that this will allocate extra space for the trailing null byte, which 108 * this method will add. 109 */ 110 static already_AddRefed<StringBuffer> Create(const char16_t* aData, 111 size_t aLength) { 112 return DoCreate(aData, aLength); 113 } 114 static already_AddRefed<StringBuffer> Create(const char* aData, 115 size_t aLength) { 116 return DoCreate(aData, aLength); 117 } 118 static already_AddRefed<StringBuffer> Create(const unsigned char* aData, 119 size_t aLength) { 120 return DoCreate(aData, aLength); 121 } 122 123 /** 124 * Resizes the given string buffer to the specified storage size. This 125 * method must not be called on a readonly string buffer. Use this API 126 * carefully!! 127 * 128 * This method behaves like the ANSI-C realloc function. (i.e., If the 129 * allocation fails, null will be returned and the given string buffer 130 * will remain unmodified.) 131 * 132 * @see IsReadonly 133 */ 134 static StringBuffer* Realloc( 135 StringBuffer* aHdr, size_t aSize, 136 mozilla::Maybe<arena_id_t> aArena = mozilla::Nothing()) { 137 MOZ_ASSERT(aSize != 0, "zero capacity allocation not allowed"); 138 MOZ_ASSERT(sizeof(StringBuffer) + aSize <= size_t(uint32_t(-1)) && 139 sizeof(StringBuffer) + aSize > aSize, 140 "mStorageSize will truncate"); 141 142 // no point in trying to save ourselves if we hit this assertion 143 MOZ_ASSERT(!aHdr->IsReadonly(), "|Realloc| attempted on readonly string"); 144 145 // Treat this as a release and addref for refcounting purposes, since we 146 // just asserted that the refcount is 1. If we don't do that, refcount 147 // logging will claim we've leaked all sorts of stuff. 148 { 149 detail::RefCountLogger::ReleaseLogger logger(aHdr); 150 logger.logRelease(0); 151 } 152 153 size_t bytes = sizeof(StringBuffer) + aSize; 154 aHdr = aArena ? (StringBuffer*)moz_arena_realloc(*aArena, aHdr, bytes) 155 : (StringBuffer*)realloc(aHdr, bytes); 156 if (aHdr) { 157 detail::RefCountLogger::logAddRef(aHdr, 1); 158 aHdr->mStorageSize = aSize; 159 } 160 161 return aHdr; 162 } 163 164 void AddRef() { 165 // Memory synchronization is not required when incrementing a 166 // reference count. The first increment of a reference count on a 167 // thread is not important, since the first use of the object on a 168 // thread can happen before it. What is important is the transfer 169 // of the pointer to that thread, which may happen prior to the 170 // first increment on that thread. The necessary memory 171 // synchronization is done by the mechanism that transfers the 172 // pointer between threads. 173 uint32_t count = mRefCount.fetch_add(1, std::memory_order_relaxed) + 1; 174 detail::RefCountLogger::logAddRef(this, count); 175 } 176 177 void Release() { 178 // Since this may be the last release on this thread, we need release 179 // semantics so that prior writes on this thread are visible to the thread 180 // that destroys the object when it reads mValue with acquire semantics. 181 detail::RefCountLogger::ReleaseLogger logger(this); 182 uint32_t count = mRefCount.fetch_sub(1, std::memory_order_release) - 1; 183 logger.logRelease(count); 184 if (count == 0) { 185 // We're going to destroy the object on this thread, so we need acquire 186 // semantics to synchronize with the memory released by the last release 187 // on other threads, that is, to ensure that writes prior to that release 188 // are now visible on this thread. 189 count = mRefCount.load(std::memory_order_acquire); 190 free(this); // We were allocated with malloc. 191 } 192 } 193 194 /** 195 * This method returns the string buffer corresponding to the given data 196 * pointer. The data pointer must have been returned previously by a 197 * call to the StringBuffer::Data method. 198 */ 199 static StringBuffer* FromData(void* aData) { 200 return reinterpret_cast<StringBuffer*>(aData) - 1; 201 } 202 203 /** 204 * This method returns the data pointer for this string buffer. 205 */ 206 void* Data() const { 207 return const_cast<char*>(reinterpret_cast<const char*>(this + 1)); 208 } 209 210 /** 211 * This function returns the storage size of a string buffer in bytes. 212 * This value is the same value that was originally passed to Alloc (or 213 * Realloc). 214 */ 215 uint32_t StorageSize() const { return mStorageSize; } 216 217 /** 218 * This function returns the allocation size of a string buffer in bytes. 219 * This includes the size of the StringBuffer header. 220 */ 221 uint32_t AllocationSize() const { 222 return sizeof(StringBuffer) + StorageSize(); 223 } 224 225 /** 226 * If this method returns false, then the caller can be sure that their 227 * reference to the string buffer is the only reference to the string 228 * buffer, and therefore it has exclusive access to the string buffer and 229 * associated data. However, if this function returns true, then other 230 * consumers may rely on the data in this buffer being immutable and 231 * other threads may access this buffer simultaneously. 232 */ 233 bool IsReadonly() const { 234 // This doesn't lead to the destruction of the buffer, so we don't 235 // need to perform acquire memory synchronization for the normal 236 // reason that a reference count needs acquire synchronization 237 // (ensuring that all writes to the object made on other threads are 238 // visible to the thread destroying the object). 239 // 240 // We then need to consider the possibility that there were prior 241 // writes to the buffer on a different thread: one that has either 242 // since released its reference count, or one that also has access 243 // to this buffer through the same reference. There are two ways 244 // for that to happen: either the buffer pointer or a data structure 245 // (e.g., string object) pointing to the buffer was transferred from 246 // one thread to another, or the data structure pointing to the 247 // buffer was already visible on both threads. In the first case 248 // (transfer), the transfer of data from one thread to another would 249 // have handled the memory synchronization. In the latter case 250 // (data structure visible on both threads), the caller needed some 251 // sort of higher level memory synchronization to protect against 252 // the string object being mutated at the same time on multiple 253 // threads. 254 255 // See bug 1603504. TSan might complain about a race when using 256 // memory_order_relaxed, so use memory_order_acquire for making TSan 257 // happy. 258 #if defined(MOZ_TSAN) 259 return mRefCount.load(std::memory_order_acquire) > 1; 260 #else 261 return mRefCount.load(std::memory_order_relaxed) > 1; 262 #endif 263 } 264 265 /** 266 * Alias for IsReadOnly. 267 */ 268 bool HasMultipleReferences() const { return IsReadonly(); } 269 270 #ifdef DEBUG 271 /** 272 * Returns the buffer's reference count. This is only exposed for logging and 273 * testing purposes. 274 */ 275 uint32_t RefCount() const { 276 return mRefCount.load(std::memory_order_acquire); 277 } 278 #endif 279 280 /** 281 * This measures the size only if the StringBuffer is unshared. 282 */ 283 size_t SizeOfIncludingThisIfUnshared(MallocSizeOf aMallocSizeOf) const { 284 return IsReadonly() ? 0 : aMallocSizeOf(this); 285 } 286 287 /** 288 * This measures the size regardless of whether the StringBuffer is 289 * unshared. 290 * 291 * WARNING: Only use this if you really know what you are doing, because 292 * it can easily lead to double-counting strings. If you do use them, 293 * please explain clearly in a comment why it's safe and won't lead to 294 * double-counting. 295 */ 296 size_t SizeOfIncludingThisEvenIfShared(MallocSizeOf aMallocSizeOf) const { 297 return aMallocSizeOf(this); 298 } 299 300 private: 301 template <typename CharT> 302 static already_AddRefed<StringBuffer> DoCreate(const CharT* aData, 303 size_t aLength) { 304 StringBuffer* buffer = Alloc((aLength + 1) * sizeof(CharT)).take(); 305 if (MOZ_LIKELY(buffer)) { 306 auto* data = reinterpret_cast<CharT*>(buffer->Data()); 307 memcpy(data, aData, aLength * sizeof(CharT)); 308 data[aLength] = 0; 309 } 310 return already_AddRefed(buffer); 311 } 312 }; 313 314 } // namespace mozilla 315 316 #endif