|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 /* |
|
6 * A class which represents a fragment of text (eg inside a text |
|
7 * node); if only codepoints below 256 are used, the text is stored as |
|
8 * a char*; otherwise the text is stored as a char16_t* |
|
9 */ |
|
10 |
|
11 #ifndef nsTextFragment_h___ |
|
12 #define nsTextFragment_h___ |
|
13 |
|
14 #include "mozilla/Attributes.h" |
|
15 #include "mozilla/MemoryReporting.h" |
|
16 |
|
17 #include "nsString.h" |
|
18 #include "nsReadableUtils.h" |
|
19 #include "nsISupportsImpl.h" |
|
20 |
|
21 class nsString; |
|
22 class nsCString; |
|
23 |
|
24 // XXX should this normalize the code to keep a \u0000 at the end? |
|
25 |
|
26 // XXX nsTextFragmentPool? |
|
27 |
|
28 /** |
|
29 * A fragment of text. If mIs2b is 1 then the m2b pointer is valid |
|
30 * otherwise the m1b pointer is valid. If m1b is used then each byte |
|
31 * of data represents a single ucs2 character with the high byte being |
|
32 * zero. |
|
33 * |
|
34 * This class does not have a virtual destructor therefore it is not |
|
35 * meant to be subclassed. |
|
36 */ |
|
37 class nsTextFragment MOZ_FINAL { |
|
38 public: |
|
39 static nsresult Init(); |
|
40 static void Shutdown(); |
|
41 |
|
42 /** |
|
43 * Default constructor. Initialize the fragment to be empty. |
|
44 */ |
|
45 nsTextFragment() |
|
46 : m1b(nullptr), mAllBits(0) |
|
47 { |
|
48 MOZ_COUNT_CTOR(nsTextFragment); |
|
49 NS_ASSERTION(sizeof(FragmentBits) == 4, "Bad field packing!"); |
|
50 } |
|
51 |
|
52 ~nsTextFragment(); |
|
53 |
|
54 /** |
|
55 * Change the contents of this fragment to be a copy of the |
|
56 * the argument fragment, or to "" if unable to allocate enough memory. |
|
57 */ |
|
58 nsTextFragment& operator=(const nsTextFragment& aOther); |
|
59 |
|
60 /** |
|
61 * Return true if this fragment is represented by char16_t data |
|
62 */ |
|
63 bool Is2b() const |
|
64 { |
|
65 return mState.mIs2b; |
|
66 } |
|
67 |
|
68 /** |
|
69 * Return true if this fragment contains Bidi text |
|
70 * For performance reasons this flag is only set if explicitely requested (by |
|
71 * setting the aUpdateBidi argument on SetTo or Append to true). |
|
72 */ |
|
73 bool IsBidi() const |
|
74 { |
|
75 return mState.mIsBidi; |
|
76 } |
|
77 |
|
78 /** |
|
79 * Get a pointer to constant char16_t data. |
|
80 */ |
|
81 const char16_t *Get2b() const |
|
82 { |
|
83 NS_ASSERTION(Is2b(), "not 2b text"); |
|
84 return m2b; |
|
85 } |
|
86 |
|
87 /** |
|
88 * Get a pointer to constant char data. |
|
89 */ |
|
90 const char *Get1b() const |
|
91 { |
|
92 NS_ASSERTION(!Is2b(), "not 1b text"); |
|
93 return (const char *)m1b; |
|
94 } |
|
95 |
|
96 /** |
|
97 * Get the length of the fragment. The length is the number of logical |
|
98 * characters, not the number of bytes to store the characters. |
|
99 */ |
|
100 uint32_t GetLength() const |
|
101 { |
|
102 return mState.mLength; |
|
103 } |
|
104 |
|
105 bool CanGrowBy(size_t n) const |
|
106 { |
|
107 return n < (1 << 29) && mState.mLength + n < (1 << 29); |
|
108 } |
|
109 |
|
110 /** |
|
111 * Change the contents of this fragment to be a copy of the given |
|
112 * buffer. If aUpdateBidi is true, contents of the fragment will be scanned, |
|
113 * and mState.mIsBidi will be turned on if it includes any Bidi characters. |
|
114 */ |
|
115 bool SetTo(const char16_t* aBuffer, int32_t aLength, bool aUpdateBidi); |
|
116 |
|
117 /** |
|
118 * Append aData to the end of this fragment. If aUpdateBidi is true, contents |
|
119 * of the fragment will be scanned, and mState.mIsBidi will be turned on if |
|
120 * it includes any Bidi characters. |
|
121 */ |
|
122 bool Append(const char16_t* aBuffer, uint32_t aLength, bool aUpdateBidi); |
|
123 |
|
124 /** |
|
125 * Append the contents of this string fragment to aString |
|
126 */ |
|
127 void AppendTo(nsAString& aString) const { |
|
128 if (!AppendTo(aString, mozilla::fallible_t())) { |
|
129 NS_ABORT_OOM(GetLength()); |
|
130 } |
|
131 } |
|
132 |
|
133 /** |
|
134 * Append the contents of this string fragment to aString |
|
135 * @return false if an out of memory condition is detected, true otherwise |
|
136 */ |
|
137 bool AppendTo(nsAString& aString, |
|
138 const mozilla::fallible_t&) const NS_WARN_UNUSED_RESULT { |
|
139 if (mState.mIs2b) { |
|
140 bool ok = aString.Append(m2b, mState.mLength, mozilla::fallible_t()); |
|
141 if (!ok) { |
|
142 return false; |
|
143 } |
|
144 |
|
145 return true; |
|
146 } else { |
|
147 return AppendASCIItoUTF16(Substring(m1b, mState.mLength), aString, |
|
148 mozilla::fallible_t()); |
|
149 } |
|
150 } |
|
151 |
|
152 /** |
|
153 * Append a substring of the contents of this string fragment to aString. |
|
154 * @param aOffset where to start the substring in this text fragment |
|
155 * @param aLength the length of the substring |
|
156 */ |
|
157 void AppendTo(nsAString& aString, int32_t aOffset, int32_t aLength) const { |
|
158 if (!AppendTo(aString, aOffset, aLength, mozilla::fallible_t())) { |
|
159 NS_ABORT_OOM(aLength); |
|
160 } |
|
161 } |
|
162 |
|
163 /** |
|
164 * Append a substring of the contents of this string fragment to aString. |
|
165 * @param aString the string in which to append |
|
166 * @param aOffset where to start the substring in this text fragment |
|
167 * @param aLength the length of the substring |
|
168 * @return false if an out of memory condition is detected, true otherwise |
|
169 */ |
|
170 bool AppendTo(nsAString& aString, int32_t aOffset, int32_t aLength, |
|
171 const mozilla::fallible_t&) const NS_WARN_UNUSED_RESULT |
|
172 { |
|
173 if (mState.mIs2b) { |
|
174 bool ok = aString.Append(m2b + aOffset, aLength, mozilla::fallible_t()); |
|
175 if (!ok) { |
|
176 return false; |
|
177 } |
|
178 |
|
179 return true; |
|
180 } else { |
|
181 return AppendASCIItoUTF16(Substring(m1b + aOffset, aLength), aString, |
|
182 mozilla::fallible_t()); |
|
183 } |
|
184 } |
|
185 |
|
186 /** |
|
187 * Make a copy of the fragments contents starting at offset for |
|
188 * count characters. The offset and count will be adjusted to |
|
189 * lie within the fragments data. The fragments data is converted if |
|
190 * necessary. |
|
191 */ |
|
192 void CopyTo(char16_t *aDest, int32_t aOffset, int32_t aCount); |
|
193 |
|
194 /** |
|
195 * Return the character in the text-fragment at the given |
|
196 * index. This always returns a char16_t. |
|
197 */ |
|
198 char16_t CharAt(int32_t aIndex) const |
|
199 { |
|
200 NS_ASSERTION(uint32_t(aIndex) < mState.mLength, "bad index"); |
|
201 return mState.mIs2b ? m2b[aIndex] : static_cast<unsigned char>(m1b[aIndex]); |
|
202 } |
|
203 |
|
204 struct FragmentBits { |
|
205 // uint32_t to ensure that the values are unsigned, because we |
|
206 // want 0/1, not 0/-1! |
|
207 // Making these bool causes Windows to not actually pack them, |
|
208 // which causes crashes because we assume this structure is no more than |
|
209 // 32 bits! |
|
210 uint32_t mInHeap : 1; |
|
211 uint32_t mIs2b : 1; |
|
212 uint32_t mIsBidi : 1; |
|
213 uint32_t mLength : 29; |
|
214 }; |
|
215 |
|
216 size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; |
|
217 |
|
218 private: |
|
219 void ReleaseText(); |
|
220 |
|
221 /** |
|
222 * Scan the contents of the fragment and turn on mState.mIsBidi if it |
|
223 * includes any Bidi characters. |
|
224 */ |
|
225 void UpdateBidiFlag(const char16_t* aBuffer, uint32_t aLength); |
|
226 |
|
227 union { |
|
228 char16_t *m2b; |
|
229 const char *m1b; // This is const since it can point to shared data |
|
230 }; |
|
231 |
|
232 union { |
|
233 uint32_t mAllBits; |
|
234 FragmentBits mState; |
|
235 }; |
|
236 }; |
|
237 |
|
238 #endif /* nsTextFragment_h___ */ |
|
239 |