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