~ubuntu-branches/ubuntu/gutsy/virtualbox-ose/gutsy

« back to all changes in this revision

Viewing changes to src/libs/xpcom18a4/xpcom/string/src/nsTSubstring.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Steve Kowalik
  • Date: 2007-09-08 16:44:58 UTC
  • Revision ID: james.westby@ubuntu.com-20070908164458-wao29470vqtr8ksy
Tags: upstream-1.5.0-dfsg2
ImportĀ upstreamĀ versionĀ 1.5.0-dfsg2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 
2
/* vim:set ts=2 sw=2 sts=2 et cindent: */
 
3
/* ***** BEGIN LICENSE BLOCK *****
 
4
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 
5
 *
 
6
 * The contents of this file are subject to the Mozilla Public License Version
 
7
 * 1.1 (the "License"); you may not use this file except in compliance with
 
8
 * the License. You may obtain a copy of the License at
 
9
 * http://www.mozilla.org/MPL/
 
10
 *
 
11
 * Software distributed under the License is distributed on an "AS IS" basis,
 
12
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 
13
 * for the specific language governing rights and limitations under the
 
14
 * License.
 
15
 *
 
16
 * The Original Code is Mozilla.
 
17
 *
 
18
 * The Initial Developer of the Original Code is IBM Corporation.
 
19
 * Portions created by IBM Corporation are Copyright (C) 2003
 
20
 * IBM Corporation. All Rights Reserved.
 
21
 *
 
22
 * Contributor(s):
 
23
 *   Darin Fisher <darin@meer.net>
 
24
 *
 
25
 * Alternatively, the contents of this file may be used under the terms of
 
26
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 
27
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 
28
 * in which case the provisions of the GPL or the LGPL are applicable instead
 
29
 * of those above. If you wish to allow use of your version of this file only
 
30
 * under the terms of either the GPL or the LGPL, and not to allow others to
 
31
 * use your version of this file under the terms of the MPL, indicate your
 
32
 * decision by deleting the provisions above and replace them with the notice
 
33
 * and other provisions required by the GPL or the LGPL. If you do not delete
 
34
 * the provisions above, a recipient may use your version of this file under
 
35
 * the terms of any one of the MPL, the GPL or the LGPL.
 
36
 *
 
37
 * ***** END LICENSE BLOCK ***** */
 
38
 
 
39
 
 
40
  /**
 
41
   * helper function for down-casting a nsTSubstring to a nsTFixedString.
 
42
   */
 
43
inline const nsTFixedString_CharT*
 
44
AsFixedString( const nsTSubstring_CharT* s )
 
45
  {
 
46
    return NS_STATIC_CAST(const nsTFixedString_CharT*, s);
 
47
  }
 
48
 
 
49
 
 
50
  /**
 
51
   * this function is called to prepare mData for writing.  the given capacity
 
52
   * indicates the required minimum storage size for mData, in sizeof(char_type)
 
53
   * increments.  this function returns true if the operation succeeds.  it also
 
54
   * returns the old data and old flags members if mData is newly allocated.
 
55
   * the old data must be released by the caller.
 
56
   */
 
57
PRBool
 
58
nsTSubstring_CharT::MutatePrep( size_type capacity, char_type** oldData, PRUint32* oldFlags )
 
