Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/graphite2/src/./../../../../gfx/graphite2/src/Segment.cpp
Warning:line 291, column 69
Although the value stored to 'clusterMin' is used in the enclosing expression, the value is never actually read from 'clusterMin'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_gfx_graphite2_src0.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/graphite2/src -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/graphite2/src -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D PACKAGE_VERSION="moz" -D PACKAGE_BUGREPORT="http://bugzilla.mozilla.org/" -D GRAPHITE2_STATIC -D GRAPHITE2_CUSTOM_HEADER="MozGrMalloc.h" -D GRAPHITE2_NFILEFACE -D GRAPHITE2_NTRACING -D GRAPHITE2_NSEGCACHE -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /root/firefox-clang/gfx/graphite2/src -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/graphite2/src -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ Unified_cpp_gfx_graphite2_src0.cpp
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
20using namespace graphite2;
21
22Segment::Segment(size_t numchars, const Face* face, uint32 script, int textDir)
23: m_freeSlots(NULL__null),
24 m_freeJustifies(NULL__null),
25 m_charinfo(new CharInfo[numchars]),
26 m_collisions(NULL__null),
27 m_face(face),
28 m_silf(face->chooseSilf(script)),
29 m_first(NULL__null),
30 m_last(NULL__null),
31 m_bufSize(numchars + 10),
32 m_numGlyphs(numchars),
33 m_numCharinfo(numchars),
34 m_defaultOriginal(0),
35 m_dir(textDir),
36 m_flags(((m_silf->flags() & 0x20) != 0) << 1),
37 m_passBits(m_silf->aPassBits() ? -1 : 0)
38{
39 freeSlot(newSlot());
40 m_bufSize = log_binary(numchars)+1;
41}
42
43Segment::~Segment()
44{
45 for (SlotRope::iterator i = m_slots.begin(); i != m_slots.end(); ++i)
46 free(*i);
47 for (AttributeRope::iterator i = m_userAttrs.begin(); i != m_userAttrs.end(); ++i)
48 free(*i);
49 for (JustifyRope::iterator i = m_justifies.begin(); i != m_justifies.end(); ++i)
50 free(*i);
51 delete[] m_charinfo;
52 free(m_collisions);
53}
54
55void Segment::appendSlot(int id, int cid, int gid, int iFeats, size_t coffset)
56{
57 Slot *aSlot = newSlot();
58
59 if (!aSlot) return;
60 m_charinfo[id].init(cid);
61 m_charinfo[id].feats(iFeats);
62 m_charinfo[id].base(coffset);
63 const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid);
64 m_charinfo[id].breakWeight(theGlyph ? theGlyph->attrs()[m_silf->aBreak()] : 0);
65
66 aSlot->child(NULL__null);
67 aSlot->setGlyph(this, gid, theGlyph);
68 aSlot->originate(id);
69 aSlot->before(id);
70 aSlot->after(id);
71 if (m_last) m_last->next(aSlot);
72 aSlot->prev(m_last);
73 m_last = aSlot;
74 if (!m_first) m_first = aSlot;
75 if (theGlyph && m_silf->aPassBits())
76 m_passBits &= theGlyph->attrs()[m_silf->aPassBits()]
77 | (m_silf->numPasses() > 16 ? (theGlyph->attrs()[m_silf->aPassBits() + 1] << 16) : 0);
78}
79
80Slot *Segment::newSlot()
81{
82 if (!m_freeSlots)
83 {
84 // check that the segment doesn't grow indefinintely
85 if (m_numGlyphs > m_numCharinfo * MAX_SEG_GROWTH_FACTOR64)
86 return NULL__null;
87 int numUser = m_silf->numUser();
88#if !defined GRAPHITE2_NTRACING1
89 if (m_face->logger()) ++numUser;
90#endif
91 Slot *newSlots = grzeroalloc<Slot>(m_bufSize);
92 int16 *newAttrs = grzeroalloc<int16>(m_bufSize * numUser);
93 if (!newSlots || !newAttrs)
94 {
95 free(newSlots);
96 free(newAttrs);
97 return NULL__null;
98 }
99 for (size_t i = 0; i < m_bufSize; i++)
100 {
101 ::new (newSlots + i) Slot(newAttrs + i * numUser);
102 newSlots[i].next(newSlots + i + 1);
103 }
104 newSlots[m_bufSize - 1].next(NULL__null);
105 newSlots[0].next(NULL__null);
106 m_slots.push_back(newSlots);
107 m_userAttrs.push_back(newAttrs);
108 m_freeSlots = (m_bufSize > 1)? newSlots + 1 : NULL__null;
109 return newSlots;
110 }
111 Slot *res = m_freeSlots;
112 m_freeSlots = m_freeSlots->next();
113 res->next(NULL__null);
114 return res;
115}
116
117void Segment::freeSlot(Slot *aSlot)
118{
119 if (aSlot == nullptr) return;
120 if (m_last == aSlot) m_last = aSlot->prev();
121 if (m_first == aSlot) m_first = aSlot->next();
122 if (aSlot->attachedTo())
123 aSlot->attachedTo()->removeChild(aSlot);
124 while (aSlot->firstChild())
125 {
126 if (aSlot->firstChild()->attachedTo() == aSlot)
127 {
128 aSlot->firstChild()->attachTo(nullptr);
129 aSlot->removeChild(aSlot->firstChild());
130 }
131 else
132 aSlot->firstChild(nullptr);
133 }
134 // reset the slot incase it is reused
135 ::new (aSlot) Slot(aSlot->userAttrs());
136 memset(aSlot->userAttrs(), 0, m_silf->numUser() * sizeof(int16));
137 // Update generation counter for debug
138#if !defined GRAPHITE2_NTRACING1
139 if (m_face->logger())
140 ++aSlot->userAttrs()[m_silf->numUser()];
141#endif
142 // update next pointer
143 if (!m_freeSlots)
144 aSlot->next(nullptr);
145 else
146 aSlot->next(m_freeSlots);
147 m_freeSlots = aSlot;
148}
149
150SlotJustify *Segment::newJustify()
151{
152 if (!m_freeJustifies)
153 {
154 const size_t justSize = SlotJustify::size_of(m_silf->numJustLevels());
155 byte *justs = grzeroalloc<byte>(justSize * m_bufSize);
156 if (!justs) return NULL__null;
157 for (ptrdiff_t i = m_bufSize - 2; i >= 0; --i)
158 {
159 SlotJustify *p = reinterpret_cast<SlotJustify *>(justs + justSize * i);
160 SlotJustify *next = reinterpret_cast<SlotJustify *>(justs + justSize * (i + 1));
161 p->next = next;
162 }
163 m_freeJustifies = (SlotJustify *)justs;
164 m_justifies.push_back(m_freeJustifies);
165 }
166 SlotJustify *res = m_freeJustifies;
167 m_freeJustifies = m_freeJustifies->next;
168 res->next = NULL__null;
169 return res;
170}
171
172void Segment::freeJustify(SlotJustify *aJustify)
173{
174 int numJust = m_silf->numJustLevels();
175 if (m_silf->numJustLevels() <= 0) numJust = 1;
176 aJustify->next = m_freeJustifies;
177 memset(aJustify->values, 0, numJust*SlotJustify::NUMJUSTPARAMS*sizeof(int16));
178 m_freeJustifies = aJustify;
179}
180
181// reverse the slots but keep diacritics in their same position after their bases
182void Segment::reverseSlots()
183{
184 m_dir = m_dir ^ 64; // invert the reverse flag
185 if (m_first == m_last) return; // skip 0 or 1 glyph runs
186
187 Slot *t = 0;
188 Slot *curr = m_first;
189 Slot *tlast;
190 Slot *tfirst;
191 Slot *out = 0;
192
193 while (curr && getSlotBidiClass(curr) == 16)
194 curr = curr->next();
195 if (!curr) return;
196 tfirst = curr->prev();
197 tlast = curr;
198
199 while (curr)
200 {
201 if (getSlotBidiClass(curr) == 16)
202 {
203 Slot *d = curr->next();
204 while (d && getSlotBidiClass(d) == 16)
205 d = d->next();
206
207 d = d ? d->prev() : m_last;
208 Slot *p = out->next(); // one after the diacritics. out can't be null
209 if (p)
210 p->prev(d);
211 else
212 tlast = d;
213 t = d->next();
214 d->next(p);
215 curr->prev(out);
216 out->next(curr);
217 }
218 else // will always fire first time round the loop
219 {
220 if (out)
221 out->prev(curr);
222 t = curr->next();
223 curr->next(out);
224 out = curr;
225 }
226 curr = t;
227 }
228 out->prev(tfirst);
229 if (tfirst)
230 tfirst->next(out);
231 else
232 m_first = out;
233 m_last = tlast;
234}
235
236void Segment::linkClusters(Slot *s, Slot * end)
237{
238 end = end->next();
239
240 for (; s != end && !s->isBase(); s = s->next());
241 Slot * ls = s;
242
243 if (m_dir & 1)
244 {
245 for (; s != end; s = s->next())
246 {
247 if (!s->isBase()) continue;
248
249 s->sibling(ls);
250 ls = s;
251 }
252 }
253 else
254 {
255 for (; s != end; s = s->next())
256 {
257 if (!s->isBase()) continue;
258
259 ls->sibling(s);
260 ls = s;
261 }
262 }
263}
264
265Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd, bool isRtl, bool isFinal)
266{
267 Position currpos(0., 0.);
268 float clusterMin = 0.;
269 Rect bbox;
270 bool reorder = (currdir() != isRtl);
271
272 if (reorder)
273 {
274 Slot *temp;
275 reverseSlots();
276 temp = iStart;
277 iStart = iEnd;
278 iEnd = temp;
279 }
280 if (!iStart) iStart = m_first;
281 if (!iEnd) iEnd = m_last;
282
283 if (!iStart || !iEnd) // only true for empty segments
284 return currpos;
285
286 if (isRtl)
287 {
288 for (Slot * s = iEnd, * const end = iStart->prev(); s && s != end; s = s->prev())
289 {
290 if (s->isBase())
291 currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isRtl, isFinal);
Although the value stored to 'clusterMin' is used in the enclosing expression, the value is never actually read from 'clusterMin'
292 }
293 }
294 else
295 {
296 for (Slot * s = iStart, * const end = iEnd->next(); s && s != end; s = s->next())
297 {
298 if (s->isBase())
299 currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isRtl, isFinal);
300 }
301 }
302 if (reorder)
303 reverseSlots();
304 return currpos;
305}
306
307
308void Segment::associateChars(int offset, size_t numChars)
309{
310 int i = 0, j = 0;
311 CharInfo *c, *cend;
312 for (c = m_charinfo + offset, cend = m_charinfo + offset + numChars; c != cend; ++c)
313 {
314 c->before(-1);
315 c->after(-1);
316 }
317 for (Slot * s = m_first; s; s->index(i++), s = s->next())
318 {
319 j = s->before();
320 if (j < 0) continue;
321
322 for (const int after = s->after(); j <= after; ++j)
323 {
324 c = charinfo(j);
325 if (c->before() == -1 || i < c->before()) c->before(i);
326 if (c->after() < i) c->after(i);
327 }
328 }
329 for (Slot *s = m_first; s; s = s->next())
330 {
331 int a;
332 for (a = s->after() + 1; a < offset + int(numChars) && charinfo(a)->after() < 0; ++a)
333 { charinfo(a)->after(s->index()); }
334 --a;
335 s->after(a);
336
337 for (a = s->before() - 1; a >= offset && charinfo(a)->before() < 0; --a)
338 { charinfo(a)->before(s->index()); }
339 ++a;
340 s->before(a);
341 }
342}
343
344
345template <typename utf_iter>
346inline void process_utf_data(Segment & seg, const Face & face, const int fid, utf_iter c, size_t n_chars)
347{
348 const Cmap & cmap = face.cmap();
349 int slotid = 0;
350
351 const typename utf_iter::codeunit_type * const base = c;
352 for (; n_chars; --n_chars, ++c, ++slotid)
353 {
354 const uint32 usv = *c;
355 uint16 gid = cmap[usv];
356 if (!gid) gid = face.findPseudo(usv);
357 seg.appendSlot(slotid, usv, gid, fid, c - base);
358 }
359}
360
361
362bool Segment::read_text(const Face *face, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void* pStart, size_t nChars)
363{
364 assert(face)(static_cast <bool> (face) ? void (0) : __assert_fail (
"face", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
365 assert(pFeats)(static_cast <bool> (pFeats) ? void (0) : __assert_fail
("pFeats", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
;
366 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 switch (enc)
370 {
371 case gr_utf8: process_utf_data(*this, *face, addFeatures(*pFeats), utf8::const_iterator(pStart), nChars); break;
372 case gr_utf16: process_utf_data(*this, *face, addFeatures(*pFeats), utf16::const_iterator(pStart), nChars); break;
373 case gr_utf32: process_utf_data(*this, *face, addFeatures(*pFeats), utf32::const_iterator(pStart), nChars); break;
374 }
375 return true;
376}
377
378void Segment::doMirror(uint16 aMirror)
379{
380 Slot * s;
381 for (s = m_first; s; s = s->next())
382 {
383 unsigned short g = glyphAttr(s->gid(), aMirror);
384 if (g && (!(dir() & 4) || !glyphAttr(s->gid(), aMirror + 1)))
385 s->setGlyph(this, g);
386 }
387}
388
389bool Segment::initCollisions()
390{
391 m_collisions = grzeroalloc<SlotCollision>(slotCount());
392 if (!m_collisions) return false;
393
394 for (Slot *p = m_first; p; p = p->next())
395 if (p->index() < slotCount())
396 ::new (collisionInfo(p)) SlotCollision(this, p);
397 else
398 return false;
399 return true;
400}