Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/harfbuzz/src/./../../../../gfx/harfbuzz/src/graph/pairpos-graph.hh
Warning:line 283, column 9
Value stored to 'class_def_1_size' is never read

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_harfbuzz_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/harfbuzz/src -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/harfbuzz/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 HAVE_OT=1 -D HAVE_ROUND=1 -D HB_NO_BUFFER_VERIFY -D HB_NO_FALLBACK_SHAPE -D HB_NO_PAINT -D HB_NO_UCD -D HB_NO_UNICODE_FUNCS -D HB_NO_VAR_COMPOSITES -D HAVE_MMAP=1 -D HAVE_SYS_MMAN_H=1 -D HAVE_UNISTD_H=1 -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/harfbuzz/src -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/harfbuzz/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 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -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_harfbuzz_src0.cpp
1/*
2 * Copyright © 2022 Google, Inc.
3 *
4 * This is part of HarfBuzz, a text shaping library.
5 *
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
11 *
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16 * DAMAGE.
17 *
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23 *
24 * Google Author(s): Garret Rieger
25 */
26
27#ifndef GRAPH_PAIRPOS_GRAPH_HH
28#define GRAPH_PAIRPOS_GRAPH_HH
29
30#include "split-helpers.hh"
31#include "coverage-graph.hh"
32#include "classdef-graph.hh"
33#include "../OT/Layout/GPOS/PairPos.hh"
34#include "../OT/Layout/GPOS/PosLookupSubTable.hh"
35
36namespace graph {
37
38struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes>
39{
40 bool sanitize (const graph_t::vertex_t& vertex) const
41 {
42 size_t vertex_len = vertex.obj.tail - vertex.obj.head;
43 unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes>::min_size;
44 if (vertex_len < min_size) return false;
45 hb_barrier ();
46
47 return vertex_len >=
48 min_size + pairSet.get_size () - pairSet.len.get_size();
49 }
50
51 hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
52 unsigned this_index)
53 {
54 hb_set_t visited;
55
56 const unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage);
57 const unsigned coverage_size = c.graph.vertices_[coverage_id].table_size ();
58 const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes>::min_size;
59
60 unsigned partial_coverage_size = 4;
61 unsigned accumulated = base_size;
62 hb_vector_t<unsigned> split_points;
63 for (unsigned i = 0; i < pairSet.len; i++)
64 {
65 unsigned pair_set_index = pair_set_graph_index (c, this_index, i);
66 unsigned accumulated_delta =
67 c.graph.find_subgraph_size (pair_set_index, visited) +
68 SmallTypes::size; // for PairSet offset.
69 partial_coverage_size += OT::HBUINT16::static_size;
70
71 accumulated += accumulated_delta;
72 unsigned total = accumulated + hb_min (partial_coverage_size, coverage_size);
73
74 if (total >= (1 << 16))
75 {
76 split_points.push (i);
77 accumulated = base_size + accumulated_delta;
78 partial_coverage_size = 6;
79 visited.clear (); // node sharing isn't allowed between splits.
80 }
81 }
82
83 split_context_t split_context {
84 c,
85 this,
86 this_index,
87 };
88
89 return actuate_subtable_split<split_context_t> (split_context, split_points);
90 }
91
92 private:
93
94 struct split_context_t {
95 gsubgpos_graph_context_t& c;
96 PairPosFormat1* thiz;
97 unsigned this_index;
98
99 unsigned original_count ()
100 {
101 return thiz->pairSet.len;
102 }
103
104 unsigned clone_range (unsigned start, unsigned end)
105 {
106 return thiz->clone_range (this->c, this->this_index, start, end);
107 }
108
109 bool shrink (unsigned count)
110 {
111 return thiz->shrink (this->c, this->this_index, count);
112 }
113 };
114
115 bool shrink (gsubgpos_graph_context_t& c,
116 unsigned this_index,
117 unsigned count)
118 {
119 DEBUG_MSG (SUBSET_REPACK, nullptr,_hb_debug_msg<(0 +0)> ("SUBSET_REPACK", (nullptr), nullptr
, false, 0, 0, " Shrinking PairPosFormat1 (%u) to [0, %u).",
this_index, count)
120 " Shrinking PairPosFormat1 (%u) to [0, %u).",_hb_debug_msg<(0 +0)> ("SUBSET_REPACK", (nullptr), nullptr
, false, 0, 0, " Shrinking PairPosFormat1 (%u) to [0, %u).",
this_index, count)
121 this_index,_hb_debug_msg<(0 +0)> ("SUBSET_REPACK", (nullptr), nullptr
, false, 0, 0, " Shrinking PairPosFormat1 (%u) to [0, %u).",
this_index, count)
122 count)_hb_debug_msg<(0 +0)> ("SUBSET_REPACK", (nullptr), nullptr
, false, 0, 0, " Shrinking PairPosFormat1 (%u) to [0, %u).",
this_index, count)
;
123 unsigned old_count = pairSet.len;
124 if (count >= old_count)
125 return true;
126
127 pairSet.len = count;
128 c.graph.vertices_[this_index].obj.tail -= (old_count - count) * SmallTypes::size;
129
130 auto coverage = c.graph.as_mutable_table<Coverage> (this_index, &this->coverage);
131 if (!coverage) return false;
132
133 unsigned coverage_size = coverage.vertex->table_size ();
134 auto new_coverage =
135 + hb_zip (coverage.table->iter (), hb_range ())
136 | hb_filter ([&] (hb_pair_t<unsigned, unsigned> p) {
137 return p.second < count;
138 })
139 | hb_map_retains_sorting (hb_first)
140 ;
141
142 return Coverage::make_coverage (c, new_coverage, coverage.index, coverage_size);
143 }
144
145 // Create a new PairPos including PairSet's from start (inclusive) to end (exclusive).
146 // Returns object id of the new object.
147 unsigned clone_range (gsubgpos_graph_context_t& c,
148 unsigned this_index,
149 unsigned start, unsigned end) const
150 {
151 DEBUG_MSG (SUBSET_REPACK, nullptr,_hb_debug_msg<(0 +0)> ("SUBSET_REPACK", (nullptr), nullptr
, false, 0, 0, " Cloning PairPosFormat1 (%u) range [%u, %u)."
, this_index, start, end)
152 " Cloning PairPosFormat1 (%u) range [%u, %u).", this_index, start, end)_hb_debug_msg<(0 +0)> ("SUBSET_REPACK", (nullptr), nullptr
, false, 0, 0, " Cloning PairPosFormat1 (%u) range [%u, %u)."
, this_index, start, end)
;
153
154 unsigned num_pair_sets = end - start;
155 unsigned prime_size = OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes>::min_size
156 + num_pair_sets * SmallTypes::size;
157
158 unsigned pair_pos_prime_id = c.create_node (prime_size);
159 if (pair_pos_prime_id == (unsigned) -1) return -1;
160
161 PairPosFormat1* pair_pos_prime = (PairPosFormat1*) c.graph.object (pair_pos_prime_id).head;
162 pair_pos_prime->format = this->format;
163 pair_pos_prime->valueFormat[0] = this->valueFormat[0];
164 pair_pos_prime->valueFormat[1] = this->valueFormat[1];
165 pair_pos_prime->pairSet.len = num_pair_sets;
166
167 for (unsigned i = start; i < end; i++)
168 {
169 c.graph.move_child<> (this_index,
170 &pairSet[i],
171 pair_pos_prime_id,
172 &pair_pos_prime->pairSet[i - start]);
173 }
174
175 unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage);
176 if (!Coverage::clone_coverage (c,
177 coverage_id,
178 pair_pos_prime_id,
179 2,
180 start, end))
181 return -1;
182
183 return pair_pos_prime_id;
184 }
185
186
187
188 unsigned pair_set_graph_index (gsubgpos_graph_context_t& c, unsigned this_index, unsigned i) const
189 {
190 return c.graph.index_for_offset (this_index, &pairSet[i]);
191 }
192};
193
194struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes>
195{
196 bool sanitize (const graph_t::vertex_t& vertex) const
197 {
198 size_t vertex_len = vertex.table_size ();
199 unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes>::min_size;
200 if (vertex_len < min_size) return false;
201 hb_barrier ();
202
203 const unsigned class1_count = class1Count;
204 return vertex_len >=
205 min_size + class1_count * get_class1_record_size ();
206 }
207
208 hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
209 unsigned this_index)
210 {
211 const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes>::min_size;
212 const unsigned class_def_2_size = size_of (c, this_index, &classDef2);
213 const Coverage* coverage = get_coverage (c, this_index);
214 const ClassDef* class_def_1 = get_class_def_1 (c, this_index);
215 auto gid_and_class =
216 + coverage->iter ()
217 | hb_map_retains_sorting ([&] (hb_codepoint_t gid) {
218 return hb_codepoint_pair_t (gid, class_def_1->get_class (gid));
219 })
220 ;
221 class_def_size_estimator_t estimator (gid_and_class);
222
223 const unsigned class1_count = class1Count;
224 const unsigned class2_count = class2Count;
225 const unsigned class1_record_size = get_class1_record_size ();
226
227 const unsigned value_1_len = valueFormat1.get_len ();
228 const unsigned value_2_len = valueFormat2.get_len ();
229 const unsigned total_value_len = value_1_len + value_2_len;
230
231 unsigned accumulated = base_size;
232 unsigned coverage_size = 4;
233 unsigned class_def_1_size = 4;
234 unsigned max_coverage_size = coverage_size;
235 unsigned max_class_def_1_size = class_def_1_size;
236
237 hb_vector_t<unsigned> split_points;
238
239 hb_hashmap_t<unsigned, unsigned> device_tables = get_all_device_tables (c, this_index);
240 hb_vector_t<unsigned> format1_device_table_indices = valueFormat1.get_device_table_indices ();
241 hb_vector_t<unsigned> format2_device_table_indices = valueFormat2.get_device_table_indices ();
242 bool has_device_tables = bool(format1_device_table_indices) || bool(format2_device_table_indices);
243
244 hb_set_t visited;
245 for (unsigned i = 0; i < class1_count; i++)
246 {
247 unsigned accumulated_delta = class1_record_size;
248 class_def_1_size = estimator.add_class_def_size (i);
249 coverage_size = estimator.coverage_size ();
250 max_coverage_size = hb_max (max_coverage_size, coverage_size);
251 max_class_def_1_size = hb_max (max_class_def_1_size, class_def_1_size);
252
253 if (has_device_tables) {
254 for (unsigned j = 0; j < class2_count; j++)
255 {
256 unsigned value1_index = total_value_len * (class2_count * i + j);
257 unsigned value2_index = value1_index + value_1_len;
258 accumulated_delta += size_of_value_record_children (c,
259 device_tables,
260 format1_device_table_indices,
261 value1_index,
262 visited);
263 accumulated_delta += size_of_value_record_children (c,
264 device_tables,
265 format2_device_table_indices,
266 value2_index,
267 visited);
268 }
269 }
270
271 accumulated += accumulated_delta;
272 unsigned total = accumulated
273 + coverage_size + class_def_1_size + class_def_2_size
274 // The largest object will pack last and can exceed the size limit.
275 - hb_max (hb_max (coverage_size, class_def_1_size), class_def_2_size);
276 if (total >= (1 << 16))
277 {
278 split_points.push (i);
279 // split does not include i, so add the size for i when we reset the size counters.
280 accumulated = base_size + accumulated_delta;
281
282 estimator.reset();
283 class_def_1_size = estimator.add_class_def_size(i);
Value stored to 'class_def_1_size' is never read
284 coverage_size = estimator.coverage_size();
285 visited.clear (); // node sharing isn't allowed between splits.
286 }
287 }
288
289 split_context_t split_context {
290 c,
291 this,
292 this_index,
293 class1_record_size,
294 total_value_len,
295 value_1_len,
296 value_2_len,
297 max_coverage_size,
298 max_class_def_1_size,
299 device_tables,
300 format1_device_table_indices,
301 format2_device_table_indices
302 };
303
304 return actuate_subtable_split<split_context_t> (split_context, split_points);
305 }
306 private:
307
308 struct split_context_t
309 {
310 gsubgpos_graph_context_t& c;
311 PairPosFormat2* thiz;
312 unsigned this_index;
313 unsigned class1_record_size;
314 unsigned value_record_len;
315 unsigned value1_record_len;
316 unsigned value2_record_len;
317 unsigned max_coverage_size;
318 unsigned max_class_def_size;
319
320 const hb_hashmap_t<unsigned, unsigned>& device_tables;
321 const hb_vector_t<unsigned>& format1_device_table_indices;
322 const hb_vector_t<unsigned>& format2_device_table_indices;
323
324 unsigned original_count ()
325 {
326 return thiz->class1Count;
327 }
328
329 unsigned clone_range (unsigned start, unsigned end)
330 {
331 return thiz->clone_range (*this, start, end);
332 }
333
334 bool shrink (unsigned count)
335 {
336 return thiz->shrink (*this, count);
337 }
338 };
339
340 size_t get_class1_record_size () const
341 {
342 const size_t class2_count = class2Count;
343 return
344 class2_count * (valueFormat1.get_size () + valueFormat2.get_size ());
345 }
346
347 unsigned clone_range (split_context_t& split_context,
348 unsigned start, unsigned end) const
349 {
350 DEBUG_MSG (SUBSET_REPACK, nullptr,_hb_debug_msg<(0 +0)> ("SUBSET_REPACK", (nullptr), nullptr
, false, 0, 0, " Cloning PairPosFormat2 (%u) range [%u, %u)."
, split_context.this_index, start, end)
351 " Cloning PairPosFormat2 (%u) range [%u, %u).", split_context.this_index, start, end)_hb_debug_msg<(0 +0)> ("SUBSET_REPACK", (nullptr), nullptr
, false, 0, 0, " Cloning PairPosFormat2 (%u) range [%u, %u)."
, split_context.this_index, start, end)
;
352
353 graph_t& graph = split_context.c.graph;
354
355 unsigned num_records = end - start;
356 unsigned prime_size = OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes>::min_size
357 + num_records * split_context.class1_record_size;
358
359 unsigned pair_pos_prime_id = split_context.c.create_node (prime_size);
360 if (pair_pos_prime_id == (unsigned) -1) return -1;
361
362 PairPosFormat2* pair_pos_prime =
363 (PairPosFormat2*) graph.object (pair_pos_prime_id).head;
364 pair_pos_prime->format = this->format;
365 pair_pos_prime->valueFormat1 = this->valueFormat1;
366 pair_pos_prime->valueFormat2 = this->valueFormat2;
367 pair_pos_prime->class1Count = num_records;
368 pair_pos_prime->class2Count = this->class2Count;
369 clone_class1_records (split_context,
370 pair_pos_prime_id,
371 start,
372 end);
373
374 unsigned coverage_id =
375 graph.index_for_offset (split_context.this_index, &coverage);
376 unsigned class_def_1_id =
377 graph.index_for_offset (split_context.this_index, &classDef1);
378 auto& coverage_v = graph.vertices_[coverage_id];
379 auto& class_def_1_v = graph.vertices_[class_def_1_id];
380 Coverage* coverage_table = (Coverage*) coverage_v.obj.head;
381 ClassDef* class_def_1_table = (ClassDef*) class_def_1_v.obj.head;
382 if (!coverage_table
383 || !coverage_table->sanitize (coverage_v)
384 || !class_def_1_table
385 || !class_def_1_table->sanitize (class_def_1_v))
386 return -1;
387
388 auto klass_map =
389 + coverage_table->iter ()
390 | hb_map_retains_sorting ([&] (hb_codepoint_t gid) {
391 return hb_codepoint_pair_t (gid, class_def_1_table->get_class (gid));
392 })
393 | hb_filter ([&] (hb_codepoint_t klass) {
394 return klass >= start && klass < end;
395 }, hb_second)
396 | hb_map_retains_sorting ([&] (hb_codepoint_pair_t gid_and_class) {
397 // Classes must be from 0...N so subtract start
398 return hb_codepoint_pair_t (gid_and_class.first, gid_and_class.second - start);
399 })
400 ;
401
402 if (!Coverage::add_coverage (split_context.c,
403 pair_pos_prime_id,
404 2,
405 + klass_map | hb_map_retains_sorting (hb_first),
406 split_context.max_coverage_size))
407 return -1;
408
409 // classDef1
410 if (!ClassDef::add_class_def (split_context.c,
411 pair_pos_prime_id,
412 8,
413 + klass_map,
414 split_context.max_class_def_size))
415 return -1;
416
417 // classDef2
418 unsigned class_def_2_id =
419 graph.index_for_offset (split_context.this_index, &classDef2);
420 auto* class_def_link = graph.vertices_[pair_pos_prime_id].obj.real_links.push ();
421 class_def_link->width = SmallTypes::size;
422 class_def_link->objidx = class_def_2_id;
423 class_def_link->position = 10;
424 graph.vertices_[class_def_2_id].add_parent (pair_pos_prime_id, false);
425
426 return pair_pos_prime_id;
427 }
428
429 void clone_class1_records (split_context_t& split_context,
430 unsigned pair_pos_prime_id,
431 unsigned start, unsigned end) const
432 {
433 PairPosFormat2* pair_pos_prime =
434 (PairPosFormat2*) split_context.c.graph.object (pair_pos_prime_id).head;
435
436 char* start_addr = ((char*)&values[0]) + start * split_context.class1_record_size;
437 unsigned num_records = end - start;
438 hb_memcpy (&pair_pos_prime->values[0],
439 start_addr,
440 num_records * split_context.class1_record_size);
441
442 if (!split_context.format1_device_table_indices
443 && !split_context.format2_device_table_indices)
444 // No device tables to move over.
445 return;
446
447 unsigned class2_count = class2Count;
448 for (unsigned i = start; i < end; i++)
449 {
450 for (unsigned j = 0; j < class2_count; j++)
451 {
452 unsigned value1_index = split_context.value_record_len * (class2_count * i + j);
453 unsigned value2_index = value1_index + split_context.value1_record_len;
454
455 unsigned new_value1_index = split_context.value_record_len * (class2_count * (i - start) + j);
456 unsigned new_value2_index = new_value1_index + split_context.value1_record_len;
457
458 transfer_device_tables (split_context,
459 pair_pos_prime_id,
460 split_context.format1_device_table_indices,
461 value1_index,
462 new_value1_index);
463
464 transfer_device_tables (split_context,
465 pair_pos_prime_id,
466 split_context.format2_device_table_indices,
467 value2_index,
468 new_value2_index);
469 }
470 }
471 }
472
473 void transfer_device_tables (split_context_t& split_context,
474 unsigned pair_pos_prime_id,
475 const hb_vector_t<unsigned>& device_table_indices,
476 unsigned old_value_record_index,
477 unsigned new_value_record_index) const
478 {
479 PairPosFormat2* pair_pos_prime =
480 (PairPosFormat2*) split_context.c.graph.object (pair_pos_prime_id).head;
481
482 for (unsigned i : device_table_indices)
483 {
484 OT::Offset16* record = (OT::Offset16*) &values[old_value_record_index + i];
485 unsigned record_position = ((char*) record) - ((char*) this);
486 if (!split_context.device_tables.has (record_position)) continue;
487
488 split_context.c.graph.move_child (
489 split_context.this_index,
490 record,
491 pair_pos_prime_id,
492 (OT::Offset16*) &pair_pos_prime->values[new_value_record_index + i]);
493 }
494 }
495
496 bool shrink (split_context_t& split_context,
497 unsigned count)
498 {
499 DEBUG_MSG (SUBSET_REPACK, nullptr,_hb_debug_msg<(0 +0)> ("SUBSET_REPACK", (nullptr), nullptr
, false, 0, 0, " Shrinking PairPosFormat2 (%u) to [0, %u).",
split_context.this_index, count)
500 " Shrinking PairPosFormat2 (%u) to [0, %u).",_hb_debug_msg<(0 +0)> ("SUBSET_REPACK", (nullptr), nullptr
, false, 0, 0, " Shrinking PairPosFormat2 (%u) to [0, %u).",
split_context.this_index, count)
501 split_context.this_index,_hb_debug_msg<(0 +0)> ("SUBSET_REPACK", (nullptr), nullptr
, false, 0, 0, " Shrinking PairPosFormat2 (%u) to [0, %u).",
split_context.this_index, count)
502 count)_hb_debug_msg<(0 +0)> ("SUBSET_REPACK", (nullptr), nullptr
, false, 0, 0, " Shrinking PairPosFormat2 (%u) to [0, %u).",
split_context.this_index, count)
;
503 unsigned old_count = class1Count;
504 if (count >= old_count)
505 return true;
506
507 graph_t& graph = split_context.c.graph;
508 class1Count = count;
509 graph.vertices_[split_context.this_index].obj.tail -=
510 (old_count - count) * split_context.class1_record_size;
511
512 auto coverage =
513 graph.as_mutable_table<Coverage> (split_context.this_index, &this->coverage);
514 if (!coverage) return false;
515
516 auto class_def_1 =
517 graph.as_mutable_table<ClassDef> (split_context.this_index, &classDef1);
518 if (!class_def_1) return false;
519
520 auto klass_map =
521 + coverage.table->iter ()
522 | hb_map_retains_sorting ([&] (hb_codepoint_t gid) {
523 return hb_codepoint_pair_t (gid, class_def_1.table->get_class (gid));
524 })
525 | hb_filter ([&] (hb_codepoint_t klass) {
526 return klass < count;
527 }, hb_second)
528 ;
529
530 auto new_coverage = + klass_map | hb_map_retains_sorting (hb_first);
531 if (!Coverage::make_coverage (split_context.c,
532 + new_coverage,
533 coverage.index,
534 // existing ranges my not be kept, worst case size is a format 1
535 // coverage table.
536 4 + new_coverage.len() * 2))
537 return false;
538
539 return ClassDef::make_class_def (split_context.c,
540 + klass_map,
541 class_def_1.index,
542 class_def_1.vertex->table_size ());
543 }
544
545 hb_hashmap_t<unsigned, unsigned>
546 get_all_device_tables (gsubgpos_graph_context_t& c,
547 unsigned this_index) const
548 {
549 const auto& v = c.graph.vertices_[this_index];
550 return v.position_to_index_map ();
551 }
552
553 const Coverage* get_coverage (gsubgpos_graph_context_t& c,
554 unsigned this_index) const
555 {
556 unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage);
557 auto& coverage_v = c.graph.vertices_[coverage_id];
558
559 Coverage* coverage_table = (Coverage*) coverage_v.obj.head;
560 if (!coverage_table || !coverage_table->sanitize (coverage_v))
561 return &Null(Coverage)NullHelper<Coverage>::get_null ();
562 return coverage_table;
563 }
564
565 const ClassDef* get_class_def_1 (gsubgpos_graph_context_t& c,
566 unsigned this_index) const
567 {
568 unsigned class_def_1_id = c.graph.index_for_offset (this_index, &classDef1);
569 auto& class_def_1_v = c.graph.vertices_[class_def_1_id];
570
571 ClassDef* class_def_1_table = (ClassDef*) class_def_1_v.obj.head;
572 if (!class_def_1_table || !class_def_1_table->sanitize (class_def_1_v))
573 return &Null(ClassDef)NullHelper<ClassDef>::get_null ();
574 return class_def_1_table;
575 }
576
577 unsigned size_of_value_record_children (gsubgpos_graph_context_t& c,
578 const hb_hashmap_t<unsigned, unsigned>& device_tables,
579 const hb_vector_t<unsigned> device_table_indices,
580 unsigned value_record_index,
581 hb_set_t& visited)
582 {
583 unsigned size = 0;
584 for (unsigned i : device_table_indices)
585 {
586 OT::Layout::GPOS_impl::Value* record = &values[value_record_index + i];
587 unsigned record_position = ((char*) record) - ((char*) this);
588 unsigned* obj_idx;
589 if (!device_tables.has (record_position, &obj_idx)) continue;
590 size += c.graph.find_subgraph_size (*obj_idx, visited);
591 }
592 return size;
593 }
594
595 unsigned size_of (gsubgpos_graph_context_t& c,
596 unsigned this_index,
597 const void* offset) const
598 {
599 const unsigned id = c.graph.index_for_offset (this_index, offset);
600 return c.graph.vertices_[id].table_size ();
601 }
602};
603
604struct PairPos : public OT::Layout::GPOS_impl::PairPos
605{
606 hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
607 unsigned this_index)
608 {
609 switch (u.format.v) {
610 case 1:
611 hb_barrier ();
612 return ((PairPosFormat1*)(&u.format1))->split_subtables (c, this_index);
613 case 2:
614 hb_barrier ();
615 return ((PairPosFormat2*)(&u.format2))->split_subtables (c, this_index);
616#ifndef HB_NO_BEYOND_64K
617 case 3: HB_FALLTHROUGH[[clang::fallthrough]];
618 case 4: HB_FALLTHROUGH[[clang::fallthrough]];
619 // Don't split 24bit PairPos's.
620#endif
621 default:
622 return hb_vector_t<unsigned> ();
623 }
624 }
625
626 bool sanitize (const graph_t::vertex_t& vertex) const
627 {
628 size_t vertex_len = vertex.obj.tail - vertex.obj.head;
629 if (vertex_len < u.format.v.get_size ()) return false;
630 hb_barrier ();
631
632 switch (u.format.v) {
633 case 1:
634 hb_barrier ();
635 return ((PairPosFormat1*)(&u.format1))->sanitize (vertex);
636 case 2:
637 hb_barrier ();
638 return ((PairPosFormat2*)(&u.format2))->sanitize (vertex);
639#ifndef HB_NO_BEYOND_64K
640 case 3: HB_FALLTHROUGH[[clang::fallthrough]];
641 case 4: HB_FALLTHROUGH[[clang::fallthrough]];
642#endif
643 default:
644 // We don't handle format 3 and 4 here.
645 return false;
646 }
647 }
648};
649
650}
651
652#endif // GRAPH_PAIRPOS_GRAPH_HH