Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright  1998-2004  David Turner and Werner Lemberg
      3  * Copyright  2004,2007,2009,2010  Red Hat, Inc.
      4  * Copyright  2011,2012  Google, Inc.
      5  *
      6  *  This is part of HarfBuzz, a text shaping library.
      7  *
      8  * Permission is hereby granted, without written agreement and without
      9  * license or royalty fees, to use, copy, modify, and distribute this
     10  * software and its documentation for any purpose, provided that the
     11  * above copyright notice and the following two paragraphs appear in
     12  * all copies of this software.
     13  *
     14  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
     15  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
     16  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
     17  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
     18  * DAMAGE.
     19  *
     20  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
     21  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
     22  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
     23  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
     24  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
     25  *
     26  * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
     27  * Google Author(s): Behdad Esfahbod
     28  */
     29 
     30 #include "hb-buffer-private.hh"
     31 #include "hb-utf-private.hh"
     32 
     33 
     34 #ifndef HB_DEBUG_BUFFER
     35 #define HB_DEBUG_BUFFER (HB_DEBUG+0)
     36 #endif
     37 
     38 
     39 hb_bool_t
     40 hb_segment_properties_equal (const hb_segment_properties_t *a,
     41 			     const hb_segment_properties_t *b)
     42 {
     43   return a->direction == b->direction &&
     44 	 a->script    == b->script    &&
     45 	 a->language  == b->language  &&
     46 	 a->reserved1 == b->reserved1 &&
     47 	 a->reserved2 == b->reserved2;
     48 
     49 }
     50 
     51 unsigned int
     52 hb_segment_properties_hash (const hb_segment_properties_t *p)
     53 {
     54   return (unsigned int) p->direction ^
     55 	 (unsigned int) p->script ^
     56 	 (intptr_t) (p->language);
     57 }
     58 
     59 
     60 
     61 /* Here is how the buffer works internally:
     62  *
     63  * There are two info pointers: info and out_info.  They always have
     64  * the same allocated size, but different lengths.
     65  *
     66  * As an optimization, both info and out_info may point to the
     67  * same piece of memory, which is owned by info.  This remains the
     68  * case as long as out_len doesn't exceed i at any time.
     69  * In that case, swap_buffers() is no-op and the glyph operations operate
     70  * mostly in-place.
     71  *
     72  * As soon as out_info gets longer than info, out_info is moved over
     73  * to an alternate buffer (which we reuse the pos buffer for!), and its
     74  * current contents (out_len entries) are copied to the new place.
     75  * This should all remain transparent to the user.  swap_buffers() then
     76  * switches info and out_info.
     77  */
     78 
     79 
     80 
     81 /* Internal API */
     82 
     83 bool
     84 hb_buffer_t::enlarge (unsigned int size)
     85 {
     86   if (unlikely (in_error))
     87     return false;
     88 
     89   unsigned int new_allocated = allocated;
     90   hb_glyph_position_t *new_pos = NULL;
     91   hb_glyph_info_t *new_info = NULL;
     92   bool separate_out = out_info != info;
     93 
     94   if (unlikely (_hb_unsigned_int_mul_overflows (size, sizeof (info[0]))))
     95     goto done;
     96 
     97   while (size >= new_allocated)
     98     new_allocated += (new_allocated >> 1) + 32;
     99 
    100   ASSERT_STATIC (sizeof (info[0]) == sizeof (pos[0]));
    101   if (unlikely (_hb_unsigned_int_mul_overflows (new_allocated, sizeof (info[0]))))
    102     goto done;
    103 
    104   new_pos = (hb_glyph_position_t *) realloc (pos, new_allocated * sizeof (pos[0]));
    105   new_info = (hb_glyph_info_t *) realloc (info, new_allocated * sizeof (info[0]));
    106 
    107 done:
    108   if (unlikely (!new_pos || !new_info))
    109     in_error = true;
    110 
    111   if (likely (new_pos))
    112     pos = new_pos;
    113 
    114   if (likely (new_info))
    115     info = new_info;
    116 
    117   out_info = separate_out ? (hb_glyph_info_t *) pos : info;
    118   if (likely (!in_error))
    119     allocated = new_allocated;
    120 
    121   return likely (!in_error);
    122 }
    123 
    124 bool
    125 hb_buffer_t::make_room_for (unsigned int num_in,
    126 			    unsigned int num_out)
    127 {
    128   if (unlikely (!ensure (out_len + num_out))) return false;
    129 
    130   if (out_info == info &&
    131       out_len + num_out > idx + num_in)
    132   {
    133     assert (have_output);
    134 
    135     out_info = (hb_glyph_info_t *) pos;
    136     memcpy (out_info, info, out_len * sizeof (out_info[0]));
    137   }
    138 
    139   return true;
    140 }
    141 
    142 bool
    143 hb_buffer_t::shift_forward (unsigned int count)
    144 {
    145   assert (have_output);
    146   if (unlikely (!ensure (len + count))) return false;
    147 
    148   memmove (info + idx + count, info + idx, (len - idx) * sizeof (info[0]));
    149   len += count;
    150   idx += count;
    151 
    152   return true;
    153 }
    154 
    155 hb_buffer_t::scratch_buffer_t *
    156 hb_buffer_t::get_scratch_buffer (unsigned int *size)
    157 {
    158   have_output = false;
    159   have_positions = false;
    160 
    161   out_len = 0;
    162   out_info = info;
    163 
    164   assert ((uintptr_t) pos % sizeof (scratch_buffer_t) == 0);
    165   *size = allocated * sizeof (pos[0]) / sizeof (scratch_buffer_t);
    166   return (scratch_buffer_t *) (void *) pos;
    167 }
    168 
    169 
    170 
    171 /* HarfBuzz-Internal API */
    172 
    173 void
    174 hb_buffer_t::reset (void)
    175 {
    176   if (unlikely (hb_object_is_inert (this)))
    177     return;
    178 
    179   hb_unicode_funcs_destroy (unicode);
    180   unicode = hb_unicode_funcs_get_default ();
    181   flags = HB_BUFFER_FLAG_DEFAULT;
    182   replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT;
    183 
    184   clear ();
    185 }
    186 
    187 void
    188 hb_buffer_t::clear (void)
    189 {
    190   if (unlikely (hb_object_is_inert (this)))
    191     return;
    192 
    193   hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT;
    194   props = default_props;
    195 
    196   content_type = HB_BUFFER_CONTENT_TYPE_INVALID;
    197   in_error = false;
    198   have_output = false;
    199   have_positions = false;
    200 
    201   idx = 0;
    202   len = 0;
    203   out_len = 0;
    204   out_info = info;
    205 
    206   serial = 0;
    207   memset (allocated_var_bytes, 0, sizeof allocated_var_bytes);
    208   memset (allocated_var_owner, 0, sizeof allocated_var_owner);
    209 
    210   memset (context, 0, sizeof context);
    211   memset (context_len, 0, sizeof context_len);
    212 }
    213 
    214 void
    215 hb_buffer_t::add (hb_codepoint_t  codepoint,
    216 		  unsigned int    cluster)
    217 {
    218   hb_glyph_info_t *glyph;
    219 
    220   if (unlikely (!ensure (len + 1))) return;
    221 
    222   glyph = &info[len];
    223 
    224   memset (glyph, 0, sizeof (*glyph));
    225   glyph->codepoint = codepoint;
    226   glyph->mask = 1;
    227   glyph->cluster = cluster;
    228 
    229   len++;
    230 }
    231 
    232 void
    233 hb_buffer_t::add_info (const hb_glyph_info_t &glyph_info)
    234 {
    235   if (unlikely (!ensure (len + 1))) return;
    236 
    237   info[len] = glyph_info;
    238 
    239   len++;
    240 }
    241 
    242 
    243 void
    244 hb_buffer_t::remove_output (void)
    245 {
    246   if (unlikely (hb_object_is_inert (this)))
    247     return;
    248 
    249   have_output = false;
    250   have_positions = false;
    251 
    252   out_len = 0;
    253   out_info = info;
    254 }
    255 
    256 void
    257 hb_buffer_t::clear_output (void)
    258 {
    259   if (unlikely (hb_object_is_inert (this)))
    260     return;
    261 
    262   have_output = true;
    263   have_positions = false;
    264 
    265   out_len = 0;
    266   out_info = info;
    267 }
    268 
    269 void
    270 hb_buffer_t::clear_positions (void)
    271 {
    272   if (unlikely (hb_object_is_inert (this)))
    273     return;
    274 
    275   have_output = false;
    276   have_positions = true;
    277 
    278   out_len = 0;
    279   out_info = info;
    280 
    281   memset (pos, 0, sizeof (pos[0]) * len);
    282 }
    283 
    284 void
    285 hb_buffer_t::swap_buffers (void)
    286 {
    287   if (unlikely (in_error)) return;
    288 
    289   assert (have_output);
    290   have_output = false;
    291 
    292   if (out_info != info)
    293   {
    294     hb_glyph_info_t *tmp_string;
    295     tmp_string = info;
    296     info = out_info;
    297     out_info = tmp_string;
    298     pos = (hb_glyph_position_t *) out_info;
    299   }
    300 
    301   unsigned int tmp;
    302   tmp = len;
    303   len = out_len;
    304   out_len = tmp;
    305 
    306   idx = 0;
    307 }
    308 
    309 
    310 void
    311 hb_buffer_t::replace_glyphs (unsigned int num_in,
    312 			     unsigned int num_out,
    313 			     const uint32_t *glyph_data)
    314 {
    315   if (unlikely (!make_room_for (num_in, num_out))) return;
    316 
    317   merge_clusters (idx, idx + num_in);
    318 
    319   hb_glyph_info_t orig_info = info[idx];
    320   hb_glyph_info_t *pinfo = &out_info[out_len];
    321   for (unsigned int i = 0; i < num_out; i++)
    322   {
    323     *pinfo = orig_info;
    324     pinfo->codepoint = glyph_data[i];
    325     pinfo++;
    326   }
    327 
    328   idx  += num_in;
    329   out_len += num_out;
    330 }
    331 
    332 void
    333 hb_buffer_t::output_glyph (hb_codepoint_t glyph_index)
    334 {
    335   if (unlikely (!make_room_for (0, 1))) return;
    336 
    337   out_info[out_len] = info[idx];
    338   out_info[out_len].codepoint = glyph_index;
    339 
    340   out_len++;
    341 }
    342 
    343 void
    344 hb_buffer_t::output_info (const hb_glyph_info_t &glyph_info)
    345 {
    346   if (unlikely (!make_room_for (0, 1))) return;
    347 
    348   out_info[out_len] = glyph_info;
    349 
    350   out_len++;
    351 }
    352 
    353 void
    354 hb_buffer_t::copy_glyph (void)
    355 {
    356   if (unlikely (!make_room_for (0, 1))) return;
    357 
    358   out_info[out_len] = info[idx];
    359 
    360   out_len++;
    361 }
    362 
    363 bool
    364 hb_buffer_t::move_to (unsigned int i)
    365 {
    366   if (!have_output)
    367   {
    368     assert (i <= len);
    369     idx = i;
    370     return true;
    371   }
    372 
    373   assert (i <= out_len + (len - idx));
    374 
    375   if (out_len < i)
    376   {
    377     unsigned int count = i - out_len;
    378     if (unlikely (!make_room_for (count, count))) return false;
    379 
    380     memmove (out_info + out_len, info + idx, count * sizeof (out_info[0]));
    381     idx += count;
    382     out_len += count;
    383   }
    384   else if (out_len > i)
    385   {
    386     /* Tricky part: rewinding... */
    387     unsigned int count = out_len - i;
    388 
    389     if (unlikely (idx < count && !shift_forward (count + 32))) return false;
    390 
    391     assert (idx >= count);
    392 
    393     idx -= count;
    394     out_len -= count;
    395     memmove (info + idx, out_info + out_len, count * sizeof (out_info[0]));
    396   }
    397 
    398   return true;
    399 }
    400 
    401 void
    402 hb_buffer_t::replace_glyph (hb_codepoint_t glyph_index)
    403 {
    404   if (unlikely (out_info != info || out_len != idx)) {
    405     if (unlikely (!make_room_for (1, 1))) return;
    406     out_info[out_len] = info[idx];
    407   }
    408   out_info[out_len].codepoint = glyph_index;
    409 
    410   idx++;
    411   out_len++;
    412 }
    413 
    414 
    415 void
    416 hb_buffer_t::set_masks (hb_mask_t    value,
    417 			hb_mask_t    mask,
    418 			unsigned int cluster_start,
    419 			unsigned int cluster_end)
    420 {
    421   hb_mask_t not_mask = ~mask;
    422   value &= mask;
    423 
    424   if (!mask)
    425     return;
    426 
    427   if (cluster_start == 0 && cluster_end == (unsigned int)-1) {
    428     unsigned int count = len;
    429     for (unsigned int i = 0; i < count; i++)
    430       info[i].mask = (info[i].mask & not_mask) | value;
    431     return;
    432   }
    433 
    434   unsigned int count = len;
    435   for (unsigned int i = 0; i < count; i++)
    436     if (cluster_start <= info[i].cluster && info[i].cluster < cluster_end)
    437       info[i].mask = (info[i].mask & not_mask) | value;
    438 }
    439 
    440 void
    441 hb_buffer_t::reverse_range (unsigned int start,
    442 			    unsigned int end)
    443 {
    444   unsigned int i, j;
    445 
    446   if (start == end - 1)
    447     return;
    448 
    449   for (i = start, j = end - 1; i < j; i++, j--) {
    450     hb_glyph_info_t t;
    451 
    452     t = info[i];
    453     info[i] = info[j];
    454     info[j] = t;
    455   }
    456 
    457   if (pos) {
    458     for (i = start, j = end - 1; i < j; i++, j--) {
    459       hb_glyph_position_t t;
    460 
    461       t = pos[i];
    462       pos[i] = pos[j];
    463       pos[j] = t;
    464     }
    465   }
    466 }
    467 
    468 void
    469 hb_buffer_t::reverse (void)
    470 {
    471   if (unlikely (!len))
    472     return;
    473 
    474   reverse_range (0, len);
    475 }
    476 
    477 void
    478 hb_buffer_t::reverse_clusters (void)
    479 {
    480   unsigned int i, start, count, last_cluster;
    481 
    482   if (unlikely (!len))
    483     return;
    484 
    485   reverse ();
    486 
    487   count = len;
    488   start = 0;
    489   last_cluster = info[0].cluster;
    490   for (i = 1; i < count; i++) {
    491     if (last_cluster != info[i].cluster) {
    492       reverse_range (start, i);
    493       start = i;
    494       last_cluster = info[i].cluster;
    495     }
    496   }
    497   reverse_range (start, i);
    498 }
    499 
    500 void
    501 hb_buffer_t::merge_clusters (unsigned int start,
    502 			     unsigned int end)
    503 {
    504 #ifdef HB_NO_MERGE_CLUSTERS
    505   return;
    506 #endif
    507 
    508   if (unlikely (end - start < 2))
    509     return;
    510 
    511   unsigned int cluster = info[start].cluster;
    512 
    513   for (unsigned int i = start + 1; i < end; i++)
    514     cluster = MIN (cluster, info[i].cluster);
    515 
    516   /* Extend end */
    517   while (end < len && info[end - 1].cluster == info[end].cluster)
    518     end++;
    519 
    520   /* Extend start */
    521   while (idx < start && info[start - 1].cluster == info[start].cluster)
    522     start--;
    523 
    524   /* If we hit the start of buffer, continue in out-buffer. */
    525   if (idx == start)
    526     for (unsigned i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--)
    527       out_info[i - 1].cluster = cluster;
    528 
    529   for (unsigned int i = start; i < end; i++)
    530     info[i].cluster = cluster;
    531 }
    532 void
    533 hb_buffer_t::merge_out_clusters (unsigned int start,
    534 				 unsigned int end)
    535 {
    536 #ifdef HB_NO_MERGE_CLUSTERS
    537   return;
    538 #endif
    539 
    540   if (unlikely (end - start < 2))
    541     return;
    542 
    543   unsigned int cluster = out_info[start].cluster;
    544 
    545   for (unsigned int i = start + 1; i < end; i++)
    546     cluster = MIN (cluster, out_info[i].cluster);
    547 
    548   /* Extend start */
    549   while (start && out_info[start - 1].cluster == out_info[start].cluster)
    550     start--;
    551 
    552   /* Extend end */
    553   while (end < out_len && out_info[end - 1].cluster == out_info[end].cluster)
    554     end++;
    555 
    556   /* If we hit the end of out-buffer, continue in buffer. */
    557   if (end == out_len)
    558     for (unsigned i = idx; i < len && info[i].cluster == out_info[end - 1].cluster; i++)
    559       info[i].cluster = cluster;
    560 
    561   for (unsigned int i = start; i < end; i++)
    562     out_info[i].cluster = cluster;
    563 }
    564 
    565 void
    566 hb_buffer_t::guess_segment_properties (void)
    567 {
    568   assert (content_type == HB_BUFFER_CONTENT_TYPE_UNICODE ||
    569 	  (!len && content_type == HB_BUFFER_CONTENT_TYPE_INVALID));
    570 
    571   /* If script is set to INVALID, guess from buffer contents */
    572   if (props.script == HB_SCRIPT_INVALID) {
    573     for (unsigned int i = 0; i < len; i++) {
    574       hb_script_t script = unicode->script (info[i].codepoint);
    575       if (likely (script != HB_SCRIPT_COMMON &&
    576 		  script != HB_SCRIPT_INHERITED &&
    577 		  script != HB_SCRIPT_UNKNOWN)) {
    578         props.script = script;
    579         break;
    580       }
    581     }
    582   }
    583 
    584   /* If direction is set to INVALID, guess from script */
    585   if (props.direction == HB_DIRECTION_INVALID) {
    586     props.direction = hb_script_get_horizontal_direction (props.script);
    587   }
    588 
    589   /* If language is not set, use default language from locale */
    590   if (props.language == HB_LANGUAGE_INVALID) {
    591     /* TODO get_default_for_script? using $LANGUAGE */
    592     props.language = hb_language_get_default ();
    593   }
    594 }
    595 
    596 
    597 static inline void
    598 dump_var_allocation (const hb_buffer_t *buffer)
    599 {
    600   char buf[80];
    601   for (unsigned int i = 0; i < 8; i++)
    602     buf[i] = '0' + buffer->allocated_var_bytes[7 - i];
    603   buf[8] = '\0';
    604   DEBUG_MSG (BUFFER, buffer,
    605 	     "Current var allocation: %s",
    606 	     buf);
    607 }
    608 
    609 void hb_buffer_t::allocate_var (unsigned int byte_i, unsigned int count, const char *owner)
    610 {
    611   assert (byte_i < 8 && byte_i + count <= 8);
    612 
    613   if (DEBUG_ENABLED (BUFFER))
    614     dump_var_allocation (this);
    615   DEBUG_MSG (BUFFER, this,
    616 	     "Allocating var bytes %d..%d for %s",
    617 	     byte_i, byte_i + count - 1, owner);
    618 
    619   for (unsigned int i = byte_i; i < byte_i + count; i++) {
    620     assert (!allocated_var_bytes[i]);
    621     allocated_var_bytes[i]++;
    622     allocated_var_owner[i] = owner;
    623   }
    624 }
    625 
    626 void hb_buffer_t::deallocate_var (unsigned int byte_i, unsigned int count, const char *owner)
    627 {
    628   if (DEBUG_ENABLED (BUFFER))
    629     dump_var_allocation (this);
    630 
    631   DEBUG_MSG (BUFFER, this,
    632 	     "Deallocating var bytes %d..%d for %s",
    633 	     byte_i, byte_i + count - 1, owner);
    634 
    635   assert (byte_i < 8 && byte_i + count <= 8);
    636   for (unsigned int i = byte_i; i < byte_i + count; i++) {
    637     assert (allocated_var_bytes[i]);
    638     assert (0 == strcmp (allocated_var_owner[i], owner));
    639     allocated_var_bytes[i]--;
    640   }
    641 }
    642 
    643 void hb_buffer_t::assert_var (unsigned int byte_i, unsigned int count, const char *owner)
    644 {
    645   if (DEBUG_ENABLED (BUFFER))
    646     dump_var_allocation (this);
    647 
    648   DEBUG_MSG (BUFFER, this,
    649 	     "Asserting var bytes %d..%d for %s",
    650 	     byte_i, byte_i + count - 1, owner);
    651 
    652   assert (byte_i < 8 && byte_i + count <= 8);
    653   for (unsigned int i = byte_i; i < byte_i + count; i++) {
    654     assert (allocated_var_bytes[i]);
    655     assert (0 == strcmp (allocated_var_owner[i], owner));
    656   }
    657 }
    658 
    659 void hb_buffer_t::deallocate_var_all (void)
    660 {
    661   memset (allocated_var_bytes, 0, sizeof (allocated_var_bytes));
    662   memset (allocated_var_owner, 0, sizeof (allocated_var_owner));
    663 }
    664 
    665 /* Public API */
    666 
    667 /**
    668  * hb_buffer_create: (Xconstructor)
    669  *
    670  *
    671  *
    672  * Return value: (transfer full)
    673  *
    674  * Since: 1.0
    675  **/
    676 hb_buffer_t *
    677 hb_buffer_create (void)
    678 {
    679   hb_buffer_t *buffer;
    680 
    681   if (!(buffer = hb_object_create<hb_buffer_t> ()))
    682     return hb_buffer_get_empty ();
    683 
    684   buffer->reset ();
    685 
    686   return buffer;
    687 }
    688 
    689 /**
    690  * hb_buffer_get_empty:
    691  *
    692  *
    693  *
    694  * Return value: (transfer full):
    695  *
    696  * Since: 1.0
    697  **/
    698 hb_buffer_t *
    699 hb_buffer_get_empty (void)
    700 {
    701   static const hb_buffer_t _hb_buffer_nil = {
    702     HB_OBJECT_HEADER_STATIC,
    703 
    704     const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil),
    705     HB_BUFFER_FLAG_DEFAULT,
    706     HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT,
    707 
    708     HB_BUFFER_CONTENT_TYPE_INVALID,
    709     HB_SEGMENT_PROPERTIES_DEFAULT,
    710     true, /* in_error */
    711     true, /* have_output */
    712     true  /* have_positions */
    713 
    714     /* Zero is good enough for everything else. */
    715   };
    716 
    717   return const_cast<hb_buffer_t *> (&_hb_buffer_nil);
    718 }
    719 
    720 /**
    721  * hb_buffer_reference: (skip)
    722  * @buffer: a buffer.
    723  *
    724  *
    725  *
    726  * Return value: (transfer full):
    727  *
    728  * Since: 1.0
    729  **/
    730 hb_buffer_t *
    731 hb_buffer_reference (hb_buffer_t *buffer)
    732 {
    733   return hb_object_reference (buffer);
    734 }
    735 
    736 /**
    737  * hb_buffer_destroy: (skip)
    738  * @buffer: a buffer.
    739  *
    740  *
    741  *
    742  * Since: 1.0
    743  **/
    744 void
    745 hb_buffer_destroy (hb_buffer_t *buffer)
    746 {
    747   if (!hb_object_destroy (buffer)) return;
    748 
    749   hb_unicode_funcs_destroy (buffer->unicode);
    750 
    751   free (buffer->info);
    752   free (buffer->pos);
    753 
    754   free (buffer);
    755 }
    756 
    757 /**
    758  * hb_buffer_set_user_data: (skip)
    759  * @buffer: a buffer.
    760  * @key:
    761  * @data:
    762  * @destroy:
    763  * @replace:
    764  *
    765  *
    766  *
    767  * Return value:
    768  *
    769  * Since: 1.0
    770  **/
    771 hb_bool_t
    772 hb_buffer_set_user_data (hb_buffer_t        *buffer,
    773 			 hb_user_data_key_t *key,
    774 			 void *              data,
    775 			 hb_destroy_func_t   destroy,
    776 			 hb_bool_t           replace)
    777 {
    778   return hb_object_set_user_data (buffer, key, data, destroy, replace);
    779 }
    780 
    781 /**
    782  * hb_buffer_get_user_data: (skip)
    783  * @buffer: a buffer.
    784  * @key:
    785  *
    786  *
    787  *
    788  * Return value:
    789  *
    790  * Since: 1.0
    791  **/
    792 void *
    793 hb_buffer_get_user_data (hb_buffer_t        *buffer,
    794 			 hb_user_data_key_t *key)
    795 {
    796   return hb_object_get_user_data (buffer, key);
    797 }
    798 
    799 
    800 /**
    801  * hb_buffer_set_content_type:
    802  * @buffer: a buffer.
    803  * @content_type:
    804  *
    805  *
    806  *
    807  * Since: 1.0
    808  **/
    809 void
    810 hb_buffer_set_content_type (hb_buffer_t              *buffer,
    811 			    hb_buffer_content_type_t  content_type)
    812 {
    813   buffer->content_type = content_type;
    814 }
    815 
    816 /**
    817  * hb_buffer_get_content_type:
    818  * @buffer: a buffer.
    819  *
    820  *
    821  *
    822  * Return value:
    823  *
    824  * Since: 1.0
    825  **/
    826 hb_buffer_content_type_t
    827 hb_buffer_get_content_type (hb_buffer_t *buffer)
    828 {
    829   return buffer->content_type;
    830 }
    831 
    832 
    833 /**
    834  * hb_buffer_set_unicode_funcs:
    835  * @buffer: a buffer.
    836  * @unicode_funcs:
    837  *
    838  *
    839  *
    840  * Since: 1.0
    841  **/
    842 void
    843 hb_buffer_set_unicode_funcs (hb_buffer_t        *buffer,
    844 			     hb_unicode_funcs_t *unicode_funcs)
    845 {
    846   if (unlikely (hb_object_is_inert (buffer)))
    847     return;
    848 
    849   if (!unicode_funcs)
    850     unicode_funcs = hb_unicode_funcs_get_default ();
    851 
    852 
    853   hb_unicode_funcs_reference (unicode_funcs);
    854   hb_unicode_funcs_destroy (buffer->unicode);
    855   buffer->unicode = unicode_funcs;
    856 }
    857 
    858 /**
    859  * hb_buffer_get_unicode_funcs:
    860  * @buffer: a buffer.
    861  *
    862  *
    863  *
    864  * Return value:
    865  *
    866  * Since: 1.0
    867  **/
    868 hb_unicode_funcs_t *
    869 hb_buffer_get_unicode_funcs (hb_buffer_t        *buffer)
    870 {
    871   return buffer->unicode;
    872 }
    873 
    874 /**
    875  * hb_buffer_set_direction:
    876  * @buffer: a buffer.
    877  * @direction:
    878  *
    879  *
    880  *
    881  * Since: 1.0
    882  **/
    883 void
    884 hb_buffer_set_direction (hb_buffer_t    *buffer,
    885 			 hb_direction_t  direction)
    886 
    887 {
    888   if (unlikely (hb_object_is_inert (buffer)))
    889     return;
    890 
    891   buffer->props.direction = direction;
    892 }
    893 
    894 /**
    895  * hb_buffer_get_direction:
    896  * @buffer: a buffer.
    897  *
    898  *
    899  *
    900  * Return value:
    901  *
    902  * Since: 1.0
    903  **/
    904 hb_direction_t
    905 hb_buffer_get_direction (hb_buffer_t    *buffer)
    906 {
    907   return buffer->props.direction;
    908 }
    909 
    910 /**
    911  * hb_buffer_set_script:
    912  * @buffer: a buffer.
    913  * @script:
    914  *
    915  *
    916  *
    917  * Since: 1.0
    918  **/
    919 void
    920 hb_buffer_set_script (hb_buffer_t *buffer,
    921 		      hb_script_t  script)
    922 {
    923   if (unlikely (hb_object_is_inert (buffer)))
    924     return;
    925 
    926   buffer->props.script = script;
    927 }
    928 
    929 /**
    930  * hb_buffer_get_script:
    931  * @buffer: a buffer.
    932  *
    933  *
    934  *
    935  * Return value:
    936  *
    937  * Since: 1.0
    938  **/
    939 hb_script_t
    940 hb_buffer_get_script (hb_buffer_t *buffer)
    941 {
    942   return buffer->props.script;
    943 }
    944 
    945 /**
    946  * hb_buffer_set_language:
    947  * @buffer: a buffer.
    948  * @language:
    949  *
    950  *
    951  *
    952  * Since: 1.0
    953  **/
    954 void
    955 hb_buffer_set_language (hb_buffer_t   *buffer,
    956 			hb_language_t  language)
    957 {
    958   if (unlikely (hb_object_is_inert (buffer)))
    959     return;
    960 
    961   buffer->props.language = language;
    962 }
    963 
    964 /**
    965  * hb_buffer_get_language:
    966  * @buffer: a buffer.
    967  *
    968  *
    969  *
    970  * Return value:
    971  *
    972  * Since: 1.0
    973  **/
    974 hb_language_t
    975 hb_buffer_get_language (hb_buffer_t *buffer)
    976 {
    977   return buffer->props.language;
    978 }
    979 
    980 /**
    981  * hb_buffer_set_segment_properties:
    982  * @buffer: a buffer.
    983  * @props:
    984  *
    985  *
    986  *
    987  * Since: 1.0
    988  **/
    989 void
    990 hb_buffer_set_segment_properties (hb_buffer_t *buffer,
    991 				  const hb_segment_properties_t *props)
    992 {
    993   if (unlikely (hb_object_is_inert (buffer)))
    994     return;
    995 
    996   buffer->props = *props;
    997 }
    998 
    999 /**
   1000  * hb_buffer_get_segment_properties:
   1001  * @buffer: a buffer.
   1002  * @props:
   1003  *
   1004  *
   1005  *
   1006  * Since: 1.0
   1007  **/
   1008 void
   1009 hb_buffer_get_segment_properties (hb_buffer_t *buffer,
   1010 				  hb_segment_properties_t *props)
   1011 {
   1012   *props = buffer->props;
   1013 }
   1014 
   1015 
   1016 /**
   1017  * hb_buffer_set_flags:
   1018  * @buffer: a buffer.
   1019  * @flags:
   1020  *
   1021  *
   1022  *
   1023  * Since: 1.0
   1024  **/
   1025 void
   1026 hb_buffer_set_flags (hb_buffer_t       *buffer,
   1027 		     hb_buffer_flags_t  flags)
   1028 {
   1029   if (unlikely (hb_object_is_inert (buffer)))
   1030     return;
   1031 
   1032   buffer->flags = flags;
   1033 }
   1034 
   1035 /**
   1036  * hb_buffer_get_flags:
   1037  * @buffer: a buffer.
   1038  *
   1039  *
   1040  *
   1041  * Return value:
   1042  *
   1043  * Since: 1.0
   1044  **/
   1045 hb_buffer_flags_t
   1046 hb_buffer_get_flags (hb_buffer_t *buffer)
   1047 {
   1048   return buffer->flags;
   1049 }
   1050 
   1051 
   1052 /**
   1053  * hb_buffer_set_replacement_codepoint:
   1054  * @buffer: a buffer.
   1055  * @replacement:
   1056  *
   1057  *
   1058  *
   1059  * Since: 1.0
   1060  **/
   1061 void
   1062 hb_buffer_set_replacement_codepoint (hb_buffer_t    *buffer,
   1063 				     hb_codepoint_t  replacement)
   1064 {
   1065   if (unlikely (hb_object_is_inert (buffer)))
   1066     return;
   1067 
   1068   buffer->replacement = replacement;
   1069 }
   1070 
   1071 /**
   1072  * hb_buffer_get_replacement_codepoint:
   1073  * @buffer: a buffer.
   1074  *
   1075  *
   1076  *
   1077  * Return value:
   1078  *
   1079  * Since: 1.0
   1080  **/
   1081 hb_codepoint_t
   1082 hb_buffer_get_replacement_codepoint (hb_buffer_t    *buffer)
   1083 {
   1084   return buffer->replacement;
   1085 }
   1086 
   1087 
   1088 /**
   1089  * hb_buffer_reset:
   1090  * @buffer: a buffer.
   1091  *
   1092  *
   1093  *
   1094  * Since: 1.0
   1095  **/
   1096 void
   1097 hb_buffer_reset (hb_buffer_t *buffer)
   1098 {
   1099   buffer->reset ();
   1100 }
   1101 
   1102 /**
   1103  * hb_buffer_clear_contents:
   1104  * @buffer: a buffer.
   1105  *
   1106  *
   1107  *
   1108  * Since: 1.0
   1109  **/
   1110 void
   1111 hb_buffer_clear_contents (hb_buffer_t *buffer)
   1112 {
   1113   buffer->clear ();
   1114 }
   1115 
   1116 /**
   1117  * hb_buffer_pre_allocate:
   1118  * @buffer: a buffer.
   1119  * @size:
   1120  *
   1121  *
   1122  *
   1123  * Return value:
   1124  *
   1125  * Since: 1.0
   1126  **/
   1127 hb_bool_t
   1128 hb_buffer_pre_allocate (hb_buffer_t *buffer, unsigned int size)
   1129 {
   1130   return buffer->ensure (size);
   1131 }
   1132 
   1133 /**
   1134  * hb_buffer_allocation_successful:
   1135  * @buffer: a buffer.
   1136  *
   1137  *
   1138  *
   1139  * Return value:
   1140  *
   1141  * Since: 1.0
   1142  **/
   1143 hb_bool_t
   1144 hb_buffer_allocation_successful (hb_buffer_t  *buffer)
   1145 {
   1146   return !buffer->in_error;
   1147 }
   1148 
   1149 /**
   1150  * hb_buffer_add:
   1151  * @buffer: a buffer.
   1152  * @codepoint:
   1153  * @cluster:
   1154  *
   1155  *
   1156  *
   1157  * Since: 1.0
   1158  **/
   1159 void
   1160 hb_buffer_add (hb_buffer_t    *buffer,
   1161 	       hb_codepoint_t  codepoint,
   1162 	       unsigned int    cluster)
   1163 {
   1164   buffer->add (codepoint, cluster);
   1165   buffer->clear_context (1);
   1166 }
   1167 
   1168 /**
   1169  * hb_buffer_set_length:
   1170  * @buffer: a buffer.
   1171  * @length:
   1172  *
   1173  *
   1174  *
   1175  * Return value:
   1176  *
   1177  * Since: 1.0
   1178  **/
   1179 hb_bool_t
   1180 hb_buffer_set_length (hb_buffer_t  *buffer,
   1181 		      unsigned int  length)
   1182 {
   1183   if (unlikely (hb_object_is_inert (buffer)))
   1184     return length == 0;
   1185 
   1186   if (!buffer->ensure (length))
   1187     return false;
   1188 
   1189   /* Wipe the new space */
   1190   if (length > buffer->len) {
   1191     memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len));
   1192     if (buffer->have_positions)
   1193       memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len));
   1194   }
   1195 
   1196   buffer->len = length;
   1197 
   1198   if (!length)
   1199   {
   1200     buffer->content_type = HB_BUFFER_CONTENT_TYPE_INVALID;
   1201     buffer->clear_context (0);
   1202   }
   1203   buffer->clear_context (1);
   1204 
   1205   return true;
   1206 }
   1207 
   1208 /**
   1209  * hb_buffer_get_length:
   1210  * @buffer: a buffer.
   1211  *
   1212  * Returns the number of items in the buffer.
   1213  *
   1214  * Return value: buffer length.
   1215  *
   1216  * Since: 1.0
   1217  **/
   1218 unsigned int
   1219 hb_buffer_get_length (hb_buffer_t *buffer)
   1220 {
   1221   return buffer->len;
   1222 }
   1223 
   1224 /**
   1225  * hb_buffer_get_glyph_infos:
   1226  * @buffer: a buffer.
   1227  * @length: (out): output array length.
   1228  *
   1229  * Returns buffer glyph information array.  Returned pointer
   1230  * is valid as long as buffer contents are not modified.
   1231  *
   1232  * Return value: (transfer none) (array length=length): buffer glyph information array.
   1233  *
   1234  * Since: 1.0
   1235  **/
   1236 hb_glyph_info_t *
   1237 hb_buffer_get_glyph_infos (hb_buffer_t  *buffer,
   1238                            unsigned int *length)
   1239 {
   1240   if (length)
   1241     *length = buffer->len;
   1242 
   1243   return (hb_glyph_info_t *) buffer->info;
   1244 }
   1245 
   1246 /**
   1247  * hb_buffer_get_glyph_positions:
   1248  * @buffer: a buffer.
   1249  * @length: (out): output length.
   1250  *
   1251  * Returns buffer glyph position array.  Returned pointer
   1252  * is valid as long as buffer contents are not modified.
   1253  *
   1254  * Return value: (transfer none) (array length=length): buffer glyph position array.
   1255  *
   1256  * Since: 1.0
   1257  **/
   1258 hb_glyph_position_t *
   1259 hb_buffer_get_glyph_positions (hb_buffer_t  *buffer,
   1260                                unsigned int *length)
   1261 {
   1262   if (!buffer->have_positions)
   1263     buffer->clear_positions ();
   1264 
   1265   if (length)
   1266     *length = buffer->len;
   1267 
   1268   return (hb_glyph_position_t *) buffer->pos;
   1269 }
   1270 
   1271 /**
   1272  * hb_buffer_reverse:
   1273  * @buffer: a buffer.
   1274  *
   1275  * Reverses buffer contents.
   1276  *
   1277  * Since: 1.0
   1278  **/
   1279 void
   1280 hb_buffer_reverse (hb_buffer_t *buffer)
   1281 {
   1282   buffer->reverse ();
   1283 }
   1284 
   1285 /**
   1286  * hb_buffer_reverse_clusters:
   1287  * @buffer: a buffer.
   1288  *
   1289  * Reverses buffer clusters.  That is, the buffer contents are
   1290  * reversed, then each cluster (consecutive items having the
   1291  * same cluster number) are reversed again.
   1292  *
   1293  * Since: 1.0
   1294  **/
   1295 void
   1296 hb_buffer_reverse_clusters (hb_buffer_t *buffer)
   1297 {
   1298   buffer->reverse_clusters ();
   1299 }
   1300 
   1301 /**
   1302  * hb_buffer_guess_segment_properties:
   1303  * @buffer: a buffer.
   1304  *
   1305  * Sets unset buffer segment properties based on buffer Unicode
   1306  * contents.  If buffer is not empty, it must have content type
   1307  * %HB_BUFFER_CONTENT_TYPE_UNICODE.
   1308  *
   1309  * If buffer script is not set (ie. is %HB_SCRIPT_INVALID), it
   1310  * will be set to the Unicode script of the first character in
   1311  * the buffer that has a script other than %HB_SCRIPT_COMMON,
   1312  * %HB_SCRIPT_INHERITED, and %HB_SCRIPT_UNKNOWN.
   1313  *
   1314  * Next, if buffer direction is not set (ie. is %HB_DIRECTION_INVALID),
   1315  * it will be set to the natural horizontal direction of the
   1316  * buffer script as returned by hb_script_get_horizontal_direction().
   1317  *
   1318  * Finally, if buffer language is not set (ie. is %HB_LANGUAGE_INVALID),
   1319  * it will be set to the process's default language as returned by
   1320  * hb_language_get_default().  This may change in the future by
   1321  * taking buffer script into consideration when choosing a language.
   1322  *
   1323  * Since: 1.0
   1324  **/
   1325 void
   1326 hb_buffer_guess_segment_properties (hb_buffer_t *buffer)
   1327 {
   1328   buffer->guess_segment_properties ();
   1329 }
   1330 
   1331 template <bool validate, typename T>
   1332 static inline void
   1333 hb_buffer_add_utf (hb_buffer_t  *buffer,
   1334 		   const T      *text,
   1335 		   int           text_length,
   1336 		   unsigned int  item_offset,
   1337 		   int           item_length)
   1338 {
   1339   typedef hb_utf_t<T, true> utf_t;
   1340   const hb_codepoint_t replacement = buffer->replacement;
   1341 
   1342   assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE ||
   1343 	  (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID));
   1344 
   1345   if (unlikely (hb_object_is_inert (buffer)))
   1346     return;
   1347 
   1348   if (text_length == -1)
   1349     text_length = utf_t::strlen (text);
   1350 
   1351   if (item_length == -1)
   1352     item_length = text_length - item_offset;
   1353 
   1354   buffer->ensure (buffer->len + item_length * sizeof (T) / 4);
   1355 
   1356   /* If buffer is empty and pre-context provided, install it.
   1357    * This check is written this way, to make sure people can
   1358    * provide pre-context in one add_utf() call, then provide
   1359    * text in a follow-up call.  See:
   1360    *
   1361    * https://bugzilla.mozilla.org/show_bug.cgi?id=801410#c13
   1362    */
   1363   if (!buffer->len && item_offset > 0)
   1364   {
   1365     /* Add pre-context */
   1366     buffer->clear_context (0);
   1367     const T *prev = text + item_offset;
   1368     const T *start = text;
   1369     while (start < prev && buffer->context_len[0] < buffer->CONTEXT_LENGTH)
   1370     {
   1371       hb_codepoint_t u;
   1372       prev = utf_t::prev (prev, start, &u, replacement);
   1373       buffer->context[0][buffer->context_len[0]++] = u;
   1374     }
   1375   }
   1376 
   1377   const T *next = text + item_offset;
   1378   const T *end = next + item_length;
   1379   while (next < end)
   1380   {
   1381     hb_codepoint_t u;
   1382     const T *old_next = next;
   1383     next = utf_t::next (next, end, &u, replacement);
   1384     buffer->add (u, old_next - (const T *) text);
   1385   }
   1386 
   1387   /* Add post-context */
   1388   buffer->clear_context (1);
   1389   end = text + text_length;
   1390   while (next < end && buffer->context_len[1] < buffer->CONTEXT_LENGTH)
   1391   {
   1392     hb_codepoint_t u;
   1393     next = utf_t::next (next, end, &u, replacement);
   1394     buffer->context[1][buffer->context_len[1]++] = u;
   1395   }
   1396 
   1397   buffer->content_type = HB_BUFFER_CONTENT_TYPE_UNICODE;
   1398 }
   1399 
   1400 /**
   1401  * hb_buffer_add_utf8:
   1402  * @buffer: a buffer.
   1403  * @text: (array length=text_length) (element-type uint8_t):
   1404  * @text_length:
   1405  * @item_offset:
   1406  * @item_length:
   1407  *
   1408  *
   1409  *
   1410  * Since: 1.0
   1411  **/
   1412 void
   1413 hb_buffer_add_utf8 (hb_buffer_t  *buffer,
   1414 		    const char   *text,
   1415 		    int           text_length,
   1416 		    unsigned int  item_offset,
   1417 		    int           item_length)
   1418 {
   1419   hb_buffer_add_utf<true> (buffer, (const uint8_t *) text, text_length, item_offset, item_length);
   1420 }
   1421 
   1422 /**
   1423  * hb_buffer_add_utf16:
   1424  * @buffer: a buffer.
   1425  * @text: (array length=text_length):
   1426  * @text_length:
   1427  * @item_offset:
   1428  * @item_length:
   1429  *
   1430  *
   1431  *
   1432  * Since: 1.0
   1433  **/
   1434 void
   1435 hb_buffer_add_utf16 (hb_buffer_t    *buffer,
   1436 		     const uint16_t *text,
   1437 		     int             text_length,
   1438 		     unsigned int    item_offset,
   1439 		     int             item_length)
   1440 {
   1441   hb_buffer_add_utf<true> (buffer, text, text_length, item_offset, item_length);
   1442 }
   1443 
   1444 /**
   1445  * hb_buffer_add_utf32:
   1446  * @buffer: a buffer.
   1447  * @text: (array length=text_length):
   1448  * @text_length:
   1449  * @item_offset:
   1450  * @item_length:
   1451  *
   1452  *
   1453  *
   1454  * Since: 1.0
   1455  **/
   1456 void
   1457 hb_buffer_add_utf32 (hb_buffer_t    *buffer,
   1458 		     const uint32_t *text,
   1459 		     int             text_length,
   1460 		     unsigned int    item_offset,
   1461 		     int             item_length)
   1462 {
   1463   hb_buffer_add_utf<true> (buffer, text, text_length, item_offset, item_length);
   1464 }
   1465 
   1466 /**
   1467  * hb_buffer_add_codepoints:
   1468  * @buffer: a buffer.
   1469  * @text: (array length=text_length):
   1470  * @text_length:
   1471  * @item_offset:
   1472  * @item_length:
   1473  *
   1474  *
   1475  *
   1476  * Since: 1.0
   1477  **/
   1478 void
   1479 hb_buffer_add_codepoints (hb_buffer_t          *buffer,
   1480 			  const hb_codepoint_t *text,
   1481 			  int                   text_length,
   1482 			  unsigned int          item_offset,
   1483 			  int                   item_length)
   1484 {
   1485   hb_buffer_add_utf<false> (buffer, text, text_length, item_offset, item_length);
   1486 }
   1487 
   1488 
   1489 static int
   1490 compare_info_codepoint (const hb_glyph_info_t *pa,
   1491 			const hb_glyph_info_t *pb)
   1492 {
   1493   return (int) pb->codepoint - (int) pa->codepoint;
   1494 }
   1495 
   1496 static inline void
   1497 normalize_glyphs_cluster (hb_buffer_t *buffer,
   1498 			  unsigned int start,
   1499 			  unsigned int end,
   1500 			  bool backward)
   1501 {
   1502   hb_glyph_position_t *pos = buffer->pos;
   1503 
   1504   /* Total cluster advance */
   1505   hb_position_t total_x_advance = 0, total_y_advance = 0;
   1506   for (unsigned int i = start; i < end; i++)
   1507   {
   1508     total_x_advance += pos[i].x_advance;
   1509     total_y_advance += pos[i].y_advance;
   1510   }
   1511 
   1512   hb_position_t x_advance = 0, y_advance = 0;
   1513   for (unsigned int i = start; i < end; i++)
   1514   {
   1515     pos[i].x_offset += x_advance;
   1516     pos[i].y_offset += y_advance;
   1517 
   1518     x_advance += pos[i].x_advance;
   1519     y_advance += pos[i].y_advance;
   1520 
   1521     pos[i].x_advance = 0;
   1522     pos[i].y_advance = 0;
   1523   }
   1524 
   1525   if (backward)
   1526   {
   1527     /* Transfer all cluster advance to the last glyph. */
   1528     pos[end - 1].x_advance = total_x_advance;
   1529     pos[end - 1].y_advance = total_y_advance;
   1530 
   1531     hb_bubble_sort (buffer->info + start, end - start - 1, compare_info_codepoint, buffer->pos + start);
   1532   } else {
   1533     /* Transfer all cluster advance to the first glyph. */
   1534     pos[start].x_advance += total_x_advance;
   1535     pos[start].y_advance += total_y_advance;
   1536     for (unsigned int i = start + 1; i < end; i++) {
   1537       pos[i].x_offset -= total_x_advance;
   1538       pos[i].y_offset -= total_y_advance;
   1539     }
   1540     hb_bubble_sort (buffer->info + start + 1, end - start - 1, compare_info_codepoint, buffer->pos + start + 1);
   1541   }
   1542 }
   1543 
   1544 /**
   1545  * hb_buffer_normalize_glyphs:
   1546  * @buffer: a buffer.
   1547  *
   1548  *
   1549  *
   1550  * Since: 1.0
   1551  **/
   1552 void
   1553 hb_buffer_normalize_glyphs (hb_buffer_t *buffer)
   1554 {
   1555   assert (buffer->have_positions);
   1556   assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS);
   1557 
   1558   bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
   1559 
   1560   unsigned int count = buffer->len;
   1561   if (unlikely (!count)) return;
   1562   hb_glyph_info_t *info = buffer->info;
   1563 
   1564   unsigned int start = 0;
   1565   unsigned int end;
   1566   for (end = start + 1; end < count; end++)
   1567     if (info[start].cluster != info[end].cluster) {
   1568       normalize_glyphs_cluster (buffer, start, end, backward);
   1569       start = end;
   1570     }
   1571   normalize_glyphs_cluster (buffer, start, end, backward);
   1572 }
   1573