1 /*
2 * Copyright (C) 2018-2019 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 <stddef.h>
18 #include <stdlib.h>
19
20 #include "GeneralCategoryLookup.h"
21 #include "PairingLookup.h"
22 #include "SBBase.h"
23 #include "SBCodepointSequence.h"
24 #include "ScriptLookup.h"
25 #include "ScriptStack.h"
26 #include "SBScriptLocator.h"
27
IsSimilarScript(SBScript lhs,SBScript rhs)28 static SBBoolean IsSimilarScript(SBScript lhs, SBScript rhs)
29 {
30 return SBScriptIsCommonOrInherited(lhs)
31 || SBScriptIsCommonOrInherited(rhs)
32 || lhs == rhs;
33 }
34
SBScriptLocatorCreate(void)35 SBScriptLocatorRef SBScriptLocatorCreate(void)
36 {
37 SBScriptLocatorRef locator;
38
39 locator = malloc(sizeof(SBScriptLocator));
40 locator->_codepointSequence.stringEncoding = SBStringEncodingUTF8;
41 locator->_codepointSequence.stringBuffer = NULL;
42 locator->_codepointSequence.stringLength = 0;
43 locator->retainCount = 1;
44 SBScriptLocatorReset(locator);
45
46 return locator;
47 }
48
SBScriptLocatorLoadCodepoints(SBScriptLocatorRef locator,const SBCodepointSequence * codepointSequence)49 void SBScriptLocatorLoadCodepoints(SBScriptLocatorRef locator, const SBCodepointSequence *codepointSequence)
50 {
51 locator->_codepointSequence = *codepointSequence;
52 SBScriptLocatorReset(locator);
53 }
54
SBScriptLocatorGetAgent(SBScriptLocatorRef locator)55 const SBScriptAgent *SBScriptLocatorGetAgent(SBScriptLocatorRef locator)
56 {
57 return &locator->agent;
58 }
59
ResolveScriptRun(SBScriptLocatorRef locator,SBUInteger offset)60 static void ResolveScriptRun(SBScriptLocatorRef locator, SBUInteger offset)
61 {
62 const SBCodepointSequence *sequence = &locator->_codepointSequence;
63 ScriptStackRef stack = &locator->_scriptStack;
64 SBScript result = SBScriptZYYY;
65 SBUInteger current = offset;
66 SBUInteger next = offset;
67 SBCodepoint codepoint;
68
69 /* Iterate over the code points of specified string buffer. */
70 while ((codepoint = SBCodepointSequenceGetCodepointAt(sequence, &next)) != SBCodepointInvalid) {
71 SBBoolean isStacked = SBFalse;
72 SBScript script;
73
74 script = LookupScript(codepoint);
75
76 /* Handle paired punctuations in case of a common script. */
77 if (script == SBScriptZYYY) {
78 SBGeneralCategory generalCategory = LookupGeneralCategory(codepoint);
79
80 /* Check if current code point is an open punctuation. */
81 if (generalCategory == SBGeneralCategoryPS) {
82 SBCodepoint mirror = LookupMirror(codepoint);
83 if (mirror) {
84 /* A closing pair exists for this punctuation, so push it onto the stack. */
85 ScriptStackPush(stack, result, mirror);
86 }
87 }
88 /* Check if current code point is a close punctuation. */
89 else if (generalCategory == SBGeneralCategoryPE) {
90 SBBoolean isMirrored = (LookupMirror(codepoint) != 0);
91 if (isMirrored) {
92 /* Find the matching entry in the stack, while popping the unmatched ones. */
93 while (!ScriptStackIsEmpty(stack)) {
94 SBCodepoint mirror = ScriptStackGetMirror(stack);
95 if (mirror != codepoint) {
96 ScriptStackPop(stack);
97 } else {
98 break;
99 }
100 }
101
102 if (!ScriptStackIsEmpty(stack)) {
103 isStacked = SBTrue;
104 /* Paired punctuation match the script of enclosing text. */
105 script = ScriptStackGetScript(stack);
106 }
107 }
108 }
109 }
110
111 if (IsSimilarScript(result, script)) {
112 if (SBScriptIsCommonOrInherited(result) && !SBScriptIsCommonOrInherited(script)) {
113 /* Set the concrete script of this code point as the result. */
114 result = script;
115 /* Seal the pending punctuations with the result. */
116 ScriptStackSealPairs(stack, result);
117 }
118
119 if (isStacked) {
120 /* Pop the paired punctuation from the stack. */
121 ScriptStackPop(stack);
122 }
123 } else {
124 /* The current code point has a different script, so finish the run. */
125 break;
126 }
127
128 current = next;
129 }
130
131 ScriptStackLeavePairs(stack);
132
133 /* Set the run info in agent. */
134 locator->agent.offset = offset;
135 locator->agent.length = current - offset;
136 locator->agent.script = result;
137 }
138
SBScriptLocatorMoveNext(SBScriptLocatorRef locator)139 SBBoolean SBScriptLocatorMoveNext(SBScriptLocatorRef locator)
140 {
141 SBUInteger offset = locator->agent.offset + locator->agent.length;
142
143 if (offset < locator->_codepointSequence.stringLength) {
144 ResolveScriptRun(locator, offset);
145 return SBTrue;
146 }
147
148 SBScriptLocatorReset(locator);
149 return SBFalse;
150 }
151
SBScriptLocatorReset(SBScriptLocatorRef locator)152 void SBScriptLocatorReset(SBScriptLocatorRef locator)
153 {
154 ScriptStackReset(&locator->_scriptStack);
155 locator->agent.offset = 0;
156 locator->agent.length = 0;
157 locator->agent.script = SBScriptNil;
158 }
159
SBScriptLocatorRetain(SBScriptLocatorRef locator)160 SBScriptLocatorRef SBScriptLocatorRetain(SBScriptLocatorRef locator)
161 {
162 if (locator) {
163 locator->retainCount += 1;
164 }
165
166 return locator;
167 }
168
SBScriptLocatorRelease(SBScriptLocatorRef locator)169 void SBScriptLocatorRelease(SBScriptLocatorRef locator)
170 {
171 if (locator && --locator->retainCount == 0) {
172 free(locator);
173 }
174 }
175