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

Tehreer / SheenBidi / 15192346582

22 May 2025 04:13PM UTC coverage: 79.545% (+1.0%) from 78.546%
15192346582

push

github

mta452
[make] Set C++ standard version to 14

1400 of 1760 relevant lines covered (79.55%)

868008.5 hits per line

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

96.58
/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
#include <SBConfig.h>
18
#include <stddef.h>
19

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

35
typedef struct _ParagraphContext {
36
    Object object;
37
    BidiChain bidiChain;
38
    StatusStack statusStack;
39
    RunQueue runQueue;
40
    IsolatingRun isolatingRun;
41
} ParagraphContext, *ParagraphContextRef;
42

43
static void PopulateBidiChain(BidiChainRef chain, const SBBidiType *types, SBUInteger length);
44
static SBBoolean ProcessRun(ParagraphContextRef context, const LevelRunRef levelRun, SBBoolean forceFinish);
45

46
#define PARAGRAPH_CONTEXT 0
47
#define BIDI_LINKS        1
48
#define BIDI_TYPES        2
49
#define BIDI_FLAGS        3
50
#define COUNT             4
51

52
static ParagraphContextRef CreateParagraphContext(const SBBidiType *types, SBLevel *levels, SBUInteger length)
861,949✔
53
{
54
    void *pointers[COUNT] = { NULL };
861,949✔
55
    SBUInteger sizes[COUNT];
861,949✔
56

57
    sizes[PARAGRAPH_CONTEXT] = sizeof(ParagraphContext);
861,949✔
58
    sizes[BIDI_LINKS]        = sizeof(BidiLink) * (length + 2);
861,949✔
59
    sizes[BIDI_TYPES]        = sizeof(SBBidiType) * (length + 2);
861,949✔
60
    sizes[BIDI_FLAGS]        = sizeof(BidiFlag) * (length + 2);
861,949✔
61

62
    if (ObjectCreate(sizes, COUNT, pointers)) {
861,949✔
63
        ParagraphContextRef context = pointers[PARAGRAPH_CONTEXT];
861,949✔
64
        BidiLink *fixedLinks = pointers[BIDI_LINKS];
861,949✔
65
        SBBidiType *fixedTypes = pointers[BIDI_TYPES];
861,949✔
66
        BidiFlag *fixedFlags = pointers[BIDI_FLAGS];
861,949✔
67

68
        BidiChainInitialize(&context->bidiChain, fixedTypes, levels, fixedFlags, fixedLinks);
861,949✔
69
        StatusStackInitialize(&context->statusStack);
861,949✔
70
        RunQueueInitialize(&context->runQueue);
861,949✔
71
        IsolatingRunInitialize(&context->isolatingRun);
861,949✔
72

73
        PopulateBidiChain(&context->bidiChain, types, length);
861,949✔
74
    }
75

76
    return pointers[PARAGRAPH_CONTEXT];
861,949✔
77
}
78

79
#undef PARAGRAPH_CONTEXT
80
#undef BIDI_LINKS
81
#undef BIDI_TYPES
82
#undef BIDI_FLAGS
83
#undef COUNT
84

85
static void DisposeParagraphContext(ParagraphContextRef context)
861,949✔
86
{
87
    StatusStackFinalize(&context->statusStack);
861,949✔
88
    RunQueueFinalize(&context->runQueue);
861,949✔
89
    IsolatingRunFinalize(&context->isolatingRun);
861,949✔
90
    ObjectDispose(&context->object);
861,949✔
91
}
861,949✔
92

93
#define PARAGRAPH 0
94
#define LEVELS    1
95
#define COUNT     2
96

97
static SBParagraphRef AllocateParagraph(SBUInteger length)
861,949✔
98
{
99
    void *pointers[COUNT] = { NULL };
861,949✔
100
    SBUInteger sizes[COUNT];
861,949✔
101

102
    sizes[PARAGRAPH] = sizeof(SBParagraph);
861,949✔
103
    sizes[LEVELS]    = sizeof(SBLevel) * (length + 2);
861,949✔
104

105
    if (ObjectCreate(sizes, COUNT, pointers)) {
861,949✔
106
        SBParagraphRef paragraph = pointers[PARAGRAPH];
861,949✔
107
        SBLevel *levels = pointers[LEVELS];
861,949✔
108

109
        paragraph->fixedLevels = levels;
861,949✔
110
    }
111

112
    return pointers[PARAGRAPH];
861,949✔
113
}
114