59
  {
 
60
    // initialize to no old data
 
61
    *oldData = nsnull;
 
62
    *oldFlags = 0;
 
63
 
 
64
    size_type curCapacity = Capacity();
 
65
 
 
66
    // |curCapacity == size_type(-1)| means that the buffer is immutable, so we
 
67
    // need to allocate a new buffer.  we cannot use the existing buffer even
 
68
    // though it might be large enough.
 
69
 
 
70
    if (curCapacity != size_type(-1))
 
71
      {
 
72
        if (capacity <= curCapacity)
 
73
          return PR_TRUE;
 
74
 
 
75
        if (curCapacity > 0)
 
76
          {
 
77
            // use doubling algorithm when forced to increase available capacity,
 
78
            // but always start out with exactly the requested amount.
 
79
            PRUint32 temp = curCapacity;
 
80
            while (temp < capacity)
 
81
              temp <<= 1;
 
82
            capacity = temp;
 
83
          }
 
84
      }
 
85
 
 
86
    //
 
87
    // several cases:
 
88
    //
 
89
    //  (1) we have a shared buffer (mFlags & F_SHARED)
 
90
    //  (2) we have an owned buffer (mFlags & F_OWNED)
 
91
    //  (3) we have a fixed buffer (mFlags & F_FIXED)
 
92
    //  (4) we have a readonly buffer
 
93
    //
 
94
    // requiring that we in some cases preserve the data before creating
 
95
    // a new buffer complicates things just a bit ;-)
 
96
    //
 
97
 
 
98
    size_type storageSize = (capacity + 1) * sizeof(char_type);
 
99
 
 
100
    // case #1
 
101
    if (mFlags & F_SHARED)
 
102
      {
 
103
        nsStringHeader* hdr = nsStringHeader::FromData(mData);
 
104
        if (!hdr->IsReadonly())
 
105
          {
 
106
            nsStringHeader *newHdr = nsStringHeader::Realloc(hdr, storageSize);
 
107
            if (newHdr)
 
108
              {
 
109
                hdr = newHdr;
 
110
                mData = (char_type*) hdr->Data();
 
111
                return PR_TRUE;
 
112
              }
 
113
            hdr->Release();
 
114
            // out of memory!!  put us in a consistent state at least.
 
115
            mData = NS_CONST_CAST(char_type*, char_traits::sEmptyBuffer);
 
116
            mLength = 0;
 
117
            SetDataFlags(F_TERMINATED);
 
118
            return PR_FALSE;
 
119
          }
 
120
      }
 
121
 
 
122
    char_type* newData;
 
123
    PRUint32 newDataFlags;
 
124
 
 
125
      // if we have a fixed buffer of sufficient size, then use it.  this helps
 
126
      // avoid heap allocations.
 
127
    if ((mFlags & F_CLASS_FIXED) && (capacity < AsFixedString(this)->mFixedCapacity))
 
128
      {
 
129
        newData = AsFixedString(this)->mFixedBuf;
 
130
        newDataFlags = F_TERMINATED | F_FIXED;
 
131
      }
 
132
    else
 
133
      {
 
134
        // if we reach here then, we must allocate a new buffer.  we cannot
 
135
        // make use of our F_OWNED or F_FIXED buffers because they are not
 
136
        // large enough.
 
137
 
 
138
        nsStringHeader* newHdr = nsStringHeader::Alloc(storageSize);
 
139
        if (!newHdr)
 
140
          return PR_FALSE; // we are still in a consistent state
 
141
 
 
142
        newData = (char_type*) newHdr->Data();
 
143
        newDataFlags = F_TERMINATED | F_SHARED;
 
144
      }
 
145
 
 
146
    // save old data and flags
 
147
    *oldData = mData;
 
148
    *oldFlags = mFlags;
 
149
 
 
150
    mData = newData;
 
151
    SetDataFlags(newDataFlags);
 
152
 
 
153
    // mLength does not change
 
154
 
 
155
    // though we are not necessarily terminated at the moment, now is probably
 
156
    // still the best time to set F_TERMINATED.
 
157
 
 
158
    return PR_TRUE;
 
159
  }
 
160
 
 
161
void
 
162
nsTSubstring_CharT::Finalize()
 
163
  {
 
164
    ::ReleaseData(mData, mFlags);
 
165
    // mData, mLength, and mFlags are purposefully left dangling
 
166
  }
 
167
 
 
168
void
 
169
nsTSubstring_CharT::ReplacePrep( index_type cutStart, size_type cutLen, size_type fragLen )
 
