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_MARKBASEPOS_GRAPH_HH 28 #define GRAPH_MARKBASEPOS_GRAPH_HH 29 30 #include "split-helpers.hh" 31 #include "coverage-graph.hh" 32 #include "../OT/Layout/GPOS/MarkBasePos.hh" 33 #include "../OT/Layout/GPOS/PosLookupSubTable.hh" 34 35 namespace graph { 36 37 struct AnchorMatrix : public OT::Layout::GPOS_impl::AnchorMatrix 38 { sanitizegraph::AnchorMatrix39 bool sanitize (graph_t::vertex_t& vertex, unsigned class_count) const 40 { 41 int64_t vertex_len = vertex.obj.tail - vertex.obj.head; 42 if (vertex_len < AnchorMatrix::min_size) return false; 43 44 return vertex_len >= AnchorMatrix::min_size + 45 OT::Offset16::static_size * class_count * this->rows; 46 } 47 shrinkgraph::AnchorMatrix48 bool shrink (gsubgpos_graph_context_t& c, 49 unsigned this_index, 50 unsigned old_class_count, 51 unsigned new_class_count) 52 { 53 if (new_class_count >= old_class_count) return false; 54 auto& o = c.graph.vertices_[this_index].obj; 55 unsigned base_count = rows; 56 o.tail = o.head + 57 AnchorMatrix::min_size + 58 OT::Offset16::static_size * base_count * new_class_count; 59 60 // Reposition links into the new indexing scheme. 61 for (auto& link : o.real_links.writer ()) 62 { 63 unsigned index = (link.position - 2) / 2; 64 unsigned base = index / old_class_count; 65 unsigned klass = index % old_class_count; 66 if (klass >= new_class_count) 67 // should have already been removed 68 return false; 69 70 unsigned new_index = base * new_class_count + klass; 71 72 link.position = (char*) &(this->matrixZ[new_index]) - (char*) this; 73 } 74 75 return true; 76 } 77 clonegraph::AnchorMatrix78 unsigned clone (gsubgpos_graph_context_t& c, 79 unsigned this_index, 80 unsigned start, 81 unsigned end, 82 unsigned class_count) 83 { 84 unsigned base_count = rows; 85 unsigned new_class_count = end - start; 86 unsigned size = AnchorMatrix::min_size + 87 OT::Offset16::static_size * new_class_count * rows; 88 unsigned prime_id = c.create_node (size); 89 if (prime_id == (unsigned) -1) return -1; 90 AnchorMatrix* prime = (AnchorMatrix*) c.graph.object (prime_id).head; 91 prime->rows = base_count; 92 93 auto& o = c.graph.vertices_[this_index].obj; 94 int num_links = o.real_links.length; 95 for (int i = 0; i < num_links; i++) 96 { 97 const auto& link = o.real_links[i]; 98 unsigned old_index = (link.position - 2) / OT::Offset16::static_size; 99 unsigned klass = old_index % class_count; 100 if (klass < start || klass >= end) continue; 101 102 unsigned base = old_index / class_count; 103 unsigned new_klass = klass - start; 104 unsigned new_index = base * new_class_count + new_klass; 105 106 107 unsigned child_idx = link.objidx; 108 c.graph.add_link (&(prime->matrixZ[new_index]), 109 prime_id, 110 child_idx); 111 112 auto& child = c.graph.vertices_[child_idx]; 113 child.remove_parent (this_index); 114 115 o.real_links.remove_unordered (i); 116 num_links--; 117 i--; 118 } 119 120 return prime_id; 121 } 122 }; 123 124 struct MarkArray : public OT::Layout::GPOS_impl::MarkArray 125 { sanitizegraph::MarkArray126 bool sanitize (graph_t::vertex_t& vertex) const 127 { 128 int64_t vertex_len = vertex.obj.tail - vertex.obj.head; 129 unsigned min_size = MarkArray::min_size; 130 if (vertex_len < min_size) return false; 131 132 return vertex_len >= get_size (); 133 } 134 shrinkgraph::MarkArray135 bool shrink (gsubgpos_graph_context_t& c, 136 const hb_hashmap_t<unsigned, unsigned>& mark_array_links, 137 unsigned this_index, 138 unsigned new_class_count) 139 { 140 auto& o = c.graph.vertices_[this_index].obj; 141 for (const auto& link : o.real_links) 142 c.graph.vertices_[link.objidx].remove_parent (this_index); 143 o.real_links.reset (); 144 145 unsigned new_index = 0; 146 for (const auto& record : this->iter ()) 147 { 148 unsigned klass = record.klass; 149 if (klass >= new_class_count) continue; 150 151 (*this)[new_index].klass = klass; 152 unsigned position = (char*) &record.markAnchor - (char*) this; 153 unsigned* objidx; 154 if (!mark_array_links.has (position, &objidx)) 155 { 156 new_index++; 157 continue; 158 } 159 160 c.graph.add_link (&(*this)[new_index].markAnchor, this_index, *objidx); 161 new_index++; 162 } 163 164 this->len = new_index; 165 o.tail = o.head + MarkArray::min_size + 166 OT::Layout::GPOS_impl::MarkRecord::static_size * new_index; 167 return true; 168 } 169 clonegraph::MarkArray170 unsigned clone (gsubgpos_graph_context_t& c, 171 unsigned this_index, 172 const hb_hashmap_t<unsigned, unsigned>& pos_to_index, 173 hb_set_t& marks, 174 unsigned start_class) 175 { 176 unsigned size = MarkArray::min_size + 177 OT::Layout::GPOS_impl::MarkRecord::static_size * 178 marks.get_population (); 179 unsigned prime_id = c.create_node (size); 180 if (prime_id == (unsigned) -1) return -1; 181 MarkArray* prime = (MarkArray*) c.graph.object (prime_id).head; 182 prime->len = marks.get_population (); 183 184 185 unsigned i = 0; 186 for (hb_codepoint_t mark : marks) 187 { 188 (*prime)[i].klass = (*this)[mark].klass - start_class; 189 unsigned offset_pos = (char*) &((*this)[mark].markAnchor) - (char*) this; 190 unsigned* anchor_index; 191 if (pos_to_index.has (offset_pos, &anchor_index)) 192 c.graph.move_child (this_index, 193 &((*this)[mark].markAnchor), 194 prime_id, 195 &((*prime)[i].markAnchor)); 196 197 i++; 198 } 199 200 return prime_id; 201 } 202 }; 203 204 struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2<SmallTypes> 205 { sanitizegraph::MarkBasePosFormat1206 bool sanitize (graph_t::vertex_t& vertex) const 207 { 208 int64_t vertex_len = vertex.obj.tail - vertex.obj.head; 209 return vertex_len >= MarkBasePosFormat1::static_size; 210 } 211 split_subtablesgraph::MarkBasePosFormat1212 hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, 213 unsigned parent_index, 214 unsigned this_index) 215 { 216 hb_set_t visited; 217 218 const unsigned base_coverage_id = c.graph.index_for_offset (this_index, &baseCoverage); 219 const unsigned base_size = 220 OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes>::min_size + 221 MarkArray::min_size + 222 AnchorMatrix::min_size + 223 c.graph.vertices_[base_coverage_id].table_size (); 224 225 hb_vector_t<class_info_t> class_to_info = get_class_info (c, this_index); 226 227 unsigned class_count = classCount; 228 auto base_array = c.graph.as_table<AnchorMatrix> (this_index, 229 &baseArray, 230 class_count); 231 if (!base_array) return hb_vector_t<unsigned> (); 232 unsigned base_count = base_array.table->rows; 233 234 unsigned partial_coverage_size = 4; 235 unsigned accumulated = base_size; 236 hb_vector_t<unsigned> split_points; 237 238 for (unsigned klass = 0; klass < class_count; klass++) 239 { 240 class_info_t& info = class_to_info[klass]; 241 partial_coverage_size += OT::HBUINT16::static_size * info.marks.get_population (); 242 unsigned accumulated_delta = 243 OT::Layout::GPOS_impl::MarkRecord::static_size * info.marks.get_population () + 244 OT::Offset16::static_size * base_count; 245 246 for (unsigned objidx : info.child_indices) 247 accumulated_delta += c.graph.find_subgraph_size (objidx, visited); 248 249 accumulated += accumulated_delta; 250 unsigned total = accumulated + partial_coverage_size; 251 252 if (total >= (1 << 16)) 253 { 254 split_points.push (klass); 255 accumulated = base_size + accumulated_delta; 256 partial_coverage_size = 4 + OT::HBUINT16::static_size * info.marks.get_population (); 257 visited.clear (); // node sharing isn't allowed between splits. 258 } 259 } 260 261 262 const unsigned mark_array_id = c.graph.index_for_offset (this_index, &markArray); 263 split_context_t split_context { 264 c, 265 this, 266 c.graph.duplicate_if_shared (parent_index, this_index), 267 std::move (class_to_info), 268 c.graph.vertices_[mark_array_id].position_to_index_map (), 269 }; 270 271 return actuate_subtable_split<split_context_t> (split_context, split_points); 272 } 273 274 private: 275 276 struct class_info_t { 277 hb_set_t marks; 278 hb_vector_t<unsigned> child_indices; 279 }; 280 281 struct split_context_t { 282 gsubgpos_graph_context_t& c; 283 MarkBasePosFormat1* thiz; 284 unsigned this_index; 285 hb_vector_t<class_info_t> class_to_info; 286 hb_hashmap_t<unsigned, unsigned> mark_array_links; 287 marks_forgraph::MarkBasePosFormat1::split_context_t288 hb_set_t marks_for (unsigned start, unsigned end) 289 { 290 hb_set_t marks; 291 for (unsigned klass = start; klass < end; klass++) 292 { 293 + class_to_info[klass].marks.iter () 294 | hb_sink (marks) 295 ; 296 } 297 return marks; 298 } 299 original_countgraph::MarkBasePosFormat1::split_context_t300 unsigned original_count () 301 { 302 return thiz->classCount; 303 } 304 clone_rangegraph::MarkBasePosFormat1::split_context_t305 unsigned clone_range (unsigned start, unsigned end) 306 { 307 return thiz->clone_range (*this, this->this_index, start, end); 308 } 309 shrinkgraph::MarkBasePosFormat1::split_context_t310 bool shrink (unsigned count) 311 { 312 return thiz->shrink (*this, this->this_index, count); 313 } 314 }; 315 get_class_infograph::MarkBasePosFormat1316 hb_vector_t<class_info_t> get_class_info (gsubgpos_graph_context_t& c, 317 unsigned this_index) 318 { 319 hb_vector_t<class_info_t> class_to_info; 320 321 unsigned class_count= classCount; 322 class_to_info.resize (class_count); 323 324 auto mark_array = c.graph.as_table<MarkArray> (this_index, &markArray); 325 if (!mark_array) return hb_vector_t<class_info_t> (); 326 unsigned mark_count = mark_array.table->len; 327 for (unsigned mark = 0; mark < mark_count; mark++) 328 { 329 unsigned klass = (*mark_array.table)[mark].get_class (); 330 class_to_info[klass].marks.add (mark); 331 } 332 333 for (const auto& link : mark_array.vertex->obj.real_links) 334 { 335 unsigned mark = (link.position - 2) / 336 OT::Layout::GPOS_impl::MarkRecord::static_size; 337 unsigned klass = (*mark_array.table)[mark].get_class (); 338 class_to_info[klass].child_indices.push (link.objidx); 339 } 340 341 unsigned base_array_id = 342 c.graph.index_for_offset (this_index, &baseArray); 343 auto& base_array_v = c.graph.vertices_[base_array_id]; 344 345 for (const auto& link : base_array_v.obj.real_links) 346 { 347 unsigned index = (link.position - 2) / OT::Offset16::static_size; 348 unsigned klass = index % class_count; 349 class_to_info[klass].child_indices.push (link.objidx); 350 } 351 352 return class_to_info; 353 } 354 shrinkgraph::MarkBasePosFormat1355 bool shrink (split_context_t& sc, 356 unsigned this_index, 357 unsigned count) 358 { 359 DEBUG_MSG (SUBSET_REPACK, nullptr, 360 " Shrinking MarkBasePosFormat1 (%u) to [0, %u).", 361 this_index, 362 count); 363 364 unsigned old_count = classCount; 365 if (count >= old_count) 366 return true; 367 368 classCount = count; 369 370 auto mark_coverage = sc.c.graph.as_mutable_table<Coverage> (this_index, 371 &markCoverage); 372 if (!mark_coverage) return false; 373 hb_set_t marks = sc.marks_for (0, count); 374 auto new_coverage = 375 + hb_enumerate (mark_coverage.table->iter ()) 376 | hb_filter (marks, hb_first) 377 | hb_map_retains_sorting (hb_second) 378 ; 379 if (!Coverage::make_coverage (sc.c, + new_coverage, 380 mark_coverage.index, 381 4 + 2 * marks.get_population ())) 382 return false; 383 384 385 auto base_array = sc.c.graph.as_mutable_table<AnchorMatrix> (this_index, 386 &baseArray, 387 old_count); 388 if (!base_array || !base_array.table->shrink (sc.c, 389 base_array.index, 390 old_count, 391 count)) 392 return false; 393 394 auto mark_array = sc.c.graph.as_mutable_table<MarkArray> (this_index, 395 &markArray); 396 if (!mark_array || !mark_array.table->shrink (sc.c, 397 sc.mark_array_links, 398 mark_array.index, 399 count)) 400 return false; 401 402 return true; 403 } 404 405 // Create a new MarkBasePos that has all of the data for classes from [start, end). clone_rangegraph::MarkBasePosFormat1406 unsigned clone_range (split_context_t& sc, 407 unsigned this_index, 408 unsigned start, unsigned end) const 409 { 410 DEBUG_MSG (SUBSET_REPACK, nullptr, 411 " Cloning MarkBasePosFormat1 (%u) range [%u, %u).", this_index, start, end); 412 413 graph_t& graph = sc.c.graph; 414 unsigned prime_size = OT::Layout::GPOS_impl::MarkBasePosFormat1_2<SmallTypes>::static_size; 415 416 unsigned prime_id = sc.c.create_node (prime_size); 417 if (prime_id == (unsigned) -1) return -1; 418 419 MarkBasePosFormat1* prime = (MarkBasePosFormat1*) graph.object (prime_id).head; 420 prime->format = this->format; 421 unsigned new_class_count = end - start; 422 prime->classCount = new_class_count; 423 424 unsigned base_coverage_id = 425 graph.index_for_offset (sc.this_index, &baseCoverage); 426 graph.add_link (&(prime->baseCoverage), prime_id, base_coverage_id); 427 graph.duplicate (prime_id, base_coverage_id); 428 429 auto mark_coverage = sc.c.graph.as_table<Coverage> (this_index, 430 &markCoverage); 431 if (!mark_coverage) return false; 432 hb_set_t marks = sc.marks_for (start, end); 433 auto new_coverage = 434 + hb_enumerate (mark_coverage.table->iter ()) 435 | hb_filter (marks, hb_first) 436 | hb_map_retains_sorting (hb_second) 437 ; 438 if (!Coverage::add_coverage (sc.c, 439 prime_id, 440 2, 441 + new_coverage, 442 marks.get_population () * 2 + 4)) 443 return -1; 444 445 auto mark_array = 446 graph.as_table <MarkArray> (sc.this_index, &markArray); 447 if (!mark_array) return -1; 448 unsigned new_mark_array = 449 mark_array.table->clone (sc.c, 450 mark_array.index, 451 sc.mark_array_links, 452 marks, 453 start); 454 graph.add_link (&(prime->markArray), prime_id, new_mark_array); 455 456 unsigned class_count = classCount; 457 auto base_array = 458 graph.as_table<AnchorMatrix> (sc.this_index, &baseArray, class_count); 459 if (!base_array) return -1; 460 unsigned new_base_array = 461 base_array.table->clone (sc.c, 462 base_array.index, 463 start, end, this->classCount); 464 graph.add_link (&(prime->baseArray), prime_id, new_base_array); 465 466 return prime_id; 467 } 468 }; 469 470 471 struct MarkBasePos : public OT::Layout::GPOS_impl::MarkBasePos 472 { split_subtablesgraph::MarkBasePos473 hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, 474 unsigned parent_index, 475 unsigned this_index) 476 { 477 switch (u.format) { 478 case 1: 479 return ((MarkBasePosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index); 480 #ifndef HB_NO_BEYOND_64K 481 case 2: HB_FALLTHROUGH; 482 // Don't split 24bit PairPos's. 483 #endif 484 default: 485 return hb_vector_t<unsigned> (); 486 } 487 } 488 sanitizegraph::MarkBasePos489 bool sanitize (graph_t::vertex_t& vertex) const 490 { 491 int64_t vertex_len = vertex.obj.tail - vertex.obj.head; 492 if (vertex_len < u.format.get_size ()) return false; 493 494 switch (u.format) { 495 case 1: 496 return ((MarkBasePosFormat1*)(&u.format1))->sanitize (vertex); 497 #ifndef HB_NO_BEYOND_64K 498 case 2: HB_FALLTHROUGH; 499 #endif 500 default: 501 // We don't handle format 3 and 4 here. 502 return false; 503 } 504 } 505 }; 506 507 508 } 509 510 #endif // GRAPH_MARKBASEPOS_GRAPH_HH 511