• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Tehreer / SheenBidi / 19308576644

12 Nov 2025 06:56PM UTC coverage: 95.907% (+16.4%) from 79.545%
19308576644

push

github

mta452
[lib] Fix memory management issues

13 of 13 new or added lines in 3 files covered. (100.0%)

29 existing lines in 10 files now uncovered.

3866 of 4031 relevant lines covered (95.91%)

462026.31 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.97
/Source/SBParagraph.c
1
/*
2
 * Copyright (C) 2014-2025 Muhammad Tayyab Akram
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17

18
#include <stddef.h>
19

20
#include <SheenBidi/SBConfig.h>
21

22
#include "BidiChain.h"
23
#include "BidiTypeLookup.h"
24
#include "IsolatingRun.h"
25
#include "LevelRun.h"
26
#include "Memory.h"
27
#include "Object.h"
28
#include "RunQueue.h"
29
#include "SBAlgorithm.h"
30
#include "SBAllocator.h"
31
#include "SBAssert.h"
32
#include "SBBase.h"
33
#include "SBCodepointSequence.h"
34
#include "SBLine.h"
35
#include "SBLog.h"
36
#include "StatusStack.h"
37
#include "SBParagraph.h"
38

39
typedef SBParagraph *SBMutableParagraphRef;
40

41
typedef struct _ParagraphContext {
42
    BidiChain bidiChain;
43
    StatusStack statusStack;
44
    RunQueue runQueue;
45
    IsolatingRun isolatingRun;
46
} ParagraphContext, *ParagraphContextRef;
47

48
static void PopulateBidiChain(BidiChainRef chain, const SBBidiType *types, SBUInteger length);
49
static SBBoolean ProcessRun(ParagraphContextRef context, const LevelRun *levelRun, SBBoolean resolveIsolatingRuns);
50
static void FinalizeParagraph(ObjectRef object);
51

52
#define BIDI_LINKS        0
53
#define BIDI_TYPES        1
54
#define BIDI_FLAGS        2
55
#define COUNT             3
56

57
static SBBoolean InitializeParagraphContext(ParagraphContextRef context, MemoryRef memory,
862,281✔
58
    const SBBidiType *types, SBLevel *levels, SBUInteger length)
59
{
60
    SBBoolean isInitialized = SBFalse;
862,281✔
61
    void *pointers[COUNT] = { NULL };
862,281✔
62
    SBUInteger sizes[COUNT];
862,281✔
63

64
    sizes[BIDI_LINKS] = sizeof(BidiLink) * (length + 2);
862,281✔
65
    sizes[BIDI_TYPES] = sizeof(SBBidiType) * (length + 2);
862,281✔
66
    sizes[BIDI_FLAGS] = sizeof(BidiFlag) * (length + 2);
862,281✔
67

68
    if (MemoryAllocateChunks(memory, MemoryTypeScratch, sizes, COUNT, pointers)) {
862,281✔
69
        BidiLink *fixedLinks = pointers[BIDI_LINKS];
862,281✔
70
        SBBidiType *fixedTypes = pointers[BIDI_TYPES];
862,281✔
71
        BidiFlag *fixedFlags = pointers[BIDI_FLAGS];
862,281✔
72

73
        BidiChainInitialize(&context->bidiChain, fixedTypes, levels, fixedFlags, fixedLinks);
862,281✔
74
        StatusStackInitialize(&context->statusStack, memory);
862,281✔
75
        RunQueueInitialize(&context->runQueue, memory);
862,281✔
76
        IsolatingRunInitialize(&context->isolatingRun, memory);
862,281✔
77

78
        PopulateBidiChain(&context->bidiChain, types, length);
862,281✔
79

80
        isInitialized = SBTrue;
862,281✔
81
    }
82

83
    return isInitialized;
862,281✔
84
}
85

86
#undef BIDI_LINKS
87
#undef BIDI_TYPES
88
#undef BIDI_FLAGS
89
#undef COUNT
90

91
#define PARAGRAPH 0
92
#define LEVELS    1
93
#define COUNT     2
94

95
static SBMutableParagraphRef AllocateParagraph(SBUInteger length)
862,281✔
96
{
97
    void *pointers[COUNT] = { NULL };
862,281✔
98
    SBUInteger sizes[COUNT] = { 0 };
862,281✔
99
    SBMutableParagraphRef paragraph;
100

101
    sizes[PARAGRAPH] = sizeof(SBParagraph);
862,281✔
102
    sizes[LEVELS]    = sizeof(SBLevel) * (length + 2);
862,281✔
103

104
    paragraph = ObjectCreate(sizes, COUNT, pointers, FinalizeParagraph);
862,281✔
105

106
    if (paragraph) {
862,281✔
107
        paragraph->fixedLevels = pointers[LEVELS];
862,281✔
108
    }
109

110
    return paragraph;
862,281✔
111
}
112

113
#undef PARAGRAPH
114
#undef LEVELS
115
#undef COUNT
116

117
static void FinalizeParagraph(ObjectRef object)
862,281✔
118
{
119
    SBParagraphRef paragraph = object;
862,281✔
120
    SBAlgorithmRef algorithm;
121

122
    algorithm = paragraph->_algorithm;
862,281✔
123

124
    if (algorithm) {
862,281✔
125
        SBAlgorithmRelease(algorithm);
861,959✔
126
    }
127
}
862,281✔
128

129
static SBUInteger DetermineBoundary(const SBCodepointSequence *codepointSequence,
862,281✔
130
    const SBBidiType *bidiTypes, SBUInteger paragraphOffset, SBUInteger suggestedLength)
131
{
132
    SBUInteger suggestedLimit = paragraphOffset + suggestedLength;
862,281✔
133
    SBUInteger stringIndex;
134

135
    for (stringIndex = paragraphOffset; stringIndex < suggestedLimit; stringIndex++) {
4,601,235✔
136
        if (bidiTypes[stringIndex] == SBBidiTypeB) {
3,772,580✔
137
            stringIndex += SBCodepointSequenceGetSeparatorLength(codepointSequence, stringIndex);
33,626✔
138
            goto Return;
33,626✔
139
        }
140
    }
141

142
Return:
828,655✔
143
    return (stringIndex - paragraphOffset);
862,281✔
144
}
145

146
static void PopulateBidiChain(BidiChainRef chain, const SBBidiType *types, SBUInteger length)
862,281✔
147
{
148
    SBBidiType type = SBBidiTypeNil;
862,281✔
149
    SBUInteger priorIndex = SBInvalidIndex;
862,281✔
150
    SBUInteger index;
151

152
    for (index = 0; index < length; index++) {
4,601,235✔
153
        SBBidiType priorType = type;
3,772,580✔
154
        type = types[index];
3,772,580✔
155

156
        switch (type) {
3,772,580✔
157
        case SBBidiTypeB:
1,899,051✔
158
        case SBBidiTypeON:
159
        case SBBidiTypeLRE:
160
        case SBBidiTypeRLE:
161
        case SBBidiTypeLRO:
162
        case SBBidiTypeRLO:
163
        case SBBidiTypePDF:
164
        case SBBidiTypeLRI:
165
        case SBBidiTypeRLI:
166
        case SBBidiTypeFSI:
167
        case SBBidiTypePDI:
168
            BidiChainAdd(chain, type, index - priorIndex);
1,899,051✔
169
            priorIndex = index;
1,899,051✔
170

171
            if (type == SBBidiTypeB) {
1,899,051✔
172
                index = length;
33,626✔
173
                goto AddLast;
33,626✔
174
            }
175
            break;
1,865,425✔
176

177
        default:
1,873,529✔
178
            if (type != priorType) {
1,873,529✔
179
                BidiChainAdd(chain, type, index - priorIndex);
1,812,815✔
180
                priorIndex = index;
1,812,815✔
181
            }
182
            break;
1,873,529✔
183
        }
184
    }
185

186
AddLast:
828,655✔
187
    BidiChainAdd(chain, SBBidiTypeNil, index - priorIndex);
862,281✔
188
}
862,281✔
189

190
static BidiLink SkipIsolatingRun(BidiChainRef chain, BidiLink skipLink, BidiLink breakLink)
114,000✔
191
{
192
    BidiLink link = skipLink;
114,000✔
193
    SBUInteger depth = 1;
114,000✔
194

195
    while ((link = BidiChainGetNext(chain, link)) != breakLink) {
393,217✔
196
        SBBidiType type = BidiChainGetType(chain, link);
286,636✔
197

198
        switch (type) {
286,636✔
199
        case SBBidiTypeLRI:
24,335✔
200
        case SBBidiTypeRLI:
201
        case SBBidiTypeFSI:
202
            depth += 1;
24,335✔
203
            break;
24,335✔
204

205
        case SBBidiTypePDI:
8,165✔
206
            if (--depth == 0) {
8,165✔
207
                return link;
7,419✔
208
            }
209
            break;
746✔
210
        }
211
    }
212

213
    return BidiLinkNone;
106,581✔
214
}
215

216
static SBLevel DetermineBaseLevel(BidiChainRef chain, BidiLink skipLink, BidiLink breakLink, SBLevel defaultLevel, SBBoolean isIsolate)
393,932✔
217
{
218
    BidiLink link = skipLink;
393,932✔
219

220
    /* Rules P2, P3 */
