Bug 1691109 [wpt PR 27513] - Increase timeout duration for wpt/fetch/api/basic/keepal...
[gecko.git] / dom / bindings / FakeString.h
blob8b34bbc0f6b8328fa20c22e8b476bea33fd0a484
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 #ifndef mozilla_dom_FakeString_h__
8 #define mozilla_dom_FakeString_h__
10 #include "nsString.h"
11 #include "nsStringBuffer.h"
12 #include "mozilla/RefPtr.h"
13 #include "mozilla/Span.h"
15 namespace mozilla {
16 namespace dom {
17 namespace binding_detail {
18 // A struct that has a layout compatible with nsAString, so that
19 // reinterpret-casting a FakeString as a const nsAString is safe, but much
20 // faster constructor and destructor behavior. FakeString uses inline storage
21 // for small strings and an nsStringBuffer for longer strings. It can also
22 // point to a literal (static-lifetime) string that's compiled into the binary,
23 // or point at the buffer of an nsAString whose lifetime is longer than that of
24 // the FakeString.
25 template <typename CharT>
26 struct FakeString {
27 using char_type = CharT;
28 using string_type = nsTString<CharT>;
29 using size_type = typename string_type::size_type;
30 using DataFlags = typename string_type::DataFlags;
31 using ClassFlags = typename string_type::ClassFlags;
32 using AString = nsTSubstring<CharT>;
34 static const size_t kInlineCapacity = 64;
35 using AutoString = nsTAutoStringN<CharT, kInlineCapacity>;
37 FakeString()
38 : mDataFlags(DataFlags::TERMINATED),
39 mClassFlags(ClassFlags::INLINE),
40 mInlineCapacity(kInlineCapacity - 1) {}
42 ~FakeString() {
43 if (mDataFlags & DataFlags::REFCOUNTED) {
44 MOZ_ASSERT(mDataInitialized);
45 nsStringBuffer::FromData(mData)->Release();
49 // Share aString's string buffer, if it has one; otherwise, make this string
50 // depend upon aString's data. aString should outlive this instance of
51 // FakeString.
52 void ShareOrDependUpon(const AString& aString) {
53 RefPtr<nsStringBuffer> sharedBuffer = nsStringBuffer::FromString(aString);
54 if (!sharedBuffer) {
55 InitData(aString.BeginReading(), aString.Length());
56 if (!aString.IsTerminated()) {
57 mDataFlags &= ~DataFlags::TERMINATED;
59 } else {
60 AssignFromStringBuffer(sharedBuffer.forget(), aString.Length());
64 void Truncate() { InitData(string_type::char_traits::sEmptyBuffer, 0); }
66 void SetIsVoid(bool aValue) {
67 MOZ_ASSERT(aValue, "We don't support SetIsVoid(false) on FakeString!");
68 Truncate();
69 mDataFlags |= DataFlags::VOIDED;
72 char_type* BeginWriting() {
73 MOZ_ASSERT(IsMutable());
74 MOZ_ASSERT(mDataInitialized);
75 return mData;
78 size_type Length() const { return mLength; }
80 operator mozilla::Span<const char_type>() const {
81 MOZ_ASSERT(mDataInitialized);
82 // Explicitly specify template argument here to avoid instantiating
83 // Span<char_type> first and then implicitly converting to Span<const
84 // char_type>
85 return mozilla::Span<const char_type>{mData, Length()};
88 operator mozilla::Span<char_type>() {
89 return mozilla::Span{BeginWriting(), Length()};
92 mozilla::Result<mozilla::BulkWriteHandle<CharT>, nsresult> BulkWrite(
93 size_type aCapacity, size_type aPrefixToPreserve, bool aAllowShrinking) {
94 MOZ_ASSERT(!mDataInitialized);
95 InitData(mStorage, 0);
96 mDataFlags |= DataFlags::INLINE;
97 return ToAStringPtr()->BulkWrite(aCapacity, aPrefixToPreserve,
98 aAllowShrinking);
101 // Reserve space to write aLength chars, not including null-terminator.
102 bool SetLength(size_type aLength, mozilla::fallible_t const&) {
103 // Use mStorage for small strings.
104 if (aLength < kInlineCapacity) {
105 InitData(mStorage, aLength);
106 mDataFlags |= DataFlags::INLINE;
107 } else {
108 RefPtr<nsStringBuffer> buf =
109 nsStringBuffer::Alloc((aLength + 1) * sizeof(char_type));
110 if (MOZ_UNLIKELY(!buf)) {
111 return false;
114 AssignFromStringBuffer(buf.forget(), aLength);
117 MOZ_ASSERT(mDataInitialized);
118 mData[mLength] = char_type(0);
119 return true;
122 // Returns false on allocation failure.
123 bool EnsureMutable() {
124 MOZ_ASSERT(mDataInitialized);
126 if (IsMutable()) {
127 return true;
130 RefPtr<nsStringBuffer> buffer;
131 if (mDataFlags & DataFlags::REFCOUNTED) {
132 // Make sure we'll drop it when we're done.
133 buffer = dont_AddRef(nsStringBuffer::FromData(mData));
134 // And make sure we don't release it twice by accident.
136 const char_type* oldChars = mData;
138 mDataFlags = DataFlags::TERMINATED;
139 #ifdef DEBUG
140 // Reset mDataInitialized because we're explicitly reinitializing
141 // it via the SetLength call.
142 mDataInitialized = false;
143 #endif // DEBUG
144 // SetLength will make sure we have our own buffer to work with. Note that
145 // we may be transitioning from having a (short) readonly stringbuffer to
146 // our inline storage or whatnot. That's all fine; SetLength is responsible
147 // for setting up our flags correctly.
148 if (!SetLength(Length(), fallible)) {
149 return false;
151 MOZ_ASSERT(oldChars != mData, "Should have new chars now!");
152 MOZ_ASSERT(IsMutable(), "Why are we still not mutable?");
153 memcpy(mData, oldChars, Length() * sizeof(char_type));
154 return true;
157 void AssignFromStringBuffer(already_AddRefed<nsStringBuffer> aBuffer,
158 size_t aLength) {
159 InitData(static_cast<char_type*>(aBuffer.take()->Data()), aLength);
160 mDataFlags |= DataFlags::REFCOUNTED;
163 // The preferred way to assign literals to a FakeString. This should only be
164 // called with actual C++ literal strings (i.e. u"stuff") or character arrays
165 // that originally come from passing such literal strings.
166 template <int N>
167 void AssignLiteral(const char_type (&aData)[N]) {
168 AssignLiteral(aData, N - 1);
171 // Assign a literal to a FakeString when it's not an actual literal
172 // in the code, but is known to be a literal somehow (e.g. it came
173 // from an nsAString that tested true for IsLiteral()).
174 void AssignLiteral(const char_type* aData, size_t aLength) {
175 InitData(aData, aLength);
176 mDataFlags |= DataFlags::LITERAL;
179 // If this ever changes, change the corresponding code in the
180 // Optional<nsA[C]String> specialization as well.
181 const AString* ToAStringPtr() const {
182 return reinterpret_cast<const string_type*>(this);
185 operator const AString&() const { return *ToAStringPtr(); }
187 private:
188 AString* ToAStringPtr() { return reinterpret_cast<string_type*>(this); }
190 // mData is left uninitialized for optimization purposes.
191 MOZ_INIT_OUTSIDE_CTOR char_type* mData;
192 // mLength is left uninitialized for optimization purposes.
193 MOZ_INIT_OUTSIDE_CTOR size_type mLength;
194 DataFlags mDataFlags;
195 const ClassFlags mClassFlags;
197 const size_type mInlineCapacity;
198 char_type mStorage[kInlineCapacity];
199 #ifdef DEBUG
200 bool mDataInitialized = false;
201 #endif // DEBUG
203 FakeString(const FakeString& other) = delete;
204 void operator=(const FakeString& other) = delete;
206 void InitData(const char_type* aData, size_type aLength) {
207 MOZ_ASSERT(mDataFlags == DataFlags::TERMINATED);
208 MOZ_ASSERT(!mDataInitialized);
209 mData = const_cast<char_type*>(aData);
210 mLength = aLength;
211 #ifdef DEBUG
212 mDataInitialized = true;
213 #endif // DEBUG
216 bool IsMutable() {
217 return (mDataFlags & DataFlags::INLINE) ||
218 ((mDataFlags & DataFlags::REFCOUNTED) &&
219 !nsStringBuffer::FromData(mData)->IsReadonly());
222 friend class NonNull<AString>;
224 // A class to use for our static asserts to ensure our object layout
225 // matches that of nsString.
226 class StringAsserter;
227 friend class StringAsserter;
229 class StringAsserter : public AutoString {
230 public:
231 static void StaticAsserts() {
232 static_assert(sizeof(AutoString) == sizeof(FakeString),
233 "Should be binary compatible with nsTAutoString");
234 static_assert(
235 offsetof(FakeString, mInlineCapacity) == sizeof(string_type),
236 "FakeString should include all nsString members");
237 static_assert(
238 offsetof(FakeString, mData) == offsetof(StringAsserter, mData),
239 "Offset of mData should match");
240 static_assert(
241 offsetof(FakeString, mLength) == offsetof(StringAsserter, mLength),
242 "Offset of mLength should match");
243 static_assert(offsetof(FakeString, mDataFlags) ==
244 offsetof(StringAsserter, mDataFlags),
245 "Offset of mDataFlags should match");
246 static_assert(offsetof(FakeString, mClassFlags) ==
247 offsetof(StringAsserter, mClassFlags),
248 "Offset of mClassFlags should match");
249 static_assert(offsetof(FakeString, mInlineCapacity) ==
250 offsetof(StringAsserter, mInlineCapacity),
251 "Offset of mInlineCapacity should match");
252 static_assert(
253 offsetof(FakeString, mStorage) == offsetof(StringAsserter, mStorage),
254 "Offset of mStorage should match");
258 } // namespace binding_detail
259 } // namespace dom
260 } // namespace mozilla
262 template <typename CharT>
263 inline void AssignFromStringBuffer(
264 nsStringBuffer* aBuffer, size_t aLength,
265 mozilla::dom::binding_detail::FakeString<CharT>& aDest) {
266 aDest.AssignFromStringBuffer(do_AddRef(aBuffer), aLength);
269 #endif /* mozilla_dom_FakeString_h__ */