Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* vim:set ts=2 sw=2 sts=2 et cindent: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #ifdef DEBUG |
michael@0 | 8 | #define ENABLE_STRING_STATS |
michael@0 | 9 | #endif |
michael@0 | 10 | |
michael@0 | 11 | #include "mozilla/Atomics.h" |
michael@0 | 12 | #include "mozilla/MemoryReporting.h" |
michael@0 | 13 | |
michael@0 | 14 | #ifdef ENABLE_STRING_STATS |
michael@0 | 15 | #include <stdio.h> |
michael@0 | 16 | #endif |
michael@0 | 17 | |
michael@0 | 18 | #include <stdlib.h> |
michael@0 | 19 | #include "nsSubstring.h" |
michael@0 | 20 | #include "nsString.h" |
michael@0 | 21 | #include "nsStringBuffer.h" |
michael@0 | 22 | #include "nsDependentString.h" |
michael@0 | 23 | #include "nsMemory.h" |
michael@0 | 24 | #include "prprf.h" |
michael@0 | 25 | #include "nsStaticAtom.h" |
michael@0 | 26 | #include "nsCOMPtr.h" |
michael@0 | 27 | |
michael@0 | 28 | #include "mozilla/IntegerPrintfMacros.h" |
michael@0 | 29 | #ifdef XP_WIN |
michael@0 | 30 | #include <windows.h> |
michael@0 | 31 | #include <process.h> |
michael@0 | 32 | #define getpid() _getpid() |
michael@0 | 33 | #define pthread_self() GetCurrentThreadId() |
michael@0 | 34 | #else |
michael@0 | 35 | #include <pthread.h> |
michael@0 | 36 | #include <unistd.h> |
michael@0 | 37 | #endif |
michael@0 | 38 | |
michael@0 | 39 | using mozilla::Atomic; |
michael@0 | 40 | |
michael@0 | 41 | // --------------------------------------------------------------------------- |
michael@0 | 42 | |
michael@0 | 43 | static const char16_t gNullChar = 0; |
michael@0 | 44 | |
michael@0 | 45 | char* const nsCharTraits<char>::sEmptyBuffer = |
michael@0 | 46 | (char*) const_cast<char16_t*>(&gNullChar); |
michael@0 | 47 | char16_t* const nsCharTraits<char16_t>::sEmptyBuffer = |
michael@0 | 48 | const_cast<char16_t*>(&gNullChar); |
michael@0 | 49 | |
michael@0 | 50 | // --------------------------------------------------------------------------- |
michael@0 | 51 | |
michael@0 | 52 | #ifdef ENABLE_STRING_STATS |
michael@0 | 53 | class nsStringStats |
michael@0 | 54 | { |
michael@0 | 55 | public: |
michael@0 | 56 | nsStringStats() |
michael@0 | 57 | : mAllocCount(0), mReallocCount(0), mFreeCount(0), mShareCount(0) {} |
michael@0 | 58 | |
michael@0 | 59 | ~nsStringStats() |
michael@0 | 60 | { |
michael@0 | 61 | // this is a hack to suppress duplicate string stats printing |
michael@0 | 62 | // in seamonkey as a result of the string code being linked |
michael@0 | 63 | // into seamonkey and libxpcom! :-( |
michael@0 | 64 | if (!mAllocCount && !mAdoptCount) |
michael@0 | 65 | return; |
michael@0 | 66 | |
michael@0 | 67 | printf("nsStringStats\n"); |
michael@0 | 68 | printf(" => mAllocCount: % 10d\n", int(mAllocCount)); |
michael@0 | 69 | printf(" => mReallocCount: % 10d\n", int(mReallocCount)); |
michael@0 | 70 | printf(" => mFreeCount: % 10d", int(mFreeCount)); |
michael@0 | 71 | if (mAllocCount > mFreeCount) |
michael@0 | 72 | printf(" -- LEAKED %d !!!\n", mAllocCount - mFreeCount); |
michael@0 | 73 | else |
michael@0 | 74 | printf("\n"); |
michael@0 | 75 | printf(" => mShareCount: % 10d\n", int(mShareCount)); |
michael@0 | 76 | printf(" => mAdoptCount: % 10d\n", int(mAdoptCount)); |
michael@0 | 77 | printf(" => mAdoptFreeCount: % 10d", int(mAdoptFreeCount)); |
michael@0 | 78 | if (mAdoptCount > mAdoptFreeCount) |
michael@0 | 79 | printf(" -- LEAKED %d !!!\n", mAdoptCount - mAdoptFreeCount); |
michael@0 | 80 | else |
michael@0 | 81 | printf("\n"); |
michael@0 | 82 | printf(" => Process ID: %" PRIuPTR ", Thread ID: %" PRIuPTR "\n", |
michael@0 | 83 | uintptr_t(getpid()), uintptr_t(pthread_self())); |
michael@0 | 84 | } |
michael@0 | 85 | |
michael@0 | 86 | Atomic<int32_t> mAllocCount; |
michael@0 | 87 | Atomic<int32_t> mReallocCount; |
michael@0 | 88 | Atomic<int32_t> mFreeCount; |
michael@0 | 89 | Atomic<int32_t> mShareCount; |
michael@0 | 90 | Atomic<int32_t> mAdoptCount; |
michael@0 | 91 | Atomic<int32_t> mAdoptFreeCount; |
michael@0 | 92 | }; |
michael@0 | 93 | static nsStringStats gStringStats; |
michael@0 | 94 | #define STRING_STAT_INCREMENT(_s) (gStringStats.m ## _s ## Count)++ |
michael@0 | 95 | #else |
michael@0 | 96 | #define STRING_STAT_INCREMENT(_s) |
michael@0 | 97 | #endif |
michael@0 | 98 | |
michael@0 | 99 | // --------------------------------------------------------------------------- |
michael@0 | 100 | |
michael@0 | 101 | inline void |
michael@0 | 102 | ReleaseData( void* data, uint32_t flags ) |
michael@0 | 103 | { |
michael@0 | 104 | if (flags & nsSubstring::F_SHARED) |
michael@0 | 105 | { |
michael@0 | 106 | nsStringBuffer::FromData(data)->Release(); |
michael@0 | 107 | } |
michael@0 | 108 | else if (flags & nsSubstring::F_OWNED) |
michael@0 | 109 | { |
michael@0 | 110 | nsMemory::Free(data); |
michael@0 | 111 | STRING_STAT_INCREMENT(AdoptFree); |
michael@0 | 112 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 113 | // Treat this as destruction of a "StringAdopt" object for leak |
michael@0 | 114 | // tracking purposes. |
michael@0 | 115 | NS_LogDtor(data, "StringAdopt", 1); |
michael@0 | 116 | #endif // NS_BUILD_REFCNT_LOGGING |
michael@0 | 117 | } |
michael@0 | 118 | // otherwise, nothing to do. |
michael@0 | 119 | } |
michael@0 | 120 | |
michael@0 | 121 | // --------------------------------------------------------------------------- |
michael@0 | 122 | |
michael@0 | 123 | // XXX or we could make nsStringBuffer be a friend of nsTAString |
michael@0 | 124 | |
michael@0 | 125 | class nsAStringAccessor : public nsAString |
michael@0 | 126 | { |
michael@0 | 127 | private: |
michael@0 | 128 | nsAStringAccessor(); // NOT IMPLEMENTED |
michael@0 | 129 | |
michael@0 | 130 | public: |
michael@0 | 131 | char_type *data() const { return mData; } |
michael@0 | 132 | size_type length() const { return mLength; } |
michael@0 | 133 | uint32_t flags() const { return mFlags; } |
michael@0 | 134 | |
michael@0 | 135 | void set(char_type *data, size_type len, uint32_t flags) |
michael@0 | 136 | { |
michael@0 | 137 | ReleaseData(mData, mFlags); |
michael@0 | 138 | mData = data; |
michael@0 | 139 | mLength = len; |
michael@0 | 140 | mFlags = flags; |
michael@0 | 141 | } |
michael@0 | 142 | }; |
michael@0 | 143 | |
michael@0 | 144 | class nsACStringAccessor : public nsACString |
michael@0 | 145 | { |
michael@0 | 146 | private: |
michael@0 | 147 | nsACStringAccessor(); // NOT IMPLEMENTED |
michael@0 | 148 | |
michael@0 | 149 | public: |
michael@0 | 150 | char_type *data() const { return mData; } |
michael@0 | 151 | size_type length() const { return mLength; } |
michael@0 | 152 | uint32_t flags() const { return mFlags; } |
michael@0 | 153 | |
michael@0 | 154 | void set(char_type *data, size_type len, uint32_t flags) |
michael@0 | 155 | { |
michael@0 | 156 | ReleaseData(mData, mFlags); |
michael@0 | 157 | mData = data; |
michael@0 | 158 | mLength = len; |
michael@0 | 159 | mFlags = flags; |
michael@0 | 160 | } |
michael@0 | 161 | }; |
michael@0 | 162 | |
michael@0 | 163 | // --------------------------------------------------------------------------- |
michael@0 | 164 | |
michael@0 | 165 | void |
michael@0 | 166 | nsStringBuffer::AddRef() |
michael@0 | 167 | { |
michael@0 | 168 | ++mRefCount; |
michael@0 | 169 | STRING_STAT_INCREMENT(Share); |
michael@0 | 170 | NS_LOG_ADDREF(this, mRefCount, "nsStringBuffer", sizeof(*this)); |
michael@0 | 171 | } |
michael@0 | 172 | |
michael@0 | 173 | void |
michael@0 | 174 | nsStringBuffer::Release() |
michael@0 | 175 | { |
michael@0 | 176 | int32_t count = --mRefCount; |
michael@0 | 177 | NS_LOG_RELEASE(this, count, "nsStringBuffer"); |
michael@0 | 178 | if (count == 0) |
michael@0 | 179 | { |
michael@0 | 180 | STRING_STAT_INCREMENT(Free); |
michael@0 | 181 | free(this); // we were allocated with |malloc| |
michael@0 | 182 | } |
michael@0 | 183 | } |
michael@0 | 184 | |
michael@0 | 185 | /** |
michael@0 | 186 | * Alloc returns a pointer to a new string header with set capacity. |
michael@0 | 187 | */ |
michael@0 | 188 | already_AddRefed<nsStringBuffer> |
michael@0 | 189 | nsStringBuffer::Alloc(size_t size) |
michael@0 | 190 | { |
michael@0 | 191 | NS_ASSERTION(size != 0, "zero capacity allocation not allowed"); |
michael@0 | 192 | NS_ASSERTION(sizeof(nsStringBuffer) + size <= size_t(uint32_t(-1)) && |
michael@0 | 193 | sizeof(nsStringBuffer) + size > size, |
michael@0 | 194 | "mStorageSize will truncate"); |
michael@0 | 195 | |
michael@0 | 196 | nsStringBuffer *hdr = |
michael@0 | 197 | (nsStringBuffer *) malloc(sizeof(nsStringBuffer) + size); |
michael@0 | 198 | if (hdr) |
michael@0 | 199 | { |
michael@0 | 200 | STRING_STAT_INCREMENT(Alloc); |
michael@0 | 201 | |
michael@0 | 202 | hdr->mRefCount = 1; |
michael@0 | 203 | hdr->mStorageSize = size; |
michael@0 | 204 | NS_LOG_ADDREF(hdr, 1, "nsStringBuffer", sizeof(*hdr)); |
michael@0 | 205 | } |
michael@0 | 206 | return dont_AddRef(hdr); |
michael@0 | 207 | } |
michael@0 | 208 | |
michael@0 | 209 | nsStringBuffer* |
michael@0 | 210 | nsStringBuffer::Realloc(nsStringBuffer* hdr, size_t size) |
michael@0 | 211 | { |
michael@0 | 212 | STRING_STAT_INCREMENT(Realloc); |
michael@0 | 213 | |
michael@0 | 214 | NS_ASSERTION(size != 0, "zero capacity allocation not allowed"); |
michael@0 | 215 | NS_ASSERTION(sizeof(nsStringBuffer) + size <= size_t(uint32_t(-1)) && |
michael@0 | 216 | sizeof(nsStringBuffer) + size > size, |
michael@0 | 217 | "mStorageSize will truncate"); |
michael@0 | 218 | |
michael@0 | 219 | // no point in trying to save ourselves if we hit this assertion |
michael@0 | 220 | NS_ASSERTION(!hdr->IsReadonly(), "|Realloc| attempted on readonly string"); |
michael@0 | 221 | |
michael@0 | 222 | // Treat this as a release and addref for refcounting purposes, since we |
michael@0 | 223 | // just asserted that the refcount is 1. If we don't do that, refcount |
michael@0 | 224 | // logging will claim we've leaked all sorts of stuff. |
michael@0 | 225 | NS_LOG_RELEASE(hdr, 0, "nsStringBuffer"); |
michael@0 | 226 | |
michael@0 | 227 | hdr = (nsStringBuffer*) realloc(hdr, sizeof(nsStringBuffer) + size); |
michael@0 | 228 | if (hdr) { |
michael@0 | 229 | NS_LOG_ADDREF(hdr, 1, "nsStringBuffer", sizeof(*hdr)); |
michael@0 | 230 | hdr->mStorageSize = size; |
michael@0 | 231 | } |
michael@0 | 232 | |
michael@0 | 233 | return hdr; |
michael@0 | 234 | } |
michael@0 | 235 | |
michael@0 | 236 | nsStringBuffer* |
michael@0 | 237 | nsStringBuffer::FromString(const nsAString& str) |
michael@0 | 238 | { |
michael@0 | 239 | const nsAStringAccessor* accessor = |
michael@0 | 240 | static_cast<const nsAStringAccessor*>(&str); |
michael@0 | 241 | |
michael@0 | 242 | if (!(accessor->flags() & nsSubstring::F_SHARED)) |
michael@0 | 243 | return nullptr; |
michael@0 | 244 | |
michael@0 | 245 | return FromData(accessor->data()); |
michael@0 | 246 | } |
michael@0 | 247 | |
michael@0 | 248 | nsStringBuffer* |
michael@0 | 249 | nsStringBuffer::FromString(const nsACString& str) |
michael@0 | 250 | { |
michael@0 | 251 | const nsACStringAccessor* accessor = |
michael@0 | 252 | static_cast<const nsACStringAccessor*>(&str); |
michael@0 | 253 | |
michael@0 | 254 | if (!(accessor->flags() & nsCSubstring::F_SHARED)) |
michael@0 | 255 | return nullptr; |
michael@0 | 256 | |
michael@0 | 257 | return FromData(accessor->data()); |
michael@0 | 258 | } |
michael@0 | 259 | |
michael@0 | 260 | void |
michael@0 | 261 | nsStringBuffer::ToString(uint32_t len, nsAString &str, |
michael@0 | 262 | bool aMoveOwnership) |
michael@0 | 263 | { |
michael@0 | 264 | char16_t* data = static_cast<char16_t*>(Data()); |
michael@0 | 265 | |
michael@0 | 266 | nsAStringAccessor* accessor = static_cast<nsAStringAccessor*>(&str); |
michael@0 | 267 | NS_ASSERTION(data[len] == char16_t(0), "data should be null terminated"); |
michael@0 | 268 | |
michael@0 | 269 | // preserve class flags |
michael@0 | 270 | uint32_t flags = accessor->flags(); |
michael@0 | 271 | flags = (flags & 0xFFFF0000) | nsSubstring::F_SHARED | nsSubstring::F_TERMINATED; |
michael@0 | 272 | |
michael@0 | 273 | if (!aMoveOwnership) { |
michael@0 | 274 | AddRef(); |
michael@0 | 275 | } |
michael@0 | 276 | accessor->set(data, len, flags); |
michael@0 | 277 | } |
michael@0 | 278 | |
michael@0 | 279 | void |
michael@0 | 280 | nsStringBuffer::ToString(uint32_t len, nsACString &str, |
michael@0 | 281 | bool aMoveOwnership) |
michael@0 | 282 | { |
michael@0 | 283 | char* data = static_cast<char*>(Data()); |
michael@0 | 284 | |
michael@0 | 285 | nsACStringAccessor* accessor = static_cast<nsACStringAccessor*>(&str); |
michael@0 | 286 | NS_ASSERTION(data[len] == char(0), "data should be null terminated"); |
michael@0 | 287 | |
michael@0 | 288 | // preserve class flags |
michael@0 | 289 | uint32_t flags = accessor->flags(); |
michael@0 | 290 | flags = (flags & 0xFFFF0000) | nsCSubstring::F_SHARED | nsCSubstring::F_TERMINATED; |
michael@0 | 291 | |
michael@0 | 292 | if (!aMoveOwnership) { |
michael@0 | 293 | AddRef(); |
michael@0 | 294 | } |
michael@0 | 295 | accessor->set(data, len, flags); |
michael@0 | 296 | } |
michael@0 | 297 | |
michael@0 | 298 | size_t |
michael@0 | 299 | nsStringBuffer::SizeOfIncludingThisMustBeUnshared(mozilla::MallocSizeOf aMallocSizeOf) const |
michael@0 | 300 | { |
michael@0 | 301 | NS_ASSERTION(!IsReadonly(), |
michael@0 | 302 | "shared StringBuffer in SizeOfIncludingThisMustBeUnshared"); |
michael@0 | 303 | return aMallocSizeOf(this); |
michael@0 | 304 | } |
michael@0 | 305 | |
michael@0 | 306 | size_t |
michael@0 | 307 | nsStringBuffer::SizeOfIncludingThisIfUnshared(mozilla::MallocSizeOf aMallocSizeOf) const |
michael@0 | 308 | { |
michael@0 | 309 | if (!IsReadonly()) |
michael@0 | 310 | { |
michael@0 | 311 | return SizeOfIncludingThisMustBeUnshared(aMallocSizeOf); |
michael@0 | 312 | } |
michael@0 | 313 | return 0; |
michael@0 | 314 | } |
michael@0 | 315 | |
michael@0 | 316 | size_t |
michael@0 | 317 | nsStringBuffer::SizeOfIncludingThisEvenIfShared(mozilla::MallocSizeOf aMallocSizeOf) const |
michael@0 | 318 | { |
michael@0 | 319 | return aMallocSizeOf(this); |
michael@0 | 320 | } |
michael@0 | 321 | |
michael@0 | 322 | // --------------------------------------------------------------------------- |
michael@0 | 323 | |
michael@0 | 324 | |
michael@0 | 325 | // define nsSubstring |
michael@0 | 326 | #include "string-template-def-unichar.h" |
michael@0 | 327 | #include "nsTSubstring.cpp" |
michael@0 | 328 | #include "string-template-undef.h" |
michael@0 | 329 | |
michael@0 | 330 | // define nsCSubstring |
michael@0 | 331 | #include "string-template-def-char.h" |
michael@0 | 332 | #include "nsTSubstring.cpp" |
michael@0 | 333 | #include "string-template-undef.h" |
michael@0 | 334 | |
michael@0 | 335 | // Check that internal and external strings have the same size. |
michael@0 | 336 | // See https://bugzilla.mozilla.org/show_bug.cgi?id=430581 |
michael@0 | 337 | |
michael@0 | 338 | #include "prlog.h" |
michael@0 | 339 | #include "nsXPCOMStrings.h" |
michael@0 | 340 | |
michael@0 | 341 | static_assert(sizeof(nsStringContainer_base) == sizeof(nsSubstring), |
michael@0 | 342 | "internal and external strings must have the same size"); |