Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/string/nsTSubstring.cpp
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
#include "double-conversion/double-conversion.h"
8
#include "mozilla/CheckedInt.h"
9
#include "mozilla/MathAlgorithms.h"
10
#include "mozilla/MemoryReporting.h"
11
#include "mozilla/Printf.h"
12
13
#include "nsASCIIMask.h"
14
15
// It's not worthwhile to reallocate the buffer and memcpy the
16
// contents over when the size difference isn't large. With
17
// power-of-two allocation buckets and 64 as the typical inline
18
// capacity, considering that above 1000 there performance aspects
19
// of realloc and memcpy seem to be absorbed, relative to the old
20
// code, by the performance benefits of the new code being exact,
21
// we need to choose which transitions of 256 to 128, 512 to 256
22
// and 1024 to 512 to allow. As a guess, let's pick the middle
23
// one as the the largest potential transition that we forgo. So
24
// we'll shrink from 1024 bucket to 512 bucket but not from 512
25
// bucket to 256 bucket. We'll decide by comparing the difference
26
// of capacities. As bucket differences, the differences are 256
27
// and 512. Since the capacities have various overheads, we
28
// can't compare with 256 or 512 exactly but it's easier to
29
// compare to some number that's between the two, so it's
30
// far away from either to ignore the overheads.
31
const uint32_t kNsStringBufferShrinkingThreshold = 384;
32
33
using double_conversion::DoubleToStringConverter;
34
35
template <typename T>
36
const typename nsTSubstring<T>::size_type nsTSubstring<T>::kMaxCapacity =
37
    (nsTSubstring<T>::size_type(-1) /
38
        2 - sizeof(nsStringBuffer)) /
39
    sizeof(nsTSubstring<T>::char_type) - 2;
40
41
#ifdef XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE
42
template <typename T>
43
nsTSubstring<T>::nsTSubstring(char_type* aData, size_type aLength,
44
                              DataFlags aDataFlags,
45
                              ClassFlags aClassFlags)
46
  : ::mozilla::detail::nsTStringRepr<T>(aData, aLength, aDataFlags, aClassFlags)
47
{
48
  AssertValid();
49
  MOZ_RELEASE_ASSERT(CheckCapacity(aLength), "String is too large.");
50
51
  if (aDataFlags & DataFlags::OWNED) {
52
    STRING_STAT_INCREMENT(Adopt);
53
    MOZ_LOG_CTOR(this->mData, "StringAdopt", 1);
54
  }
55
}
56
#endif /* XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE */
57
58
/**
59
 * helper function for down-casting a nsTSubstring to an nsTAutoString.
60
 */
61
template <typename T>
62
inline const nsTAutoString<T>*
63
AsAutoString(const nsTSubstring<T>* aStr)
64
119M
{
65
119M
  return static_cast<const nsTAutoString<T>*>(aStr);
66
119M
}
nsTAutoStringN<char, 64ul> const* AsAutoString<char>(nsTSubstring<char> const*)
Line
Count
Source
64
111M
{
65
111M
  return static_cast<const nsTAutoString<T>*>(aStr);
66
111M
}
nsTAutoStringN<char16_t, 64ul> const* AsAutoString<char16_t>(nsTSubstring<char16_t> const*)
Line
Count
Source
64
8.01M
{
65
8.01M
  return static_cast<const nsTAutoString<T>*>(aStr);
66
8.01M
}
67
68
template<typename T>
69
mozilla::BulkWriteHandle<T>
70
nsTSubstring<T>::BulkWrite(size_type aCapacity,
71
                           size_type aPrefixToPreserve,
72
                           bool aAllowShrinking,
73
                           nsresult& aRv)
74
0
{
75
0
  auto r = StartBulkWriteImpl(aCapacity,
76
0
                              aPrefixToPreserve,
77
0
                              aAllowShrinking);
78
0
  if (MOZ_UNLIKELY(r.isErr())) {
79
0
    aRv = r.unwrapErr();
80
0
    return mozilla::BulkWriteHandle<T>(nullptr, 0);
81
0
  }
82
0
  aRv = NS_OK;
83
0
  return mozilla::BulkWriteHandle<T>(this, r.unwrap());
84
0
}
Unexecuted instantiation: nsTSubstring<char>::BulkWrite(unsigned int, unsigned int, bool, nsresult&)
Unexecuted instantiation: nsTSubstring<char16_t>::BulkWrite(unsigned int, unsigned int, bool, nsresult&)
85
86
87
template<typename T>
88
mozilla::Result<uint32_t, nsresult>
89
nsTSubstring<T>::StartBulkWriteImpl(size_type aCapacity,
90
                                    size_type aPrefixToPreserve,
91
                                    bool aAllowShrinking,
92
                                    size_type aSuffixLength,
93
                                    size_type aOldSuffixStart,
94
                                    size_type aNewSuffixStart)
