Bug 1874684 - Part 4: Prefer const references instead of copying Instant values....
[gecko.git] / xpcom / string / nsStringBuffer.cpp
blobb5d506333f9344cc3483c75404927aaf4a87f0b8
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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsStringBuffer.h"
9 #include "mozilla/MemoryReporting.h"
10 #include "nsISupportsImpl.h"
11 #include "nsString.h"
13 #ifdef DEBUG
14 # include "nsStringStats.h"
15 #else
16 # define STRING_STAT_INCREMENT(_s)
17 #endif
19 void nsStringBuffer::AddRef() {
20 // Memory synchronization is not required when incrementing a
21 // reference count. The first increment of a reference count on a
22 // thread is not important, since the first use of the object on a
23 // thread can happen before it. What is important is the transfer
24 // of the pointer to that thread, which may happen prior to the
25 // first increment on that thread. The necessary memory
26 // synchronization is done by the mechanism that transfers the
27 // pointer between threads.
28 #ifdef NS_BUILD_REFCNT_LOGGING
29 uint32_t count =
30 #endif
31 mRefCount.fetch_add(1, std::memory_order_relaxed)
32 #ifdef NS_BUILD_REFCNT_LOGGING
33 + 1
34 #endif
36 STRING_STAT_INCREMENT(Share);
37 NS_LOG_ADDREF(this, count, "nsStringBuffer", sizeof(*this));
40 void nsStringBuffer::Release() {
41 // Since this may be the last release on this thread, we need
42 // release semantics so that prior writes on this thread are visible
43 // to the thread that destroys the object when it reads mValue with
44 // acquire semantics.
45 uint32_t count = mRefCount.fetch_sub(1, std::memory_order_release) - 1;
46 NS_LOG_RELEASE(this, count, "nsStringBuffer");
47 if (count == 0) {
48 // We're going to destroy the object on this thread, so we need
49 // acquire semantics to synchronize with the memory released by
50 // the last release on other threads, that is, to ensure that
51 // writes prior to that release are now visible on this thread.
52 count = mRefCount.load(std::memory_order_acquire);
54 STRING_STAT_INCREMENT(Free);
55 free(this); // we were allocated with |malloc|
59 /**
60 * Alloc returns a pointer to a new string header with set capacity.
62 already_AddRefed<nsStringBuffer> nsStringBuffer::Alloc(size_t aSize) {
63 NS_ASSERTION(aSize != 0, "zero capacity allocation not allowed");
64 NS_ASSERTION(sizeof(nsStringBuffer) + aSize <= size_t(uint32_t(-1)) &&
65 sizeof(nsStringBuffer) + aSize > aSize,
66 "mStorageSize will truncate");
68 auto* hdr = (nsStringBuffer*)malloc(sizeof(nsStringBuffer) + aSize);
69 if (hdr) {
70 STRING_STAT_INCREMENT(Alloc);
72 hdr->mRefCount = 1;
73 hdr->mStorageSize = aSize;
74 NS_LOG_ADDREF(hdr, 1, "nsStringBuffer", sizeof(*hdr));
76 return already_AddRefed(hdr);
79 template <typename CharT>
80 static already_AddRefed<nsStringBuffer> DoCreate(const CharT* aData,
81 size_t aLength) {
82 RefPtr<nsStringBuffer> buffer =
83 nsStringBuffer::Alloc((aLength + 1) * sizeof(CharT));
84 if (MOZ_UNLIKELY(!buffer)) {
85 return nullptr;
87 auto* data = reinterpret_cast<CharT*>(buffer->Data());
88 memcpy(data, aData, aLength * sizeof(CharT));
89 data[aLength] = 0;
90 return buffer.forget();
93 already_AddRefed<nsStringBuffer> nsStringBuffer::Create(const char* aData,
94 size_t aLength) {
95 return DoCreate(aData, aLength);
98 already_AddRefed<nsStringBuffer> nsStringBuffer::Create(const char16_t* aData,
99 size_t aLength) {
100 return DoCreate(aData, aLength);
103 nsStringBuffer* nsStringBuffer::Realloc(nsStringBuffer* aHdr, size_t aSize) {
104 STRING_STAT_INCREMENT(Realloc);
106 NS_ASSERTION(aSize != 0, "zero capacity allocation not allowed");
107 NS_ASSERTION(sizeof(nsStringBuffer) + aSize <= size_t(uint32_t(-1)) &&
108 sizeof(nsStringBuffer) + aSize > aSize,
109 "mStorageSize will truncate");
111 // no point in trying to save ourselves if we hit this assertion
112 NS_ASSERTION(!aHdr->IsReadonly(), "|Realloc| attempted on readonly string");
114 // Treat this as a release and addref for refcounting purposes, since we
115 // just asserted that the refcount is 1. If we don't do that, refcount
116 // logging will claim we've leaked all sorts of stuff.
117 NS_LOG_RELEASE(aHdr, 0, "nsStringBuffer");
119 aHdr = (nsStringBuffer*)realloc(aHdr, sizeof(nsStringBuffer) + aSize);
120 if (aHdr) {
121 NS_LOG_ADDREF(aHdr, 1, "nsStringBuffer", sizeof(*aHdr));
122 aHdr->mStorageSize = aSize;
125 return aHdr;
128 nsStringBuffer* nsStringBuffer::FromString(const nsAString& aStr) {
129 if (!(aStr.mDataFlags & nsAString::DataFlags::REFCOUNTED)) {
130 return nullptr;
133 return FromData(aStr.mData);
136 nsStringBuffer* nsStringBuffer::FromString(const nsACString& aStr) {
137 if (!(aStr.mDataFlags & nsACString::DataFlags::REFCOUNTED)) {
138 return nullptr;
141 return FromData(aStr.mData);
144 void nsStringBuffer::ToString(uint32_t aLen, nsAString& aStr,
145 bool aMoveOwnership) {
146 char16_t* data = static_cast<char16_t*>(Data());
148 MOZ_DIAGNOSTIC_ASSERT(data[aLen] == char16_t(0),
149 "data should be null terminated");
151 nsAString::DataFlags flags =
152 nsAString::DataFlags::REFCOUNTED | nsAString::DataFlags::TERMINATED;
154 if (!aMoveOwnership) {
155 AddRef();
157 aStr.Finalize();
158 aStr.SetData(data, aLen, flags);
161 void nsStringBuffer::ToString(uint32_t aLen, nsACString& aStr,
162 bool aMoveOwnership) {
163 char* data = static_cast<char*>(Data());
165 MOZ_DIAGNOSTIC_ASSERT(data[aLen] == char(0),
166 "data should be null terminated");
168 nsACString::DataFlags flags =
169 nsACString::DataFlags::REFCOUNTED | nsACString::DataFlags::TERMINATED;
171 if (!aMoveOwnership) {
172 AddRef();
174 aStr.Finalize();
175 aStr.SetData(data, aLen, flags);
178 size_t nsStringBuffer::SizeOfIncludingThisIfUnshared(
179 mozilla::MallocSizeOf aMallocSizeOf) const {
180 return IsReadonly() ? 0 : aMallocSizeOf(this);
183 size_t nsStringBuffer::SizeOfIncludingThisEvenIfShared(
184 mozilla::MallocSizeOf aMallocSizeOf) const {
185 return aMallocSizeOf(this);