/src/mozilla-central/dom/base/AttrArray.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | /* |
8 | | * Storage of the children and attributes of a DOM node; storage for |
9 | | * the two is unified to minimize footprint. |
10 | | */ |
11 | | |
12 | | #include "AttrArray.h" |
13 | | |
14 | | #include "mozilla/CheckedInt.h" |
15 | | #include "mozilla/MathAlgorithms.h" |
16 | | #include "mozilla/MemoryReporting.h" |
17 | | |
18 | | #include "nsMappedAttributeElement.h" |
19 | | #include "nsString.h" |
20 | | #include "nsHTMLStyleSheet.h" |
21 | | #include "nsMappedAttributes.h" |
22 | | #include "nsUnicharUtils.h" |
23 | | #include "nsContentUtils.h" // nsAutoScriptBlocker |
24 | | |
25 | | using mozilla::CheckedUint32; |
26 | | |
27 | | AttrArray::Impl::~Impl() |
28 | 0 | { |
29 | 0 | for (InternalAttr& attr : NonMappedAttrs()) { |
30 | 0 | attr.~InternalAttr(); |
31 | 0 | } |
32 | 0 |
|
33 | 0 | NS_IF_RELEASE(mMappedAttrs); |
34 | 0 | } |
35 | | |
36 | | const nsAttrValue* |
37 | | AttrArray::GetAttr(nsAtom* aLocalName, int32_t aNamespaceID) const |
38 | 0 | { |
39 | 0 | if (aNamespaceID == kNameSpaceID_None) { |
40 | 0 | // This should be the common case so lets make an optimized loop |
41 | 0 | for (const InternalAttr& attr : NonMappedAttrs()) { |
42 | 0 | if (attr.mName.Equals(aLocalName)) { |
43 | 0 | return &attr.mValue; |
44 | 0 | } |
45 | 0 | } |
46 | 0 |
|
47 | 0 | if (mImpl && mImpl->mMappedAttrs) { |
48 | 0 | return mImpl->mMappedAttrs->GetAttr(aLocalName); |
49 | 0 | } |
50 | 0 | } else { |
51 | 0 | for (const InternalAttr& attr : NonMappedAttrs()) { |
52 | 0 | if (attr.mName.Equals(aLocalName, aNamespaceID)) { |
53 | 0 | return &attr.mValue; |
54 | 0 | } |
55 | 0 | } |
56 | 0 | } |
57 | 0 |
|
58 | 0 | return nullptr; |
59 | 0 | } |
60 | | |
61 | | const nsAttrValue* |
62 | | AttrArray::GetAttr(const nsAString& aLocalName) const |
63 | 0 | { |
64 | 0 | for (const InternalAttr& attr : NonMappedAttrs()) { |
65 | 0 | if (attr.mName.Equals(aLocalName)) { |
66 | 0 | return &attr.mValue; |
67 | 0 | } |
68 | 0 | } |
69 | 0 |
|
70 | 0 | if (mImpl && mImpl->mMappedAttrs) { |
71 | 0 | return mImpl->mMappedAttrs->GetAttr(aLocalName); |
72 | 0 | } |
73 | 0 | |
74 | 0 | return nullptr; |
75 | 0 | } |
76 | | |
77 | | const nsAttrValue* |
78 | | AttrArray::GetAttr(const nsAString& aName, |
79 | | nsCaseTreatment aCaseSensitive) const |
80 | 0 | { |
81 | 0 | // Check whether someone is being silly and passing non-lowercase |
82 | 0 | // attr names. |
83 | 0 | if (aCaseSensitive == eIgnoreCase && |
84 | 0 | nsContentUtils::StringContainsASCIIUpper(aName)) { |
85 | 0 | // Try again with a lowercased name, but make sure we can't reenter this |
86 | 0 | // block by passing eCaseSensitive for aCaseSensitive. |
87 | 0 | nsAutoString lowercase; |
88 | 0 | nsContentUtils::ASCIIToLower(aName, lowercase); |
89 | 0 | return GetAttr(lowercase, eCaseMatters); |
90 | 0 | } |
91 | 0 | |
92 | 0 | for (const InternalAttr& attr : NonMappedAttrs()) { |
93 | 0 | if (attr.mName.QualifiedNameEquals(aName)) { |
94 | 0 | return &attr.mValue; |
95 | 0 | } |
96 | 0 | } |
97 | 0 |
|
98 | 0 | if (mImpl && mImpl->mMappedAttrs) { |
99 | 0 | return mImpl->mMappedAttrs->GetAttr(aName); |
100 | 0 | } |
101 | 0 | |
102 | 0 | return nullptr; |
103 | 0 | } |
104 | | |
105 | | const nsAttrValue* |
106 | | AttrArray::AttrAt(uint32_t aPos) const |
107 | 0 | { |
108 | 0 | NS_ASSERTION(aPos < AttrCount(), |
109 | 0 | "out-of-bounds access in AttrArray"); |
110 | 0 |
|
111 | 0 | uint32_t nonmapped = NonMappedAttrCount(); |
112 | 0 | if (aPos < nonmapped) { |
113 | 0 | return &mImpl->NonMappedAttrs()[aPos].mValue; |
114 | 0 | } |
115 | 0 | |
116 | 0 | return mImpl->mMappedAttrs->AttrAt(aPos - nonmapped); |
117 | 0 | } |
118 | | |
119 | | template<typename Name> |
120 | | inline nsresult |
121 | | AttrArray::AddNewAttribute(Name* aName, nsAttrValue& aValue) |
122 | 0 | { |
123 | 0 | MOZ_ASSERT(!mImpl || mImpl->mCapacity >= mImpl->mAttrCount); |
124 | 0 | if (!mImpl || mImpl->mCapacity == mImpl->mAttrCount) { |
125 | 0 | if (!GrowBy(1)) { |
126 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
127 | 0 | } |
128 | 0 | } |
129 | 0 | |
130 | 0 | InternalAttr& attr = mImpl->mBuffer[mImpl->mAttrCount++]; |
131 | 0 | new (&attr.mName) nsAttrName(aName); |
132 | 0 | new (&attr.mValue) nsAttrValue(); |
133 | 0 | attr.mValue.SwapValueWith(aValue); |
134 | 0 | return NS_OK; |
135 | 0 | } Unexecuted instantiation: nsresult AttrArray::AddNewAttribute<nsAtom>(nsAtom*, nsAttrValue&) Unexecuted instantiation: nsresult AttrArray::AddNewAttribute<mozilla::dom::NodeInfo>(mozilla::dom::NodeInfo*, nsAttrValue&) |
136 | | |
137 | | nsresult |
138 | | AttrArray::SetAndSwapAttr(nsAtom* aLocalName, nsAttrValue& aValue, |
139 | | bool* aHadValue) |
140 | 0 | { |
141 | 0 | *aHadValue = false; |
142 | 0 |
|
143 | 0 | for (InternalAttr& attr : NonMappedAttrs()) { |
144 | 0 | if (attr.mName.Equals(aLocalName)) { |
145 | 0 | attr.mValue.SwapValueWith(aValue); |
146 | 0 | *aHadValue = true; |
147 | 0 | return NS_OK; |
148 | 0 | } |
149 | 0 | } |
150 | 0 |
|
151 | 0 | return AddNewAttribute(aLocalName, aValue); |
152 | 0 | } |
153 | | |
154 | | nsresult |
155 | | AttrArray::SetAndSwapAttr(mozilla::dom::NodeInfo* aName, |
156 | | nsAttrValue& aValue, bool* aHadValue) |
157 | 0 | { |
158 | 0 | int32_t namespaceID = aName->NamespaceID(); |
159 | 0 | nsAtom* localName = aName->NameAtom(); |
160 | 0 | if (namespaceID == kNameSpaceID_None) { |
161 | 0 | return SetAndSwapAttr(localName, aValue, aHadValue); |
162 | 0 | } |
163 | 0 | |
164 | 0 | *aHadValue = false; |
165 | 0 | for (InternalAttr& attr : NonMappedAttrs()) { |
166 | 0 | if (attr.mName.Equals(localName, namespaceID)) { |
167 | 0 | attr.mName.SetTo(aName); |
168 | 0 | attr.mValue.SwapValueWith(aValue); |
169 | 0 | *aHadValue = true; |
170 | 0 | return NS_OK; |
171 | 0 | } |
172 | 0 | } |
173 | 0 |
|
174 | 0 | return AddNewAttribute(aName, aValue); |
175 | 0 | } |
176 | | |
177 | | nsresult |
178 | | AttrArray::RemoveAttrAt(uint32_t aPos, nsAttrValue& aValue) |
179 | 0 | { |
180 | 0 | NS_ASSERTION(aPos < AttrCount(), "out-of-bounds"); |
181 | 0 |
|
182 | 0 | uint32_t nonmapped = NonMappedAttrCount(); |
183 | 0 | if (aPos < nonmapped) { |
184 | 0 | mImpl->mBuffer[aPos].mValue.SwapValueWith(aValue); |
185 | 0 | mImpl->mBuffer[aPos].~InternalAttr(); |
186 | 0 |
|
187 | 0 | memmove(mImpl->mBuffer + aPos, |
188 | 0 | mImpl->mBuffer + aPos + 1, |
189 | 0 | (mImpl->mAttrCount - aPos - 1) * sizeof(InternalAttr)); |
190 | 0 |
|
191 | 0 | --mImpl->mAttrCount; |
192 | 0 |
|
193 | 0 | return NS_OK; |
194 | 0 | } |
195 | 0 | |
196 | 0 | if (MappedAttrCount() == 1) { |
197 | 0 | // We're removing the last mapped attribute. Can't swap in this |
198 | 0 | // case; have to copy. |
199 | 0 | aValue.SetTo(*mImpl->mMappedAttrs->AttrAt(0)); |
200 | 0 | NS_RELEASE(mImpl->mMappedAttrs); |
201 | 0 |
|
202 | 0 | return NS_OK; |
203 | 0 | } |
204 | 0 |
|
205 | 0 | RefPtr<nsMappedAttributes> mapped = |
206 | 0 | GetModifiableMapped(nullptr, nullptr, false); |
207 | 0 |
|
208 | 0 | mapped->RemoveAttrAt(aPos - nonmapped, aValue); |
209 | 0 |
|
210 | 0 | return MakeMappedUnique(mapped); |
211 | 0 | } |
212 | | |
213 | | mozilla::dom::BorrowedAttrInfo |
214 | | AttrArray::AttrInfoAt(uint32_t aPos) const |
215 | 0 | { |
216 | 0 | NS_ASSERTION(aPos < AttrCount(), |
217 | 0 | "out-of-bounds access in AttrArray"); |
218 | 0 |
|
219 | 0 | uint32_t nonmapped = NonMappedAttrCount(); |
220 | 0 | if (aPos < nonmapped) { |
221 | 0 | InternalAttr& attr = mImpl->mBuffer[aPos]; |
222 | 0 | return BorrowedAttrInfo(&attr.mName, &attr.mValue); |
223 | 0 | } |
224 | 0 | |
225 | 0 | return BorrowedAttrInfo( |
226 | 0 | mImpl->mMappedAttrs->NameAt(aPos - nonmapped), |
227 | 0 | mImpl->mMappedAttrs->AttrAt(aPos - nonmapped)); |
228 | 0 | } |
229 | | |
230 | | const nsAttrName* |
231 | | AttrArray::AttrNameAt(uint32_t aPos) const |
232 | 0 | { |
233 | 0 | NS_ASSERTION(aPos < AttrCount(), |
234 | 0 | "out-of-bounds access in AttrArray"); |
235 | 0 |
|
236 | 0 | uint32_t nonmapped = NonMappedAttrCount(); |
237 | 0 | if (aPos < nonmapped) { |
238 | 0 | return &mImpl->mBuffer[aPos].mName; |
239 | 0 | } |
240 | 0 | |
241 | 0 | return mImpl->mMappedAttrs->NameAt(aPos - nonmapped); |
242 | 0 | } |
243 | | |
244 | | const nsAttrName* |
245 | | AttrArray::GetSafeAttrNameAt(uint32_t aPos) const |
246 | 0 | { |
247 | 0 | uint32_t nonmapped = NonMappedAttrCount(); |
248 | 0 | if (aPos < nonmapped) { |
249 | 0 | return &mImpl->mBuffer[aPos].mName; |
250 | 0 | } |
251 | 0 | |
252 | 0 | if (aPos >= AttrCount()) { |
253 | 0 | return nullptr; |
254 | 0 | } |
255 | 0 | |
256 | 0 | return mImpl->mMappedAttrs->NameAt(aPos - nonmapped); |
257 | 0 | } |
258 | | |
259 | | const nsAttrName* |
260 | | AttrArray::GetExistingAttrNameFromQName(const nsAString& aName) const |
261 | 0 | { |
262 | 0 | for (const InternalAttr& attr : NonMappedAttrs()) { |
263 | 0 | if (attr.mName.QualifiedNameEquals(aName)) { |
264 | 0 | return &attr.mName; |
265 | 0 | } |
266 | 0 | } |
267 | 0 |
|
268 | 0 | if (mImpl && mImpl->mMappedAttrs) { |
269 | 0 | return mImpl->mMappedAttrs->GetExistingAttrNameFromQName(aName); |
270 | 0 | } |
271 | 0 | |
272 | 0 | return nullptr; |
273 | 0 | } |
274 | | |
275 | | int32_t |
276 | | AttrArray::IndexOfAttr(nsAtom* aLocalName, int32_t aNamespaceID) const |
277 | 0 | { |
278 | 0 | if (!mImpl) { |
279 | 0 | return -1; |
280 | 0 | } |
281 | 0 | |
282 | 0 | int32_t idx; |
283 | 0 | if (mImpl->mMappedAttrs && aNamespaceID == kNameSpaceID_None) { |
284 | 0 | idx = mImpl->mMappedAttrs->IndexOfAttr(aLocalName); |
285 | 0 | if (idx >= 0) { |
286 | 0 | return NonMappedAttrCount() + idx; |
287 | 0 | } |
288 | 0 | } |
289 | 0 | |
290 | 0 | uint32_t i = 0; |
291 | 0 | if (aNamespaceID == kNameSpaceID_None) { |
292 | 0 | // This should be the common case so lets make an optimized loop |
293 | 0 | // Note that here we don't check for AttrSlotIsTaken() in the loop |
294 | 0 | // condition for the sake of performance because comparing aLocalName |
295 | 0 | // against null would fail in the loop body (since Equals() just compares |
296 | 0 | // the raw pointer value of aLocalName to what AttrSlotIsTaken() would be |
297 | 0 | // checking. |
298 | 0 | for (const InternalAttr& attr : NonMappedAttrs()) { |
299 | 0 | if (attr.mName.Equals(aLocalName)) { |
300 | 0 | return i; |
301 | 0 | } |
302 | 0 | ++i; |
303 | 0 | } |
304 | 0 | } else { |
305 | 0 | for (const InternalAttr& attr : NonMappedAttrs()) { |
306 | 0 | if (attr.mName.Equals(aLocalName, aNamespaceID)) { |
307 | 0 | return i; |
308 | 0 | } |
309 | 0 | ++i; |
310 | 0 | } |
311 | 0 | } |
312 | 0 |
|
313 | 0 | return -1; |
314 | 0 | } |
315 | | |
316 | | nsresult |
317 | | AttrArray::SetAndSwapMappedAttr(nsAtom* aLocalName, |
318 | | nsAttrValue& aValue, |
319 | | nsMappedAttributeElement* aContent, |
320 | | nsHTMLStyleSheet* aSheet, |
321 | | bool* aHadValue) |
322 | 0 | { |
323 | 0 | bool willAdd = true; |
324 | 0 | if (mImpl && mImpl->mMappedAttrs) { |
325 | 0 | willAdd = !mImpl->mMappedAttrs->GetAttr(aLocalName); |
326 | 0 | } |
327 | 0 |
|
328 | 0 | RefPtr<nsMappedAttributes> mapped = |
329 | 0 | GetModifiableMapped(aContent, aSheet, willAdd); |
330 | 0 |
|
331 | 0 | mapped->SetAndSwapAttr(aLocalName, aValue, aHadValue); |
332 | 0 |
|
333 | 0 | return MakeMappedUnique(mapped); |
334 | 0 | } |
335 | | |
336 | | nsresult |
337 | | AttrArray::DoSetMappedAttrStyleSheet(nsHTMLStyleSheet* aSheet) |
338 | 0 | { |
339 | 0 | MOZ_ASSERT(mImpl && mImpl->mMappedAttrs, |
340 | 0 | "Should have mapped attrs here!"); |
341 | 0 | if (aSheet == mImpl->mMappedAttrs->GetStyleSheet()) { |
342 | 0 | return NS_OK; |
343 | 0 | } |
344 | 0 | |
345 | 0 | RefPtr<nsMappedAttributes> mapped = |
346 | 0 | GetModifiableMapped(nullptr, nullptr, false); |
347 | 0 |
|
348 | 0 | mapped->SetStyleSheet(aSheet); |
349 | 0 |
|
350 | 0 | return MakeMappedUnique(mapped); |
351 | 0 | } |
352 | | |
353 | | nsresult |
354 | | AttrArray::DoUpdateMappedAttrRuleMapper(nsMappedAttributeElement& aElement) |
355 | 0 | { |
356 | 0 | MOZ_ASSERT(mImpl && mImpl->mMappedAttrs, "Should have mapped attrs here!"); |
357 | 0 |
|
358 | 0 | // First two args don't matter if the assert holds. |
359 | 0 | RefPtr<nsMappedAttributes> mapped = |
360 | 0 | GetModifiableMapped(nullptr, nullptr, false); |
361 | 0 |
|
362 | 0 | mapped->SetRuleMapper(aElement.GetAttributeMappingFunction()); |
363 | 0 |
|
364 | 0 | return MakeMappedUnique(mapped); |
365 | 0 | } |
366 | | |
367 | | void |
368 | | AttrArray::Compact() |
369 | 0 | { |
370 | 0 | if (!mImpl) { |
371 | 0 | return; |
372 | 0 | } |
373 | 0 | |
374 | 0 | if (!mImpl->mAttrCount && !mImpl->mMappedAttrs) { |
375 | 0 | mImpl.reset(); |
376 | 0 | return; |
377 | 0 | } |
378 | 0 | |
379 | 0 | // Nothing to do. |
380 | 0 | if (mImpl->mAttrCount == mImpl->mCapacity) { |
381 | 0 | return; |
382 | 0 | } |
383 | 0 | |
384 | 0 | Impl* impl = mImpl.release(); |
385 | 0 | impl = static_cast<Impl*>( |
386 | 0 | realloc(impl, Impl::AllocationSizeForAttributes(impl->mAttrCount))); |
387 | 0 | MOZ_ASSERT(impl, "failed to reallocate to a smaller buffer!"); |
388 | 0 | impl->mCapacity = impl->mAttrCount; |
389 | 0 | mImpl.reset(impl); |
390 | 0 | } |
391 | | |
392 | | uint32_t |
393 | | AttrArray::DoGetMappedAttrCount() const |
394 | 0 | { |
395 | 0 | MOZ_ASSERT(mImpl && mImpl->mMappedAttrs); |
396 | 0 | return static_cast<uint32_t>(mImpl->mMappedAttrs->Count()); |
397 | 0 | } |
398 | | |
399 | | nsresult |
400 | | AttrArray::ForceMapped(nsMappedAttributeElement* aContent, nsIDocument* aDocument) |
401 | 0 | { |
402 | 0 | nsHTMLStyleSheet* sheet = aDocument->GetAttributeStyleSheet(); |
403 | 0 | RefPtr<nsMappedAttributes> mapped = GetModifiableMapped(aContent, sheet, false, 0); |
404 | 0 | return MakeMappedUnique(mapped); |
405 | 0 | } |
406 | | |
407 | | void |
408 | | AttrArray::ClearMappedServoStyle() |
409 | 0 | { |
410 | 0 | if (mImpl && mImpl->mMappedAttrs) { |
411 | 0 | mImpl->mMappedAttrs->ClearServoStyle(); |
412 | 0 | } |
413 | 0 | } |
414 | | |
415 | | nsMappedAttributes* |
416 | | AttrArray::GetModifiableMapped(nsMappedAttributeElement* aContent, |
417 | | nsHTMLStyleSheet* aSheet, |
418 | | bool aWillAddAttr, |
419 | | int32_t aAttrCount) |
420 | 0 | { |
421 | 0 | if (mImpl && mImpl->mMappedAttrs) { |
422 | 0 | return mImpl->mMappedAttrs->Clone(aWillAddAttr); |
423 | 0 | } |
424 | 0 | |
425 | 0 | MOZ_ASSERT(aContent, "Trying to create modifiable without content"); |
426 | 0 |
|
427 | 0 | nsMapRuleToAttributesFunc mapRuleFunc = |
428 | 0 | aContent->GetAttributeMappingFunction(); |
429 | 0 | return new (aAttrCount) nsMappedAttributes(aSheet, mapRuleFunc); |
430 | 0 | } |
431 | | |
432 | | nsresult |
433 | | AttrArray::MakeMappedUnique(nsMappedAttributes* aAttributes) |
434 | 0 | { |
435 | 0 | NS_ASSERTION(aAttributes, "missing attributes"); |
436 | 0 |
|
437 | 0 | if (!mImpl && !GrowBy(1)) { |
438 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
439 | 0 | } |
440 | 0 | |
441 | 0 | if (!aAttributes->GetStyleSheet()) { |
442 | 0 | // This doesn't currently happen, but it could if we do loading right |
443 | 0 |
|
444 | 0 | RefPtr<nsMappedAttributes> mapped(aAttributes); |
445 | 0 | mapped.swap(mImpl->mMappedAttrs); |
446 | 0 |
|
447 | 0 | return NS_OK; |
448 | 0 | } |
449 | 0 | |
450 | 0 | RefPtr<nsMappedAttributes> mapped = |
451 | 0 | aAttributes->GetStyleSheet()->UniqueMappedAttributes(aAttributes); |
452 | 0 | NS_ENSURE_TRUE(mapped, NS_ERROR_OUT_OF_MEMORY); |
453 | 0 |
|
454 | 0 | if (mapped != aAttributes) { |
455 | 0 | // Reset the stylesheet of aAttributes so that it doesn't spend time |
456 | 0 | // trying to remove itself from the hash. There is no risk that aAttributes |
457 | 0 | // is in the hash since it will always have come from GetModifiableMapped, |
458 | 0 | // which never returns maps that are in the hash (such hashes are by |
459 | 0 | // nature not modifiable). |
460 | 0 | aAttributes->DropStyleSheetReference(); |
461 | 0 | } |
462 | 0 | mapped.swap(mImpl->mMappedAttrs); |
463 | 0 |
|
464 | 0 | return NS_OK; |
465 | 0 | } |
466 | | |
467 | | const nsMappedAttributes* |
468 | | AttrArray::GetMapped() const |
469 | 0 | { |
470 | 0 | return mImpl ? mImpl->mMappedAttrs : nullptr; |
471 | 0 | } |
472 | | |
473 | | nsresult |
474 | | AttrArray::EnsureCapacityToClone(const AttrArray& aOther) |
475 | 0 | { |
476 | 0 | MOZ_ASSERT(!mImpl, "AttrArray::EnsureCapacityToClone requires the array be empty when called"); |
477 | 0 |
|
478 | 0 | uint32_t attrCount = aOther.NonMappedAttrCount(); |
479 | 0 | if (!attrCount) { |
480 | 0 | return NS_OK; |
481 | 0 | } |
482 | 0 | |
483 | 0 | // No need to use a CheckedUint32 because we are cloning. We know that we |
484 | 0 | // have already allocated an AttrArray of this size. |
485 | 0 | mImpl.reset(static_cast<Impl*>(malloc( |
486 | 0 | Impl::AllocationSizeForAttributes(attrCount)))); |
487 | 0 | NS_ENSURE_TRUE(mImpl, NS_ERROR_OUT_OF_MEMORY); |
488 | 0 |
|
489 | 0 | mImpl->mMappedAttrs = nullptr; |
490 | 0 | mImpl->mCapacity = attrCount; |
491 | 0 | mImpl->mAttrCount = 0; |
492 | 0 |
|
493 | 0 | return NS_OK; |
494 | 0 | } |
495 | | |
496 | | bool |
497 | | AttrArray::GrowBy(uint32_t aGrowSize) |
498 | 0 | { |
499 | 0 | const uint32_t kLinearThreshold = 16; |
500 | 0 | const uint32_t kLinearGrowSize = 4; |
501 | 0 |
|
502 | 0 | CheckedUint32 capacity = mImpl ? mImpl->mCapacity : 0; |
503 | 0 | CheckedUint32 minCapacity = capacity; |
504 | 0 | minCapacity += aGrowSize; |
505 | 0 | if (!minCapacity.isValid()) { |
506 | 0 | return false; |
507 | 0 | } |
508 | 0 | |
509 | 0 | if (capacity.value() <= kLinearThreshold) { |
510 | 0 | do { |
511 | 0 | capacity += kLinearGrowSize; |
512 | 0 | if (!capacity.isValid()) { |
513 | 0 | return false; |
514 | 0 | } |
515 | 0 | } while (capacity.value() < minCapacity.value()); |
516 | 0 | } else { |
517 | 0 | uint32_t shift = mozilla::CeilingLog2(minCapacity.value()); |
518 | 0 | if (shift >= 32) { |
519 | 0 | return false; |
520 | 0 | } |
521 | 0 | capacity = 1u << shift; |
522 | 0 | } |
523 | 0 |
|
524 | 0 | CheckedUint32 sizeInBytes = capacity.value(); |
525 | 0 | sizeInBytes *= sizeof(InternalAttr); |
526 | 0 | if (!sizeInBytes.isValid()) { |
527 | 0 | return false; |
528 | 0 | } |
529 | 0 | |
530 | 0 | sizeInBytes += sizeof(Impl); |
531 | 0 | if (!sizeInBytes.isValid()) { |
532 | 0 | return false; |
533 | 0 | } |
534 | 0 | |
535 | 0 | MOZ_ASSERT(sizeInBytes.value() == |
536 | 0 | Impl::AllocationSizeForAttributes(capacity.value())); |
537 | 0 |
|
538 | 0 | const bool needToInitialize = !mImpl; |
539 | 0 | Impl* newImpl = static_cast<Impl*>(realloc(mImpl.release(), sizeInBytes.value())); |
540 | 0 | NS_ENSURE_TRUE(newImpl, false); |
541 | 0 |
|
542 | 0 | mImpl.reset(newImpl); |
543 | 0 |
|
544 | 0 | // Set initial counts if we didn't have a buffer before |
545 | 0 | if (needToInitialize) { |
546 | 0 | mImpl->mMappedAttrs = nullptr; |
547 | 0 | mImpl->mAttrCount = 0; |
548 | 0 | } |
549 | 0 |
|
550 | 0 | mImpl->mCapacity = capacity.value(); |
551 | 0 | return true; |
552 | 0 | } |
553 | | |
554 | | size_t |
555 | | AttrArray::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const |
556 | 0 | { |
557 | 0 | size_t n = 0; |
558 | 0 | if (mImpl) { |
559 | 0 | // Don't add the size taken by *mMappedAttrs because it's shared. |
560 | 0 |
|
561 | 0 | n += aMallocSizeOf(mImpl.get()); |
562 | 0 |
|
563 | 0 | for (const InternalAttr& attr : NonMappedAttrs()) { |
564 | 0 | n += attr.mValue.SizeOfExcludingThis(aMallocSizeOf); |
565 | 0 | } |
566 | 0 | } |
567 | 0 |
|
568 | 0 | return n; |
569 | 0 | } |