Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/workdir/UnpackedTarball/graphite/src/Segment.cpp
Line
Count
Source
1
// SPDX-License-Identifier: MIT OR MPL-2.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later
2
// Copyright 2010, SIL International, All rights reserved.
3
4
#include "inc/UtfCodec.h"
5
#include <cstring>
6
#include <cstdlib>
7
8
#include "inc/bits.h"
9
#include "inc/Segment.h"
10
#include "graphite2/Font.h"
11
#include "inc/CharInfo.h"
12
#include "inc/debug.h"
13
#include "inc/Slot.h"
14
#include "inc/Main.h"
15
#include "inc/CmapCache.h"
16
#include "inc/Collider.h"
17
#include "graphite2/Segment.h"
18
19
20
using namespace graphite2;
21
22
Segment::Segment(size_t numchars, const Face* face, uint32 script, int textDir)
23
0
: m_freeSlots(NULL),
24
0
  m_freeJustifies(NULL),
25
0
  m_charinfo(new CharInfo[numchars]),
26
0
  m_collisions(NULL),
27
0
  m_face(face),
28
0
  m_silf(face->chooseSilf(script)),
29
0
  m_first(NULL),
30
0
  m_last(NULL),
31
0
  m_bufSize(numchars + 10),
32
0
  m_numGlyphs(numchars),
33
0
  m_numCharinfo(numchars),
34
0
  m_defaultOriginal(0),
35
0
  m_dir(textDir),
36
0
  m_flags(((m_silf->flags() & 0x20) != 0) << 1),
37
0
  m_passBits(m_silf->aPassBits() ? -1 : 0)
38
0
{
39
0
    freeSlot(newSlot());
40
0
    m_bufSize = log_binary(numchars)+1;
41
0
}
42
43
Segment::~Segment()
44
0
{
45
0
    for (SlotRope::iterator i = m_slots.begin(); i != m_slots.end(); ++i)
46
0
        free(*i);
47
0
    for (AttributeRope::iterator i = m_userAttrs.begin(); i != m_userAttrs.end(); ++i)
48
0
        free(*i);
49
0
    for (JustifyRope::iterator i = m_justifies.begin(); i != m_justifies.end(); ++i)
50
0
        free(*i);
51
0
    delete[] m_charinfo;
52
0
    free(m_collisions);
53
0
}
54
55
void Segment::appendSlot(int id, int cid, int gid, int iFeats, size_t coffset)
56
0
{
57
0
    Slot *aSlot = newSlot();
58
59
0
    if (!aSlot) return;
60
0
    m_charinfo[id].init(cid);
61
0
    m_charinfo[id].feats(iFeats);
62
0
    m_charinfo[id].base(coffset);
63
0
    const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid);
64
0
    m_charinfo[id].breakWeight(theGlyph ? theGlyph->attrs()[m_silf->aBreak()] : 0);
65
66
0
    aSlot->child(NULL);
67
0
    aSlot->setGlyph(this, gid, theGlyph);
68
0
    aSlot->originate(id);
69
0
    aSlot->before(id);
70
0
    aSlot->after(id);
71
0
    if (m_last) m_last->next(aSlot);
72
0
    aSlot->prev(m_last);
73
0
    m_last = aSlot;
74
0
    if (!m_first) m_first = aSlot;
75
0
    if (theGlyph && m_silf->aPassBits())
76
0
        m_passBits &= theGlyph->attrs()[m_silf->aPassBits()]
77
0
                    | (m_silf->numPasses() > 16 ? (theGlyph->attrs()[m_silf->aPassBits() + 1] << 16) : 0);
