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

Tehreer / SheenBidi / 21298031641

23 Jan 2026 07:10PM UTC coverage: 95.567% (-0.8%) from 96.403%
21298031641

push

github

mta452
Make text editing and analysis API optional

2393 of 2504 relevant lines covered (95.57%)

743160.63 hits per line

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

95.93
/Source/API/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 <API/SBAlgorithm.h>
21
#include <API/SBAllocator.h>
22
#include <API/SBAssert.h>
23
#include <API/SBBase.h>
24
#include <API/SBCodepointSequence.h>
25
#include <API/SBLine.h>
26
#include <API/SBLog.h>
27
#include <Core/Memory.h>
28
#include <Core/Object.h>
29
#include <UBA/BidiChain.h>
30
#include <UBA/IsolatingRun.h>
31
#include <UBA/LevelRun.h>
32
#include <UBA/RunQueue.h>
33
#include <UBA/StatusStack.h>
34

35
#include "SBParagraph.h"
36

37
typedef SBParagraph *SBMutableParagraphRef;
38

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

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

50
#define BIDI_LINKS        0
51
#define BIDI_TYPES        1
52
#define BIDI_FLAGS        2
53
#define COUNT             3
54

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

62
    sizes[BIDI_LINKS] = sizeof(BidiLink) * (length + 2);
861,959✔
63
    sizes[BIDI_TYPES] = sizeof(SBBidiType) * (length + 2);
861,959✔
64
    sizes[BIDI_FLAGS] = sizeof(BidiFlag) * (length + 2);
861,959✔
65

66
    if (MemoryAllocateChunks(memory, MemoryTypeScratch, sizes, COUNT, pointers)) {
861,959✔
67
        BidiLink *fixedLinks = pointers[BIDI_LINKS];
861,959✔
68
        SBBidiType *fixedTypes = pointers[BIDI_TYPES];
861,959✔
69
        BidiFlag *fixedFlags = pointers[BIDI_FLAGS];
861,959✔
70

71
        BidiChainInitialize(&context->bidiChain, fixedTypes, levels, fixedFlags, fixedLinks);
861,959✔
72
        StatusStackInitialize(&context->statusStack, memory);
861,959✔
73
        RunQueueInitialize(&context->runQueue, memory);
861,959✔
74
        IsolatingRunInitialize(&context->isolatingRun, memory);
861,959✔
75

76
        PopulateBidiChain(&context->bidiChain, types, length);
861,959✔
77

78
        isInitialized = SBTrue;
861,959✔
79
    }
80

81
    return isInitialized;
861,959✔
82
}
83

84
#undef BIDI_LINKS
85
#undef BIDI_TYPES
86
#undef BIDI_FLAGS
87
#undef COUNT
88

89
#define PARAGRAPH 0
90
#define LEVELS    1
91
#define COUNT     2
92

93
static SBMutableParagraphRef AllocateParagraph(SBUInteger length)
861,959✔
94
{
95
    void *pointers[COUNT] = { NULL };
861,959✔
96
    SBUInteger sizes[COUNT] = { 0 };
861,959✔
97
    SBMutableParagraphRef paragraph;
98

99
    sizes[PARAGRAPH] = sizeof(SBParagraph);
861,959✔
100
    sizes[LEVELS]    = sizeof(SBLevel) * (length + 2);
861,959✔
101

102
    paragraph = ObjectCreate(sizes, COUNT, pointers, FinalizeParagraph);
861,959✔
103

104
    if (paragraph) {
861,959✔
105
        paragraph->fixedLevels = pointers[LEVELS];
861,959✔
106
    }
107

108
    return paragraph;
861,959✔
109
}
110

111
#undef PARAGRAPH
112
#undef LEVELS
113
#undef COUNT
114

115
static void FinalizeParagraph(ObjectRef object)
861,959✔
116
{
117
    SBParagraphRef paragraph = object;
861,959✔
118
    SBAlgorithmRef algorithm;
119

120
    algorithm = paragraph->_algorithm;
861,959✔
121

122
    if (algorithm) {
861,959✔
123
        SBAlgorithmRelease(algorithm);
861,959✔
124
    }
125
}
861,959✔
126

