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

Tehreer / SheenBidi / 15618549131

12 Jun 2025 06:45PM UTC coverage: 97.027% (+0.4%) from 96.656%
15618549131

push

github

mta452
[lib] Mark SBLine as immutable

3 of 3 new or added lines in 1 file covered. (100.0%)

31 existing lines in 5 files now uncovered.

2121 of 2186 relevant lines covered (97.03%)

754359.57 hits per line

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

97.22
/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 "SBAssert.h"
31
#include "SBBase.h"
32
#include "SBCodepointSequence.h"
33
#include "SBLine.h"
34
#include "SBLog.h"
35
#include "StatusStack.h"
36
#include "SBParagraph.h"
37

38
typedef SBParagraph *SBMutableParagraphRef;
39

40
typedef struct _ParagraphContext {
41
    Memory memory;
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 LevelRunRef levelRun, SBBoolean forceFinish);
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,
861,949✔
58
    const SBBidiType *types, SBLevel *levels, SBUInteger length)
59
{
60
    SBBoolean isInitialized = SBFalse;
861,949✔
61
    void *pointers[COUNT] = { NULL };
861,949✔
62
    SBUInteger sizes[COUNT];
861,949✔
63

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

68
    MemoryInitialize(&context->memory);
861,949✔
69

70
    if (MemoryAllocateChunks(&context->memory, sizes, COUNT, pointers)) {
861,949✔
71
        BidiLink *fixedLinks = pointers[BIDI_LINKS];
861,949✔
72
        SBBidiType *fixedTypes = pointers[BIDI_TYPES];
861,949✔
73
        BidiFlag *fixedFlags = pointers[BIDI_FLAGS];
861,949✔
74

75
        BidiChainInitialize(&context->bidiChain, fixedTypes, levels, fixedFlags, fixedLinks);
861,949✔
76
        StatusStackInitialize(&context->statusStack);
861,949✔
77
        RunQueueInitialize(&context->runQueue);
861,949✔
78
        IsolatingRunInitialize(&context->isolatingRun);
861,949✔
79

80
        PopulateBidiChain(&context->bidiChain, types, length);
861,949✔
81

82
        isInitialized = SBTrue;
861,949✔
83
    }
84

85
    return isInitialized;
861,949✔
86
}
87

88
#undef BIDI_LINKS
89
#undef BIDI_TYPES
90
#undef BIDI_FLAGS
91
#undef COUNT
92

93
static void FinalizeParagraphContext(ParagraphContextRef context)
861,949✔
94
{
95
    StatusStackFinalize(&context->statusStack);
861,949✔
96
    RunQueueFinalize(&context->runQueue);
861,949✔
97
    IsolatingRunFinalize(&context->isolatingRun);
861,949✔
98
    MemoryFinalize(&context->memory);
861,949✔
99
}
861,949✔
100

101
#define PARAGRAPH 0
102
#define LEVELS    1
103
#define COUNT     2
104

105
static SBMutableParagraphRef AllocateParagraph(SBUInteger length)
861,949✔
106
{
107
    void *pointers[COUNT] = { NULL };
861,949✔
108
    SBUInteger sizes[COUNT] = { 0 };
861,949✔
109
    SBMutableParagraphRef paragraph;
110

111
    sizes[PARAGRAPH] = sizeof(SBParagraph);
861,949✔
112
    sizes[LEVELS]    = sizeof(SBLevel) * (length + 2);
861,949✔
113

114
    paragraph = ObjectCreate(sizes, COUNT, pointers, &FinalizeParagraph);
861,949✔
115

116
    if (paragraph) {
861,949✔
117
        paragraph->fixedLevels = pointers[LEVELS];
861,949✔
118
    }
119

120
    return paragraph;
861,949✔
121
}
122

123
#undef PARAGRAPH
124
#undef LEVELS
125
#undef COUNT
126

127
static void FinalizeParagraph(ObjectRef object)
861,949✔
128
{
129
    SBParagraphRef paragraph = object;
861,949✔
130
    SBAlgorithmRelease(paragraph->algorithm);
861,949✔
131
}
861,949✔
132

