Bug 1696969 [wpt PR 27896] - Allow fuzzy matching for replaced-element-003, a=testonly
[gecko.git] / xpcom / string / nsSubstring.cpp
blob6c194638029a156560189b5eaff567b59cc33bae
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/. */
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 "nsAString.h"
20 #include "nsString.h"
21 #include "nsStringBuffer.h"
22 #include "nsDependentString.h"
23 #include "nsMemory.h"
24 #include "prprf.h"
25 #include "nsCOMPtr.h"
27 #include "mozilla/IntegerPrintfMacros.h"
28 #ifdef XP_WIN
29 # include <windows.h>
30 # include <process.h>
31 # define getpid() _getpid()
32 # define pthread_self() GetCurrentThreadId()
33 #else
34 # include <pthread.h>
35 # include <unistd.h>
36 #endif
38 using mozilla::Atomic;
40 // ---------------------------------------------------------------------------
42 static const char16_t gNullChar = 0;
44 char* const nsCharTraits<char>::sEmptyBuffer =
45 (char*)const_cast<char16_t*>(&gNullChar);
46 char16_t* const nsCharTraits<char16_t>::sEmptyBuffer =
47 const_cast<char16_t*>(&gNullChar);
49 // ---------------------------------------------------------------------------
51 #ifdef ENABLE_STRING_STATS
52 class nsStringStats {
53 public:
54 nsStringStats()
55 : mAllocCount(0), mReallocCount(0), mFreeCount(0), mShareCount(0) {}
57 ~nsStringStats() {
58 // this is a hack to suppress duplicate string stats printing
59 // in seamonkey as a result of the string code being linked
60 // into seamonkey and libxpcom! :-(
61 if (!mAllocCount && !mAdoptCount) {
62 return;
65 // Only print the stats if we detect a leak.
66 if (mAllocCount <= mFreeCount && mAdoptCount <= mAdoptFreeCount) {
67 return;
70 printf("nsStringStats\n");
71 printf(" => mAllocCount: % 10d\n", int(mAllocCount));
72 printf(" => mReallocCount: % 10d\n", int(mReallocCount));
73 printf(" => mFreeCount: % 10d", int(mFreeCount));
74 if (mAllocCount > mFreeCount) {
75 printf(" -- LEAKED %d !!!\n", mAllocCount - mFreeCount);
76 } else {
77 printf("\n");
79 printf(" => mShareCount: % 10d\n", int(mShareCount));
80 printf(" => mAdoptCount: % 10d\n", int(mAdoptCount));
81 printf(" => mAdoptFreeCount: % 10d", int(mAdoptFreeCount));
82 if (mAdoptCount > mAdoptFreeCount) {
83 printf(" -- LEAKED %d !!!\n", mAdoptCount - mAdoptFreeCount);
84 } else {
85 printf("\n");
87 printf(" => Process ID: %" PRIuPTR ", Thread ID: %" PRIuPTR "\n",
88 uintptr_t(getpid()), uintptr_t(pthread_self()));
91 typedef Atomic<int32_t, mozilla::SequentiallyConsistent> AtomicInt;
93 AtomicInt mAllocCount;
94 AtomicInt mReallocCount;
95 AtomicInt mFreeCount;
96 AtomicInt mShareCount;
97 AtomicInt mAdoptCount;
98 AtomicInt mAdoptFreeCount;
100 static nsStringStats gStringStats;
101 # define STRING_STAT_INCREMENT(_s) (gStringStats.m##_s##Count)++
102 #else
103 # define STRING_STAT_INCREMENT(_s)
104 #endif
106 // ---------------------------------------------------------------------------
108 void ReleaseData(void* aData, nsAString::DataFlags aFlags) {
109 if (aFlags & nsAString::DataFlags::REFCOUNTED) {
110 nsStringBuffer::FromData(aData)->Release();
111 } else if (aFlags & nsAString::DataFlags::OWNED) {
112 free(aData);
113 STRING_STAT_INCREMENT(AdoptFree);
114 // Treat this as destruction of a "StringAdopt" object for leak
115 // tracking purposes.
116 MOZ_LOG_DTOR(aData, "StringAdopt", 1);
118 // otherwise, nothing to do.
121 // ---------------------------------------------------------------------------
123 // XXX or we could make nsStringBuffer be a friend of nsTAString
125 class nsAStringAccessor : public nsAString {
126 private:
127 nsAStringAccessor(); // NOT IMPLEMENTED
129 public:
130 char_type* data() const { return mData; }
131 size_type length() const { return mLength; }
132 DataFlags flags() const { return mDataFlags; }
134 void set(char_type* aData, size_type aLen, DataFlags aDataFlags) {
135 ReleaseData(mData, mDataFlags);
136 SetData(aData, aLen, aDataFlags);
140 class nsACStringAccessor : public nsACString {
141 private:
142 nsACStringAccessor(); // NOT IMPLEMENTED
144 public:
145 char_type* data() const { return mData; }
146 size_type length() const { return mLength; }
147 DataFlags flags() const { return mDataFlags; }
149 void set(char_type* aData, size_type aLen, DataFlags aDataFlags) {
150 ReleaseData(mData, mDataFlags);
151 SetData(aData, aLen, aDataFlags);
155 // ---------------------------------------------------------------------------
157 void nsStringBuffer::AddRef() {
158 // Memory synchronization is not required when incrementing a
159 // reference count. The first increment of a reference count on a
160 // thread is not important, since the first use of the object on a
161 // thread can happen before it. What is important is the transfer
162 // of the pointer to that thread, which may happen prior to the
163 // first increment on that thread. The necessary memory
164 // synchronization is done by the mechanism that transfers the
165 // pointer between threads.
166 #ifdef NS_BUILD_REFCNT_LOGGING
167 uint32_t count =
168 #endif
169 mRefCount.fetch_add(1, std::memory_order_relaxed)
170 #ifdef NS_BUILD_REFCNT_LOGGING
172 #endif
174 STRING_STAT_INCREMENT(Share);
175 NS_LOG_ADDREF(this, count, "nsStringBuffer", sizeof(*this));
178 void nsStringBuffer::Release() {
179 // Since this may be the last release on this thread, we need
180 // release semantics so that prior writes on this thread are visible
181 // to the thread that destroys the object when it reads mValue with
182 // acquire semantics.
183 uint32_t count = mRefCount.fetch_sub(1, std::memory_order_release) - 1;
184 NS_LOG_RELEASE(this, count, "nsStringBuffer");
185 if (count == 0) {
186 // We're going to destroy the object on this thread, so we need
187 // acquire semantics to synchronize with the memory released by
188 // the last release on other threads, that is, to ensure that
189 // writes prior to that release are now visible on this thread.
190 count = mRefCount.load(std::memory_order_acquire);
192 STRING_STAT_INCREMENT(Free);
193 free(this); // we were allocated with |malloc|
198 * Alloc returns a pointer to a new string header with set capacity.
200 already_AddRefed<nsStringBuffer> nsStringBuffer::Alloc(size_t aSize) {
201 NS_ASSERTION(aSize != 0, "zero capacity allocation not allowed");
202 NS_ASSERTION(sizeof(nsStringBuffer) + aSize <= size_t(uint32_t(-1)) &&
203 sizeof(nsStringBuffer) + aSize > aSize,
204 "mStorageSize will truncate");
206 nsStringBuffer* hdr = (nsStringBuffer*)malloc(sizeof(nsStringBuffer) + aSize);
207 if (hdr) {
208 STRING_STAT_INCREMENT(Alloc);
210 hdr->mRefCount = 1;
211 hdr->mStorageSize = aSize;
212 NS_LOG_ADDREF(hdr, 1, "nsStringBuffer", sizeof(*hdr));
214 return dont_AddRef(hdr);
217 nsStringBuffer* nsStringBuffer::Realloc(nsStringBuffer* aHdr, size_t aSize) {
218 STRING_STAT_INCREMENT(Realloc);
220 NS_ASSERTION(aSize != 0, "zero capacity allocation not allowed");
221 NS_ASSERTION(sizeof(nsStringBuffer) + aSize <= size_t(uint32_t(-1)) &&
222 sizeof(nsStringBuffer) + aSize > aSize,
223 "mStorageSize will truncate");
225 // no point in trying to save ourselves if we hit this assertion
226 NS_ASSERTION(!aHdr->IsReadonly(), "|Realloc| attempted on readonly string");
228 // Treat this as a release and addref for refcounting purposes, since we
229 // just asserted that the refcount is 1. If we don't do that, refcount
230 // logging will claim we've leaked all sorts of stuff.
231 NS_LOG_RELEASE(aHdr, 0, "nsStringBuffer");
233 aHdr = (nsStringBuffer*)realloc(aHdr, sizeof(nsStringBuffer) + aSize);
234 if (aHdr) {
235 NS_LOG_ADDREF(aHdr, 1, "nsStringBuffer", sizeof(*aHdr));
236 aHdr->mStorageSize = aSize;
239 return aHdr;
242 nsStringBuffer* nsStringBuffer::FromString(const nsAString& aStr) {
243 const nsAStringAccessor* accessor =
244 static_cast<const nsAStringAccessor*>(&aStr);
246 if (!(accessor->flags() & nsAString::DataFlags::REFCOUNTED)) {
247 return nullptr;
250 return FromData(accessor->data());
253 nsStringBuffer* nsStringBuffer::FromString(const nsACString& aStr) {
254 const nsACStringAccessor* accessor =
255 static_cast<const nsACStringAccessor*>(&aStr);
257 if (!(accessor->flags() & nsACString::DataFlags::REFCOUNTED)) {
258 return nullptr;
261 return FromData(accessor->data());
264 void nsStringBuffer::ToString(uint32_t aLen, nsAString& aStr,
265 bool aMoveOwnership) {
266 char16_t* data = static_cast<char16_t*>(Data());
268 nsAStringAccessor* accessor = static_cast<nsAStringAccessor*>(&aStr);
269 MOZ_DIAGNOSTIC_ASSERT(data[aLen] == char16_t(0),
270 "data should be null terminated");
272 nsAString::DataFlags flags =
273 nsAString::DataFlags::REFCOUNTED | nsAString::DataFlags::TERMINATED;
275 if (!aMoveOwnership) {
276 AddRef();
278 accessor->set(data, aLen, flags);
281 void nsStringBuffer::ToString(uint32_t aLen, nsACString& aStr,
282 bool aMoveOwnership) {
283 char* data = static_cast<char*>(Data());
285 nsACStringAccessor* accessor = static_cast<nsACStringAccessor*>(&aStr);
286 MOZ_DIAGNOSTIC_ASSERT(data[aLen] == char(0),
287 "data should be null terminated");
289 nsACString::DataFlags flags =
290 nsACString::DataFlags::REFCOUNTED | nsACString::DataFlags::TERMINATED;
292 if (!aMoveOwnership) {
293 AddRef();
295 accessor->set(data, aLen, flags);
298 size_t nsStringBuffer::SizeOfIncludingThisIfUnshared(
299 mozilla::MallocSizeOf aMallocSizeOf) const {
300 return IsReadonly() ? 0 : aMallocSizeOf(this);
303 size_t nsStringBuffer::SizeOfIncludingThisEvenIfShared(
304 mozilla::MallocSizeOf aMallocSizeOf) const {
305 return aMallocSizeOf(this);
308 // ---------------------------------------------------------------------------
310 // define nsAString
311 #include "nsTSubstring.cpp"
313 // Provide rust bindings to the nsA[C]String types
314 extern "C" {
316 // This is a no-op on release, so we ifdef it out such that using it in release
317 // results in a linker error.
318 #ifdef DEBUG
319 void Gecko_IncrementStringAdoptCount(void* aData) {
320 MOZ_LOG_CTOR(aData, "StringAdopt", 1);
322 #elif defined(MOZ_DEBUG_RUST)
323 void Gecko_IncrementStringAdoptCount(void* aData) {}
324 #endif
326 void Gecko_FinalizeCString(nsACString* aThis) { aThis->~nsACString(); }
328 void Gecko_AssignCString(nsACString* aThis, const nsACString* aOther) {
329 aThis->Assign(*aOther);
332 void Gecko_TakeFromCString(nsACString* aThis, nsACString* aOther) {
333 aThis->Assign(std::move(*aOther));
336 void Gecko_AppendCString(nsACString* aThis, const nsACString* aOther) {
337 aThis->Append(*aOther);
340 void Gecko_SetLengthCString(nsACString* aThis, uint32_t aLength) {
341 aThis->SetLength(aLength);
344 bool Gecko_FallibleAssignCString(nsACString* aThis, const nsACString* aOther) {
345 return aThis->Assign(*aOther, mozilla::fallible);
348 bool Gecko_FallibleTakeFromCString(nsACString* aThis, nsACString* aOther) {
349 return aThis->Assign(std::move(*aOther), mozilla::fallible);
352 bool Gecko_FallibleAppendCString(nsACString* aThis, const nsACString* aOther) {
353 return aThis->Append(*aOther, mozilla::fallible);
356 bool Gecko_FallibleSetLengthCString(nsACString* aThis, uint32_t aLength) {
357 return aThis->SetLength(aLength, mozilla::fallible);
360 char* Gecko_BeginWritingCString(nsACString* aThis) {
361 return aThis->BeginWriting();
364 char* Gecko_FallibleBeginWritingCString(nsACString* aThis) {
365 return aThis->BeginWriting(mozilla::fallible);
368 uint32_t Gecko_StartBulkWriteCString(nsACString* aThis, uint32_t aCapacity,
369 uint32_t aUnitsToPreserve,
370 bool aAllowShrinking) {
371 return aThis->StartBulkWriteImpl(aCapacity, aUnitsToPreserve, aAllowShrinking)
372 .unwrapOr(UINT32_MAX);
375 void Gecko_FinalizeString(nsAString* aThis) { aThis->~nsAString(); }
377 void Gecko_AssignString(nsAString* aThis, const nsAString* aOther) {
378 aThis->Assign(*aOther);
381 void Gecko_TakeFromString(nsAString* aThis, nsAString* aOther) {
382 aThis->Assign(std::move(*aOther));
385 void Gecko_AppendString(nsAString* aThis, const nsAString* aOther) {
386 aThis->Append(*aOther);
389 void Gecko_SetLengthString(nsAString* aThis, uint32_t aLength) {
390 aThis->SetLength(aLength);
393 bool Gecko_FallibleAssignString(nsAString* aThis, const nsAString* aOther) {
394 return aThis->Assign(*aOther, mozilla::fallible);
397 bool Gecko_FallibleTakeFromString(nsAString* aThis, nsAString* aOther) {
398 return aThis->Assign(std::move(*aOther), mozilla::fallible);
401 bool Gecko_FallibleAppendString(nsAString* aThis, const nsAString* aOther) {
402 return aThis->Append(*aOther, mozilla::fallible);
405 bool Gecko_FallibleSetLengthString(nsAString* aThis, uint32_t aLength) {
406 return aThis->SetLength(aLength, mozilla::fallible);
409 char16_t* Gecko_BeginWritingString(nsAString* aThis) {
410 return aThis->BeginWriting();
413 char16_t* Gecko_FallibleBeginWritingString(nsAString* aThis) {
414 return aThis->BeginWriting(mozilla::fallible);
417 uint32_t Gecko_StartBulkWriteString(nsAString* aThis, uint32_t aCapacity,
418 uint32_t aUnitsToPreserve,
419 bool aAllowShrinking) {
420 return aThis->StartBulkWriteImpl(aCapacity, aUnitsToPreserve, aAllowShrinking)
421 .unwrapOr(UINT32_MAX);
424 } // extern "C"