78
0
}
79
80
Slot *Segment::newSlot()
81
0
{
82
0
    if (!m_freeSlots)
83
0
    {
84
        // check that the segment doesn't grow indefinintely
85
0
        if (m_numGlyphs > m_numCharinfo * MAX_SEG_GROWTH_FACTOR)
86
0
            return NULL;
87
0
        int numUser = m_silf->numUser();
88
#if !defined GRAPHITE2_NTRACING
89
        if (m_face->logger()) ++numUser;
90
#endif
91
0
        Slot *newSlots = grzeroalloc<Slot>(m_bufSize);
92
0
        int16 *newAttrs = grzeroalloc<int16>(m_bufSize * numUser);
93
0
        if (!newSlots || !newAttrs)
94
0
        {
95
0
            free(newSlots);
96
0
            free(newAttrs);
97
0
            return NULL;
98
0
        }
99
0
        for (size_t i = 0; i < m_bufSize; i++)
100
0
        {
101
0
            ::new (newSlots + i) Slot(newAttrs + i * numUser);
102
0
            newSlots[i].next(newSlots + i + 1);
103
0
        }
104
0
        newSlots[m_bufSize - 1].next(NULL);
105
0
        newSlots[0].next(NULL);
106
0
        m_slots.push_back(newSlots);
107
0
        m_userAttrs.push_back(newAttrs);
108
0
        m_freeSlots = (m_bufSize > 1)? newSlots + 1 : NULL;
109
0
        return newSlots;
110
0
    }
111
0
    Slot *res = m_freeSlots;
112
0
    m_freeSlots = m_freeSlots->next();
113
0
    res->next(NULL);
114
0
    return res;
115
0
}
116
117
void Segment::freeSlot(Slot *aSlot)
118
0
{
119
0
    if (aSlot == nullptr) return;
120
0
    if (m_last == aSlot) m_last = aSlot->prev();
121
0
    if (m_first == aSlot) m_first = aSlot->next();
122
0
    if (aSlot->attachedTo())
123
0
        aSlot->attachedTo()->removeChild(aSlot);
124
0
    while (aSlot->firstChild())
125
0
    {
126
0
        if (aSlot->firstChild()->attachedTo() == aSlot)
127
0
        {
128
0
            aSlot->firstChild()->attachTo(nullptr);
129
0
            aSlot->removeChild(aSlot->firstChild());
130
0
        }
131
0
        else
132
0
            aSlot->firstChild(nullptr);
133
0
    }
134
    // reset the slot incase it is reused
135
0
    ::new (aSlot) Slot(aSlot->userAttrs());
136
0
    memset(aSlot->userAttrs(), 0, m_silf->numUser() * sizeof(int16));
137
    // Update generation counter for debug
138
#if !defined GRAPHITE2_NTRACING
139
    if (m_face->logger())
140
        ++aSlot->userAttrs()[m_silf->numUser()];
141
#endif
142
    // update next pointer
143
0
    if (!m_freeSlots)
144
0
        aSlot->next(nullptr);
145
0
    else
146
0
        aSlot->next(m_freeSlots);
147
0
    m_freeSlots = aSlot;
148
0
}
149
150
SlotJustify *Segment::newJustify()
151
0
{
152
0
    if (!m_freeJustifies)
153
0
    {
154
0
        const size_t justSize = SlotJustify::size_of(m_silf->numJustLevels());
155
0
        byte *justs = grzeroalloc<byte>(justSize * m_bufSize);
156
0
        if (!justs) return NULL;
157
0
        for (ptrdiff_t i = m_bufSize - 2; i >= 0; --i)
158
0
        {
159
0
            SlotJustify *p = reinterpret_cast<SlotJustify *>(justs + justSize * i);
160
0
            SlotJustify *next = reinterpret_cast<SlotJustify *>(justs + justSize * (i + 1));
161
0
            p->next = next;
162
0
        }
163
0
        m_freeJustifies = (SlotJustify *)justs;
164
0
        m_justifies.push_back(m_freeJustifies);
165
0
    }
166
0
    SlotJustify *res = m_freeJustifies;
167
0
    m_freeJustifies = m_freeJustifies->next;
168
0
    res->next = NULL;
169
0
    return res;
170
0
}
171
172
void Segment::freeJustify(SlotJustify *aJustify)
173
0
{
174
0
    int numJust = m_silf->numJustLevels();
175
0
    if (m_silf->numJustLevels() <= 0) numJust = 1;
176
0
    aJustify->next = m_freeJustifies;
177
0
    memset(aJustify->values, 0, numJust*SlotJustify::NUMJUSTPARAMS*sizeof(int16));
178
0
    m_freeJustifies = aJustify;
179
0
}
180
181
// reverse the slots but keep diacritics in their same position after their bases
182
void Segment::reverseSlots()
183
0
{
184
0
    m_dir = m_dir ^ 64;                 // invert the reverse flag
185
0
    if (m_first == m_last) return;      // skip 0 or 1 glyph runs
186
187
0
    Slot *t = 0;
188
0
    Slot *curr = m_first;
189
0
    Slot *tlast;
190
0
    Slot *tfirst;
191
0
    Slot *out = 0;
192
193
0
    while (curr && getSlotBidiClass(curr) == 16)
194
0
        curr = curr->next();
195
0
    if (!curr) return;
196
0
    tfirst = curr->prev();
197
0
    tlast = curr;
198
199
0
    while (curr)
200
0
    {
201
0
        if (getSlotBidiClass(curr) == 16)
202
0
        {
203
0
            Slot *d = curr->next();
204
0
            while (d && getSlotBidiClass(d) == 16)
205
0
                d = d->next();
206
207
0
            d = d ? d->prev() : m_last;
208
0
            Slot *p = out->next();    // one after the diacritics. out can't be null
209
0
            if (p)
210
0
                p->prev(d);
211
0
            else
212
0
                tlast = d;
213
0
            t = d->next();
214
0
            d->next(p);
215
0
            curr->prev(out);
216
0
            out->next(curr);
217
0
        }
218
0
        else    // will always fire first time round the loop
219
0
        {
220
0
            if (out)
221
0
                out->prev(curr);
222
0
            t = curr->next();
223
0
            curr->next(out);
224
0
            out = curr;
225
0
        }
226
0
        curr = t;
227
0
    }
228
0
    out->prev(tfirst);
229
0
    if (tfirst)
230
0
        tfirst->next(out);
231
0
    else
232
0
        m_first = out;
233
0
    m_last = tlast;
234
0
}
235
236
void Segment::linkClusters(Slot *s, Slot * end)
237
0
{
238
0
    end = end->next();
239
240
0
    for (; s != end && !s->isBase(); s = s->next());
241
0
    Slot * ls = s;
242
243
0
    if (m_dir & 1)
244
0
    {
245
0
        for (; s != end; s = s->next())
246
0
        {
247
0
            if (!s->isBase())   continue;
248
249
0
            s->sibling(ls);
250
0
            ls = s;
251
0
        }
252
0
    }
253
0
    else
254
0
    {
255
0
        for (; s != end; s = s->next())
256
0
        {
257
0
            if (!s->isBase())   continue;
258
259
0
            ls->sibling(s);
260
0
            ls = s;
261
0
        }
262
0
    }
263
0
}
264
265
Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd, bool isRtl, bool isFinal)
266
0
{
267
0
    Position currpos(0., 0.);
268
0
    float clusterMin = 0.;
269
0
    Rect bbox;
270
0
    bool reorder = (currdir() != isRtl);
271
272
0
    if (reorder)
273
0
    {
274
0
        Slot *temp;
275
0
        reverseSlots();
276
0
        temp = iStart;
277
0
        iStart = iEnd;
278
0
        iEnd = temp;
279
0
    }
280
0
    if (!iStart)    iStart = m_first;
281
0
    if (!iEnd)      iEnd   = m_last;
282
283
0
    if (!iStart || !iEnd)   // only true for empty segments
284
0
        return currpos;
285
286
0
    if (isRtl)
287
0
    {
288
0
        for (Slot * s = iEnd, * const end = iStart->prev(); s && s != end; s = s->prev())
289
0
        {
290
0
            if (s->isBase())
291
0
                currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isRtl, isFinal);
292
0
        }
293
0
    }