133
static SBUInteger DetermineBoundary(SBAlgorithmRef algorithm, SBUInteger paragraphOffset, SBUInteger suggestedLength)
861,949✔
134
{
135
    SBBidiType *bidiTypes = algorithm->fixedTypes;
861,949✔
136
    SBUInteger suggestedLimit = paragraphOffset + suggestedLength;
861,949✔
137
    SBUInteger stringIndex;
138

139
    for (stringIndex = paragraphOffset; stringIndex < suggestedLimit; stringIndex++) {
4,595,608✔
140
        if (bidiTypes[stringIndex] == SBBidiTypeB) {
3,767,125✔
141
            stringIndex += SBAlgorithmGetSeparatorLength(algorithm, stringIndex);
33,466✔
142
            goto Return;
33,466✔
143
        }
144
    }
145

146
Return:
828,483✔
147
    return (stringIndex - paragraphOffset);
861,949✔
148
}
149

150
static void PopulateBidiChain(BidiChainRef chain, const SBBidiType *types, SBUInteger length)
861,949✔
151
{
152
    SBBidiType type = SBBidiTypeNil;
861,949✔
153
    SBUInteger priorIndex = SBInvalidIndex;
861,949✔
154
    SBUInteger index;
155

156
    for (index = 0; index < length; index++) {
4,595,608✔
157
        SBBidiType priorType = type;
3,767,125✔
158
        type = types[index];
3,767,125✔
159

160
        switch (type) {
3,767,125✔
161
        case SBBidiTypeB:
1,898,882✔
162
        case SBBidiTypeON:
163
        case SBBidiTypeLRE:
164
        case SBBidiTypeRLE:
165
        case SBBidiTypeLRO:
166
        case SBBidiTypeRLO:
167
        case SBBidiTypePDF:
168
        case SBBidiTypeLRI:
169
        case SBBidiTypeRLI:
170
        case SBBidiTypeFSI:
171
        case SBBidiTypePDI:
172
            BidiChainAdd(chain, type, index - priorIndex);
1,898,882✔
173
            priorIndex = index;
1,898,882✔
174

175
            if (type == SBBidiTypeB) {
1,898,882✔
176
                index = length;
33,466✔
177
                goto AddLast;
33,466✔
178
            }
179
            break;
1,865,416✔
180

181
        default:
1,868,243✔
182
            if (type != priorType) {
1,868,243✔
183
                BidiChainAdd(chain, type, index - priorIndex);
1,811,748✔
184
                priorIndex = index;
1,811,748✔
185
            }
186
            break;
1,868,243✔
187
        }
188
    }
189

190
AddLast:
828,483✔
191
    BidiChainAdd(chain, SBBidiTypeNil, index - priorIndex);
861,949✔
192
}
861,949✔
193

194
static BidiLink SkipIsolatingRun(BidiChainRef chain, BidiLink skipLink, BidiLink breakLink)
114,000✔
195
{
196
    BidiLink link = skipLink;
114,000✔
197
    SBUInteger depth = 1;
114,000✔
198

199
    while ((link = BidiChainGetNext(chain, link)) != breakLink) {
393,217✔
200
        SBBidiType type = BidiChainGetType(chain, link);
286,636✔
201

202
        switch (type) {
286,636✔
203
        case SBBidiTypeLRI:
24,335✔
204
        case SBBidiTypeRLI:
205
        case SBBidiTypeFSI:
206
            depth += 1;
24,335✔
207
            break;
24,335✔
208

209
        case SBBidiTypePDI:
8,165✔
210
            if (--depth == 0) {
8,165✔
211
                return link;
7,419✔
212
            }
213
            break;
746✔
214
        }
215
    }
216

217
    return BidiLinkNone;
106,581✔
218
}
219

