Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/StringBuffer.h
Warning:line 188, column 7
Value stored to 'count' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O0 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name TestCallbackBinding.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/bindings/test -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/bindings/test -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D IMPL_LIBXUL -D MOZILLA_INTERNAL_API -I /root/firefox-clang/dom/bindings/test -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/bindings/test -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/dom -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/bindings -I /root/firefox-clang/dom/bindings -I /root/firefox-clang/js/xpconnect/src -I /root/firefox-clang/js/xpconnect/wrappers -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /root/firefox-clang/ipc/chromium/src -I /root/firefox-clang/third_party/abseil-cpp -I /root/firefox-clang/toolkit/components/telemetry -I /root/firefox-clang/xpcom/base -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ ../TestCallbackBinding.cpp
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#ifndef StringBuffer_h_
6#define StringBuffer_h_
7
8#include <atomic>
9#include <cstring>
10
11#include "mozilla/AlreadyAddRefed.h"
12#include "mozilla/Assertions.h"
13#include "mozilla/CheckedInt.h"
14#include "mozilla/Maybe.h"
15#include "mozilla/MemoryReporting.h"
16#include "mozilla/RefCounted.h"
17#include "mozmemory.h"
18
19namespace mozilla {
20
21/**
22 * This structure precedes the string buffers "we" allocate. It may be the
23 * case that nsTAString::mData does not point to one of these special
24 * buffers. The mDataFlags member variable distinguishes the buffer type.
25 *
26 * When this header is in use, it enables reference counting, and capacity
27 * tracking. NOTE: A string buffer can be modified only if its reference
28 * count is 1.
29 */
30class StringBuffer {
31 private:
32 std::atomic<uint32_t> mRefCount;
33 uint32_t mStorageSize;
34
35 public:
36 MOZ_DECLARE_REFCOUNTED_TYPENAME(StringBuffer)const char* typeName() const { return "StringBuffer"; } size_t
typeSize() const { return sizeof(*this); }
37
38 /**
39 * Allocates a new string buffer, with given size in bytes and a
40 * reference count of one. When the string buffer is no longer needed,
41 * it should be released via Release.
42 *
43 * It is up to the caller to set the bytes corresponding to the string
44 * buffer by calling the Data method to fetch the raw data pointer. Care
45 * must be taken to properly null terminate the character array. The
46 * storage size can be greater than the length of the actual string
47 * (i.e., it is not required that the null terminator appear in the last
48 * storage unit of the string buffer's data).
49 *
50 * This guarantees that StorageSize() returns aSize if the returned
51 * buffer is non-null. Some callers like nsAttrValue rely on it.
52 *
53 * @return new string buffer or null if out of memory.
54 */
55 static already_AddRefed<StringBuffer> Alloc(
56 size_t aSize, mozilla::Maybe<arena_id_t> aArena = mozilla::Nothing()) {
57 MOZ_ASSERT(aSize != 0, "zero capacity allocation not allowed")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aSize != 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aSize != 0))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("aSize != 0" " (" "zero capacity allocation not allowed"
")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/StringBuffer.h"
, 57); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aSize != 0" ") ("
"zero capacity allocation not allowed" ")"); do { MOZ_CrashSequence
(__null, 57); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
58 MOZ_ASSERT(sizeof(StringBuffer) + aSize <= size_t(uint32_t(-1)) &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(sizeof(StringBuffer) + aSize <= size_t(uint32_t(-
1)) && sizeof(StringBuffer) + aSize > aSize)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(sizeof(StringBuffer) + aSize <= size_t(uint32_t(-
1)) && sizeof(StringBuffer) + aSize > aSize))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("sizeof(StringBuffer) + aSize <= size_t(uint32_t(-1)) && sizeof(StringBuffer) + aSize > aSize"
" (" "mStorageSize will truncate" ")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/StringBuffer.h"
, 60); AnnotateMozCrashReason("MOZ_ASSERT" "(" "sizeof(StringBuffer) + aSize <= size_t(uint32_t(-1)) && sizeof(StringBuffer) + aSize > aSize"
") (" "mStorageSize will truncate" ")"); do { MOZ_CrashSequence
(__null, 60); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
59 sizeof(StringBuffer) + aSize > aSize,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(sizeof(StringBuffer) + aSize <= size_t(uint32_t(-
1)) && sizeof(StringBuffer) + aSize > aSize)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(sizeof(StringBuffer) + aSize <= size_t(uint32_t(-
1)) && sizeof(StringBuffer) + aSize > aSize))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("sizeof(StringBuffer) + aSize <= size_t(uint32_t(-1)) && sizeof(StringBuffer) + aSize > aSize"
" (" "mStorageSize will truncate" ")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/StringBuffer.h"
, 60); AnnotateMozCrashReason("MOZ_ASSERT" "(" "sizeof(StringBuffer) + aSize <= size_t(uint32_t(-1)) && sizeof(StringBuffer) + aSize > aSize"
") (" "mStorageSize will truncate" ")"); do { MOZ_CrashSequence
(__null, 60); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
60 "mStorageSize will truncate")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(sizeof(StringBuffer) + aSize <= size_t(uint32_t(-
1)) && sizeof(StringBuffer) + aSize > aSize)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(sizeof(StringBuffer) + aSize <= size_t(uint32_t(-
1)) && sizeof(StringBuffer) + aSize > aSize))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("sizeof(StringBuffer) + aSize <= size_t(uint32_t(-1)) && sizeof(StringBuffer) + aSize > aSize"
" (" "mStorageSize will truncate" ")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/StringBuffer.h"
, 60); AnnotateMozCrashReason("MOZ_ASSERT" "(" "sizeof(StringBuffer) + aSize <= size_t(uint32_t(-1)) && sizeof(StringBuffer) + aSize > aSize"
") (" "mStorageSize will truncate" ")"); do { MOZ_CrashSequence
(__null, 60); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
61
62 size_t bytes = sizeof(StringBuffer) + aSize;
63 void* hdr = aArena ? moz_arena_malloc(*aArena, bytes) : malloc(bytes);
64 if (!hdr) {
65 return nullptr;
66 }
67 return ConstructInPlace(hdr, aSize);
68 }
69
70 /**
71 * Like Alloc, but use aBuffer instead of allocating a new buffer. This can
72 * be used when the caller already has a malloced buffer of the right size and
73 * allocating a new one would be too expensive.
74 *
75 * aStorageSize must be the string's length in bytes (including the null
76 * terminator). The caller must initialize all of these bytes either before or
77 * after calling this function.
78 *
79 * @return the new StringBuffer header.
80 */
81 static already_AddRefed<StringBuffer> ConstructInPlace(void* aBuffer,
82 size_t aStorageSize) {
83 MOZ_ASSERT(aBuffer, "must have a valid buffer")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aBuffer)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aBuffer))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aBuffer" " (" "must have a valid buffer"
")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/StringBuffer.h"
, 83); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aBuffer" ") ("
"must have a valid buffer" ")"); do { MOZ_CrashSequence(__null
, 83); __attribute__((nomerge)) ::abort(); } while (false); }
} while (false)
;
84 MOZ_ASSERT(aStorageSize != 0, "zero capacity StringBuffer not allowed")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aStorageSize != 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aStorageSize != 0))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("aStorageSize != 0"
" (" "zero capacity StringBuffer not allowed" ")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/StringBuffer.h"
, 84); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aStorageSize != 0"
") (" "zero capacity StringBuffer not allowed" ")"); do { MOZ_CrashSequence
(__null, 84); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
85 auto* hdr = new (aBuffer) StringBuffer();
86 hdr->mRefCount = 1;
87 hdr->mStorageSize = aStorageSize;
88 detail::RefCountLogger::logAddRef(hdr, 1);
89 return already_AddRefed(hdr);
90 }
91
92 /**
93 * Returns true if (aLength + 1) * sizeof(CharT) is a valid allocation size
94 * for Alloc. Adds +1 to aLength for the null-terminator.
95 */
96 template <typename CharT>
97 static constexpr bool IsValidLength(size_t aLength) {
98 auto checkedSize =
99 (CheckedUint32(aLength) + 1) * sizeof(CharT) + sizeof(StringBuffer);
100 return checkedSize.isValid();
101 }
102
103 /**
104 * Returns a string buffer initialized with the given string on it, or null on
105 * OOM.
106 * Note that this will allocate extra space for the trailing null byte, which
107 * this method will add.
108 */
109 static already_AddRefed<StringBuffer> Create(const char16_t* aData,
110 size_t aLength) {
111 return DoCreate(aData, aLength);
112 }
113 static already_AddRefed<StringBuffer> Create(const char* aData,
114 size_t aLength) {
115 return DoCreate(aData, aLength);
116 }
117 static already_AddRefed<StringBuffer> Create(const unsigned char* aData,
118 size_t aLength) {
119 return DoCreate(aData, aLength);
120 }
121
122 /**
123 * Resizes the given string buffer to the specified storage size. This
124 * method must not be called on a readonly string buffer. Use this API
125 * carefully!!
126 *
127 * This method behaves like the ANSI-C realloc function. (i.e., If the
128 * allocation fails, null will be returned and the given string buffer
129 * will remain unmodified.)
130 *
131 * @see IsReadonly
132 */
133 static StringBuffer* Realloc(
134 StringBuffer* aHdr, size_t aSize,
135 mozilla::Maybe<arena_id_t> aArena = mozilla::Nothing()) {
136 MOZ_ASSERT(aSize != 0, "zero capacity allocation not allowed")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aSize != 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aSize != 0))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("aSize != 0" " (" "zero capacity allocation not allowed"
")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/StringBuffer.h"
, 136); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aSize != 0" ") ("
"zero capacity allocation not allowed" ")"); do { MOZ_CrashSequence
(__null, 136); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
137 MOZ_ASSERT(sizeof(StringBuffer) + aSize <= size_t(uint32_t(-1)) &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(sizeof(StringBuffer) + aSize <= size_t(uint32_t(-
1)) && sizeof(StringBuffer) + aSize > aSize)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(sizeof(StringBuffer) + aSize <= size_t(uint32_t(-
1)) && sizeof(StringBuffer) + aSize > aSize))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("sizeof(StringBuffer) + aSize <= size_t(uint32_t(-1)) && sizeof(StringBuffer) + aSize > aSize"
" (" "mStorageSize will truncate" ")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/StringBuffer.h"
, 139); AnnotateMozCrashReason("MOZ_ASSERT" "(" "sizeof(StringBuffer) + aSize <= size_t(uint32_t(-1)) && sizeof(StringBuffer) + aSize > aSize"
") (" "mStorageSize will truncate" ")"); do { MOZ_CrashSequence
(__null, 139); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
138 sizeof(StringBuffer) + aSize > aSize,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(sizeof(StringBuffer) + aSize <= size_t(uint32_t(-
1)) && sizeof(StringBuffer) + aSize > aSize)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(sizeof(StringBuffer) + aSize <= size_t(uint32_t(-
1)) && sizeof(StringBuffer) + aSize > aSize))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("sizeof(StringBuffer) + aSize <= size_t(uint32_t(-1)) && sizeof(StringBuffer) + aSize > aSize"
" (" "mStorageSize will truncate" ")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/StringBuffer.h"
, 139); AnnotateMozCrashReason("MOZ_ASSERT" "(" "sizeof(StringBuffer) + aSize <= size_t(uint32_t(-1)) && sizeof(StringBuffer) + aSize > aSize"
") (" "mStorageSize will truncate" ")"); do { MOZ_CrashSequence
(__null, 139); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
139 "mStorageSize will truncate")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(sizeof(StringBuffer) + aSize <= size_t(uint32_t(-
1)) && sizeof(StringBuffer) + aSize > aSize)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(sizeof(StringBuffer) + aSize <= size_t(uint32_t(-
1)) && sizeof(StringBuffer) + aSize > aSize))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("sizeof(StringBuffer) + aSize <= size_t(uint32_t(-1)) && sizeof(StringBuffer) + aSize > aSize"
" (" "mStorageSize will truncate" ")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/StringBuffer.h"
, 139); AnnotateMozCrashReason("MOZ_ASSERT" "(" "sizeof(StringBuffer) + aSize <= size_t(uint32_t(-1)) && sizeof(StringBuffer) + aSize > aSize"
") (" "mStorageSize will truncate" ")"); do { MOZ_CrashSequence
(__null, 139); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
140
141 // no point in trying to save ourselves if we hit this assertion
142 MOZ_ASSERT(!aHdr->IsReadonly(), "|Realloc| attempted on readonly string")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aHdr->IsReadonly())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aHdr->IsReadonly()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("!aHdr->IsReadonly()"
" (" "|Realloc| attempted on readonly string" ")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/StringBuffer.h"
, 142); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aHdr->IsReadonly()"
") (" "|Realloc| attempted on readonly string" ")"); do { MOZ_CrashSequence
(__null, 142); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
143
144 // Treat this as a release and addref for refcounting purposes, since we
145 // just asserted that the refcount is 1. If we don't do that, refcount
146 // logging will claim we've leaked all sorts of stuff.
147 {
148 detail::RefCountLogger::ReleaseLogger logger(aHdr);
149 logger.logRelease(0);
150 }
151
152 size_t bytes = sizeof(StringBuffer) + aSize;
153 aHdr = aArena ? (StringBuffer*)moz_arena_realloc(*aArena, aHdr, bytes)
154 : (StringBuffer*)realloc(aHdr, bytes);
155 if (aHdr) {
156 detail::RefCountLogger::logAddRef(aHdr, 1);
157 aHdr->mStorageSize = aSize;
158 }
159
160 return aHdr;
161 }
162
163 void AddRef() {
164 // Memory synchronization is not required when incrementing a
165 // reference count. The first increment of a reference count on a
166 // thread is not important, since the first use of the object on a
167 // thread can happen before it. What is important is the transfer
168 // of the pointer to that thread, which may happen prior to the
169 // first increment on that thread. The necessary memory
170 // synchronization is done by the mechanism that transfers the
171 // pointer between threads.
172 uint32_t count = mRefCount.fetch_add(1, std::memory_order_relaxed) + 1;
173 detail::RefCountLogger::logAddRef(this, count);
174 }
175
176 void Release() {
177 // Since this may be the last release on this thread, we need release
178 // semantics so that prior writes on this thread are visible to the thread
179 // that destroys the object when it reads mValue with acquire semantics.
180 detail::RefCountLogger::ReleaseLogger logger(this);
181 uint32_t count = mRefCount.fetch_sub(1, std::memory_order_release) - 1;
182 logger.logRelease(count);
183 if (count == 0) {
184 // We're going to destroy the object on this thread, so we need acquire
185 // semantics to synchronize with the memory released by the last release
186 // on other threads, that is, to ensure that writes prior to that release
187 // are now visible on this thread.
188 count = mRefCount.load(std::memory_order_acquire);
Value stored to 'count' is never read
189 free(this); // We were allocated with malloc.
190 }
191 }
192
193 /**
194 * This method returns the string buffer corresponding to the given data
195 * pointer. The data pointer must have been returned previously by a
196 * call to the StringBuffer::Data method.
197 */
198 static StringBuffer* FromData(void* aData) {
199 return reinterpret_cast<StringBuffer*>(aData) - 1;
200 }
201
202 /**
203 * This method returns the data pointer for this string buffer.
204 */
205 void* Data() const {
206 return const_cast<char*>(reinterpret_cast<const char*>(this + 1));
207 }
208
209 /**
210 * This function returns the storage size of a string buffer in bytes.
211 * This value is the same value that was originally passed to Alloc (or
212 * Realloc).
213 */
214 uint32_t StorageSize() const { return mStorageSize; }
215
216 /**
217 * This function returns the allocation size of a string buffer in bytes.
218 * This includes the size of the StringBuffer header.
219 */
220 uint32_t AllocationSize() const {
221 return sizeof(StringBuffer) + StorageSize();
222 }
223
224 /**
225 * If this method returns false, then the caller can be sure that their
226 * reference to the string buffer is the only reference to the string
227 * buffer, and therefore it has exclusive access to the string buffer and
228 * associated data. However, if this function returns true, then other
229 * consumers may rely on the data in this buffer being immutable and
230 * other threads may access this buffer simultaneously.
231 */
232 bool IsReadonly() const {
233 // This doesn't lead to the destruction of the buffer, so we don't
234 // need to perform acquire memory synchronization for the normal
235 // reason that a reference count needs acquire synchronization
236 // (ensuring that all writes to the object made on other threads are
237 // visible to the thread destroying the object).
238 //
239 // We then need to consider the possibility that there were prior
240 // writes to the buffer on a different thread: one that has either
241 // since released its reference count, or one that also has access
242 // to this buffer through the same reference. There are two ways
243 // for that to happen: either the buffer pointer or a data structure
244 // (e.g., string object) pointing to the buffer was transferred from
245 // one thread to another, or the data structure pointing to the
246 // buffer was already visible on both threads. In the first case
247 // (transfer), the transfer of data from one thread to another would
248 // have handled the memory synchronization. In the latter case
249 // (data structure visible on both threads), the caller needed some
250 // sort of higher level memory synchronization to protect against
251 // the string object being mutated at the same time on multiple
252 // threads.
253
254 // See bug 1603504. TSan might complain about a race when using
255 // memory_order_relaxed, so use memory_order_acquire for making TSan
256 // happy.
257#if defined(MOZ_TSAN)
258 return mRefCount.load(std::memory_order_acquire) > 1;
259#else
260 return mRefCount.load(std::memory_order_relaxed) > 1;
261#endif
262 }
263
264 /**
265 * Alias for IsReadOnly.
266 */
267 bool HasMultipleReferences() const { return IsReadonly(); }
268
269#ifdef DEBUG1
270 /**
271 * Returns the buffer's reference count. This is only exposed for logging and
272 * testing purposes.
273 */
274 uint32_t RefCount() const {
275 return mRefCount.load(std::memory_order_acquire);
276 }
277#endif
278
279 /**
280 * This measures the size only if the StringBuffer is unshared.
281 */
282 size_t SizeOfIncludingThisIfUnshared(MallocSizeOf aMallocSizeOf) const {
283 return IsReadonly() ? 0 : aMallocSizeOf(this);
284 }
285
286 /**
287 * This measures the size regardless of whether the StringBuffer is
288 * unshared.
289 *
290 * WARNING: Only use this if you really know what you are doing, because
291 * it can easily lead to double-counting strings. If you do use them,
292 * please explain clearly in a comment why it's safe and won't lead to
293 * double-counting.
294 */
295 size_t SizeOfIncludingThisEvenIfShared(MallocSizeOf aMallocSizeOf) const {
296 return aMallocSizeOf(this);
297 }
298
299 private:
300 template <typename CharT>
301 static already_AddRefed<StringBuffer> DoCreate(const CharT* aData,
302 size_t aLength) {
303 StringBuffer* buffer = Alloc((aLength + 1) * sizeof(CharT)).take();
304 if (MOZ_LIKELY(buffer)(__builtin_expect(!!(buffer), 1))) {
305 auto* data = reinterpret_cast<CharT*>(buffer->Data());
306 memcpy(data, aData, aLength * sizeof(CharT));
307 data[aLength] = 0;
308 }
309 return already_AddRefed(buffer);
310 }
311};
312
313} // namespace mozilla
314
315#endif