LCOV - code coverage report
Current view: top level - gfx/graphite2/src - Segment.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 231 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 17 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*  GRAPHITE2 LICENSING
       2             : 
       3             :     Copyright 2010, SIL International
       4             :     All rights reserved.
       5             : 
       6             :     This library is free software; you can redistribute it and/or modify
       7             :     it under the terms of the GNU Lesser General Public License as published
       8             :     by the Free Software Foundation; either version 2.1 of License, or
       9             :     (at your option) any later version.
      10             : 
      11             :     This program is distributed in the hope that it will be useful,
      12             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14             :     Lesser General Public License for more details.
      15             : 
      16             :     You should also have received a copy of the GNU Lesser General Public
      17             :     License along with this library in the file named "LICENSE".
      18             :     If not, write to the Free Software Foundation, 51 Franklin Street, 
      19             :     Suite 500, Boston, MA 02110-1335, USA or visit their web page on the 
      20             :     internet at http://www.fsf.org/licenses/lgpl.html.
      21             : 
      22             : Alternatively, the contents of this file may be used under the terms of the
      23             : Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
      24             : License, as published by the Free Software Foundation, either version 2
      25             : of the License or (at your option) any later version.
      26             : */
      27             : #include "inc/UtfCodec.h"
      28             : #include <cstring>
      29             : #include <cstdlib>
      30             : 
      31             : #include "inc/bits.h"
      32             : #include "inc/Segment.h"
      33             : #include "graphite2/Font.h"
      34             : #include "inc/CharInfo.h"
      35             : #include "inc/debug.h"
      36             : #include "inc/Slot.h"
      37             : #include "inc/Main.h"
      38             : #include "inc/CmapCache.h"
      39             : #include "inc/Collider.h"
      40             : #include "graphite2/Segment.h"
      41             : 
      42             : 
      43             : using namespace graphite2;
      44             : 
      45           0 : Segment::Segment(unsigned int numchars, const Face* face, uint32 script, int textDir)
      46             : : m_freeSlots(NULL),
      47             :   m_freeJustifies(NULL),
      48           0 :   m_charinfo(new CharInfo[numchars]),
      49             :   m_collisions(NULL),
      50             :   m_face(face),
      51           0 :   m_silf(face->chooseSilf(script)),
      52             :   m_first(NULL),
      53             :   m_last(NULL),
      54           0 :   m_bufSize(numchars + 10),
      55             :   m_numGlyphs(numchars),
      56             :   m_numCharinfo(numchars),
      57           0 :   m_passBits(m_silf->aPassBits() ? -1 : 0),
      58             :   m_defaultOriginal(0),
      59             :   m_dir(textDir),
      60           0 :   m_flags(((m_silf->flags() & 0x20) != 0) << 1)
      61             : {
      62           0 :     freeSlot(newSlot());
      63           0 :     m_bufSize = log_binary(numchars)+1;
      64           0 : }
      65             : 
      66           0 : Segment::~Segment()
      67             : {
      68           0 :     for (SlotRope::iterator i = m_slots.begin(); i != m_slots.end(); ++i)
      69           0 :         free(*i);
      70           0 :     for (AttributeRope::iterator i = m_userAttrs.begin(); i != m_userAttrs.end(); ++i)
      71           0 :         free(*i);
      72           0 :     for (JustifyRope::iterator i = m_justifies.begin(); i != m_justifies.end(); ++i)
      73           0 :         free(*i);
      74           0 :     delete[] m_charinfo;
      75           0 :     free(m_collisions);
      76           0 : }
      77             : 
      78             : #ifndef GRAPHITE2_NSEGCACHE
      79             : SegmentScopeState Segment::setScope(Slot * firstSlot, Slot * lastSlot, size_t subLength)
      80             : {
      81             :     SegmentScopeState state;
      82             :     state.numGlyphsOutsideScope = m_numGlyphs - subLength;
      83             :     state.realFirstSlot = m_first;
      84             :     state.slotBeforeScope = firstSlot->prev();
      85             :     state.slotAfterScope = lastSlot->next();
      86             :     state.realLastSlot = m_last;
      87             :     firstSlot->prev(NULL);
      88             :     lastSlot->next(NULL);
      89             :     assert(m_defaultOriginal == 0);
      90             :     m_defaultOriginal = firstSlot->original();
      91             :     m_numGlyphs = subLength;
      92             :     m_first = firstSlot;
      93             :     m_last = lastSlot;
      94             :     return state;
      95             : }
      96             : 
      97             : void Segment::removeScope(SegmentScopeState & state)
      98             : {
      99             :     m_numGlyphs = state.numGlyphsOutsideScope + m_numGlyphs;
     100             :     if (state.slotBeforeScope)
     101             :     {
     102             :         state.slotBeforeScope->next(m_first);
     103             :         m_first->prev(state.slotBeforeScope);
     104             :         m_first = state.realFirstSlot;
     105             :     }
     106             :     if (state.slotAfterScope)
     107             :     {
     108             :         state.slotAfterScope->prev(m_last);
     109             :         m_last->next(state.slotAfterScope);
     110             :         m_last = state.realLastSlot;
     111             :     }
     112             :     m_defaultOriginal = 0;
     113             : }
     114             : 
     115             : #if 0
     116             : void Segment::append(const Segment &other)
     117             : {
     118             :     Rect bbox = other.m_bbox + m_advance;
     119             : 
     120             :     m_slots.insert(m_slots.end(), other.m_slots.begin(), other.m_slots.end());
     121             :     CharInfo* pNewCharInfo = new CharInfo[m_numCharinfo+other.m_numCharinfo];       //since CharInfo has no constructor, this doesn't do much
     122             :     for (unsigned int i=0 ; i<m_numCharinfo ; ++i)
     123             :     pNewCharInfo[i] = m_charinfo[i];
     124             :     m_last->next(other.m_first);
     125             :     other.m_last->prev(m_last);
     126             :     m_userAttrs.insert(m_userAttrs.end(), other.m_userAttrs.begin(), other.m_userAttrs.end());
     127             :     
     128             :     delete[] m_charinfo;
     129             :     m_charinfo = pNewCharInfo;
     130             :     pNewCharInfo += m_numCharinfo ;
     131             :     for (unsigned int i=0 ; i<m_numCharinfo ; ++i)
     132             :         pNewCharInfo[i] = other.m_charinfo[i];
     133             :  
     134             :     m_numCharinfo += other.m_numCharinfo;
     135             :     m_numGlyphs += other.m_numGlyphs;
     136             :     m_advance = m_advance + other.m_advance;
     137             :     m_bbox = m_bbox.widen(bbox);
     138             :     m_passBits &= other.passBits();
     139             : }
     140             : #endif
     141             : #endif // GRAPHITE2_NSEGCACHE
     142             : 
     143           0 : void Segment::appendSlot(int id, int cid, int gid, int iFeats, size_t coffset)
     144             : {
     145           0 :     Slot *aSlot = newSlot();
     146             :     
     147           0 :     if (!aSlot) return;
     148           0 :     m_charinfo[id].init(cid);
     149           0 :     m_charinfo[id].feats(iFeats);
     150           0 :     m_charinfo[id].base(coffset);
     151           0 :     const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid);
     152           0 :     m_charinfo[id].breakWeight(theGlyph ? theGlyph->attrs()[m_silf->aBreak()] : 0);
     153             :     
     154           0 :     aSlot->child(NULL);
     155           0 :     aSlot->setGlyph(this, gid, theGlyph);
     156           0 :     aSlot->originate(id);
     157           0 :     aSlot->before(id);
     158           0 :     aSlot->after(id);
     159           0 :     if (m_last) m_last->next(aSlot);
     160           0 :     aSlot->prev(m_last);
     161           0 :     m_last = aSlot;
     162           0 :     if (!m_first) m_first = aSlot;
     163           0 :     if (theGlyph && m_silf->aPassBits())
     164           0 :         m_passBits &= theGlyph->attrs()[m_silf->aPassBits()] 
     165           0 :                     | (m_silf->numPasses() > 16 ? (theGlyph->attrs()[m_silf->aPassBits() + 1] << 16) : 0);
     166             : }
     167             : 
     168           0 : Slot *Segment::newSlot()
     169             : {
     170           0 :     if (!m_freeSlots)
     171             :     {
     172             :         // check that the segment doesn't grow indefinintely
     173           0 :         if (m_numGlyphs > m_numCharinfo * MAX_SEG_GROWTH_FACTOR)
     174           0 :             return NULL;
     175           0 :         int numUser = m_silf->numUser();
     176             : #if !defined GRAPHITE2_NTRACING
     177             :         if (m_face->logger()) ++numUser;
     178             : #endif
     179           0 :         Slot *newSlots = grzeroalloc<Slot>(m_bufSize);
     180           0 :         int16 *newAttrs = grzeroalloc<int16>(m_bufSize * numUser);
     181           0 :         if (!newSlots || !newAttrs)
     182             :         {
     183           0 :             free(newSlots);
     184           0 :             free(newAttrs);
     185           0 :             return NULL;
     186             :         }
     187           0 :         for (size_t i = 0; i < m_bufSize; i++)
     188             :         {
     189           0 :             ::new (newSlots + i) Slot(newAttrs + i * numUser);
     190           0 :             newSlots[i].next(newSlots + i + 1);
     191             :         }
     192           0 :         newSlots[m_bufSize - 1].next(NULL);
     193           0 :         newSlots[0].next(NULL);
     194           0 :         m_slots.push_back(newSlots);
     195           0 :         m_userAttrs.push_back(newAttrs);
     196           0 :         m_freeSlots = (m_bufSize > 1)? newSlots + 1 : NULL;
     197           0 :         return newSlots;
     198             :     }
     199           0 :     Slot *res = m_freeSlots;
     200           0 :     m_freeSlots = m_freeSlots->next();
     201           0 :     res->next(NULL);
     202           0 :     return res;
     203             : }
     204             : 
     205           0 : void Segment::freeSlot(Slot *aSlot)
     206             : {
     207           0 :     if (m_last == aSlot) m_last = aSlot->prev();
     208           0 :     if (m_first == aSlot) m_first = aSlot->next();
     209           0 :     if (aSlot->attachedTo())
     210           0 :         aSlot->attachedTo()->removeChild(aSlot);
     211           0 :     while (aSlot->firstChild())
     212             :     {
     213           0 :         if (aSlot->firstChild()->attachedTo() == aSlot)
     214             :         {
     215           0 :             aSlot->firstChild()->attachTo(NULL);
     216           0 :             aSlot->removeChild(aSlot->firstChild());
     217             :         }
     218             :         else
     219           0 :             aSlot->firstChild(NULL);
     220             :     }
     221             :     // reset the slot incase it is reused
     222           0 :     ::new (aSlot) Slot(aSlot->userAttrs());
     223           0 :     memset(aSlot->userAttrs(), 0, m_silf->numUser() * sizeof(int16));
     224             :     // Update generation counter for debug
     225             : #if !defined GRAPHITE2_NTRACING
     226             :     if (m_face->logger())
     227             :         ++aSlot->userAttrs()[m_silf->numUser()];
     228             : #endif
     229             :     // update next pointer
     230           0 :     if (!m_freeSlots)
     231           0 :         aSlot->next(NULL);
     232             :     else
     233           0 :         aSlot->next(m_freeSlots);
     234           0 :     m_freeSlots = aSlot;
     235           0 : }
     236             : 
     237           0 : SlotJustify *Segment::newJustify()
     238             : {
     239           0 :     if (!m_freeJustifies)
     240             :     {
     241           0 :         const size_t justSize = SlotJustify::size_of(m_silf->numJustLevels());
     242           0 :         byte *justs = grzeroalloc<byte>(justSize * m_bufSize);
     243           0 :         if (!justs) return NULL;
     244           0 :         for (int i = m_bufSize - 2; i >= 0; --i)
     245             :         {
     246           0 :             SlotJustify *p = reinterpret_cast<SlotJustify *>(justs + justSize * i);
     247           0 :             SlotJustify *next = reinterpret_cast<SlotJustify *>(justs + justSize * (i + 1));
     248           0 :             p->next = next;
     249             :         }
     250           0 :         m_freeJustifies = (SlotJustify *)justs;
     251           0 :         m_justifies.push_back(m_freeJustifies);
     252             :     }
     253           0 :     SlotJustify *res = m_freeJustifies;
     254           0 :     m_freeJustifies = m_freeJustifies->next;
     255           0 :     res->next = NULL;
     256           0 :     return res;
     257             : }
     258             : 
     259           0 : void Segment::freeJustify(SlotJustify *aJustify)
     260             : {
     261           0 :     int numJust = m_silf->numJustLevels();
     262           0 :     if (m_silf->numJustLevels() <= 0) numJust = 1;
     263           0 :     aJustify->next = m_freeJustifies;
     264           0 :     memset(aJustify->values, 0, numJust*SlotJustify::NUMJUSTPARAMS*sizeof(int16));
     265           0 :     m_freeJustifies = aJustify;
     266           0 : }
     267             : 
     268             : #ifndef GRAPHITE2_NSEGCACHE
     269             : void Segment::splice(size_t offset, size_t length, Slot * const startSlot,
     270             :                        Slot * endSlot, const Slot * srcSlot,
     271             :                        const size_t numGlyphs)
     272             : {
     273             :     size_t numChars = length;
     274             :     extendLength(numGlyphs - length);
     275             :     // remove any extra
     276             :     if (numGlyphs < length)
     277             :     {
     278             :         Slot * end = endSlot->next();
     279             :         do
     280             :         {
     281             :             endSlot = endSlot->prev();
     282             :             freeSlot(endSlot->next());
     283             :         } while (numGlyphs < --length);
     284             :         endSlot->next(end);
     285             :         if (end)
     286             :             end->prev(endSlot);
     287             :     }
     288             :     else
     289             :     {
     290             :         // insert extra slots if needed
     291             :         while (numGlyphs > length)
     292             :         {
     293             :             Slot * extra = newSlot();
     294             :             if (!extra) return;
     295             :             extra->prev(endSlot);
     296             :             extra->next(endSlot->next());
     297             :             endSlot->next(extra);
     298             :             if (extra->next())
     299             :                 extra->next()->prev(extra);
     300             :             if (m_last == endSlot)
     301             :                 m_last = extra;
     302             :             endSlot = extra;
     303             :             ++length;
     304             :         }
     305             :     }
     306             : 
     307             :     endSlot = endSlot->next();
     308             :     assert(numGlyphs == length);
     309             :     assert(offset + numChars <= m_numCharinfo);
     310             :     Slot * indexmap[eMaxSpliceSize*3];
     311             :     assert(numGlyphs < sizeof indexmap/sizeof *indexmap);
     312             :     Slot * slot = startSlot;
     313             :     for (uint16 i=0; i < numGlyphs; slot = slot->next(), ++i)
     314             :         indexmap[i] = slot;
     315             : 
     316             :     for (slot = startSlot; slot != endSlot; slot = slot->next(), srcSlot = srcSlot->next())
     317             :     {
     318             :         slot->set(*srcSlot, offset, m_silf->numUser(), m_silf->numJustLevels(), numChars);
     319             :         if (srcSlot->attachedTo())  slot->attachTo(indexmap[srcSlot->attachedTo()->index()]);
     320             :         if (srcSlot->nextSibling()) slot->m_sibling = indexmap[srcSlot->nextSibling()->index()];
     321             :         if (srcSlot->firstChild())  slot->m_child = indexmap[srcSlot->firstChild()->index()];
     322             :     }
     323             : }
     324             : #endif // GRAPHITE2_NSEGCACHE
     325             : 
     326             : // reverse the slots but keep diacritics in their same position after their bases
     327           0 : void Segment::reverseSlots()
     328             : {
     329           0 :     m_dir = m_dir ^ 64;                 // invert the reverse flag
     330           0 :     if (m_first == m_last) return;      // skip 0 or 1 glyph runs
     331             : 
     332           0 :     Slot *t = 0;
     333           0 :     Slot *curr = m_first;
     334             :     Slot *tlast;
     335             :     Slot *tfirst;
     336           0 :     Slot *out = 0;
     337             : 
     338           0 :     while (curr && getSlotBidiClass(curr) == 16)
     339           0 :         curr = curr->next();
     340           0 :     if (!curr) return;
     341           0 :     tfirst = curr->prev();
     342           0 :     tlast = curr;
     343             : 
     344           0 :     while (curr)
     345             :     {
     346           0 :         if (getSlotBidiClass(curr) == 16)
     347             :         {
     348           0 :             Slot *d = curr->next();
     349           0 :             while (d && getSlotBidiClass(d) == 16)
     350           0 :                 d = d->next();
     351             : 
     352           0 :             d = d ? d->prev() : m_last;
     353           0 :             Slot *p = out->next();    // one after the diacritics. out can't be null
     354           0 :             if (p)
     355           0 :                 p->prev(d);
     356             :             else
     357           0 :                 tlast = d;
     358           0 :             t = d->next();
     359           0 :             d->next(p);
     360           0 :             curr->prev(out);
     361           0 :             out->next(curr);
     362             :         }
     363             :         else    // will always fire first time round the loop
     364             :         {
     365           0 :             if (out)
     366           0 :                 out->prev(curr);
     367           0 :             t = curr->next();
     368           0 :             curr->next(out);
     369           0 :             out = curr;
     370             :         }
     371           0 :         curr = t;
     372             :     }
     373           0 :     out->prev(tfirst);
     374           0 :     if (tfirst)
     375           0 :         tfirst->next(out);
     376             :     else
     377           0 :         m_first = out;
     378           0 :     m_last = tlast;
     379             : }
     380             : 
     381           0 : void Segment::linkClusters(Slot *s, Slot * end)
     382             : {
     383           0 :     end = end->next();
     384             : 
     385           0 :     for (; s != end && !s->isBase(); s = s->next());
     386           0 :     Slot * ls = s;
     387             : 
     388           0 :     if (m_dir & 1)
     389             :     {
     390           0 :         for (; s != end; s = s->next())
     391             :         {
     392           0 :             if (!s->isBase())   continue;
     393             : 
     394           0 :             s->sibling(ls);
     395           0 :             ls = s;
     396             :         }
     397             :     }
     398             :     else
     399             :     {
     400           0 :         for (; s != end; s = s->next())
     401             :         {
     402           0 :             if (!s->isBase())   continue;
     403             : 
     404           0 :             ls->sibling(s);
     405           0 :             ls = s;
     406             :         }
     407             :     }
     408           0 : }
     409             : 
     410           0 : Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd, bool isRtl, bool isFinal)
     411             : {
     412           0 :     Position currpos(0., 0.);
     413           0 :     float clusterMin = 0.;
     414           0 :     Rect bbox;
     415           0 :     bool reorder = (currdir() != isRtl);
     416             : 
     417           0 :     if (reorder)
     418             :     {
     419             :         Slot *temp;
     420           0 :         reverseSlots();
     421           0 :         temp = iStart;
     422           0 :         iStart = iEnd;
     423           0 :         iEnd = temp;
     424             :     }
     425           0 :     if (!iStart)    iStart = m_first;
     426           0 :     if (!iEnd)      iEnd   = m_last;
     427             : 
     428           0 :     if (!iStart || !iEnd)   // only true for empty segments
     429           0 :         return currpos;
     430             : 
     431           0 :     if (isRtl)
     432             :     {
     433           0 :         for (Slot * s = iEnd, * const end = iStart->prev(); s && s != end; s = s->prev())
     434             :         {
     435           0 :             if (s->isBase())
     436           0 :                 currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isRtl, isFinal);
     437             :         }
     438             :     }
     439             :     else
     440             :     {
     441           0 :         for (Slot * s = iStart, * const end = iEnd->next(); s && s != end; s = s->next())
     442             :         {
     443           0 :             if (s->isBase())
     444           0 :                 currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isRtl, isFinal);
     445             :         }
     446             :     }
     447           0 :     if (reorder)
     448           0 :         reverseSlots();
     449           0 :     return currpos;
     450             : }
     451             : 
     452             : 
     453           0 : void Segment::associateChars(int offset, int numChars)
     454             : {
     455           0 :     int i = 0, j = 0;
     456             :     CharInfo *c, *cend;
     457           0 :     for (c = m_charinfo + offset, cend = m_charinfo + offset + numChars; c != cend; ++c)
     458             :     {
     459           0 :         c->before(-1);
     460           0 :         c->after(-1);
     461             :     }
     462           0 :     for (Slot * s = m_first; s; s->index(i++), s = s->next())
     463             :     {
     464           0 :         j = s->before();
     465           0 :         if (j < 0)  continue;
     466             : 
     467           0 :         for (const int after = s->after(); j <= after; ++j)
     468             :         {
     469           0 :             c = charinfo(j);
     470           0 :             if (c->before() == -1 || i < c->before())   c->before(i);
     471           0 :             if (c->after() < i)                         c->after(i);
     472             :         }
     473             :     }
     474           0 :     for (Slot *s = m_first; s; s = s->next())
     475             :     {
     476             :         int a;
     477           0 :         for (a = s->after() + 1; a < offset + numChars && charinfo(a)->after() < 0; ++a)
     478           0 :         { charinfo(a)->after(s->index()); }
     479           0 :         --a;
     480           0 :         s->after(a);
     481             : 
     482           0 :         for (a = s->before() - 1; a >= offset && charinfo(a)->before() < 0; --a)
     483           0 :         { charinfo(a)->before(s->index()); }
     484           0 :         ++a;
     485           0 :         s->before(a);
     486             :     }
     487           0 : }
     488             : 
     489             : 
     490             : template <typename utf_iter>
     491           0 : inline void process_utf_data(Segment & seg, const Face & face, const int fid, utf_iter c, size_t n_chars)
     492             : {
     493           0 :     const Cmap    & cmap = face.cmap();
     494           0 :     int slotid = 0;
     495             : 
     496           0 :     const typename utf_iter::codeunit_type * const base = c;
     497           0 :     for (; n_chars; --n_chars, ++c, ++slotid)
     498             :     {
     499           0 :         const uint32 usv = *c;
     500           0 :         uint16 gid = cmap[usv];
     501           0 :         if (!gid)   gid = face.findPseudo(usv);
     502           0 :         seg.appendSlot(slotid, usv, gid, fid, c - base);
     503             :     }
     504           0 : }
     505             : 
     506             : 
     507           0 : bool Segment::read_text(const Face *face, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void* pStart, size_t nChars)
     508             : {
     509           0 :     assert(face);
     510           0 :     assert(pFeats);
     511           0 :     if (!m_charinfo) return false;
     512             : 
     513             :     // utf iterator is self recovering so we don't care about the error state of the iterator.
     514           0 :     switch (enc)
     515             :     {
     516           0 :     case gr_utf8:   process_utf_data(*this, *face, addFeatures(*pFeats), utf8::const_iterator(pStart), nChars); break;
     517           0 :     case gr_utf16:  process_utf_data(*this, *face, addFeatures(*pFeats), utf16::const_iterator(pStart), nChars); break;
     518           0 :     case gr_utf32:  process_utf_data(*this, *face, addFeatures(*pFeats), utf32::const_iterator(pStart), nChars); break;
     519             :     }
     520           0 :     return true;
     521             : }
     522             : 
     523           0 : void Segment::doMirror(uint16 aMirror)
     524             : {
     525             :     Slot * s;
     526           0 :     for (s = m_first; s; s = s->next())
     527             :     {
     528           0 :         unsigned short g = glyphAttr(s->gid(), aMirror);
     529           0 :         if (g && (!(dir() & 4) || !glyphAttr(s->gid(), aMirror + 1)))
     530           0 :             s->setGlyph(this, g);
     531             :     }
     532           0 : }
     533             : 
     534           0 : bool Segment::initCollisions()
     535             : {
     536           0 :     m_collisions = grzeroalloc<SlotCollision>(slotCount());
     537           0 :     if (!m_collisions) return false;
     538             : 
     539           0 :     for (Slot *p = m_first; p; p = p->next())
     540           0 :         if (p->index() < slotCount())
     541           0 :             ::new (collisionInfo(p)) SlotCollision(this, p);
     542             :         else
     543           0 :             return false;
     544           0 :     return true;
     545             : }

Generated by: LCOV version 1.13