170
  {
 
171
    // bound cut length
 
172
    cutLen = NS_MIN(cutLen, mLength - cutStart);
 
173
 
 
174
    PRUint32 newLen = mLength - cutLen + fragLen;
 
175
 
 
176
    char_type* oldData;
 
177
    PRUint32 oldFlags;
 
178
    if (!MutatePrep(newLen, &oldData, &oldFlags))
 
179
      return; // XXX out-of-memory error occured!
 
180
 
 
181
    if (oldData)
 
182
      {
 
183
        // determine whether or not we need to copy part of the old string
 
184
        // over to the new string.
 
185
 
 
186
        if (cutStart > 0)
 
187
          {
 
188
            // copy prefix from old string
 
189
            char_traits::copy(mData, oldData, cutStart);
 
190
          }
 
191
 
 
192
        if (cutStart + cutLen < mLength)
 
193
          {
 
194
            // copy suffix from old string to new offset
 
195
            size_type from = cutStart + cutLen;
 
196
            size_type fromLen = mLength - from;
 
197
            PRUint32 to = cutStart + fragLen;
 
198
            char_traits::copy(mData + to, oldData + from, fromLen);
 
199
          }
 
200
 
 
201
        ::ReleaseData(oldData, oldFlags);
 
202
      }
 
203
    else
 
204
      {
 
205
        // original data remains intact
 
206
 
 
207
        // determine whether or not we need to move part of the existing string
 
208
        // to make room for the requested hole.
 
209
        if (fragLen != cutLen && cutStart + cutLen < mLength)
 
210
          {
 
211
            PRUint32 from = cutStart + cutLen;
 
212
            PRUint32 fromLen = mLength - from;
 
213
            PRUint32 to = cutStart + fragLen;
 
214
            char_traits::move(mData + to, mData + from, fromLen);
 
215
          }
 
216
      }
 
217
 
 
218
    // add null terminator (mutable mData always has room for the null-
 
219
    // terminator).
 
220
    mData[newLen] = char_type(0);
 
221
    mLength = newLen;
 
222
  }
 
223
 
 
224
nsTSubstring_CharT::size_type
 
225
nsTSubstring_CharT::Capacity() const
 
226
  {
 
227
    // return size_type(-1) to indicate an immutable buffer
 
228
 
 
229
    size_type capacity;
 
230
    if (mFlags & F_SHARED)
 
231
      {
 
232
        // if the string is readonly, then we pretend that it has no capacity.
 
233
        nsStringHeader* hdr = nsStringHeader::FromData(mData);
 
234
        if (hdr->IsReadonly())
 
235
          capacity = size_type(-1);
 
236
        else
 
237
          capacity = (hdr->StorageSize() / sizeof(char_type)) - 1;
 
238
      }
 
239
    else if (mFlags & F_FIXED)
 
240
      {
 
241
        capacity = AsFixedString(this)->mFixedCapacity;
 
242
      }
 
243
    else if (mFlags & F_OWNED)
 
244
      {
 
245
        // we don't store the capacity of an adopted buffer because that would
 
246
        // require an additional member field.  the best we can do is base the
 
247
        // capacity on our length.  remains to be seen if this is the right
 
248
        // trade-off.
 
249
        capacity = mLength;
 
250
      }
 
251
    else
 
252
      {
 
253
        capacity = size_type(-1);
 
254
      }
 
255
 
 
256
    return capacity;
 
257
  }
 
258
 
 
259
void
 
260
nsTSubstring_CharT::EnsureMutable()
 
261
  {
 
262
    if (mFlags & (F_FIXED | F_OWNED))
 
263
      return;
 
264
    if ((mFlags & F_SHARED) && !nsStringHeader::FromData(mData)->IsReadonly())
 
265
      return;
 
266
 
 
267
    // promote to a shared string buffer
 
268
    Assign(string_type(mData, mLength));
 
269
  }
 
270
 
 
271
// ---------------------------------------------------------------------------
 
272
 
 
273
void
 
274
nsTSubstring_CharT::Assign( const char_type* data, size_type length )
 
275
  {
 
276
      // unfortunately, some callers pass null :-(
 
277
    if (!data)
 
278
      {
 
279
        Truncate();
 
280
        return;
 
281
      }
 
282
 
 
283
    if (length == size_type(-1))
 
284
      length = char_traits::length(data);
 
285
 
 
286
    if (IsDependentOn(data, data + length))
 
287
      {
 
288
        // take advantage of sharing here...
 
289
        Assign(string_type(data, length));
 
290
        return;
 
291
      }
 
292
 
 
293
    ReplacePrep(0, mLength, length);
 
294
    char_traits::copy(mData, data, length);
 
295
  }
 
296
 
 
297
void
 
298
nsTSubstring_CharT::AssignASCII( const char* data, size_type length )
 