115
#undef PARAGRAPH
116
#undef LEVELS
117
#undef COUNT
118

119
static void DisposeParagraph(SBParagraphRef paragraph)
861,949✔
120
{
121
    ObjectDispose(&paragraph->_object);
861,949✔
122
}
861,949✔
123

124
static SBUInteger DetermineBoundary(SBAlgorithmRef algorithm, SBUInteger paragraphOffset, SBUInteger suggestedLength)
861,949✔
125
{
126
    SBBidiType *bidiTypes = algorithm->fixedTypes;
861,949✔
127
    SBUInteger suggestedLimit = paragraphOffset + suggestedLength;
861,949✔
128
    SBUInteger stringIndex;
129

130
    for (stringIndex = paragraphOffset; stringIndex < suggestedLimit; stringIndex++) {
4,595,608✔
131
        if (bidiTypes[stringIndex] == SBBidiTypeB) {
3,767,125✔
132
            stringIndex += SBAlgorithmGetSeparatorLength(algorithm, stringIndex);
33,466✔
133
            goto Return;
33,466✔
134
        }
135
    }
136

137
Return:
828,483✔
138
    return (stringIndex - paragraphOffset);
861,949✔
139
}
140

141
static void PopulateBidiChain(BidiChainRef chain, const SBBidiType *types, SBUInteger length)
861,949✔
142
{
143
    SBBidiType type = SBBidiTypeNil;
861,949✔
144
    SBUInteger priorIndex = SBInvalidIndex;
861,949✔
145
    SBUInteger index;
146

147
    for (index = 0; index < length; index++) {
4,595,608✔
148
        SBBidiType priorType = type;
3,767,125✔
149
        type = types[index];
3,767,125✔
150

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

166
            if (type == SBBidiTypeB) {
1,898,882✔
167
                index = length;
33,466✔
168
                goto AddLast;
33,466✔
169
            }
170
            break;
1,865,416✔
171

172
        default:
1,868,243✔
173
            if (type != priorType) {
1,868,243✔
174
                BidiChainAdd(chain, type, index - priorIndex);
1,811,748✔
175
                priorIndex = index;
1,811,748✔
176
            }
177
            break;
1,868,243✔
178
        }
179
    }
180

181
AddLast:
828,483✔
182
    BidiChainAdd(chain, SBBidiTypeNil, index - priorIndex);
861,949✔
183
}
861,949✔
184