221
    while ((link = BidiChainGetNext(chain, link)) != breakLink) {
1,163,465✔
222
        SBBidiType type = BidiChainGetType(chain, link);
998,012✔
223

224
        switch (type) {
998,012✔
225
        case SBBidiTypeL:
38,391✔
226
            return 0;
38,391✔
227

228
        case SBBidiTypeAL:
76,111✔
229
        case SBBidiTypeR:
230
            return 1;
76,111✔
231

232
        case SBBidiTypeLRI:
114,000✔
233
        case SBBidiTypeRLI:
234
        case SBBidiTypeFSI:
235
            link = SkipIsolatingRun(chain, link, breakLink);
114,000✔
236
            if (link == BidiLinkNone) {
114,000✔
237
                goto Default;
106,581✔
238
            }
239
            break;
7,419✔
240

241
        case SBBidiTypePDI:
38,002✔
242
            if (isIsolate) {
38,002✔
243
                /*
244
                 * In case of isolating run, the PDI will be the last code point.
245
                 * NOTE:
246
                 *      The inner isolating runs will be skipped by the case above this one.
247
                 */
248
                goto Default;
7,396✔
249
            }
250
            break;
30,606✔
251
        }
252
    }
253

254
Default:
165,453✔
255
    return defaultLevel;
