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 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla.
17 : *
18 : * The Initial Developer of the Original Code is IBM Corporation.
19 : * Portions created by IBM Corporation are Copyright (C) 2003
20 : * IBM Corporation. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Darin Fisher <darin@meer.net>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #ifdef DEBUG
40 : #define ENABLE_STRING_STATS
41 : #endif
42 :
43 : #ifdef ENABLE_STRING_STATS
44 : #include <stdio.h>
45 : #endif
46 :
47 : #include <stdlib.h>
48 : #include "nsSubstring.h"
49 : #include "nsString.h"
50 : #include "nsStringBuffer.h"
51 : #include "nsDependentString.h"
52 : #include "nsMemory.h"
53 : #include "pratom.h"
54 : #include "prprf.h"
55 : #include "nsStaticAtom.h"
56 :
57 : // ---------------------------------------------------------------------------
58 :
59 : static PRUnichar gNullChar = 0;
60 :
61 : char* nsCharTraits<char> ::sEmptyBuffer = (char*) &gNullChar;
62 : PRUnichar* nsCharTraits<PRUnichar>::sEmptyBuffer = &gNullChar;
63 :
64 : // ---------------------------------------------------------------------------
65 :
66 : #ifdef ENABLE_STRING_STATS
67 : class nsStringStats
68 : {
69 : public:
70 1464 : nsStringStats()
71 1464 : : mAllocCount(0), mReallocCount(0), mFreeCount(0), mShareCount(0) {}
72 :
73 1487 : ~nsStringStats()
74 : {
75 : // this is a hack to suppress duplicate string stats printing
76 : // in seamonkey as a result of the string code being linked
77 : // into seamonkey and libxpcom! :-(
78 1487 : if (!mAllocCount && !mAdoptCount)
79 40 : return;
80 :
81 1447 : printf("nsStringStats\n");
82 1447 : printf(" => mAllocCount: % 10d\n", mAllocCount);
83 1447 : printf(" => mReallocCount: % 10d\n", mReallocCount);
84 1447 : printf(" => mFreeCount: % 10d", mFreeCount);
85 1447 : if (mAllocCount > mFreeCount)
86 40 : printf(" -- LEAKED %d !!!\n", mAllocCount - mFreeCount);
87 : else
88 1407 : printf("\n");
89 1447 : printf(" => mShareCount: % 10d\n", mShareCount);
90 1447 : printf(" => mAdoptCount: % 10d\n", mAdoptCount);
91 1447 : printf(" => mAdoptFreeCount: % 10d", mAdoptFreeCount);
92 1447 : if (mAdoptCount > mAdoptFreeCount)
93 3 : printf(" -- LEAKED %d !!!\n", mAdoptCount - mAdoptFreeCount);
94 : else
95 1444 : printf("\n");
96 1487 : }
97 :
98 : PRInt32 mAllocCount;
99 : PRInt32 mReallocCount;
100 : PRInt32 mFreeCount;
101 : PRInt32 mShareCount;
102 : PRInt32 mAdoptCount;
103 : PRInt32 mAdoptFreeCount;
104 : };
105 1464 : static nsStringStats gStringStats;
106 : #define STRING_STAT_INCREMENT(_s) PR_ATOMIC_INCREMENT(&gStringStats.m ## _s ## Count)
107 : #else
108 : #define STRING_STAT_INCREMENT(_s)
109 : #endif
110 :
111 : // ---------------------------------------------------------------------------
112 :
113 : inline void
114 96041591 : ReleaseData( void* data, PRUint32 flags )
115 : {
116 96041591 : if (flags & nsSubstring::F_SHARED)
117 : {
118 15727237 : nsStringBuffer::FromData(data)->Release();
119 : }
120 80314354 : else if (flags & nsSubstring::F_OWNED)
121 : {
122 238003 : nsMemory::Free(data);
123 238003 : STRING_STAT_INCREMENT(AdoptFree);
124 : #ifdef NS_BUILD_REFCNT_LOGGING
125 : // Treat this as destruction of a "StringAdopt" object for leak
126 : // tracking purposes.
127 238003 : NS_LogDtor(data, "StringAdopt", 1);
128 : #endif // NS_BUILD_REFCNT_LOGGING
129 : }
130 : // otherwise, nothing to do.
131 96041603 : }
132 :
133 : // ---------------------------------------------------------------------------
134 :
135 : // XXX or we could make nsStringBuffer be a friend of nsTAString
136 :
137 : class nsAStringAccessor : public nsAString
138 : {
139 : private:
140 : nsAStringAccessor(); // NOT IMPLEMENTED
141 :
142 : public:
143 113385 : char_type *data() const { return mData; }
144 : size_type length() const { return mLength; }
145 4649360 : PRUint32 flags() const { return mFlags; }
146 :
147 3257140 : void set(char_type *data, size_type len, PRUint32 flags)
148 : {
149 3257140 : ReleaseData(mData, mFlags);
150 3257140 : mData = data;
151 3257140 : mLength = len;
152 3257140 : mFlags = flags;
153 3257140 : }
154 : };
155 :
156 : class nsACStringAccessor : public nsACString
157 : {
158 : private:
159 : nsACStringAccessor(); // NOT IMPLEMENTED
160 :
161 : public:
162 0 : char_type *data() const { return mData; }
163 : size_type length() const { return mLength; }
164 0 : PRUint32 flags() const { return mFlags; }
165 :
166 0 : void set(char_type *data, size_type len, PRUint32 flags)
167 : {
168 0 : ReleaseData(mData, mFlags);
169 0 : mData = data;
170 0 : mLength = len;
171 0 : mFlags = flags;
172 0 : }
173 : };
174 :
175 : // ---------------------------------------------------------------------------
176 :
177 : void
178 11588833 : nsStringBuffer::AddRef()
179 : {
180 11588833 : PR_ATOMIC_INCREMENT(&mRefCount);
181 11588833 : STRING_STAT_INCREMENT(Share);
182 11588833 : NS_LOG_ADDREF(this, mRefCount, "nsStringBuffer", sizeof(*this));
183 11588833 : }
184 :
185 : void
186 19097888 : nsStringBuffer::Release()
187 : {
188 19097888 : PRInt32 count = PR_ATOMIC_DECREMENT(&mRefCount);
189 19097888 : NS_LOG_RELEASE(this, count, "nsStringBuffer");
190 19097941 : if (count == 0)
191 : {
192 7510498 : STRING_STAT_INCREMENT(Free);
193 7510498 : free(this); // we were allocated with |malloc|
194 : }
195 19097941 : }
196 :
197 : /**
198 : * Alloc returns a pointer to a new string header with set capacity.
199 : */
200 : nsStringBuffer*
201 7513918 : nsStringBuffer::Alloc(size_t size)
202 : {
203 7513918 : NS_ASSERTION(size != 0, "zero capacity allocation not allowed");
204 7513922 : NS_ASSERTION(sizeof(nsStringBuffer) + size <= size_t(PRUint32(-1)) &&
205 : sizeof(nsStringBuffer) + size > size,
206 : "mStorageSize will truncate");
207 :
208 : nsStringBuffer *hdr =
209 7513922 : (nsStringBuffer *) malloc(sizeof(nsStringBuffer) + size);
210 7514145 : if (hdr)
211 : {
212 7514143 : STRING_STAT_INCREMENT(Alloc);
213 :
214 7514143 : hdr->mRefCount = 1;
215 7514143 : hdr->mStorageSize = size;
216 7514143 : NS_LOG_ADDREF(hdr, 1, "nsStringBuffer", sizeof(*hdr));
217 : }
218 7514152 : return hdr;
219 : }
220 :
221 : nsStringBuffer*
222 557702 : nsStringBuffer::Realloc(nsStringBuffer* hdr, size_t size)
223 : {
224 557702 : STRING_STAT_INCREMENT(Realloc);
225 :
226 557702 : NS_ASSERTION(size != 0, "zero capacity allocation not allowed");
227 557702 : NS_ASSERTION(sizeof(nsStringBuffer) + size <= size_t(PRUint32(-1)) &&
228 : sizeof(nsStringBuffer) + size > size,
229 : "mStorageSize will truncate");
230 :
231 : // no point in trying to save ourselves if we hit this assertion
232 557702 : NS_ASSERTION(!hdr->IsReadonly(), "|Realloc| attempted on readonly string");
233 :
234 : // Treat this as a release and addref for refcounting purposes, since we
235 : // just asserted that the refcount is 1. If we don't do that, refcount
236 : // logging will claim we've leaked all sorts of stuff.
237 557702 : NS_LOG_RELEASE(hdr, 0, "nsStringBuffer");
238 :
239 557702 : hdr = (nsStringBuffer*) realloc(hdr, sizeof(nsStringBuffer) + size);
240 557702 : if (hdr) {
241 557702 : NS_LOG_ADDREF(hdr, 1, "nsStringBuffer", sizeof(*hdr));
242 557702 : hdr->mStorageSize = size;
243 : }
244 :
245 557702 : return hdr;
246 : }
247 :
248 : nsStringBuffer*
249 1392220 : nsStringBuffer::FromString(const nsAString& str)
250 : {
251 : const nsAStringAccessor* accessor =
252 1392220 : static_cast<const nsAStringAccessor*>(&str);
253 :
254 1392220 : if (!(accessor->flags() & nsSubstring::F_SHARED))
255 1278835 : return nsnull;
256 :
257 113385 : return FromData(accessor->data());
258 : }
259 :
260 : nsStringBuffer*
261 0 : nsStringBuffer::FromString(const nsACString& str)
262 : {
263 : const nsACStringAccessor* accessor =
264 0 : static_cast<const nsACStringAccessor*>(&str);
265 :
266 0 : if (!(accessor->flags() & nsCSubstring::F_SHARED))
267 0 : return nsnull;
268 :
269 0 : return FromData(accessor->data());
270 : }
271 :
272 : void
273 3257140 : nsStringBuffer::ToString(PRUint32 len, nsAString &str,
274 : bool aMoveOwnership)
275 : {
276 3257140 : PRUnichar* data = static_cast<PRUnichar*>(Data());
277 :
278 3257140 : nsAStringAccessor* accessor = static_cast<nsAStringAccessor*>(&str);
279 3257140 : NS_ASSERTION(data[len] == PRUnichar(0), "data should be null terminated");
280 :
281 : // preserve class flags
282 3257140 : PRUint32 flags = accessor->flags();
283 3257140 : flags = (flags & 0xFFFF0000) | nsSubstring::F_SHARED | nsSubstring::F_TERMINATED;
284 :
285 3257140 : if (!aMoveOwnership) {
286 3256676 : AddRef();
287 : }
288 3257140 : accessor->set(data, len, flags);
289 3257140 : }
290 :
291 : void
292 0 : nsStringBuffer::ToString(PRUint32 len, nsACString &str,
293 : bool aMoveOwnership)
294 : {
295 0 : char* data = static_cast<char*>(Data());
296 :
297 0 : nsACStringAccessor* accessor = static_cast<nsACStringAccessor*>(&str);
298 0 : NS_ASSERTION(data[len] == char(0), "data should be null terminated");
299 :
300 : // preserve class flags
301 0 : PRUint32 flags = accessor->flags();
302 0 : flags = (flags & 0xFFFF0000) | nsCSubstring::F_SHARED | nsCSubstring::F_TERMINATED;
303 :
304 0 : if (!aMoveOwnership) {
305 0 : AddRef();
306 : }
307 0 : accessor->set(data, len, flags);
308 0 : }
309 :
310 : size_t
311 0 : nsStringBuffer::SizeOfIncludingThisMustBeUnshared(nsMallocSizeOfFun aMallocSizeOf) const
312 : {
313 0 : NS_ASSERTION(!IsReadonly(),
314 : "shared StringBuffer in SizeOfIncludingThisMustBeUnshared");
315 0 : return aMallocSizeOf(this);
316 : }
317 :
318 : size_t
319 0 : nsStringBuffer::SizeOfIncludingThisIfUnshared(nsMallocSizeOfFun aMallocSizeOf) const
320 : {
321 0 : if (!IsReadonly())
322 : {
323 0 : return SizeOfIncludingThisMustBeUnshared(aMallocSizeOf);
324 : }
325 0 : return 0;
326 : }
327 :
328 : // ---------------------------------------------------------------------------
329 :
330 :
331 : // define nsSubstring
332 : #include "string-template-def-unichar.h"
333 : #include "nsTSubstring.cpp"
334 : #include "string-template-undef.h"
335 :
336 : // define nsCSubstring
337 : #include "string-template-def-char.h"
338 : #include "nsTSubstring.cpp"
339 : #include "string-template-undef.h"
340 :
341 : // Check that internal and external strings have the same size.
342 : // See https://bugzilla.mozilla.org/show_bug.cgi?id=430581
343 :
344 : #include "prlog.h"
345 : #include "nsXPCOMStrings.h"
346 :
347 4392 : PR_STATIC_ASSERT(sizeof(nsStringContainer_base) == sizeof(nsSubstring));
|