299
  {
 
300
    // A Unicode string can't depend on an ASCII string buffer,
 
301
    // so this dependence check only applies to CStrings.
 
302
#ifdef CharT_is_char
 
303
    if (IsDependentOn(data, data + length))
 
304
      {
 
305
        // take advantage of sharing here...
 
306
        Assign(string_type(data, length));
 
307
        return;
 
308
      }
 
309
#endif
 
310
 
 
311
    ReplacePrep(0, mLength, length);
 
312
    char_traits::copyASCII(mData, data, length);
 
313
  }
 
314
 
 
315
void
 
316
nsTSubstring_CharT::AssignASCII( const char* data )
 
317
  {
 
318
    AssignASCII(data, strlen(data));
 
319
  }
 
320
 
 
321
void
 
322
nsTSubstring_CharT::Assign( const self_type& str )
 
323
  {
 
324
    // |str| could be sharable.  we need to check its flags to know how to
 
325
    // deal with it.
 
326
 
 
327
    if (&str == this)
 
328
      return;
 
329
 
 
330
    if (str.mFlags & F_SHARED)
 
331
      {
 
332
        // nice! we can avoid a string copy :-)
 
333
 
 
334
        // |str| should be null-terminated
 
335
        NS_ASSERTION(str.mFlags & F_TERMINATED, "shared, but not terminated");
 
336
 
 
337
        ::ReleaseData(mData, mFlags);
 
338
 
 
339
        mData = str.mData;
 
340
        mLength = str.mLength;
 
341
        SetDataFlags(F_TERMINATED | F_SHARED);
 
342
 
 
343
        // get an owning reference to the mData
 
344
        nsStringHeader::FromData(mData)->AddRef();
 
345
      }
 
346
    else if (str.mFlags & F_VOIDED)
 
347
      {
 
348
        // inherit the F_VOIDED attribute
 
349
        SetIsVoid(PR_TRUE);
 
350
      }
 
351
    else
 
352
      {
 
353
        // else, treat this like an ordinary assignment.
 
354
        Assign(str.Data(), str.Length());
 
355
      }
 
356
  }
 
357
 
 
358
void
 
359
nsTSubstring_CharT::Assign( const substring_tuple_type& tuple )
 
360
  {
 
361
    if (tuple.IsDependentOn(mData, mData + mLength))
 
362
      {
 
363
        // take advantage of sharing here...
 
364
        Assign(string_type(tuple));
 
365
        return;
 
366
      }
 
367
 
 
368
    size_type length = tuple.Length();
 
369
 
 
370
    ReplacePrep(0, mLength, length);
 
371
    if (length)
 
372
      tuple.WriteTo(mData, length);
 
373
  }
 
374
 
 
375
  // this is non-inline to reduce codesize at the callsite
 
376
void
 
377
nsTSubstring_CharT::Assign( const abstract_string_type& readable )
 
378
  {
 
379
      // promote to string if possible to take advantage of sharing
 
380
    if (readable.mVTable == nsTObsoleteAString_CharT::sCanonicalVTable)
 
381
      Assign(*readable.AsSubstring());
 
382
    else
 
383
      Assign(readable.ToSubstring());
 
384
  }
 
385
 
 
386
 
 
387
void
 
388
nsTSubstring_CharT::Adopt( char_type* data, size_type length )
 
389
  {
 
390
    if (data)
 
391
      {
 
392
        ::ReleaseData(mData, mFlags);
 
393
 
 
394
        if (length == size_type(-1))
 
395
          length = char_traits::length(data);
 
396
 
 
397
        mData = data;
 
398
        mLength = length;
 
399
        SetDataFlags(F_TERMINATED | F_OWNED);
 
400
 
 
401
        STRING_STAT_INCREMENT(Adopt);
 
402
      }
 
403
    else
 
404
      {
 
405
        SetIsVoid(PR_TRUE);
 
406
      }
 
407
  }
 
408
 
 
409
 
 
410
void
 
411
nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const char_type* data, size_type length )
 
412
  {
 
413
      // unfortunately, some callers pass null :-(
 
414
    if (!data)
 
415
      {
 
416
        length = 0;
 
417
      }
 
418
    else
 
419
      {
 
420
        if (length == size_type(-1))
 
421
          length = char_traits::length(data);
 
422
 
 
423
        if (IsDependentOn(data, data + length))
 
424
          {
 
425
            nsTAutoString_CharT temp(data, length);
 
426
            Replace(cutStart, cutLength, temp);
 
427
            return;
 
428
          }
 
429
      }
 
430
 
 
431
    cutStart = PR_MIN(cutStart, Length());
 
432
 
 
433
    ReplacePrep(cutStart, cutLength, length);
 
434
 
 
435
    if (length > 0)
 
436
      char_traits::copy(mData + cutStart, data, length);
 
437
  }
 