127
static SBUInteger DetermineBoundary(const SBCodepointSequence *codepointSequence,
861,959✔
128
    const SBBidiType *bidiTypes, SBUInteger paragraphOffset, SBUInteger suggestedLength)
129
{
130
    SBUInteger suggestedLimit = paragraphOffset + suggestedLength;
861,959✔
131
    SBUInteger stringIndex;
132

133
    for (stringIndex = paragraphOffset; stringIndex < suggestedLimit; stringIndex++) {
4,595,636✔
134
        if (bidiTypes[stringIndex] == SBBidiTypeB) {
3,767,150✔
135
            stringIndex += SBCodepointSequenceGetSeparatorLength(codepointSequence, stringIndex);
33,473✔
136
            goto Return;
33,473✔
137
        }
138
    }
139

140
Return:
828,486✔
141
    return (stringIndex - paragraphOffset);
861,959✔
142
}
143

144
static void PopulateBidiChain(BidiChainRef chain, const SBBidiType *types, SBUInteger length)
861,959✔
145
{
146
    SBBidiType type = SBBidiTypeNil;
861,959✔
147
    SBUInteger priorIndex = SBInvalidIndex;
861,959✔
148
    SBUInteger index;
149

150
    for (index = 0; index < length; index++) {
4,595,636✔
151
        SBBidiType priorType = type;
3,767,150✔
152
        type = types[index];
3,767,150✔
153

154
        switch (type) {
3,767,150✔
155
        case SBBidiTypeB:
1,898,889✔
156
        case SBBidiTypeON:
157
        case SBBidiTypeLRE:
158
        case SBBidiTypeRLE:
159
        case SBBidiTypeLRO:
160
        case SBBidiTypeRLO:
161
        case SBBidiTypePDF:
162
        case SBBidiTypeLRI:
163
        case SBBidiTypeRLI:
164
        case SBBidiTypeFSI:
165
        case SBBidiTypePDI:
166
            BidiChainAdd(chain, type, index - priorIndex);
1,898,889✔
167
            priorIndex = index;
1,898,889✔
168

169
            if (type == SBBidiTypeB) {
1,898,889✔
170
                index = length;
33,473✔
171
                goto AddLast;
33,473✔
172
            }
173
            break;
1,865,416✔
174

175
        default:
1,868,261✔
176
            if (type != priorType) {
1,868,261✔
177
                BidiChainAdd(chain, type, index - priorIndex);
1,811,754✔
178
                priorIndex = index;
1,811,754✔
179
            }
180
            break;
1,868,261✔
181
        }
182
    }
183

184
AddLast:
828,486✔
185
    BidiChainAdd(chain, SBBidiTypeNil, index - priorIndex);
861,959✔
186
}
861,959✔
187

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

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

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

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

211
    return BidiLinkNone;
106,581✔
212
}
213