95
95.0M
{
96
95.0M
  // Note! Capacity does not include room for the terminating null char.
97
95.0M
98
95.0M
  MOZ_ASSERT(aPrefixToPreserve <= aCapacity,
99
95.0M
             "Requested preservation of an overlong prefix.");
100
95.0M
  MOZ_ASSERT(aNewSuffixStart + aSuffixLength <= aCapacity,
101
95.0M
             "Requesed move of suffix to out-of-bounds location.");
102
95.0M
  // Can't assert aOldSuffixStart, because mLength may not be valid anymore,
103
95.0M
  // since this method allows itself to be called more than once.
104
95.0M
105
95.0M
  // If zero capacity is requested, set the string to the special empty
106
95.0M
  // string.
107
95.0M
  if (MOZ_UNLIKELY(!aCapacity)) {
108
25.2k
    ::ReleaseData(this->mData, this->mDataFlags);
109
25.2k
    SetToEmptyBuffer();
110
25.2k
    return 0;
111
25.2k
  }
112
95.0M
113
95.0M
  // Note! Capacity() returns 0 when the string is immutable.
114
95.0M
  const size_type curCapacity = Capacity();
115
95.0M
116
95.0M
  bool shrinking = false;
117
95.0M
118
95.0M
  // We've established that aCapacity > 0.
119
95.0M
  // |curCapacity == 0| means that the buffer is immutable or 0-sized, so we
120
95.0M
  // need to allocate a new buffer. We cannot use the existing buffer even
121
95.0M
  // though it might be large enough.
122
95.0M
123
95.0M
  if (aCapacity <= curCapacity) {
124
65.8M
    if (aAllowShrinking) {
125
23.4M
      shrinking = true;
126
42.4M
    } else {
127
42.4M
      char_traits::move(this->mData + aNewSuffixStart,
128
42.4M
                        this->mData + aOldSuffixStart,
129
42.4M
                        aSuffixLength);
130
42.4M
      if (aSuffixLength) {
131
3.18k
        char_traits::uninitialize(
132
3.18k
          this->mData + aPrefixToPreserve,
133
3.18k
          XPCOM_MIN(size_t(aNewSuffixStart - aPrefixToPreserve),
134
3.18k
                    kNsStringBufferMaxPoison));
135
3.18k
        char_traits::uninitialize(
136
3.18k
          this->mData + aNewSuffixStart + aSuffixLength,
137
3.18k
          XPCOM_MIN(size_t(curCapacity + 1 - aNewSuffixStart - aSuffixLength),
138
3.18k
                    kNsStringBufferMaxPoison));
139
42.4M
      } else {
140
42.4M
        char_traits::uninitialize(
141
42.4M
          this->mData + aPrefixToPreserve,
142
42.4M
          XPCOM_MIN(size_t(curCapacity + 1 - aPrefixToPreserve),
143
42.4M
                    kNsStringBufferMaxPoison));
144
42.4M
      }
145
42.4M
      return curCapacity;
146
42.4M
    }
147
52.6M
  }
148
52.6M
149
52.6M
  char_type* oldData = this->mData;
150
52.6M
  DataFlags oldFlags = this->mDataFlags;
151
52.6M
152
52.6M
  char_type* newData;
153
52.6M
  DataFlags newDataFlags;
154
52.6M
  size_type newCapacity;
155
52.6M
156
52.6M
  // If this is an nsTAutoStringN, it's possible that we can use the inline
157
52.6M
  // buffer.
158
52.6M
  if ((this->mClassFlags & ClassFlags::INLINE) &&
159
52.6M
      (aCapacity <= AsAutoString(this)->mInlineCapacity)) {
160
29.2M
    newCapacity = AsAutoString(this)->mInlineCapacity;
161
29.2M
    newData = (char_type*)AsAutoString(this)->mStorage;
162
29.2M
    newDataFlags = DataFlags::TERMINATED | DataFlags::INLINE;
163
29.2M
  } else {
164
23.3M
    // If |aCapacity > kMaxCapacity|, then our doubling algorithm may not be
165
23.3M
    // able to allocate it.  Just bail out in cases like that.  We don't want
166
23.3M
    // to be allocating 2GB+ strings anyway.
167
23.3M
    static_assert((sizeof(nsStringBuffer) & 0x1) == 0,
168
23.3M
                  "bad size for nsStringBuffer");
169
23.3M
    if (MOZ_UNLIKELY(!CheckCapacity(aCapacity))) {
170
0
      return mozilla::Err(NS_ERROR_OUT_OF_MEMORY);
171
0
    }
172
23.3M
173
23.3M
    // We increase our capacity so that the allocated buffer grows
174
23.3M
    // exponentially, which gives us amortized O(1) appending. Below the
175
23.3M
    // threshold, we use powers-of-two. Above the threshold, we grow by at
176
23.3M
    // least 1.125, rounding up to the nearest MiB.
177
23.3M
    const size_type slowGrowthThreshold = 8 * 1024 * 1024;
178
23.3M
179
23.3M
    // nsStringBuffer allocates sizeof(nsStringBuffer) + passed size, and
180
23.3M
    // storageSize below wants extra 1 * sizeof(char_type).
181
23.3M
    const size_type neededExtraSpace =
182
23.3M
      sizeof(nsStringBuffer) / sizeof(char_type) + 1;
183
23.3M
184
23.3M
    size_type temp;
185
23.3M
    if (aCapacity >= slowGrowthThreshold) {
186
0
      size_type minNewCapacity = curCapacity + (curCapacity >> 3); // multiply by 1.125
187
0
      temp = XPCOM_MAX(aCapacity, minNewCapacity) + neededExtraSpace;
188
0
189
0
      // Round up to the next multiple of MiB, but ensure the expected
190
0
      // capacity doesn't include the extra space required by nsStringBuffer
191
0
      // and null-termination.
192
0
      const size_t MiB = 1 << 20;
193
0
      temp = (MiB * ((temp + MiB - 1) / MiB)) - neededExtraSpace;
194
23.3M
    } else {
195
23.3M
      // Round up to the next power of two.
196
23.3M
      temp =
197
23.3M
        mozilla::RoundUpPow2(aCapacity + neededExtraSpace) - neededExtraSpace;
198
23.3M
    }
199
23.3M
200
23.3M
    newCapacity = XPCOM_MIN(temp, kMaxCapacity);
201
23.3M
    MOZ_ASSERT(newCapacity >= aCapacity,
202
23.3M
               "should have hit the early return at the top");
203
23.3M
    // Avoid shrinking if the new buffer size is close to the old. Note that
204
23.3M
    // unsigned underflow is defined behavior.
205
23.3M
    if ((curCapacity - newCapacity) <= kNsStringBufferShrinkingThreshold &&
206
23.3M
        (this->mDataFlags & DataFlags::REFCOUNTED)) {
207
1.11M
      MOZ_ASSERT(aAllowShrinking, "How come we didn't return earlier?");
208
1.11M
      // We're already close enough to the right size.
209
1.11M
      newData = oldData;
210
1.11M
      newCapacity = curCapacity;
211
22.2M
    } else {
212
22.2M
      size_type storageSize = (newCapacity + 1) * sizeof(char_type);
213
22.2M
      // Since we allocate only by powers of 2 we always fit into a full mozjemalloc
214
22.2M
      // bucket, it's not useful to use realloc, which may spend time uselessly
215
22.2M
      // copying too much.
216
22.2M
      nsStringBuffer* newHdr = nsStringBuffer::Alloc(storageSize).take();
217
22.2M
      if (newHdr) {
218
22.2M
        newData = (char_type*)newHdr->Data();
219
22.2M
      } else if (shrinking) {
220
0
        // We're still in a consistent state.
221
0
        //
222
0
        // Since shrinking is just a memory footprint optimization, we
223
0
        // don't propagate OOM if we tried to shrink in order to avoid
224
0
        // OOM crashes from infallible callers. If we're lucky, soon enough
225
0
        // a fallible caller reaches OOM and is able to deal or we end up
226
0
        // disposing of this string before reaching OOM again.
227
0
        newData = oldData;
228
0
        newCapacity = curCapacity;
229
0
      } else {
230
0
        return mozilla::Err(NS_ERROR_OUT_OF_MEMORY);
231
0
      }
232
23.3M
    }
233
23.3M
    newDataFlags = DataFlags::TERMINATED | DataFlags::REFCOUNTED;
234
23.3M
  }
235
52.6M
236
52.6M
  this->mData = newData;
237
52.6M
  this->mDataFlags = newDataFlags;
238
52.6M
239
52.6M
  if (oldData == newData) {
240
23.4M
    char_traits::move(
241
23.4M
      newData + aNewSuffixStart, oldData + aOldSuffixStart, aSuffixLength);
242
23.4M
    if (aSuffixLength) {
243
0
      char_traits::uninitialize(
244
0
        this->mData + aPrefixToPreserve,
245
0
        XPCOM_MIN(size_t(aNewSuffixStart - aPrefixToPreserve),
246
0
                  kNsStringBufferMaxPoison));
247
0
      char_traits::uninitialize(
248
0
        this->mData + aNewSuffixStart + aSuffixLength,
249
0
        XPCOM_MIN(size_t(newCapacity + 1 - aNewSuffixStart - aSuffixLength),
250
0
                  kNsStringBufferMaxPoison));
251
23.4M
    } else {
252
23.4M
      char_traits::uninitialize(
253
23.4M
        this->mData + aPrefixToPreserve,
254
23.4M
        XPCOM_MIN(size_t(newCapacity + 1 - aPrefixToPreserve),
255
23.4M
                  kNsStringBufferMaxPoison));
256
23.4M
    }
257
29.1M
  } else {
258
29.1M
    char_traits::copy(newData, oldData, aPrefixToPreserve);
259
29.1M
    char_traits::copy(
260
29.1M
      newData + aNewSuffixStart, oldData + aOldSuffixStart, aSuffixLength);
261
29.1M
    ::ReleaseData(oldData, oldFlags);
262
29.1M
  }
263
52.6M
264
52.6M
  return newCapacity;
265
52.6M
}
nsTSubstring<char>::StartBulkWriteImpl(unsigned int, unsigned int, bool, unsigned int, unsigned int, unsigned int)
Line
Count
Source
95
46.1M
{
96
46.1M
  // Note! Capacity does not include room for the terminating null char.
97
46.1M
98
46.1M
  MOZ_ASSERT(aPrefixToPreserve <= aCapacity,
99
46.1M
             "Requested preservation of an overlong prefix.");
100
46.1M
  MOZ_ASSERT(aNewSuffixStart + aSuffixLength <= aCapacity,
101
46.1M
             "Requesed move of suffix to out-of-bounds location.");
102
46.1M
  // Can't assert aOldSuffixStart, because mLength may not be valid anymore,
103
46.1M
  // since this method allows itself to be called more than once.
104
46.1M
105
46.1M
  // If zero capacity is requested, set the string to the special empty
106
46.1M
  // string.
107
46.1M
  if (MOZ_UNLIKELY(!aCapacity)) {
108
25.2k
    ::ReleaseData(this->mData, this->mDataFlags);
109
25.2k
    SetToEmptyBuffer();
110
25.2k
    return 0;
111
25.2k
  }
112
46.1M
113
46.1M
  // Note! Capacity() returns 0 when the string is immutable.
114
46.1M
  const size_type curCapacity = Capacity();
115
46.1M
116
46.1M
  bool shrinking = false;
117
46.1M
118
46.1M
  // We've established that aCapacity > 0.
119
46.1M
  // |curCapacity == 0| means that the buffer is immutable or 0-sized, so we
120
46.1M
  // need to allocate a new buffer. We cannot use the existing buffer even
121
46.1M
  // though it might be large enough.
122
46.1M
123
46.1M
  if (aCapacity <= curCapacity) {
124
29.4M
    if (aAllowShrinking) {
125
21.7M
      shrinking = true;
126
21.7M
    } else {
127
7.69M
      char_traits::move(this->mData + aNewSuffixStart,
128
7.69M
                        this->mData + aOldSuffixStart,
129
7.69M
                        aSuffixLength);
130
7.69M
      if (aSuffixLength) {
131
3.16k
        char_traits::uninitialize(
132
3.16k
          this->mData + aPrefixToPreserve,
133
3.16k
          XPCOM_MIN(size_t(aNewSuffixStart - aPrefixToPreserve),
134
3.16k
                    kNsStringBufferMaxPoison));
135
3.16k
        char_traits::uninitialize(
136
3.16k
          this->mData + aNewSuffixStart + aSuffixLength,
137
3.16k
          XPCOM_MIN(size_t(curCapacity + 1 - aNewSuffixStart - aSuffixLength),
138
3.16k
                    kNsStringBufferMaxPoison));
139
7.68M
      } else {
140
7.68M
        char_traits::uninitialize(
141
7.68M
          this->mData + aPrefixToPreserve,
142
7.68M
          XPCOM_MIN(size_t(curCapacity + 1 - aPrefixToPreserve),
143
7.68M
                    kNsStringBufferMaxPoison));
144
7.68M
      }
145
7.69M
      return curCapacity;
146
7.69M
    }
147
38.4M
  }
148
38.4M
149
38.4M
  char_type* oldData = this->mData;
150
38.4M
  DataFlags oldFlags = this->mDataFlags;
151
38.4M
152
38.4M
  char_type* newData;
153
38.4M
  DataFlags newDataFlags;
154
38.4M
  size_type newCapacity;
155
38.4M
156
38.4M
  // If this is an nsTAutoStringN, it's possible that we can use the inline
157
38.4M
  // buffer.
158
38.4M
  if ((this->mClassFlags & ClassFlags::INLINE) &&
159
38.4M
      (aCapacity <= AsAutoString(this)->mInlineCapacity)) {
160
27.5M
    newCapacity = AsAutoString(this)->mInlineCapacity;
161
27.5M
    newData = (char_type*)AsAutoString(this)->mStorage;
162
27.5M
    newDataFlags = DataFlags::TERMINATED | DataFlags::INLINE;
163
27.5M
  } else {
164
10.9M
    // If |aCapacity > kMaxCapacity|, then our doubling algorithm may not be
165
10.9M
    // able to allocate it.  Just bail out in cases like that.  We don't want
166
10.9M
    // to be allocating 2GB+ strings anyway.
167
10.9M
    static_assert((sizeof(nsStringBuffer) & 0x1) == 0,
168
10.9M
                  "bad size for nsStringBuffer");
169
10.9M
    if (MOZ_UNLIKELY(!CheckCapacity(aCapacity))) {
170
0
      return mozilla::Err(NS_ERROR_OUT_OF_MEMORY);
171
0
    }
172
10.9M
173
10.9M
    // We increase our capacity so that the allocated buffer grows
174
10.9M
    // exponentially, which gives us amortized O(1) appending. Below the
175
10.9M
    // threshold, we use powers-of-two. Above the threshold, we grow by at
176
10.9M
    // least 1.125, rounding up to the nearest MiB.
177
10.9M
    const size_type slowGrowthThreshold = 8 * 1024 * 1024;
178
10.9M
179
10.9M
    // nsStringBuffer allocates sizeof(nsStringBuffer) + passed size, and
180
10.9M
    // storageSize below wants extra 1 * sizeof(char_type).
181
10.9M
    const size_type neededExtraSpace =
182
10.9M
      sizeof(nsStringBuffer) / sizeof(char_type) + 1;
183
10.9M
184
10.9M
    size_type temp;
185
10.9M
    if (aCapacity >= slowGrowthThreshold) {
186
0
      size_type minNewCapacity = curCapacity + (curCapacity >> 3); // multiply by 1.125
187
0
      temp = XPCOM_MAX(aCapacity, minNewCapacity) + neededExtraSpace;
188
0
189
0
      // Round up to the next multiple of MiB, but ensure the expected
190
0
      // capacity doesn't include the extra space required by nsStringBuffer
191
0
      // and null-termination.
192
0
      const size_t MiB = 1 << 20;
193
0
      temp = (MiB * ((temp + MiB - 1) / MiB)) - neededExtraSpace;
194
10.9M
    } else {
195
10.9M
      // Round up to the next power of two.
196
10.9M
      temp =
197
10.9M
        mozilla::RoundUpPow2(aCapacity + neededExtraSpace) - neededExtraSpace;
198
10.9M
    }
199
10.9M
200
10.9M
    newCapacity = XPCOM_MIN(temp, kMaxCapacity);
201
10.9M
    MOZ_ASSERT(newCapacity >= aCapacity,
202
10.9M
               "should have hit the early return at the top");
203
10.9M
    // Avoid shrinking if the new buffer size is close to the old. Note that
204
10.9M
    // unsigned underflow is defined behavior.
205
10.9M
    if ((curCapacity - newCapacity) <= kNsStringBufferShrinkingThreshold &&
206
10.9M
        (this->mDataFlags & DataFlags::REFCOUNTED)) {
207
1.11M
      MOZ_ASSERT(aAllowShrinking, "How come we didn't return earlier?");
208
1.11M
      // We're already close enough to the right size.
209
1.11M
      newData = oldData;
210
1.11M
      newCapacity = curCapacity;
211
9.82M
    } else {
212
9.82M
      size_type storageSize = (newCapacity + 1) * sizeof(char_type);
213
9.82M
      // Since we allocate only by powers of 2 we always fit into a full mozjemalloc
214
9.82M
      // bucket, it's not useful to use realloc, which may spend time uselessly
215
9.82M
      // copying too much.
216
9.82M
      nsStringBuffer* newHdr = nsStringBuffer::Alloc(storageSize).take();
217
9.82M
      if (newHdr) {
218
9.82M
        newData = (char_type*)newHdr->Data();
219
9.82M
      } else if (shrinking) {
220
0
        // We're still in a consistent state.
221
0
        //
222
0
        // Since shrinking is just a memory footprint optimization, we
223
0
        // don't propagate OOM if we tried to shrink in order to avoid
224
0
        // OOM crashes from infallible callers. If we're lucky, soon enough
225
0
        // a fallible caller reaches OOM and is able to deal or we end up
226
0
        // disposing of this string before reaching OOM again.
227
0
        newData = oldData;
228
0
        newCapacity = curCapacity;
229
0
      } else {
230
0
        return mozilla::Err(NS_ERROR_OUT_OF_MEMORY);
231
0
      }
232
10.9M
    }
233
10.9M
    newDataFlags = DataFlags::TERMINATED | DataFlags::REFCOUNTED;
234
10.9M
  }
235
38.4M
236
38.4M
  this->mData = newData;
237
38.4M
  this->mDataFlags = newDataFlags;
238
38.4M
239
38.4M
  if (oldData == newData) {
240
21.7M
    char_traits::move(
241
21.7M
      newData + aNewSuffixStart, oldData + aOldSuffixStart, aSuffixLength);
242
21.7M
    if (aSuffixLength) {
243
0
      char_traits::uninitialize(
244
0
        this->mData + aPrefixToPreserve,
245
0
        XPCOM_MIN(size_t(aNewSuffixStart - aPrefixToPreserve),
246
0
                  kNsStringBufferMaxPoison));
247
0
      char_traits::uninitialize(
248
0
        this->mData + aNewSuffixStart + aSuffixLength,
249
0
        XPCOM_MIN(size_t(newCapacity + 1 - aNewSuffixStart - aSuffixLength),
250
0
                  kNsStringBufferMaxPoison));
251
21.7M
    } else {
252
21.7M
      char_traits::uninitialize(
253
21.7M
        this->mData + aPrefixToPreserve,
254
21.7M
        XPCOM_MIN(size_t(newCapacity + 1 - aPrefixToPreserve),
255
21.7M
                  kNsStringBufferMaxPoison));
256
21.7M
    }
257
21.7M
  } else {
258
16.7M
    char_traits::copy(newData, oldData, aPrefixToPreserve);
259
16.7M
    char_traits::copy(
260
16.7M
      newData + aNewSuffixStart, oldData + aOldSuffixStart, aSuffixLength);
261
16.7M
    ::ReleaseData(oldData, oldFlags);
262
16.7M
  }
263
38.4M
264
38.4M
  return newCapacity;
265
38.4M
}
nsTSubstring<char16_t>::StartBulkWriteImpl(unsigned int, unsigned int, bool, unsigned int, unsigned int, unsigned int)
Line
Count
Source
95
48.8M
{
96
48.8M
  // Note! Capacity does not include room for the terminating null char.
97
48.8M
98
48.8M
  MOZ_ASSERT(aPrefixToPreserve <= aCapacity,
99
48.8M
             "Requested preservation of an overlong prefix.");
100
48.8M
  MOZ_ASSERT(aNewSuffixStart + aSuffixLength <= aCapacity,
101
48.8M
             "Requesed move of suffix to out-of-bounds location.");
102
48.8M
  // Can't assert aOldSuffixStart, because mLength may not be valid anymore,
103
48.8M
  // since this method allows itself to be called more than once.
104
48.8M
105
48.8M
  // If zero capacity is requested, set the string to the special empty
106
48.8M
  // string.
107
48.8M
  if (MOZ_UNLIKELY(!aCapacity)) {
108
51
    ::ReleaseData(this->mData, this->mDataFlags);
109
51
    SetToEmptyBuffer();
110
51
    return 0;
111
51
  }
112
48.8M
113
48.8M
  // Note! Capacity() returns 0 when the string is immutable.
114
48.8M
  const size_type curCapacity = Capacity();
115
48.8M
116
48.8M
  bool shrinking = false;
117
48.8M
118
48.8M
  // We've established that aCapacity > 0.
119
48.8M
  // |curCapacity == 0| means that the buffer is immutable or 0-sized, so we
120
48.8M
  // need to allocate a new buffer. We cannot use the existing buffer even
121
48.8M
  // though it might be large enough.
122
48.8M
123
48.8M
  if (aCapacity <= curCapacity) {
124
36.4M
    if (aAllowShrinking) {
125
1.70M
      shrinking = true;
126
34.7M
    } else {
127
34.7M
      char_traits::move(this->mData + aNewSuffixStart,
128
34.7M
                        this->mData + aOldSuffixStart,
129
34.7M
                        aSuffixLength);
130
34.7M
      if (aSuffixLength) {
131
24
        char_traits::uninitialize(
132
24
          this->mData + aPrefixToPreserve,
133
24
          XPCOM_MIN(size_t(aNewSuffixStart - aPrefixToPreserve),
134
24
                    kNsStringBufferMaxPoison));
135
24
        char_traits::uninitialize(
136
24
          this->mData + aNewSuffixStart + aSuffixLength,
137
24
          XPCOM_MIN(size_t(curCapacity + 1 - aNewSuffixStart - aSuffixLength),
138
24
                    kNsStringBufferMaxPoison));
139
34.7M
      } else {
140
34.7M
        char_traits::uninitialize(
141
34.7M
          this->mData + aPrefixToPreserve,
142
34.7M
          XPCOM_MIN(size_t(curCapacity + 1 - aPrefixToPreserve),
143
34.7M
                    kNsStringBufferMaxPoison));
144
34.7M
      }
145
34.7M
      return curCapacity;
146
34.7M
    }
147
14.1M
  }
148
14.1M
149
14.1M
  char_type* oldData = this->mData;
150
14.1M
  DataFlags oldFlags = this->mDataFlags;
151
14.1M
152
14.1M
  char_type* newData;
153
14.1M
  DataFlags newDataFlags;
154
14.1M
  size_type newCapacity;
155
14.1M
156
14.1M
  // If this is an nsTAutoStringN, it's possible that we can use the inline
157
14.1M
  // buffer.
158
14.1M
  if ((this->mClassFlags & ClassFlags::INLINE) &&
159
14.1M
      (aCapacity <= AsAutoString(this)->mInlineCapacity)) {
160
1.71M
    newCapacity = AsAutoString(this)->mInlineCapacity;
161
1.71M
    newData = (char_type*)AsAutoString(this)->mStorage;
162
1.71M
    newDataFlags = DataFlags::TERMINATED | DataFlags::INLINE;
163
12.4M
  } else {
164
12.4M
    // If |aCapacity > kMaxCapacity|, then our doubling algorithm may not be
165
12.4M
    // able to allocate it.  Just bail out in cases like that.  We don't want
166
12.4M
    // to be allocating 2GB+ strings anyway.
167
12.4M
    static_assert((sizeof(nsStringBuffer) & 0x1) == 0,
168
12.4M
                  "bad size for nsStringBuffer");
169
12.4M
    if (MOZ_UNLIKELY(!CheckCapacity(aCapacity))) {
170
0
      return mozilla::Err(NS_ERROR_OUT_OF_MEMORY);
171
0
    }
172
12.4M
173
12.4M
    // We increase our capacity so that the allocated buffer grows
174
12.4M
    // exponentially, which gives us amortized O(1) appending. Below the
175
12.4M
    // threshold, we use powers-of-two. Above the threshold, we grow by at
176
12.4M
    // least 1.125, rounding up to the nearest MiB.
177
12.4M
    const size_type slowGrowthThreshold = 8 * 1024 * 1024;
178
12.4M
179
12.4M
    // nsStringBuffer allocates sizeof(nsStringBuffer) + passed size, and
180
12.4M
    // storageSize below wants extra 1 * sizeof(char_type).
181
12.4M
    const size_type neededExtraSpace =
182
12.4M
      sizeof(nsStringBuffer) / sizeof(char_type) + 1;
183
12.4M
184
12.4M
    size_type temp;
185
12.4M
    if (aCapacity >= slowGrowthThreshold) {
186
0
      size_type minNewCapacity = curCapacity + (curCapacity >> 3); // multiply by 1.125
187
0
      temp = XPCOM_MAX(aCapacity, minNewCapacity) + neededExtraSpace;
188
0
189
0
      // Round up to the next multiple of MiB, but ensure the expected
190
0
      // capacity doesn't include the extra space required by nsStringBuffer
191
0
      // and null-termination.
192
0
      const size_t MiB = 1 << 20;
193
0
      temp = (MiB * ((temp + MiB - 1) / MiB)) - neededExtraSpace;
194
12.4M
    } else {
195
12.4M
      // Round up to the next power of two.
196
12.4M
      temp =
197
12.4M
        mozilla::RoundUpPow2(aCapacity + neededExtraSpace) - neededExtraSpace;
198
12.4M
    }
199
12.4M
200
12.4M
    newCapacity = XPCOM_MIN(temp, kMaxCapacity);
201
12.4M
    MOZ_ASSERT(newCapacity >= aCapacity,
202
12.4M
               "should have hit the early return at the top");
203
12.4M
    // Avoid shrinking if the new buffer size is close to the old. Note that
204
12.4M
    // unsigned underflow is defined behavior.
205
12.4M
    if ((curCapacity - newCapacity) <= kNsStringBufferShrinkingThreshold &&
206
12.4M
        (this->mDataFlags & DataFlags::REFCOUNTED)) {
207
2.30k
      MOZ_ASSERT(aAllowShrinking, "How come we didn't return earlier?");
208
2.30k
      // We're already close enough to the right size.
209
2.30k
      newData = oldData;
210
2.30k
      newCapacity = curCapacity;
211
12.4M
    } else {
212
12.4M
      size_type storageSize = (newCapacity + 1) * sizeof(char_type);
213
12.4M
      // Since we allocate only by powers of 2 we always fit into a full mozjemalloc
214
12.4M
      // bucket, it's not useful to use realloc, which may spend time uselessly
215
12.4M
      // copying too much.
216
12.4M
      nsStringBuffer* newHdr = nsStringBuffer::Alloc(storageSize).take();
217
12.4M
      if (newHdr) {
218
12.4M
        newData = (char_type*)newHdr->Data();
219
12.4M
      } else if (shrinking) {
220
0
        // We're still in a consistent state.
221
0
        //
222
0
        // Since shrinking is just a memory footprint optimization, we
223
0
        // don't propagate OOM if we tried to shrink in order to avoid
224
0
        // OOM crashes from infallible callers. If we're lucky, soon enough
225
0
        // a fallible caller reaches OOM and is able to deal or we end up
226
0
        // disposing of this string before reaching OOM again.
227
0
        newData = oldData;
228
0
        newCapacity = curCapacity;
229
0
      } else {
230
0
        return mozilla::Err(NS_ERROR_OUT_OF_MEMORY);
231
0
      }
232
12.4M
    }
233
12.4M
    newDataFlags = DataFlags::TERMINATED | DataFlags::REFCOUNTED;
234
12.4M
  }
235
14.1M
236
14.1M
  this->mData = newData;
237
14.1M
  this->mDataFlags = newDataFlags;
238
14.1M
239
14.1M
  if (oldData == newData) {
240
1.70M
    char_traits::move(
241
1.70M
      newData + aNewSuffixStart, oldData + aOldSuffixStart, aSuffixLength);
242
1.70M
    if (aSuffixLength) {
243
0
      char_traits::uninitialize(
244
0
        this->mData + aPrefixToPreserve,
245
0
        XPCOM_MIN(size_t(aNewSuffixStart - aPrefixToPreserve),
246
0
                  kNsStringBufferMaxPoison));
247
0
      char_traits::uninitialize(
248
0
        this->mData + aNewSuffixStart + aSuffixLength,
249
0
        XPCOM_MIN(size_t(newCapacity + 1 - aNewSuffixStart - aSuffixLength),
250
0
                  kNsStringBufferMaxPoison));
251
1.70M
    } else {
252
1.70M
      char_traits::uninitialize(
253
1.70M
        this->mData + aPrefixToPreserve,
254
1.70M
        XPCOM_MIN(size_t(newCapacity + 1 - aPrefixToPreserve),
255
1.70M
                  kNsStringBufferMaxPoison));
256
1.70M
    }
257
12.4M
  } else {
258
12.4M
    char_traits::copy(newData, oldData, aPrefixToPreserve);
259
12.4M
    char_traits::copy(
260
12.4M
      newData + aNewSuffixStart, oldData + aOldSuffixStart, aSuffixLength);
261
12.4M
    ::ReleaseData(oldData, oldFlags);
262
12.4M
  }
263
14.1M
264
14.1M
  return newCapacity;
265
14.1M
}
266
267
template<typename T>
268
void
269
nsTSubstring<T>::FinishBulkWriteImpl(size_type aLength)
270
91.8M
{
271
91.8M
  MOZ_ASSERT(aLength != UINT32_MAX, "OOM magic value passed as length.");
272
91.8M
  if (aLength) {
273
91.8M
    FinishBulkWriteImplImpl(aLength);
274
91.8M
  } else {
275
22.1k
    ::ReleaseData(this->mData, this->mDataFlags);
276
22.1k
    SetToEmptyBuffer();
277
22.1k
  }
278
91.8M
  AssertValid();
279
91.8M
}
nsTSubstring<char>::FinishBulkWriteImpl(unsigned int)
Line
Count
Source
270
43.0M
{
271
43.0M
  MOZ_ASSERT(aLength != UINT32_MAX, "OOM magic value passed as length.");
272
43.0M
  if (aLength) {
273
43.0M
    FinishBulkWriteImplImpl(aLength);
274
43.0M
  } else {
275
22.1k
    ::ReleaseData(this->mData, this->mDataFlags);
276
22.1k
    SetToEmptyBuffer();
277
22.1k
  }
278
43.0M
  AssertValid();
279
43.0M
}
nsTSubstring<char16_t>::FinishBulkWriteImpl(unsigned int)
Line
Count
Source
270
48.7M
{
271
48.7M
  MOZ_ASSERT(aLength != UINT32_MAX, "OOM magic value passed as length.");
272
48.7M
  if (aLength) {
273
48.7M
    FinishBulkWriteImplImpl(aLength);
274
48.7M
  } else {
275
50
    ::ReleaseData(this->mData, this->mDataFlags);
276
50
    SetToEmptyBuffer();
277
50
  }
278
48.7M
  AssertValid();
279
48.7M
}
280
281
template <typename T>
282
void
283
nsTSubstring<T>::Finalize()
284
225M
{
285
225M
  ::ReleaseData(this->mData, this->mDataFlags);
286
225M
  // this->mData, this->mLength, and this->mDataFlags are purposefully left dangling
287
225M
}
nsTSubstring<char>::Finalize()
Line
Count
Source
284
207M
{
285
207M
  ::ReleaseData(this->mData, this->mDataFlags);
286
207M
  // this->mData, this->mLength, and this->mDataFlags are purposefully left dangling
287
207M
}
nsTSubstring<char16_t>::Finalize()
Line
Count
Source
284
17.5M
{
285
17.5M
  ::ReleaseData(this->mData, this->mDataFlags);
286
17.5M
  // this->mData, this->mLength, and this->mDataFlags are purposefully left dangling
287
17.5M
}
288
289
template <typename T>
290
bool
291
nsTSubstring<T>::ReplacePrep(index_type aCutStart,
292
                             size_type aCutLength,
293
                             size_type aNewLength)
294
1.06M
{
295
1.06M
  aCutLength = XPCOM_MIN(aCutLength, this->mLength - aCutStart);
296
1.06M
297
1.06M
  mozilla::CheckedInt<size_type> newTotalLen = this->mLength;
298
1.06M
  newTotalLen += aNewLength;
299
1.06M
  newTotalLen -= aCutLength;
300
1.06M
  if (!newTotalLen.isValid()) {
301
0
    return false;
302
0
  }
303
1.06M
304
1.06M
  if (aCutStart == this->mLength && Capacity() > newTotalLen.value()) {
305
2.00k
    this->mDataFlags &= ~DataFlags::VOIDED;
306
2.00k
    this->mData[newTotalLen.value()] = char_type(0);
307
2.00k
    this->mLength = newTotalLen.value();
308
2.00k
    return true;
309
2.00k
  }
310
1.06M
311
1.06M
  return ReplacePrepInternal(aCutStart, aCutLength, aNewLength,
312
1.06M
                             newTotalLen.value());
313
1.06M
}
nsTSubstring<char>::ReplacePrep(unsigned int, unsigned int, unsigned int)
Line
Count
Source
294
5.51k
{
295
5.51k
  aCutLength = XPCOM_MIN(aCutLength, this->mLength - aCutStart);
296
5.51k
297
5.51k
  mozilla::CheckedInt<size_type> newTotalLen = this->mLength;
298
5.51k
  newTotalLen += aNewLength;
299
5.51k
  newTotalLen -= aCutLength;
300
5.51k
  if (!newTotalLen.isValid()) {
301
0
    return false;
302
0
  }
303
5.51k
304
5.51k
  if (aCutStart == this->mLength && Capacity() > newTotalLen.value()) {
305
2.00k
    this->mDataFlags &= ~DataFlags::VOIDED;
306
2.00k
    this->mData[newTotalLen.value()] = char_type(0);
307
2.00k
    this->mLength = newTotalLen.value();
308
2.00k
    return true;
309
2.00k
  }
310
3.51k
311
3.51k
  return ReplacePrepInternal(aCutStart, aCutLength, aNewLength,
312
3.51k
                             newTotalLen.value());
313
3.51k
}
nsTSubstring<char16_t>::ReplacePrep(unsigned int, unsigned int, unsigned int)
Line
Count
Source
294
1.06M
{
295
1.06M
  aCutLength = XPCOM_MIN(aCutLength, this->mLength - aCutStart);
296
1.06M
297
1.06M
  mozilla::CheckedInt<size_type> newTotalLen = this->mLength;
298
1.06M
  newTotalLen += aNewLength;
299
1.06M
  newTotalLen -= aCutLength;
300
1.06M
  if (!newTotalLen.isValid()) {
301
0
    return false;
302
0
  }
303
1.06M
304
1.06M
  if (aCutStart == this->mLength && Capacity() > newTotalLen.value()) {
305
0
    this->mDataFlags &= ~DataFlags::VOIDED;
306
0
    this->mData[newTotalLen.value()] = char_type(0);
307
0
    this->mLength = newTotalLen.value();
308
0
    return true;
309
0
  }
310
1.06M
311
1.06M
  return ReplacePrepInternal(aCutStart, aCutLength, aNewLength,
312
1.06M
                             newTotalLen.value());
313
1.06M
}
314
315
template <typename T>
316
bool
317
nsTSubstring<T>::ReplacePrepInternal(index_type aCutStart, size_type aCutLen,
318
                                     size_type aFragLen, size_type aNewLen)
