Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/nsTextFragment.h
Line
Count
Source (jump to first uncovered line)
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 "nsStringBuffer.h"
21
#include "nsReadableUtils.h"
22
#include "nsISupportsImpl.h"
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 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
0
  {
48
0
    MOZ_COUNT_CTOR(nsTextFragment);
49
0
    NS_ASSERTION(sizeof(FragmentBits) == 4, "Bad field packing!");
50
0
  }
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
0
  {
65
0
    return mState.mIs2b;
66
0
  }
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
0
  {
75
0
    return mState.mIsBidi;
76
0
  }
77
78
  /**
79
   * Get a pointer to constant char16_t data.
80
   */
81
  const char16_t *Get2b() const
82
0
  {
83
0
    MOZ_ASSERT(Is2b(), "not 2b text");
84
0
    return static_cast<char16_t*>(m2b->Data());
85
0
  }
86
87
  /**
88
   * Get a pointer to constant char data.
89
   */
90
  const char *Get1b() const
91
0
  {
92
0
    NS_ASSERTION(!Is2b(), "not 1b text");
93
0
    return (const char *)m1b;
94
0
  }
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
0
  {
102
0
    return mState.mLength;
103
0
  }
104
105
  bool CanGrowBy(size_t n) const
106
0
  {
107
0
    return n < (1 << 29) && mState.mLength + n < (1 << 29);
108
0
  }
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
   * If aForce2b is true, aBuffer will be stored as char16_t as is.  Then,
115
   * you can access the value faster but may waste memory if all characters
116
   * are less than U+0100.
117
   */
118
  bool SetTo(const char16_t* aBuffer, int32_t aLength, bool aUpdateBidi,
119
             bool aForce2b);
120
121
  bool SetTo(const nsString& aString, bool aUpdateBidi, bool aForce2b)
122
0
  {
123
0
    ReleaseText();
124
0
    if (aForce2b && !aUpdateBidi) {
125
0
      nsStringBuffer* buffer = nsStringBuffer::FromString(aString);
126
0
      if (buffer) {
127
0
        NS_ADDREF(m2b = buffer);
128
0
        mState.mInHeap = true;
129
0
        mState.mIs2b = true;
130
0
        mState.mLength = aString.Length();
131
0
        return true;
132
0
      }
133
0
    }
134
0
135
0
    return SetTo(aString.get(), aString.Length(), aUpdateBidi, aForce2b);
136
0
  }
137
138
  /**
139
   * Append aData to the end of this fragment. If aUpdateBidi is true, contents
140
   * of the fragment will be scanned, and mState.mIsBidi will be turned on if
141
   * it includes any Bidi characters.
142
   * If aForce2b is true, the string will be stored as char16_t as is.  Then,
143
   * you can access the value faster but may waste memory if all characters
144
   * are less than U+0100.
145
   */
146
  bool Append(const char16_t* aBuffer, uint32_t aLength, bool aUpdateBidi,
147
              bool aForce2b);
148
149
  /**
150
   * Append the contents of this string fragment to aString
151
   */
152
0
  void AppendTo(nsAString& aString) const {
153
0
    if (!AppendTo(aString, mozilla::fallible)) {
154
0
      aString.AllocFailed(aString.Length() + GetLength());
155
0
    }
156
0
  }
157
158
  /**
159
   * Append the contents of this string fragment to aString
160
   * @return false if an out of memory condition is detected, true otherwise
161
   */
162
  MOZ_MUST_USE
163
  bool AppendTo(nsAString& aString,
164
0
                const mozilla::fallible_t& aFallible) const {
165
0
    if (mState.mIs2b) {
166
0
      if (aString.IsEmpty()) {
167
0
        m2b->ToString(mState.mLength, aString);
168
0
        return true;
169
0
      }
170
0
      bool ok = aString.Append(Get2b(), mState.mLength, aFallible);
171
0
      if (!ok) {
172
0
        return false;
173
0
      }
174
0
175
0
      return true;
176
0
    } else {
177
0
      return AppendASCIItoUTF16(Substring(m1b, mState.mLength), aString,
178
0
                                aFallible);
179
0
    }
180
0
  }
