hb-buffer.cc revision e1ac38f8dd04c29d2d4140f5a492cdaf25d72901
1/*
2 * Copyright © 1998-2004  David Turner and Werner Lemberg
3 * Copyright © 2004,2007,2009,2010  Red Hat, Inc.
4 * Copyright © 2011  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
32#include <string.h>
33
34
35
36#ifndef HB_DEBUG_BUFFER
37#define HB_DEBUG_BUFFER (HB_DEBUG+0)
38#endif
39
40#define _HB_BUFFER_UNICODE_FUNCS_DEFAULT (const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_default))
41#define _HB_BUFFER_PROPS_DEFAULT { HB_DIRECTION_INVALID, HB_SCRIPT_INVALID, HB_LANGUAGE_INVALID }
42
43/* Here is how the buffer works internally:
44 *
45 * There are two info pointers: info and out_info.  They always have
46 * the same allocated size, but different lengths.
47 *
48 * As an optimization, both info and out_info may point to the
49 * same piece of memory, which is owned by info.  This remains the
50 * case as long as out_len doesn't exceed i at any time.
51 * In that case, swap_buffers() is no-op and the glyph operations operate
52 * mostly in-place.
53 *
54 * As soon as out_info gets longer than info, out_info is moved over
55 * to an alternate buffer (which we reuse the pos buffer for!), and its
56 * current contents (out_len entries) are copied to the new place.
57 * This should all remain transparent to the user.  swap_buffers() then
58 * switches info and out_info.
59 */
60
61
62
63/* Internal API */
64
65bool
66hb_buffer_t::enlarge (unsigned int size)
67{
68  if (unlikely (in_error))
69    return FALSE;
70
71  unsigned int new_allocated = allocated;
72  hb_glyph_position_t *new_pos = NULL;
73  hb_glyph_info_t *new_info = NULL;
74  bool separate_out = out_info != info;
75
76  if (unlikely (_hb_unsigned_int_mul_overflows (size, sizeof (info[0]))))
77    goto done;
78
79  while (size > new_allocated)
80    new_allocated += (new_allocated >> 1) + 32;
81
82  ASSERT_STATIC (sizeof (info[0]) == sizeof (pos[0]));
83  if (unlikely (_hb_unsigned_int_mul_overflows (new_allocated, sizeof (info[0]))))
84    goto done;
85
86  new_pos = (hb_glyph_position_t *) realloc (pos, new_allocated * sizeof (pos[0]));
87  new_info = (hb_glyph_info_t *) realloc (info, new_allocated * sizeof (info[0]));
88
89done:
90  if (unlikely (!new_pos || !new_info))
91    in_error = TRUE;
92
93  if (likely (new_pos))
94    pos = new_pos;
95
96  if (likely (new_info))
97    info = new_info;
98
99  out_info = separate_out ? (hb_glyph_info_t *) pos : info;
100  if (likely (!in_error))
101    allocated = new_allocated;
102
103  return likely (!in_error);
104}
105
106bool
107hb_buffer_t::make_room_for (unsigned int num_in,
108			    unsigned int num_out)
109{
110  if (unlikely (!ensure (out_len + num_out))) return FALSE;
111
112  if (out_info == info &&
113      out_len + num_out > idx + num_in)
114  {
115    assert (have_output);
116
117    out_info = (hb_glyph_info_t *) pos;
118    memcpy (out_info, info, out_len * sizeof (out_info[0]));
119  }
120
121  return TRUE;
122}
123
124void *
125hb_buffer_t::get_scratch_buffer (unsigned int *size)
126{
127  have_output = FALSE;
128  have_positions = FALSE;
129  out_len = 0;
130  *size = allocated * sizeof (pos[0]);
131  return pos;
132}
133
134
135/* HarfBuzz-Internal API */
136
137void
138hb_buffer_t::reset (void)
139{
140  if (unlikely (hb_object_is_inert (this)))
141    return;
142
143  hb_unicode_funcs_destroy (unicode);
144  unicode = _HB_BUFFER_UNICODE_FUNCS_DEFAULT;
145
146  hb_segment_properties_t default_props = _HB_BUFFER_PROPS_DEFAULT;
147  props = default_props;
148
149  in_error = FALSE;
150  have_output = FALSE;
151  have_positions = FALSE;
152
153  idx = 0;
154  len = 0;
155  out_len = 0;
156
157  serial = 0;
158  memset (allocated_var_bytes, 0, sizeof allocated_var_bytes);
159  memset (allocated_var_owner, 0, sizeof allocated_var_owner);
160
161  out_info = info;
162}
163
164void
165hb_buffer_t::add (hb_codepoint_t  codepoint,
166		  hb_mask_t       mask,
167		  unsigned int    cluster)
168{
169  hb_glyph_info_t *glyph;
170
171  if (unlikely (!ensure (len + 1))) return;
172
173  glyph = &info[len];
174
175  memset (glyph, 0, sizeof (*glyph));
176  glyph->codepoint = codepoint;
177  glyph->mask = mask;
178  glyph->cluster = cluster;
179
180  len++;
181}
182
183void
184hb_buffer_t::clear_output (void)
185{
186  if (unlikely (hb_object_is_inert (this)))
187    return;
188
189  have_output = TRUE;
190  have_positions = FALSE;
191
192  out_len = 0;
193  out_info = info;
194}
195
196void
197hb_buffer_t::clear_positions (void)
198{
199  if (unlikely (hb_object_is_inert (this)))
200    return;
201
202  have_output = FALSE;
203  have_positions = TRUE;
204
205  memset (pos, 0, sizeof (pos[0]) * len);
206}
207
208void
209hb_buffer_t::swap_buffers (void)
210{
211  if (unlikely (in_error)) return;
212
213  assert (have_output);
214  have_output = FALSE;
215
216  if (out_info != info)
217  {
218    hb_glyph_info_t *tmp_string;
219    tmp_string = info;
220    info = out_info;
221    out_info = tmp_string;
222    pos = (hb_glyph_position_t *) out_info;
223  }
224
225  unsigned int tmp;
226  tmp = len;
227  len = out_len;
228  out_len = tmp;
229
230  idx = 0;
231}
232
233void
234hb_buffer_t::replace_glyphs_be16 (unsigned int num_in,
235				  unsigned int num_out,
236				  const uint16_t *glyph_data_be)
237{
238  if (!make_room_for (num_in, num_out)) return;
239
240  hb_glyph_info_t orig_info = info[idx];
241  for (unsigned int i = 1; i < num_in; i++)
242  {
243    hb_glyph_info_t *inf = &info[idx + i];
244    orig_info.cluster = MIN (orig_info.cluster, inf->cluster);
245  }
246
247  hb_glyph_info_t *pinfo = &out_info[out_len];
248  for (unsigned int i = 0; i < num_out; i++)
249  {
250    *pinfo = orig_info;
251    pinfo->codepoint = hb_be_uint16 (glyph_data_be[i]);
252    pinfo++;
253  }
254
255  idx  += num_in;
256  out_len += num_out;
257}
258
259void
260hb_buffer_t::replace_glyphs (unsigned int num_in,
261			     unsigned int num_out,
262			     const uint32_t *glyph_data)
263{
264  if (!make_room_for (num_in, num_out)) return;
265
266  hb_glyph_info_t orig_info = info[idx];
267  for (unsigned int i = 1; i < num_in; i++)
268  {
269    hb_glyph_info_t *inf = &info[idx + i];
270    orig_info.cluster = MIN (orig_info.cluster, inf->cluster);
271  }
272
273  hb_glyph_info_t *pinfo = &out_info[out_len];
274  for (unsigned int i = 0; i < num_out; i++)
275  {
276    *pinfo = orig_info;
277    pinfo->codepoint = glyph_data[i];
278    pinfo++;
279  }
280
281  idx  += num_in;
282  out_len += num_out;
283}
284
285void
286hb_buffer_t::output_glyph (hb_codepoint_t glyph_index)
287{
288  if (!make_room_for (0, 1)) return;
289
290  out_info[out_len] = info[idx];
291  out_info[out_len].codepoint = glyph_index;
292
293  out_len++;
294}
295
296void
297hb_buffer_t::copy_glyph (void)
298{
299  if (!make_room_for (0, 1)) return;
300
301  out_info[out_len] = info[idx];
302
303  out_len++;
304}
305
306void
307hb_buffer_t::replace_glyph (hb_codepoint_t glyph_index)
308{
309  if (!make_room_for (1, 1)) return;
310
311  out_info[out_len] = info[idx];
312  out_info[out_len].codepoint = glyph_index;
313
314  idx++;
315  out_len++;
316}
317
318void
319hb_buffer_t::next_glyph (void)
320{
321  if (have_output)
322  {
323    if (out_info != info)
324    {
325      if (unlikely (!ensure (out_len + 1))) return;
326      out_info[out_len] = info[idx];
327    }
328    else if (out_len != idx)
329      out_info[out_len] = info[idx];
330
331    out_len++;
332  }
333
334  idx++;
335}
336
337void
338hb_buffer_t::set_masks (hb_mask_t    value,
339			hb_mask_t    mask,
340			unsigned int cluster_start,
341			unsigned int cluster_end)
342{
343  hb_mask_t not_mask = ~mask;
344  value &= mask;
345
346  if (!mask)
347    return;
348
349  if (cluster_start == 0 && cluster_end == (unsigned int)-1) {
350    unsigned int count = len;
351    for (unsigned int i = 0; i < count; i++)
352      info[i].mask = (info[i].mask & not_mask) | value;
353    return;
354  }
355
356  unsigned int count = len;
357  for (unsigned int i = 0; i < count; i++)
358    if (cluster_start <= info[i].cluster && info[i].cluster < cluster_end)
359      info[i].mask = (info[i].mask & not_mask) | value;
360}
361
362void
363hb_buffer_t::reverse_range (unsigned int start,
364			    unsigned int end)
365{
366  unsigned int i, j;
367
368  if (start == end - 1)
369    return;
370
371  for (i = start, j = end - 1; i < j; i++, j--) {
372    hb_glyph_info_t t;
373
374    t = info[i];
375    info[i] = info[j];
376    info[j] = t;
377  }
378
379  if (pos) {
380    for (i = start, j = end - 1; i < j; i++, j--) {
381      hb_glyph_position_t t;
382
383      t = pos[i];
384      pos[i] = pos[j];
385      pos[j] = t;
386    }
387  }
388}
389
390void
391hb_buffer_t::reverse (void)
392{
393  if (unlikely (!len))
394    return;
395
396  reverse_range (0, len);
397}
398
399void
400hb_buffer_t::reverse_clusters (void)
401{
402  unsigned int i, start, count, last_cluster;
403
404  if (unlikely (!len))
405    return;
406
407  reverse ();
408
409  count = len;
410  start = 0;
411  last_cluster = info[0].cluster;
412  for (i = 1; i < count; i++) {
413    if (last_cluster != info[i].cluster) {
414      reverse_range (start, i);
415      start = i;
416      last_cluster = info[i].cluster;
417    }
418  }
419  reverse_range (start, i);
420}
421
422void
423hb_buffer_t::merge_clusters (unsigned int start,
424			     unsigned int end)
425{
426  unsigned int cluster = this->info[start].cluster;
427
428  for (unsigned int i = start + 1; i < end; i++)
429    cluster = MIN (cluster, this->info[i].cluster);
430  for (unsigned int i = start; i < end; i++)
431    this->info[i].cluster = cluster;
432}
433void
434hb_buffer_t::merge_out_clusters (unsigned int start,
435				 unsigned int end)
436{
437  unsigned int cluster = this->out_info[start].cluster;
438
439  for (unsigned int i = start + 1; i < end; i++)
440    cluster = MIN (cluster, this->out_info[i].cluster);
441  for (unsigned int i = start; i < end; i++)
442    this->out_info[i].cluster = cluster;
443}
444
445void
446hb_buffer_t::guess_properties (void)
447{
448  /* If script is set to INVALID, guess from buffer contents */
449  if (props.script == HB_SCRIPT_INVALID) {
450    for (unsigned int i = 0; i < len; i++) {
451      hb_script_t script = hb_unicode_script (unicode, info[i].codepoint);
452      if (likely (script != HB_SCRIPT_COMMON &&
453		  script != HB_SCRIPT_INHERITED &&
454		  script != HB_SCRIPT_UNKNOWN)) {
455        props.script = script;
456        break;
457      }
458    }
459  }
460
461  /* If direction is set to INVALID, guess from script */
462  if (props.direction == HB_DIRECTION_INVALID) {
463    props.direction = hb_script_get_horizontal_direction (props.script);
464  }
465
466  /* If language is not set, use default language from locale */
467  if (props.language == HB_LANGUAGE_INVALID) {
468    /* TODO get_default_for_script? using $LANGUAGE */
469    props.language = hb_language_get_default ();
470  }
471}
472
473
474static inline void
475dump_var_allocation (const hb_buffer_t *buffer)
476{
477  char buf[80];
478  for (unsigned int i = 0; i < 8; i++)
479    buf[i] = '0' + buffer->allocated_var_bytes[7 - i];
480  buf[8] = '\0';
481  DEBUG_MSG (BUFFER, buffer,
482	     "Current var allocation: %s",
483	     buf);
484}
485
486void hb_buffer_t::allocate_var (unsigned int byte_i, unsigned int count, const char *owner)
487{
488  assert (byte_i < 8 && byte_i + count <= 8);
489
490  if (DEBUG (BUFFER))
491    dump_var_allocation (this);
492  DEBUG_MSG (BUFFER, this,
493	     "Allocating var bytes %d..%d for %s",
494	     byte_i, byte_i + count - 1, owner);
495
496  for (unsigned int i = byte_i; i < byte_i + count; i++) {
497    assert (!allocated_var_bytes[i]);
498    allocated_var_bytes[i]++;
499    allocated_var_owner[i] = owner;
500  }
501}
502
503void hb_buffer_t::deallocate_var (unsigned int byte_i, unsigned int count, const char *owner)
504{
505  if (DEBUG (BUFFER))
506    dump_var_allocation (this);
507
508  DEBUG_MSG (BUFFER, this,
509	     "Deallocating var bytes %d..%d for %s",
510	     byte_i, byte_i + count - 1, owner);
511
512  assert (byte_i < 8 && byte_i + count <= 8);
513  for (unsigned int i = byte_i; i < byte_i + count; i++) {
514    assert (allocated_var_bytes[i]);
515    assert (0 == strcmp (allocated_var_owner[i], owner));
516    allocated_var_bytes[i]--;
517  }
518}
519
520void hb_buffer_t::deallocate_var_all (void)
521{
522  memset (allocated_var_bytes, 0, sizeof (allocated_var_bytes));
523  memset (allocated_var_owner, 0, sizeof (allocated_var_owner));
524}
525
526/* Public API */
527
528hb_buffer_t *
529hb_buffer_create ()
530{
531  hb_buffer_t *buffer;
532
533  if (!(buffer = hb_object_create<hb_buffer_t> ()))
534    return hb_buffer_get_empty ();
535
536  buffer->reset ();
537
538  return buffer;
539}
540
541hb_buffer_t *
542hb_buffer_get_empty (void)
543{
544  static const hb_buffer_t _hb_buffer_nil = {
545    HB_OBJECT_HEADER_STATIC,
546
547    _HB_BUFFER_UNICODE_FUNCS_DEFAULT,
548    _HB_BUFFER_PROPS_DEFAULT,
549
550    TRUE, /* in_error */
551    TRUE, /* have_output */
552    TRUE  /* have_positions */
553  };
554
555  return const_cast<hb_buffer_t *> (&_hb_buffer_nil);
556}
557
558hb_buffer_t *
559hb_buffer_reference (hb_buffer_t *buffer)
560{
561  return hb_object_reference (buffer);
562}
563
564void
565hb_buffer_destroy (hb_buffer_t *buffer)
566{
567  if (!hb_object_destroy (buffer)) return;
568
569  hb_unicode_funcs_destroy (buffer->unicode);
570
571  free (buffer->info);
572  free (buffer->pos);
573
574  free (buffer);
575}
576
577hb_bool_t
578hb_buffer_set_user_data (hb_buffer_t        *buffer,
579			 hb_user_data_key_t *key,
580			 void *              data,
581			 hb_destroy_func_t   destroy,
582			 hb_bool_t           replace)
583{
584  return hb_object_set_user_data (buffer, key, data, destroy, replace);
585}
586
587void *
588hb_buffer_get_user_data (hb_buffer_t        *buffer,
589			 hb_user_data_key_t *key)
590{
591  return hb_object_get_user_data (buffer, key);
592}
593
594
595void
596hb_buffer_set_unicode_funcs (hb_buffer_t        *buffer,
597			     hb_unicode_funcs_t *unicode)
598{
599  if (unlikely (hb_object_is_inert (buffer)))
600    return;
601
602  if (!unicode)
603    unicode = _HB_BUFFER_UNICODE_FUNCS_DEFAULT;
604
605  hb_unicode_funcs_reference (unicode);
606  hb_unicode_funcs_destroy (buffer->unicode);
607  buffer->unicode = unicode;
608}
609
610hb_unicode_funcs_t *
611hb_buffer_get_unicode_funcs (hb_buffer_t        *buffer)
612{
613  return buffer->unicode;
614}
615
616void
617hb_buffer_set_direction (hb_buffer_t    *buffer,
618			 hb_direction_t  direction)
619
620{
621  if (unlikely (hb_object_is_inert (buffer)))
622    return;
623
624  buffer->props.direction = direction;
625}
626
627hb_direction_t
628hb_buffer_get_direction (hb_buffer_t    *buffer)
629{
630  return buffer->props.direction;
631}
632
633void
634hb_buffer_set_script (hb_buffer_t *buffer,
635		      hb_script_t  script)
636{
637  if (unlikely (hb_object_is_inert (buffer)))
638    return;
639
640  buffer->props.script = script;
641}
642
643hb_script_t
644hb_buffer_get_script (hb_buffer_t *buffer)
645{
646  return buffer->props.script;
647}
648
649void
650hb_buffer_set_language (hb_buffer_t   *buffer,
651			hb_language_t  language)
652{
653  if (unlikely (hb_object_is_inert (buffer)))
654    return;
655
656  buffer->props.language = language;
657}
658
659hb_language_t
660hb_buffer_get_language (hb_buffer_t *buffer)
661{
662  return buffer->props.language;
663}
664
665
666void
667hb_buffer_reset (hb_buffer_t *buffer)
668{
669  buffer->reset ();
670}
671
672hb_bool_t
673hb_buffer_pre_allocate (hb_buffer_t *buffer, unsigned int size)
674{
675  return buffer->ensure (size);
676}
677
678hb_bool_t
679hb_buffer_allocation_successful (hb_buffer_t  *buffer)
680{
681  return !buffer->in_error;
682}
683
684void
685hb_buffer_add (hb_buffer_t    *buffer,
686	       hb_codepoint_t  codepoint,
687	       hb_mask_t       mask,
688	       unsigned int    cluster)
689{
690  buffer->add (codepoint, mask, cluster);
691}
692
693hb_bool_t
694hb_buffer_set_length (hb_buffer_t  *buffer,
695		      unsigned int  length)
696{
697  if (unlikely (hb_object_is_inert (buffer)))
698    return length == 0;
699
700  if (!buffer->ensure (length))
701    return FALSE;
702
703  /* Wipe the new space */
704  if (length > buffer->len) {
705    memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len));
706    if (buffer->have_positions)
707      memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len));
708  }
709
710  buffer->len = length;
711  return TRUE;
712}
713
714unsigned int
715hb_buffer_get_length (hb_buffer_t *buffer)
716{
717  return buffer->len;
718}
719
720/* Return value valid as long as buffer not modified */
721hb_glyph_info_t *
722hb_buffer_get_glyph_infos (hb_buffer_t  *buffer,
723                           unsigned int *length)
724{
725  if (length)
726    *length = buffer->len;
727
728  return (hb_glyph_info_t *) buffer->info;
729}
730
731/* Return value valid as long as buffer not modified */
732hb_glyph_position_t *
733hb_buffer_get_glyph_positions (hb_buffer_t  *buffer,
734                               unsigned int *length)
735{
736  if (!buffer->have_positions)
737    buffer->clear_positions ();
738
739  if (length)
740    *length = buffer->len;
741
742  return (hb_glyph_position_t *) buffer->pos;
743}
744
745void
746hb_buffer_reverse (hb_buffer_t *buffer)
747{
748  buffer->reverse ();
749}
750
751void
752hb_buffer_reverse_clusters (hb_buffer_t *buffer)
753{
754  buffer->reverse_clusters ();
755}
756
757void
758hb_buffer_guess_properties (hb_buffer_t *buffer)
759{
760  buffer->guess_properties ();
761}
762
763#define ADD_UTF(T) \
764	HB_STMT_START { \
765	  if (text_length == -1) { \
766	    text_length = 0; \
767	    const T *p = (const T *) text; \
768	    while (*p) { \
769	      text_length++; \
770	      p++; \
771	    } \
772	  } \
773	  if (item_length == -1) \
774	    item_length = text_length - item_offset; \
775	  buffer->ensure (buffer->len + item_length * sizeof (T) / 4); \
776	  const T *next = (const T *) text + item_offset; \
777	  const T *end = next + item_length; \
778	  while (next < end) { \
779	    hb_codepoint_t u; \
780	    const T *old_next = next; \
781	    next = UTF_NEXT (next, end, u); \
782	    hb_buffer_add (buffer, u, 1,  old_next - (const T *) text); \
783	  } \
784	} HB_STMT_END
785
786
787#define UTF8_COMPUTE(Char, Mask, Len) \
788  if (Char < 128) { Len = 1; Mask = 0x7f; } \
789  else if ((Char & 0xe0) == 0xc0) { Len = 2; Mask = 0x1f; } \
790  else if ((Char & 0xf0) == 0xe0) { Len = 3; Mask = 0x0f; } \
791  else if ((Char & 0xf8) == 0xf0) { Len = 4; Mask = 0x07; } \
792  else Len = 0;
793
794static inline const uint8_t *
795hb_utf8_next (const uint8_t *text,
796	      const uint8_t *end,
797	      hb_codepoint_t *unicode)
798{
799  uint8_t c = *text;
800  unsigned int mask, len;
801
802  /* TODO check for overlong sequences? */
803
804  UTF8_COMPUTE (c, mask, len);
805  if (unlikely (!len || (unsigned int) (end - text) < len)) {
806    *unicode = -1;
807    return text + 1;
808  } else {
809    hb_codepoint_t result;
810    unsigned int i;
811    result = c & mask;
812    for (i = 1; i < len; i++)
813      {
814	if (unlikely ((text[i] & 0xc0) != 0x80))
815	  {
816	    *unicode = -1;
817	    return text + 1;
818	  }
819	result <<= 6;
820	result |= (text[i] & 0x3f);
821      }
822    *unicode = result;
823    return text + len;
824  }
825}
826
827void
828hb_buffer_add_utf8 (hb_buffer_t  *buffer,
829		    const char   *text,
830		    int           text_length,
831		    unsigned int  item_offset,
832		    int           item_length)
833{
834#define UTF_NEXT(S, E, U)	hb_utf8_next (S, E, &(U))
835  ADD_UTF (uint8_t);
836#undef UTF_NEXT
837}
838
839static inline const uint16_t *
840hb_utf16_next (const uint16_t *text,
841	       const uint16_t *end,
842	       hb_codepoint_t *unicode)
843{
844  uint16_t c = *text++;
845
846  if (unlikely (c >= 0xd800 && c < 0xdc00)) {
847    /* high surrogate */
848    uint16_t l;
849    if (text < end && ((l = *text), likely (l >= 0xdc00 && l < 0xe000))) {
850      /* low surrogate */
851      *unicode = ((hb_codepoint_t) ((c) - 0xd800) * 0x400 + (l) - 0xdc00 + 0x10000);
852       text++;
853    } else
854      *unicode = -1;
855  } else
856    *unicode = c;
857
858  return text;
859}
860
861void
862hb_buffer_add_utf16 (hb_buffer_t    *buffer,
863		     const uint16_t *text,
864		     int             text_length,
865		     unsigned int    item_offset,
866		     int            item_length)
867{
868#define UTF_NEXT(S, E, U)	hb_utf16_next (S, E, &(U))
869  ADD_UTF (uint16_t);
870#undef UTF_NEXT
871}
872
873void
874hb_buffer_add_utf32 (hb_buffer_t    *buffer,
875		     const uint32_t *text,
876		     int             text_length,
877		     unsigned int    item_offset,
878		     int             item_length)
879{
880#define UTF_NEXT(S, E, U)	((U) = *(S), (S)+1)
881  ADD_UTF (uint32_t);
882#undef UTF_NEXT
883}
884
885
886