319
1.06M
{
320
1.06M
  size_type newSuffixStart = aCutStart + aFragLen;
321
1.06M
  size_type oldSuffixStart = aCutStart + aCutLen;
322
1.06M
  size_type suffixLength = this->mLength - oldSuffixStart;
323
1.06M
324
1.06M
  mozilla::Result<uint32_t, nsresult> r = StartBulkWriteImpl(
325
1.06M
    aNewLen, aCutStart, false, suffixLength, oldSuffixStart, newSuffixStart);
326
1.06M
  if (r.isErr()) {
327
0
    return false;
328
0
  }
329
1.06M
  FinishBulkWriteImpl(aNewLen);
330
1.06M
  return true;
331
1.06M
}
nsTSubstring<char>::ReplacePrepInternal(unsigned int, unsigned int, unsigned int, unsigned int)
Line
Count
Source
319
3.51k
{
320
3.51k
  size_type newSuffixStart = aCutStart + aFragLen;
321
3.51k
  size_type oldSuffixStart = aCutStart + aCutLen;
322
3.51k
  size_type suffixLength = this->mLength - oldSuffixStart;
323
3.51k
324
3.51k
  mozilla::Result<uint32_t, nsresult> r = StartBulkWriteImpl(
325
3.51k
    aNewLen, aCutStart, false, suffixLength, oldSuffixStart, newSuffixStart);
326
3.51k
  if (r.isErr()) {
327
0
    return false;
328
0
  }
329
3.51k
  FinishBulkWriteImpl(aNewLen);
330
3.51k
  return true;
331
3.51k
}
nsTSubstring<char16_t>::ReplacePrepInternal(unsigned int, unsigned int, unsigned int, unsigned int)
Line
Count
Source
319
1.06M
{
320
1.06M
  size_type newSuffixStart = aCutStart + aFragLen;
321
1.06M
  size_type oldSuffixStart = aCutStart + aCutLen;
322
1.06M
  size_type suffixLength = this->mLength - oldSuffixStart;
323
1.06M
324
1.06M
  mozilla::Result<uint32_t, nsresult> r = StartBulkWriteImpl(
325
1.06M
    aNewLen, aCutStart, false, suffixLength, oldSuffixStart, newSuffixStart);
326
1.06M
  if (r.isErr()) {
327
0
    return false;
328
0
  }
329
1.06M
  FinishBulkWriteImpl(aNewLen);
330
1.06M
  return true;
331
1.06M
}
332
333
template <typename T>
334
typename nsTSubstring<T>::size_type
335
nsTSubstring<T>::Capacity() const
336
95.0M
{
337
95.0M
  // return 0 to indicate an immutable or 0-sized buffer
338
95.0M
339
95.0M
  size_type capacity;
340
95.0M
  if (this->mDataFlags & DataFlags::REFCOUNTED) {
341
42.4M
    // if the string is readonly, then we pretend that it has no capacity.
342
42.4M
    nsStringBuffer* hdr = nsStringBuffer::FromData(this->mData);
343
42.4M
    if (hdr->IsReadonly()) {
344
3.61M
      capacity = 0;
345
38.8M
    } else {
346
38.8M
      capacity = (hdr->StorageSize() / sizeof(char_type)) - 1;
347
38.8M
    }
348
52.6M
  } else if (this->mDataFlags & DataFlags::INLINE) {
349
31.3M
    capacity = AsAutoString(this)->mInlineCapacity;
350
31.3M
  } else if (this->mDataFlags & DataFlags::OWNED) {
351
0
    // we don't store the capacity of an adopted buffer because that would
352
0
    // require an additional member field.  the best we can do is base the
353
0
    // capacity on our length.  remains to be seen if this is the right
354
0
    // trade-off.
355
0
    capacity = this->mLength;
356
21.3M
  } else {
357
21.3M
    capacity = 0;
358
21.3M
  }
359
95.0M
360
95.0M
  return capacity;
361
95.0M
}
nsTSubstring<char>::Capacity() const
Line
Count
Source
336
46.1M
{
337
46.1M
  // return 0 to indicate an immutable or 0-sized buffer
338
46.1M
339
46.1M
  size_type capacity;
340
46.1M
  if (this->mDataFlags & DataFlags::REFCOUNTED) {
341
1.31M
    // if the string is readonly, then we pretend that it has no capacity.
342
1.31M
    nsStringBuffer* hdr = nsStringBuffer::FromData(this->mData);
343
1.31M
    if (hdr->IsReadonly()) {
344
9.46k
      capacity = 0;
345
1.30M
    } else {
346
1.30M
      capacity = (hdr->StorageSize() / sizeof(char_type)) - 1;
347
1.30M
    }
348
44.8M
  } else if (this->mDataFlags & DataFlags::INLINE) {
349
28.4M
    capacity = AsAutoString(this)->mInlineCapacity;
350
28.4M
  } else if (this->mDataFlags & DataFlags::OWNED) {
351
0
    // we don't store the capacity of an adopted buffer because that would
352
0
    // require an additional member field.  the best we can do is base the
353
0
    // capacity on our length.  remains to be seen if this is the right
354
0
    // trade-off.
355
0
    capacity = this->mLength;
356
16.4M
  } else {
357
16.4M
    capacity = 0;
358
16.4M
  }
359
46.1M
360
46.1M
  return capacity;
361
46.1M
}
nsTSubstring<char16_t>::Capacity() const
Line
Count
Source
336
48.8M
{
337
48.8M
  // return 0 to indicate an immutable or 0-sized buffer
338
48.8M
339
48.8M
  size_type capacity;
340
48.8M
  if (this->mDataFlags & DataFlags::REFCOUNTED) {
341
41.0M
    // if the string is readonly, then we pretend that it has no capacity.
342
41.0M
    nsStringBuffer* hdr = nsStringBuffer::FromData(this->mData);
343
41.0M
    if (hdr->IsReadonly()) {
344
3.60M
      capacity = 0;
345
37.4M
    } else {
346
37.4M
      capacity = (hdr->StorageSize() / sizeof(char_type)) - 1;
347
37.4M
    }
348
41.0M
  } else if (this->mDataFlags & DataFlags::INLINE) {
349
2.86M
    capacity = AsAutoString(this)->mInlineCapacity;
350
4.89M
  } else if (this->mDataFlags & DataFlags::OWNED) {
351
0
    // we don't store the capacity of an adopted buffer because that would
352
0
    // require an additional member field.  the best we can do is base the
353
0
    // capacity on our length.  remains to be seen if this is the right
354
0
    // trade-off.
355
0
    capacity = this->mLength;
356
4.89M
  } else {
357
4.89M
    capacity = 0;
358
4.89M
  }
359
48.8M
360
48.8M
  return capacity;
361
48.8M
}
362
363
template <typename T>
364
bool
365
nsTSubstring<T>::EnsureMutable(size_type aNewLen)
366
21.0M
{
367
21.0M
  if (aNewLen == size_type(-1) || aNewLen == this->mLength) {
368
21.0M
    if (this->mDataFlags & (DataFlags::INLINE | DataFlags::OWNED)) {
369
12.4M
      return true;
370
12.4M
    }
371
8.63M
    if ((this->mDataFlags & DataFlags::REFCOUNTED) &&
372
8.63M
        !nsStringBuffer::FromData(this->mData)->IsReadonly()) {
373
8.43M
      return true;
374
8.43M
    }
375
204k
376
204k
    aNewLen = this->mLength;
377
204k
  }
378
21.0M
  return SetLength(aNewLen, mozilla::fallible);
379
21.0M
}
nsTSubstring<char>::EnsureMutable(unsigned int)
Line
Count
Source
366
20.6M
{
367
20.6M
  if (aNewLen == size_type(-1) || aNewLen == this->mLength) {
368
20.6M
    if (this->mDataFlags & (DataFlags::INLINE | DataFlags::OWNED)) {
369
12.4M
      return true;
370
12.4M
    }
371
8.22M
    if ((this->mDataFlags & DataFlags::REFCOUNTED) &&
372
8.22M
        !nsStringBuffer::FromData(this->mData)->IsReadonly()) {
373
8.21M
      return true;
374
8.21M
    }
375
14.2k
376
14.2k
    aNewLen = this->mLength;
377
14.2k
  }
378
20.6M
  return SetLength(aNewLen, mozilla::fallible);
379
20.6M
}
nsTSubstring<char16_t>::EnsureMutable(unsigned int)
Line
Count
Source
366
410k
{
367
410k
  if (aNewLen == size_type(-1) || aNewLen == this->mLength) {
368
410k
    if (this->mDataFlags & (DataFlags::INLINE | DataFlags::OWNED)) {
369
36
      return true;
370
36
    }
371
410k
    if ((this->mDataFlags & DataFlags::REFCOUNTED) &&
372
410k
        !nsStringBuffer::FromData(this->mData)->IsReadonly()) {
373
219k
      return true;
374
219k
    }
375
190k
376
190k
    aNewLen = this->mLength;
377
190k
  }
378
410k
  return SetLength(aNewLen, mozilla::fallible);
379
410k
}
380
381
// ---------------------------------------------------------------------------
382
383
// This version of Assign is optimized for single-character assignment.
384
template <typename T>
385
void
386
nsTSubstring<T>::Assign(char_type aChar)
387
0
{
388
0
  if (MOZ_UNLIKELY(!Assign(aChar, mozilla::fallible))) {
389
0
    AllocFailed(1);
390
0
  }
391
0
}
Unexecuted instantiation: nsTSubstring<char>::Assign(char)
Unexecuted instantiation: nsTSubstring<char16_t>::Assign(char16_t)
392
393
template <typename T>
394
bool
395
nsTSubstring<T>::Assign(char_type aChar, const fallible_t&)
396
0
{
397
0
  auto r = StartBulkWriteImpl(1, 0, true);
398
0
  if (MOZ_UNLIKELY(r.isErr())) {
399
0
    return false;
400
0
  }
401
0
  *this->mData = aChar;
402
0
  FinishBulkWriteImpl(1);
403
0
  return true;
404
0
}
Unexecuted instantiation: nsTSubstring<char>::Assign(char, std::nothrow_t const&)
Unexecuted instantiation: nsTSubstring<char16_t>::Assign(char16_t, std::nothrow_t const&)
405
406
template <typename T>
407
void
408
nsTSubstring<T>::Assign(const char_type* aData, size_type aLength)
409
6.83M
{
410
6.83M
  if (MOZ_UNLIKELY(!Assign(aData, aLength, mozilla::fallible))) {
411
0
    AllocFailed(aLength == size_type(-1) ? char_traits::length(aData)
412
0
                                         : aLength);
413
0
  }
414
6.83M
}
nsTSubstring<char>::Assign(char const*, unsigned int)
Line
Count
Source
409
6.74M
{
410
6.74M
  if (MOZ_UNLIKELY(!Assign(aData, aLength, mozilla::fallible))) {
411
0
    AllocFailed(aLength == size_type(-1) ? char_traits::length(aData)
412
0
                                         : aLength);
413
0
  }
414
6.74M
}
nsTSubstring<char16_t>::Assign(char16_t const*, unsigned int)
Line
Count
Source
409
94.8k
{
410
94.8k
  if (MOZ_UNLIKELY(!Assign(aData, aLength, mozilla::fallible))) {
411
0
    AllocFailed(aLength == size_type(-1) ? char_traits::length(aData)
412
0
                                         : aLength);
413
0
  }
414
94.8k
}
415
416
template <typename T>
417
bool
418
nsTSubstring<T>::Assign(const char_type* aData,
419
                        const fallible_t& aFallible)
420
0
{
421
0
  return Assign(aData, size_type(-1), aFallible);
422
0
}
Unexecuted instantiation: nsTSubstring<char>::Assign(char const*, std::nothrow_t const&)
Unexecuted instantiation: nsTSubstring<char16_t>::Assign(char16_t const*, std::nothrow_t const&)
423
424
template <typename T>
425
bool
426
nsTSubstring<T>::Assign(const char_type* aData, size_type aLength,
427
                        const fallible_t& aFallible)
428
32.8M
{
429
32.8M
  if (!aData || aLength == 0) {
430
2.26M
    Truncate();
431
2.26M
    return true;
432
2.26M
  }
433
30.5M
434
30.5M
  if (MOZ_UNLIKELY(aLength == size_type(-1))) {
435
4.17M
    aLength = char_traits::length(aData);
436
4.17M
  }
437
30.5M
438
30.5M
  if (MOZ_UNLIKELY(this->IsDependentOn(aData, aData + aLength))) {
439
0
    return Assign(string_type(aData, aLength), aFallible);
440
0
  }
441
30.5M
442
30.5M
  auto r = StartBulkWriteImpl(aLength, 0, true);
443
30.5M
  if (MOZ_UNLIKELY(r.isErr())) {
444
0
    return false;
445
0
  }
446
30.5M
  char_traits::copy(this->mData, aData, aLength);
447
30.5M
  FinishBulkWriteImpl(aLength);
448
30.5M
  return true;
449
30.5M
}
nsTSubstring<char>::Assign(char const*, unsigned int, std::nothrow_t const&)
Line
Count
Source
428
32.5M
{
429
32.5M
  if (!aData || aLength == 0) {
430
2.25M
    Truncate();
431
2.25M
    return true;
432
2.25M
  }
433
30.2M
434
30.2M
  if (MOZ_UNLIKELY(aLength == size_type(-1))) {
435
4.16M
    aLength = char_traits::length(aData);
436
4.16M
  }
437
30.2M
438
30.2M
  if (MOZ_UNLIKELY(this->IsDependentOn(aData, aData + aLength))) {
439
0
    return Assign(string_type(aData, aLength), aFallible);
440
0
  }
441
30.2M
442
30.2M
  auto r = StartBulkWriteImpl(aLength, 0, true);
443
30.2M
  if (MOZ_UNLIKELY(r.isErr())) {
444
0
    return false;
445
0
  }
446
30.2M
  char_traits::copy(this->mData, aData, aLength);
447
30.2M
  FinishBulkWriteImpl(aLength);
448
30.2M
  return true;
449
30.2M
}
nsTSubstring<char16_t>::Assign(char16_t const*, unsigned int, std::nothrow_t const&)
Line
Count
Source
428
260k
{
429
260k
  if (!aData || aLength == 0) {
430
2.65k
    Truncate();
431
2.65k
    return true;
432
2.65k
  }
433
258k
434
258k
  if (MOZ_UNLIKELY(aLength == size_type(-1))) {
435
8.90k
    aLength = char_traits::length(aData);
436
8.90k
  }
437
258k
438
258k
  if (MOZ_UNLIKELY(this->IsDependentOn(aData, aData + aLength))) {
439
0
    return Assign(string_type(aData, aLength), aFallible);
440
0
  }
441
258k
442
258k
  auto r = StartBulkWriteImpl(aLength, 0, true);
443
258k
  if (MOZ_UNLIKELY(r.isErr())) {
444
0
    return false;
445
0
  }
446
258k
  char_traits::copy(this->mData, aData, aLength);
447
258k
  FinishBulkWriteImpl(aLength);
448
258k
  return true;
449
258k
}
450
451
template <typename T>
452
void
453
nsTSubstring<T>::AssignASCII(const char* aData, size_type aLength)
454
1.60M
{
455
1.60M
  if (MOZ_UNLIKELY(!AssignASCII(aData, aLength, mozilla::fallible))) {
456
0
    AllocFailed(aLength);
457
0
  }
458
1.60M
}
Unexecuted instantiation: nsTSubstring<char>::AssignASCII(char const*, unsigned int)
nsTSubstring<char16_t>::AssignASCII(char const*, unsigned int)
Line
Count
Source
454
1.60M
{
455
1.60M
  if (MOZ_UNLIKELY(!AssignASCII(aData, aLength, mozilla::fallible))) {
456
0
    AllocFailed(aLength);
457
0
  }
458
1.60M
}
459
460
template <typename T>
461
bool
462
nsTSubstring<T>::AssignASCII(const char* aData, size_type aLength,
463
                                const fallible_t& aFallible)
