2 Port from c++ is protected by a GNU Lesser GPLv3
3 Copyright © 2013 Sylvain BERTRAND <sylvain.bertrand@gmail.com>
11 #include "hb-private.h"
12 #include "hb-atomic-private.h"
13 #include "hb-buffer-private.h"
14 #include "hb-unicode-private.h"
15 #include "hb-utf-private.h"
17 hb_bool_t
hb_segment_properties_equal(const hb_segment_properties_t
* a
,
18 const hb_segment_properties_t
* b
)
20 return a
->direction
== b
->direction
&&
21 a
->script
== b
->script
&&
22 a
->language
== b
->language
&&
23 a
->reserved1
== b
->reserved1
&& a
->reserved2
== b
->reserved2
;
26 unsigned hb_buffer_get_length(hb_buffer_t
* buffer
)
31 hb_glyph_info_t
*hb_buffer_get_glyph_infos(hb_buffer_t
* buffer
,
35 *length
= buffer
->len
;
39 hb_glyph_position_t
*hb_buffer_get_glyph_positions(hb_buffer_t
* buffer
,
42 if (!buffer
->have_positions
)
43 hb_buffer_clear_positions(buffer
);
46 *length
= buffer
->len
;
50 void hb_buffer_set_unicode_funcs(hb_buffer_t
* buffer
,
51 hb_unicode_funcs_t
* unicode_funcs
)
53 if (hb_atomic_int32_get(&buffer
->ref_cnt
) == REF_CNT_INVALID_VAL
)
57 unicode_funcs
= hb_unicode_funcs_get_default();
59 hb_unicode_funcs_reference(unicode_funcs
);
60 hb_unicode_funcs_destroy(buffer
->unicode
);
61 buffer
->unicode
= unicode_funcs
;
64 void hb_buffer_reverse_range(hb_buffer_t
* buffer
, unsigned start
, unsigned end
)
71 for (i
= start
, j
= end
- 1; i
< j
; i
++, j
--) {
75 buffer
->info
[i
] = buffer
->info
[j
];
80 for (i
= start
, j
= end
- 1; i
< j
; i
++, j
--) {
81 hb_glyph_position_t t
;
84 buffer
->pos
[i
] = buffer
->pos
[j
];
90 void hb_buffer_reverse(hb_buffer_t
* buffer
)
95 hb_buffer_reverse_range(buffer
, 0, buffer
->len
);
98 void hb_buffer_set_direction(hb_buffer_t
* buffer
, hb_direction_t direction
)
100 if (hb_atomic_int32_get(&buffer
->ref_cnt
) == REF_CNT_INVALID_VAL
)
103 buffer
->props
.direction
= direction
;
106 void hb_buffer_clear_positions(hb_buffer_t
* buffer
)
108 if (hb_atomic_int32_get(&buffer
->ref_cnt
) == REF_CNT_INVALID_VAL
)
111 buffer
->have_output
= FALSE
;
112 buffer
->have_positions
= TRUE
;
115 buffer
->out_info
= buffer
->info
;
117 memset(buffer
->pos
, 0, sizeof(buffer
->pos
[0]) * buffer
->len
);
120 /*XXX:should go in lib "global init"*/
121 static hb_buffer_t hb_buffer_nil
= {
122 REF_CNT_INVALID_VAL
, /*ref_cnt */
123 &_hb_unicode_funcs_nil
, /*unicode */
124 HB_SEGMENT_PROPERTIES_DEFAULT
,
125 HB_BUFFER_FLAG_DEFAULT
,
127 HB_BUFFER_CONTENT_TYPE_INVALID
,
129 FALSE
, /*have_output */
130 FALSE
/*have_positions */
131 /*Zero is good enough for everything else. */
134 hb_buffer_t
*hb_buffer_get_empty(void)
136 return &hb_buffer_nil
;
139 void hb_buffer_clear(hb_buffer_t
* buffer
)
141 hb_segment_properties_t default_props
;
143 if (hb_atomic_int32_get(&buffer
->ref_cnt
) == REF_CNT_INVALID_VAL
)
146 /*XXX:default_props = HB_SEGMENT_PROPERTIES_DEFAULT;*/
147 HB_SEGMENT_PROPERTIES_DEFAULT_INIT(default_props
);
149 buffer
->props
= default_props
;
150 buffer
->flags
= HB_BUFFER_FLAG_DEFAULT
;
152 buffer
->content_type
= HB_BUFFER_CONTENT_TYPE_INVALID
;
153 buffer
->in_error
= FALSE
;
154 buffer
->have_output
= FALSE
;
155 buffer
->have_positions
= FALSE
;
160 buffer
->out_info
= buffer
->info
;
163 memset(buffer
->allocated_var_bytes
, 0,
164 sizeof(buffer
->allocated_var_bytes
));
165 memset(buffer
->allocated_var_owner
, 0,
166 sizeof(buffer
->allocated_var_owner
));
168 memset(buffer
->context
, 0, sizeof(buffer
->context
));
169 memset(buffer
->context_len
, 0, sizeof(buffer
->context_len
));
172 void hb_buffer_reset(hb_buffer_t
* buffer
)
174 if (hb_atomic_int32_get(&buffer
->ref_cnt
) == REF_CNT_INVALID_VAL
)
177 hb_unicode_funcs_destroy(buffer
->unicode
);
178 buffer
->unicode
= hb_unicode_funcs_get_default();
180 hb_buffer_clear(buffer
);
183 void hb_buffer_destroy(hb_buffer_t
* buffer
)
187 if (hb_atomic_int32_get(&buffer
->ref_cnt
) == REF_CNT_INVALID_VAL
)
189 hb_atomic_int32_add(&buffer
->ref_cnt
, -1);
190 if (hb_atomic_int32_get(&buffer
->ref_cnt
) > 0)
192 hb_atomic_int32_set(&buffer
->ref_cnt
, REF_CNT_INVALID_VAL
);
194 hb_unicode_funcs_destroy(buffer
->unicode
);
201 hb_buffer_t
*hb_buffer_create(void)
203 hb_buffer_t
*buffer
= calloc(1, sizeof(*buffer
));
205 return hb_buffer_get_empty();
207 hb_atomic_int32_set(&buffer
->ref_cnt
, 1);
208 buffer
->unicode
= hb_unicode_funcs_get_empty();
210 hb_buffer_reset(buffer
);
214 void hb_buffer_set_script(hb_buffer_t
* buffer
, hb_script_t script
)
216 if (hb_atomic_int32_get(&buffer
->ref_cnt
) == REF_CNT_INVALID_VAL
)
219 buffer
->props
.script
= script
;
223 Here is how the buffer works internally:
225 There are two info pointers: info and out_info. They always have
226 the same allocated size, but different lengths.
228 As an optimization, both info and out_info may point to the
229 same piece of memory, which is owned by info. This remains the
230 case as long as out_len doesn't exceed i at any time.
231 In that case, swap_buffers() is no-op and the glyph operations operate
234 As soon as out_info gets longer than info, out_info is moved over
235 to an alternate buffer (which we reuse the pos buffer for!), and its
236 current contents (out_len entries) are copied to the new place.
237 This should all remain transparent to the user. swap_buffers() then
238 switches info and out_info.
241 static hb_bool_t
enlarge(hb_buffer_t
* buffer
, unsigned int size
)
243 unsigned new_allocated
;
244 hb_glyph_position_t
*new_pos
;
245 hb_glyph_info_t
*new_info
;
246 hb_bool_t separate_out
;
248 if (buffer
->in_error
)
251 new_allocated
= buffer
->allocated
;
254 separate_out
= buffer
->out_info
!= buffer
->info
;
256 if (hb_unsigned_int_mul_overflows(size
, sizeof(buffer
->info
[0])))
259 while (size
>= new_allocated
)
260 new_allocated
+= (new_allocated
>> 1) + 32;
262 /*ASSERT_STATIC (sizeof (info[0]) == sizeof (pos[0])); */
263 if (hb_unsigned_int_mul_overflows
264 (new_allocated
, sizeof(buffer
->info
[0])))
267 new_pos
= (hb_glyph_position_t
*) realloc(buffer
->pos
, new_allocated
268 * sizeof(buffer
->pos
[0]));
269 new_info
= (hb_glyph_info_t
*) realloc(buffer
->info
, new_allocated
270 * sizeof(buffer
->info
[0]));
273 if (!new_pos
|| !new_info
)
274 buffer
->in_error
= TRUE
;
277 buffer
->pos
= new_pos
;
280 buffer
->info
= new_info
;
282 buffer
->out_info
= separate_out
? (hb_glyph_info_t
*) buffer
->pos
284 if (!buffer
->in_error
)
285 buffer
->allocated
= new_allocated
;
286 return !buffer
->in_error
;
289 static hb_bool_t
ensure(hb_buffer_t
* buffer
, unsigned int size
)
291 return (size
< buffer
->allocated
) ? TRUE
: enlarge(buffer
, size
);
294 static void clear_context(hb_buffer_t
* buffer
, unsigned int side
)
296 buffer
->context_len
[side
] = 0;
299 static void add(hb_buffer_t
* buffer
, hb_codepoint_t codepoint
,
300 unsigned int cluster
)
302 hb_glyph_info_t
*glyph
;
304 if (!ensure(buffer
, buffer
->len
+ 1))
307 glyph
= &buffer
->info
[buffer
->len
];
309 memset(glyph
, 0, sizeof(*glyph
));
310 glyph
->codepoint
= codepoint
;
312 glyph
->cluster
= cluster
;
319 unsigned (*len
) (void *text
);
320 void *(*ptr_offset
) (void *text
, unsigned offset
);
321 void *(*prev
) (void *text
, void *start
, hb_codepoint_t
* unicode
);
322 unsigned (*diff
) (void *a
, void *b
);
323 void *(*next
) (void *text
, void *end
, hb_codepoint_t
* unicode
);
326 /*XXX:should go in lib "global init"*/
327 static struct utf utf8
= {
330 hb_utf_ptr_offset_utf8
,
336 /*XXX:should go in lib "global init"*/
337 static HB_UNUSED
struct utf utf16
= {
340 hb_utf_ptr_offset_utf16
,
346 /*XXX:should go in lib "global init"*/
347 static HB_UNUSED
struct utf utf32
= {
350 hb_utf_ptr_offset_utf32
,
356 /*to unroll the original c++, could have used a macro
357 ASSEMBLY:maybe worth to be unrolled to fine tuned assembly*/
358 static void hb_buffer_add_utf(hb_buffer_t
* buffer
, struct utf
*utf
, void *text
,
359 int text_length
, unsigned item_offset
,
365 assert(buffer
->content_type
== HB_BUFFER_CONTENT_TYPE_UNICODE
||
367 && buffer
->content_type
== HB_BUFFER_CONTENT_TYPE_INVALID
));
369 if (hb_atomic_int32_get(&buffer
->ref_cnt
) == REF_CNT_INVALID_VAL
)
372 if (text_length
== -1)
373 text_length
= utf
->len(text
);
375 if (item_length
== -1)
376 item_length
= text_length
- item_offset
;
378 ensure(buffer
, buffer
->len
+ item_length
* utf
->bytes_n
/ 4);
380 /*If buffer is empty and pre-context provided, install it.
381 This check is written this way, to make sure people can
382 provide pre-context in one add_utf() call, then provide
383 text in a follow-up call. See:
385 https://bugzilla.mozilla.org/show_bug.cgi?id=801410#c13 */
386 if (!buffer
->len
&& item_offset
> 0) {
391 clear_context(buffer
, 0);
392 prev
= utf
->ptr_offset(text
, item_offset
);
395 && buffer
->context_len
[0] < HB_BUFFER_CONTEXT_LENGTH
) {
397 prev
= utf
->prev(prev
, start
, &u
);
398 buffer
->context
[0][buffer
->context_len
[0]++] = u
;
402 next
= utf
->ptr_offset(text
, item_offset
);
403 end
= utf
->ptr_offset(next
, item_length
);
409 next
= utf
->next(next
, end
, &u
);
410 add(buffer
, u
, utf
->diff(old_next
, text
));
413 /*Add post-context */
414 clear_context(buffer
, 1);
415 end
= utf
->ptr_offset(text
, text_length
);
416 while (next
< end
&& buffer
->context_len
[1] < HB_BUFFER_CONTEXT_LENGTH
) {
419 next
= utf
->next(next
, end
, &u
);
420 buffer
->context
[1][buffer
->context_len
[1]++] = u
;
423 buffer
->content_type
= HB_BUFFER_CONTENT_TYPE_UNICODE
;
426 void hb_buffer_add_utf8(hb_buffer_t
* buffer
, const char *text
, int text_length
,
427 unsigned int item_offset
, int item_length
)
429 hb_buffer_add_utf(buffer
, &utf8
, (void *)text
, text_length
, item_offset
,
433 void hb_buffer_set_flags(hb_buffer_t
* buffer
, hb_buffer_flags_t flags
)
435 if (hb_atomic_int32_get(&buffer
->ref_cnt
) == REF_CNT_INVALID_VAL
)
438 buffer
->flags
= flags
;
441 void hb_buffer_set_language(hb_buffer_t
* buffer
, hb_language_t language
)
443 if (hb_atomic_int32_get(&buffer
->ref_cnt
) == REF_CNT_INVALID_VAL
)
446 buffer
->props
.language
= language
;
449 hb_direction_t
hb_buffer_get_direction(hb_buffer_t
* buffer
)
451 return buffer
->props
.direction
;
454 void hb_buffer_add(hb_buffer_t
* buffer
, hb_codepoint_t codepoint
,
455 unsigned int cluster
)
457 add(buffer
, codepoint
, cluster
);
458 clear_context(buffer
, 1);