Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / base / nsTextFragment.h
blob53308156837b3937b9d48d272c6904c2669655b4
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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * A class which represents a fragment of text (eg inside a text
9 * node); if only codepoints below 256 are used, the text is stored as
10 * a char*; otherwise the text is stored as a char16_t*
13 #ifndef nsTextFragment_h___
14 #define nsTextFragment_h___
16 #include "mozilla/Attributes.h"
17 #include "mozilla/MemoryReporting.h"
19 #include "nsCharTraits.h"
20 #include "nsString.h"
21 #include "nsStringBuffer.h"
22 #include "nsReadableUtils.h"
23 #include "nsISupportsImpl.h"
25 // XXX should this normalize the code to keep a \u0000 at the end?
27 // XXX nsTextFragmentPool?
29 /**
30 * A fragment of text. If mIs2b is 1 then the m2b pointer is valid
31 * otherwise the m1b pointer is valid. If m1b is used then each byte
32 * of data represents a single ucs2 character with the high byte being
33 * zero.
35 * This class does not have a virtual destructor therefore it is not
36 * meant to be subclassed.
38 class nsTextFragment final {
39 public:
40 static nsresult Init();
41 static void Shutdown();
43 /**
44 * Default constructor. Initialize the fragment to be empty.
46 nsTextFragment() : m1b(nullptr), mAllBits(0) {
47 MOZ_COUNT_CTOR(nsTextFragment);
48 NS_ASSERTION(sizeof(FragmentBits) == 4, "Bad field packing!");
51 ~nsTextFragment();
53 /**
54 * Change the contents of this fragment to be a copy of the
55 * the argument fragment, or to "" if unable to allocate enough memory.
57 nsTextFragment& operator=(const nsTextFragment& aOther);
59 /**
60 * Return true if this fragment is represented by char16_t data
62 bool Is2b() const { return mState.mIs2b; }
64 /**
65 * Return true if this fragment contains Bidi text
66 * For performance reasons this flag is only set if explicitely requested (by
67 * setting the aUpdateBidi argument on SetTo or Append to true).
69 bool IsBidi() const { return mState.mIsBidi; }
71 /**
72 * Get a pointer to constant char16_t data.
74 const char16_t* Get2b() const {
75 MOZ_ASSERT(Is2b(), "not 2b text");
76 return static_cast<char16_t*>(m2b->Data());
79 /**
80 * Get a pointer to constant char data.
82 const char* Get1b() const {
83 NS_ASSERTION(!Is2b(), "not 1b text");
84 return (const char*)m1b;
87 /**
88 * Get the length of the fragment. The length is the number of logical
89 * characters, not the number of bytes to store the characters.
91 uint32_t GetLength() const { return mState.mLength; }
93 #define NS_MAX_TEXT_FRAGMENT_LENGTH (static_cast<uint32_t>(0x1FFFFFFF))
95 bool CanGrowBy(size_t n) const {
96 return n < (1 << 29) && mState.mLength + n < (1 << 29);
99 /**
100 * Change the contents of this fragment to be a copy of the given
101 * buffer. If aUpdateBidi is true, contents of the fragment will be scanned,
102 * and mState.mIsBidi will be turned on if it includes any Bidi characters.
103 * If aForce2b is true, aBuffer will be stored as char16_t as is. Then,
104 * you can access the value faster but may waste memory if all characters
105 * are less than U+0100.
107 bool SetTo(const char16_t* aBuffer, uint32_t aLength, bool aUpdateBidi,
108 bool aForce2b);
110 bool SetTo(const nsString& aString, bool aUpdateBidi, bool aForce2b) {
111 if (MOZ_UNLIKELY(aString.Length() > NS_MAX_TEXT_FRAGMENT_LENGTH)) {
112 return false;
114 ReleaseText();
115 if (aForce2b && !aUpdateBidi) {
116 nsStringBuffer* buffer = nsStringBuffer::FromString(aString);
117 if (buffer) {
118 NS_ADDREF(m2b = buffer);
119 mState.mInHeap = true;
120 mState.mIs2b = true;
121 mState.mLength = aString.Length();
122 return true;
126 return SetTo(aString.get(), aString.Length(), aUpdateBidi, aForce2b);
130 * Append aData to the end of this fragment. If aUpdateBidi is true, contents
131 * of the fragment will be scanned, and mState.mIsBidi will be turned on if
132 * it includes any Bidi characters.
133 * If aForce2b is true, the string will be stored as char16_t as is. Then,
134 * you can access the value faster but may waste memory if all characters
135 * are less than U+0100.
137 bool Append(const char16_t* aBuffer, uint32_t aLength, bool aUpdateBidi,
138 bool aForce2b);
141 * Append the contents of this string fragment to aString
143 void AppendTo(nsAString& aString) const {
144 if (!AppendTo(aString, mozilla::fallible)) {
145 aString.AllocFailed(aString.Length() + GetLength());
150 * Append the contents of this string fragment to aString
151 * @return false if an out of memory condition is detected, true otherwise
153 [[nodiscard]] bool AppendTo(nsAString& aString,
154 const mozilla::fallible_t& aFallible) const {
155 if (mState.mIs2b) {
156 if (aString.IsEmpty()) {
157 m2b->ToString(mState.mLength, aString);
158 return true;
160 bool ok = aString.Append(Get2b(), mState.mLength, aFallible);
161 if (!ok) {
162 return false;
165 return true;
166 } else {
167 return AppendASCIItoUTF16(Substring(m1b, mState.mLength), aString,
168 aFallible);
173 * Append a substring of the contents of this string fragment to aString.
174 * @param aOffset where to start the substring in this text fragment
175 * @param aLength the length of the substring
177 void AppendTo(nsAString& aString, uint32_t aOffset, uint32_t aLength) const {
178 if (!AppendTo(aString, aOffset, aLength, mozilla::fallible)) {
179 aString.AllocFailed(aString.Length() + aLength);
184 * Append a substring of the contents of this string fragment to aString.
185 * @param aString the string in which to append
186 * @param aOffset where to start the substring in this text fragment
187 * @param aLength the length of the substring
188 * @return false if an out of memory condition is detected, true otherwise
190 [[nodiscard]] bool AppendTo(nsAString& aString, uint32_t aOffset,
191 uint32_t aLength,
192 const mozilla::fallible_t& aFallible) const {
193 if (mState.mIs2b) {
194 bool ok = aString.Append(Get2b() + aOffset, aLength, aFallible);
195 if (!ok) {
196 return false;
199 return true;
200 } else {
201 return AppendASCIItoUTF16(Substring(m1b + aOffset, aLength), aString,
202 aFallible);
207 * Make a copy of the fragments contents starting at offset for
208 * count characters. The offset and count will be adjusted to
209 * lie within the fragments data. The fragments data is converted if
210 * necessary.
212 void CopyTo(char16_t* aDest, uint32_t aOffset, uint32_t aCount);
215 * Return the character in the text-fragment at the given
216 * index. This always returns a char16_t.
218 char16_t CharAt(uint32_t aIndex) const {
219 MOZ_ASSERT(aIndex < mState.mLength, "bad index");
220 return mState.mIs2b ? Get2b()[aIndex]
221 : static_cast<unsigned char>(m1b[aIndex]);
225 * IsHighSurrogateFollowedByLowSurrogateAt() returns true if character at
226 * aIndex is high surrogate and it's followed by low surrogate.
228 inline bool IsHighSurrogateFollowedByLowSurrogateAt(uint32_t aIndex) const {
229 MOZ_ASSERT(aIndex < mState.mLength);
230 if (!mState.mIs2b || aIndex + 1 >= mState.mLength) {
231 return false;
233 return NS_IS_SURROGATE_PAIR(Get2b()[aIndex], Get2b()[aIndex + 1]);
237 * IsLowSurrogateFollowingHighSurrogateAt() returns true if character at
238 * aIndex is low surrogate and it follows high surrogate.
240 inline bool IsLowSurrogateFollowingHighSurrogateAt(uint32_t aIndex) const {
241 MOZ_ASSERT(aIndex < mState.mLength);
242 if (!mState.mIs2b || !aIndex) {
243 return false;
245 return NS_IS_SURROGATE_PAIR(Get2b()[aIndex - 1], Get2b()[aIndex]);
249 * ScalarValueAt() returns a Unicode scalar value at aIndex. If the character
250 * at aIndex is a high surrogate followed by low surrogate, returns character
251 * code for the pair. If the index is low surrogate, or a high surrogate but
252 * not in a pair, returns 0.
254 inline char32_t ScalarValueAt(uint32_t aIndex) const {
255 MOZ_ASSERT(aIndex < mState.mLength);
256 if (!mState.mIs2b) {
257 return static_cast<unsigned char>(m1b[aIndex]);
259 char16_t ch = Get2b()[aIndex];
260 if (!IS_SURROGATE(ch)) {
261 return ch;
263 if (aIndex + 1 < mState.mLength && NS_IS_HIGH_SURROGATE(ch)) {
264 char16_t nextCh = Get2b()[aIndex + 1];
265 if (NS_IS_LOW_SURROGATE(nextCh)) {
266 return SURROGATE_TO_UCS4(ch, nextCh);
269 return 0;
272 void SetBidi(bool aBidi) { mState.mIsBidi = aBidi; }
274 struct FragmentBits {
275 // uint32_t to ensure that the values are unsigned, because we
276 // want 0/1, not 0/-1!
277 // Making these bool causes Windows to not actually pack them,
278 // which causes crashes because we assume this structure is no more than
279 // 32 bits!
280 uint32_t mInHeap : 1;
281 uint32_t mIs2b : 1;
282 uint32_t mIsBidi : 1;
283 // Note that when you change the bits of mLength, you also need to change
284 // NS_MAX_TEXT_FRAGMENT_LENGTH.
285 uint32_t mLength : 29;
288 size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
291 * Check whether the text in this fragment is the same as the text in the
292 * other fragment.
294 [[nodiscard]] bool TextEquals(const nsTextFragment& aOther) const;
296 private:
297 void ReleaseText();
300 * Scan the contents of the fragment and turn on mState.mIsBidi if it
301 * includes any Bidi characters.
303 void UpdateBidiFlag(const char16_t* aBuffer, uint32_t aLength);
305 union {
306 nsStringBuffer* m2b;
307 const char* m1b; // This is const since it can point to shared data
310 union {
311 uint32_t mAllBits;
312 FragmentBits mState;
316 #endif /* nsTextFragment_h___ */