220
static SBLevel DetermineBaseLevel(BidiChainRef chain, BidiLink skipLink, BidiLink breakLink, SBLevel defaultLevel, SBBoolean isIsolate)
393,629✔
221
{
222
    BidiLink link = skipLink;
393,629✔
223

224
    /* Rules P2, P3 */
225
    while ((link = BidiChainGetNext(chain, link)) != breakLink) {
1,163,123✔
226
        SBBidiType type = BidiChainGetType(chain, link);
997,684✔
227

228
        switch (type) {
997,684✔
229
        case SBBidiTypeL:
38,111✔
230
            return 0;
38,111✔
231

232
        case SBBidiTypeAL:
76,102✔
233
        case SBBidiTypeR:
234
            return 1;
76,102✔
235

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

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

258
Default:
165,439✔
259
    return defaultLevel;
279,416✔
260
}
261

262
static SBLevel DetermineParagraphLevel(BidiChainRef chain, SBLevel baseLevel)
861,949✔
263
{
264
    if (baseLevel >= SBLevelMax) {
861,949✔
265
        return DetermineBaseLevel(chain, chain->roller, chain->roller,
256,775✔
266
                                  (baseLevel != SBLevelDefaultRTL ? 0 : 1),
267
                                  SBFalse);
268
    }
269

270
    return baseLevel;
605,174✔
271
}
272

273
static SBBoolean DetermineLevels(ParagraphContextRef context, SBLevel baseLevel)
861,949✔
274
{
275
    BidiChainRef chain = &context->bidiChain;
861,949✔
276
    StatusStackRef stack = &context->statusStack;
861,949✔
277
    BidiLink roller = chain->roller;
861,949✔
278
    BidiLink link;
279

280
    BidiLink priorLink;
281
    BidiLink firstLink;
282
    BidiLink lastLink;
283

284
    SBLevel priorLevel;
285
    SBBidiType sor;
286
    SBBidiType eor;
287

288
    SBUInteger overIsolate;
289
    SBUInteger overEmbedding;
290
    SBUInteger validIsolate;
291

292
    priorLink = chain->roller;
861,949✔
293
    firstLink = BidiLinkNone;
861,949✔
294
    lastLink = BidiLinkNone;
861,949✔
295

296
    priorLevel = baseLevel;
861,949✔
297
    sor = SBBidiTypeNil;
861,949✔
298

299
    /* Rule X1 */
300
    overIsolate = 0;
861,949✔
301
    overEmbedding = 0;
861,949✔
302
    validIsolate = 0;
861,949✔
303

304
    StatusStackPush(stack, baseLevel, SBBidiTypeON, SBFalse);
861,949✔
305

306
    BidiChainForEach(chain, roller, link) {
5,434,528✔
307
        SBBoolean forceFinish = SBFalse;
4,572,579✔
308
        SBBoolean bnEquivalent = SBFalse;
4,572,579✔
309
        SBBidiType type;
310

311
        type = BidiChainGetType(chain, link);
4,572,579✔
312

313
#define LeastGreaterOddLevel()                                              \
314
(                                                                           \
315
        (StatusStackGetEmbeddingLevel(stack) + 1) | 1                       \
316
)
317

318
#define LeastGreaterEvenLevel()                                             \
319
(                                                                           \
320
        (StatusStackGetEmbeddingLevel(stack) + 2) & ~1                      \
321
)
322

323
#define MergeLinkIfNeeded()                                                 \
324
{                                                                           \
325
        if (BidiChainMergeNext(chain, priorLink)) {                         \
326
            continue;                                                       \
327
        }                                                                   \
328
}
329

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

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

371
        switch (type) {
4,572,579✔
372
        /* Rule X2 */
373
        case SBBidiTypeRLE:
136,944✔
374
            PushEmbedding(LeastGreaterOddLevel(), SBBidiTypeON);
136,944✔
375
            break;
136,944✔
376

377
        /* Rule X3 */
378
        case SBBidiTypeLRE:
138,300✔
379
            PushEmbedding(LeastGreaterEvenLevel(), SBBidiTypeON);
138,300✔
380
            break;
138,300✔
381

382
        /* Rule X4 */
383
        case SBBidiTypeRLO:
136,946✔
384
            PushEmbedding(LeastGreaterOddLevel(), SBBidiTypeR);
136,946✔
385
            break;
136,946✔
386

387
        /* Rule X5 */
388
        case SBBidiTypeLRO:
137,222✔
389
            PushEmbedding(LeastGreaterEvenLevel(), SBBidiTypeL);
137,222✔
390
            break;
137,222✔
391

392
        /* Rule X5a */
393
        case SBBidiTypeRLI:
136,981✔
394
            PushIsolate(LeastGreaterOddLevel(), SBBidiTypeON);
136,981✔
395
            break;
132,214✔
396

397
        /* Rule X5b */
398
        case SBBidiTypeLRI:
136,998✔
399
            PushIsolate(LeastGreaterEvenLevel(), SBBidiTypeON);
136,998✔
400
            break;
132,228✔
401

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

410
        /* Rule X6 */
411
        default:
2,310,442✔
412
            BidiChainSetLevel(chain, link, StatusStackGetEmbeddingLevel(stack));
2,310,442✔
413

414
            if (StatusStackGetOverrideStatus(stack) != SBBidiTypeON) {
2,310,442✔
415
                BidiChainSetType(chain, link, StatusStackGetOverrideStatus(stack));
168,189✔
416
                MergeLinkIfNeeded();
168,189✔
417
            }
418
            break;
2,257,149✔
419

420
        /* Rule X6a */
421
        case SBBidiTypePDI:
137,326✔
422
        {
423
            SBBidiType overrideStatus;
424

425
            if (overIsolate != 0) {
137,326✔
426
                overIsolate -= 1;
9✔
427
            } else if (validIsolate == 0) {
137,317✔
428
                /* Do nothing */
429
            } else {
430
                overEmbedding = 0;
24,818✔
431

432
                while (!StatusStackGetIsolateStatus(stack)) {
28,093✔
433
                    StatusStackPop(stack);
3,275✔
434
                }
435
                StatusStackPop(stack);
24,818✔
436

437
                validIsolate -= 1;
24,818✔
438
            }
439

440
            BidiChainSetLevel(chain, link, StatusStackGetEmbeddingLevel(stack));
137,326✔
441
            overrideStatus = StatusStackGetOverrideStatus(stack);
137,326✔
442

443
            if (overrideStatus != SBBidiTypeON) {
137,326✔
444
                BidiChainSetType(chain, link, overrideStatus);
14,383✔
445
                MergeLinkIfNeeded();
14,383✔
446
            }
447
            break;
131,578✔
448
        }
449

450
        /* Rule X7 */
451
        case SBBidiTypePDF:
137,005✔
452
            bnEquivalent = SBTrue;
137,005✔
453

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

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

472
            overIsolate = 0;
33,466✔
473
            overEmbedding = 0;
33,466✔
474
            validIsolate = 0;
33,466✔
475

476
            BidiChainSetLevel(chain, link, baseLevel);
33,466✔
477
            break;
33,466✔
478

479
        case SBBidiTypeBN:
132,146✔
480
            bnEquivalent = SBTrue;
132,146✔
481
            break;
132,146✔
482

483
        case SBBidiTypeNil:
861,949✔
484
            forceFinish = SBTrue;
861,949✔
485
            BidiChainSetLevel(chain, link, baseLevel);
861,949✔
486
            break;
861,949✔
487
        }
488

489
        /* Rule X9 */
490
        if (bnEquivalent) {
4,499,243✔
491
            /* The type of this link is BN equivalent, so abandon it and continue the loop. */
492
            BidiChainAbandonNext(chain, priorLink);
818,563✔
493
            continue;
818,563✔
494
        }
495

496
        if (sor == SBBidiTypeNil) {
3,680,680✔
497
            sor = SBLevelAsNormalBidiType(SBNumberGetMax(baseLevel, BidiChainGetLevel(chain, link)));
861,949✔
498
            firstLink = link;
861,949✔
499
            priorLevel = BidiChainGetLevel(chain, link);
861,949✔
500
        } else if (priorLevel != BidiChainGetLevel(chain, link) || forceFinish) {
2,818,731✔
501
            LevelRun levelRun;
1,264,807✔
502
            SBLevel currentLevel;
503

504
            /* Since the level has changed at this link, therefore the run must end at prior link. */
505
            lastLink = priorLink;
1,264,807✔
506

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

517
            LevelRunInitialize(&levelRun, chain, firstLink, lastLink, sor, eor);
1,264,807✔
518

519
            if (!ProcessRun(context, &levelRun, forceFinish)) {
1,264,807✔
UNCOV
520
                return SBFalse;
×
521
            }
522

523
            /* The sor of next run (if any) should be technically equal to eor of this run. */
524
            sor = eor;
1,264,807✔
525
            /* The next run (if any) will start from this index. */
526
            firstLink = link;
1,264,807✔
527

528
            priorLevel = currentLevel;
1,264,807✔
529
        }
530

531
        priorLink = link;
3,680,680✔
532
    }
533

534
    return SBTrue;
861,949✔
535
}
536

537
static SBBoolean ProcessRun(ParagraphContextRef context, const LevelRunRef levelRun, SBBoolean forceFinish)
1,264,807✔
538
{
539
    RunQueueRef queue = &context->runQueue;
1,264,807✔
540

541
    if (!RunQueueEnqueue(queue, levelRun)) {
1,264,807✔
UNCOV
542
        return SBFalse;
×
543
    }
544

545
    if (queue->shouldDequeue || forceFinish) {
1,264,807✔
546
        IsolatingRunRef isolatingRun = &context->isolatingRun;
857,287✔
547

548
        /* Rule X10 */
549
        for (; queue->count != 0; RunQueueDequeue(queue)) {
2,122,094✔
550
            LevelRunRef front = RunQueueGetFront(queue);
1,264,807✔
551

552
            if (RunKindIsAttachedTerminating(front->kind)) {
1,264,807✔
553
                continue;
7,758✔
554
            }
555

556
            isolatingRun->baseLevelRun = front;
1,257,049✔
557

558
            if (!IsolatingRunResolve(isolatingRun)) {
1,257,049✔
UNCOV
559
                return SBFalse;
×
560
            }
561
        }
562
    }
563

564
    return SBTrue;
1,264,807✔
565
}
566

567
static void SaveLevels(BidiChainRef chain, SBLevel *levels, SBLevel baseLevel)
861,949✔
568
{
569
    BidiLink roller = chain->roller;
861,949✔
570
    BidiLink link;
571

572
    SBUInteger index = 0;
861,949✔
573
    SBLevel level = baseLevel;
861,949✔
574

575
    BidiChainForEach(chain, roller, link) {
4,460,953✔
576
        SBUInteger offset = BidiChainGetOffset(chain, link);
3,599,004✔
577

578
        for (; index < offset; index++) {
7,366,130✔
579
            levels[index] = level;
3,767,126✔
580
        }
581

582
        level = BidiChainGetLevel(chain, link);
3,599,004✔
583
    }
584
}
861,949✔
585

586
static SBBoolean ResolveParagraph(SBMutableParagraphRef paragraph,
861,949✔
587
    SBAlgorithmRef algorithm, SBUInteger offset, SBUInteger length, SBLevel baseLevel)
588
{
589
    const SBBidiType *bidiTypes = algorithm->fixedTypes + offset;
861,949✔
590
    SBBoolean isSucceeded = SBFalse;
861,949✔
591
    ParagraphContext context;
861,949✔
592

593
    if (InitializeParagraphContext(&context, bidiTypes, paragraph->fixedLevels, length)) {
861,949✔
594
        SBLevel resolvedLevel = DetermineParagraphLevel(&context.bidiChain, baseLevel);
861,949✔
595

596
        SB_LOG_BLOCK_OPENER("Determined Paragraph Level");
597
        SB_LOG_STATEMENT("Base Level", 1, SB_LOG_LEVEL(resolvedLevel));
598
        SB_LOG_BLOCK_CLOSER();
599

600
        context.isolatingRun.codepointSequence = &algorithm->codepointSequence;
861,949✔
601
        context.isolatingRun.bidiTypes = bidiTypes;
861,949✔
602
        context.isolatingRun.bidiChain = &context.bidiChain;
861,949✔
603
        context.isolatingRun.paragraphOffset = offset;
861,949✔
604
        context.isolatingRun.paragraphLevel = resolvedLevel;
861,949✔
605

606
        if (DetermineLevels(&context, resolvedLevel)) {
861,949✔
607
            SaveLevels(&context.bidiChain, ++paragraph->fixedLevels, resolvedLevel);
861,949✔
608

609
            SB_LOG_BLOCK_OPENER("Determined Embedding Levels");
610
            SB_LOG_STATEMENT("Levels", 1, SB_LOG_LEVELS_ARRAY(paragraph->fixedLevels, length));
611
            SB_LOG_BLOCK_CLOSER();
612

613
            paragraph->algorithm = SBAlgorithmRetain(algorithm);
861,949✔
614
            paragraph->refTypes = bidiTypes;
861,949✔
615
            paragraph->offset = offset;
861,949✔
616
            paragraph->length = length;
861,949✔
617
            paragraph->baseLevel = resolvedLevel;
861,949✔
618

619
            isSucceeded = SBTrue;
861,949✔
620
        }
621

622
        FinalizeParagraphContext(&context);
861,949✔
623
    }
624

625
    return isSucceeded;
861,949✔
626
}
627

628
SB_INTERNAL SBParagraphRef SBParagraphCreate(SBAlgorithmRef algorithm,
861,949✔
629
    SBUInteger paragraphOffset, SBUInteger suggestedLength, SBLevel baseLevel)
630
{
631
    const SBCodepointSequence *codepointSequence = &algorithm->codepointSequence;
861,949✔
632
    SBUInteger stringLength = codepointSequence->stringLength;
861,949✔
633
    SBUInteger actualLength;
634

635
    SBMutableParagraphRef paragraph;
636

637
    /* The given range MUST be valid. */
638
    SBAssert(SBUIntegerVerifyRange(stringLength, paragraphOffset, suggestedLength) && suggestedLength > 0);
861,949✔
639

640
    SB_LOG_BLOCK_OPENER("Paragraph Input");
641
    SB_LOG_STATEMENT("Paragraph Offset", 1, SB_LOG_NUMBER(paragraphOffset));
642
    SB_LOG_STATEMENT("Suggested Length", 1, SB_LOG_NUMBER(suggestedLength));
643
    SB_LOG_STATEMENT("Base Direction",   1, SB_LOG_BASE_LEVEL(baseLevel));
644
    SB_LOG_BLOCK_CLOSER();
645

646
    actualLength = DetermineBoundary(algorithm, paragraphOffset, suggestedLength);
861,949✔
647

648
    SB_LOG_BLOCK_OPENER("Determined Paragraph Boundary");
649
    SB_LOG_STATEMENT("Actual Length", 1, SB_LOG_NUMBER(actualLength));
650
    SB_LOG_BLOCK_CLOSER();
651

652
    paragraph = AllocateParagraph(actualLength);
861,949✔
653

654
    if (paragraph) {
861,949✔
655
        if (ResolveParagraph(paragraph, algorithm, paragraphOffset, actualLength, baseLevel)) {
861,949✔
656
            return paragraph;
861,949✔
657
        }
658

UNCOV
659
        ObjectRelease(paragraph);
×
660
    }
661

662
    SB_LOG_BREAKER();
663

UNCOV
664
    return NULL;
×
665
}
666

667
SBUInteger SBParagraphGetOffset(SBParagraphRef paragraph)
1✔
668
{
669
    return paragraph->offset;
1✔
670
}
671

672
SBUInteger SBParagraphGetLength(SBParagraphRef paragraph)
1✔
673
{
674
    return paragraph->length;
1✔
675
}
676

677
SBLevel SBParagraphGetBaseLevel(SBParagraphRef paragraph)
861,948✔
678
{
679
    return paragraph->baseLevel;
861,948✔
680
}
681

682
const SBLevel *SBParagraphGetLevelsPtr(SBParagraphRef paragraph)
1✔
683
{
684
    return paragraph->fixedLevels;
1✔
685
}
686

687
SBLineRef SBParagraphCreateLine(SBParagraphRef paragraph, SBUInteger lineOffset, SBUInteger lineLength)
861,948✔
688
{
689
    SBUInteger paragraphOffset = paragraph->offset;
861,948✔
690
    SBUInteger paragraphLength = paragraph->length;
861,948✔
691
    SBUInteger paragraphLimit = paragraphOffset + paragraphLength;
861,948✔
692
    SBUInteger lineLimit = lineOffset + lineLength;
861,948✔
693

694
    if (lineOffset < lineLimit && lineOffset >= paragraphOffset && lineLimit <= paragraphLimit) {
861,948✔
695
        return SBLineCreate(paragraph, lineOffset, lineLength);
861,948✔
696
    }
697

UNCOV
698
    return NULL;
×
699
}
700

UNCOV
701
SBParagraphRef SBParagraphRetain(SBParagraphRef paragraph)
×
702
{
703
    return ObjectRetain((ObjectRef)paragraph);
×
704
}
705

706
void SBParagraphRelease(SBParagraphRef paragraph)
861,949✔
707
{
708
    ObjectRelease((ObjectRef)paragraph);
861,949✔
709
}
861,949✔
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