/src/fribidi/lib/fribidi-run.c
Line | Count | Source |
1 | | /* FriBidi |
2 | | * fribidi-run.c - text run data type |
3 | | * |
4 | | * Authors: |
5 | | * Behdad Esfahbod, 2001, 2002, 2004 |
6 | | * Dov Grobgeld, 1999, 2000, 2017 |
7 | | * |
8 | | * Copyright (C) 2004 Sharif FarsiWeb, Inc |
9 | | * Copyright (C) 2001,2002 Behdad Esfahbod |
10 | | * Copyright (C) 1999,2000,2017 Dov Grobgeld |
11 | | * |
12 | | * This library is free software; you can redistribute it and/or |
13 | | * modify it under the terms of the GNU Lesser General Public |
14 | | * License as published by the Free Software Foundation; either |
15 | | * version 2.1 of the License, or (at your option) any later version. |
16 | | * |
17 | | * This library is distributed in the hope that it will be useful, |
18 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
19 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
20 | | * Lesser General Public License for more details. |
21 | | * |
22 | | * You should have received a copy of the GNU Lesser General Public License |
23 | | * along with this library, in a file named COPYING; if not, write to the |
24 | | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
25 | | * Boston, MA 02110-1301, USA |
26 | | * |
27 | | * For licensing issues, contact <fribidi.license@gmail.com>. |
28 | | */ |
29 | | |
30 | | #include "common.h" |
31 | | |
32 | | #include <limits.h> |
33 | | #include <stdint.h> |
34 | | |
35 | | #include <fribidi-bidi-types.h> |
36 | | |
37 | | #include "run.h" |
38 | | #include "bidi-types.h" |
39 | | |
40 | | /* size, in FriBidiRun elements, of the first chunk allocated by a pool; |
41 | | subsequent chunks (needed only if the initial size_hint underestimates |
42 | | the eventual run count) double in size each time. */ |
43 | 3.09k | #define FRIBIDI_RUN_POOL_MIN_CHUNK 16 |
44 | | |
45 | | #define FRIBIDI_RUN_POOL_CHUNK_RUNS(chunk) \ |
46 | 8.88M | ((FriBidiRun *) (void *) ((chunk) + 1)) |
47 | | |
48 | | static FriBidiRunPoolChunk * |
49 | | run_pool_chunk_new ( |
50 | | FriBidiStrIndex capacity |
51 | | ) |
52 | 2.37k | { |
53 | 2.37k | FriBidiRunPoolChunk *chunk; |
54 | | |
55 | 2.37k | if (capacity < FRIBIDI_RUN_POOL_MIN_CHUNK) |
56 | 717 | capacity = FRIBIDI_RUN_POOL_MIN_CHUNK; |
57 | | |
58 | | /* Refuse sizes whose byte count is not representable in a size_t |
59 | | (possible on 32-bit targets), rather than allocating a wrapped, |
60 | | too-small block while still recording the large capacity. */ |
61 | 2.37k | if UNLIKELY |
62 | 2.37k | ((size_t) capacity > |
63 | 2.37k | (SIZE_MAX - sizeof (FriBidiRunPoolChunk)) / sizeof (FriBidiRun)) |
64 | 0 | return NULL; |
65 | | |
66 | 2.37k | chunk = fribidi_malloc (sizeof (FriBidiRunPoolChunk) + |
67 | 2.37k | (size_t) capacity * sizeof (FriBidiRun)); |
68 | 2.37k | if LIKELY |
69 | 2.37k | (chunk) |
70 | 2.37k | { |
71 | 2.37k | chunk->next = NULL; |
72 | 2.37k | chunk->used = 0; |
73 | 2.37k | chunk->capacity = capacity; |
74 | 2.37k | } |
75 | 2.37k | return chunk; |
76 | 2.37k | } |
77 | | |
78 | | FriBidiRunPool * |
79 | | fribidi_run_pool_new ( |
80 | | FriBidiStrIndex size_hint |
81 | | ) |
82 | 1.71k | { |
83 | 1.71k | FriBidiRunPool *pool = fribidi_malloc (sizeof (FriBidiRunPool)); |
84 | | |
85 | 1.71k | if UNLIKELY |
86 | 1.71k | (!pool) return NULL; |
87 | | |
88 | | /* Every FriBidiRun allocation made while resolving one paragraph's |
89 | | embedding levels is, in the worst case, bounded by a small multiple |
90 | | of the paragraph length (each run always covers at least one |
91 | | character, and later stages only ever subdivide or merge existing |
92 | | runs). Sizing the first chunk to fit the whole paragraph means a |
93 | | single allocation almost always suffices. */ |
94 | 1.71k | pool->chunk = run_pool_chunk_new (size_hint); |
95 | 1.71k | if UNLIKELY |
96 | 1.71k | (!pool->chunk) |
97 | 0 | { |
98 | 0 | fribidi_free (pool); |
99 | 0 | return NULL; |
100 | 0 | } |
101 | | |
102 | 1.71k | return pool; |
103 | 1.71k | } |
104 | | |
105 | | void |
106 | | fribidi_run_pool_free ( |
107 | | FriBidiRunPool *pool |
108 | | ) |
109 | 1.71k | { |
110 | 1.71k | FriBidiRunPoolChunk *chunk; |
111 | | |
112 | 1.71k | if (!pool) |
113 | 1 | return; |
114 | | |
115 | 1.71k | chunk = pool->chunk; |
116 | 4.09k | while (chunk) |
117 | 2.37k | { |
118 | 2.37k | FriBidiRunPoolChunk *next = chunk->next; |
119 | 2.37k | fribidi_free (chunk); |
120 | 2.37k | chunk = next; |
121 | 2.37k | } |
122 | 1.71k | fribidi_free (pool); |
123 | 1.71k | } |
124 | | |
125 | | static FriBidiRun * |
126 | | run_pool_alloc ( |
127 | | FriBidiRunPool *pool |
128 | | ) |
129 | 8.88M | { |
130 | 8.88M | FriBidiRunPoolChunk *chunk = pool->chunk; |
131 | | |
132 | 8.88M | if UNLIKELY |
133 | 8.88M | (chunk->used == chunk->capacity) |
134 | 662 | { |
135 | | /* The size_hint under-estimated the run count (rare); grow. */ |
136 | 662 | FriBidiRunPoolChunk *new_chunk; |
137 | | |
138 | | /* Doubling would overflow FriBidiStrIndex. */ |
139 | 662 | if UNLIKELY |
140 | 662 | (chunk->capacity > INT_MAX / 2) return NULL; |
141 | | |
142 | 662 | new_chunk = run_pool_chunk_new (chunk->capacity * 2); |
143 | 662 | if UNLIKELY |
144 | 662 | (!new_chunk) return NULL; |
145 | 662 | new_chunk->next = chunk; |
146 | 662 | pool->chunk = new_chunk; |
147 | 662 | chunk = new_chunk; |
148 | 662 | } |
149 | | |
150 | 8.88M | return FRIBIDI_RUN_POOL_CHUNK_RUNS (chunk) + chunk->used++; |
151 | 8.88M | } |
152 | | |
153 | | FriBidiRun * |
154 | | new_run ( |
155 | | FriBidiRunPool *pool |
156 | | ) |
157 | 8.88M | { |
158 | 8.88M | register FriBidiRun *run; |
159 | | |
160 | 8.88M | run = run_pool_alloc (pool); |
161 | | |
162 | 8.88M | if LIKELY |
163 | 8.88M | (run) |
164 | 8.88M | { |
165 | 8.88M | run->len = run->pos = run->level = run->isolate_level = 0; |
166 | 8.88M | run->next = run->prev = run->prev_isolate = run->next_isolate = NULL; |
167 | 8.88M | run->fsi_base_level = 0; |
168 | 8.88M | } |
169 | 8.88M | return run; |
170 | 8.88M | } |
171 | | |
172 | | FriBidiRun * |
173 | | new_run_list ( |
174 | | FriBidiRunPool *pool |
175 | | ) |
176 | 5.14k | { |
177 | 5.14k | register FriBidiRun *run; |
178 | | |
179 | 5.14k | run = new_run (pool); |
180 | | |
181 | 5.14k | if LIKELY |
182 | 5.14k | (run) |
183 | 5.14k | { |
184 | 5.14k | run->type = FRIBIDI_TYPE_SENTINEL; |
185 | 5.14k | run->level = FRIBIDI_SENTINEL; |
186 | 5.14k | run->pos = FRIBIDI_SENTINEL; |
187 | 5.14k | run->len = FRIBIDI_SENTINEL; |
188 | 5.14k | run->next = run->prev = run; |
189 | 5.14k | } |
190 | | |
191 | 5.14k | return run; |
192 | 5.14k | } |
193 | | |
194 | | void |
195 | | free_run_list ( |
196 | | FriBidiRun *run_list |
197 | | ) |
198 | 2.47k | { |
199 | | /* Runs are owned by a FriBidiRunPool and are all released together via |
200 | | fribidi_run_pool_free() once the paragraph they belong to has been |
201 | | fully processed; there is nothing to do per-list here. This function |
202 | | is kept (as a no-op) so existing call sites don't need to change. */ |
203 | 2.47k | if (!run_list) |
204 | 0 | return; |
205 | | |
206 | 2.47k | fribidi_validate_run_list (run_list); |
207 | 2.47k | } |
208 | | |
209 | | |
210 | | FriBidiRun * |
211 | | run_list_encode_bidi_types ( |
212 | | /* input */ |
213 | | const FriBidiCharType *bidi_types, |
214 | | const FriBidiBracketType *bracket_types, |
215 | | const FriBidiStrIndex len, |
216 | | FriBidiRunPool *pool, |
217 | | /* output */ |
218 | | fribidi_boolean *has_isolate |
219 | | ) |
220 | 1.71k | { |
221 | 1.71k | FriBidiRun *list, *last; |
222 | 1.71k | register FriBidiRun *run = NULL; |
223 | 1.71k | FriBidiStrIndex i; |
224 | | |
225 | 1.71k | fribidi_assert (bidi_types); |
226 | | |
227 | 1.71k | *has_isolate = false; |
228 | | |
229 | | /* Create the list sentinel */ |
230 | 1.71k | list = new_run_list (pool); |
231 | 1.71k | if UNLIKELY |
232 | 1.71k | (!list) return NULL; |
233 | 1.71k | last = list; |
234 | | |
235 | | /* Scan over the character types */ |
236 | 19.2M | for (i = 0; i < len; i++) |
237 | 19.2M | { |
238 | 19.2M | register FriBidiCharType char_type = bidi_types[i]; |
239 | 19.2M | register FriBidiBracketType bracket_type = FRIBIDI_NO_BRACKET; |
240 | 19.2M | register fribidi_boolean is_isolate = FRIBIDI_IS_ISOLATE (char_type); |
241 | 19.2M | if (bracket_types) |
242 | 19.2M | bracket_type = bracket_types[i]; |
243 | 19.2M | if UNLIKELY |
244 | 19.2M | (is_isolate) *has_isolate = true; |
245 | | |
246 | 19.2M | if (char_type != last->type |
247 | 15.2M | || bracket_type != FRIBIDI_NO_BRACKET /* Always separate bracket into single char runs! */ |
248 | 13.3M | || last->bracket_type != FRIBIDI_NO_BRACKET |
249 | 13.3M | || is_isolate |
250 | 19.2M | ) |
251 | 7.27M | { |
252 | 7.27M | run = new_run (pool); |
253 | 7.27M | if UNLIKELY |
254 | 7.27M | (!run) break; |
255 | 7.27M | run->type = char_type; |
256 | 7.27M | run->pos = i; |
257 | 7.27M | last->len = run->pos - last->pos; |
258 | 7.27M | last->next = run; |
259 | 7.27M | run->prev = last; |
260 | 7.27M | run->bracket_type = bracket_type; |
261 | 7.27M | last = run; |
262 | 7.27M | } |
263 | 19.2M | } |
264 | | |
265 | | /* Close the circle */ |
266 | 1.71k | last->len = len - last->pos; |
267 | 1.71k | last->next = list; |
268 | 1.71k | list->prev = last; |
269 | | |
270 | 1.71k | if UNLIKELY |
271 | 1.71k | (!run) |
272 | 0 | { |
273 | | /* Memory allocation failed */ |
274 | 0 | free_run_list (list); |
275 | 0 | return NULL; |
276 | 0 | } |
277 | | |
278 | 1.71k | fribidi_validate_run_list (list); |
279 | | |
280 | 1.71k | return list; |
281 | 1.71k | } |
282 | | |
283 | | /* override the run list 'base', with the runs in the list 'over', to |
284 | | reinsert the previously-removed explicit codes (at X9) from |
285 | | 'explicits_list' back into 'type_rl_list' for example. This is used at the |
286 | | end of I2 to restore the explicit marks, and also to reset the character |
287 | | types of characters at L1. |
288 | | |
289 | | it is assumed that the 'pos' of the first element in 'base' list is not |
290 | | more than the 'pos' of the first element of the 'over' list, and the |
291 | | 'pos' of the last element of the 'base' list is not less than the 'pos' |
292 | | of the last element of the 'over' list. these two conditions are always |
293 | | satisfied for the two usages mentioned above. |
294 | | |
295 | | Note: |
296 | | frees the over list. |
297 | | |
298 | | Todo: |
299 | | use some explanatory names instead of p, q, ... |
300 | | rewrite comment above to remove references to special usage. |
301 | | */ |
302 | | fribidi_boolean |
303 | | shadow_run_list ( |
304 | | /* input */ |
305 | | FriBidiRun *base, |
306 | | FriBidiRun *over, |
307 | | fribidi_boolean preserve_length, |
308 | | FriBidiRunPool *pool |
309 | | ) |
310 | 2.47k | { |
311 | 2.47k | register FriBidiRun *p = base, *q, *r, *t; |
312 | 2.47k | register FriBidiStrIndex pos = 0, pos2; |
313 | 2.47k | fribidi_boolean status = false; |
314 | | |
315 | 2.47k | fribidi_validate_run_list (base); |
316 | 2.47k | fribidi_validate_run_list (over); |
317 | | |
318 | 2.47k | for_run_list (q, over) |
319 | 1.46M | { |
320 | 1.46M | if UNLIKELY |
321 | 1.46M | (!q->len || q->pos < pos) continue; |
322 | 1.46M | pos = q->pos; |
323 | 6.32M | while (p->next->type != FRIBIDI_TYPE_SENTINEL && p->next->pos <= pos) |
324 | 4.86M | p = p->next; |
325 | | /* now p is the element that q must be inserted 'in'. */ |
326 | 1.46M | pos2 = pos + q->len; |
327 | 1.46M | r = p; |
328 | 1.49M | while (r->next->type != FRIBIDI_TYPE_SENTINEL && r->next->pos < pos2) |
329 | 35.9k | r = r->next; |
330 | 1.46M | if (preserve_length) |
331 | 1.25M | r->len += q->len; |
332 | | /* now r is the last element that q affects. */ |
333 | 1.46M | if LIKELY |
334 | 1.46M | (p == r) |
335 | 1.44M | { |
336 | | /* split p into at most 3 intervals, and insert q in the place of |
337 | | the second interval, set r to be the third part. */ |
338 | | /* third part needed? */ |
339 | 1.44M | if (p->pos + p->len > pos2) |
340 | 1.40M | { |
341 | 1.40M | r = new_run (pool); |
342 | 1.40M | if UNLIKELY |
343 | 1.40M | (!r) goto out; |
344 | 1.40M | p->next->prev = r; |
345 | 1.40M | r->next = p->next; |
346 | 1.40M | r->level = p->level; |
347 | 1.40M | r->isolate_level = p->isolate_level; |
348 | 1.40M | r->type = p->type; |
349 | 1.40M | r->len = p->pos + p->len - pos2; |
350 | 1.40M | r->pos = pos2; |
351 | 1.40M | } |
352 | 41.1k | else |
353 | 41.1k | r = r->next; |
354 | | |
355 | 1.44M | if LIKELY |
356 | 1.44M | (p->pos + p->len >= pos) |
357 | 1.44M | { |
358 | | /* first part needed? */ |
359 | 1.44M | if (p->pos < pos) |
360 | | /* cut the end of p. */ |
361 | 429k | p->len = pos - p->pos; |
362 | 1.01M | else |
363 | | /* p is pool-owned; it just becomes unreachable garbage |
364 | | within the pool instead of being freed individually. */ |
365 | 1.01M | p = p->prev; |
366 | 1.44M | } |
367 | 1.44M | } |
368 | 14.0k | else |
369 | 14.0k | { |
370 | 14.0k | if LIKELY |
371 | 14.0k | (p->pos + p->len >= pos) |
372 | 14.0k | { |
373 | | /* p needed? */ |
374 | 14.0k | if (p->pos < pos) |
375 | | /* cut the end of p. */ |
376 | 1.54k | p->len = pos - p->pos; |
377 | 12.5k | else |
378 | 12.5k | p = p->prev; |
379 | 14.0k | } |
380 | | |
381 | | /* r needed? */ |
382 | 14.0k | if (r->pos + r->len > pos2) |
383 | 2.99k | { |
384 | | /* cut the beginning of r. */ |
385 | 2.99k | r->len = r->pos + r->len - pos2; |
386 | 2.99k | r->pos = pos2; |
387 | 2.99k | } |
388 | 11.0k | else |
389 | 11.0k | r = r->next; |
390 | | |
391 | | /* the elements between p and r are simply dropped: they are |
392 | | pool-owned, so no individual free is needed (see comment |
393 | | above new_run_list()). */ |
394 | 14.0k | } |
395 | | /* before updating the next and prev runs to point to the inserted q, |
396 | | we must remember the next element of q in the 'over' list. |
397 | | */ |
398 | 1.46M | t = q; |
399 | 1.46M | q = q->prev; |
400 | 1.46M | delete_node (t); |
401 | 1.46M | p->next = t; |
402 | 1.46M | t->prev = p; |
403 | 1.46M | t->next = r; |
404 | 1.46M | r->prev = t; |
405 | 1.46M | } |
406 | 2.47k | status = true; |
407 | | |
408 | 2.47k | fribidi_validate_run_list (base); |
409 | | |
410 | 2.47k | out: |
411 | 2.47k | free_run_list (over); |
412 | | |
413 | 2.47k | return status; |
414 | 2.47k | } |
415 | | |
416 | | #ifdef DEBUG |
417 | | |
418 | | void |
419 | | fribidi_validate_run_list ( |
420 | | FriBidiRun *run_list /* input run list */ |
421 | | ) |
422 | 11.6k | { |
423 | 11.6k | register FriBidiRun *q; |
424 | | |
425 | 11.6k | fribidi_assert (run_list); |
426 | 11.6k | fribidi_assert (run_list->next); |
427 | 11.6k | fribidi_assert (run_list->next->prev == run_list); |
428 | 11.6k | fribidi_assert (run_list->type == FRIBIDI_TYPE_SENTINEL); |
429 | 11.6k | for_run_list (q, run_list) |
430 | 17.2M | { |
431 | 17.2M | fribidi_assert (q->next); |
432 | 17.2M | fribidi_assert (q->next->prev == q); |
433 | 17.2M | } |
434 | | fribidi_assert (q == run_list); |
435 | 11.6k | } |
436 | | |
437 | | #endif /* !DEBUG */ |
438 | | |
439 | | /* Editor directions: |
440 | | * vim:textwidth=78:tabstop=8:shiftwidth=2:autoindent:cindent |
441 | | */ |