185
static BidiLink SkipIsolatingRun(BidiChainRef chain, BidiLink skipLink, BidiLink breakLink)
114,000✔
186
{
187
    BidiLink link = skipLink;
114,000✔
188
    SBUInteger depth = 1;
114,000✔
189

190
    while ((link = BidiChainGetNext(chain, link)) != breakLink) {
393,217✔
191
        SBBidiType type = BidiChainGetType(chain, link);
286,636✔
192

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

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

208
    return BidiLinkNone;
106,581✔
209
}
210

211
static SBLevel DetermineBaseLevel(BidiChainRef chain, BidiLink skipLink, BidiLink breakLink, SBLevel defaultLevel, SBBoolean isIsolate)
393,629✔
212
{
213
    BidiLink link = skipLink;
393,629✔
214

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

219
        switch (type) {
997,684✔
220
        case SBBidiTypeL:
38,111✔
221
            return 0;
38,111✔
222

223
        case SBBidiTypeAL:
76,102✔
224
        case SBBidiTypeR:
225
            return 1;
76,102✔
226

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

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

249
Default:
165,439✔
250
    return defaultLevel;
279,416✔
251
}
252

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

261
    return baseLevel;
605,174✔
262
}
263

264
static SBBoolean DetermineLevels(ParagraphContextRef context, SBLevel baseLevel)
861,949✔
265
{
266
    BidiChainRef chain = &context->bidiChain;
861,949✔
267
    StatusStackRef stack = &context->statusStack;
861,949✔
268
    BidiLink roller = chain->roller;
861,949✔
269
    BidiLink link;
270

271
    BidiLink priorLink;
272
    BidiLink firstLink;
273
    BidiLink lastLink;
274

275
    SBLevel priorLevel;
276
    SBBidiType sor;
277
    SBBidiType eor;
278

279
    SBUInteger overIsolate;
280
    SBUInteger overEmbedding;
281
    SBUInteger validIsolate;
282

283
    priorLink = chain->roller;
861,949✔
284
    firstLink = BidiLinkNone;
861,949✔
285
    lastLink = BidiLinkNone;
861,949✔
286

287
    priorLevel = baseLevel;
861,949✔
288
    sor = SBBidiTypeNil;
861,949✔
289

290
    /* Rule X1 */
291
    overIsolate = 0;
861,949✔
292
    overEmbedding = 0;
861,949✔
293
    validIsolate = 0;
861,949✔
294

295
    StatusStackPush(stack, baseLevel, SBBidiTypeON, SBFalse);
861,949✔
296

297
    BidiChainForEach(chain, roller, link) {
5,434,528✔
298
        SBBoolean forceFinish = SBFalse;
4,572,579✔
299
        SBBoolean bnEquivalent = SBFalse;
4,572,579✔
300
        SBBidiType type;
301

302
        type = BidiChainGetType(chain, link);
4,572,579✔
303

304
#define LeastGreaterOddLevel()                                              \
305
(                                                                           \
306
        (StatusStackGetEmbeddingLevel(stack) + 1) | 1                       \
307
)
308

309
#define LeastGreaterEvenLevel()                                             \
310
(                                                                           \
311
        (StatusStackGetEmbeddingLevel(stack) + 2) & ~1                      \
312
)
313

314
#define MergeLinkIfNeeded()                                                 \
315
{                                                                           \
316
        if (BidiChainMergeNext(chain, priorLink)) {                         \
317
            continue;                                                       \
318
        }                                                                   \
319
}
320

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

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

362
        switch (type) {
4,572,579✔
363
        /* Rule X2 */
364
        case SBBidiTypeRLE:
136,944✔
365
            PushEmbedding(LeastGreaterOddLevel(), SBBidiTypeON);
136,944✔
366
            break;
136,944✔
367

368
        /* Rule X3 */
369
        case SBBidiTypeLRE:
138,300✔
370
            PushEmbedding(LeastGreaterEvenLevel(), SBBidiTypeON);
138,300✔
371
            break;
138,300✔
372

373
        /* Rule X4 */
374
        case SBBidiTypeRLO:
136,946✔
375
            PushEmbedding(LeastGreaterOddLevel(), SBBidiTypeR);
136,946✔
376
            break;
136,946✔
377

378
        /* Rule X5 */
379
        case SBBidiTypeLRO:
137,222✔
380
            PushEmbedding(LeastGreaterEvenLevel(), SBBidiTypeL);
137,222✔
381
            break;
137,222✔
382

383
        /* Rule X5a */
384
        case SBBidiTypeRLI:
136,981✔
385
            PushIsolate(LeastGreaterOddLevel(), SBBidiTypeON);
136,981✔
386
            break;
132,214✔
387

388
        /* Rule X5b */
389
        case SBBidiTypeLRI:
136,998✔
390
            PushIsolate(LeastGreaterEvenLevel(), SBBidiTypeON);
136,998✔
391
            break;
132,228✔
392

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

401
        /* Rule X6 */
402
        default:
2,310,442✔
403
            BidiChainSetLevel(chain, link, StatusStackGetEmbeddingLevel(stack));
2,310,442✔
404

405
            if (StatusStackGetOverrideStatus(stack) != SBBidiTypeON) {
2,310,442✔
406
                BidiChainSetType(chain, link, StatusStackGetOverrideStatus(stack));
168,189✔
407
                MergeLinkIfNeeded();
168,189✔
408
            }
409
            break;
2,257,149✔
410

411
        /* Rule X6a */
412
        case SBBidiTypePDI:
137,326✔
413
        {
414
            SBBidiType overrideStatus;
415

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

423
                while (!StatusStackGetIsolateStatus(stack)) {
28,093✔
424
                    StatusStackPop(stack);
3,275✔
425
                }
426
                StatusStackPop(stack);
24,818✔
427

428
                validIsolate -= 1;
24,818✔
429
            }
430

431
            BidiChainSetLevel(chain, link, StatusStackGetEmbeddingLevel(stack));
137,326✔
432
            overrideStatus = StatusStackGetOverrideStatus(stack);
137,326✔
433

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

441
        /* Rule X7 */
442
        case SBBidiTypePDF:
137,005✔
443
            bnEquivalent = SBTrue;
137,005✔
444

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

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

463
            overIsolate = 0;
33,466✔
464
            overEmbedding = 0;
33,466✔
465
            validIsolate = 0;
33,466✔
466

467
            BidiChainSetLevel(chain, link, baseLevel);
33,466✔
468
            break;
33,466✔
469

470
        case SBBidiTypeBN:
132,146✔
471
            bnEquivalent = SBTrue;
132,146✔
472
            break;
132,146✔
473

474
        case SBBidiTypeNil:
861,949✔
475
            forceFinish = SBTrue;
861,949✔
476
            BidiChainSetLevel(chain, link, baseLevel);
861,949✔
477
            break;
861,949✔
478
        }
479

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

487
        if (sor == SBBidiTypeNil) {
3,680,680✔
488
            sor = SBLevelAsNormalBidiType(SBNumberGetMax(baseLevel, BidiChainGetLevel(chain, link)));
861,949✔
489
            firstLink = link;
861,949✔
490
            priorLevel = BidiChainGetLevel(chain, link);
861,949✔
491
        } else if (priorLevel != BidiChainGetLevel(chain, link) || forceFinish) {
2,818,731✔
492
            LevelRun levelRun;
1,264,807✔
493
            SBLevel currentLevel;
494

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

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

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

510
            if (!ProcessRun(context, &levelRun, forceFinish)) {
1,264,807✔
511
                return SBFalse;
×
512
            }
513

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

519
            priorLevel = currentLevel;
1,264,807✔
520
        }
521

522
        priorLink = link;
3,680,680✔
523
    }
524

525
    return SBTrue;
861,949✔
526
}
527

528
static SBBoolean ProcessRun(ParagraphContextRef context, const LevelRunRef levelRun, SBBoolean forceFinish)
1,264,807✔
529
{
530
    RunQueueRef queue = &context->runQueue;
1,264,807✔
531

532
    if (!RunQueueEnqueue(queue, levelRun)) {
1,264,807✔
533
        return SBFalse;
×
534
    }
535

536
    if (queue->shouldDequeue || forceFinish) {
1,264,807✔
537
        IsolatingRunRef isolatingRun = &context->isolatingRun;
857,287✔
538
        LevelRunRef peek;
539

540
        /* Rule X10 */
541
        for (; queue->count != 0; RunQueueDequeue(queue)) {
2,122,094✔
542
            peek = queue->peek;
1,264,807✔
543
            if (RunKindIsAttachedTerminating(peek->kind)) {
1,264,807✔
544
                continue;
7,758✔
545
            }
546

547
            isolatingRun->baseLevelRun = peek;
1,257,049✔
548

549
            if (!IsolatingRunResolve(isolatingRun)) {
1,257,049✔
550
                return SBFalse;
×
551
            }
552
        }
553
    }
554

555
    return SBTrue;
1,264,807✔
556
}
557

558
static void SaveLevels(BidiChainRef chain, SBLevel *levels, SBLevel baseLevel)
861,949✔
559
{
560
    BidiLink roller = chain->roller;
861,949✔
561
    BidiLink link;
562

563
    SBUInteger index = 0;
861,949✔
564
    SBLevel level = baseLevel;
861,949✔
565

566
    BidiChainForEach(chain, roller, link) {
4,460,953✔
567
        SBUInteger offset = BidiChainGetOffset(chain, link);
3,599,004✔
568

569
        for (; index < offset; index++) {
7,366,130✔
570
            levels[index] = level;
3,767,126✔
571
        }
572

573
        level = BidiChainGetLevel(chain, link);
3,599,004✔
574
    }
575
}
861,949✔
576

577
static SBBoolean ResolveParagraph(SBParagraphRef paragraph,
861,949✔
578
    SBAlgorithmRef algorithm, SBUInteger offset, SBUInteger length, SBLevel baseLevel)
579
{
580
    const SBBidiType *bidiTypes = algorithm->fixedTypes + offset;
861,949✔
581
    SBBoolean isSucceeded = SBFalse;
861,949✔
582
    ParagraphContextRef context;
583
    SBLevel resolvedLevel;
584

585
    context = CreateParagraphContext(bidiTypes, paragraph->fixedLevels, length);
861,949✔
586

587
    if (context) {
861,949✔
588
        resolvedLevel = DetermineParagraphLevel(&context->bidiChain, baseLevel);
861,949✔
589

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

594
        context->isolatingRun.codepointSequence = &algorithm->codepointSequence;
861,949✔
595
        context->isolatingRun.bidiTypes = bidiTypes;
861,949✔
596
        context->isolatingRun.bidiChain = &context->bidiChain;
861,949✔
597
        context->isolatingRun.paragraphOffset = offset;
861,949✔
598
        context->isolatingRun.paragraphLevel = resolvedLevel;
861,949✔
599

600
        if (DetermineLevels(context, resolvedLevel)) {
861,949✔
601
            SaveLevels(&context->bidiChain, ++paragraph->fixedLevels, resolvedLevel);
861,949✔
602

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

607
            paragraph->algorithm = SBAlgorithmRetain(algorithm);
861,949✔
608
            paragraph->refTypes = bidiTypes;
861,949✔
609
            paragraph->offset = offset;
861,949✔
610
            paragraph->length = length;
861,949✔
611
            paragraph->baseLevel = resolvedLevel;
861,949✔
612
            paragraph->retainCount = 1;
861,949✔
613

614
            isSucceeded = SBTrue;
861,949✔
615
        }
616

617
        DisposeParagraphContext(context);
861,949✔
618
    }
619

620
    return isSucceeded;
861,949✔
621
}
622

623
SB_INTERNAL SBParagraphRef SBParagraphCreate(SBAlgorithmRef algorithm,
861,949✔
624
    SBUInteger paragraphOffset, SBUInteger suggestedLength, SBLevel baseLevel)
625
{
626
    const SBCodepointSequence *codepointSequence = &algorithm->codepointSequence;
861,949✔
627
    SBUInteger stringLength = codepointSequence->stringLength;
861,949✔
628
    SBUInteger actualLength;
629

630
    SBParagraphRef paragraph;
631

632
    /* The given range MUST be valid. */
633
    SBAssert(SBUIntegerVerifyRange(stringLength, paragraphOffset, suggestedLength) && suggestedLength > 0);
861,949✔
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(algorithm, paragraphOffset, suggestedLength);
861,949✔
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);
861,949✔
648

649
    if (paragraph) {
861,949✔
650
        if (ResolveParagraph(paragraph, algorithm, paragraphOffset, actualLength, baseLevel)) {
861,949✔
651
            return paragraph;
861,949✔
652
        }
653

654
        DisposeParagraph(paragraph);
×
655
    }
656

657
    SB_LOG_BREAKER();
658

659
    return NULL;
×
660
}
661