181
182
  /**
183
   * Append a substring of the contents of this string fragment to aString.
184
   * @param aOffset where to start the substring in this text fragment
185
   * @param aLength the length of the substring
186
   */
187
0
  void AppendTo(nsAString& aString, int32_t aOffset, int32_t aLength) const {
188
0
    if (!AppendTo(aString, aOffset, aLength, mozilla::fallible)) {
189
0
      aString.AllocFailed(aString.Length() + aLength);
190
0
    }
191
0
  }
192
193
  /**
194
   * Append a substring of the contents of this string fragment to aString.
195
   * @param aString the string in which to append
196
   * @param aOffset where to start the substring in this text fragment
197
   * @param aLength the length of the substring
198
   * @return false if an out of memory condition is detected, true otherwise
199
   */
200
  MOZ_MUST_USE
201
  bool AppendTo(nsAString& aString, int32_t aOffset, int32_t aLength,
202
                const mozilla::fallible_t& aFallible) const
203
0
  {
204
0
    if (mState.mIs2b) {
205
0
      bool ok = aString.Append(Get2b() + aOffset, aLength, aFallible);
206
0
      if (!ok) {
207
0
        return false;
208
0
      }
209
0
210
0
      return true;
211
0
    } else {
212
0
      return AppendASCIItoUTF16(Substring(m1b + aOffset, aLength), aString,
213
0
                                aFallible);
214
0
    }
215
0
  }
216
217
  /**
218
   * Make a copy of the fragments contents starting at offset for
219
   * count characters. The offset and count will be adjusted to
220
   * lie within the fragments data. The fragments data is converted if
221
   * necessary.
222
   */
223
  void CopyTo(char16_t *aDest, int32_t aOffset, int32_t aCount);
224
225
  /**
226
   * Return the character in the text-fragment at the given
227
   * index. This always returns a char16_t.
228
   */
229
  char16_t CharAt(int32_t aIndex) const
230
0
  {
231
0
    MOZ_ASSERT(uint32_t(aIndex) < mState.mLength, "bad index");
232
0
    return mState.mIs2b ? Get2b()[aIndex] : static_cast<unsigned char>(m1b[aIndex]);
233
0
  }
234
235
  void SetBidi(bool aBidi)
236
0
  {
237
0
    mState.mIsBidi = aBidi;
238
0
  }
239
240
  struct FragmentBits {
241
    // uint32_t to ensure that the values are unsigned, because we
242
    // want 0/1, not 0/-1!
243
    // Making these bool causes Windows to not actually pack them,
244
    // which causes crashes because we assume this structure is no more than
245
    // 32 bits!
246
    uint32_t mInHeap : 1;
247
    uint32_t mIs2b : 1;
248
    uint32_t mIsBidi : 1;
249
    // Note that when you change the bits of mLength, you also need to change
250
    // NS_MAX_TEXT_FRAGMENT_LENGTH.
251
    uint32_t mLength : 29;
252
  };
253
254
0
#define NS_MAX_TEXT_FRAGMENT_LENGTH (static_cast<uint32_t>(0x1FFFFFFF))
255
256
  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
257
258
private:
259
  void ReleaseText();
260
261
  /**
262
   * Scan the contents of the fragment and turn on mState.mIsBidi if it
263
   * includes any Bidi characters.
264
   */
265
  void UpdateBidiFlag(const char16_t* aBuffer, uint32_t aLength);
266
267
  union {
268
    nsStringBuffer* m2b;
269
    const char* m1b; // This is const since it can point to shared data
270
  };
271
272
  union {
273
    uint32_t mAllBits;
274
    FragmentBits mState;
275
  };
276
};
277
278
#endif /* nsTextFragment_h___ */
279