Bug Summary

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