438
 
 
439
void
 
440
nsTSubstring_CharT::ReplaceASCII( index_type cutStart, size_type cutLength, const char* data, size_type length )
 
441
  {
 
442
    if (length == size_type(-1))
 
443
      length = strlen(data);
 
444
    
 
445
    // A Unicode string can't depend on an ASCII string buffer,
 
446
    // so this dependence check only applies to CStrings.
 
447
#ifdef CharT_is_char
 
448
    if (IsDependentOn(data, data + length))
 
449
      {
 
450
        nsTAutoString_CharT temp(data, length);
 
451
        Replace(cutStart, cutLength, temp);
 
452
        return;
 
453
      }
 
454
#endif
 
455
 
 
456
    cutStart = PR_MIN(cutStart, Length());
 
457
 
 
458
    ReplacePrep(cutStart, cutLength, length);
 
459
 
 
460
    if (length > 0)
 
461
      char_traits::copyASCII(mData + cutStart, data, length);
 
462
  }
 
463
 
 
464
void
 
465
nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const substring_tuple_type& tuple )
 
466
  {
 
467
    if (tuple.IsDependentOn(mData, mData + mLength))
 
468
      {
 
469
        nsTAutoString_CharT temp(tuple);
 
470
        Replace(cutStart, cutLength, temp);
 
471
        return;
 
472
      }
 
473
 
 
474
    size_type length = tuple.Length();
 
475
 
 
476
    cutStart = PR_MIN(cutStart, Length());
 
477
 
 
478
    ReplacePrep(cutStart, cutLength, length);
 
479
 
 
480
    if (length > 0)
 
481
      tuple.WriteTo(mData + cutStart, length);
 
482
  }
 
483
 
 
484
void
 
485
nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const abstract_string_type& readable )
 
486
  {
 
487
    Replace(cutStart, cutLength, readable.ToSubstring());
 
488
  }
 
489
 
 
490
void
 
491
nsTSubstring_CharT::SetCapacity( size_type capacity )
 
492
  {
 
493
    // capacity does not include room for the terminating null char
 
494
 
 
495
    // if our capacity is reduced to zero, then free our buffer.
 
496
    if (capacity == 0)
 
497
      {
 
498
        ::ReleaseData(mData, mFlags);
 
499
        mData = NS_CONST_CAST(char_type*, char_traits::sEmptyBuffer);
 
500
        mLength = 0;
 
501
        SetDataFlags(F_TERMINATED);
 
502
      }
 
503
    else
 
504
      {
 
505
        char_type* oldData;
 
506
        PRUint32 oldFlags;
 
507
        if (!MutatePrep(capacity, &oldData, &oldFlags))
 
508
          return; // XXX out-of-memory error occured!
 
509
 
 
510
        // compute new string length
 
511
        size_type newLen = NS_MIN(mLength, capacity);
 
512
 
 
513
        if (oldData)
 
514
          {
 
515
            // preserve old data
 
516
            if (mLength > 0)
 
517
              char_traits::copy(mData, oldData, newLen);
 
518
 
 
519
            ::ReleaseData(oldData, oldFlags);
 
520
          }
 
521
 
 
522
        // adjust mLength if our buffer shrunk down in size
 
523
        if (newLen < mLength)
 
524
          mLength = newLen;
 
525
 
 
526
        // always null-terminate here, even if the buffer got longer.  this is
 
527
        // for backwards compat with the old string implementation.
 
528
        mData[capacity] = char_type(0);
 
529
      }
 
530
  }
 
531
 
 
532
void
 
533
nsTSubstring_CharT::SetLength( size_type length )
 
534
  {
 
535
    SetCapacity(length);
 
536
    mLength = length;
 
537
  }
 
538
 
 
539
void
 
540
nsTSubstring_CharT::SetIsVoid( PRBool val )
 