214
static SBLevel DetermineBaseLevel(BidiChainRef chain, BidiLink skipLink, BidiLink breakLink, SBLevel defaultLevel, SBBoolean isIsolate)
393,629✔
215
{
216
    BidiLink link = skipLink;
393,629✔
217

218
    /* Rules P2, P3 */
219
    while ((link = BidiChainGetNext(chain, link)) != breakLink) {
1,163,123✔
220
        SBBidiType type = BidiChainGetType(chain, link);
997,684✔
221

222
        switch (type) {
997,684✔
223
        case SBBidiTypeL:
38,111✔
224
            return 0;
38,111✔
225

226
        case SBBidiTypeAL:
76,102✔
227
        case SBBidiTypeR:
228
            return 1;
76,102✔
229

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

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

252
Default:
165,439✔
253
    return defaultLevel;
279,416✔
254
}
255

256
static SBLevel DetermineParagraphLevel(BidiChainRef chain, SBLevel baseLevel)
861,959✔
257
{
258
    if (baseLevel >= SBLevelMax) {
861,959✔
259
        return DetermineBaseLevel(chain, chain->roller, chain->roller,
256,775✔
260
                                  (baseLevel != SBLevelDefaultRTL ? 0 : 1),
261
                                  SBFalse);
262
    }
263

264
    return baseLevel;
605,184✔
265
}
266

267
static SBBoolean DetermineLevels(ParagraphContextRef context, SBLevel baseLevel)
861,959✔
268
{
269
    BidiChainRef chain = &context->bidiChain;
861,959✔
270
    StatusStackRef stack = &context->statusStack;
861,959✔
271
    BidiLink roller = chain->roller;
861,959✔
272
    BidiLink link;
273

274
    BidiLink priorLink;
275
    BidiLink firstLink;
276
    BidiLink lastLink;
277

278
    SBLevel priorLevel;
279
    SBBidiType sor;
280
    SBBidiType eor;
281

282
    SBUInteger overIsolate;
283
    SBUInteger overEmbedding;
284
    SBUInteger validIsolate;
285

286
    priorLink = chain->roller;
861,959✔
287
    firstLink = BidiLinkNone;
861,959✔
288
    lastLink = BidiLinkNone;
861,959✔
289

290
    priorLevel = baseLevel;
861,959✔
291
    sor = SBBidiTypeNil;
861,959✔
292

293
    /* Rule X1 */
294
    overIsolate = 0;
861,959✔
295
    overEmbedding = 0;
861,959✔
296
    validIsolate = 0;
861,959✔
297

298
    StatusStackPush(stack, baseLevel, SBBidiTypeON, SBFalse);
861,959✔
299

300
    BidiChainForEach(chain, roller, link) {
5,434,561✔
301
        SBBoolean forceFinish = SBFalse;
4,572,602✔
302
        SBBoolean bnEquivalent = SBFalse;
4,572,602✔
303
        SBBidiType type;
304

305
        type = BidiChainGetType(chain, link);
4,572,602✔
306

307
#define LeastGreaterOddLevel()                                              \
308
(                                                                           \
309
        (StatusStackGetEmbeddingLevel(stack) + 1) | 1                       \
310
)
311

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

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

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

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

365
        switch (type) {
4,572,602✔
366
        /* Rule X2 */
367
        case SBBidiTypeRLE:
136,944✔
368
            PushEmbedding(LeastGreaterOddLevel(), SBBidiTypeON);
136,944✔
369
            break;
136,944✔
370

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

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

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

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

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

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

404
        /* Rule X6 */
405
        default:
2,310,448✔
406
            BidiChainSetLevel(chain, link, StatusStackGetEmbeddingLevel(stack));
2,310,448✔
407

408
            if (StatusStackGetOverrideStatus(stack) != SBBidiTypeON) {
2,310,448✔
409
                BidiChainSetType(chain, link, StatusStackGetOverrideStatus(stack));
168,189✔
410
                MergeLinkIfNeeded();
168,189✔
411
            }
412
            break;
2,257,155✔
413

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

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

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

431
                validIsolate -= 1;
24,818✔
432
            }
433

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

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

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

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

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

466
            overIsolate = 0;
33,473✔
467
            overEmbedding = 0;
33,473✔
468
            validIsolate = 0;
33,473✔
469

470
            BidiChainSetLevel(chain, link, baseLevel);
33,473✔
471
            break;
33,473✔
472

473
        case SBBidiTypeBN:
132,146✔
474
            bnEquivalent = SBTrue;
132,146✔
475
            break;
132,146✔
476

477
        case SBBidiTypeNil:
861,959✔
478
            forceFinish = SBTrue;
861,959✔
479
            BidiChainSetLevel(chain, link, baseLevel);
861,959✔
480
            break;
861,959✔
481
        }
482

483
        /* Rule X9 */
484
        if (bnEquivalent) {
4,499,266✔
485
            /* The type of this link is BN equivalent, so abandon it and continue the loop. */
486
            BidiChainAbandonNext(chain, priorLink);
818,563✔
487
            continue;
818,563✔
488
        }
489

490
        if (sor == SBBidiTypeNil) {
3,680,703✔
491
            sor = SBLevelAsNormalBidiType(SBNumberGetMax(baseLevel, BidiChainGetLevel(chain, link)));
861,959✔
492
            firstLink = link;
861,959✔
493
            priorLevel = BidiChainGetLevel(chain, link);
861,959✔
494
        } else if (priorLevel != BidiChainGetLevel(chain, link) || forceFinish) {
2,818,744✔
495
            LevelRun levelRun;
1,264,817✔
496
            SBLevel currentLevel;
497

498
            /* Since the level has changed at this link, therefore the run must end at prior link. */
499
            lastLink = priorLink;
1,264,817✔
500

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

511
            LevelRunInitialize(&levelRun, chain, firstLink, lastLink, sor, eor);
1,264,817✔
512

513
            if (!ProcessRun(context, &levelRun, forceFinish)) {
1,264,817✔
514
                return SBFalse;
×
515
            }
516

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

522
            priorLevel = currentLevel;
1,264,817✔
523
        }
524

525
        priorLink = link;
3,680,703✔
526
    }
527

528
    return SBTrue;
861,959✔
529
}
530

531
static SBBoolean ProcessRun(ParagraphContextRef context, const LevelRun *levelRun, SBBoolean resolveIsolatingRuns)
1,264,817✔
532
{
533
    RunQueueRef queue = &context->runQueue;
1,264,817✔
534

535
    if (!RunQueueEnqueue(queue, levelRun)) {
1,264,817✔
536
        return SBFalse;
×
537
    }
538

539
    if (resolveIsolatingRuns) {
1,264,817✔
540
        IsolatingRunRef isolatingRun = &context->isolatingRun;
857,297✔
541

542
        /* Rule X10 */
543
        for (; queue->count > 0; RunQueueDequeue(queue)) {
2,122,114✔
544
            const LevelRun *front = RunQueueGetFront(queue);
1,264,817✔
545

546
            if (RunKindIsAttachedTerminating(front->kind)) {
1,264,817✔
547
                continue;
7,758✔
548
            }
549

550
            isolatingRun->baseLevelRun = front;
1,257,059✔
551

552
            if (!IsolatingRunResolve(isolatingRun)) {
1,257,059✔
553
                return SBFalse;
×
554
            }
555
        }
556
    }
557

558
    return SBTrue;
1,264,817✔
559
}
560

561
static void SaveLevels(BidiChainRef chain, SBLevel *levels, SBLevel baseLevel)
861,959✔
562
{
563
    BidiLink roller = chain->roller;
861,959✔
564
    BidiLink link;
565

566
    SBUInteger index = 0;
861,959✔
567
    SBLevel level = baseLevel;
861,959✔
568

569
    BidiChainForEach(chain, roller, link) {
4,460,986✔
570
        SBUInteger offset = BidiChainGetOffset(chain, link);
3,599,027✔
571

572
        for (; index < offset; index++) {
7,366,185✔
573
            levels[index] = level;
3,767,158✔
574
        }
575

576
        level = BidiChainGetLevel(chain, link);
3,599,027✔
577
    }
578
}
861,959✔
579

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

588
    if (InitializeParagraphContext(&context, memory, bidiTypes, paragraph->fixedLevels, length)) {
861,959✔
589
        SBLevel resolvedLevel = DetermineParagraphLevel(&context.bidiChain, baseLevel);
861,959✔
590

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

595
        context.isolatingRun.codepointSequence = codepointSequence;
861,959✔
596
        context.isolatingRun.bidiTypes = bidiTypes;
861,959✔
597
        context.isolatingRun.bidiChain = &context.bidiChain;
861,959✔
598
        context.isolatingRun.paragraphOffset = offset;
861,959✔
599
        context.isolatingRun.paragraphLevel = resolvedLevel;
861,959✔
600

601
        if (DetermineLevels(&context, resolvedLevel)) {
861,959✔
602
            SaveLevels(&context.bidiChain, paragraph->fixedLevels, resolvedLevel);
861,959✔
603

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

608
            paragraph->codepointSequence = *codepointSequence;
861,959✔
609
            paragraph->refTypes = bidiTypes;
861,959✔
610
            paragraph->offset = offset;
861,959✔
611
            paragraph->length = length;
861,959✔
612
            paragraph->baseLevel = resolvedLevel;
861,959✔
613

614
            isSucceeded = SBTrue;
861,959✔
615
        }
616
    }
617

618
    return isSucceeded;
861,959✔
619
}
620

