LCOV - code coverage report
Current view: top level - xpcom/ds - nsTArray-inl.h (source / functions) Hit Total Coverage
Test: output.info Lines: 135 165 81.8 %
Date: 2017-07-14 16:53:18 Functions: 47 180 26.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #ifndef nsTArray_h__
       8             : #  error "Don't include this file directly"
       9             : #endif
      10             : 
      11             : template<class Alloc, class Copy>
      12      186087 : nsTArray_base<Alloc, Copy>::nsTArray_base()
      13      186087 :   : mHdr(EmptyHdr())
      14             : {
      15      186085 :   MOZ_COUNT_CTOR(nsTArray_base);
      16      186085 : }
      17             : 
      18             : template<class Alloc, class Copy>
      19      140825 : nsTArray_base<Alloc, Copy>::~nsTArray_base()
      20             : {
      21      140825 :   if (mHdr != EmptyHdr() && !UsesAutoArrayBuffer()) {
      22         366 :     Alloc::Free(mHdr);
      23             :   }
      24      140826 :   MOZ_COUNT_DTOR(nsTArray_base);
      25      140827 : }
      26             : 
      27             : template<class Alloc, class Copy>
      28             : const nsTArrayHeader*
      29      463925 : nsTArray_base<Alloc, Copy>::GetAutoArrayBufferUnsafe(size_t aElemAlign) const
      30             : {
      31             :   // Assuming |this| points to an nsAutoArray, we want to get a pointer to
      32             :   // mAutoBuf.  So just cast |this| to nsAutoArray* and read &mAutoBuf!
      33             : 
      34             :   const void* autoBuf =
      35      463925 :     &reinterpret_cast<const AutoTArray<nsTArray<uint32_t>, 1>*>(this)->mAutoBuf;
      36             : 
      37             :   // If we're on a 32-bit system and aElemAlign is 8, we need to adjust our
      38             :   // pointer to take into account the extra alignment in the auto array.
      39             : 
      40             :   static_assert(sizeof(void*) != 4 ||
      41             :                 (MOZ_ALIGNOF(mozilla::AlignedElem<8>) == 8 &&
      42             :                  sizeof(AutoTArray<mozilla::AlignedElem<8>, 1>) ==
      43             :                    sizeof(void*) + sizeof(nsTArrayHeader) +
      44             :                    4 + sizeof(mozilla::AlignedElem<8>)),
      45             :                 "auto array padding wasn't what we expected");
      46             : 
      47             :   // We don't support alignments greater than 8 bytes.
      48      463925 :   MOZ_ASSERT(aElemAlign <= 4 || aElemAlign == 8,
      49             :              "unsupported alignment.");
      50             :   if (sizeof(void*) == 4 && aElemAlign == 8) {
      51             :     autoBuf = reinterpret_cast<const char*>(autoBuf) + 4;
      52             :   }
      53             : 
      54      463925 :   return reinterpret_cast<const Header*>(autoBuf);
      55             : }
      56             : 
      57             : template<class Alloc, class Copy>
      58             : bool
      59      192381 : nsTArray_base<Alloc, Copy>::UsesAutoArrayBuffer() const
      60             : {
      61      192381 :   if (!mHdr->mIsAutoArray) {
      62       66228 :     return false;
      63             :   }
      64             : 
      65             :   // This is nuts.  If we were sane, we'd pass aElemAlign as a parameter to
      66             :   // this function.  Unfortunately this function is called in nsTArray_base's
      67             :   // destructor, at which point we don't know elem_type's alignment.
      68             :   //
      69             :   // We'll fall on our face and return true when we should say false if
      70             :   //
      71             :   //   * we're not using our auto buffer,
      72             :   //   * aElemAlign == 4, and
      73             :   //   * mHdr == GetAutoArrayBuffer(8).
      74             :   //
      75             :   // This could happen if |*this| lives on the heap and malloc allocated our
      76             :   // buffer on the heap adjacent to |*this|.
      77             :   //
      78             :   // However, we can show that this can't happen.  If |this| is an auto array
      79             :   // (as we ensured at the beginning of the method), GetAutoArrayBuffer(8)
      80             :   // always points to memory owned by |*this|, because (as we assert below)
      81             :   //
      82             :   //   * GetAutoArrayBuffer(8) is at most 4 bytes past GetAutoArrayBuffer(4), and
      83             :   //   * sizeof(nsTArrayHeader) > 4.
      84             :   //
      85             :   // Since AutoTArray always contains an nsTArrayHeader,
      86             :   // GetAutoArrayBuffer(8) will always point inside the auto array object,
      87             :   // even if it doesn't point at the beginning of the header.
      88             :   //
      89             :   // Note that this means that we can't store elements with alignment 16 in an
      90             :   // nsTArray, because GetAutoArrayBuffer(16) could lie outside the memory
      91             :   // owned by this AutoTArray.  We statically assert that elem_type's
      92             :   // alignment is 8 bytes or less in AutoTArray.
      93             : 
      94             :   static_assert(sizeof(nsTArrayHeader) > 4,
      95             :                 "see comment above");
      96             : 
      97             : #ifdef DEBUG
      98      252306 :   ptrdiff_t diff = reinterpret_cast<const char*>(GetAutoArrayBuffer(8)) -
      99      252306 :                    reinterpret_cast<const char*>(GetAutoArrayBuffer(4));
     100      126153 :   MOZ_ASSERT(diff >= 0 && diff <= 4,
     101             :              "GetAutoArrayBuffer doesn't do what we expect.");
     102             : #endif
     103             : 
     104      126153 :   return mHdr == GetAutoArrayBuffer(4) || mHdr == GetAutoArrayBuffer(8);
     105             : }
     106             : 
     107             : // defined in nsTArray.cpp
     108             : bool IsTwiceTheRequiredBytesRepresentableAsUint32(size_t aCapacity,
     109             :                                                   size_t aElemSize);
     110             : 
     111             : template<class Alloc, class Copy>
     112             : template<typename ActualAlloc>
     113             : typename ActualAlloc::ResultTypeProxy
     114      225639 : nsTArray_base<Alloc, Copy>::EnsureCapacity(size_type aCapacity,
     115             :                                            size_type aElemSize)
     116             : {
     117             :   // This should be the most common case so test this first
     118      225639 :   if (aCapacity <= mHdr->mCapacity) {
     119      191164 :     return ActualAlloc::SuccessResult();
     120             :   }
     121             : 
     122             :   // If the requested memory allocation exceeds size_type(-1)/2, then
     123             :   // our doubling algorithm may not be able to allocate it.
     124             :   // Additionally, if it exceeds uint32_t(-1) then we couldn't fit in the
     125             :   // Header::mCapacity member. Just bail out in cases like that.  We don't want
     126             :   // to be allocating 2 GB+ arrays anyway.
     127       34475 :   if (!IsTwiceTheRequiredBytesRepresentableAsUint32(aCapacity, aElemSize)) {
     128           0 :     ActualAlloc::SizeTooBig((size_t)aCapacity * aElemSize);
     129           0 :     return ActualAlloc::FailureResult();
     130             :   }
     131             : 
     132       34476 :   size_t reqSize = sizeof(Header) + aCapacity * aElemSize;
     133             : 
     134       34476 :   if (mHdr == EmptyHdr()) {
     135             :     // Malloc() new data
     136       17920 :     Header* header = static_cast<Header*>(ActualAlloc::Malloc(reqSize));
     137       17920 :     if (!header) {
     138           0 :       return ActualAlloc::FailureResult();
     139             :     }
     140       17920 :     header->mLength = 0;
     141       17920 :     header->mCapacity = aCapacity;
     142       17920 :     header->mIsAutoArray = 0;
     143       17920 :     mHdr = header;
     144             : 
     145       17920 :     return ActualAlloc::SuccessResult();
     146             :   }
     147             : 
     148             :   // We increase our capacity so that the allocated buffer grows exponentially,
     149             :   // which gives us amortized O(1) appending. Below the threshold, we use
     150             :   // powers-of-two. Above the threshold, we grow by at least 1.125, rounding up
     151             :   // to the nearest MiB.
     152       16556 :   const size_t slowGrowthThreshold = 8 * 1024 * 1024;
     153             : 
     154             :   size_t bytesToAlloc;
     155       16556 :   if (reqSize >= slowGrowthThreshold) {
     156           0 :     size_t currSize = sizeof(Header) + Capacity() * aElemSize;
     157           0 :     size_t minNewSize = currSize + (currSize >> 3); // multiply by 1.125
     158           0 :     bytesToAlloc = reqSize > minNewSize ? reqSize : minNewSize;
     159             : 
     160             :     // Round up to the next multiple of MiB.
     161           0 :     const size_t MiB = 1 << 20;
     162           0 :     bytesToAlloc = MiB * ((bytesToAlloc + MiB - 1) / MiB);
     163             :   } else {
     164             :     // Round up to the next power of two.
     165       16556 :     bytesToAlloc = mozilla::RoundUpPow2(reqSize);
     166             :   }
     167             : 
     168             :   Header* header;
     169       16556 :   if (UsesAutoArrayBuffer() || !Copy::allowRealloc) {
     170             :     // Malloc() and copy
     171        4545 :     header = static_cast<Header*>(ActualAlloc::Malloc(bytesToAlloc));
     172        4545 :     if (!header) {
     173           0 :       return ActualAlloc::FailureResult();
     174             :     }
     175             : 
     176        4545 :     Copy::MoveNonOverlappingRegionWithHeader(header, mHdr, Length(), aElemSize);
     177             : 
     178        4545 :     if (!UsesAutoArrayBuffer()) {
     179           9 :       ActualAlloc::Free(mHdr);
     180             :     }
     181             :   } else {
     182             :     // Realloc() existing data
     183       12011 :     header = static_cast<Header*>(ActualAlloc::Realloc(mHdr, bytesToAlloc));
     184       12011 :     if (!header) {
     185           0 :       return ActualAlloc::FailureResult();
     186             :     }
     187             :   }
     188             : 
     189             :   // How many elements can we fit in bytesToAlloc?
     190       16556 :   size_t newCapacity = (bytesToAlloc - sizeof(Header)) / aElemSize;
     191       16556 :   MOZ_ASSERT(newCapacity >= aCapacity, "Didn't enlarge the array enough!");
     192       16556 :   header->mCapacity = newCapacity;
     193             : 
     194       16556 :   mHdr = header;
     195             : 
     196       16556 :   return ActualAlloc::SuccessResult();
     197             : }
     198             : 
     199             : // We don't need use Alloc template parameter specified here because failure to
     200             : // shrink the capacity will leave the array unchanged.
     201             : template<class Alloc, class Copy>
     202             : void
     203       40041 : nsTArray_base<Alloc, Copy>::ShrinkCapacity(size_type aElemSize,
     204             :                                            size_t aElemAlign)
     205             : {
     206       40041 :   if (mHdr == EmptyHdr() || UsesAutoArrayBuffer()) {
     207       28296 :     return;
     208             :   }
     209             : 
     210       11747 :   if (mHdr->mLength >= mHdr->mCapacity) { // should never be greater than...
     211          19 :     return;
     212             :   }
     213             : 
     214       11728 :   size_type length = Length();
     215             : 
     216       11728 :   if (IsAutoArray() && GetAutoArrayBuffer(aElemAlign)->mCapacity >= length) {
     217        1056 :     Header* header = GetAutoArrayBuffer(aElemAlign);
     218             : 
     219             :     // Move the data, but don't copy the header to avoid overwriting mCapacity.
     220        1056 :     header->mLength = length;
     221        1056 :     Copy::MoveNonOverlappingRegion(header + 1, mHdr + 1, length, aElemSize);
     222             : 
     223        1056 :     nsTArrayFallibleAllocator::Free(mHdr);
     224        1056 :     mHdr = header;
     225        1056 :     return;
     226             :   }
     227             : 
     228       10672 :   if (length == 0) {
     229       10672 :     MOZ_ASSERT(!IsAutoArray(), "autoarray should have fit 0 elements");
     230       10672 :     nsTArrayFallibleAllocator::Free(mHdr);
     231       10673 :     mHdr = EmptyHdr();
     232       10673 :     return;
     233             :   }
     234             : 
     235           0 :   size_type size = sizeof(Header) + length * aElemSize;
     236           0 :   void* ptr = nsTArrayFallibleAllocator::Realloc(mHdr, size);
     237           0 :   if (!ptr) {
     238           0 :     return;
     239             :   }
     240           0 :   mHdr = static_cast<Header*>(ptr);
     241           0 :   mHdr->mCapacity = length;
     242             : }
     243             : 
     244             : template<class Alloc, class Copy>
     245             : template<typename ActualAlloc>
     246             : void
     247       78519 : nsTArray_base<Alloc, Copy>::ShiftData(index_type aStart,
     248             :                                       size_type aOldLen, size_type aNewLen,
     249             :                                       size_type aElemSize, size_t aElemAlign)
     250             : {
     251       78519 :   if (aOldLen == aNewLen) {
     252       22612 :     return;
     253             :   }
     254             : 
     255             :   // Determine how many elements need to be shifted
     256       55907 :   size_type num = mHdr->mLength - (aStart + aOldLen);
     257             : 
     258             :   // Compute the resulting length of the array
     259       55907 :   mHdr->mLength += aNewLen - aOldLen;
     260       55907 :   if (mHdr->mLength == 0) {
     261       39295 :     ShrinkCapacity(aElemSize, aElemAlign);
     262             :   } else {
     263             :     // Maybe nothing needs to be shifted
     264       16612 :     if (num == 0) {
     265       14367 :       return;
     266             :     }
     267             :     // Perform shift (change units to bytes first)
     268        2245 :     aStart *= aElemSize;
     269        2245 :     aNewLen *= aElemSize;
     270        2245 :     aOldLen *= aElemSize;
     271        2245 :     char* baseAddr = reinterpret_cast<char*>(mHdr + 1) + aStart;
     272        2245 :     Copy::MoveOverlappingRegion(baseAddr + aNewLen, baseAddr + aOldLen, num, aElemSize);
     273             :   }
     274             : }
     275             : 
     276             : template<class Alloc, class Copy>
     277             : template<typename ActualAlloc>
     278             : bool
     279        2180 : nsTArray_base<Alloc, Copy>::InsertSlotsAt(index_type aIndex, size_type aCount,
     280             :                                           size_type aElemSize,
     281             :                                           size_t aElemAlign)
     282             : {
     283        2180 :   if (MOZ_UNLIKELY(aIndex > Length())) {
     284           0 :     InvalidArrayIndex_CRASH(aIndex, Length());
     285             :   }
     286             : 
     287        2180 :   size_type newLen = Length() + aCount;
     288             : 
     289        2180 :   EnsureCapacity<ActualAlloc>(newLen, aElemSize);
     290             : 
     291             :   // Check for out of memory conditions
     292        2180 :   if (Capacity() < newLen) {
     293           0 :     return false;
     294             :   }
     295             : 
     296             :   // Move the existing elements as needed.  Note that this will
     297             :   // change our mLength, so no need to call IncrementLength.
     298        2180 :   ShiftData<ActualAlloc>(aIndex, 0, aCount, aElemSize, aElemAlign);
     299             : 
     300        2180 :   return true;
     301             : }
     302             : 
     303             : // nsTArray_base::IsAutoArrayRestorer is an RAII class which takes
     304             : // |nsTArray_base &array| in its constructor.  When it's destructed, it ensures
     305             : // that
     306             : //
     307             : //   * array.mIsAutoArray has the same value as it did when we started, and
     308             : //   * if array has an auto buffer and mHdr would otherwise point to sEmptyHdr,
     309             : //     array.mHdr points to array's auto buffer.
     310             : 
     311             : template<class Alloc, class Copy>
     312       40814 : nsTArray_base<Alloc, Copy>::IsAutoArrayRestorer::IsAutoArrayRestorer(
     313             :       nsTArray_base<Alloc, Copy>& aArray,
     314             :       size_t aElemAlign)
     315             :   : mArray(aArray)
     316             :   , mElemAlign(aElemAlign)
     317       40814 :   , mIsAuto(aArray.IsAutoArray())
     318             : {
     319       40814 : }
     320             : 
     321             : template<class Alloc, class Copy>
     322       40814 : nsTArray_base<Alloc, Copy>::IsAutoArrayRestorer::~IsAutoArrayRestorer()
     323             : {
     324             :   // Careful: We don't want to set mIsAutoArray = 1 on sEmptyHdr.
     325       40814 :   if (mIsAuto && mArray.mHdr == mArray.EmptyHdr()) {
     326             :     // Call GetAutoArrayBufferUnsafe() because GetAutoArrayBuffer() asserts
     327             :     // that mHdr->mIsAutoArray is true, which surely isn't the case here.
     328        1821 :     mArray.mHdr = mArray.GetAutoArrayBufferUnsafe(mElemAlign);
     329        1821 :     mArray.mHdr->mLength = 0;
     330       38993 :   } else if (mArray.mHdr != mArray.EmptyHdr()) {
     331       20265 :     mArray.mHdr->mIsAutoArray = mIsAuto;
     332             :   }
     333       40814 : }
     334             : 
     335             : template<class Alloc, class Copy>
     336             : template<typename ActualAlloc, class Allocator>
     337             : typename ActualAlloc::ResultTypeProxy
     338       20407 : nsTArray_base<Alloc, Copy>::SwapArrayElements(nsTArray_base<Allocator,
     339             :                                                             Copy>& aOther,
     340             :                                               size_type aElemSize,
     341             :                                               size_t aElemAlign)
     342             : {
     343             : 
     344             :   // EnsureNotUsingAutoArrayBuffer will set mHdr = sEmptyHdr even if we have an
     345             :   // auto buffer.  We need to point mHdr back to our auto buffer before we
     346             :   // return, otherwise we'll forget that we have an auto buffer at all!
     347             :   // IsAutoArrayRestorer takes care of this for us.
     348             : 
     349       40814 :   IsAutoArrayRestorer ourAutoRestorer(*this, aElemAlign);
     350             :   typename nsTArray_base<Allocator, Copy>::IsAutoArrayRestorer
     351       40814 :     otherAutoRestorer(aOther, aElemAlign);
     352             : 
     353             :   // If neither array uses an auto buffer which is big enough to store the
     354             :   // other array's elements, then ensure that both arrays use malloc'ed storage
     355             :   // and swap their mHdr pointers.
     356       45329 :   if ((!UsesAutoArrayBuffer() || Capacity() < aOther.Length()) &&
     357       12461 :       (!aOther.UsesAutoArrayBuffer() || aOther.Capacity() < Length())) {
     358             : 
     359       24922 :     if (!EnsureNotUsingAutoArrayBuffer<ActualAlloc>(aElemSize) ||
     360       12461 :         !aOther.template EnsureNotUsingAutoArrayBuffer<ActualAlloc>(aElemSize)) {
     361           0 :       return ActualAlloc::FailureResult();
     362             :     }
     363             : 
     364       12461 :     Header* temp = mHdr;
     365       12461 :     mHdr = aOther.mHdr;
     366       12461 :     aOther.mHdr = temp;
     367             : 
     368       12461 :     return ActualAlloc::SuccessResult();
     369             :   }
     370             : 
     371             :   // Swap the two arrays by copying, since at least one is using an auto
     372             :   // buffer which is large enough to hold all of the aOther's elements.  We'll
     373             :   // copy the shorter array into temporary storage.
     374             :   //
     375             :   // (We could do better than this in some circumstances.  Suppose we're
     376             :   // swapping arrays X and Y.  X has space for 2 elements in its auto buffer,
     377             :   // but currently has length 4, so it's using malloc'ed storage.  Y has length
     378             :   // 2.  When we swap X and Y, we don't need to use a temporary buffer; we can
     379             :   // write Y straight into X's auto buffer, write X's malloc'ed buffer on top
     380             :   // of Y, and then switch X to using its auto buffer.)
     381             : 
     382       23838 :   if (!ActualAlloc::Successful(EnsureCapacity<ActualAlloc>(aOther.Length(), aElemSize)) ||
     383       15892 :       !Allocator::Successful(aOther.template EnsureCapacity<Allocator>(Length(), aElemSize))) {
     384           0 :     return ActualAlloc::FailureResult();
     385             :   }
     386             : 
     387             :   // The EnsureCapacity calls above shouldn't have caused *both* arrays to
     388             :   // switch from their auto buffers to malloc'ed space.
     389        7946 :   MOZ_ASSERT(UsesAutoArrayBuffer() || aOther.UsesAutoArrayBuffer(),
     390             :              "One of the arrays should be using its auto buffer.");
     391             : 
     392        7946 :   size_type smallerLength = XPCOM_MIN(Length(), aOther.Length());
     393        7946 :   size_type largerLength = XPCOM_MAX(Length(), aOther.Length());
     394             :   void* smallerElements;
     395             :   void* largerElements;
     396        7946 :   if (Length() <= aOther.Length()) {
     397        7946 :     smallerElements = Hdr() + 1;
     398        7946 :     largerElements = aOther.Hdr() + 1;
     399             :   } else {
     400           0 :     smallerElements = aOther.Hdr() + 1;
     401           0 :     largerElements = Hdr() + 1;
     402             :   }
     403             : 
     404             :   // Allocate temporary storage for the smaller of the two arrays.  We want to
     405             :   // allocate this space on the stack, if it's not too large.  Sounds like a
     406             :   // job for AutoTArray!  (One of the two arrays we're swapping is using an
     407             :   // auto buffer, so we're likely not allocating a lot of space here.  But one
     408             :   // could, in theory, allocate a huge AutoTArray on the heap.)
     409       15892 :   AutoTArray<nsTArray_Impl<uint8_t, ActualAlloc>, 64> temp;
     410        7946 :   if (!ActualAlloc::Successful(temp.template EnsureCapacity<ActualAlloc>(smallerLength,
     411             :                                                                          aElemSize))) {
     412           0 :     return ActualAlloc::FailureResult();
     413             :   }
     414             : 
     415        7946 :   Copy::MoveNonOverlappingRegion(temp.Elements(), smallerElements, smallerLength, aElemSize);
     416        7946 :   Copy::MoveNonOverlappingRegion(smallerElements, largerElements, largerLength, aElemSize);
     417        7946 :   Copy::MoveNonOverlappingRegion(largerElements, temp.Elements(), smallerLength, aElemSize);
     418             : 
     419             :   // Swap the arrays' lengths.
     420        7946 :   MOZ_ASSERT((aOther.Length() == 0 || mHdr != EmptyHdr()) &&
     421             :              (Length() == 0 || aOther.mHdr != EmptyHdr()),
     422             :              "Don't set sEmptyHdr's length.");
     423        7946 :   size_type tempLength = Length();
     424             : 
     425             :   // Avoid writing to EmptyHdr, since it can trigger false
     426             :   // positives with TSan.
     427        7946 :   if (mHdr != EmptyHdr()) {
     428        7946 :     mHdr->mLength = aOther.Length();
     429             :   }
     430        7946 :   if (aOther.mHdr != EmptyHdr()) {
     431        7946 :     aOther.mHdr->mLength = tempLength;
     432             :   }
     433             : 
     434        7946 :   return ActualAlloc::SuccessResult();
     435             : }
     436             : 
     437             : template<class Alloc, class Copy>
     438             : template<typename ActualAlloc>
     439             : bool
     440       24922 : nsTArray_base<Alloc, Copy>::EnsureNotUsingAutoArrayBuffer(size_type aElemSize)
     441             : {
     442       24922 :   if (UsesAutoArrayBuffer()) {
     443             : 
     444             :     // If you call this on a 0-length array, we'll set that array's mHdr to
     445             :     // sEmptyHdr, in flagrant violation of the AutoTArray invariants.  It's
     446             :     // up to you to set it back!  (If you don't, the AutoTArray will forget
     447             :     // that it has an auto buffer.)
     448        1821 :     if (Length() == 0) {
     449        1821 :       mHdr = EmptyHdr();
     450        1821 :       return true;
     451             :     }
     452             : 
     453           0 :     size_type size = sizeof(Header) + Length() * aElemSize;
     454             : 
     455           0 :     Header* header = static_cast<Header*>(ActualAlloc::Malloc(size));
     456           0 :     if (!header) {
     457           0 :       return false;
     458             :     }
     459             : 
     460           0 :     Copy::MoveNonOverlappingRegionWithHeader(header, mHdr, Length(), aElemSize);
     461           0 :     header->mCapacity = Length();
     462           0 :     mHdr = header;
     463             :   }
     464             : 
     465       23101 :   return true;
     466             : }

Generated by: LCOV version 1.13