279,430✔
256
}
257

258
static SBLevel DetermineParagraphLevel(BidiChainRef chain, SBLevel baseLevel)
862,281✔
259
{
260
    if (baseLevel >= SBLevelMax) {
862,281✔
261
        return DetermineBaseLevel(chain, chain->roller, chain->roller,
257,078✔
262
                                  (baseLevel != SBLevelDefaultRTL ? 0 : 1),
263
                                  SBFalse);
264
    }
265

266
    return baseLevel;
605,203✔
267
}
268

269
static SBBoolean DetermineLevels(ParagraphContextRef context, SBLevel baseLevel)
862,281✔
270
{
271
    BidiChainRef chain = &context->bidiChain;
862,281✔
272
    StatusStackRef stack = &context->statusStack;
862,281✔
273
    BidiLink roller = chain->roller;
862,281✔
274
    BidiLink link;
275

276
    BidiLink priorLink;
277
    BidiLink firstLink;
278
    BidiLink lastLink;
279

280
    SBLevel priorLevel;
281
    SBBidiType sor;
282
    SBBidiType eor;
283

284
    SBUInteger overIsolate;
285
    SBUInteger overEmbedding;
286
    SBUInteger validIsolate;
287

288
    priorLink = chain->roller;
862,281✔
289
    firstLink = BidiLinkNone;
862,281✔
290
    lastLink = BidiLinkNone;
862,281✔
291

292
    priorLevel = baseLevel;
862,281✔
293
    sor = SBBidiTypeNil;
862,281✔
294

295
    /* Rule X1 */
296
    overIsolate = 0;
862,281✔
297
    overEmbedding = 0;
862,281✔
298
    validIsolate = 0;
862,281✔
299

300
    StatusStackPush(stack, baseLevel, SBBidiTypeON, SBFalse);
862,281✔
301

302
    BidiChainForEach(chain, roller, link) {
5,436,428✔
303
        SBBoolean forceFinish = SBFalse;
4,574,147✔
304
        SBBoolean bnEquivalent = SBFalse;
4,574,147✔
305
        SBBidiType type;
306

307
        type = BidiChainGetType(chain, link);
4,574,147✔
308

309
#define LeastGreaterOddLevel()                                              \
310
(                                                                           \
311
        (StatusStackGetEmbeddingLevel(stack) + 1) | 1                       \
312
)
313

314
#define LeastGreaterEvenLevel()                                             \
315
(                                                                           \
316
        (StatusStackGetEmbeddingLevel(stack) + 2) & ~1                      \
317
)
318

319
#define MergeLinkIfNeeded()                                                 \
320
{                                                                           \
321
        if (BidiChainMergeNext(chain, priorLink)) {                         \
322
            continue;                                                       \
323
        }                                                                   \
324
}
325

326
#define PushEmbedding(l, o)                                                 \
327
{                                                                           \
328
        SBLevel newLevel = l;                                               \
329
                                                                            \
330
        bnEquivalent = SBTrue;                                              \
331
                                                                            \
332
        if (newLevel <= SBLevelMax && !overIsolate && !overEmbedding) {     \
333
            if (!StatusStackPush(stack, newLevel, o, SBFalse)) {            \
334
                return SBFalse;                                             \
335
            }                                                               \
336
        } else {                                                            \
337
            if (!overIsolate) {                                             \
338
                overEmbedding += 1;                                         \
339
            }                                                               \
340
        }                                                                   \
341
}
342

343
#define PushIsolate(l, o)                                                   \
344
{                                                                           \
345
        SBBidiType priorStatus = StatusStackGetOverrideStatus(stack);       \
346
        SBLevel newLevel = l;                                               \
347
                                                                            \
348
        BidiChainSetLevel(chain, link,                                      \
349
                          StatusStackGetEmbeddingLevel(stack));             \
350
                                                                            \
351
        if (newLevel <= SBLevelMax && !overIsolate && !overEmbedding) {     \
352
            validIsolate += 1;                                              \
353
                                                                            \
354
            if (!StatusStackPush(stack, newLevel, o, SBTrue)) {             \
355
                return SBFalse;                                             \
356
            }                                                               \
357
        } else {                                                            \
358
            overIsolate += 1;                                               \
359
        }                                                                   \
360
                                                                            \
361
        if (priorStatus != SBBidiTypeON) {                                  \
362
            BidiChainSetType(chain, link, priorStatus);                     \
363
            MergeLinkIfNeeded();                                            \
364
        }                                                                   \
365
}
366

367
        switch (type) {
4,574,147✔
368
        /* Rule X2 */
369
        case SBBidiTypeRLE:
136,944✔
370
            PushEmbedding(LeastGreaterOddLevel(), SBBidiTypeON);
136,944✔
371
            break;
136,944✔
372

373
        /* Rule X3 */
374
        case SBBidiTypeLRE:
138,300✔
375
            PushEmbedding(LeastGreaterEvenLevel(), SBBidiTypeON);
138,300✔
376
            break;
138,300✔
377

378
        /* Rule X4 */
379
        case SBBidiTypeRLO:
136,946✔
380
            PushEmbedding(LeastGreaterOddLevel(), SBBidiTypeR);
136,946✔
381
            break;
136,946✔
382

383
        /* Rule X5 */
384
        case SBBidiTypeLRO:
137,222✔
385
            PushEmbedding(LeastGreaterEvenLevel(), SBBidiTypeL);
137,222✔
386
            break;
137,222✔
387

388
        /* Rule X5a */
389
        case SBBidiTypeRLI:
136,981✔
390
            PushIsolate(LeastGreaterOddLevel(), SBBidiTypeON);
136,981✔
391
            break;
132,214✔
392

393
        /* Rule X5b */
394
        case SBBidiTypeLRI:
136,998✔
395
            PushIsolate(LeastGreaterEvenLevel(), SBBidiTypeON);
136,998✔
396
            break;
132,228✔
397

398
        /* Rule X5c */
399
        case SBBidiTypeFSI:
136,854✔
400
        {
401
            SBBoolean isRTL = (DetermineBaseLevel(chain, link, roller, 0, SBTrue) == 1);
136,854✔
402
            PushIsolate(isRTL ? LeastGreaterOddLevel() : LeastGreaterEvenLevel(), SBBidiTypeON);
136,854✔
403
            break;
132,096✔
404
        }
405

406
        /* Rule X6 */
407
        default:
2,311,472✔
408
            BidiChainSetLevel(chain, link, StatusStackGetEmbeddingLevel(stack));
2,311,472✔
409

410
            if (StatusStackGetOverrideStatus(stack) != SBBidiTypeON) {
2,311,472✔
411
                BidiChainSetType(chain, link, StatusStackGetOverrideStatus(stack));
168,189✔
412
                MergeLinkIfNeeded();
168,189✔
413
            }
414
            break;
2,258,179✔
415

416
        /* Rule X6a */
417
        case SBBidiTypePDI:
137,326✔
418
        {
419
            SBBidiType overrideStatus;
420

421
            if (overIsolate != 0) {
137,326✔
422
                overIsolate -= 1;
9✔
423
            } else if (validIsolate == 0) {
137,317✔
424
                /* Do nothing */
425
            } else {
426
                overEmbedding = 0;
24,818✔
427

428
                while (!StatusStackGetIsolateStatus(stack)) {
28,093✔
429
                    StatusStackPop(stack);
3,275✔
430
                }
431
                StatusStackPop(stack);
24,818✔
432

433
                validIsolate -= 1;
24,818✔
434
            }
435

436
            BidiChainSetLevel(chain, link, StatusStackGetEmbeddingLevel(stack));
137,326✔
437
            overrideStatus = StatusStackGetOverrideStatus(stack);
137,326✔
438

439
            if (overrideStatus != SBBidiTypeON) {
137,326✔
440
                BidiChainSetType(chain, link, overrideStatus);
14,383✔
441
                MergeLinkIfNeeded();
14,383✔
442
            }
443
            break;
131,578✔
444
        }
445

446
        /* Rule X7 */
447
        case SBBidiTypePDF:
137,005✔
448
            bnEquivalent = SBTrue;
137,005✔
449

450
            if (overIsolate != 0) {
137,005✔
451
                /* Do nothing */
452
            } else if (overEmbedding != 0) {
137,005✔
453
                overEmbedding -= 1;
3✔
454
            } else if (!StatusStackGetIsolateStatus(stack) && stack->count >= 2) {
137,002✔
455
                StatusStackPop(stack);
28,854✔
456
            }
457
            break;
137,005✔
458

459
        /* Rule X8 */
460
        case SBBidiTypeB:
33,626✔
461
            /*
462
             * These values are reset for clarity, in this implementation B can only occur as the
463
             * last code in the array.
464
             */
465
            StatusStackSetEmpty(stack);
33,626✔
466
            StatusStackPush(stack, baseLevel, SBBidiTypeON, SBFalse);
33,626✔
467

468
            overIsolate = 0;
33,626✔
469
            overEmbedding = 0;
33,626✔
470
            validIsolate = 0;
33,626✔
471

472
            BidiChainSetLevel(chain, link, baseLevel);
33,626✔
473
            break;
33,626✔
474

475
        case SBBidiTypeBN:
132,192✔
476
            bnEquivalent = SBTrue;
132,192✔
477
            break;
132,192✔
478

479
        case SBBidiTypeNil:
862,281✔
480
            forceFinish = SBTrue;
862,281✔
481
            BidiChainSetLevel(chain, link, baseLevel);
862,281✔
482
            break;
862,281✔
483
        }
484

485
        /* Rule X9 */
486
        if (bnEquivalent) {
4,500,811✔
487
            /* The type of this link is BN equivalent, so abandon it and continue the loop. */
488
            BidiChainAbandonNext(chain, priorLink);
818,609✔
489
            continue;
818,609✔
490
        }
491

492
        if (sor == SBBidiTypeNil) {
3,682,202✔
493
            sor = SBLevelAsNormalBidiType(SBNumberGetMax(baseLevel, BidiChainGetLevel(chain, link)));
862,281✔
494
            firstLink = link;
862,281✔
495
            priorLevel = BidiChainGetLevel(chain, link);
862,281✔
496
        } else if (priorLevel != BidiChainGetLevel(chain, link) || forceFinish) {
2,819,921✔
497
            LevelRun levelRun;
1,265,139✔
498
            SBLevel currentLevel;
499

500
            /* Since the level has changed at this link, therefore the run must end at prior link. */
501
            lastLink = priorLink;
1,265,139✔
502

503
            /* Save the current level i.e. level of the next run. */
504
            currentLevel = BidiChainGetLevel(chain, link);
1,265,139✔
505
            /*
506
             * Now we have both the prior level and the current level i.e. unchanged levels of both
507
             * the current run and the next run. So, identify eor of the current run.
508
             * NOTE:
509
             *      sor of the run has been already determined at this stage.
510
             */
511
            eor = SBLevelAsNormalBidiType(SBNumberGetMax(priorLevel, currentLevel));
1,265,139✔
512

513
            LevelRunInitialize(&levelRun, chain, firstLink, lastLink, sor, eor);
1,265,139✔
514

515
            if (!ProcessRun(context, &levelRun, forceFinish)) {
1,265,139✔
UNCOV
516
                return SBFalse;
×
517
            }
518

519
            /* The sor of next run (if any) should be technically equal to eor of this run. */
520
            sor = eor;
1,265,139✔
521
            /* The next run (if any) will start from this index. */
522
            firstLink = link;
1,265,139✔
523

524
            priorLevel = currentLevel;
1,265,139✔
525
        }
526

527
        priorLink = link;
3,682,202✔
528
    }
529

530
    return SBTrue;
862,281✔
531
}
532

