Coverage Report

Created: 2026-10-17 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
 */