Line data Source code
1 : // © 2016 and later: Unicode, Inc. and others.
2 : // License & terms of use: http://www.unicode.org/copyright.html
3 : /*
4 : **********************************************************************
5 : * Copyright (C) 2014, International Business Machines
6 : * Corporation and others. All Rights Reserved.
7 : **********************************************************************
8 : *
9 : * scriptset.cpp
10 : *
11 : * created on: 2013 Jan 7
12 : * created by: Andy Heninger
13 : */
14 :
15 : #include "unicode/utypes.h"
16 :
17 : #include "unicode/uchar.h"
18 : #include "unicode/unistr.h"
19 :
20 : #include "scriptset.h"
21 : #include "uassert.h"
22 : #include "cmemory.h"
23 :
24 : U_NAMESPACE_BEGIN
25 :
26 : //----------------------------------------------------------------------------
27 : //
28 : // ScriptSet implementation
29 : //
30 : //----------------------------------------------------------------------------
31 0 : ScriptSet::ScriptSet() {
32 0 : for (uint32_t i=0; i<UPRV_LENGTHOF(bits); i++) {
33 0 : bits[i] = 0;
34 : }
35 0 : }
36 :
37 0 : ScriptSet::~ScriptSet() {
38 0 : }
39 :
40 0 : ScriptSet::ScriptSet(const ScriptSet &other) {
41 0 : *this = other;
42 0 : }
43 :
44 :
45 0 : ScriptSet & ScriptSet::operator =(const ScriptSet &other) {
46 0 : for (uint32_t i=0; i<UPRV_LENGTHOF(bits); i++) {
47 0 : bits[i] = other.bits[i];
48 : }
49 0 : return *this;
50 : }
51 :
52 :
53 0 : UBool ScriptSet::operator == (const ScriptSet &other) const {
54 0 : for (uint32_t i=0; i<UPRV_LENGTHOF(bits); i++) {
55 0 : if (bits[i] != other.bits[i]) {
56 0 : return FALSE;
57 : }
58 : }
59 0 : return TRUE;
60 : }
61 :
62 0 : UBool ScriptSet::test(UScriptCode script, UErrorCode &status) const {
63 0 : if (U_FAILURE(status)) {
64 0 : return FALSE;
65 : }
66 0 : if (script < 0 || script >= (int32_t)sizeof(bits) * 8) {
67 0 : status = U_ILLEGAL_ARGUMENT_ERROR;
68 0 : return FALSE;
69 : }
70 0 : uint32_t index = script / 32;
71 0 : uint32_t bit = 1 << (script & 31);
72 0 : return ((bits[index] & bit) != 0);
73 : }
74 :
75 :
76 0 : ScriptSet &ScriptSet::set(UScriptCode script, UErrorCode &status) {
77 0 : if (U_FAILURE(status)) {
78 0 : return *this;
79 : }
80 0 : if (script < 0 || script >= (int32_t)sizeof(bits) * 8) {
81 0 : status = U_ILLEGAL_ARGUMENT_ERROR;
82 0 : return *this;
83 : }
84 0 : uint32_t index = script / 32;
85 0 : uint32_t bit = 1 << (script & 31);
86 0 : bits[index] |= bit;
87 0 : return *this;
88 : }
89 :
90 0 : ScriptSet &ScriptSet::reset(UScriptCode script, UErrorCode &status) {
91 0 : if (U_FAILURE(status)) {
92 0 : return *this;
93 : }
94 0 : if (script < 0 || script >= (int32_t)sizeof(bits) * 8) {
95 0 : status = U_ILLEGAL_ARGUMENT_ERROR;
96 0 : return *this;
97 : }
98 0 : uint32_t index = script / 32;
99 0 : uint32_t bit = 1 << (script & 31);
100 0 : bits[index] &= ~bit;
101 0 : return *this;
102 : }
103 :
104 :
105 :
106 0 : ScriptSet &ScriptSet::Union(const ScriptSet &other) {
107 0 : for (uint32_t i=0; i<UPRV_LENGTHOF(bits); i++) {
108 0 : bits[i] |= other.bits[i];
109 : }
110 0 : return *this;
111 : }
112 :
113 0 : ScriptSet &ScriptSet::intersect(const ScriptSet &other) {
114 0 : for (uint32_t i=0; i<UPRV_LENGTHOF(bits); i++) {
115 0 : bits[i] &= other.bits[i];
116 : }
117 0 : return *this;
118 : }
119 :
120 0 : ScriptSet &ScriptSet::intersect(UScriptCode script, UErrorCode &status) {
121 0 : ScriptSet t;
122 0 : t.set(script, status);
123 0 : if (U_SUCCESS(status)) {
124 0 : this->intersect(t);
125 : }
126 0 : return *this;
127 : }
128 :
129 0 : UBool ScriptSet::intersects(const ScriptSet &other) const {
130 0 : for (uint32_t i=0; i<UPRV_LENGTHOF(bits); i++) {
131 0 : if ((bits[i] & other.bits[i]) != 0) {
132 0 : return true;
133 : }
134 : }
135 0 : return false;
136 : }
137 :
138 0 : UBool ScriptSet::contains(const ScriptSet &other) const {
139 0 : ScriptSet t(*this);
140 0 : t.intersect(other);
141 0 : return (t == other);
142 : }
143 :
144 :
145 0 : ScriptSet &ScriptSet::setAll() {
146 0 : for (uint32_t i=0; i<UPRV_LENGTHOF(bits); i++) {
147 0 : bits[i] = 0xffffffffu;
148 : }
149 0 : return *this;
150 : }
151 :
152 :
153 0 : ScriptSet &ScriptSet::resetAll() {
154 0 : for (uint32_t i=0; i<UPRV_LENGTHOF(bits); i++) {
155 0 : bits[i] = 0;
156 : }
157 0 : return *this;
158 : }
159 :
160 0 : int32_t ScriptSet::countMembers() const {
161 : // This bit counter is good for sparse numbers of '1's, which is
162 : // very much the case that we will usually have.
163 0 : int32_t count = 0;
164 0 : for (uint32_t i=0; i<UPRV_LENGTHOF(bits); i++) {
165 0 : uint32_t x = bits[i];
166 0 : while (x > 0) {
167 0 : count++;
168 0 : x &= (x - 1); // and off the least significant one bit.
169 : }
170 : }
171 0 : return count;
172 : }
173 :
174 0 : int32_t ScriptSet::hashCode() const {
175 0 : int32_t hash = 0;
176 0 : for (int32_t i=0; i<UPRV_LENGTHOF(bits); i++) {
177 0 : hash ^= bits[i];
178 : }
179 0 : return hash;
180 : }
181 :
182 0 : int32_t ScriptSet::nextSetBit(int32_t fromIndex) const {
183 : // TODO: Wants a better implementation.
184 0 : if (fromIndex < 0) {
185 0 : return -1;
186 : }
187 0 : UErrorCode status = U_ZERO_ERROR;
188 0 : for (int32_t scriptIndex = fromIndex; scriptIndex < (int32_t)sizeof(bits)*8; scriptIndex++) {
189 0 : if (test((UScriptCode)scriptIndex, status)) {
190 0 : return scriptIndex;
191 : }
192 : }
193 0 : return -1;
194 : }
195 :
196 0 : UBool ScriptSet::isEmpty() const {
197 0 : for (uint32_t i=0; i<UPRV_LENGTHOF(bits); i++) {
198 0 : if (bits[i] != 0) {
199 0 : return FALSE;
200 : }
201 : }
202 0 : return TRUE;
203 : }
204 :
205 0 : UnicodeString &ScriptSet::displayScripts(UnicodeString &dest) const {
206 0 : UBool firstTime = TRUE;
207 0 : for (int32_t i = nextSetBit(0); i >= 0; i = nextSetBit(i + 1)) {
208 0 : if (!firstTime) {
209 0 : dest.append((UChar)0x20);
210 : }
211 0 : firstTime = FALSE;
212 0 : const char *scriptName = uscript_getShortName((UScriptCode(i)));
213 0 : dest.append(UnicodeString(scriptName, -1, US_INV));
214 : }
215 0 : return dest;
216 : }
217 :
218 0 : ScriptSet &ScriptSet::parseScripts(const UnicodeString &scriptString, UErrorCode &status) {
219 0 : resetAll();
220 0 : if (U_FAILURE(status)) {
221 0 : return *this;
222 : }
223 0 : UnicodeString oneScriptName;
224 0 : for (int32_t i=0; i<scriptString.length();) {
225 0 : UChar32 c = scriptString.char32At(i);
226 0 : i = scriptString.moveIndex32(i, 1);
227 0 : if (!u_isUWhiteSpace(c)) {
228 0 : oneScriptName.append(c);
229 0 : if (i < scriptString.length()) {
230 0 : continue;
231 : }
232 : }
233 0 : if (oneScriptName.length() > 0) {
234 : char buf[40];
235 0 : oneScriptName.extract(0, oneScriptName.length(), buf, sizeof(buf)-1, US_INV);
236 0 : buf[sizeof(buf)-1] = 0;
237 0 : int32_t sc = u_getPropertyValueEnum(UCHAR_SCRIPT, buf);
238 0 : if (sc == UCHAR_INVALID_CODE) {
239 0 : status = U_ILLEGAL_ARGUMENT_ERROR;
240 : } else {
241 0 : this->set((UScriptCode)sc, status);
242 : }
243 0 : if (U_FAILURE(status)) {
244 0 : return *this;
245 : }
246 0 : oneScriptName.remove();
247 : }
248 : }
249 0 : return *this;
250 : }
251 :
252 0 : void ScriptSet::setScriptExtensions(UChar32 codePoint, UErrorCode& status) {
253 0 : if (U_FAILURE(status)) { return; }
254 : static const int32_t FIRST_GUESS_SCRIPT_CAPACITY = 5;
255 0 : MaybeStackArray<UScriptCode,FIRST_GUESS_SCRIPT_CAPACITY> scripts;
256 0 : UErrorCode internalStatus = U_ZERO_ERROR;
257 0 : int32_t script_count = -1;
258 :
259 : while (TRUE) {
260 0 : script_count = uscript_getScriptExtensions(
261 0 : codePoint, scripts.getAlias(), scripts.getCapacity(), &internalStatus);
262 0 : if (internalStatus == U_BUFFER_OVERFLOW_ERROR) {
263 : // Need to allocate more space
264 0 : if (scripts.resize(script_count) == NULL) {
265 0 : status = U_MEMORY_ALLOCATION_ERROR;
266 0 : return;
267 : }
268 0 : internalStatus = U_ZERO_ERROR;
269 : } else {
270 0 : break;
271 : }
272 : }
273 :
274 : // Check if we failed for some reason other than buffer overflow
275 0 : if (U_FAILURE(internalStatus)) {
276 0 : status = internalStatus;
277 0 : return;
278 : }
279 :
280 : // Load the scripts into the ScriptSet and return
281 0 : for (int32_t i = 0; i < script_count; i++) {
282 0 : this->set(scripts[i], status);
283 0 : if (U_FAILURE(status)) { return; }
284 : }
285 : }
286 :
287 : U_NAMESPACE_END
288 :
289 : U_CAPI UBool U_EXPORT2
290 0 : uhash_equalsScriptSet(const UElement key1, const UElement key2) {
291 0 : icu::ScriptSet *s1 = static_cast<icu::ScriptSet *>(key1.pointer);
292 0 : icu::ScriptSet *s2 = static_cast<icu::ScriptSet *>(key2.pointer);
293 0 : return (*s1 == *s2);
294 : }
295 :
296 : U_CAPI int8_t U_EXPORT2
297 0 : uhash_compareScriptSet(UElement key0, UElement key1) {
298 0 : icu::ScriptSet *s0 = static_cast<icu::ScriptSet *>(key0.pointer);
299 0 : icu::ScriptSet *s1 = static_cast<icu::ScriptSet *>(key1.pointer);
300 0 : int32_t diff = s0->countMembers() - s1->countMembers();
301 0 : if (diff != 0) return diff;
302 0 : int32_t i0 = s0->nextSetBit(0);
303 0 : int32_t i1 = s1->nextSetBit(0);
304 0 : while ((diff = i0-i1) == 0 && i0 > 0) {
305 0 : i0 = s0->nextSetBit(i0+1);
306 0 : i1 = s1->nextSetBit(i1+1);
307 : }
308 0 : return (int8_t)diff;
309 : }
310 :
311 : U_CAPI int32_t U_EXPORT2
312 0 : uhash_hashScriptSet(const UElement key) {
313 0 : icu::ScriptSet *s = static_cast<icu::ScriptSet *>(key.pointer);
314 0 : return s->hashCode();
315 : }
316 :
317 : U_CAPI void U_EXPORT2
318 0 : uhash_deleteScriptSet(void *obj) {
319 0 : icu::ScriptSet *s = static_cast<icu::ScriptSet *>(obj);
320 0 : delete s;
321 0 : }
|