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"
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
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
>;
34 static const size_t kInlineCapacity
= 64;
35 using AutoString
= nsTAutoStringN
<CharT
, kInlineCapacity
>;
38 : mDataFlags(DataFlags::TERMINATED
),
39 mClassFlags(ClassFlags::INLINE
),
40 mInlineCapacity(kInlineCapacity
- 1) {}
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
52 void ShareOrDependUpon(const AString
& aString
) {
53 RefPtr
<nsStringBuffer
> sharedBuffer
= nsStringBuffer::FromString(aString
);
55 InitData(aString
.BeginReading(), aString
.Length());
56 if (!aString
.IsTerminated()) {
57 mDataFlags
&= ~DataFlags::TERMINATED
;
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!");
69 mDataFlags
|= DataFlags::VOIDED
;
72 char_type
* BeginWriting() {
73 MOZ_ASSERT(IsMutable());
74 MOZ_ASSERT(mDataInitialized
);
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
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
,
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
;
108 RefPtr
<nsStringBuffer
> buf
=
109 nsStringBuffer::Alloc((aLength
+ 1) * sizeof(char_type
));
110 if (MOZ_UNLIKELY(!buf
)) {
114 AssignFromStringBuffer(buf
.forget(), aLength
);
117 MOZ_ASSERT(mDataInitialized
);
118 mData
[mLength
] = char_type(0);
122 // Returns false on allocation failure.
123 bool EnsureMutable() {
124 MOZ_ASSERT(mDataInitialized
);
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
;
140 // Reset mDataInitialized because we're explicitly reinitializing
141 // it via the SetLength call.
142 mDataInitialized
= false;
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
)) {
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
));
157 void AssignFromStringBuffer(already_AddRefed
<nsStringBuffer
> aBuffer
,
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.
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(); }
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
];
200 bool mDataInitialized
= false;
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
);
212 mDataInitialized
= true;
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
{
231 static void StaticAsserts() {
232 static_assert(sizeof(AutoString
) == sizeof(FakeString
),
233 "Should be binary compatible with nsTAutoString");
235 offsetof(FakeString
, mInlineCapacity
) == sizeof(string_type
),
236 "FakeString should include all nsString members");
238 offsetof(FakeString
, mData
) == offsetof(StringAsserter
, mData
),
239 "Offset of mData should match");
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");
253 offsetof(FakeString
, mStorage
) == offsetof(StringAsserter
, mStorage
),
254 "Offset of mStorage should match");
258 } // namespace binding_detail
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__ */