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 void *
    143 hb_buffer_t::get_scratch_buffer (unsigned int *size)
    144 {
    145   have_output = false;
    146   have_positions = false;
    147 
    148   out_len = 0;
    149   out_info = info;
    150 
    151   *size = allocated * sizeof (pos[0]);
    152   return pos;
    153 }
    154 
    155 
    156 
    157 /* HarfBuzz-Internal API */
    158 
    159 void
    160 hb_buffer_t::reset (void)
    161 {
    162   if (unlikely (hb_object_is_inert (this)))
    163     return;
    164 
    165   hb_unicode_funcs_destroy (unicode);
    166   unicode = hb_unicode_funcs_get_default ();
    167 
    168   clear ();
    169 }
    170 
    171 void
    172 hb_buffer_t::clear (void)
    173 {
    174   if (unlikely (hb_object_is_inert (this)))
    175     return;
    176 
    177   hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT;
    178   props = default_props;
    179   flags = HB_BUFFER_FLAGS_DEFAULT;
    180 
    181   content_type = HB_BUFFER_CONTENT_TYPE_INVALID;
    182   in_error = false;
    183   have_output = false;
    184   have_positions = false;
    185 
    186   idx = 0;
    187   len = 0;
    188   out_len = 0;
    189   out_info = info;
    190 
    191   serial = 0;
    192   memset (allocated_var_bytes, 0, sizeof allocated_var_bytes);
    193   memset (allocated_var_owner, 0, sizeof allocated_var_owner);
    194 
    195   memset (context, 0, sizeof context);
    196   memset (context_len, 0, sizeof context_len);
    197 }
    198 
    199 void
    200 hb_buffer_t::add (hb_codepoint_t  codepoint,
    201 		  unsigned int    cluster)
    202 {
    203   hb_glyph_info_t *glyph;
    204 
    205   if (unlikely (!ensure (len + 1))) return;
    206 
    207   glyph = &info[len];
    208 
    209   memset (glyph, 0, sizeof (*glyph));
    210   glyph->codepoint = codepoint;
    211   glyph->mask = 1;
    212   glyph->cluster = cluster;
    213 
    214   len++;
    215 }
    216 
    217 void
    218 hb_buffer_t::add_info (const hb_glyph_info_t &glyph_info)
    219 {
    220   if (unlikely (!ensure (len + 1))) return;
    221 
    222   info[len] = glyph_info;
    223 
    224   len++;
    225 }
    226 
    227 
    228 void
    229 hb_buffer_t::remove_output (void)
    230 {
    231   if (unlikely (hb_object_is_inert (this)))
    232     return;
    233 
    234   have_output = false;
    235   have_positions = false;
    236 
    237   out_len = 0;
    238   out_info = info;
    239 }
    240 
    241 void
    242 hb_buffer_t::clear_output (void)
    243 {
    244   if (unlikely (hb_object_is_inert (this)))
    245     return;
    246 
    247   have_output = true;
    248   have_positions = false;
    249 
    250   out_len = 0;
    251   out_info = info;
    252 }
    253 
    254 void
    255 hb_buffer_t::clear_positions (void)
    256 {
    257   if (unlikely (hb_object_is_inert (this)))
    258     return;
    259 
    260   have_output = false;
    261   have_positions = true;
    262 
    263   out_len = 0;
    264   out_info = info;
    265 
    266   memset (pos, 0, sizeof (pos[0]) * len);
    267 }
    268 
    269 void
    270 hb_buffer_t::swap_buffers (void)
    271 {
    272   if (unlikely (in_error)) return;
    273 
    274   assert (have_output);
    275   have_output = false;
    276 
    277   if (out_info != info)
    278   {
    279     hb_glyph_info_t *tmp_string;
    280     tmp_string = info;
    281     info = out_info;
    282     out_info = tmp_string;
    283     pos = (hb_glyph_position_t *) out_info;
    284   }
    285 
    286   unsigned int tmp;
    287   tmp = len;
    288   len = out_len;
    289   out_len = tmp;
    290 
    291   idx = 0;
    292 }
    293 
    294 
    295 void
    296 hb_buffer_t::replace_glyphs (unsigned int num_in,
    297 			     unsigned int num_out,
    298 			     const uint32_t *glyph_data)
    299 {
    300   if (unlikely (!make_room_for (num_in, num_out))) return;
    301 
    302   merge_clusters (idx, idx + num_in);
    303 
    304   hb_glyph_info_t orig_info = info[idx];
    305   hb_glyph_info_t *pinfo = &out_info[out_len];
    306   for (unsigned int i = 0; i < num_out; i++)
    307   {
    308     *pinfo = orig_info;
    309     pinfo->codepoint = glyph_data[i];
    310     pinfo++;
    311   }
    312 
    313   idx  += num_in;
    314   out_len += num_out;
    315 }
    316 
    317 void
    318 hb_buffer_t::output_glyph (hb_codepoint_t glyph_index)
    319 {
    320   if (unlikely (!make_room_for (0, 1))) return;
    321 
    322   out_info[out_len] = info[idx];
    323   out_info[out_len].codepoint = glyph_index;
    324 
    325   out_len++;
    326 }
    327 
    328 void
    329 hb_buffer_t::output_info (const hb_glyph_info_t &glyph_info)
    330 {
    331   if (unlikely (!make_room_for (0, 1))) return;
    332 
    333   out_info[out_len] = glyph_info;
    334 
    335   out_len++;
    336 }
    337 
    338 void
    339 hb_buffer_t::copy_glyph (void)
    340 {
    341   if (unlikely (!make_room_for (0, 1))) return;
    342 
    343   out_info[out_len] = info[idx];
    344 
    345   out_len++;
    346 }
    347 
    348 void
    349 hb_buffer_t::replace_glyph (hb_codepoint_t glyph_index)
    350 {
    351   if (unlikely (out_info != info || out_len != idx)) {
    352     if (unlikely (!make_room_for (1, 1))) return;
    353     out_info[out_len] = info[idx];
    354   }
    355   out_info[out_len].codepoint = glyph_index;
    356 
    357   idx++;
    358   out_len++;
    359 }
    360 
    361 
    362 void
    363 hb_buffer_t::set_masks (hb_mask_t    value,
    364 			hb_mask_t    mask,
    365 			unsigned int cluster_start,
    366 			unsigned int cluster_end)
    367 {
    368   hb_mask_t not_mask = ~mask;
    369   value &= mask;
    370 
    371   if (!mask)
    372     return;
    373 
    374   if (cluster_start == 0 && cluster_end == (unsigned int)-1) {
    375     unsigned int count = len;
    376     for (unsigned int i = 0; i < count; i++)
    377       info[i].mask = (info[i].mask & not_mask) | value;
    378     return;
    379   }
    380 
    381   unsigned int count = len;
    382   for (unsigned int i = 0; i < count; i++)
    383     if (cluster_start <= info[i].cluster && info[i].cluster < cluster_end)
    384       info[i].mask = (info[i].mask & not_mask) | value;
    385 }
    386 
    387 void
    388 hb_buffer_t::reverse_range (unsigned int start,
    389 			    unsigned int end)
    390 {
    391   unsigned int i, j;
    392 
    393   if (start == end - 1)
    394     return;
    395 
    396   for (i = start, j = end - 1; i < j; i++, j--) {
    397     hb_glyph_info_t t;
    398 
    399     t = info[i];
    400     info[i] = info[j];
    401     info[j] = t;
    402   }
    403 
    404   if (pos) {
    405     for (i = start, j = end - 1; i < j; i++, j--) {
    406       hb_glyph_position_t t;
    407 
    408       t = pos[i];
    409       pos[i] = pos[j];
    410       pos[j] = t;
    411     }
    412   }
    413 }
    414 
    415 void
    416 hb_buffer_t::reverse (void)
    417 {
    418   if (unlikely (!len))
    419     return;
    420 
    421   reverse_range (0, len);
    422 }
    423 
    424 void
    425 hb_buffer_t::reverse_clusters (void)
    426 {
    427   unsigned int i, start, count, last_cluster;
    428 
    429   if (unlikely (!len))
    430     return;
    431 
    432   reverse ();
    433 
    434   count = len;
    435   start = 0;
    436   last_cluster = info[0].cluster;
    437   for (i = 1; i < count; i++) {
    438     if (last_cluster != info[i].cluster) {
    439       reverse_range (start, i);
    440       start = i;
    441       last_cluster = info[i].cluster;
    442     }
    443   }
    444   reverse_range (start, i);
    445 }
    446 
    447 void
    448 hb_buffer_t::merge_clusters (unsigned int start,
    449 			     unsigned int end)
    450 {
    451   if (unlikely (end - start < 2))
    452     return;
    453 
    454   unsigned int cluster = info[start].cluster;
    455 
    456   for (unsigned int i = start + 1; i < end; i++)
    457     cluster = MIN (cluster, info[i].cluster);
    458 
    459   /* Extend end */
    460   while (end < len && info[end - 1].cluster == info[end].cluster)
    461     end++;
    462 
    463   /* Extend start */
    464   while (idx < start && info[start - 1].cluster == info[start].cluster)
    465     start--;
    466 
    467   /* If we hit the start of buffer, continue in out-buffer. */
    468   if (idx == start)
    469     for (unsigned i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--)
    470       out_info[i - 1].cluster = cluster;
    471 
    472   for (unsigned int i = start; i < end; i++)
    473     info[i].cluster = cluster;
    474 }
    475 void
    476 hb_buffer_t::merge_out_clusters (unsigned int start,
    477 				 unsigned int end)
    478 {
    479   if (unlikely (end - start < 2))
    480     return;
    481 
    482   unsigned int cluster = out_info[start].cluster;
    483 
    484   for (unsigned int i = start + 1; i < end; i++)
    485     cluster = MIN (cluster, out_info[i].cluster);
    486 
    487   /* Extend start */
    488   while (start && out_info[start - 1].cluster == out_info[start].cluster)
    489     start--;
    490 
    491   /* Extend end */
    492   while (end < out_len && out_info[end - 1].cluster == out_info[end].cluster)
    493     end++;
    494 
    495   /* If we hit the end of out-buffer, continue in buffer. */
    496   if (end == out_len)
    497     for (unsigned i = idx; i < len && info[i].cluster == out_info[end - 1].cluster; i++)
    498       info[i].cluster = cluster;
    499 
    500   for (unsigned int i = start; i < end; i++)
    501     out_info[i].cluster = cluster;
    502 }
    503 
    504 void
    505 hb_buffer_t::guess_segment_properties (void)
    506 {
    507   assert (content_type == HB_BUFFER_CONTENT_TYPE_UNICODE ||
    508 	  (!len && content_type == HB_BUFFER_CONTENT_TYPE_INVALID));
    509 
    510   /* If script is set to INVALID, guess from buffer contents */
    511   if (props.script == HB_SCRIPT_INVALID) {
    512     for (unsigned int i = 0; i < len; i++) {
    513       hb_script_t script = unicode->script (info[i].codepoint);
    514       if (likely (script != HB_SCRIPT_COMMON &&
    515 		  script != HB_SCRIPT_INHERITED &&
    516 		  script != HB_SCRIPT_UNKNOWN)) {
    517         props.script = script;
    518         break;
    519       }
    520     }
    521   }
    522 
    523   /* If direction is set to INVALID, guess from script */
    524   if (props.direction == HB_DIRECTION_INVALID) {
    525     props.direction = hb_script_get_horizontal_direction (props.script);
    526   }
    527 
    528   /* If language is not set, use default language from locale */
    529   if (props.language == HB_LANGUAGE_INVALID) {
    530     /* TODO get_default_for_script? using $LANGUAGE */
    531     props.language = hb_language_get_default ();
    532   }
    533 }
    534 
    535 
    536 static inline void
    537 dump_var_allocation (const hb_buffer_t *buffer)
    538 {
    539   char buf[80];
    540   for (unsigned int i = 0; i < 8; i++)
    541     buf[i] = '0' + buffer->allocated_var_bytes[7 - i];
    542   buf[8] = '\0';
    543   DEBUG_MSG (BUFFER, buffer,
    544 	     "Current var allocation: %s",
    545 	     buf);
    546 }
    547 
    548 void hb_buffer_t::allocate_var (unsigned int byte_i, unsigned int count, const char *owner)
    549 {
    550   assert (byte_i < 8 && byte_i + count <= 8);
    551 
    552   if (DEBUG (BUFFER))
    553     dump_var_allocation (this);
    554   DEBUG_MSG (BUFFER, this,
    555 	     "Allocating var bytes %d..%d for %s",
    556 	     byte_i, byte_i + count - 1, owner);
    557 
    558   for (unsigned int i = byte_i; i < byte_i + count; i++) {
    559     assert (!allocated_var_bytes[i]);
    560     allocated_var_bytes[i]++;
    561     allocated_var_owner[i] = owner;
    562   }
    563 }
    564 
    565 void hb_buffer_t::deallocate_var (unsigned int byte_i, unsigned int count, const char *owner)
    566 {
    567   if (DEBUG (BUFFER))
    568     dump_var_allocation (this);
    569 
    570   DEBUG_MSG (BUFFER, this,
    571 	     "Deallocating var bytes %d..%d for %s",
    572 	     byte_i, byte_i + count - 1, owner);
    573 
    574   assert (byte_i < 8 && byte_i + count <= 8);
    575   for (unsigned int i = byte_i; i < byte_i + count; i++) {
    576     assert (allocated_var_bytes[i]);
    577     assert (0 == strcmp (allocated_var_owner[i], owner));
    578     allocated_var_bytes[i]--;
    579   }
    580 }
    581 
    582 void hb_buffer_t::assert_var (unsigned int byte_i, unsigned int count, const char *owner)
    583 {
    584   if (DEBUG (BUFFER))
    585     dump_var_allocation (this);
    586 
    587   DEBUG_MSG (BUFFER, this,
    588 	     "Asserting var bytes %d..%d for %s",
    589 	     byte_i, byte_i + count - 1, owner);
    590 
    591   assert (byte_i < 8 && byte_i + count <= 8);
    592   for (unsigned int i = byte_i; i < byte_i + count; i++) {
    593     assert (allocated_var_bytes[i]);
    594     assert (0 == strcmp (allocated_var_owner[i], owner));
    595   }
    596 }
    597 
    598 void hb_buffer_t::deallocate_var_all (void)
    599 {
    600   memset (allocated_var_bytes, 0, sizeof (allocated_var_bytes));
    601   memset (allocated_var_owner, 0, sizeof (allocated_var_owner));
    602 }
    603 
    604 /* Public API */
    605 
    606 hb_buffer_t *
    607 hb_buffer_create (void)
    608 {
    609   hb_buffer_t *buffer;
    610 
    611   if (!(buffer = hb_object_create<hb_buffer_t> ()))
    612     return hb_buffer_get_empty ();
    613 
    614   buffer->reset ();
    615 
    616   return buffer;
    617 }
    618 
    619 hb_buffer_t *
    620 hb_buffer_get_empty (void)
    621 {
    622   static const hb_buffer_t _hb_buffer_nil = {
    623     HB_OBJECT_HEADER_STATIC,
    624 
    625     const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil),
    626     HB_SEGMENT_PROPERTIES_DEFAULT,
    627     HB_BUFFER_FLAGS_DEFAULT,
    628 
    629     HB_BUFFER_CONTENT_TYPE_INVALID,
    630     true, /* in_error */
    631     true, /* have_output */
    632     true  /* have_positions */
    633 
    634     /* Zero is good enough for everything else. */
    635   };
    636 
    637   return const_cast<hb_buffer_t *> (&_hb_buffer_nil);
    638 }
    639 
    640 hb_buffer_t *
    641 hb_buffer_reference (hb_buffer_t *buffer)
    642 {
    643   return hb_object_reference (buffer);
    644 }
    645 
    646 void
    647 hb_buffer_destroy (hb_buffer_t *buffer)
    648 {
    649   if (!hb_object_destroy (buffer)) return;
    650 
    651   hb_unicode_funcs_destroy (buffer->unicode);
    652 
    653   free (buffer->info);
    654   free (buffer->pos);
    655 
    656   free (buffer);
    657 }
    658 
    659 hb_bool_t
    660 hb_buffer_set_user_data (hb_buffer_t        *buffer,
    661 			 hb_user_data_key_t *key,
    662 			 void *              data,
    663 			 hb_destroy_func_t   destroy,
    664 			 hb_bool_t           replace)
    665 {
    666   return hb_object_set_user_data (buffer, key, data, destroy, replace);
    667 }
    668 
    669 void *
    670 hb_buffer_get_user_data (hb_buffer_t        *buffer,
    671 			 hb_user_data_key_t *key)
    672 {
    673   return hb_object_get_user_data (buffer, key);
    674 }
    675 
    676 
    677 void
    678 hb_buffer_set_content_type (hb_buffer_t              *buffer,
    679 			    hb_buffer_content_type_t  content_type)
    680 {
    681   buffer->content_type = content_type;
    682 }
    683 
    684 hb_buffer_content_type_t
    685 hb_buffer_get_content_type (hb_buffer_t *buffer)
    686 {
    687   return buffer->content_type;
    688 }
    689 
    690 
    691 void
    692 hb_buffer_set_unicode_funcs (hb_buffer_t        *buffer,
    693 			     hb_unicode_funcs_t *unicode)
    694 {
    695   if (unlikely (hb_object_is_inert (buffer)))
    696     return;
    697 
    698   if (!unicode)
    699     unicode = hb_unicode_funcs_get_default ();
    700 
    701 
    702   hb_unicode_funcs_reference (unicode);
    703   hb_unicode_funcs_destroy (buffer->unicode);
    704   buffer->unicode = unicode;
    705 }
    706 
    707 hb_unicode_funcs_t *
    708 hb_buffer_get_unicode_funcs (hb_buffer_t        *buffer)
    709 {
    710   return buffer->unicode;
    711 }
    712 
    713 void
    714 hb_buffer_set_direction (hb_buffer_t    *buffer,
    715 			 hb_direction_t  direction)
    716 
    717 {
    718   if (unlikely (hb_object_is_inert (buffer)))
    719     return;
    720 
    721   buffer->props.direction = direction;
    722 }
    723 
    724 hb_direction_t
    725 hb_buffer_get_direction (hb_buffer_t    *buffer)
    726 {
    727   return buffer->props.direction;
    728 }
    729 
    730 void
    731 hb_buffer_set_script (hb_buffer_t *buffer,
    732 		      hb_script_t  script)
    733 {
    734   if (unlikely (hb_object_is_inert (buffer)))
    735     return;
    736 
    737   buffer->props.script = script;
    738 }
    739 
    740 hb_script_t
    741 hb_buffer_get_script (hb_buffer_t *buffer)
    742 {
    743   return buffer->props.script;
    744 }
    745 
    746 void
    747 hb_buffer_set_language (hb_buffer_t   *buffer,
    748 			hb_language_t  language)
    749 {
    750   if (unlikely (hb_object_is_inert (buffer)))
    751     return;
    752 
    753   buffer->props.language = language;
    754 }
    755 
    756 hb_language_t
    757 hb_buffer_get_language (hb_buffer_t *buffer)
    758 {
    759   return buffer->props.language;
    760 }
    761 
    762 void
    763 hb_buffer_set_segment_properties (hb_buffer_t *buffer,
    764 				  const hb_segment_properties_t *props)
    765 {
    766   if (unlikely (hb_object_is_inert (buffer)))
    767     return;
    768 
    769   buffer->props = *props;
    770 }
    771 
    772 void
    773 hb_buffer_get_segment_properties (hb_buffer_t *buffer,
    774 				  hb_segment_properties_t *props)
    775 {
    776   *props = buffer->props;
    777 }
    778 
    779 
    780 void
    781 hb_buffer_set_flags (hb_buffer_t       *buffer,
    782 		     hb_buffer_flags_t  flags)
    783 {
    784   if (unlikely (hb_object_is_inert (buffer)))
    785     return;
    786 
    787   buffer->flags = flags;
    788 }
    789 
    790 hb_buffer_flags_t
    791 hb_buffer_get_flags (hb_buffer_t *buffer)
    792 {
    793   return buffer->flags;
    794 }
    795 
    796 
    797 void
    798 hb_buffer_reset (hb_buffer_t *buffer)
    799 {
    800   buffer->reset ();
    801 }
    802 
    803 void
    804 hb_buffer_clear_contents (hb_buffer_t *buffer)
    805 {
    806   buffer->clear ();
    807 }
    808 
    809 hb_bool_t
    810 hb_buffer_pre_allocate (hb_buffer_t *buffer, unsigned int size)
    811 {
    812   return buffer->ensure (size);
    813 }
    814 
    815 hb_bool_t
    816 hb_buffer_allocation_successful (hb_buffer_t  *buffer)
    817 {
    818   return !buffer->in_error;
    819 }
    820 
    821 void
    822 hb_buffer_add (hb_buffer_t    *buffer,
    823 	       hb_codepoint_t  codepoint,
    824 	       unsigned int    cluster)
    825 {
    826   buffer->add (codepoint, cluster);
    827   buffer->clear_context (1);
    828 }
    829 
    830 hb_bool_t
    831 hb_buffer_set_length (hb_buffer_t  *buffer,
    832 		      unsigned int  length)
    833 {
    834   if (unlikely (hb_object_is_inert (buffer)))
    835     return length == 0;
    836 
    837   if (!buffer->ensure (length))
    838     return false;
    839 
    840   /* Wipe the new space */
    841   if (length > buffer->len) {
    842     memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len));
    843     if (buffer->have_positions)
    844       memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len));
    845   }
    846 
    847   buffer->len = length;
    848 
    849   if (!length)
    850     buffer->clear_context (0);
    851   buffer->clear_context (1);
    852 
    853   return true;
    854 }
    855 
    856 unsigned int
    857 hb_buffer_get_length (hb_buffer_t *buffer)
    858 {
    859   return buffer->len;
    860 }
    861 
    862 /* Return value valid as long as buffer not modified */
    863 hb_glyph_info_t *
    864 hb_buffer_get_glyph_infos (hb_buffer_t  *buffer,
    865                            unsigned int *length)
    866 {
    867   if (length)
    868     *length = buffer->len;
    869 
    870   return (hb_glyph_info_t *) buffer->info;
    871 }
    872 
    873 /* Return value valid as long as buffer not modified */
    874 hb_glyph_position_t *
    875 hb_buffer_get_glyph_positions (hb_buffer_t  *buffer,
    876                                unsigned int *length)
    877 {
    878   if (!buffer->have_positions)
    879     buffer->clear_positions ();
    880 
    881   if (length)
    882     *length = buffer->len;
    883 
    884   return (hb_glyph_position_t *) buffer->pos;
    885 }
    886 
    887 void
    888 hb_buffer_reverse (hb_buffer_t *buffer)
    889 {
    890   buffer->reverse ();
    891 }
    892 
    893 void
    894 hb_buffer_reverse_clusters (hb_buffer_t *buffer)
    895 {
    896   buffer->reverse_clusters ();
    897 }
    898 
    899 void
    900 hb_buffer_guess_segment_properties (hb_buffer_t *buffer)
    901 {
    902   buffer->guess_segment_properties ();
    903 }
    904 
    905 template <typename T>
    906 static inline void
    907 hb_buffer_add_utf (hb_buffer_t  *buffer,
    908 		   const T      *text,
    909 		   int           text_length,
    910 		   unsigned int  item_offset,
    911 		   int           item_length)
    912 {
    913   assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE ||
    914 	  (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID));
    915 
    916   if (unlikely (hb_object_is_inert (buffer)))
    917     return;
    918 
    919   if (text_length == -1)
    920     text_length = hb_utf_strlen (text);
    921 
    922   if (item_length == -1)
    923     item_length = text_length - item_offset;
    924 
    925   buffer->ensure (buffer->len + item_length * sizeof (T) / 4);
    926 
    927   /* If buffer is empty and pre-context provided, install it.
    928    * This check is written this way, to make sure people can
    929    * provide pre-context in one add_utf() call, then provide
    930    * text in a follow-up call.  See:
    931    *
    932    * https://bugzilla.mozilla.org/show_bug.cgi?id=801410#c13
    933    */
    934   if (!buffer->len && item_offset > 0)
    935   {
    936     /* Add pre-context */
    937     buffer->clear_context (0);
    938     const T *prev = text + item_offset;
    939     const T *start = text;
    940     while (start < prev && buffer->context_len[0] < buffer->CONTEXT_LENGTH)
    941     {
    942       hb_codepoint_t u;
    943       prev = hb_utf_prev (prev, start, &u);
    944       buffer->context[0][buffer->context_len[0]++] = u;
    945     }
    946   }
    947 
    948   const T *next = text + item_offset;
    949   const T *end = next + item_length;
    950   while (next < end)
    951   {
    952     hb_codepoint_t u;
    953     const T *old_next = next;
    954     next = hb_utf_next (next, end, &u);
    955     buffer->add (u, old_next - (const T *) text);
    956   }
    957 
    958   /* Add post-context */
    959   buffer->clear_context (1);
    960   end = text + text_length;
    961   while (next < end && buffer->context_len[1] < buffer->CONTEXT_LENGTH)
    962   {
    963     hb_codepoint_t u;
    964     next = hb_utf_next (next, end, &u);
    965     buffer->context[1][buffer->context_len[1]++] = u;
    966   }
    967 
    968   buffer->content_type = HB_BUFFER_CONTENT_TYPE_UNICODE;
    969 }
    970 
    971 void
    972 hb_buffer_add_utf8 (hb_buffer_t  *buffer,
    973 		    const char   *text,
    974 		    int           text_length,
    975 		    unsigned int  item_offset,
    976 		    int           item_length)
    977 {
    978   hb_buffer_add_utf (buffer, (const uint8_t *) text, text_length, item_offset, item_length);
    979 }
    980 
    981 void
    982 hb_buffer_add_utf16 (hb_buffer_t    *buffer,
    983 		     const uint16_t *text,
    984 		     int             text_length,
    985 		     unsigned int    item_offset,
    986 		     int            item_length)
    987 {
    988   hb_buffer_add_utf (buffer, text, text_length, item_offset, item_length);
    989 }
    990 
    991 void
    992 hb_buffer_add_utf32 (hb_buffer_t    *buffer,
    993 		     const uint32_t *text,
    994 		     int             text_length,
    995 		     unsigned int    item_offset,
    996 		     int             item_length)
    997 {
    998   hb_buffer_add_utf (buffer, text, text_length, item_offset, item_length);
    999 }
   1000 
   1001 
   1002 static int
   1003 compare_info_codepoint (const hb_glyph_info_t *pa,
   1004 			const hb_glyph_info_t *pb)
   1005 {
   1006   return (int) pb->codepoint - (int) pa->codepoint;
   1007 }
   1008 
   1009 static inline void
   1010 normalize_glyphs_cluster (hb_buffer_t *buffer,
   1011 			  unsigned int start,
   1012 			  unsigned int end,
   1013 			  bool backward)
   1014 {
   1015   hb_glyph_position_t *pos = buffer->pos;
   1016 
   1017   /* Total cluster advance */
   1018   hb_position_t total_x_advance = 0, total_y_advance = 0;
   1019   for (unsigned int i = start; i < end; i++)
   1020   {
   1021     total_x_advance += pos[i].x_advance;
   1022     total_y_advance += pos[i].y_advance;
   1023   }
   1024 
   1025   hb_position_t x_advance = 0, y_advance = 0;
   1026   for (unsigned int i = start; i < end; i++)
   1027   {
   1028     pos[i].x_offset += x_advance;
   1029     pos[i].y_offset += y_advance;
   1030 
   1031     x_advance += pos[i].x_advance;
   1032     y_advance += pos[i].y_advance;
   1033 
   1034     pos[i].x_advance = 0;
   1035     pos[i].y_advance = 0;
   1036   }
   1037 
   1038   if (backward)
   1039   {
   1040     /* Transfer all cluster advance to the last glyph. */
   1041     pos[end - 1].x_advance = total_x_advance;
   1042     pos[end - 1].y_advance = total_y_advance;
   1043 
   1044     hb_bubble_sort (buffer->info + start, end - start - 1, compare_info_codepoint, buffer->pos + start);
   1045   } else {
   1046     /* Transfer all cluster advance to the first glyph. */
   1047     pos[start].x_advance += total_x_advance;
   1048     pos[start].y_advance += total_y_advance;
   1049     for (unsigned int i = start + 1; i < end; i++) {
   1050       pos[i].x_offset -= total_x_advance;
   1051       pos[i].y_offset -= total_y_advance;
   1052     }
   1053     hb_bubble_sort (buffer->info + start + 1, end - start - 1, compare_info_codepoint, buffer->pos + start + 1);
   1054   }
   1055 }
   1056 
   1057 void
   1058 hb_buffer_normalize_glyphs (hb_buffer_t *buffer)
   1059 {
   1060   assert (buffer->have_positions);
   1061   assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS);
   1062 
   1063   bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
   1064 
   1065   unsigned int count = buffer->len;
   1066   if (unlikely (!count)) return;
   1067   hb_glyph_info_t *info = buffer->info;
   1068 
   1069   unsigned int start = 0;
   1070   unsigned int end;
   1071   for (end = start + 1; end < count; end++)
   1072     if (info[start].cluster != info[end].cluster) {
   1073       normalize_glyphs_cluster (buffer, start, end, backward);
   1074       start = end;
   1075     }
   1076   normalize_glyphs_cluster (buffer, start, end, backward);
   1077 }
   1078