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 |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | |
20 | namespace 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 | */ |
31 | class 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 |