533
static SBBoolean ProcessRun(ParagraphContextRef context, const LevelRun *levelRun, SBBoolean resolveIsolatingRuns)
1,265,139✔
534
{
535
    RunQueueRef queue = &context->runQueue;
1,265,139✔
536

537
    if (!RunQueueEnqueue(queue, levelRun)) {
1,265,139✔
UNCOV
538
        return SBFalse;
×
539
    }
540

541
    if (resolveIsolatingRuns) {
1,265,139✔
542
        IsolatingRunRef isolatingRun = &context->isolatingRun;
857,619✔
543

544
        /* Rule X10 */
545
        for (; queue->count > 0; RunQueueDequeue(queue)) {
2,122,758✔
546
            const LevelRun *front = RunQueueGetFront(queue);
1,265,139✔
547

548
            if (RunKindIsAttachedTerminating(front->kind)) {
1,265,139✔
549
                continue;
7,758✔
550
            }
551

552
            isolatingRun->baseLevelRun = front;
1,257,381✔
553

554
            if (!IsolatingRunResolve(isolatingRun)) {
1,257,381✔
UNCOV
555
                return SBFalse;
×
556
            }
557
        }
558
    }
559

560
    return SBTrue;
1,265,139✔
561
}
562

563
static void SaveLevels(BidiChainRef chain, SBLevel *levels, SBLevel baseLevel)
862,281✔
564
{
565
    BidiLink roller = chain->roller;
862,281✔
566
    BidiLink link;
567

568
    SBUInteger index = 0;
862,281✔
569
    SBLevel level = baseLevel;
862,281✔
570

571
    BidiChainForEach(chain, roller, link) {
4,462,597✔
572
        SBUInteger offset = BidiChainGetOffset(chain, link);
3,600,316✔
573

574
        for (; index < offset; index++) {
7,372,920✔
575
            levels[index] = level;
3,772,604✔
576
        }
577

578
        level = BidiChainGetLevel(chain, link);
3,600,316✔
579
    }
580
}
862,281✔
581

