no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / dom / bindings / FakeString.h
blobf51f7890d9cdcc23280ad8e88e4e01db96e95542
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"
14 #include "js/String.h"
15 #include "nsTStringRepr.h"
17 namespace mozilla::dom::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>;
33 using LengthStorage = mozilla::detail::nsTStringLengthStorage<CharT>;
35 static const size_t kInlineCapacity = 64;
36 using AutoString = nsTAutoStringN<CharT, kInlineCapacity>;
38 FakeString()
39 : mDataFlags(DataFlags::TERMINATED),
40 mClassFlags(ClassFlags::INLINE),
41 mInlineCapacity(kInlineCapacity - 1) {}
43 ~FakeString() {
44 if (mDataFlags & DataFlags::REFCOUNTED) {
45 MOZ_ASSERT(mDataInitialized);
46 nsStringBuffer::FromData(mData)->Release();
50 // Share aString's string buffer, if it has one; otherwise, make this string
51 // depend upon aString's data. aString should outlive this instance of
52 // FakeString.
53 void ShareOrDependUpon(const AString& aString) {
54 RefPtr<nsStringBuffer> sharedBuffer = nsStringBuffer::FromString(aString);
55 if (!sharedBuffer) {
56 InitData(aString.BeginReading(), aString.Length());
57 if (!aString.IsTerminated()) {
58 mDataFlags &= ~DataFlags::TERMINATED;
60 } else {
61 AssignFromStringBuffer(sharedBuffer.forget(), aString.Length());
65 void Truncate() { InitData(string_type::char_traits::sEmptyBuffer, 0); }
67 void SetIsVoid(bool aValue) {
68 MOZ_ASSERT(aValue, "We don't support SetIsVoid(false) on FakeString!");
69 Truncate();
70 mDataFlags |= DataFlags::VOIDED;
73 char_type* BeginWriting() {
74 MOZ_ASSERT(IsMutable());
75 MOZ_ASSERT(mDataInitialized);
76 return mData;
79 size_type Length() const { return mLength; }
81 operator mozilla::Span<const char_type>() const {
82 MOZ_ASSERT(mDataInitialized);
83 // Explicitly specify template argument here to avoid instantiating
84 // Span<char_type> first and then implicitly converting to Span<const
85 // char_type>
86 return mozilla::Span<const char_type>{mData, Length()};
89 mozilla::Result<mozilla::BulkWriteHandle<CharT>, nsresult> BulkWrite(
90 size_type aCapacity, size_type aPrefixToPreserve, bool aAllowShrinking) {
91 MOZ_ASSERT(!mDataInitialized);
92 InitData(mStorage, 0);
93 mDataFlags |= DataFlags::INLINE;
94 return ToAStringPtr()->BulkWrite(aCapacity, aPrefixToPreserve,
95 aAllowShrinking);
98 // Reserve space to write aLength chars, not including null-terminator.
99 bool SetLength(size_type aLength, mozilla::fallible_t const&) {
100 // Use mStorage for small strings.
101 if (aLength < kInlineCapacity) {
102 InitData(mStorage, aLength);
103 mDataFlags |= DataFlags::INLINE;
104 } else {
105 RefPtr<nsStringBuffer> buf =
106 nsStringBuffer::Alloc((aLength + 1) * sizeof(char_type));
107 if (MOZ_UNLIKELY(!buf)) {
108 return false;
111 AssignFromStringBuffer(buf.forget(), aLength);
114 MOZ_ASSERT(mDataInitialized);
115 mData[mLength] = char_type(0);
116 return true;
119 // Returns false on allocation failure.
120 bool EnsureMutable() {
121 MOZ_ASSERT(mDataInitialized);
123 if (IsMutable()) {
124 return true;
127 RefPtr<nsStringBuffer> buffer;
128 if (mDataFlags & DataFlags::REFCOUNTED) {
129 // Make sure we'll drop it when we're done.
130 buffer = dont_AddRef(nsStringBuffer::FromData(mData));
131 // And make sure we don't release it twice by accident.
133 const char_type* oldChars = mData;
135 mDataFlags = DataFlags::TERMINATED;
136 #ifdef DEBUG
137 // Reset mDataInitialized because we're explicitly reinitializing
138 // it via the SetLength call.
139 mDataInitialized = false;
140 #endif // DEBUG
141 // SetLength will make sure we have our own buffer to work with. Note that
142 // we may be transitioning from having a (short) readonly stringbuffer to
143 // our inline storage or whatnot. That's all fine; SetLength is responsible
144 // for setting up our flags correctly.
145 if (!SetLength(Length(), fallible)) {
146 return false;
148 MOZ_ASSERT(oldChars != mData, "Should have new chars now!");
149 MOZ_ASSERT(IsMutable(), "Why are we still not mutable?");
150 memcpy(mData, oldChars, Length() * sizeof(char_type));
151 return true;
154 void AssignFromStringBuffer(already_AddRefed<nsStringBuffer> aBuffer,
155 size_t aLength) {
156 InitData(static_cast<char_type*>(aBuffer.take()->Data()), aLength);
157 mDataFlags |= DataFlags::REFCOUNTED;
160 // The preferred way to assign literals to a FakeString. This should only be
161 // called with actual C++ literal strings (i.e. u"stuff") or character arrays
162 // that originally come from passing such literal strings.
163 template <int N>
164 void AssignLiteral(const char_type (&aData)[N]) {
165 AssignLiteral(aData, N - 1);
168 // Assign a literal to a FakeString when it's not an actual literal
169 // in the code, but is known to be a literal somehow (e.g. it came
170 // from an nsAString that tested true for IsLiteral()).
171 void AssignLiteral(const char_type* aData, size_t aLength) {
172 InitData(aData, aLength);
173 mDataFlags |= DataFlags::LITERAL;
176 // If this ever changes, change the corresponding code in the
177 // Optional<nsA[C]String> specialization as well.
178 const AString* ToAStringPtr() const {
179 return reinterpret_cast<const string_type*>(this);
182 operator const AString&() const { return *ToAStringPtr(); }
184 private:
185 AString* ToAStringPtr() { return reinterpret_cast<string_type*>(this); }
187 // mData is left uninitialized for optimization purposes.
188 MOZ_INIT_OUTSIDE_CTOR char_type* mData;
189 // mLength is left uninitialized for optimization purposes.
190 MOZ_INIT_OUTSIDE_CTOR uint32_t mLength;
191 DataFlags mDataFlags;
192 const ClassFlags mClassFlags;
194 const uint32_t mInlineCapacity;
195 char_type mStorage[kInlineCapacity];
196 #ifdef DEBUG
197 bool mDataInitialized = false;
198 #endif // DEBUG
200 FakeString(const FakeString& other) = delete;
201 void operator=(const FakeString& other) = delete;
203 void InitData(const char_type* aData, size_type aLength) {
204 MOZ_ASSERT(aLength <= LengthStorage::kMax, "string is too large");
205 MOZ_ASSERT(mDataFlags == DataFlags::TERMINATED);
206 MOZ_ASSERT(!mDataInitialized);
207 mData = const_cast<char_type*>(aData);
208 mLength = uint32_t(aLength);
209 #ifdef DEBUG
210 mDataInitialized = true;
211 #endif // DEBUG
214 bool IsMutable() {
215 return (mDataFlags & DataFlags::INLINE) ||
216 ((mDataFlags & DataFlags::REFCOUNTED) &&
217 !nsStringBuffer::FromData(mData)->IsReadonly());
220 friend class NonNull<AString>;
222 // A class to use for our static asserts to ensure our object layout
223 // matches that of nsString.
224 class StringAsserter;
225 friend class StringAsserter;
227 class StringAsserter : public AutoString {
228 public:
229 static void StaticAsserts() {
230 static_assert(sizeof(AutoString) == sizeof(FakeString),
231 "Should be binary compatible with nsTAutoString");
232 static_assert(
233 offsetof(FakeString, mInlineCapacity) == sizeof(string_type),
234 "FakeString should include all nsString members");
235 static_assert(
236 offsetof(FakeString, mData) == offsetof(StringAsserter, mData),
237 "Offset of mData should match");
238 static_assert(
239 offsetof(FakeString, mLength) == offsetof(StringAsserter, mLength),
240 "Offset of mLength should match");
241 static_assert(offsetof(FakeString, mDataFlags) ==
242 offsetof(StringAsserter, mDataFlags),
243 "Offset of mDataFlags should match");
244 static_assert(offsetof(FakeString, mClassFlags) ==
245 offsetof(StringAsserter, mClassFlags),
246 "Offset of mClassFlags should match");
247 static_assert(offsetof(FakeString, mInlineCapacity) ==
248 offsetof(StringAsserter, mInlineCapacity),
249 "Offset of mInlineCapacity should match");
250 static_assert(
251 offsetof(FakeString, mStorage) == offsetof(StringAsserter, mStorage),
252 "Offset of mStorage should match");
253 static_assert(JS::MaxStringLength <= LengthStorage::kMax,
254 "JS::MaxStringLength fits in a nsTString");
258 } // namespace mozilla::dom::binding_detail
260 template <typename CharT>
261 inline void AssignFromStringBuffer(
262 nsStringBuffer* aBuffer, size_t aLength,
263 mozilla::dom::binding_detail::FakeString<CharT>& aDest) {
264 aDest.AssignFromStringBuffer(do_AddRef(aBuffer), aLength);
267 #endif /* mozilla_dom_FakeString_h__ */