464
1.60M
{
465
1.60M
  MOZ_ASSERT(aLength != size_type(-1));
466
1.60M
467
1.60M
  // A Unicode string can't depend on an ASCII string buffer,
468
1.60M
  // so this dependence check only applies to CStrings.
469
#ifdef CharT_is_char
470
  if (this->IsDependentOn(aData, aData + aLength)) {
471
    return Assign(string_type(aData, aLength), aFallible);
472
  }
473
#endif
474
475
1.60M
  auto r = StartBulkWriteImpl(aLength, 0, true);
476
1.60M
  if (MOZ_UNLIKELY(r.isErr())) {
477
0
    return false;
478
0
  }
479
1.60M
  char_traits::copyASCII(this->mData, aData, aLength);
480
1.60M
  FinishBulkWriteImpl(aLength);
481
1.60M
  return true;
482
1.60M
}
Unexecuted instantiation: nsTSubstring<char>::AssignASCII(char const*, unsigned int, std::nothrow_t const&)
nsTSubstring<char16_t>::AssignASCII(char const*, unsigned int, std::nothrow_t const&)
Line
Count
Source
464
1.60M
{
465
1.60M
  MOZ_ASSERT(aLength != size_type(-1));
466
1.60M
467
1.60M
  // A Unicode string can't depend on an ASCII string buffer,
468
1.60M
  // so this dependence check only applies to CStrings.
469
#ifdef CharT_is_char
470
  if (this->IsDependentOn(aData, aData + aLength)) {
471
    return Assign(string_type(aData, aLength), aFallible);
472
  }
473
#endif
474
475
1.60M
  auto r = StartBulkWriteImpl(aLength, 0, true);
476
1.60M
  if (MOZ_UNLIKELY(r.isErr())) {
477
0
    return false;
478
0
  }
479
1.60M
  char_traits::copyASCII(this->mData, aData, aLength);
480
1.60M
  FinishBulkWriteImpl(aLength);
481
1.60M
  return true;
482
1.60M
}
483
484
template <typename T>
485
void
486
nsTSubstring<T>::AssignLiteral(const char_type* aData, size_type aLength)
487
18.5k
{
488
18.5k
  ::ReleaseData(this->mData, this->mDataFlags);
489
18.5k
  SetData(const_cast<char_type*>(aData), aLength,
490
18.5k
          DataFlags::TERMINATED | DataFlags::LITERAL);
491
18.5k
}
nsTSubstring<char>::AssignLiteral(char const*, unsigned int)
Line
Count
Source
487
13.7k
{
488
13.7k
  ::ReleaseData(this->mData, this->mDataFlags);
489
13.7k
  SetData(const_cast<char_type*>(aData), aLength,
490
13.7k
          DataFlags::TERMINATED | DataFlags::LITERAL);
491
13.7k
}
nsTSubstring<char16_t>::AssignLiteral(char16_t const*, unsigned int)
Line
Count
Source
487
4.74k
{
488
4.74k
  ::ReleaseData(this->mData, this->mDataFlags);
489
4.74k
  SetData(const_cast<char_type*>(aData), aLength,
490
4.74k
          DataFlags::TERMINATED | DataFlags::LITERAL);
491
4.74k
}
492
493
template <typename T>
494
void
495
nsTSubstring<T>::Assign(const self_type& aStr)
496
53.7M
{
497
53.7M
  if (!Assign(aStr, mozilla::fallible)) {
498
0
    AllocFailed(aStr.Length());
499
0
  }
500
53.7M
}
nsTSubstring<char>::Assign(nsTSubstring<char> const&)
Line
Count
Source
496
34.7M
{
497
34.7M
  if (!Assign(aStr, mozilla::fallible)) {
498
0
    AllocFailed(aStr.Length());
499
0
  }
500
34.7M
}
nsTSubstring<char16_t>::Assign(nsTSubstring<char16_t> const&)
Line
Count
Source
496
18.9M
{
497
18.9M
  if (!Assign(aStr, mozilla::fallible)) {
498
0
    AllocFailed(aStr.Length());
499
0
  }
500
18.9M
}
501
502
template <typename T>
503
bool
504
nsTSubstring<T>::Assign(const self_type& aStr, const fallible_t& aFallible)
505
63.3M
{
506
63.3M
  // |aStr| could be sharable. We need to check its flags to know how to
507
63.3M
  // deal with it.
508
63.3M
509
63.3M
  if (&aStr == this) {
510
0
    return true;
511
0
  }
512
63.3M
513
63.3M
  if (!aStr.mLength) {
514
17.3M
    Truncate();
515
17.3M
    this->mDataFlags |= aStr.mDataFlags & DataFlags::VOIDED;
516
17.3M
    return true;
517
17.3M
  }
518
45.9M
519
45.9M
  if (aStr.mDataFlags & DataFlags::REFCOUNTED) {
520
19.9M
    // nice! we can avoid a string copy :-)
521
19.9M
522
19.9M
    // |aStr| should be null-terminated
523
19.9M
    NS_ASSERTION(aStr.mDataFlags & DataFlags::TERMINATED, "shared, but not terminated");
524
19.9M
525
19.9M
    ::ReleaseData(this->mData, this->mDataFlags);
526
19.9M
527
19.9M
    SetData(aStr.mData, aStr.mLength,
528
19.9M
            DataFlags::TERMINATED | DataFlags::REFCOUNTED);
529
19.9M
530
19.9M
    // get an owning reference to the this->mData
531
19.9M
    nsStringBuffer::FromData(this->mData)->AddRef();
532
19.9M
    return true;
533
25.9M
  } else if (aStr.mDataFlags & DataFlags::LITERAL) {
534
10.4k
    MOZ_ASSERT(aStr.mDataFlags & DataFlags::TERMINATED, "Unterminated literal");
535
10.4k
536
10.4k
    AssignLiteral(aStr.mData, aStr.mLength);
537
10.4k
    return true;
538
10.4k
  }
539
25.9M
540
25.9M
  // else, treat this like an ordinary assignment.
541
25.9M
  return Assign(aStr.Data(), aStr.Length(), aFallible);
542
25.9M
}
nsTSubstring<char>::Assign(nsTSubstring<char> const&, std::nothrow_t const&)
Line
Count
Source
505
44.3M
{
506
44.3M
  // |aStr| could be sharable. We need to check its flags to know how to
507
44.3M
  // deal with it.
508
44.3M
509
44.3M
  if (&aStr == this) {
510
0
    return true;
511
0
  }
512
44.3M
513
44.3M
  if (!aStr.mLength) {
514
17.3M
    Truncate();
515
17.3M
    this->mDataFlags |= aStr.mDataFlags & DataFlags::VOIDED;
516
17.3M
    return true;
517
17.3M
  }
518
26.9M
519
26.9M
  if (aStr.mDataFlags & DataFlags::REFCOUNTED) {
520
1.17M
    // nice! we can avoid a string copy :-)
521
1.17M
522
1.17M
    // |aStr| should be null-terminated
523
1.17M
    NS_ASSERTION(aStr.mDataFlags & DataFlags::TERMINATED, "shared, but not terminated");
524
1.17M
525
1.17M
    ::ReleaseData(this->mData, this->mDataFlags);
526
1.17M
527
1.17M
    SetData(aStr.mData, aStr.mLength,
528
1.17M
            DataFlags::TERMINATED | DataFlags::REFCOUNTED);
529
1.17M
530
1.17M
    // get an owning reference to the this->mData
531
1.17M
    nsStringBuffer::FromData(this->mData)->AddRef();
532
1.17M
    return true;
533
25.8M
  } else if (aStr.mDataFlags & DataFlags::LITERAL) {
534
6.08k
    MOZ_ASSERT(aStr.mDataFlags & DataFlags::TERMINATED, "Unterminated literal");
535
6.08k
536
6.08k
    AssignLiteral(aStr.mData, aStr.mLength);
537
6.08k
    return true;
538
6.08k
  }
539
25.8M
540
25.8M
  // else, treat this like an ordinary assignment.
541
25.8M
  return Assign(aStr.Data(), aStr.Length(), aFallible);
542
25.8M
}
nsTSubstring<char16_t>::Assign(nsTSubstring<char16_t> const&, std::nothrow_t const&)
Line
Count
Source
505
18.9M
{
506
18.9M
  // |aStr| could be sharable. We need to check its flags to know how to
507
18.9M
  // deal with it.
508
18.9M
509
18.9M
  if (&aStr == this) {
510
0
    return true;
511
0
  }
512
18.9M
513
18.9M
  if (!aStr.mLength) {
514
9.09k
    Truncate();
515
9.09k
    this->mDataFlags |= aStr.mDataFlags & DataFlags::VOIDED;
516
9.09k
    return true;
517
9.09k
  }
518
18.9M
519
18.9M
  if (aStr.mDataFlags & DataFlags::REFCOUNTED) {
520
18.8M
    // nice! we can avoid a string copy :-)
521
18.8M
522
18.8M
    // |aStr| should be null-terminated
523
18.8M
    NS_ASSERTION(aStr.mDataFlags & DataFlags::TERMINATED, "shared, but not terminated");
524
18.8M
525
18.8M
    ::ReleaseData(this->mData, this->mDataFlags);
526
18.8M
527
18.8M
    SetData(aStr.mData, aStr.mLength,
528
18.8M
            DataFlags::TERMINATED | DataFlags::REFCOUNTED);
529
18.8M
530
18.8M
    // get an owning reference to the this->mData
531
18.8M
    nsStringBuffer::FromData(this->mData)->AddRef();
532
18.8M
    return true;
533
18.8M
  } else if (aStr.mDataFlags & DataFlags::LITERAL) {
534
4.33k
    MOZ_ASSERT(aStr.mDataFlags & DataFlags::TERMINATED, "Unterminated literal");
535
4.33k
536
4.33k
    AssignLiteral(aStr.mData, aStr.mLength);
537
4.33k
    return true;
538
4.33k
  }
539
166k
540
166k
  // else, treat this like an ordinary assignment.
541
166k
  return Assign(aStr.Data(), aStr.Length(), aFallible);
542
166k
}
543
544
template <typename T>
545
void
546
nsTSubstring<T>::Assign(self_type&& aStr)
547
5.00M
{
548
5.00M
  if (!Assign(std::move(aStr), mozilla::fallible)) {
549
0
    AllocFailed(aStr.Length());
550
0
  }
551
5.00M
}
nsTSubstring<char>::Assign(nsTSubstring<char>&&)
Line
Count
Source
547
5.00M
{
548
5.00M
  if (!Assign(std::move(aStr), mozilla::fallible)) {
549
0
    AllocFailed(aStr.Length());
550
0
  }
551
5.00M
}
nsTSubstring<char16_t>::Assign(nsTSubstring<char16_t>&&)
Line
Count
Source
547
24
{
548
24
  if (!Assign(std::move(aStr), mozilla::fallible)) {
549
0
    AllocFailed(aStr.Length());
550
0
  }
551
24
}
552
553
template <typename T>
554
bool
555
nsTSubstring<T>::Assign(self_type&& aStr, const fallible_t& aFallible)
556
5.00M
{
557
5.00M
  // We're moving |aStr| in this method, so we need to try to steal the data,
558
5.00M
  // and in the fallback perform a copy-assignment followed by a truncation of
559
5.00M
  // the original string.
560
5.00M
561
5.00M
  if (&aStr == this) {
562
0
    NS_WARNING("Move assigning a string to itself?");
563
0
    return true;
564
0
  }
565
5.00M
566
5.00M
  if (aStr.mDataFlags & (DataFlags::REFCOUNTED | DataFlags::OWNED)) {
567
1.12M
    // If they have a REFCOUNTED or OWNED buffer, we can avoid a copy - so steal
568
1.12M
    // their buffer and reset them to the empty string.
569
1.12M
570
1.12M
    // |aStr| should be null-terminated
571
1.12M
    NS_ASSERTION(aStr.mDataFlags & DataFlags::TERMINATED,
572
1.12M
                 "shared or owned, but not terminated");
573
1.12M
574
1.12M
    ::ReleaseData(this->mData, this->mDataFlags);
575
1.12M
576
1.12M
    SetData(aStr.mData, aStr.mLength, aStr.mDataFlags);
577
1.12M
    aStr.SetToEmptyBuffer();
578
1.12M
    return true;
579
1.12M
  }
580
3.88M
581
3.88M
  // Otherwise treat this as a normal assignment, and truncate the moved string.
582
3.88M
  // We don't truncate the source string if the allocation failed.
583
3.88M
  if (!Assign(aStr, aFallible)) {
584
0
    return false;
585
0
  }
586
3.88M
  aStr.Truncate();
587
3.88M
  return true;
588
3.88M
}
nsTSubstring<char>::Assign(nsTSubstring<char>&&, std::nothrow_t const&)
Line
Count
Source
556
5.00M
{
557
5.00M
  // We're moving |aStr| in this method, so we need to try to steal the data,
558
5.00M
  // and in the fallback perform a copy-assignment followed by a truncation of
559
5.00M
  // the original string.
560
5.00M
561
5.00M
  if (&aStr == this) {
562
0
    NS_WARNING("Move assigning a string to itself?");
563
0
    return true;
564
0
  }
565
5.00M
566
5.00M
  if (aStr.mDataFlags & (DataFlags::REFCOUNTED | DataFlags::OWNED)) {
567
1.12M
    // If they have a REFCOUNTED or OWNED buffer, we can avoid a copy - so steal
568
1.12M
    // their buffer and reset them to the empty string.
569
1.12M
570
1.12M
    // |aStr| should be null-terminated
571
1.12M
    NS_ASSERTION(aStr.mDataFlags & DataFlags::TERMINATED,
572
1.12M
                 "shared or owned, but not terminated");
573
1.12M
574
1.12M
    ::ReleaseData(this->mData, this->mDataFlags);
575
1.12M
576
1.12M
    SetData(aStr.mData, aStr.mLength, aStr.mDataFlags);
577
1.12M
    aStr.SetToEmptyBuffer();
578
1.12M
    return true;
579
1.12M
  }
580
3.88M
581
3.88M
  // Otherwise treat this as a normal assignment, and truncate the moved string.
582
3.88M
  // We don't truncate the source string if the allocation failed.
583
3.88M
  if (!Assign(aStr, aFallible)) {
584
0
    return false;
585
0
  }
586
3.88M
  aStr.Truncate();
587
3.88M
  return true;
588
3.88M
}
nsTSubstring<char16_t>::Assign(nsTSubstring<char16_t>&&, std::nothrow_t const&)
Line
Count
Source
556
24
{
557
24
  // We're moving |aStr| in this method, so we need to try to steal the data,
558
24
  // and in the fallback perform a copy-assignment followed by a truncation of
559
24
  // the original string.
560
24
561
24
  if (&aStr == this) {
562
0
    NS_WARNING("Move assigning a string to itself?");
563
0
    return true;
564
0
  }
565
24
566
24
  if (aStr.mDataFlags & (DataFlags::REFCOUNTED | DataFlags::OWNED)) {
567
21
    // If they have a REFCOUNTED or OWNED buffer, we can avoid a copy - so steal
568
21
    // their buffer and reset them to the empty string.
569
21
570
21
    // |aStr| should be null-terminated
571
21
    NS_ASSERTION(aStr.mDataFlags & DataFlags::TERMINATED,
572
21
                 "shared or owned, but not terminated");
573
21
574
21
    ::ReleaseData(this->mData, this->mDataFlags);
575
21
576
21
    SetData(aStr.mData, aStr.mLength, aStr.mDataFlags);
577
21
    aStr.SetToEmptyBuffer();
578
21
    return true;
579
21
  }
580
3
581
3
  // Otherwise treat this as a normal assignment, and truncate the moved string.
582
3
  // We don't truncate the source string if the allocation failed.
583
3
  if (!Assign(aStr, aFallible)) {
584
0
    return false;
585
0
  }
586
3
  aStr.Truncate();
587
3
  return true;
588
3
}
589
590
template <typename T>
591
void
592
nsTSubstring<T>::Assign(const substring_tuple_type& aTuple)
593
35.6k
{
594
35.6k
  if (!Assign(aTuple, mozilla::fallible)) {
595
0
    AllocFailed(aTuple.Length());
596
0
  }
597
35.6k
}
nsTSubstring<char>::Assign(nsTSubstringTuple<char> const&)
Line
Count
Source
593
35.5k
{
594
35.5k
  if (!Assign(aTuple, mozilla::fallible)) {
595
0
    AllocFailed(aTuple.Length());
596
0
  }
597
35.5k
}
nsTSubstring<char16_t>::Assign(nsTSubstringTuple<char16_t> const&)
Line
Count
Source
593
15
{
594
15
  if (!Assign(aTuple, mozilla::fallible)) {
595
0
    AllocFailed(aTuple.Length());
596
0
  }
597
15
}
598
599
template <typename T>
600
bool
601
nsTSubstring<T>::Assign(const substring_tuple_type& aTuple,
602
                        const fallible_t& aFallible)
603
35.6k
{
604
35.6k
  if (aTuple.IsDependentOn(this->mData, this->mData + this->mLength)) {
605
0
    // take advantage of sharing here...
606
0
    return Assign(string_type(aTuple), aFallible);
607
0
  }
608
35.6k
609
35.6k
  size_type length = aTuple.Length();
610
35.6k
611
35.6k
  mozilla::Result<uint32_t, nsresult> r = StartBulkWriteImpl(length);
612
35.6k
  if (r.isErr()) {
613
0
    return false;
614
0
  }
615
35.6k
616
35.6k
  aTuple.WriteTo(this->mData, length);
617
35.6k
618
35.6k
  FinishBulkWriteImpl(length);
619
35.6k
  return true;
620
35.6k
}
nsTSubstring<char>::Assign(nsTSubstringTuple<char> const&, std::nothrow_t const&)
Line
Count
Source
603
35.5k
{
604
35.5k
  if (aTuple.IsDependentOn(this->mData, this->mData + this->mLength)) {
605
0
    // take advantage of sharing here...
606
0
    return Assign(string_type(aTuple), aFallible);
607
0
  }
608
35.5k
609
35.5k
  size_type length = aTuple.Length();
610
35.5k
611
35.5k
  mozilla::Result<uint32_t, nsresult> r = StartBulkWriteImpl(length);
612
35.5k
  if (r.isErr()) {
613
0
    return false;
614
0
  }
615
35.5k
616
35.5k
  aTuple.WriteTo(this->mData, length);
617
35.5k
618
35.5k
  FinishBulkWriteImpl(length);
619
35.5k
  return true;
620
35.5k
}
nsTSubstring<char16_t>::Assign(nsTSubstringTuple<char16_t> const&, std::nothrow_t const&)
Line
Count
Source
603
15
{
604
15
  if (aTuple.IsDependentOn(this->mData, this->mData + this->mLength)) {
605
0
    // take advantage of sharing here...
606
0
    return Assign(string_type(aTuple), aFallible);
607
0
  }
608
15
609
15
  size_type length = aTuple.Length();
610
15
611
15
  mozilla::Result<uint32_t, nsresult> r = StartBulkWriteImpl(length);
612
15
  if (r.isErr()) {
613
0
    return false;
614
0
  }
615
15
616
15
  aTuple.WriteTo(this->mData, length);
617
15
618
15
  FinishBulkWriteImpl(length);
619
15
  return true;
620
15
}
621
622
template <typename T>
623
void
624
nsTSubstring<T>::Adopt(char_type* aData, size_type aLength)
625
2.73M
{
626
2.73M
  if (aData) {
627
2.73M
    ::ReleaseData(this->mData, this->mDataFlags);
628
2.73M
629
2.73M
    if (aLength == size_type(-1)) {
630
2.73M
      aLength = char_traits::length(aData);
631
2.73M
    }
632
2.73M
633
2.73M
    MOZ_RELEASE_ASSERT(CheckCapacity(aLength), "adopting a too-long string");
634
2.73M
635
2.73M
    SetData(aData, aLength, DataFlags::TERMINATED | DataFlags::OWNED);
636
2.73M
637
2.73M
    STRING_STAT_INCREMENT(Adopt);
638
2.73M
    // Treat this as construction of a "StringAdopt" object for leak
639
2.73M
    // tracking purposes.
640
2.73M
    MOZ_LOG_CTOR(this->mData, "StringAdopt", 1);
641
2.73M
  } else {
642
0
    SetIsVoid(true);
643
0
  }
644
2.73M
}
nsTSubstring<char>::Adopt(char*, unsigned int)
Line
Count
Source
625
2.73M
{
626
2.73M
  if (aData) {
627
2.73M
    ::ReleaseData(this->mData, this->mDataFlags);
628
2.73M
629
2.73M
    if (aLength == size_type(-1)) {
630
2.73M
      aLength = char_traits::length(aData);
631
2.73M
    }
632
2.73M
633
2.73M
    MOZ_RELEASE_ASSERT(CheckCapacity(aLength), "adopting a too-long string");
634
2.73M
635
2.73M
    SetData(aData, aLength, DataFlags::TERMINATED | DataFlags::OWNED);
636
2.73M
637
2.73M
    STRING_STAT_INCREMENT(Adopt);
638
2.73M
    // Treat this as construction of a "StringAdopt" object for leak
639
2.73M
    // tracking purposes.
640
2.73M
    MOZ_LOG_CTOR(this->mData, "StringAdopt", 1);
641
2.73M
  } else {
642
0
    SetIsVoid(true);
643
0
  }
644
2.73M
}
nsTSubstring<char16_t>::Adopt(char16_t*, unsigned int)
Line
Count
Source
625
18
{
626
18
  if (aData) {
627
18
    ::ReleaseData(this->mData, this->mDataFlags);
628
18
629
18
    if (aLength == size_type(-1)) {
630
18
      aLength = char_traits::length(aData);
631
18
    }
632
18
633
18
    MOZ_RELEASE_ASSERT(CheckCapacity(aLength), "adopting a too-long string");
634
18
635
18
    SetData(aData, aLength, DataFlags::TERMINATED | DataFlags::OWNED);
636
18
637
18
    STRING_STAT_INCREMENT(Adopt);
638
18
    // Treat this as construction of a "StringAdopt" object for leak
639
18
    // tracking purposes.
640
18
    MOZ_LOG_CTOR(this->mData, "StringAdopt", 1);
641
18
  } else {
642
0
    SetIsVoid(true);
643
0
  }
644
18
}
645
646
647
// This version of Replace is optimized for single-character replacement.
648
template <typename T>
649
void
650
nsTSubstring<T>::Replace(index_type aCutStart, size_type aCutLength,
651
                         char_type aChar)
652
18
{
653
18
  aCutStart = XPCOM_MIN(aCutStart, this->Length());
654
18
655
18
  if (ReplacePrep(aCutStart, aCutLength, 1)) {
656
18
    this->mData[aCutStart] = aChar;
657
18
  }
658
18
}
nsTSubstring<char>::Replace(unsigned int, unsigned int, char)
Line
Count
Source
652
6
{
653
6
  aCutStart = XPCOM_MIN(aCutStart, this->Length());
654
6
655
6
  if (ReplacePrep(aCutStart, aCutLength, 1)) {
656
6
    this->mData[aCutStart] = aChar;
657
6
  }
658
6
}
nsTSubstring<char16_t>::Replace(unsigned int, unsigned int, char16_t)
Line
Count
Source
652
12
{
653
12
  aCutStart = XPCOM_MIN(aCutStart, this->Length());
654
12
655
12
  if (ReplacePrep(aCutStart, aCutLength, 1)) {
656
12
    this->mData[aCutStart] = aChar;
657
12
  }
658
12
}
659
660
template <typename T>
661
bool
662
nsTSubstring<T>::Replace(index_type aCutStart, size_type aCutLength,
663
                         char_type aChar,
664
                         const fallible_t&)
665
0
{
666
0
  aCutStart = XPCOM_MIN(aCutStart, this->Length());
667
0
668
0
  if (!ReplacePrep(aCutStart, aCutLength, 1)) {
669
0
    return false;
670
0
  }
671
0
672
0
  this->mData[aCutStart] = aChar;
673
0
674
0
  return true;
675
0
}
Unexecuted instantiation: nsTSubstring<char>::Replace(unsigned int, unsigned int, char, std::nothrow_t const&)
Unexecuted instantiation: nsTSubstring<char16_t>::Replace(unsigned int, unsigned int, char16_t, std::nothrow_t const&)
676
677
template <typename T>
678
void
679
nsTSubstring<T>::Replace(index_type aCutStart, size_type aCutLength,
680
                         const char_type* aData, size_type aLength)
681
1.06M
{
682
1.06M
  if (!Replace(aCutStart, aCutLength, aData, aLength,
683
1.06M
               mozilla::fallible)) {
684
0
    AllocFailed(this->Length() - aCutLength + 1);
685
0
  }
686
1.06M
}
nsTSubstring<char>::Replace(unsigned int, unsigned int, char const*, unsigned int)
Line
Count
Source
681
2.34k
{
682
2.34k
  if (!Replace(aCutStart, aCutLength, aData, aLength,
683
2.34k
               mozilla::fallible)) {
684
0
    AllocFailed(this->Length() - aCutLength + 1);
685
0
  }
686
2.34k
}
nsTSubstring<char16_t>::Replace(unsigned int, unsigned int, char16_t const*, unsigned int)
Line
Count
Source
681
1.06M
{
682
1.06M
  if (!Replace(aCutStart, aCutLength, aData, aLength,
683
1.06M
               mozilla::fallible)) {
684
0
    AllocFailed(this->Length() - aCutLength + 1);
685
0
  }
686
1.06M
}
687
688
template <typename T>
689
bool
690
nsTSubstring<T>::Replace(index_type aCutStart, size_type aCutLength,
691
                         const char_type* aData, size_type aLength,
692
                         const fallible_t& aFallible)