582
static SBBoolean ResolveParagraph(SBMutableParagraphRef paragraph, MemoryRef memory,
862,281✔
583
    const SBCodepointSequence *codepointSequence, const SBBidiType *refBidiTypes,
584
    SBUInteger offset, SBUInteger length, SBLevel baseLevel)
585
{
586
    const SBBidiType *bidiTypes = &refBidiTypes[offset];
862,281✔
587
    SBBoolean isSucceeded = SBFalse;
862,281✔
588
    ParagraphContext context;
862,281✔
589

590
    if (InitializeParagraphContext(&context, memory, bidiTypes, paragraph->fixedLevels, length)) {
862,281✔
591
        SBLevel resolvedLevel = DetermineParagraphLevel(&context.bidiChain, baseLevel);
862,281✔
592

593
        SB_LOG_BLOCK_OPENER("Determined Paragraph Level");
594
        SB_LOG_STATEMENT("Base Level", 1, SB_LOG_LEVEL(resolvedLevel));
595
        SB_LOG_BLOCK_CLOSER();
596

597
        context.isolatingRun.codepointSequence = codepointSequence;
862,281✔
598
        context.isolatingRun.bidiTypes = bidiTypes;
862,281✔
599
        context.isolatingRun.bidiChain = &context.bidiChain;
862,281✔
600
        context.isolatingRun.paragraphOffset = offset;
862,281✔
601
        context.isolatingRun.paragraphLevel = resolvedLevel;
862,281✔
602

603
        if (DetermineLevels(&context, resolvedLevel)) {
862,281✔
604
            SaveLevels(&context.bidiChain, paragraph->fixedLevels, resolvedLevel);
862,281✔
605

606
            SB_LOG_BLOCK_OPENER("Determined Embedding Levels");
607
            SB_LOG_STATEMENT("Levels", 1, SB_LOG_LEVELS_ARRAY(paragraph->fixedLevels, length));
608
            SB_LOG_BLOCK_CLOSER();
609

610
            paragraph->codepointSequence = *codepointSequence;
862,281✔
611
            paragraph->refTypes = bidiTypes;
862,281✔
612
            paragraph->offset = offset;
862,281✔
613
            paragraph->length = length;
862,281✔
614
            paragraph->baseLevel = resolvedLevel;
862,281✔
615

616
            isSucceeded = SBTrue;
862,281✔
617
        }
618
    }
619

620
    return isSucceeded;
862,281✔
621
}
622

