add explicit freetype2 linking
[charfbuzz.git] / hb-buffer.c
blob24118c098a5412ec39226fe2a18b7e192dd902b2
1 /*
2 Port from c++ is protected by a GNU Lesser GPLv3
3 Copyright © 2013 Sylvain BERTRAND <sylvain.bertrand@gmail.com>
4 <sylware@legeek.net>
5 */
6 #include <string.h>
7 #include <stdlib.h>
8 #include <assert.h>
10 #include "hb.h"
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)
28 return buffer->len;
31 hb_glyph_info_t *hb_buffer_get_glyph_infos(hb_buffer_t * buffer,
32 unsigned *length)
34 if (length)
35 *length = buffer->len;
36 return buffer->info;
39 hb_glyph_position_t *hb_buffer_get_glyph_positions(hb_buffer_t * buffer,
40 unsigned *length)
42 if (!buffer->have_positions)
43 hb_buffer_clear_positions(buffer);
45 if (length)
46 *length = buffer->len;
47 return buffer->pos;
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)
54 return;
56 if (!unicode_funcs)
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)
66 unsigned i, j;
68 if (start == end - 1)
69 return;
71 for (i = start, j = end - 1; i < j; i++, j--) {
72 hb_glyph_info_t t;
74 t = buffer->info[i];
75 buffer->info[i] = buffer->info[j];
76 buffer->info[j] = t;
79 if (buffer->pos) {
80 for (i = start, j = end - 1; i < j; i++, j--) {
81 hb_glyph_position_t t;
83 t = buffer->pos[i];
84 buffer->pos[i] = buffer->pos[j];
85 buffer->pos[j] = t;
90 void hb_buffer_reverse(hb_buffer_t * buffer)
92 if (!buffer->len)
93 return;
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)
101 return;
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)
109 return;
111 buffer->have_output = FALSE;
112 buffer->have_positions = TRUE;
114 buffer->out_len = 0;
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,
128 FALSE, /*in_error */
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)
144 return;
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;
157 buffer->idx = 0;
158 buffer->len = 0;
159 buffer->out_len = 0;
160 buffer->out_info = buffer->info;
162 buffer->serial = 0;
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)
175 return;
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)
185 if (!buffer)
186 return;
187 if (hb_atomic_int32_get(&buffer->ref_cnt) == REF_CNT_INVALID_VAL)
188 return;
189 hb_atomic_int32_add(&buffer->ref_cnt, -1);
190 if (hb_atomic_int32_get(&buffer->ref_cnt) > 0)
191 return;
192 hb_atomic_int32_set(&buffer->ref_cnt, REF_CNT_INVALID_VAL);
194 hb_unicode_funcs_destroy(buffer->unicode);
196 free(buffer->info);
197 free(buffer->pos);
198 free(buffer);
201 hb_buffer_t *hb_buffer_create(void)
203 hb_buffer_t *buffer = calloc(1, sizeof(*buffer));
204 if (!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);
211 return 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)
217 return;
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
232 mostly in-place.
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)
249 return FALSE;
251 new_allocated = buffer->allocated;
252 new_pos = NULL;
253 new_info = NULL;
254 separate_out = buffer->out_info != buffer->info;
256 if (hb_unsigned_int_mul_overflows(size, sizeof(buffer->info[0])))
257 goto done;
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])))
265 goto done;
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]));
272 done:
273 if (!new_pos || !new_info)
274 buffer->in_error = TRUE;
276 if (new_pos)
277 buffer->pos = new_pos;
279 if (new_info)
280 buffer->info = new_info;
282 buffer->out_info = separate_out ? (hb_glyph_info_t *) buffer->pos
283 : buffer->info;
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))
305 return;
307 glyph = &buffer->info[buffer->len];
309 memset(glyph, 0, sizeof(*glyph));
310 glyph->codepoint = codepoint;
311 glyph->mask = 1;
312 glyph->cluster = cluster;
314 buffer->len++;
317 struct utf {
318 unsigned bytes_n;
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 = {
328 sizeof(uint8_t),
329 hb_utf_strlen_utf8,
330 hb_utf_ptr_offset_utf8,
331 hb_utf_prev_utf8,
332 hb_utf_diff_utf8,
333 hb_utf_next_utf8
336 /*XXX:should go in lib "global init"*/
337 static HB_UNUSED struct utf utf16 = {
338 sizeof(uint16_t),
339 hb_utf_strlen_utf16,
340 hb_utf_ptr_offset_utf16,
341 hb_utf_prev_utf16,
342 hb_utf_diff_utf16,
343 hb_utf_next_utf16
346 /*XXX:should go in lib "global init"*/
347 static HB_UNUSED struct utf utf32 = {
348 sizeof(uint32_t),
349 hb_utf_strlen_utf32,
350 hb_utf_ptr_offset_utf32,
351 hb_utf_prev_utf32,
352 hb_utf_diff_utf32,
353 hb_utf_next_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,
360 int item_length)
362 void *next;
363 void *end;
365 assert(buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE ||
366 (!buffer->len
367 && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID));
369 if (hb_atomic_int32_get(&buffer->ref_cnt) == REF_CNT_INVALID_VAL)
370 return;
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) {
387 void *prev;
388 void *start;
390 /*Add pre-context */
391 clear_context(buffer, 0);
392 prev = utf->ptr_offset(text, item_offset);
393 start = text;
394 while (start < prev
395 && buffer->context_len[0] < HB_BUFFER_CONTEXT_LENGTH) {
396 hb_codepoint_t u;
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);
404 while (next < end) {
405 hb_codepoint_t u;
406 void *old_next;
408 old_next = next;
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) {
417 hb_codepoint_t u;
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,
430 item_length);
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)
436 return;
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)
444 return;
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);