621
static SBParagraphRef CreateParagraph(SBAlgorithmRef algorithm,
861,959✔
622
    const SBCodepointSequence *codepointSequence, const SBBidiType *refBidiTypes,
623
    SBUInteger paragraphOffset, SBUInteger suggestedLength, SBLevel baseLevel)
624
{
625
    SBUInteger actualLength;
626
    SBMutableParagraphRef paragraph;
627

628
    if (algorithm) {
861,959✔
629
        codepointSequence = &algorithm->codepointSequence;
861,959✔
630
        refBidiTypes = algorithm->fixedTypes;
861,959✔
631
    }
632

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

639
    actualLength = DetermineBoundary(codepointSequence, refBidiTypes, paragraphOffset, suggestedLength);
861,959✔
640

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

645
    paragraph = AllocateParagraph(actualLength);
861,959✔
646

647
    if (paragraph) {
861,959✔
648
        SBBoolean isResolved;
649
        Memory memory;
861,959✔
650

651
        MemoryInitialize(&memory);
861,959✔
652
        isResolved = ResolveParagraph(
861,959✔
653
            paragraph, &memory, codepointSequence, refBidiTypes,
654
            paragraphOffset, actualLength, baseLevel
655
        );
656

657
        if (isResolved) {
861,959✔
658
            paragraph->_algorithm = (algorithm ? SBAlgorithmRetain(algorithm) : NULL);
861,959✔
659
        } else {
660
            ObjectRelease(paragraph);
×
661
            paragraph = NULL;
×
662
        }
663

664
        MemoryFinalize(&memory);
861,959✔
665
        SBAllocatorResetScratch(NULL);
861,959✔
666
    }
667

668
    SB_LOG_BREAKER();
669

670
    return paragraph;
861,959✔
671
}
672

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

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

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