541
  {
 
542
    if (val)
 
543
      {
 
544
        Truncate();
 
545
        mFlags |= F_VOIDED;
 
546
      }
 
547
    else
 
548
      {
 
549
        mFlags &= ~F_VOIDED;
 
550
      }
 
551
  }
 
552
 
 
553
PRBool
 
554
nsTSubstring_CharT::Equals( const self_type& str ) const
 
555
  {
 
556
    return mLength == str.mLength && char_traits::compare(mData, str.mData, mLength) == 0;
 
557
  }
 
558
 
 
559
PRBool
 
560
nsTSubstring_CharT::Equals( const self_type& str, const comparator_type& comp ) const
 
561
  {
 
562
    return mLength == str.mLength && comp(mData, str.mData, mLength) == 0;
 
563
  }
 
564
 
 
565
PRBool
 
566
nsTSubstring_CharT::Equals( const abstract_string_type& readable ) const
 
567
  {
 
568
    const char_type* data;
 
569
    size_type length = readable.GetReadableBuffer(&data);
 
570
 
 
571
    return mLength == length && char_traits::compare(mData, data, mLength) == 0;
 
572
  }
 
573
 
 
574
PRBool
 
575
nsTSubstring_CharT::Equals( const abstract_string_type& readable, const comparator_type& comp ) const
 
576
  {
 
577
    const char_type* data;
 
578
    size_type length = readable.GetReadableBuffer(&data);
 
579
 
 
580
    return mLength == length && comp(mData, data, mLength) == 0;
 
581
  }
 
582
 
 
583
PRBool
 
584
nsTSubstring_CharT::Equals( const char_type* data ) const
 
585
  {
 
586
    // unfortunately, some callers pass null :-(
 
587
    if (!data)
 
588
      {
 
589
        NS_NOTREACHED("null data pointer");
 
590
        return mLength == 0;
 
591
      }
 
592
 
 
593
    // XXX avoid length calculation?
 
594
    size_type length = char_traits::length(data);
 
595
    return mLength == length && char_traits::compare(mData, data, mLength) == 0;
 
596
  }
 
597
 
 
598
PRBool
 
599
nsTSubstring_CharT::Equals( const char_type* data, const comparator_type& comp ) const
 
600
  {
 
601
    // unfortunately, some callers pass null :-(
 
602
    if (!data)
 
603
      {
 
604
        NS_NOTREACHED("null data pointer");
 
605
        return mLength == 0;
 
606
      }
 
607
 
 
608
    // XXX avoid length calculation?
 
609
    size_type length = char_traits::length(data);
 
610
    return mLength == length && comp(mData, data, mLength) == 0;
 
611
  }
 
612
 
 
613
PRBool
 
614
nsTSubstring_CharT::EqualsASCII( const char* data, size_type len ) const
 
615
  {
 
616
    return mLength == len && char_traits::compareASCII(mData, data, len) == 0;
 
617
  }
 
618
 
 
619
PRBool
 
620
nsTSubstring_CharT::EqualsASCII( const char* data ) const
 
621
  {
 
622
    return char_traits::compareASCIINullTerminated(mData, mLength, data) == 0;
 
623
  }
 
624
 
 
625
PRBool
 
626
nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data, size_type len ) const
 
627
  {
 
628
    return mLength == len && char_traits::compareLowerCaseToASCII(mData, data, len) == 0;
 
629
  }
 
630
 
 
631
PRBool
 
632
nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data ) const
 
633
  {
 
634
    return char_traits::compareLowerCaseToASCIINullTerminated(mData, mLength, data) == 0;
 
635
  }
 
636
 
 
637
nsTSubstring_CharT::size_type
 
638
nsTSubstring_CharT::CountChar( char_type c ) const
 
639
  {
 
640
    const char_type *start = mData;
 
641
    const char_type *end   = mData + mLength;
 
642
 
 
643
    return NS_COUNT(start, end, c);
 
644
  }
 
645
 
 
646
PRInt32
 
647
nsTSubstring_CharT::FindChar( char_type c, index_type offset ) const
 
648
  {
 
649
    if (offset < mLength)
 
650
      {
 
651
        const char_type* result = char_traits::find(mData + offset, mLength - offset, c);
 
652
        if (result)
 
653
          return result - mData;
 
654
      }
 
655
    return -1;
 
656
  }