693
1.06M
{
694
1.06M
  // unfortunately, some callers pass null :-(
695
1.06M
  if (!aData) {
696
0
    aLength = 0;
697
1.06M
  } else {
698
1.06M
    if (aLength == size_type(-1)) {
699
0
      aLength = char_traits::length(aData);
700
0
    }
701
1.06M
702
1.06M
    if (this->IsDependentOn(aData, aData + aLength)) {
703
0
      nsTAutoString<T> temp(aData, aLength);
704
0
      return Replace(aCutStart, aCutLength, temp, aFallible);
705
0
    }
706
1.06M
  }
707
1.06M
708
1.06M
  aCutStart = XPCOM_MIN(aCutStart, this->Length());
709
1.06M
710
1.06M
  bool ok = ReplacePrep(aCutStart, aCutLength, aLength);
711
1.06M
  if (!ok) {
712
0
    return false;
713
0
  }
714
1.06M
715
1.06M
  if (aLength > 0) {
716
1.71k
    char_traits::copy(this->mData + aCutStart, aData, aLength);
717
1.71k
  }
718
1.06M
719
1.06M
  return true;
720
1.06M
}
nsTSubstring<char>::Replace(unsigned int, unsigned int, char const*, unsigned int, std::nothrow_t const&)
Line
Count
Source
693
2.34k
{
694
2.34k
  // unfortunately, some callers pass null :-(
695
2.34k
  if (!aData) {
696
0
    aLength = 0;
697
2.34k
  } else {
698
2.34k
    if (aLength == size_type(-1)) {
699
0
      aLength = char_traits::length(aData);
700
0
    }
701
2.34k
702
2.34k
    if (this->IsDependentOn(aData, aData + aLength)) {
703
0
      nsTAutoString<T> temp(aData, aLength);
704
0
      return Replace(aCutStart, aCutLength, temp, aFallible);
705
0
    }
706
2.34k
  }
707
2.34k
708
2.34k
  aCutStart = XPCOM_MIN(aCutStart, this->Length());
709
2.34k
710
2.34k
  bool ok = ReplacePrep(aCutStart, aCutLength, aLength);
711
2.34k
  if (!ok) {
712
0
    return false;
713
0
  }
714
2.34k
715
2.34k
  if (aLength > 0) {
716
1.70k
    char_traits::copy(this->mData + aCutStart, aData, aLength);
717
1.70k
  }
718
2.34k
719
2.34k
  return true;
720
2.34k
}
nsTSubstring<char16_t>::Replace(unsigned int, unsigned int, char16_t const*, unsigned int, std::nothrow_t const&)
Line
Count
Source
693
1.06M
{
694
1.06M
  // unfortunately, some callers pass null :-(
695
1.06M
  if (!aData) {
696
0
    aLength = 0;
697
1.06M
  } else {
698
1.06M
    if (aLength == size_type(-1)) {
699
0
      aLength = char_traits::length(aData);
700
0
    }
701
1.06M
702
1.06M
    if (this->IsDependentOn(aData, aData + aLength)) {
703
0
      nsTAutoString<T> temp(aData, aLength);
704
0
      return Replace(aCutStart, aCutLength, temp, aFallible);
705
0
    }
706
1.06M
  }
707
1.06M
708
1.06M
  aCutStart = XPCOM_MIN(aCutStart, this->Length());
709
1.06M
710
1.06M
  bool ok = ReplacePrep(aCutStart, aCutLength, aLength);
711
1.06M
  if (!ok) {
712
0
    return false;
713
0
  }
714
1.06M
715
1.06M
  if (aLength > 0) {
716
12
    char_traits::copy(this->mData + aCutStart, aData, aLength);
717
12
  }
718
1.06M
719
1.06M
  return true;
720
1.06M
}
721
722
template <typename T>
723
void
724
nsTSubstring<T>::ReplaceASCII(index_type aCutStart, size_type aCutLength,
725
                              const char* aData, size_type aLength)
726
0
{
727
0
  if (!ReplaceASCII(aCutStart, aCutLength, aData, aLength, mozilla::fallible)) {
728
0
    AllocFailed(this->Length() - aCutLength + 1);
729
0
  }
730
0
}
Unexecuted instantiation: nsTSubstring<char>::ReplaceASCII(unsigned int, unsigned int, char const*, unsigned int)
Unexecuted instantiation: nsTSubstring<char16_t>::ReplaceASCII(unsigned int, unsigned int, char const*, unsigned int)
731
732
template <typename T>
733
bool
734
nsTSubstring<T>::ReplaceASCII(index_type aCutStart, size_type aCutLength,
735
                              const char* aData, size_type aLength,
736
                              const fallible_t& aFallible)
737
0
{
738
0
  if (aLength == size_type(-1)) {
739
0
    aLength = strlen(aData);
740
0
  }
741
0
742
0
  // A Unicode string can't depend on an ASCII string buffer,
743
0
  // so this dependence check only applies to CStrings.
744
#ifdef CharT_is_char
745
  if (this->IsDependentOn(aData, aData + aLength)) {
746
    nsTAutoString_CharT temp(aData, aLength);
747
    return Replace(aCutStart, aCutLength, temp, aFallible);
748
  }
749
#endif
750
751
0
  aCutStart = XPCOM_MIN(aCutStart, this->Length());
752
0
753
0
  bool ok = ReplacePrep(aCutStart, aCutLength, aLength);
754
0
  if (!ok) {
755
0
    return false;
756
0
  }
757
0
758
0
  if (aLength > 0) {
759
0
    char_traits::copyASCII(this->mData + aCutStart, aData, aLength);
760
0
  }
761
0
762
0
  return true;
763
0
}
Unexecuted instantiation: nsTSubstring<char>::ReplaceASCII(unsigned int, unsigned int, char const*, unsigned int, std::nothrow_t const&)
Unexecuted instantiation: nsTSubstring<char16_t>::ReplaceASCII(unsigned int, unsigned int, char const*, unsigned int, std::nothrow_t const&)
764
765
template <typename T>
766
void
767
nsTSubstring<T>::Replace(index_type aCutStart, size_type aCutLength,
768
                         const substring_tuple_type& aTuple)
769
0
{
770
0
  if (aTuple.IsDependentOn(this->mData, this->mData + this->mLength)) {
771
0
    nsTAutoString<T> temp(aTuple);
772
0
    Replace(aCutStart, aCutLength, temp);
773
0
    return;
774
0
  }
775
0
776
0
  size_type length = aTuple.Length();
777
0
778
0
  aCutStart = XPCOM_MIN(aCutStart, this->Length());
779
0
780
0
  if (ReplacePrep(aCutStart, aCutLength, length) && length > 0) {
781
0
    aTuple.WriteTo(this->mData + aCutStart, length);
782
0
  }
783
0
}
Unexecuted instantiation: nsTSubstring<char>::Replace(unsigned int, unsigned int, nsTSubstringTuple<char> const&)
Unexecuted instantiation: nsTSubstring<char16_t>::Replace(unsigned int, unsigned int, nsTSubstringTuple<char16_t> const&)
784
785
template <typename T>
786
void
787
nsTSubstring<T>::ReplaceLiteral(index_type aCutStart, size_type aCutLength,
788
                                const char_type* aData, size_type aLength)
789
4.43k
{
790
4.43k
  aCutStart = XPCOM_MIN(aCutStart, this->Length());
791
4.43k
792
4.43k
  if (!aCutStart && aCutLength == this->Length() &&
793
4.43k
      !(this->mDataFlags & DataFlags::REFCOUNTED)) {
794
1.26k
    // Check for REFCOUNTED above to avoid undoing the effect of
795
1.26k
    // SetCapacity().
796
1.26k
    AssignLiteral(aData, aLength);
797
3.16k
  } else if (ReplacePrep(aCutStart, aCutLength, aLength) && aLength > 0) {
798
3.16k
    char_traits::copy(this->mData + aCutStart, aData, aLength);
799
3.16k
  }
800
4.43k
}
nsTSubstring<char>::ReplaceLiteral(unsigned int, unsigned int, char const*, unsigned int)
Line
Count
Source
789
4.43k
{
790
4.43k
  aCutStart = XPCOM_MIN(aCutStart, this->Length());
791
4.43k
792
4.43k
  if (!aCutStart && aCutLength == this->Length() &&
793
4.43k
      !(this->mDataFlags & DataFlags::REFCOUNTED)) {
794
1.26k
    // Check for REFCOUNTED above to avoid undoing the effect of
795
1.26k
    // SetCapacity().
796
1.26k
    AssignLiteral(aData, aLength);
797
3.16k
  } else if (ReplacePrep(aCutStart, aCutLength, aLength) && aLength > 0) {
798
3.16k
    char_traits::copy(this->mData + aCutStart, aData, aLength);
799
3.16k
  }
800
4.43k
}
Unexecuted instantiation: nsTSubstring<char16_t>::ReplaceLiteral(unsigned int, unsigned int, char16_t const*, unsigned int)
801
802
template <typename T>
803
void
804
nsTSubstring<T>::Append(char_type aChar)
805
45.8M
{
806
45.8M
  if (MOZ_UNLIKELY(!Append(aChar, mozilla::fallible))) {
807
0
    AllocFailed(this->mLength + 1);
808
0
  }
809
45.8M
}
nsTSubstring<char>::Append(char)
Line
Count
Source
805
335k
{
806
335k
  if (MOZ_UNLIKELY(!Append(aChar, mozilla::fallible))) {
807
0
    AllocFailed(this->mLength + 1);
808
0
  }
809
335k
}
nsTSubstring<char16_t>::Append(char16_t)
Line
Count
Source
805
45.4M
{
806
45.4M
  if (MOZ_UNLIKELY(!Append(aChar, mozilla::fallible))) {
807
0
    AllocFailed(this->mLength + 1);
808
0
  }
809
45.4M
}
810
811
template <typename T>
812
bool
813
nsTSubstring<T>::Append(char_type aChar,
814
                        const fallible_t& aFallible)
815
45.8M
{
816
45.8M
  size_type oldLen = this->mLength;
817
45.8M
  size_type newLen = oldLen + 1; // Can't overflow
818
45.8M
  auto r = StartBulkWriteImpl(newLen, oldLen, false);
819
45.8M
  if (MOZ_UNLIKELY(r.isErr())) {
820
0
    return false;
821
0
  }
822
45.8M
  this->mData[oldLen] = aChar;
823
45.8M
  FinishBulkWriteImpl(newLen);
824
45.8M
  return true;
825
45.8M
}
nsTSubstring<char>::Append(char, std::nothrow_t const&)
Line
Count
Source
815
335k
{
816
335k
  size_type oldLen = this->mLength;
817
335k
  size_type newLen = oldLen + 1; // Can't overflow
818
335k
  auto r = StartBulkWriteImpl(newLen, oldLen, false);
819
335k
  if (MOZ_UNLIKELY(r.isErr())) {
820
0
    return false;
821
0
  }
822
335k
  this->mData[oldLen] = aChar;
823
335k
  FinishBulkWriteImpl(newLen);
824
335k
  return true;
825
335k
}
nsTSubstring<char16_t>::Append(char16_t, std::nothrow_t const&)
Line
Count
Source
815
45.4M
{
816
45.4M
  size_type oldLen = this->mLength;
817
45.4M
  size_type newLen = oldLen + 1; // Can't overflow
818
45.4M
  auto r = StartBulkWriteImpl(newLen, oldLen, false);
819
45.4M
  if (MOZ_UNLIKELY(r.isErr())) {
820
0
    return false;
821
0
  }
822
45.4M
  this->mData[oldLen] = aChar;
823
45.4M
  FinishBulkWriteImpl(newLen);
824
45.4M
  return true;
825
45.4M
}
826
827
template <typename T>
828
void
829
nsTSubstring<T>::Append(const char_type* aData, size_type aLength)
830
3.88M
{
831
3.88M
  if (MOZ_UNLIKELY(!Append(aData, aLength, mozilla::fallible))) {
832
0
    AllocFailed(this->mLength + (aLength == size_type(-1)
833
0
                                 ? char_traits::length(aData)
834
0
                                 : aLength));
835
0
  }
836
3.88M
}
nsTSubstring<char>::Append(char const*, unsigned int)
Line
Count
Source
830
3.88M
{
831
3.88M
  if (MOZ_UNLIKELY(!Append(aData, aLength, mozilla::fallible))) {
832
0
    AllocFailed(this->mLength + (aLength == size_type(-1)
833
0
                                 ? char_traits::length(aData)
834
0
                                 : aLength));
835
0
  }
836
3.88M
}
nsTSubstring<char16_t>::Append(char16_t const*, unsigned int)
Line
Count
Source
830
321
{
831
321
  if (MOZ_UNLIKELY(!Append(aData, aLength, mozilla::fallible))) {
832
0
    AllocFailed(this->mLength + (aLength == size_type(-1)
833
0
                                 ? char_traits::length(aData)
834
0
                                 : aLength));
835
0
  }
836
321
}
837
838
template <typename T>
839
bool
840
nsTSubstring<T>::Append(const char_type* aData, size_type aLength,
841
                        const fallible_t& aFallible)
842
5.11M
{
843
5.11M
  if (MOZ_UNLIKELY(aLength == size_type(-1))) {
844
3.86M
    aLength = char_traits::length(aData);
845
3.86M
  }
846
5.11M
847
5.11M
  if (MOZ_UNLIKELY(!aLength)) {
848
381k
    // Avoid undoing the effect of SetCapacity() if both
849
381k
    // mLength and aLength are zero.
850
381k
    return true;
851
381k
  }
852
4.72M
853
4.72M
  if (MOZ_UNLIKELY(this->IsDependentOn(aData, aData + aLength))) {
854
0
    return Append(string_type(aData, aLength), mozilla::fallible);
855
0
  }
856
4.72M
  size_type oldLen = this->mLength;
857
4.72M
  mozilla::CheckedInt<size_type> newLen(oldLen);
858
4.72M
  newLen += aLength;
859
4.72M
  if (MOZ_UNLIKELY(!newLen.isValid())) {
860
0
    return false;
861
0
  }
862
4.72M
  auto r = StartBulkWriteImpl(newLen.value(), oldLen, false);
863
4.72M
  if (MOZ_UNLIKELY(r.isErr())) {
864
0
    return false;
865
0
  }
866
4.72M
  char_traits::copy(this->mData + oldLen, aData, aLength);
867
4.72M
  FinishBulkWriteImpl(newLen.value());
868
4.72M
  return true;
869
4.72M
}
nsTSubstring<char>::Append(char const*, unsigned int, std::nothrow_t const&)
Line
Count
Source
842
5.09M
{
843
5.09M
  if (MOZ_UNLIKELY(aLength == size_type(-1))) {
844
3.86M
    aLength = char_traits::length(aData);
845
3.86M
  }
846
5.09M
847
5.09M
  if (MOZ_UNLIKELY(!aLength)) {
848
378k
    // Avoid undoing the effect of SetCapacity() if both
849
378k
    // mLength and aLength are zero.
850
378k
    return true;
851
378k
  }
852
4.71M
853
4.71M
  if (MOZ_UNLIKELY(this->IsDependentOn(aData, aData + aLength))) {
854
0
    return Append(string_type(aData, aLength), mozilla::fallible);
855
0
  }
856
4.71M
  size_type oldLen = this->mLength;
857
4.71M
  mozilla::CheckedInt<size_type> newLen(oldLen);
858
4.71M
  newLen += aLength;
859
4.71M
  if (MOZ_UNLIKELY(!newLen.isValid())) {
860
0
    return false;
861
0
  }
862
4.71M
  auto r = StartBulkWriteImpl(newLen.value(), oldLen, false);
863
4.71M
  if (MOZ_UNLIKELY(r.isErr())) {
864
0
    return false;
865
0
  }
866
4.71M
  char_traits::copy(this->mData + oldLen, aData, aLength);
867
4.71M
  FinishBulkWriteImpl(newLen.value());
868
4.71M
  return true;
869
4.71M
}
nsTSubstring<char16_t>::Append(char16_t const*, unsigned int, std::nothrow_t const&)
Line
Count
Source
842
18.7k
{
843
18.7k
  if (MOZ_UNLIKELY(aLength == size_type(-1))) {
844
285
    aLength = char_traits::length(aData);
845
285
  }
846
18.7k
847
18.7k
  if (MOZ_UNLIKELY(!aLength)) {
848
2.61k
    // Avoid undoing the effect of SetCapacity() if both
849
2.61k
    // mLength and aLength are zero.
850
2.61k
    return true;
851
2.61k
  }
852
16.1k
853
16.1k
  if (MOZ_UNLIKELY(this->IsDependentOn(aData, aData + aLength))) {
854
0
    return Append(string_type(aData, aLength), mozilla::fallible);
855
0
  }
856
16.1k
  size_type oldLen = this->mLength;
857
16.1k
  mozilla::CheckedInt<size_type> newLen(oldLen);
858
16.1k
  newLen += aLength;
859
16.1k
  if (MOZ_UNLIKELY(!newLen.isValid())) {
860
0
    return false;
861
0
  }
862
16.1k
  auto r = StartBulkWriteImpl(newLen.value(), oldLen, false);
863
16.1k
  if (MOZ_UNLIKELY(r.isErr())) {
864
0
    return false;
865
0
  }
866
16.1k
  char_traits::copy(this->mData + oldLen, aData, aLength);
867
16.1k
  FinishBulkWriteImpl(newLen.value());
868
16.1k
  return true;
869
16.1k
}
870
871
template <typename T>
872
void
873
nsTSubstring<T>::AppendASCII(const char* aData, size_type aLength)
874
37.1k
{
875
37.1k
  if (MOZ_UNLIKELY(!AppendASCII(aData, aLength, mozilla::fallible))) {
876
0
    AllocFailed(this->mLength +
877
0
                (aLength == size_type(-1) ? strlen(aData) : aLength));
878
0
  }
879
37.1k
}
nsTSubstring<char>::AppendASCII(char const*, unsigned int)
Line
Count
Source
874
28.3k
{
875
28.3k
  if (MOZ_UNLIKELY(!AppendASCII(aData, aLength, mozilla::fallible))) {
876
0
    AllocFailed(this->mLength +
877
0
                (aLength == size_type(-1) ? strlen(aData) : aLength));
878
0
  }
879
28.3k
}
nsTSubstring<char16_t>::AppendASCII(char const*, unsigned int)
Line
Count
Source
874
8.72k
{
875
8.72k
  if (MOZ_UNLIKELY(!AppendASCII(aData, aLength, mozilla::fallible))) {
876
0
    AllocFailed(this->mLength +
877
0
                (aLength == size_type(-1) ? strlen(aData) : aLength));
878
0
  }
879
8.72k
}
880
881
template <typename T>
882
bool
883
nsTSubstring<T>::AppendASCII(const char* aData,
884
                             const fallible_t& aFallible)
885
0
{
886
0
  return AppendASCII(aData, size_type(-1), aFallible);
887
0
}
Unexecuted instantiation: nsTSubstring<char>::AppendASCII(char const*, std::nothrow_t const&)
Unexecuted instantiation: nsTSubstring<char16_t>::AppendASCII(char const*, std::nothrow_t const&)
888
889
template <typename T>
890
bool
891
nsTSubstring<T>::AppendASCII(const char* aData, size_type aLength,
892
                             const fallible_t& aFallible)
