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