623
static SBParagraphRef CreateParagraph(SBAlgorithmRef algorithm,
862,281✔
624
    const SBCodepointSequence *codepointSequence, const SBBidiType *refBidiTypes,
625
    SBUInteger paragraphOffset, SBUInteger suggestedLength, SBLevel baseLevel)
626
{
627
    SBUInteger actualLength;
628
    SBMutableParagraphRef paragraph;
629

630
    if (algorithm) {
862,281✔
631
        codepointSequence = &algorithm->codepointSequence;
861,959✔
632
        refBidiTypes = algorithm->fixedTypes;
861,959✔
633
    }
634

635
    SB_LOG_BLOCK_OPENER("Paragraph Input");
636
    SB_LOG_STATEMENT("Paragraph Offset", 1, SB_LOG_NUMBER(paragraphOffset));
637
    SB_LOG_STATEMENT("Suggested Length", 1, SB_LOG_NUMBER(suggestedLength));
638
    SB_LOG_STATEMENT("Base Direction",   1, SB_LOG_BASE_LEVEL(baseLevel));
639
    SB_LOG_BLOCK_CLOSER();
640

641
    actualLength = DetermineBoundary(codepointSequence, refBidiTypes, paragraphOffset, suggestedLength);
862,281✔
642

643
    SB_LOG_BLOCK_OPENER("Determined Paragraph Boundary");
644
    SB_LOG_STATEMENT("Actual Length", 1, SB_LOG_NUMBER(actualLength));
645
    SB_LOG_BLOCK_CLOSER();
646

647
    paragraph = AllocateParagraph(actualLength);
862,281✔
648

649
    if (paragraph) {
862,281✔
650
        SBBoolean isResolved;
651
        Memory memory;
862,281✔
652

653
        MemoryInitialize(&memory);
862,281✔
654
        isResolved = ResolveParagraph(
862,281✔
655
            paragraph, &memory, codepointSequence, refBidiTypes,
656
            paragraphOffset, actualLength, baseLevel
657
        );
658

659
        if (isResolved) {
862,281✔
660
            paragraph->_algorithm = (algorithm ? SBAlgorithmRetain(algorithm) : NULL);
862,281✔
661
        } else {
UNCOV
662
            ObjectRelease(paragraph);
×
UNCOV
663
            paragraph = NULL;
×
664
        }
665

666
        MemoryFinalize(&memory);
862,281✔
667
        SBAllocatorResetScratch(NULL);
862,281✔
668
    }
669

670
    SB_LOG_BREAKER();
671

672
    return paragraph;
862,281✔
673
}
674