662
SBUInteger SBParagraphGetOffset(SBParagraphRef paragraph)
1✔
663
{
664
    return paragraph->offset;
1✔
665
}
666

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

672
SBLevel SBParagraphGetBaseLevel(SBParagraphRef paragraph)
861,948✔
673
{
674
    return paragraph->baseLevel;
861,948✔
675
}
676

677
const SBLevel *SBParagraphGetLevelsPtr(SBParagraphRef paragraph)
1✔
678
{
679
    return paragraph->fixedLevels;
1✔
680
}
681

682
SBLineRef SBParagraphCreateLine(SBParagraphRef paragraph, SBUInteger lineOffset, SBUInteger lineLength)
861,948✔
683
{
684
    SBUInteger paragraphOffset = paragraph->offset;
861,948✔
685
    SBUInteger paragraphLength = paragraph->length;
861,948✔
686
    SBUInteger paragraphLimit = paragraphOffset + paragraphLength;
861,948✔
687
    SBUInteger lineLimit = lineOffset + lineLength;
861,948✔
688

689
    if (lineOffset < lineLimit && lineOffset >= paragraphOffset && lineLimit <= paragraphLimit) {
861,948✔
690
        return SBLineCreate(paragraph, lineOffset, lineLength);
861,948✔
691
    }
692

693
    return NULL;
×
694
}
695

696
SBParagraphRef SBParagraphRetain(SBParagraphRef paragraph)
×
697
{
698
    if (paragraph) {
×
699
        paragraph->retainCount += 1;
×
700
    }
701
    
702
    return paragraph;
×
703
}
704

705
void SBParagraphRelease(SBParagraphRef paragraph)
861,949✔
706
{
707
    if (paragraph && --paragraph->retainCount == 0) {
861,949✔
708
        SBAlgorithmRelease(paragraph->algorithm);
861,949✔
709
        DisposeParagraph(paragraph);
861,949✔
710
    }
711
}
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