684
SB_INTERNAL SBParagraphRef SBParagraphCreateWithCodepointSequence(
×
685
    const SBCodepointSequence *codepointSequence, const SBBidiType *refBidiTypes,
686
    SBUInteger paragraphOffset, SBUInteger suggestedLength, SBLevel baseLevel)
687
{
688
    SBUInteger stringLength = codepointSequence->stringLength;
×
689

690
    /* The specified range MUST be valid */
691
    SBAssert(SBUIntegerVerifyRange(stringLength, paragraphOffset, suggestedLength) && suggestedLength > 0);
×
692

693
    return CreateParagraph(NULL, codepointSequence, refBidiTypes, paragraphOffset, suggestedLength, baseLevel);
×
694
}
695

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

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

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

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

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

723
    if (lineOffset < lineLimit && lineOffset >= paragraphOffset && lineLimit <= paragraphLimit) {
861,948✔
724
        return SBLineCreate(paragraph, lineOffset, lineLength);
861,948✔
725
    }
726

727
    return NULL;
×
728
}
729

730
SBParagraphRef SBParagraphRetain(SBParagraphRef paragraph)
×
731
{
732
    return ObjectRetain((ObjectRef)paragraph);
×
733
}
734

735
void SBParagraphRelease(SBParagraphRef paragraph)
861,959✔
736
{
737
    ObjectRelease((ObjectRef)paragraph);
861,959✔
738
}
861,959✔
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