893
37.1k
{
894
37.1k
  if (MOZ_UNLIKELY(aLength == size_type(-1))) {
895
0
    aLength = strlen(aData);
896
0
  }
897
37.1k
898
37.1k
  if (MOZ_UNLIKELY(!aLength)) {
899
0
    // Avoid undoing the effect of SetCapacity() if both
900
0
    // mLength and aLength are zero.
901
0
    return true;
902
0
  }
903
37.1k
904
#ifdef CharT_is_char
905
  // 16-bit string can't depend on an 8-bit buffer
906
  if (MOZ_UNLIKELY(this->IsDependentOn(aData, aData + aLength))) {
907
    return Append(string_type(aData, aLength), mozilla::fallible);
908
  }
909
#endif
910
37.1k
  size_type oldLen = this->mLength;
911
37.1k
  mozilla::CheckedInt<size_type> newLen(oldLen);
912
37.1k
  newLen += aLength;
913
37.1k
  if (MOZ_UNLIKELY(!newLen.isValid())) {
914
0
    return false;
915
0
  }
916
37.1k
  auto r = StartBulkWriteImpl(newLen.value(), oldLen, false);
917
37.1k
  if (MOZ_UNLIKELY(r.isErr())) {
918
0
    return false;
919
0
  }
920
37.1k
  char_traits::copyASCII(this->mData + oldLen, aData, aLength);
921
37.1k
  FinishBulkWriteImpl(newLen.value());
922
37.1k
  return true;
923
37.1k
}
nsTSubstring<char>::AppendASCII(char const*, unsigned int, std::nothrow_t const&)
Line
Count
Source
893
28.3k
{
894
28.3k
  if (MOZ_UNLIKELY(aLength == size_type(-1))) {
895
0
    aLength = strlen(aData);
896
0
  }
897
28.3k
898
28.3k
  if (MOZ_UNLIKELY(!aLength)) {
899
0
    // Avoid undoing the effect of SetCapacity() if both
900
0
    // mLength and aLength are zero.
901
0
    return true;
902
0
  }
903
28.3k
904
#ifdef CharT_is_char
905
  // 16-bit string can't depend on an 8-bit buffer
906
  if (MOZ_UNLIKELY(this->IsDependentOn(aData, aData + aLength))) {
907
    return Append(string_type(aData, aLength), mozilla::fallible);
908
  }
909
#endif
910
28.3k
  size_type oldLen = this->mLength;
911
28.3k
  mozilla::CheckedInt<size_type> newLen(oldLen);
912
28.3k
  newLen += aLength;
913
28.3k
  if (MOZ_UNLIKELY(!newLen.isValid())) {
914
0
    return false;
915
0
  }
916
28.3k
  auto r = StartBulkWriteImpl(newLen.value(), oldLen, false);
917
28.3k
  if (MOZ_UNLIKELY(r.isErr())) {
918
0
    return false;
919
0
  }
920
28.3k
  char_traits::copyASCII(this->mData + oldLen, aData, aLength);
921
28.3k
  FinishBulkWriteImpl(newLen.value());
922
28.3k
  return true;
923
28.3k
}
nsTSubstring<char16_t>::AppendASCII(char const*, unsigned int, std::nothrow_t const&)
Line
Count
Source
893
8.72k
{
894
8.72k
  if (MOZ_UNLIKELY(aLength == size_type(-1))) {
895
0
    aLength = strlen(aData);
896
0
  }
897
8.72k
898
8.72k
  if (MOZ_UNLIKELY(!aLength)) {
899
0
    // Avoid undoing the effect of SetCapacity() if both
900
0
    // mLength and aLength are zero.
901
0
    return true;
902
0
  }
903
8.72k
904
#ifdef CharT_is_char
905
  // 16-bit string can't depend on an 8-bit buffer
906
  if (MOZ_UNLIKELY(this->IsDependentOn(aData, aData + aLength))) {
907
    return Append(string_type(aData, aLength), mozilla::fallible);
908
  }
909
#endif
910
8.72k
  size_type oldLen = this->mLength;
911
8.72k
  mozilla::CheckedInt<size_type> newLen(oldLen);
912
8.72k
  newLen += aLength;
913
8.72k
  if (MOZ_UNLIKELY(!newLen.isValid())) {
914
0
    return false;
915
0
  }
916
8.72k
  auto r = StartBulkWriteImpl(newLen.value(), oldLen, false);
917
8.72k
  if (MOZ_UNLIKELY(r.isErr())) {
918
0
    return false;
919
0
  }
920
8.72k
  char_traits::copyASCII(this->mData + oldLen, aData, aLength);
921
8.72k
  FinishBulkWriteImpl(newLen.value());
922
8.72k
  return true;
923
8.72k
}
924
925
template <typename T>
926
void
927
nsTSubstring<T>::Append(const self_type& aStr)
928
137k
{
929
137k
  if (MOZ_UNLIKELY(!Append(aStr, mozilla::fallible))) {
930
0
    AllocFailed(this->mLength + aStr.Length());
931
0
  }
932
137k
}
nsTSubstring<char>::Append(nsTSubstring<char> const&)
Line
Count
Source
928
78.4k
{
929
78.4k
  if (MOZ_UNLIKELY(!Append(aStr, mozilla::fallible))) {
930
0
    AllocFailed(this->mLength + aStr.Length());
931
0
  }
932
78.4k
}
nsTSubstring<char16_t>::Append(nsTSubstring<char16_t> const&)
Line
Count
Source
928
58.7k
{
929
58.7k
  if (MOZ_UNLIKELY(!Append(aStr, mozilla::fallible))) {
930
0
    AllocFailed(this->mLength + aStr.Length());
931
0
  }
932
58.7k
}
933
934
template <typename T>
935
bool
936
nsTSubstring<T>::Append(const self_type& aStr, const fallible_t& aFallible)
937
137k
{
938
137k
  // Check refcounted to avoid undoing the effects of SetCapacity().
939
137k
  if (MOZ_UNLIKELY(!this->mLength && !(this->mDataFlags & DataFlags::REFCOUNTED))) {
940
70.0k
    return Assign(aStr, mozilla::fallible);
941
70.0k
  }
942
67.2k
  return Append(aStr.BeginReading(), aStr.Length(), mozilla::fallible);
943
67.2k
}
nsTSubstring<char>::Append(nsTSubstring<char> const&, std::nothrow_t const&)
Line
Count
Source
937
78.4k
{
938
78.4k
  // Check refcounted to avoid undoing the effects of SetCapacity().
939
78.4k
  if (MOZ_UNLIKELY(!this->mLength && !(this->mDataFlags & DataFlags::REFCOUNTED))) {
940
29.6k
    return Assign(aStr, mozilla::fallible);
941
29.6k
  }
942
48.8k
  return Append(aStr.BeginReading(), aStr.Length(), mozilla::fallible);
943
48.8k
}
nsTSubstring<char16_t>::Append(nsTSubstring<char16_t> const&, std::nothrow_t const&)
Line
Count
Source
937
58.7k
{
938
58.7k
  // Check refcounted to avoid undoing the effects of SetCapacity().
939
58.7k
  if (MOZ_UNLIKELY(!this->mLength && !(this->mDataFlags & DataFlags::REFCOUNTED))) {
940
40.3k
    return Assign(aStr, mozilla::fallible);
941
40.3k
  }
942
18.4k
  return Append(aStr.BeginReading(), aStr.Length(), mozilla::fallible);
943
18.4k
}
944
945
template <typename T>
946
void
947
nsTSubstring<T>::Append(const substring_tuple_type& aTuple)
948
1.56k
{
949
1.56k
  if (MOZ_UNLIKELY(!Append(aTuple, mozilla::fallible))) {
950
0
    AllocFailed(this->mLength + aTuple.Length());
951
0
  }
952
1.56k
}
nsTSubstring<char>::Append(nsTSubstringTuple<char> const&)
Line
Count
Source
948
1.56k
{
949
1.56k
  if (MOZ_UNLIKELY(!Append(aTuple, mozilla::fallible))) {
950
0
    AllocFailed(this->mLength + aTuple.Length());
951
0
  }
952
1.56k
}
Unexecuted instantiation: nsTSubstring<char16_t>::Append(nsTSubstringTuple<char16_t> const&)
953
954
template <typename T>
955
bool
956
nsTSubstring<T>::Append(const substring_tuple_type& aTuple,
957
                        const fallible_t& aFallible)
