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__
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
25 template <typename CharT
>
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
>;
39 : mDataFlags(DataFlags::TERMINATED
),
40 mClassFlags(ClassFlags::INLINE
),
41 mInlineCapacity(kInlineCapacity
- 1) {}
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
53 void ShareOrDependUpon(const AString
& aString
) {
54 RefPtr
<nsStringBuffer
> sharedBuffer
= nsStringBuffer::FromString(aString
);
56 InitData(aString
.BeginReading(), aString
.Length());
57 if (!aString
.IsTerminated()) {
58 mDataFlags
&= ~DataFlags::TERMINATED
;
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!");
70 mDataFlags
|= DataFlags::VOIDED
;
73 char_type
* BeginWriting() {
74 MOZ_ASSERT(IsMutable());
75 MOZ_ASSERT(mDataInitialized
);
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
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
,
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
;
105 RefPtr
<nsStringBuffer
> buf
=
106 nsStringBuffer::Alloc((aLength
+ 1) * sizeof(char_type
));
107 if (MOZ_UNLIKELY(!buf
)) {
111 AssignFromStringBuffer(buf
.forget(), aLength
);
114 MOZ_ASSERT(mDataInitialized
);
115 mData
[mLength
] = char_type(0);
119 // Returns false on allocation failure.
120 bool EnsureMutable() {
121 MOZ_ASSERT(mDataInitialized
);
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
;
137 // Reset mDataInitialized because we're explicitly reinitializing
138 // it via the SetLength call.
139 mDataInitialized
= false;
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
)) {
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
));
154 void AssignFromStringBuffer(already_AddRefed
<nsStringBuffer
> aBuffer
,
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.
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(); }
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
];
197 bool mDataInitialized
= false;
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
);
210 mDataInitialized
= true;
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
{
229 static void StaticAsserts() {
230 static_assert(sizeof(AutoString
) == sizeof(FakeString
),
231 "Should be binary compatible with nsTAutoString");
233 offsetof(FakeString
, mInlineCapacity
) == sizeof(string_type
),
234 "FakeString should include all nsString members");
236 offsetof(FakeString
, mData
) == offsetof(StringAsserter
, mData
),
237 "Offset of mData should match");
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");
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__ */