1 /* 2 * Copyright © 2012,2017 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): Behdad Esfahbod 25 */ 26 27 #ifndef HB_SET_HH 28 #define HB_SET_HH 29 30 #include "hb.hh" 31 #include "hb-machinery.hh" 32 33 34 /* 35 * hb_set_t 36 */ 37 38 /* TODO Keep a free-list so we can free pages that are completely zeroed. At that 39 * point maybe also use a sentinel value for "all-1" pages? */ 40 41 struct hb_set_t 42 { 43 HB_DELETE_COPY_ASSIGN (hb_set_t); hb_set_thb_set_t44 hb_set_t () { init (); } ~hb_set_thb_set_t45 ~hb_set_t () { fini (); } 46 47 struct page_map_t 48 { cmphb_set_t::page_map_t49 int cmp (const page_map_t &o) const { return (int) o.major - (int) major; } 50 51 uint32_t major; 52 uint32_t index; 53 }; 54 55 struct page_t 56 { init0hb_set_t::page_t57 void init0 () { v.clear (); } init1hb_set_t::page_t58 void init1 () { v.clear (0xFF); } 59 lenhb_set_t::page_t60 unsigned int len () const 61 { return ARRAY_LENGTH_CONST (v); } 62 is_emptyhb_set_t::page_t63 bool is_empty () const 64 { 65 for (unsigned int i = 0; i < len (); i++) 66 if (v[i]) 67 return false; 68 return true; 69 } 70 addhb_set_t::page_t71 void add (hb_codepoint_t g) { elt (g) |= mask (g); } delhb_set_t::page_t72 void del (hb_codepoint_t g) { elt (g) &= ~mask (g); } gethb_set_t::page_t73 bool get (hb_codepoint_t g) const { return elt (g) & mask (g); } 74 add_rangehb_set_t::page_t75 void add_range (hb_codepoint_t a, hb_codepoint_t b) 76 { 77 elt_t *la = &elt (a); 78 elt_t *lb = &elt (b); 79 if (la == lb) 80 *la |= (mask (b) << 1) - mask(a); 81 else 82 { 83 *la |= ~(mask (a) - 1); 84 la++; 85 86 memset (la, 0xff, (char *) lb - (char *) la); 87 88 *lb |= ((mask (b) << 1) - 1); 89 } 90 } 91 is_equalhb_set_t::page_t92 bool is_equal (const page_t *other) const 93 { 94 return 0 == hb_memcmp (&v, &other->v, sizeof (v)); 95 } 96 get_populationhb_set_t::page_t97 unsigned int get_population () const 98 { 99 unsigned int pop = 0; 100 for (unsigned int i = 0; i < len (); i++) 101 pop += hb_popcount (v[i]); 102 return pop; 103 } 104 nexthb_set_t::page_t105 bool next (hb_codepoint_t *codepoint) const 106 { 107 unsigned int m = (*codepoint + 1) & MASK; 108 if (!m) 109 { 110 *codepoint = INVALID; 111 return false; 112 } 113 unsigned int i = m / ELT_BITS; 114 unsigned int j = m & ELT_MASK; 115 116 const elt_t vv = v[i] & ~((elt_t (1) << j) - 1); 117 for (const elt_t *p = &vv; i < len (); p = &v[++i]) 118 if (*p) 119 { 120 *codepoint = i * ELT_BITS + elt_get_min (*p); 121 return true; 122 } 123 124 *codepoint = INVALID; 125 return false; 126 } previoushb_set_t::page_t127 bool previous (hb_codepoint_t *codepoint) const 128 { 129 unsigned int m = (*codepoint - 1) & MASK; 130 if (m == MASK) 131 { 132 *codepoint = INVALID; 133 return false; 134 } 135 unsigned int i = m / ELT_BITS; 136 unsigned int j = m & ELT_MASK; 137 138 const elt_t vv = v[i] & ((elt_t (1) << (j + 1)) - 1); 139 for (const elt_t *p = &vv; (int) i >= 0; p = &v[--i]) 140 if (*p) 141 { 142 *codepoint = i * ELT_BITS + elt_get_max (*p); 143 return true; 144 } 145 146 *codepoint = INVALID; 147 return false; 148 } get_minhb_set_t::page_t149 hb_codepoint_t get_min () const 150 { 151 for (unsigned int i = 0; i < len (); i++) 152 if (v[i]) 153 return i * ELT_BITS + elt_get_min (v[i]); 154 return INVALID; 155 } get_maxhb_set_t::page_t156 hb_codepoint_t get_max () const 157 { 158 for (int i = len () - 1; i >= 0; i--) 159 if (v[i]) 160 return i * ELT_BITS + elt_get_max (v[i]); 161 return 0; 162 } 163 164 typedef unsigned long long elt_t; 165 static constexpr unsigned PAGE_BITS = 512; 166 static_assert ((PAGE_BITS & ((PAGE_BITS) - 1)) == 0, ""); 167 elt_get_minhb_set_t::page_t168 static unsigned int elt_get_min (const elt_t &elt) { return hb_ctz (elt); } elt_get_maxhb_set_t::page_t169 static unsigned int elt_get_max (const elt_t &elt) { return hb_bit_storage (elt) - 1; } 170 171 typedef hb_vector_size_t<elt_t, PAGE_BITS / 8> vector_t; 172 173 static constexpr unsigned ELT_BITS = sizeof (elt_t) * 8; 174 static constexpr unsigned ELT_MASK = ELT_BITS - 1; 175 static constexpr unsigned BITS = sizeof (vector_t) * 8; 176 static constexpr unsigned MASK = BITS - 1; 177 static_assert ((unsigned) PAGE_BITS == (unsigned) BITS, ""); 178 elthb_set_t::page_t179 elt_t &elt (hb_codepoint_t g) { return v[(g & MASK) / ELT_BITS]; } elthb_set_t::page_t180 elt_t const &elt (hb_codepoint_t g) const { return v[(g & MASK) / ELT_BITS]; } maskhb_set_t::page_t181 elt_t mask (hb_codepoint_t g) const { return elt_t (1) << (g & ELT_MASK); } 182 183 vector_t v; 184 }; 185 static_assert (page_t::PAGE_BITS == sizeof (page_t) * 8, ""); 186 187 hb_object_header_t header; 188 bool successful; /* Allocations successful */ 189 mutable unsigned int population; 190 hb_sorted_vector_t<page_map_t> page_map; 191 hb_vector_t<page_t> pages; 192 init_shallowhb_set_t193 void init_shallow () 194 { 195 successful = true; 196 population = 0; 197 page_map.init (); 198 pages.init (); 199 } inithb_set_t200 void init () 201 { 202 hb_object_init (this); 203 init_shallow (); 204 } fini_shallowhb_set_t205 void fini_shallow () 206 { 207 population = 0; 208 page_map.fini (); 209 pages.fini (); 210 } finihb_set_t211 void fini () 212 { 213 hb_object_fini (this); 214 fini_shallow (); 215 } 216 in_errorhb_set_t217 bool in_error () const { return !successful; } 218 resizehb_set_t219 bool resize (unsigned int count) 220 { 221 if (unlikely (!successful)) return false; 222 if (!pages.resize (count) || !page_map.resize (count)) 223 { 224 pages.resize (page_map.length); 225 successful = false; 226 return false; 227 } 228 return true; 229 } 230 resethb_set_t231 void reset () 232 { 233 if (unlikely (hb_object_is_immutable (this))) 234 return; 235 clear (); 236 successful = true; 237 } 238 clearhb_set_t239 void clear () 240 { 241 if (unlikely (hb_object_is_immutable (this))) 242 return; 243 population = 0; 244 page_map.resize (0); 245 pages.resize (0); 246 } is_emptyhb_set_t247 bool is_empty () const 248 { 249 unsigned int count = pages.length; 250 for (unsigned int i = 0; i < count; i++) 251 if (!pages[i].is_empty ()) 252 return false; 253 return true; 254 } 255 dirtyhb_set_t256 void dirty () { population = (unsigned int) -1; } 257 addhb_set_t258 void add (hb_codepoint_t g) 259 { 260 if (unlikely (!successful)) return; 261 if (unlikely (g == INVALID)) return; 262 dirty (); 263 page_t *page = page_for_insert (g); if (unlikely (!page)) return; 264 page->add (g); 265 } add_rangehb_set_t266 bool add_range (hb_codepoint_t a, hb_codepoint_t b) 267 { 268 if (unlikely (!successful)) return true; /* https://github.com/harfbuzz/harfbuzz/issues/657 */ 269 if (unlikely (a > b || a == INVALID || b == INVALID)) return false; 270 dirty (); 271 unsigned int ma = get_major (a); 272 unsigned int mb = get_major (b); 273 if (ma == mb) 274 { 275 page_t *page = page_for_insert (a); if (unlikely (!page)) return false; 276 page->add_range (a, b); 277 } 278 else 279 { 280 page_t *page = page_for_insert (a); if (unlikely (!page)) return false; 281 page->add_range (a, major_start (ma + 1) - 1); 282 283 for (unsigned int m = ma + 1; m < mb; m++) 284 { 285 page = page_for_insert (major_start (m)); if (unlikely (!page)) return false; 286 page->init1 (); 287 } 288 289 page = page_for_insert (b); if (unlikely (!page)) return false; 290 page->add_range (major_start (mb), b); 291 } 292 return true; 293 } 294 295 template <typename T> add_arrayhb_set_t296 void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) 297 { 298 if (unlikely (!successful)) return; 299 if (!count) return; 300 dirty (); 301 hb_codepoint_t g = *array; 302 while (count) 303 { 304 unsigned int m = get_major (g); 305 page_t *page = page_for_insert (g); if (unlikely (!page)) return; 306 unsigned int start = major_start (m); 307 unsigned int end = major_start (m + 1); 308 do 309 { 310 page->add (g); 311 312 array = &StructAtOffsetUnaligned<T> (array, stride); 313 count--; 314 } 315 while (count && (g = *array, start <= g && g < end)); 316 } 317 } 318 319 /* Might return false if array looks unsorted. 320 * Used for faster rejection of corrupt data. */ 321 template <typename T> add_sorted_arrayhb_set_t322 bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) 323 { 324 if (unlikely (!successful)) return true; /* https://github.com/harfbuzz/harfbuzz/issues/657 */ 325 if (!count) return true; 326 dirty (); 327 hb_codepoint_t g = *array; 328 hb_codepoint_t last_g = g; 329 while (count) 330 { 331 unsigned int m = get_major (g); 332 page_t *page = page_for_insert (g); if (unlikely (!page)) return false; 333 unsigned int end = major_start (m + 1); 334 do 335 { 336 /* If we try harder we can change the following comparison to <=; 337 * Not sure if it's worth it. */ 338 if (g < last_g) return false; 339 last_g = g; 340 page->add (g); 341 342 array = (const T *) ((const char *) array + stride); 343 count--; 344 } 345 while (count && (g = *array, g < end)); 346 } 347 return true; 348 } 349 delhb_set_t350 void del (hb_codepoint_t g) 351 { 352 /* TODO perform op even if !successful. */ 353 if (unlikely (!successful)) return; 354 page_t *page = page_for (g); 355 if (!page) 356 return; 357 dirty (); 358 page->del (g); 359 } del_rangehb_set_t360 void del_range (hb_codepoint_t a, hb_codepoint_t b) 361 { 362 /* TODO perform op even if !successful. */ 363 /* TODO Optimize, like add_range(). */ 364 if (unlikely (!successful)) return; 365 for (unsigned int i = a; i < b + 1; i++) 366 del (i); 367 } gethb_set_t368 bool get (hb_codepoint_t g) const 369 { 370 const page_t *page = page_for (g); 371 if (!page) 372 return false; 373 return page->get (g); 374 } 375 376 /* Has interface. */ 377 static constexpr bool SENTINEL = false; 378 typedef bool value_t; operator []hb_set_t379 value_t operator [] (hb_codepoint_t k) const { return get (k); } hashb_set_t380 bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; } 381 /* Predicate. */ operator ()hb_set_t382 bool operator () (hb_codepoint_t k) const { return has (k); } 383 384 /* Sink interface. */ operator <<hb_set_t385 hb_set_t& operator << (hb_codepoint_t v) { add (v); return *this; } 386 intersectshb_set_t387 bool intersects (hb_codepoint_t first, hb_codepoint_t last) const 388 { 389 hb_codepoint_t c = first - 1; 390 return next (&c) && c <= last; 391 } sethb_set_t392 void set (const hb_set_t *other) 393 { 394 if (unlikely (!successful)) return; 395 unsigned int count = other->pages.length; 396 if (!resize (count)) 397 return; 398 population = other->population; 399 memcpy ((void *) pages, (const void *) other->pages, count * pages.item_size); 400 memcpy ((void *) page_map, (const void *) other->page_map, count * page_map.item_size); 401 } 402 is_equalhb_set_t403 bool is_equal (const hb_set_t *other) const 404 { 405 if (get_population () != other->get_population ()) 406 return false; 407 408 unsigned int na = pages.length; 409 unsigned int nb = other->pages.length; 410 411 unsigned int a = 0, b = 0; 412 for (; a < na && b < nb; ) 413 { 414 if (page_at (a).is_empty ()) { a++; continue; } 415 if (other->page_at (b).is_empty ()) { b++; continue; } 416 if (page_map[a].major != other->page_map[b].major || 417 !page_at (a).is_equal (&other->page_at (b))) 418 return false; 419 a++; 420 b++; 421 } 422 for (; a < na; a++) 423 if (!page_at (a).is_empty ()) { return false; } 424 for (; b < nb; b++) 425 if (!other->page_at (b).is_empty ()) { return false; } 426 427 return true; 428 } 429 is_subsethb_set_t430 bool is_subset (const hb_set_t *larger_set) const 431 { 432 if (get_population () > larger_set->get_population ()) 433 return false; 434 435 /* TODO Optimize to use pages. */ 436 hb_codepoint_t c = INVALID; 437 while (next (&c)) 438 if (!larger_set->has (c)) 439 return false; 440 441 return true; 442 } 443 444 template <typename Op> processhb_set_t445 void process (const Op& op, const hb_set_t *other) 446 { 447 if (unlikely (!successful)) return; 448 449 dirty (); 450 451 unsigned int na = pages.length; 452 unsigned int nb = other->pages.length; 453 unsigned int next_page = na; 454 455 unsigned int count = 0, newCount = 0; 456 unsigned int a = 0, b = 0; 457 for (; a < na && b < nb; ) 458 { 459 if (page_map[a].major == other->page_map[b].major) 460 { 461 count++; 462 a++; 463 b++; 464 } 465 else if (page_map[a].major < other->page_map[b].major) 466 { 467 if (Op::passthru_left) 468 count++; 469 a++; 470 } 471 else 472 { 473 if (Op::passthru_right) 474 count++; 475 b++; 476 } 477 } 478 if (Op::passthru_left) 479 count += na - a; 480 if (Op::passthru_right) 481 count += nb - b; 482 483 if (count > pages.length) 484 if (!resize (count)) 485 return; 486 newCount = count; 487 488 /* Process in-place backward. */ 489 a = na; 490 b = nb; 491 for (; a && b; ) 492 { 493 if (page_map[a - 1].major == other->page_map[b - 1].major) 494 { 495 a--; 496 b--; 497 count--; 498 page_map[count] = page_map[a]; 499 page_at (count).v = op (page_at (a).v, other->page_at (b).v); 500 } 501 else if (page_map[a - 1].major > other->page_map[b - 1].major) 502 { 503 a--; 504 if (Op::passthru_left) 505 { 506 count--; 507 page_map[count] = page_map[a]; 508 } 509 } 510 else 511 { 512 b--; 513 if (Op::passthru_right) 514 { 515 count--; 516 page_map[count].major = other->page_map[b].major; 517 page_map[count].index = next_page++; 518 page_at (count).v = other->page_at (b).v; 519 } 520 } 521 } 522 if (Op::passthru_left) 523 while (a) 524 { 525 a--; 526 count--; 527 page_map[count] = page_map [a]; 528 } 529 if (Op::passthru_right) 530 while (b) 531 { 532 b--; 533 count--; 534 page_map[count].major = other->page_map[b].major; 535 page_map[count].index = next_page++; 536 page_at (count).v = other->page_at (b).v; 537 } 538 assert (!count); 539 if (pages.length > newCount) 540 resize (newCount); 541 } 542 union_hb_set_t543 void union_ (const hb_set_t *other) 544 { 545 process (hb_bitwise_or, other); 546 } intersecthb_set_t547 void intersect (const hb_set_t *other) 548 { 549 process (hb_bitwise_and, other); 550 } subtracthb_set_t551 void subtract (const hb_set_t *other) 552 { 553 process (hb_bitwise_sub, other); 554 } symmetric_differencehb_set_t555 void symmetric_difference (const hb_set_t *other) 556 { 557 process (hb_bitwise_xor, other); 558 } nexthb_set_t559 bool next (hb_codepoint_t *codepoint) const 560 { 561 if (unlikely (*codepoint == INVALID)) { 562 *codepoint = get_min (); 563 return *codepoint != INVALID; 564 } 565 566 page_map_t map = {get_major (*codepoint), 0}; 567 unsigned int i; 568 page_map.bfind (map, &i, HB_BFIND_NOT_FOUND_STORE_CLOSEST); 569 if (i < page_map.length && page_map[i].major == map.major) 570 { 571 if (pages[page_map[i].index].next (codepoint)) 572 { 573 *codepoint += page_map[i].major * page_t::PAGE_BITS; 574 return true; 575 } 576 i++; 577 } 578 for (; i < page_map.length; i++) 579 { 580 hb_codepoint_t m = pages[page_map[i].index].get_min (); 581 if (m != INVALID) 582 { 583 *codepoint = page_map[i].major * page_t::PAGE_BITS + m; 584 return true; 585 } 586 } 587 *codepoint = INVALID; 588 return false; 589 } previoushb_set_t590 bool previous (hb_codepoint_t *codepoint) const 591 { 592 if (unlikely (*codepoint == INVALID)) { 593 *codepoint = get_max (); 594 return *codepoint != INVALID; 595 } 596 597 page_map_t map = {get_major (*codepoint), 0}; 598 unsigned int i; 599 page_map.bfind (map, &i, HB_BFIND_NOT_FOUND_STORE_CLOSEST); 600 if (i < page_map.length && page_map[i].major == map.major) 601 { 602 if (pages[page_map[i].index].previous (codepoint)) 603 { 604 *codepoint += page_map[i].major * page_t::PAGE_BITS; 605 return true; 606 } 607 } 608 i--; 609 for (; (int) i >= 0; i--) 610 { 611 hb_codepoint_t m = pages[page_map[i].index].get_max (); 612 if (m != INVALID) 613 { 614 *codepoint = page_map[i].major * page_t::PAGE_BITS + m; 615 return true; 616 } 617 } 618 *codepoint = INVALID; 619 return false; 620 } next_rangehb_set_t621 bool next_range (hb_codepoint_t *first, hb_codepoint_t *last) const 622 { 623 hb_codepoint_t i; 624 625 i = *last; 626 if (!next (&i)) 627 { 628 *last = *first = INVALID; 629 return false; 630 } 631 632 /* TODO Speed up. */ 633 *last = *first = i; 634 while (next (&i) && i == *last + 1) 635 (*last)++; 636 637 return true; 638 } previous_rangehb_set_t639 bool previous_range (hb_codepoint_t *first, hb_codepoint_t *last) const 640 { 641 hb_codepoint_t i; 642 643 i = *first; 644 if (!previous (&i)) 645 { 646 *last = *first = INVALID; 647 return false; 648 } 649 650 /* TODO Speed up. */ 651 *last = *first = i; 652 while (previous (&i) && i == *first - 1) 653 (*first)--; 654 655 return true; 656 } 657 get_populationhb_set_t658 unsigned int get_population () const 659 { 660 if (population != (unsigned int) -1) 661 return population; 662 663 unsigned int pop = 0; 664 unsigned int count = pages.length; 665 for (unsigned int i = 0; i < count; i++) 666 pop += pages[i].get_population (); 667 668 population = pop; 669 return pop; 670 } get_minhb_set_t671 hb_codepoint_t get_min () const 672 { 673 unsigned int count = pages.length; 674 for (unsigned int i = 0; i < count; i++) 675 if (!page_at (i).is_empty ()) 676 return page_map[i].major * page_t::PAGE_BITS + page_at (i).get_min (); 677 return INVALID; 678 } get_maxhb_set_t679 hb_codepoint_t get_max () const 680 { 681 unsigned int count = pages.length; 682 for (int i = count - 1; i >= 0; i++) 683 if (!page_at (i).is_empty ()) 684 return page_map[(unsigned) i].major * page_t::PAGE_BITS + page_at (i).get_max (); 685 return INVALID; 686 } 687 688 static constexpr hb_codepoint_t INVALID = HB_SET_VALUE_INVALID; 689 690 /* 691 * Iterator implementation. 692 */ 693 struct iter_t : hb_iter_with_fallback_t<iter_t, hb_codepoint_t> 694 { 695 static constexpr bool is_sorted_iterator = true; iter_thb_set_t::iter_t696 iter_t (const hb_set_t &s_ = Null(hb_set_t)) : 697 s (&s_), v (INVALID), l (s->get_population () + 1) { __next__ (); } 698 699 typedef hb_codepoint_t __item_t__; __item__hb_set_t::iter_t700 hb_codepoint_t __item__ () const { return v; } __more__hb_set_t::iter_t701 bool __more__ () const { return v != INVALID; } __next__hb_set_t::iter_t702 void __next__ () { s->next (&v); if (l) l--; } __prev__hb_set_t::iter_t703 void __prev__ () { s->previous (&v); } __len__hb_set_t::iter_t704 unsigned __len__ () const { return l; } endhb_set_t::iter_t705 iter_t end () const { return iter_t (*s); } operator !=hb_set_t::iter_t706 bool operator != (const iter_t& o) const 707 { return s != o.s || v != o.v; } 708 709 protected: 710 const hb_set_t *s; 711 hb_codepoint_t v; 712 unsigned l; 713 }; iterhb_set_t714 iter_t iter () const { return iter_t (*this); } operator iter_thb_set_t715 operator iter_t () const { return iter (); } 716 717 protected: 718 page_for_inserthb_set_t719 page_t *page_for_insert (hb_codepoint_t g) 720 { 721 page_map_t map = {get_major (g), pages.length}; 722 unsigned int i; 723 if (!page_map.bfind (map, &i, HB_BFIND_NOT_FOUND_STORE_CLOSEST)) 724 { 725 if (!resize (pages.length + 1)) 726 return nullptr; 727 728 pages[map.index].init0 (); 729 memmove (page_map + i + 1, 730 page_map + i, 731 (page_map.length - 1 - i) * page_map.item_size); 732 page_map[i] = map; 733 } 734 return &pages[page_map[i].index]; 735 } page_forhb_set_t736 page_t *page_for (hb_codepoint_t g) 737 { 738 page_map_t key = {get_major (g)}; 739 const page_map_t *found = page_map.bsearch (key); 740 if (found) 741 return &pages[found->index]; 742 return nullptr; 743 } page_forhb_set_t744 const page_t *page_for (hb_codepoint_t g) const 745 { 746 page_map_t key = {get_major (g)}; 747 const page_map_t *found = page_map.bsearch (key); 748 if (found) 749 return &pages[found->index]; 750 return nullptr; 751 } page_athb_set_t752 page_t &page_at (unsigned int i) { return pages[page_map[i].index]; } page_athb_set_t753 const page_t &page_at (unsigned int i) const { return pages[page_map[i].index]; } get_majorhb_set_t754 unsigned int get_major (hb_codepoint_t g) const { return g / page_t::PAGE_BITS; } major_starthb_set_t755 hb_codepoint_t major_start (unsigned int major) const { return major * page_t::PAGE_BITS; } 756 }; 757 758 759 #endif /* HB_SET_HH */ 760