958
1.56k
{
959
1.56k
  size_type tupleLength = aTuple.Length();
960
1.56k
961
1.56k
  if (MOZ_UNLIKELY(!tupleLength)) {
962
0
    // Avoid undoing the effect of SetCapacity() if both
963
0
    // mLength and tupleLength are zero.
964
0
    return true;
965
0
  }
966
1.56k
967
1.56k
  if (MOZ_UNLIKELY(aTuple.IsDependentOn(this->mData, this->mData + this->mLength))) {
968
0
    return Append(string_type(aTuple), aFallible);
969
0
  }
970
1.56k
971
1.56k
  size_type oldLen = this->mLength;
972
1.56k
  mozilla::CheckedInt<size_type> newLen(oldLen);
973
1.56k
  newLen += tupleLength;
974
1.56k
  if (MOZ_UNLIKELY(!newLen.isValid())) {
975
0
    return false;
976
0
  }
977
1.56k
  auto r = StartBulkWriteImpl(newLen.value(), oldLen, false);
978
1.56k
  if (MOZ_UNLIKELY(r.isErr())) {
979
0
    return false;
980
0
  }
981
1.56k
  aTuple.WriteTo(this->mData + oldLen, tupleLength);
982
1.56k
  FinishBulkWriteImpl(newLen.value());
983
1.56k
  return true;
984
1.56k
}
nsTSubstring<char>::Append(nsTSubstringTuple<char> const&, std::nothrow_t const&)
Line
Count
Source
958
1.56k
{
959
1.56k
  size_type tupleLength = aTuple.Length();
960
1.56k
961
1.56k
  if (MOZ_UNLIKELY(!tupleLength)) {
962
0
    // Avoid undoing the effect of SetCapacity() if both
963
0
    // mLength and tupleLength are zero.
964
0
    return true;
965
0
  }
966
1.56k
967
1.56k
  if (MOZ_UNLIKELY(aTuple.IsDependentOn(this->mData, this->mData + this->mLength))) {
968
0
    return Append(string_type(aTuple), aFallible);
969
0
  }
970
1.56k
971
1.56k
  size_type oldLen = this->mLength;
972
1.56k
  mozilla::CheckedInt<size_type> newLen(oldLen);
973
1.56k
  newLen += tupleLength;
974
1.56k
  if (MOZ_UNLIKELY(!newLen.isValid())) {
975
0
    return false;
976
0
  }
977
1.56k
  auto r = StartBulkWriteImpl(newLen.value(), oldLen, false);
978
1.56k
  if (MOZ_UNLIKELY(r.isErr())) {
979
0
    return false;
980
0
  }
981
1.56k
  aTuple.WriteTo(this->mData + oldLen, tupleLength);
982
1.56k
  FinishBulkWriteImpl(newLen.value());
983
1.56k
  return true;
984
1.56k
}
Unexecuted instantiation: nsTSubstring<char16_t>::Append(nsTSubstringTuple<char16_t> const&, std::nothrow_t const&)
985
986
template <typename T>
987
void
988
nsTSubstring<T>::SetCapacity(size_type aCapacity)
989
2.57k
{
990
2.57k
  if (!SetCapacity(aCapacity, mozilla::fallible)) {
991
0
    AllocFailed(aCapacity);
992
0
  }
993
2.57k
}
nsTSubstring<char>::SetCapacity(unsigned int)
Line
Count
Source
989
2.57k
{
990
2.57k
  if (!SetCapacity(aCapacity, mozilla::fallible)) {
991
0
    AllocFailed(aCapacity);
992
0
  }
993
2.57k
}
Unexecuted instantiation: nsTSubstring<char16_t>::SetCapacity(unsigned int)
994
995
template <typename T>
996
bool
997
nsTSubstring<T>::SetCapacity(size_type aCapacity, const fallible_t&)
998
2.57k
{
999
2.57k
  size_type length = this->mLength;
1000
2.57k
  // This method can no longer be used to shorten the
1001
2.57k
  // logical length.
1002
2.57k
  size_type capacity = XPCOM_MAX(aCapacity, length);
1003
2.57k
1004
2.57k
  mozilla::Result<uint32_t, nsresult> r =
1005
2.57k
    StartBulkWriteImpl(capacity, length, true);
1006
2.57k
  if (r.isErr()) {
1007
0
    return false;
1008
0
  }
1009
2.57k
1010
2.57k
  if (MOZ_UNLIKELY(!capacity)) {
1011
0
    // Zero capacity was requested on a zero-length
1012
0
    // string. In this special case, we are pointing
1013
0
    // to the special empty buffer, which is already
1014
0
    // zero-terminated and not writable, so we must
1015
0
    // not attempt to zero-terminate it.
1016
0
    AssertValid();
1017
0
    return true;
1018
0
  }
1019
2.57k
1020
2.57k
  // FinishBulkWriteImpl with argument zero releases
1021
2.57k
  // the heap-allocated buffer. However, SetCapacity()
1022
2.57k
  // is a special case that allows mLength to be zero
1023
2.57k
  // while a heap-allocated buffer exists.
1024
2.57k
  // By calling FinishBulkWriteImplImpl, we skip the
1025
2.57k
  // zero case handling that's inappropriate in the
1026
2.57k
  // SetCapacity() case.
1027
2.57k
  FinishBulkWriteImplImpl(length);
1028
2.57k
  return true;
1029
2.57k
}
nsTSubstring<char>::SetCapacity(unsigned int, std::nothrow_t const&)
Line
Count
Source
998
2.57k
{
999
2.57k
  size_type length = this->mLength;
1000
2.57k
  // This method can no longer be used to shorten the
1001
2.57k
  // logical length.
1002
2.57k
  size_type capacity = XPCOM_MAX(aCapacity, length);
1003
2.57k
1004
2.57k
  mozilla::Result<uint32_t, nsresult> r =
1005
2.57k
    StartBulkWriteImpl(capacity, length, true);
1006
2.57k
  if (r.isErr()) {
1007
0
    return false;
1008
0
  }
1009
2.57k
1010
2.57k
  if (MOZ_UNLIKELY(!capacity)) {
1011
0
    // Zero capacity was requested on a zero-length
1012
0
    // string. In this special case, we are pointing
1013
0
    // to the special empty buffer, which is already
1014
0
    // zero-terminated and not writable, so we must
1015
0
    // not attempt to zero-terminate it.
1016
0
    AssertValid();
1017
0
    return true;
1018
0
  }
1019
2.57k
1020
2.57k
  // FinishBulkWriteImpl with argument zero releases
1021
2.57k
  // the heap-allocated buffer. However, SetCapacity()
1022
2.57k
  // is a special case that allows mLength to be zero
1023
2.57k
  // while a heap-allocated buffer exists.
1024
2.57k
  // By calling FinishBulkWriteImplImpl, we skip the
1025
2.57k
  // zero case handling that's inappropriate in the
1026
2.57k
  // SetCapacity() case.
1027
2.57k
  FinishBulkWriteImplImpl(length);
1028
2.57k
  return true;
1029
2.57k
}
Unexecuted instantiation: nsTSubstring<char16_t>::SetCapacity(unsigned int, std::nothrow_t const&)
1030
1031
template <typename T>
1032
void
1033
nsTSubstring<T>::SetLength(size_type aLength)
1034
4.54M
{
1035
4.54M
  if (!SetLength(aLength, mozilla::fallible)) {
1036
0
    AllocFailed(aLength);
1037
0
  }
1038
4.54M
}
nsTSubstring<char>::SetLength(unsigned int)
Line
Count
Source
1034
4.37M
{
1035
4.37M
  if (!SetLength(aLength, mozilla::fallible)) {
1036
0
    AllocFailed(aLength);
1037
0
  }
1038
4.37M
}
nsTSubstring<char16_t>::SetLength(unsigned int)
Line
Count
Source
1034
176k
{
1035
176k
  if (!SetLength(aLength, mozilla::fallible)) {
1036
0
    AllocFailed(aLength);
1037
0
  }
1038
176k
}
1039
1040
template <typename T>
1041
bool
1042
nsTSubstring<T>::SetLength(size_type aLength, const fallible_t& aFallible)
1043
8.04M
{
1044
8.04M
  size_type preserve = XPCOM_MIN(aLength, this->mLength);
1045
8.04M
  mozilla::Result<uint32_t, nsresult> r =
1046
8.04M
    StartBulkWriteImpl(aLength, preserve, true);
1047
8.04M
  if (r.isErr()) {
1048
0
    return false;
1049
0
  }
1050
8.04M
1051
8.04M
  FinishBulkWriteImpl(aLength);
1052
8.04M
1053
8.04M
  return true;
1054
8.04M
}
nsTSubstring<char>::SetLength(unsigned int, std::nothrow_t const&)
Line
Count
Source
1043
7.67M
{
1044
7.67M
  size_type preserve = XPCOM_MIN(aLength, this->mLength);
1045
7.67M
  mozilla::Result<uint32_t, nsresult> r =
1046
7.67M
    StartBulkWriteImpl(aLength, preserve, true);
1047
7.67M
  if (r.isErr()) {
1048
0
    return false;
1049
0
  }
1050
7.67M
1051
7.67M
  FinishBulkWriteImpl(aLength);
1052
7.67M
1053
7.67M
  return true;
1054
7.67M
}
nsTSubstring<char16_t>::SetLength(unsigned int, std::nothrow_t const&)
Line
Count
Source
1043
366k
{
1044
366k
  size_type preserve = XPCOM_MIN(aLength, this->mLength);
1045
366k
  mozilla::Result<uint32_t, nsresult> r =
1046
366k
    StartBulkWriteImpl(aLength, preserve, true);
1047
366k
  if (r.isErr()) {
1048
0
    return false;
1049
0
  }
1050
366k
1051
366k
  FinishBulkWriteImpl(aLength);
1052
366k
1053
366k
  return true;
1054
366k
}
1055
1056
template<typename T>
1057
void
1058
nsTSubstring<T>::Truncate()
1059
53.6M
{
1060
53.6M
  ::ReleaseData(this->mData, this->mDataFlags);
1061
53.6M
  SetToEmptyBuffer();
1062
53.6M
  AssertValid();
1063
53.6M
}
nsTSubstring<char>::Truncate()
Line
Count
Source
1059
45.2M
{
1060
45.2M
  ::ReleaseData(this->mData, this->mDataFlags);
1061
45.2M
  SetToEmptyBuffer();
1062
45.2M
  AssertValid();
1063
45.2M
}
nsTSubstring<char16_t>::Truncate()
Line
Count
Source
1059
8.41M
{
1060
8.41M
  ::ReleaseData(this->mData, this->mDataFlags);
1061
8.41M
  SetToEmptyBuffer();
1062
8.41M
  AssertValid();
1063
8.41M
}
1064
1065
template <typename T>
1066
void
1067
nsTSubstring<T>::SetIsVoid(bool aVal)
1068
1.20k
{
1069
1.20k
  if (aVal) {
1070
1.20k
    Truncate();
1071
1.20k
    this->mDataFlags |= DataFlags::VOIDED;
1072
1.20k
  } else {
1073
0
    this->mDataFlags &= ~DataFlags::VOIDED;
1074
0
  }
1075
1.20k
}
nsTSubstring<char>::SetIsVoid(bool)
Line
Count
Source
1068
1.20k
{
1069
1.20k
  if (aVal) {
1070
1.20k
    Truncate();
1071
1.20k
    this->mDataFlags |= DataFlags::VOIDED;
1072
1.20k
  } else {
1073
0
    this->mDataFlags &= ~DataFlags::VOIDED;
1074
0
  }
1075
1.20k
}
Unexecuted instantiation: nsTSubstring<char16_t>::SetIsVoid(bool)
1076
1077
namespace mozilla {
1078
namespace detail {
1079
1080
template <typename T>
1081
typename nsTStringRepr<T>::char_type
1082
nsTStringRepr<T>::First() const
1083
1.27M
{
1084
1.27M
  MOZ_RELEASE_ASSERT(this->mLength > 0, "|First()| called on an empty string");
1085
1.27M
  return this->mData[0];
1086
1.27M
}
mozilla::detail::nsTStringRepr<char>::First() const
Line
Count
Source
1083
1.07M
{
1084
1.07M
  MOZ_RELEASE_ASSERT(this->mLength > 0, "|First()| called on an empty string");
1085
1.07M
  return this->mData[0];
1086
1.07M
}
mozilla::detail::nsTStringRepr<char16_t>::First() const
Line
Count
Source
1083
203k
{
1084
203k
  MOZ_RELEASE_ASSERT(this->mLength > 0, "|First()| called on an empty string");
1085
203k
  return this->mData[0];
1086
203k
}
1087
1088
template <typename T>
1089
typename nsTStringRepr<T>::char_type
1090
nsTStringRepr<T>::Last() const
1091
8.59k
{
1092
8.59k
  MOZ_RELEASE_ASSERT(this->mLength > 0, "|Last()| called on an empty string");
1093
8.59k
  return this->mData[this->mLength - 1];
1094
8.59k
}
mozilla::detail::nsTStringRepr<char>::Last() const
Line
Count
Source
1091
2.50k
{
1092
2.50k
  MOZ_RELEASE_ASSERT(this->mLength > 0, "|Last()| called on an empty string");
1093
2.50k
  return this->mData[this->mLength - 1];
1094
2.50k
}
mozilla::detail::nsTStringRepr<char16_t>::Last() const
Line
Count
Source
1091
6.08k
{
1092
6.08k
  MOZ_RELEASE_ASSERT(this->mLength > 0, "|Last()| called on an empty string");
1093
6.08k
  return this->mData[this->mLength - 1];
1094
6.08k
}
1095
1096
template <typename T>
1097
bool
1098
nsTStringRepr<T>::Equals(const self_type& aStr) const
1099
5.14M
{
1100
5.14M
  return this->mLength == aStr.mLength &&
1101
5.14M
         char_traits::compare(this->mData, aStr.mData, this->mLength) == 0;
1102
5.14M
}
mozilla::detail::nsTStringRepr<char>::Equals(mozilla::detail::nsTStringRepr<char> const&) const
Line
Count
Source
1099
3.73M
{
1100
3.73M
  return this->mLength == aStr.mLength &&
1101
3.73M
         char_traits::compare(this->mData, aStr.mData, this->mLength) == 0;
1102
3.73M
}
mozilla::detail::nsTStringRepr<char16_t>::Equals(mozilla::detail::nsTStringRepr<char16_t> const&) const
Line
Count
Source
1099
1.41M
{
1100
1.41M
  return this->mLength == aStr.mLength &&
1101
1.41M
         char_traits::compare(this->mData, aStr.mData, this->mLength) == 0;
1102
1.41M
}
1103
1104
template <typename T>
1105
bool
1106
nsTStringRepr<T>::Equals(const self_type& aStr,
1107
                         const comparator_type& aComp) const
1108
22.9k
{
1109
22.9k
  return this->mLength == aStr.mLength &&
1110
22.9k
         aComp(this->mData, aStr.mData, this->mLength, aStr.mLength) == 0;
1111
22.9k
}
mozilla::detail::nsTStringRepr<char>::Equals(mozilla::detail::nsTStringRepr<char> const&, nsTStringComparator<char> const&) const
Line
Count
Source
1108
6
{
1109
6
  return this->mLength == aStr.mLength &&
1110
6
         aComp(this->mData, aStr.mData, this->mLength, aStr.mLength) == 0;
1111
6
}
mozilla::detail::nsTStringRepr<char16_t>::Equals(mozilla::detail::nsTStringRepr<char16_t> const&, nsTStringComparator<char16_t> const&) const
Line
Count
Source
1108
22.9k
{
1109
22.9k
  return this->mLength == aStr.mLength &&
1110
22.9k
         aComp(this->mData, aStr.mData, this->mLength, aStr.mLength) == 0;
1111
22.9k
}
1112
1113
template <typename T>
1114
bool
1115
nsTStringRepr<T>::Equals(const substring_tuple_type& aTuple) const
1116
0
{
1117
0
  return Equals(substring_type(aTuple));
1118
0
}
Unexecuted instantiation: mozilla::detail::nsTStringRepr<char>::Equals(nsTSubstringTuple<char> const&) const
Unexecuted instantiation: mozilla::detail::nsTStringRepr<char16_t>::Equals(nsTSubstringTuple<char16_t> const&) const
1119
1120
template <typename T>
1121
bool
1122
nsTStringRepr<T>::Equals(const substring_tuple_type& aTuple,
1123
                         const comparator_type& aComp) const
1124
0
{
1125
0
  return Equals(substring_type(aTuple), aComp);
1126
0
}
Unexecuted instantiation: mozilla::detail::nsTStringRepr<char>::Equals(nsTSubstringTuple<char> const&, nsTStringComparator<char> const&) const
Unexecuted instantiation: mozilla::detail::nsTStringRepr<char16_t>::Equals(nsTSubstringTuple<char16_t> const&, nsTStringComparator<char16_t> const&) const
1127
1128
template <typename T>
1129
bool
1130
nsTStringRepr<T>::Equals(const char_type* aData) const
1131
5.80M
{
1132
5.80M
  // unfortunately, some callers pass null :-(
1133
5.80M
  if (!aData) {
1134
0
    MOZ_ASSERT_UNREACHABLE("null data pointer");
1135
0
    return this->mLength == 0;
1136
0
  }
1137
5.80M
1138
5.80M
  // XXX avoid length calculation?
1139
5.80M
  size_type length = char_traits::length(aData);
1140
5.80M
  return this->mLength == length &&
1141
5.80M
         char_traits::compare(this->mData, aData, this->mLength) == 0;
1142
5.80M
}
mozilla::detail::nsTStringRepr<char>::Equals(char const*) const
Line
Count
Source
1131
5.80M
{
1132
5.80M
  // unfortunately, some callers pass null :-(
1133
5.80M
  if (!aData) {
1134
0
    MOZ_ASSERT_UNREACHABLE("null data pointer");
1135
0
    return this->mLength == 0;
1136
0
  }
1137
5.80M
1138
5.80M
  // XXX avoid length calculation?
1139
5.80M
  size_type length = char_traits::length(aData);
1140
5.80M
  return this->mLength == length &&
1141
5.80M
         char_traits::compare(this->mData, aData, this->mLength) == 0;
1142
5.80M
}
mozilla::detail::nsTStringRepr<char16_t>::Equals(char16_t const*) const
Line
Count
Source
1131
2.46k
{
1132
2.46k
  // unfortunately, some callers pass null :-(
1133
2.46k
  if (!aData) {
1134
0
    MOZ_ASSERT_UNREACHABLE("null data pointer");
1135
0
    return this->mLength == 0;
1136
0
  }
1137
2.46k
1138
2.46k
  // XXX avoid length calculation?
1139
2.46k
  size_type length = char_traits::length(aData);
1140
2.46k
  return this->mLength == length &&
1141
2.46k
         char_traits::compare(this->mData, aData, this->mLength) == 0;
1142
2.46k
}
1143
1144
template <typename T>
1145
bool
1146
nsTStringRepr<T>::Equals(const char_type* aData,
1147
                         const comparator_type& aComp) const
1148
0
{
1149
0
  // unfortunately, some callers pass null :-(
1150
0
  if (!aData) {
1151
0
    MOZ_ASSERT_UNREACHABLE("null data pointer");
1152
0
    return this->mLength == 0;
1153
0
  }
1154
0
1155
0
  // XXX avoid length calculation?
1156
0
  size_type length = char_traits::length(aData);
1157
0
  return this->mLength == length && aComp(this->mData, aData, this->mLength, length) == 0;
1158
0
}
Unexecuted instantiation: mozilla::detail::nsTStringRepr<char>::Equals(char const*, nsTStringComparator<char> const&) const
Unexecuted instantiation: mozilla::detail::nsTStringRepr<char16_t>::Equals(char16_t const*, nsTStringComparator<char16_t> const&) const
1159
1160
template <typename T>
1161
bool
1162
nsTStringRepr<T>::EqualsASCII(const char* aData, size_type aLen) const
1163
13.0k
{
1164
13.0k
  return this->mLength == aLen &&
1165
13.0k
         char_traits::compareASCII(this->mData, aData, aLen) == 0;
1166
13.0k
}
mozilla::detail::nsTStringRepr<char>::EqualsASCII(char const*, unsigned int) const
Line
Count
Source
1163
13.0k
{
1164
13.0k
  return this->mLength == aLen &&
1165
13.0k
         char_traits::compareASCII(this->mData, aData, aLen) == 0;
1166
13.0k
}
Unexecuted instantiation: mozilla::detail::nsTStringRepr<char16_t>::EqualsASCII(char const*, unsigned int) const
1167
1168
template <typename T>
1169
bool
1170
nsTStringRepr<T>::EqualsASCII(const char* aData) const
1171
433k
{
1172
433k
  return char_traits::compareASCIINullTerminated(this->mData, this->mLength, aData) == 0;
1173
433k
}
mozilla::detail::nsTStringRepr<char>::EqualsASCII(char const*) const
Line
Count
Source
1171
866
{
1172
866
  return char_traits::compareASCIINullTerminated(this->mData, this->mLength, aData) == 0;
1173
866
}
mozilla::detail::nsTStringRepr<char16_t>::EqualsASCII(char const*) const
Line
Count
Source
1171
432k
{
1172
432k
  return char_traits::compareASCIINullTerminated(this->mData, this->mLength, aData) == 0;
1173
432k
}
1174
1175
template <typename T>
1176
bool
1177
nsTStringRepr<T>::LowerCaseEqualsASCII(const char* aData,
1178
                                       size_type aLen) const
1179
14.0M
{
1180
14.0M
  return this->mLength == aLen &&
1181
14.0M
         char_traits::compareLowerCaseToASCII(this->mData, aData, aLen) == 0;
1182
14.0M
}
mozilla::detail::nsTStringRepr<char>::LowerCaseEqualsASCII(char const*, unsigned int) const
Line
Count
Source
1179
14.0M
{
1180
14.0M
  return this->mLength == aLen &&
1181
14.0M
         char_traits::compareLowerCaseToASCII(this->mData, aData, aLen) == 0;
1182
14.0M
}
Unexecuted instantiation: mozilla::detail::nsTStringRepr<char16_t>::LowerCaseEqualsASCII(char const*, unsigned int) const
1183
1184
template <typename T>
1185
bool
1186
nsTStringRepr<T>::LowerCaseEqualsASCII(const char* aData) const
1187
8.15M
{
1188
8.15M
  return char_traits::compareLowerCaseToASCIINullTerminated(this->mData,
1189
8.15M
                                                            this->mLength,
1190
8.15M
                                                            aData) == 0;
1191
8.15M
}
mozilla::detail::nsTStringRepr<char>::LowerCaseEqualsASCII(char const*) const
Line
Count
Source
1187
81
{
1188
81
  return char_traits::compareLowerCaseToASCIINullTerminated(this->mData,
1189
81
                                                            this->mLength,
1190
81
                                                            aData) == 0;
1191
81
}
mozilla::detail::nsTStringRepr<char16_t>::LowerCaseEqualsASCII(char const*) const
Line
Count
Source
1187
8.15M
{
1188
8.15M
  return char_traits::compareLowerCaseToASCIINullTerminated(this->mData,
1189
8.15M
                                                            this->mLength,
1190
8.15M
                                                            aData) == 0;
1191
8.15M
}
1192
1193
template <typename T>
1194
typename nsTStringRepr<T>::size_type
1195
nsTStringRepr<T>::CountChar(char_type aChar) const
1196
18
{
1197
18
  const char_type* start = this->mData;
1198
18
  const char_type* end   = this->mData + this->mLength;
1199
18
1200
18
  return NS_COUNT(start, end, aChar);
1201
18
}
mozilla::detail::nsTStringRepr<char>::CountChar(char) const
Line
Count
Source
1196
18
{
1197
18
  const char_type* start = this->mData;
1198
18
  const char_type* end   = this->mData + this->mLength;
1199
18
1200
18
  return NS_COUNT(start, end, aChar);
1201
18
}
Unexecuted instantiation: mozilla::detail::nsTStringRepr<char16_t>::CountChar(char16_t) const
1202
1203
template <typename T>
1204
int32_t
1205
nsTStringRepr<T>::FindChar(char_type aChar, index_type aOffset) const
1206
7.97M
{
1207
7.97M
  if (aOffset < this->mLength) {
1208
7.18M
    const char_type* result = char_traits::find(this->mData + aOffset,
1209
7.18M
                                                this->mLength - aOffset, aChar);
1210
7.18M
    if (result) {
1211
1.94M
      return result - this->mData;
1212
1.94M
    }
1213
6.02M
  }
1214
6.02M
  return -1;
1215
6.02M
}
mozilla::detail::nsTStringRepr<char>::FindChar(char, unsigned int) const
Line
Count
Source
1206
7.96M
{
1207
7.96M
  if (aOffset < this->mLength) {
1208
7.17M
    const char_type* result = char_traits::find(this->mData + aOffset,
1209
7.17M
                                                this->mLength - aOffset, aChar);
1210
7.17M
    if (result) {
1211
1.94M
      return result - this->mData;
1212
1.94M
    }
1213
6.02M
  }
1214
6.02M
  return -1;
1215
6.02M
}
mozilla::detail::nsTStringRepr<char16_t>::FindChar(char16_t, unsigned int) const
Line
Count
Source
1206
3.78k
{
1207
3.78k
  if (aOffset < this->mLength) {
1208
3.68k
    const char_type* result = char_traits::find(this->mData + aOffset,
1209
3.68k
                                                this->mLength - aOffset, aChar);
1210
3.68k
    if (result) {
1211
3.01k
      return result - this->mData;
1212
3.01k
    }
1213
769
  }
1214
769
  return -1;
1215
769
}
1216
1217
} // namespace detail
1218
} // namespace mozilla
1219
1220
template <typename T>
1221
void
1222
nsTSubstring<T>::StripChar(char_type aChar)
1223
0
{
1224
0
  if (this->mLength == 0) {
1225
0
    return;
1226
0
  }
1227
0
1228
0
  if (!EnsureMutable()) { // XXX do this lazily?
1229
0
    AllocFailed(this->mLength);
1230
0
  }
1231
0
1232
0
  // XXX(darin): this code should defer writing until necessary.
1233
0
1234
0
  char_type* to   = this->mData;
1235
0
  char_type* from = this->mData;
1236
0
  char_type* end  = this->mData + this->mLength;
1237
0
1238
0
  while (from < end) {
1239
0
    char_type theChar = *from++;
1240
0
    if (aChar != theChar) {
1241
0
      *to++ = theChar;
1242
0
    }
1243
0
  }
1244
0
  *to = char_type(0); // add the null
1245
0
  this->mLength = to - this->mData;
1246
0
}
Unexecuted instantiation: nsTSubstring<char>::StripChar(char)
Unexecuted instantiation: nsTSubstring<char16_t>::StripChar(char16_t)
1247
1248
template <typename T>
1249
void
1250
nsTSubstring<T>::StripChars(const char_type* aChars)
1251
1
{
1252
1
  if (this->mLength == 0) {
1253
0
    return;
1254
0
  }
1255
1
1256
1
  if (!EnsureMutable()) { // XXX do this lazily?
1257
0
    AllocFailed(this->mLength);
1258
0
  }
1259
1
1260
1
  // XXX(darin): this code should defer writing until necessary.
1261
1
1262
1
  char_type* to   = this->mData;
1263
1
  char_type* from = this->mData;
1264
1
  char_type* end  = this->mData + this->mLength;
1265
1
1266
8
  while (from < end) {
1267
7
    char_type theChar = *from++;
1268
7
    const char_type* test = aChars;
1269
7
1270
133
    for (; *test && *test != theChar; ++test);
1271
7
1272
7
    if (!*test) {
1273
7
      // Not stripped, copy this char.
1274
7
      *to++ = theChar;
1275
7
    }
1276
7
  }
1277
1
  *to = char_type(0); // add the null
1278
1
  this->mLength = to - this->mData;
1279
1
}
nsTSubstring<char>::StripChars(char const*)
Line
Count
Source
1251
1
{
1252
1
  if (this->mLength == 0) {
1253
0
    return;
1254
0
  }
1255
1
1256
1
  if (!EnsureMutable()) { // XXX do this lazily?
1257
0
    AllocFailed(this->mLength);
1258
0
  }
1259
1
1260
1
  // XXX(darin): this code should defer writing until necessary.
1261
1
1262
1
  char_type* to   = this->mData;
1263
1
  char_type* from = this->mData;
1264
1
  char_type* end  = this->mData + this->mLength;
1265
1
1266
8
  while (from < end) {
1267
7
    char_type theChar = *from++;
1268
7
    const char_type* test = aChars;
1269
7
1270
133
    for (; *test && *test != theChar; ++test);
1271
7
1272
7
    if (!*test) {
1273
7
      // Not stripped, copy this char.
1274
7
      *to++ = theChar;
1275
7
    }
1276
7
  }
1277
1
  *to = char_type(0); // add the null
1278
1
  this->mLength = to - this->mData;
1279
1
}
Unexecuted instantiation: nsTSubstring<char16_t>::StripChars(char16_t const*)
1280
1281
template <typename T>
1282
void
1283
nsTSubstring<T>::StripTaggedASCII(const ASCIIMaskArray& aToStrip)
1284
3.91M
{
1285
3.91M
  if (this->mLength == 0) {
1286
0
    return;
1287
0
  }
1288
3.91M
1289
3.91M
  if (!EnsureMutable()) {
1290
0
    AllocFailed(this->mLength);
1291
0
  }
1292
3.91M
1293
3.91M
  char_type* to   = this->mData;
1294
3.91M
  char_type* from = this->mData;
1295
3.91M
  char_type* end  = this->mData + this->mLength;
1296
3.91M
1297
19.3M
  while (from < end) {
1298
15.4M
    uint32_t theChar = (uint32_t)*from++;
1299
15.4M
    // Replacing this with a call to ASCIIMask::IsMasked
1300
15.4M
    // regresses performance somewhat, so leaving it inlined.
1301
15.4M
    if (!mozilla::ASCIIMask::IsMasked(aToStrip, theChar)) {
1302
15.4M
      // Not stripped, copy this char.
1303
15.4M
      *to++ = (char_type)theChar;
1304
15.4M
    }
1305
15.4M
  }
1306
3.91M
  *to = char_type(0); // add the null
1307
3.91M
  this->mLength = to - this->mData;
1308
3.91M
}
nsTSubstring<char>::StripTaggedASCII(std::__1::array<bool, 128ul> const&)
Line
Count
Source
1284
3.91M
{
1285
3.91M
  if (this->mLength == 0) {
1286
0
    return;
1287
0
  }
1288
3.91M
1289
3.91M
  if (!EnsureMutable()) {
1290
0
    AllocFailed(this->mLength);
1291
0
  }
1292
3.91M
1293
3.91M
  char_type* to   = this->mData;
1294
3.91M
  char_type* from = this->mData;
1295
3.91M
  char_type* end  = this->mData + this->mLength;
1296
3.91M
1297
19.3M
  while (from < end) {
1298
15.4M
    uint32_t theChar = (uint32_t)*from++;
1299
15.4M
    // Replacing this with a call to ASCIIMask::IsMasked
1300
15.4M
    // regresses performance somewhat, so leaving it inlined.
1301
15.4M
    if (!mozilla::ASCIIMask::IsMasked(aToStrip, theChar)) {
1302
15.4M
      // Not stripped, copy this char.
1303
15.4M
      *to++ = (char_type)theChar;
1304
15.4M
    }
1305
15.4M
  }
1306
3.91M
  *to = char_type(0); // add the null
1307
3.91M
  this->mLength = to - this->mData;
1308
3.91M
}
Unexecuted instantiation: nsTSubstring<char16_t>::StripTaggedASCII(std::__1::array<bool, 128ul> const&)
1309
1310
template <typename T>
1311
void
1312
nsTSubstring<T>::StripCRLF()
1313
0
{
1314
0
  // Expanding this call to copy the code from StripTaggedASCII
1315
0
  // instead of just calling it does somewhat help with performance
1316
0
  // but it is not worth it given the duplicated code.
1317
0
  StripTaggedASCII(mozilla::ASCIIMask::MaskCRLF());
1318
0
}
Unexecuted instantiation: nsTSubstring<char>::StripCRLF()
Unexecuted instantiation: nsTSubstring<char16_t>::StripCRLF()
1319
1320
template <typename T>
1321
struct MOZ_STACK_CLASS PrintfAppend : public mozilla::PrintfTarget
1322
{
1323
  explicit PrintfAppend(nsTSubstring<T>* aString)
1324
    : mString(aString)
1325
4.41k
  {
1326
4.41k
  }
PrintfAppend<char>::PrintfAppend(nsTSubstring<char>*)
Line
Count
Source
1325
4.41k
  {
1326
4.41k
  }
Unexecuted instantiation: PrintfAppend<char16_t>::PrintfAppend(nsTSubstring<char16_t>*)
1327
1328
28.3k
  bool append(const char* aStr, size_t aLen) override {
1329
28.3k
    if (aLen == 0) {
1330
0
      return true;
1331
0
    }
1332
28.3k
1333
28.3k
    mString->AppendASCII(aStr, aLen);
1334
28.3k
    return true;
1335
28.3k
  }
PrintfAppend<char>::append(char const*, unsigned long)
Line
Count
Source
1328
28.3k
  bool append(const char* aStr, size_t aLen) override {
1329
28.3k
    if (aLen == 0) {
1330
0
      return true;
1331
0
    }
1332
28.3k
1333
28.3k
    mString->AppendASCII(aStr, aLen);
1334
28.3k
    return true;
1335
28.3k
  }
Unexecuted instantiation: PrintfAppend<char16_t>::append(char const*, unsigned long)
1336
1337
private:
1338
1339
  nsTSubstring<T>* mString;
1340
};
1341
1342
template <typename T>
1343
void
1344
nsTSubstring<T>::AppendPrintf(const char* aFormat, ...)
1345
404
{
1346
404
  PrintfAppend<T> appender(this);
1347
404
  va_list ap;
1348
404
  va_start(ap, aFormat);
1349
404
  bool r = appender.vprint(aFormat, ap);
1350
404
  if (!r) {
1351
0
    MOZ_CRASH("Allocation or other failure in PrintfTarget::print");
1352
0
  }
1353
404
  va_end(ap);
1354
404
}
nsTSubstring<char>::AppendPrintf(char const*, ...)
Line
Count
Source
1345
404
{
1346
404
  PrintfAppend<T> appender(this);
1347
404
  va_list ap;
1348
404
  va_start(ap, aFormat);
1349
404
  bool r = appender.vprint(aFormat, ap);
1350
404
  if (!r) {
1351
0
    MOZ_CRASH("Allocation or other failure in PrintfTarget::print");
1352
0
  }
1353
404
  va_end(ap);
1354
404
}
Unexecuted instantiation: nsTSubstring<char16_t>::AppendPrintf(char const*, ...)
1355
1356
template <typename T>
1357
void
1358
nsTSubstring<T>::AppendPrintf(const char* aFormat, va_list aAp)
1359
4.00k
{
1360
4.00k
  PrintfAppend<T> appender(this);
1361
4.00k
  bool r = appender.vprint(aFormat, aAp);
1362
4.00k
  if (!r) {
1363
0
    MOZ_CRASH("Allocation or other failure in PrintfTarget::print");
1364
0
  }
1365
4.00k
}
nsTSubstring<char>::AppendPrintf(char const*, __va_list_tag*)
Line
Count
Source
1359
4.00k
{
1360
4.00k
  PrintfAppend<T> appender(this);
1361
4.00k
  bool r = appender.vprint(aFormat, aAp);
1362
4.00k
  if (!r) {
1363
0
    MOZ_CRASH("Allocation or other failure in PrintfTarget::print");
1364
0
  }
1365
4.00k
}
Unexecuted instantiation: nsTSubstring<char16_t>::AppendPrintf(char const*, __va_list_tag*)
1366
1367
// Returns the length of the formatted aDouble in aBuf.
1368
static int
1369
FormatWithoutTrailingZeros(char (&aBuf)[40], double aDouble,
1370
                           int aPrecision)