294
0
    else
295
0
    {
296
0
        for (Slot * s = iStart, * const end = iEnd->next(); s && s != end; s = s->next())
297
0
        {
298
0
            if (s->isBase())
299
0
                currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isRtl, isFinal);
300
0
        }
301
0
    }
302
0
    if (reorder)
303
0
        reverseSlots();
304
0
    return currpos;
305
0
}
306
307
308
void Segment::associateChars(int offset, size_t numChars)
309
0
{
310
0
    int i = 0, j = 0;
311
0
    CharInfo *c, *cend;
312
0
    for (c = m_charinfo + offset, cend = m_charinfo + offset + numChars; c != cend; ++c)
313
0
    {
314
0
        c->before(-1);
315
0
        c->after(-1);
316
0
    }
317
0
    for (Slot * s = m_first; s; s->index(i++), s = s->next())
318
0
    {
319
0
        j = s->before();
320
0
        if (j < 0)  continue;
321
322
0
        for (const int after = s->after(); j <= after; ++j)
323
0
        {
324
0
            c = charinfo(j);
325
0
            if (c->before() == -1 || i < c->before())   c->before(i);
326
0
            if (c->after() < i)                         c->after(i);
327
0
        }
328
0
    }
329
0
    for (Slot *s = m_first; s; s = s->next())
330
0
    {
331
0
        int a;
332
0
        for (a = s->after() + 1; a < offset + int(numChars) && charinfo(a)->after() < 0; ++a)
333
0
        { charinfo(a)->after(s->index()); }
334
0
        --a;
335
0
        s->after(a);
336
337
0
        for (a = s->before() - 1; a >= offset && charinfo(a)->before() < 0; --a)
338
0
        { charinfo(a)->before(s->index()); }
339
0
        ++a;
340
0
        s->before(a);
341
0
    }
342
0
}
343
344
345
template <typename utf_iter>
346
inline void process_utf_data(Segment & seg, const Face & face, const int fid, utf_iter c, size_t n_chars)
347
0
{
348
0
    const Cmap    & cmap = face.cmap();
349
0
    int slotid = 0;
350
351
0
    const typename utf_iter::codeunit_type * const base = c;
352
0
    for (; n_chars; --n_chars, ++c, ++slotid)
353
0
    {
354
0
        const uint32 usv = *c;
355
0
        uint16 gid = cmap[usv];
356
0
        if (!gid)   gid = face.findPseudo(usv);
357
0
        seg.appendSlot(slotid, usv, gid, fid, c - base);
358
0
    }
359
0
}
Unexecuted instantiation: void process_utf_data<graphite2::_utf_iterator<unsigned char const> >(graphite2::Segment&, graphite2::Face const&, int, graphite2::_utf_iterator<unsigned char const>, unsigned long)
Unexecuted instantiation: void process_utf_data<graphite2::_utf_iterator<unsigned short const> >(graphite2::Segment&, graphite2::Face const&, int, graphite2::_utf_iterator<unsigned short const>, unsigned long)
Unexecuted instantiation: void process_utf_data<graphite2::_utf_iterator<unsigned int const> >(graphite2::Segment&, graphite2::Face const&, int, graphite2::_utf_iterator<unsigned int const>, unsigned long)
360
361
362
bool Segment::read_text(const Face *face, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void* pStart, size_t nChars)
363
0
{
364
0
    assert(face);
365
0
    assert(pFeats);
366
0
    if (!m_charinfo) return false;
367
368
    // utf iterator is self recovering so we don't care about the error state of the iterator.
369
0
    switch (enc)
370
0
    {
371
0
    case gr_utf8:   process_utf_data(*this, *face, addFeatures(*pFeats), utf8::const_iterator(pStart), nChars); break;
372
0
    case gr_utf16:  process_utf_data(*this, *face, addFeatures(*pFeats), utf16::const_iterator(pStart), nChars); break;
373
0
    case gr_utf32:  process_utf_data(*this, *face, addFeatures(*pFeats), utf32::const_iterator(pStart), nChars); break;
374
0
    }
375
0
    return true;
376
0
}
377
378
void Segment::doMirror(uint16 aMirror)
379
0
{
380
0
    Slot * s;
381
0
    for (s = m_first; s; s = s->next())
382
0
    {
383
0
        unsigned short g = glyphAttr(s->gid(), aMirror);
384
0
        if (g && (!(dir() & 4) || !glyphAttr(s->gid(), aMirror + 1)))
385
0
            s->setGlyph(this, g);
386
0
    }
387
0
}
388
389
bool Segment::initCollisions()
390
0
{
391
0
    m_collisions = grzeroalloc<SlotCollision>(slotCount());
392
0
    if (!m_collisions) return false;
393
394
0
    for (Slot *p = m_first; p; p = p->next())
395
0
        if (p->index() < slotCount())
396
0
            ::new (collisionInfo(p)) SlotCollision(this, p);
397
0
        else
398
0
            return false;
399
0
    return true;
400
0
}