675
SB_INTERNAL SBParagraphRef SBParagraphCreateWithAlgorithm(SBAlgorithmRef algorithm,
861,959✔
676
    SBUInteger paragraphOffset, SBUInteger suggestedLength, SBLevel baseLevel)
677
{
678
    SBUInteger stringLength = algorithm->codepointSequence.stringLength;
861,959✔
679

680
    /* The specified range MUST be valid */
681
    SBAssert(SBUIntegerVerifyRange(stringLength, paragraphOffset, suggestedLength) && suggestedLength > 0);
861,959✔
682

683
    return CreateParagraph(algorithm, NULL, NULL, paragraphOffset, suggestedLength, baseLevel);
861,959✔
684
}
685

686
SB_INTERNAL SBParagraphRef SBParagraphCreateWithCodepointSequence(
322✔
687
    const SBCodepointSequence *codepointSequence, const SBBidiType *refBidiTypes,
688
    SBUInteger paragraphOffset, SBUInteger suggestedLength, SBLevel baseLevel)
689
{
690
    SBUInteger stringLength = codepointSequence->stringLength;
322✔
691

692
    /* The specified range MUST be valid */
693
    SBAssert(SBUIntegerVerifyRange(stringLength, paragraphOffset, suggestedLength) && suggestedLength > 0);
322✔
694

695
    return CreateParagraph(NULL, codepointSequence, refBidiTypes, paragraphOffset, suggestedLength, baseLevel);
322✔
696
}
697

