• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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