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/. */
6 
7 #ifndef mozilla_dom_FakeString_h__
8 #define mozilla_dom_FakeString_h__
9 
10 #include "nsString.h"
11 #include "nsStringBuffer.h"
12 #include "mozilla/RefPtr.h"
13 
14 namespace mozilla {
15 namespace dom {
16 namespace binding_detail {
17 // A struct that has the same layout as an nsString but much faster
18 // constructor and destructor behavior. FakeString uses inline storage
19 // for small strings and a nsStringBuffer for longer strings.
20 struct FakeString {
FakeStringFakeString21   FakeString() :
22     mFlags(nsString::F_TERMINATED)
23   {
24   }
25 
~FakeStringFakeString26   ~FakeString() {
27     if (mFlags & nsString::F_SHARED) {
28       nsStringBuffer::FromData(mData)->Release();
29     }
30   }
31 
RebindFakeString32   void Rebind(const nsString::char_type* aData, nsString::size_type aLength) {
33     MOZ_ASSERT(mFlags == nsString::F_TERMINATED);
34     mData = const_cast<nsString::char_type*>(aData);
35     mLength = aLength;
36   }
37 
38   // Share aString's string buffer, if it has one; otherwise, make this string
39   // depend upon aString's data.  aString should outlive this instance of
40   // FakeString.
ShareOrDependUponFakeString41   void ShareOrDependUpon(const nsAString& aString) {
42     RefPtr<nsStringBuffer> sharedBuffer = nsStringBuffer::FromString(aString);
43     if (!sharedBuffer) {
44       Rebind(aString.Data(), aString.Length());
45     } else {
46       AssignFromStringBuffer(sharedBuffer.forget());
47       mLength = aString.Length();
48     }
49   }
50 
TruncateFakeString51   void Truncate() {
52     MOZ_ASSERT(mFlags == nsString::F_TERMINATED);
53     mData = nsString::char_traits::sEmptyBuffer;
54     mLength = 0;
55   }
56 
SetIsVoidFakeString57   void SetIsVoid(bool aValue) {
58     MOZ_ASSERT(aValue,
59                "We don't support SetIsVoid(false) on FakeString!");
60     Truncate();
61     mFlags |= nsString::F_VOIDED;
62   }
63 
DataFakeString64   const nsString::char_type* Data() const
65   {
66     return mData;
67   }
68 
BeginWritingFakeString69   nsString::char_type* BeginWriting()
70   {
71     return mData;
72   }
73 
LengthFakeString74   nsString::size_type Length() const
75   {
76     return mLength;
77   }
78 
79   // Reserve space to write aLength chars, not including null-terminator.
SetLengthFakeString80   bool SetLength(nsString::size_type aLength, mozilla::fallible_t const&) {
81     // Use mInlineStorage for small strings.
82     if (aLength < sInlineCapacity) {
83       SetData(mInlineStorage);
84     } else {
85       RefPtr<nsStringBuffer> buf = nsStringBuffer::Alloc((aLength + 1) * sizeof(nsString::char_type));
86       if (MOZ_UNLIKELY(!buf)) {
87         return false;
88       }
89 
90       AssignFromStringBuffer(buf.forget());
91     }
92     mLength = aLength;
93     mData[mLength] = char16_t(0);
94     return true;
95   }
96 
97   // If this ever changes, change the corresponding code in the
98   // Optional<nsAString> specialization as well.
ToAStringPtrFakeString99   const nsAString* ToAStringPtr() const {
100     return reinterpret_cast<const nsString*>(this);
101   }
102 
103 operator const nsAString& () const {
104     return *reinterpret_cast<const nsString*>(this);
105   }
106 
107 private:
ToAStringPtrFakeString108   nsAString* ToAStringPtr() {
109     return reinterpret_cast<nsString*>(this);
110   }
111 
112   nsString::char_type* mData;
113   nsString::size_type mLength;
114   uint32_t mFlags;
115 
116   static const size_t sInlineCapacity = 64;
117   nsString::char_type mInlineStorage[sInlineCapacity];
118 
119   FakeString(const FakeString& other) = delete;
120   void operator=(const FakeString& other) = delete;
121 
SetDataFakeString122   void SetData(nsString::char_type* aData) {
123     MOZ_ASSERT(mFlags == nsString::F_TERMINATED);
124     mData = const_cast<nsString::char_type*>(aData);
125   }
AssignFromStringBufferFakeString126   void AssignFromStringBuffer(already_AddRefed<nsStringBuffer> aBuffer) {
127     SetData(static_cast<nsString::char_type*>(aBuffer.take()->Data()));
128     mFlags = nsString::F_SHARED | nsString::F_TERMINATED;
129   }
130 
131   friend class NonNull<nsAString>;
132 
133   // A class to use for our static asserts to ensure our object layout
134   // matches that of nsString.
135   class StringAsserter;
136   friend class StringAsserter;
137 
138   class StringAsserter : public nsString {
139   public:
StaticAssertsFakeString140     static void StaticAsserts() {
141       static_assert(offsetof(FakeString, mInlineStorage) ==
142                       sizeof(nsString),
143                     "FakeString should include all nsString members");
144       static_assert(offsetof(FakeString, mData) ==
145                       offsetof(StringAsserter, mData),
146                     "Offset of mData should match");
147       static_assert(offsetof(FakeString, mLength) ==
148                       offsetof(StringAsserter, mLength),
149                     "Offset of mLength should match");
150       static_assert(offsetof(FakeString, mFlags) ==
151                       offsetof(StringAsserter, mFlags),
152                     "Offset of mFlags should match");
153     }
154   };
155 };
156 } // namespace binding_detail
157 } // namespace dom
158 } // namespace mozilla
159 
160 #endif /* mozilla_dom_FakeString_h__ */