1371
0
{
1372
0
  static const DoubleToStringConverter converter(DoubleToStringConverter::UNIQUE_ZERO |
1373
0
                                                 DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN,
1374
0
                                                 "Infinity",
1375
0
                                                 "NaN",
1376
0
                                                 'e',
1377
0
                                                 -6, 21,
1378
0
                                                 6, 1);
1379
0
  double_conversion::StringBuilder builder(aBuf, sizeof(aBuf));
1380
0
  bool exponential_notation = false;
1381
0
  converter.ToPrecision(aDouble, aPrecision, &exponential_notation, &builder);
1382
0
  int length = builder.position();
1383
0
  char* formattedDouble = builder.Finalize();
1384
0
1385
0
  // If we have a shorter string than aPrecision, it means we have a special
1386
0
  // value (NaN or Infinity).  All other numbers will be formatted with at
1387
0
  // least aPrecision digits.
1388
0
  if (length <= aPrecision) {
1389
0
    return length;
1390
0
  }
1391
0
1392
0
  char* end = formattedDouble + length;
1393
0
  char* decimalPoint = strchr(aBuf, '.');
1394
0
  // No trailing zeros to remove.
1395
0
  if (!decimalPoint) {
1396
0
    return length;
1397
0
  }
1398
0
1399
0
  if (MOZ_UNLIKELY(exponential_notation)) {
1400
0
    // We need to check for cases like 1.00000e-10 (yes, this is
1401
0
    // disgusting).
1402
0
    char* exponent = end - 1;
1403
0
    for (; ; --exponent) {
1404
0
      if (*exponent == 'e') {
1405
0
        break;
1406
0
      }
1407
0
    }
1408
0
    char* zerosBeforeExponent = exponent - 1;
1409
0
    for (; zerosBeforeExponent != decimalPoint; --zerosBeforeExponent) {
1410
0
      if (*zerosBeforeExponent != '0') {
1411
0
        break;
1412
0
      }
1413
0
    }
1414
0
    if (zerosBeforeExponent == decimalPoint) {
1415
0
      --zerosBeforeExponent;
1416
0
    }
1417
0
    // Slide the exponent to the left over the trailing zeros.  Don't
1418
0
    // worry about copying the trailing NUL character.
1419
0
    size_t exponentSize = end - exponent;
1420
0
    memmove(zerosBeforeExponent + 1, exponent, exponentSize);
1421
0
    length -= exponent - (zerosBeforeExponent + 1);
1422
0
  } else {
1423
0
    char* trailingZeros = end - 1;
1424
0
    for (; trailingZeros != decimalPoint; --trailingZeros) {
1425
0
      if (*trailingZeros != '0') {
1426
0
        break;
1427
0
      }
1428
0
    }
1429
0
    if (trailingZeros == decimalPoint) {
1430
0
      --trailingZeros;
1431
0
    }
1432
0
    length -= end - (trailingZeros + 1);
1433
0
  }
1434
0
1435
0
  return length;
1436
0
}
1437
1438
template <typename T>
1439
void
1440
nsTSubstring<T>::AppendFloat(float aFloat)
1441
0
{
1442
0
  char buf[40];
1443
0
  int length = FormatWithoutTrailingZeros(buf, aFloat, 6);
1444
0
  AppendASCII(buf, length);
1445
0
}
Unexecuted instantiation: nsTSubstring<char>::AppendFloat(float)
Unexecuted instantiation: nsTSubstring<char16_t>::AppendFloat(float)
1446
1447
template <typename T>
1448
void
1449
nsTSubstring<T>::AppendFloat(double aFloat)
1450
0
{
1451
0
  char buf[40];
1452
0
  int length = FormatWithoutTrailingZeros(buf, aFloat, 15);
1453
0
  AppendASCII(buf, length);
1454
0
}
Unexecuted instantiation: nsTSubstring<char>::AppendFloat(double)
Unexecuted instantiation: nsTSubstring<char16_t>::AppendFloat(double)
1455
1456
template <typename T>
1457
size_t
1458
nsTSubstring<T>::SizeOfExcludingThisIfUnshared(
1459
    mozilla::MallocSizeOf aMallocSizeOf) const
1460
0
{
1461
0
  if (this->mDataFlags & DataFlags::REFCOUNTED) {
1462
0
    return nsStringBuffer::FromData(this->mData)->
1463
0
      SizeOfIncludingThisIfUnshared(aMallocSizeOf);
1464
0
  }
1465
0
  if (this->mDataFlags & DataFlags::OWNED) {
1466
0
    return aMallocSizeOf(this->mData);
1467
0
  }
1468
0
1469
0
  // If we reach here, exactly one of the following must be true:
1470
0
  // - DataFlags::VOIDED is set, and this->mData points to sEmptyBuffer;
1471
0
  // - DataFlags::INLINE is set, and this->mData points to a buffer within a
1472
0
  //   string object (e.g. nsAutoString);
1473
0
  // - None of DataFlags::REFCOUNTED, DataFlags::OWNED, DataFlags::INLINE is set,
1474
0
  //   and this->mData points to a buffer owned by something else.
1475
0
  //
1476
0
  // In all three cases, we don't measure it.
1477
0
  return 0;
1478
0
}
Unexecuted instantiation: nsTSubstring<char>::SizeOfExcludingThisIfUnshared(unsigned long (*)(void const*)) const
Unexecuted instantiation: nsTSubstring<char16_t>::SizeOfExcludingThisIfUnshared(unsigned long (*)(void const*)) const
1479
1480
template <typename T>
1481
size_t
1482
nsTSubstring<T>::SizeOfExcludingThisEvenIfShared(
1483
    mozilla::MallocSizeOf aMallocSizeOf) const
1484
0
{
1485
0
  // This is identical to SizeOfExcludingThisIfUnshared except for the
1486
0
  // DataFlags::REFCOUNTED case.
1487
0
  if (this->mDataFlags & DataFlags::REFCOUNTED) {
1488
0
    return nsStringBuffer::FromData(this->mData)->
1489
0
      SizeOfIncludingThisEvenIfShared(aMallocSizeOf);
1490
0
  }
1491
0
  if (this->mDataFlags & DataFlags::OWNED) {
1492
0
    return aMallocSizeOf(this->mData);
1493
0
  }
1494
0
  return 0;
1495
0
}
Unexecuted instantiation: nsTSubstring<char>::SizeOfExcludingThisEvenIfShared(unsigned long (*)(void const*)) const
Unexecuted instantiation: nsTSubstring<char16_t>::SizeOfExcludingThisEvenIfShared(unsigned long (*)(void const*)) const
1496
1497
template <typename T>
1498
size_t
1499
nsTSubstring<T>::SizeOfIncludingThisIfUnshared(
1500
    mozilla::MallocSizeOf aMallocSizeOf) const
1501
0
{
1502
0
  return aMallocSizeOf(this) + SizeOfExcludingThisIfUnshared(aMallocSizeOf);
1503
0
}
Unexecuted instantiation: nsTSubstring<char>::SizeOfIncludingThisIfUnshared(unsigned long (*)(void const*)) const
Unexecuted instantiation: nsTSubstring<char16_t>::SizeOfIncludingThisIfUnshared(unsigned long (*)(void const*)) const
1504
1505
template <typename T>
1506
size_t
1507
nsTSubstring<T>::SizeOfIncludingThisEvenIfShared(
1508
    mozilla::MallocSizeOf aMallocSizeOf) const
1509
0
{
1510
0
  return aMallocSizeOf(this) + SizeOfExcludingThisEvenIfShared(aMallocSizeOf);
1511
0
}
Unexecuted instantiation: nsTSubstring<char>::SizeOfIncludingThisEvenIfShared(unsigned long (*)(void const*)) const
Unexecuted instantiation: nsTSubstring<char16_t>::SizeOfIncludingThisEvenIfShared(unsigned long (*)(void const*)) const
1512
1513
template <typename T>
1514
inline
1515
nsTSubstringSplitter<T>::nsTSubstringSplitter(
1516
    const nsTSubstring<T>* aStr, char_type aDelim)
1517
  : mStr(aStr)
1518
  , mArray(nullptr)
1519
  , mDelim(aDelim)
1520
24
{
1521
24
  if (mStr->IsEmpty()) {
1522
6
    mArraySize = 0;
1523
6
    return;
1524
6
  }
1525
18
1526
18
  size_type delimCount = mStr->CountChar(aDelim);
1527
18
  mArraySize = delimCount + 1;
1528
18
  mArray.reset(new nsTDependentSubstring<T>[mArraySize]);
1529
18
1530
18
  size_t seenParts = 0;
1531
18
  size_type start = 0;
1532
36
  do {
1533
36
    MOZ_ASSERT(seenParts < mArraySize);
1534
36
    int32_t offset = mStr->FindChar(aDelim, start);
1535
36
    if (offset != -1) {
1536
18
      size_type length = static_cast<size_type>(offset) - start;
1537
18
      mArray[seenParts++].Rebind(mStr->Data() + start, length);
1538
18
      start = static_cast<size_type>(offset) + 1;
1539
18
    } else {
1540
18
      // Get the remainder
1541
18
      mArray[seenParts++].Rebind(mStr->Data() + start, mStr->Length() - start);
1542
18
      break;
1543
18
    }
1544
18
  } while (start < mStr->Length());
1545
18
}
nsTSubstringSplitter<char>::nsTSubstringSplitter(nsTSubstring<char> const*, char)
Line
Count
Source
1520
24
{
1521
24
  if (mStr->IsEmpty()) {
1522
6
    mArraySize = 0;
1523
6
    return;
1524
6
  }
1525
18
1526
18
  size_type delimCount = mStr->CountChar(aDelim);
1527
18
  mArraySize = delimCount + 1;
1528
18
  mArray.reset(new nsTDependentSubstring<T>[mArraySize]);
1529
18
1530
18
  size_t seenParts = 0;
1531
18
  size_type start = 0;
1532
36
  do {
1533
36
    MOZ_ASSERT(seenParts < mArraySize);
1534
36
    int32_t offset = mStr->FindChar(aDelim, start);
1535
36
    if (offset != -1) {
1536
18
      size_type length = static_cast<size_type>(offset) - start;
1537
18
      mArray[seenParts++].Rebind(mStr->Data() + start, length);
1538
18
      start = static_cast<size_type>(offset) + 1;
1539
18
    } else {
1540
18
      // Get the remainder
1541
18
      mArray[seenParts++].Rebind(mStr->Data() + start, mStr->Length() - start);
1542
18
      break;
1543
18
    }
1544
18
  } while (start < mStr->Length());
1545
18
}
Unexecuted instantiation: nsTSubstringSplitter<char16_t>::nsTSubstringSplitter(nsTSubstring<char16_t> const*, char16_t)
1546
1547
template <typename T>
1548
nsTSubstringSplitter<T>
1549
nsTSubstring<T>::Split(const char_type aChar) const
1550
24
{
1551
24
  return nsTSubstringSplitter<T>(this, aChar);
1552
24
}
nsTSubstring<char>::Split(char) const
Line
Count
Source
1550
24
{
1551
24
  return nsTSubstringSplitter<T>(this, aChar);
1552
24
}
Unexecuted instantiation: nsTSubstring<char16_t>::Split(char16_t) const
1553
1554
template <typename T>
1555
const nsTDependentSubstring<T>&
1556
nsTSubstringSplitter<T>::nsTSubstringSplit_Iter::operator* () const
1557
36
{
1558
36
   return mObj.Get(mPos);
1559
36
}
nsTSubstringSplitter<char>::nsTSubstringSplit_Iter::operator*() const
Line
Count
Source
1557
36
{
1558
36
   return mObj.Get(mPos);
1559
36
}
Unexecuted instantiation: nsTSubstringSplitter<char16_t>::nsTSubstringSplit_Iter::operator*() const
1560
1561
// Common logic for nsTSubstring<T>::ToInteger and nsTSubstring<T>::ToInteger64.
1562
template<typename T, typename int_type>
1563
int_type
1564
ToIntegerCommon(const nsTSubstring<T>& aSrc, nsresult* aErrorCode, uint32_t aRadix)
1565
1.25k
{
1566
1.25k
  MOZ_ASSERT(aRadix == 10 || aRadix == 16);
1567
1.25k
1568
1.25k
  // Initial value, override if we find an integer.
1569
1.25k
  *aErrorCode = NS_ERROR_ILLEGAL_VALUE;
1570
1.25k
1571
1.25k
  // Begin by skipping over leading chars that shouldn't be part of the number.
1572
1.25k
  auto cp = aSrc.BeginReading();
1573
1.25k
  auto endcp = aSrc.EndReading();
1574
1.25k
  bool negate = false;
1575
1.25k
  bool done = false;
1576
1.25k
1577
1.25k
  // NB: For backwards compatibility I'm not going to change this logic but
1578
1.25k
  //     it seems really odd. Previously there was logic to auto-detect the
1579
1.25k
  //     radix if kAutoDetect was passed in. In practice this value was never
1580
1.25k
  //     used, so it pretended to auto detect and skipped some preceding
1581
1.25k
  //     letters (excluding valid hex digits) but never used the result.
1582
1.25k
  //
1583
1.25k
  //     For example if you pass in "Get the number: 10", aRadix = 10 we'd
1584
1.25k
  //     skip the 'G', and then fail to parse "et the number: 10". If aRadix =
1585
1.25k
  //     16 we'd skip the 'G', and parse just 'e' returning 14.
1586
12.8k
  while ((cp < endcp) && (!done)) {
1587
11.5k
    switch (*cp++) {
1588
11.5k
      // clang-format off
1589
11.5k
      case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
1590
1.24k
      case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
1591
1.24k
      case '0': case '1': case '2': case '3': case '4':
1592
1.24k
      case '5': case '6': case '7': case '8': case '9':
1593
1.24k
        done = true;
1594
1.24k
        break;
1595
1.24k
      // clang-format on
1596
1.24k
      case '-':
1597
276
        negate = true;
1598
276
        break;
1599
10.0k
      default:
1600
10.0k
        break;
1601
11.5k
    }
1602
11.5k
  }
1603
1.25k
1604
1.25k
  if (!done) {
1605
12
    // No base 16 or base 10 digits were found.
1606
12
    return 0;
1607
12
  }
1608
1.24k
1609
1.24k
  // Step back.
1610
1.24k
  cp--;
1611
1.24k
1612
1.24k
  mozilla::CheckedInt<int_type> result;
1613
1.24k
1614
1.24k
  // Now iterate the numeric chars and build our result.
1615
4.64k
  while (cp < endcp) {
1616
3.93k
    auto theChar = *cp++;
1617
3.93k
    if (('0' <= theChar) && (theChar <= '9')) {
1618
3.42k
      result = (aRadix * result) + (theChar - '0');
1619
3.42k
    } else if ((theChar >= 'A') && (theChar <= 'F')) {
1620
0
      if (10 == aRadix) {
1621
0
        // Invalid base 10 digit, error out.
1622
0
        return 0;
1623
0
      } else {
1624
0
        result = (aRadix * result) + ((theChar - 'A') + 10);
1625
0
      }
1626
511
    } else if ((theChar >= 'a') && (theChar <= 'f')) {
1627
267
      if (10 == aRadix) {
1628
267
        // Invalid base 10 digit, error out.
1629
267
        return 0;
1630
267
      } else {
1631
0
        result = (aRadix * result) + ((theChar - 'a') + 10);
1632
0
      }
1633
267
    } else if ((('X' == theChar) || ('x' == theChar)) && result == 0) {
1634
0
      // For some reason we support a leading 'x' regardless of radix. For
1635
0
      // example: "000000x500", aRadix = 10 would be parsed as 500 rather
1636
0
      // than 0.
1637
0
      continue;
1638
244
    } else {
1639
244
      // We've encountered a char that's not a legal number or sign and we can
1640
244
      // terminate processing.
1641
244
      break;
1642
244
    }
1643
3.42k
1644
3.42k
    if (!result.isValid()) {
1645
22
      // Overflow!
1646
22
      return 0;
1647
22
    }
1648
3.42k
  }
1649
1.24k
1650
1.24k
  // Integer found.
1651
1.24k
  *aErrorCode = NS_OK;
1652
956
1653
956
  if (negate) {
1654
271
    result = -result;
1655
271
  }
1656
956
1657
956
  return result.value();
1658
1.24k
}
int ToIntegerCommon<char, int>(nsTSubstring<char> const&, nsresult*, unsigned int)
Line
Count
Source
1565
1.25k
{
1566
1.25k
  MOZ_ASSERT(aRadix == 10 || aRadix == 16);
1567
1.25k
1568
1.25k
  // Initial value, override if we find an integer.
1569
1.25k
  *aErrorCode = NS_ERROR_ILLEGAL_VALUE;
1570
1.25k
1571
1.25k
  // Begin by skipping over leading chars that shouldn't be part of the number.
1572
1.25k
  auto cp = aSrc.BeginReading();
1573
1.25k
  auto endcp = aSrc.EndReading();
1574
1.25k
  bool negate = false;
1575
1.25k
  bool done = false;
1576
1.25k
1577
1.25k
  // NB: For backwards compatibility I'm not going to change this logic but
1578
1.25k
  //     it seems really odd. Previously there was logic to auto-detect the
1579
1.25k
  //     radix if kAutoDetect was passed in. In practice this value was never
1580
1.25k
  //     used, so it pretended to auto detect and skipped some preceding
1581
1.25k
  //     letters (excluding valid hex digits) but never used the result.
1582
1.25k
  //
1583
1.25k
  //     For example if you pass in "Get the number: 10", aRadix = 10 we'd
1584
1.25k
  //     skip the 'G', and then fail to parse "et the number: 10". If aRadix =
1585
1.25k
  //     16 we'd skip the 'G', and parse just 'e' returning 14.
1586
12.8k
  while ((cp < endcp) && (!done)) {
1587
11.5k
    switch (*cp++) {
1588
11.5k
      // clang-format off
1589
11.5k
      case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
1590
1.24k
      case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
1591
1.24k
      case '0': case '1': case '2': case '3': case '4':
1592
1.24k
      case '5': case '6': case '7': case '8': case '9':
1593
1.24k
        done = true;
1594
1.24k
        break;
1595
1.24k
      // clang-format on
1596
1.24k
      case '-':
1597
276
        negate = true;
1598
276
        break;
1599
10.0k
      default:
1600
10.0k
        break;
1601
11.5k
    }
1602
11.5k
  }
1603
1.25k
1604
1.25k
  if (!done) {
1605
12
    // No base 16 or base 10 digits were found.
1606
12
    return 0;
1607
12
  }
1608
1.24k
1609
1.24k
  // Step back.
1610
1.24k
  cp--;
1611
1.24k
1612
1.24k
  mozilla::CheckedInt<int_type> result;
1613
1.24k
1614
1.24k
  // Now iterate the numeric chars and build our result.
1615
4.64k
  while (cp < endcp) {
1616
3.93k
    auto theChar = *cp++;
1617
3.93k
    if (('0' <= theChar) && (theChar <= '9')) {
1618
3.42k
      result = (aRadix * result) + (theChar - '0');
1619
3.42k
    } else if ((theChar >= 'A') && (theChar <= 'F')) {
1620
0
      if (10 == aRadix) {
1621
0
        // Invalid base 10 digit, error out.
1622
0
        return 0;
1623
0
      } else {
1624
0
        result = (aRadix * result) + ((theChar - 'A') + 10);
1625
0
      }
1626
511
    } else if ((theChar >= 'a') && (theChar <= 'f')) {
1627
267
      if (10 == aRadix) {
1628
267
        // Invalid base 10 digit, error out.
1629
267
        return 0;
1630
267
      } else {
1631
0
        result = (aRadix * result) + ((theChar - 'a') + 10);
1632
0
      }
1633
267
    } else if ((('X' == theChar) || ('x' == theChar)) && result == 0) {
1634
0
      // For some reason we support a leading 'x' regardless of radix. For
1635
0
      // example: "000000x500", aRadix = 10 would be parsed as 500 rather
1636
0
      // than 0.
1637
0
      continue;
1638
244
    } else {
1639
244
      // We've encountered a char that's not a legal number or sign and we can
1640
244
      // terminate processing.
1641
244
      break;
1642
244
    }
1643
3.42k
1644
3.42k
    if (!result.isValid()) {
1645
22
      // Overflow!
1646
22
      return 0;
1647
22
    }
1648
3.42k
  }
1649
1.24k
1650
1.24k
  // Integer found.
1651
1.24k
  *aErrorCode = NS_OK;
1652
956
1653
956
  if (negate) {
1654
271
    result = -result;
1655
271
  }
1656
956
1657
956
  return result.value();
1658
1.24k
}
Unexecuted instantiation: long ToIntegerCommon<char, long>(nsTSubstring<char> const&, nsresult*, unsigned int)
Unexecuted instantiation: int ToIntegerCommon<char16_t, int>(nsTSubstring<char16_t> const&, nsresult*, unsigned int)
Unexecuted instantiation: long ToIntegerCommon<char16_t, long>(nsTSubstring<char16_t> const&, nsresult*, unsigned int)
1659
1660
1661
template <typename T>
1662
int32_t
1663
nsTSubstring<T>::ToInteger(nsresult* aErrorCode, uint32_t aRadix) const
1664
1.25k
{
1665
1.25k
  return ToIntegerCommon<T, int32_t>(*this, aErrorCode, aRadix);
1666
1.25k
}
nsTSubstring<char>::ToInteger(nsresult*, unsigned int) const
Line
Count
Source
1664
1.25k
{
1665
1.25k
  return ToIntegerCommon<T, int32_t>(*this, aErrorCode, aRadix);
1666
1.25k
}
Unexecuted instantiation: nsTSubstring<char16_t>::ToInteger(nsresult*, unsigned int) const
1667
1668
1669
/**
1670
 * nsTSubstring::ToInteger64
1671
 */
1672
template <typename T>
1673
int64_t
1674
nsTSubstring<T>::ToInteger64(nsresult* aErrorCode, uint32_t aRadix) const
1675
0
{
1676
0
  return ToIntegerCommon<T, int64_t>(*this, aErrorCode, aRadix);
1677
0
}
Unexecuted instantiation: nsTSubstring<char>::ToInteger64(nsresult*, unsigned int) const
Unexecuted instantiation: nsTSubstring<char16_t>::ToInteger64(nsresult*, unsigned int) const