698
SBUInteger SBParagraphGetOffset(SBParagraphRef paragraph)
11✔
699
{
700
    return paragraph->offset;
11✔
701
}
702

703
SBUInteger SBParagraphGetLength(SBParagraphRef paragraph)
11✔
704
{
705
    return paragraph->length;
11✔
706
}
707

708
SBLevel SBParagraphGetBaseLevel(SBParagraphRef paragraph)
861,948✔
709
{
710
    return paragraph->baseLevel;
861,948✔
711
}
712

713
const SBLevel *SBParagraphGetLevelsPtr(SBParagraphRef paragraph)
11✔
714
{
715
    return paragraph->fixedLevels;
11✔
716
}
717

718
SBLineRef SBParagraphCreateLine(SBParagraphRef paragraph, SBUInteger lineOffset, SBUInteger lineLength)
861,963✔
719
{
720
    SBUInteger paragraphOffset = paragraph->offset;
861,963✔
721
    SBUInteger paragraphLength = paragraph->length;
861,963✔
722
    SBUInteger paragraphLimit = paragraphOffset + paragraphLength;
861,963✔
723
    SBUInteger lineLimit = lineOffset + lineLength;
861,963✔
724

725
    if (lineOffset < lineLimit && lineOffset >= paragraphOffset && lineLimit <= paragraphLimit) {
861,963✔
726
        return SBLineCreate(paragraph, lineOffset, lineLength);
861,963✔
727
    }
728

UNCOV
729
    return NULL;
×
730
}
731

732
SBParagraphRef SBParagraphRetain(SBParagraphRef paragraph)
4✔
733
{
734
    return ObjectRetain((ObjectRef)paragraph);
4✔
735
}
736

737
void SBParagraphRelease(SBParagraphRef paragraph)
862,285✔
738
{
739
    ObjectRelease((ObjectRef)paragraph);
862,285✔
740
}
862,285✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc