]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/number_stringbuilder.cpp
ICU-62141.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / number_stringbuilder.cpp
CommitLineData
0f5d89e8
A
1// © 2017 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3
4#include "unicode/utypes.h"
5
6#if !UCONFIG_NO_FORMATTING
7
8#include "number_stringbuilder.h"
9#include "unicode/utf16.h"
10
11using namespace icu;
12using namespace icu::number;
13using namespace icu::number::impl;
14
15namespace {
16
17// A version of uprv_memcpy that checks for length 0.
18// By default, uprv_memcpy requires a length of at least 1.
19inline void uprv_memcpy2(void* dest, const void* src, size_t len) {
20 if (len > 0) {
21 uprv_memcpy(dest, src, len);
22 }
23}
24
25// A version of uprv_memmove that checks for length 0.
26// By default, uprv_memmove requires a length of at least 1.
27inline void uprv_memmove2(void* dest, const void* src, size_t len) {
28 if (len > 0) {
29 uprv_memmove(dest, src, len);
30 }
31}
32
33} // namespace
34
35NumberStringBuilder::NumberStringBuilder() = default;
36
37NumberStringBuilder::~NumberStringBuilder() {
38 if (fUsingHeap) {
39 uprv_free(fChars.heap.ptr);
40 uprv_free(fFields.heap.ptr);
41 }
42}
43
44NumberStringBuilder::NumberStringBuilder(const NumberStringBuilder &other) {
45 *this = other;
46}
47
48NumberStringBuilder &NumberStringBuilder::operator=(const NumberStringBuilder &other) {
49 // Check for self-assignment
50 if (this == &other) {
51 return *this;
52 }
53
54 // Continue with deallocation and copying
55 if (fUsingHeap) {
56 uprv_free(fChars.heap.ptr);
57 uprv_free(fFields.heap.ptr);
58 fUsingHeap = false;
59 }
60
61 int32_t capacity = other.getCapacity();
62 if (capacity > DEFAULT_CAPACITY) {
63 // FIXME: uprv_malloc
64 // C++ note: malloc appears in two places: here and in prepareForInsertHelper.
65 auto newChars = static_cast<char16_t *> (uprv_malloc(sizeof(char16_t) * capacity));
66 auto newFields = static_cast<Field *>(uprv_malloc(sizeof(Field) * capacity));
67 if (newChars == nullptr || newFields == nullptr) {
68 // UErrorCode is not available; fail silently.
69 uprv_free(newChars);
70 uprv_free(newFields);
71 *this = NumberStringBuilder(); // can't fail
72 return *this;
73 }
74
75 fUsingHeap = true;
76 fChars.heap.capacity = capacity;
77 fChars.heap.ptr = newChars;
78 fFields.heap.capacity = capacity;
79 fFields.heap.ptr = newFields;
80 }
81
82 uprv_memcpy2(getCharPtr(), other.getCharPtr(), sizeof(char16_t) * capacity);
83 uprv_memcpy2(getFieldPtr(), other.getFieldPtr(), sizeof(Field) * capacity);
84
85 fZero = other.fZero;
86 fLength = other.fLength;
87 return *this;
88}
89
90int32_t NumberStringBuilder::length() const {
91 return fLength;
92}
93
94int32_t NumberStringBuilder::codePointCount() const {
95 return u_countChar32(getCharPtr() + fZero, fLength);
96}
97
98UChar32 NumberStringBuilder::getFirstCodePoint() const {
99 if (fLength == 0) {
100 return -1;
101 }
102 UChar32 cp;
103 U16_GET(getCharPtr() + fZero, 0, 0, fLength, cp);
104 return cp;
105}
106
107UChar32 NumberStringBuilder::getLastCodePoint() const {
108 if (fLength == 0) {
109 return -1;
110 }
111 int32_t offset = fLength;
112 U16_BACK_1(getCharPtr() + fZero, 0, offset);
113 UChar32 cp;
114 U16_GET(getCharPtr() + fZero, 0, offset, fLength, cp);
115 return cp;
116}
117
118UChar32 NumberStringBuilder::codePointAt(int32_t index) const {
119 UChar32 cp;
120 U16_GET(getCharPtr() + fZero, 0, index, fLength, cp);
121 return cp;
122}
123
124UChar32 NumberStringBuilder::codePointBefore(int32_t index) const {
125 int32_t offset = index;
126 U16_BACK_1(getCharPtr() + fZero, 0, offset);
127 UChar32 cp;
128 U16_GET(getCharPtr() + fZero, 0, offset, fLength, cp);
129 return cp;
130}
131
132NumberStringBuilder &NumberStringBuilder::clear() {
133 // TODO: Reset the heap here?
134 fZero = getCapacity() / 2;
135 fLength = 0;
136 return *this;
137}
138
139int32_t NumberStringBuilder::appendCodePoint(UChar32 codePoint, Field field, UErrorCode &status) {
140 return insertCodePoint(fLength, codePoint, field, status);
141}
142
143int32_t
144NumberStringBuilder::insertCodePoint(int32_t index, UChar32 codePoint, Field field, UErrorCode &status) {
145 int32_t count = U16_LENGTH(codePoint);
146 int32_t position = prepareForInsert(index, count, status);
147 if (U_FAILURE(status)) {
148 return count;
149 }
150 if (count == 1) {
151 getCharPtr()[position] = (char16_t) codePoint;
152 getFieldPtr()[position] = field;
153 } else {
154 getCharPtr()[position] = U16_LEAD(codePoint);
155 getCharPtr()[position + 1] = U16_TRAIL(codePoint);
156 getFieldPtr()[position] = getFieldPtr()[position + 1] = field;
157 }
158 return count;
159}
160
161int32_t NumberStringBuilder::append(const UnicodeString &unistr, Field field, UErrorCode &status) {
162 return insert(fLength, unistr, field, status);
163}
164
165int32_t NumberStringBuilder::insert(int32_t index, const UnicodeString &unistr, Field field,
166 UErrorCode &status) {
167 if (unistr.length() == 0) {
168 // Nothing to insert.
169 return 0;
170 } else if (unistr.length() == 1) {
171 // Fast path: insert using insertCodePoint.
172 return insertCodePoint(index, unistr.charAt(0), field, status);
173 } else {
174 return insert(index, unistr, 0, unistr.length(), field, status);
175 }
176}
177
178int32_t
179NumberStringBuilder::insert(int32_t index, const UnicodeString &unistr, int32_t start, int32_t end,
180 Field field, UErrorCode &status) {
181 int32_t count = end - start;
182 int32_t position = prepareForInsert(index, count, status);
183 if (U_FAILURE(status)) {
184 return count;
185 }
186 for (int32_t i = 0; i < count; i++) {
187 getCharPtr()[position + i] = unistr.charAt(start + i);
188 getFieldPtr()[position + i] = field;
189 }
190 return count;
191}
192
193int32_t
194NumberStringBuilder::splice(int32_t startThis, int32_t endThis, const UnicodeString &unistr,
195 int32_t startOther, int32_t endOther, Field field, UErrorCode& status) {
196 int32_t thisLength = endThis - startThis;
197 int32_t otherLength = endOther - startOther;
198 int32_t count = otherLength - thisLength;
199 int32_t position;
200 if (count > 0) {
201 // Overall, chars need to be added.
202 position = prepareForInsert(startThis, count, status);
203 } else {
204 // Overall, chars need to be removed or kept the same.
205 position = remove(startThis, -count);
206 }
207 if (U_FAILURE(status)) {
208 return count;
209 }
210 for (int32_t i = 0; i < otherLength; i++) {
211 getCharPtr()[position + i] = unistr.charAt(startOther + i);
212 getFieldPtr()[position + i] = field;
213 }
214 return count;
215}
216
217int32_t NumberStringBuilder::append(const NumberStringBuilder &other, UErrorCode &status) {
218 return insert(fLength, other, status);
219}
220
221int32_t
222NumberStringBuilder::insert(int32_t index, const NumberStringBuilder &other, UErrorCode &status) {
223 if (this == &other) {
224 status = U_ILLEGAL_ARGUMENT_ERROR;
225 return 0;
226 }
227 int32_t count = other.fLength;
228 if (count == 0) {
229 // Nothing to insert.
230 return 0;
231 }
232 int32_t position = prepareForInsert(index, count, status);
233 if (U_FAILURE(status)) {
234 return count;
235 }
236 for (int32_t i = 0; i < count; i++) {
237 getCharPtr()[position + i] = other.charAt(i);
238 getFieldPtr()[position + i] = other.fieldAt(i);
239 }
240 return count;
241}
242
243int32_t NumberStringBuilder::prepareForInsert(int32_t index, int32_t count, UErrorCode &status) {
244 if (index == 0 && fZero - count >= 0) {
245 // Append to start
246 fZero -= count;
247 fLength += count;
248 return fZero;
249 } else if (index == fLength && fZero + fLength + count < getCapacity()) {
250 // Append to end
251 fLength += count;
252 return fZero + fLength - count;
253 } else {
254 // Move chars around and/or allocate more space
255 return prepareForInsertHelper(index, count, status);
256 }
257}
258
259int32_t NumberStringBuilder::prepareForInsertHelper(int32_t index, int32_t count, UErrorCode &status) {
260 int32_t oldCapacity = getCapacity();
261 int32_t oldZero = fZero;
262 char16_t *oldChars = getCharPtr();
263 Field *oldFields = getFieldPtr();
264 if (fLength + count > oldCapacity) {
265 int32_t newCapacity = (fLength + count) * 2;
266 int32_t newZero = newCapacity / 2 - (fLength + count) / 2;
267
268 // C++ note: malloc appears in two places: here and in the assignment operator.
269 auto newChars = static_cast<char16_t *> (uprv_malloc(sizeof(char16_t) * newCapacity));
270 auto newFields = static_cast<Field *>(uprv_malloc(sizeof(Field) * newCapacity));
271 if (newChars == nullptr || newFields == nullptr) {
272 uprv_free(newChars);
273 uprv_free(newFields);
274 status = U_MEMORY_ALLOCATION_ERROR;
275 return -1;
276 }
277
278 // First copy the prefix and then the suffix, leaving room for the new chars that the
279 // caller wants to insert.
280 // C++ note: memcpy is OK because the src and dest do not overlap.
281 uprv_memcpy2(newChars + newZero, oldChars + oldZero, sizeof(char16_t) * index);
282 uprv_memcpy2(newChars + newZero + index + count,
283 oldChars + oldZero + index,
284 sizeof(char16_t) * (fLength - index));
285 uprv_memcpy2(newFields + newZero, oldFields + oldZero, sizeof(Field) * index);
286 uprv_memcpy2(newFields + newZero + index + count,
287 oldFields + oldZero + index,
288 sizeof(Field) * (fLength - index));
289
290 if (fUsingHeap) {
291 uprv_free(oldChars);
292 uprv_free(oldFields);
293 }
294 fUsingHeap = true;
295 fChars.heap.ptr = newChars;
296 fChars.heap.capacity = newCapacity;
297 fFields.heap.ptr = newFields;
298 fFields.heap.capacity = newCapacity;
299 fZero = newZero;
300 fLength += count;
301 } else {
302 int32_t newZero = oldCapacity / 2 - (fLength + count) / 2;
303
304 // C++ note: memmove is required because src and dest may overlap.
305 // First copy the entire string to the location of the prefix, and then move the suffix
306 // to make room for the new chars that the caller wants to insert.
307 uprv_memmove2(oldChars + newZero, oldChars + oldZero, sizeof(char16_t) * fLength);
308 uprv_memmove2(oldChars + newZero + index + count,
309 oldChars + newZero + index,
310 sizeof(char16_t) * (fLength - index));
311 uprv_memmove2(oldFields + newZero, oldFields + oldZero, sizeof(Field) * fLength);
312 uprv_memmove2(oldFields + newZero + index + count,
313 oldFields + newZero + index,
314 sizeof(Field) * (fLength - index));
315
316 fZero = newZero;
317 fLength += count;
318 }
319 return fZero + index;
320}
321
322int32_t NumberStringBuilder::remove(int32_t index, int32_t count) {
323 // TODO: Reset the heap here? (If the string after removal can fit on stack?)
324 int32_t position = index + fZero;
325 uprv_memmove2(getCharPtr() + position,
326 getCharPtr() + position + count,
327 sizeof(char16_t) * (fLength - index - count));
328 uprv_memmove2(getFieldPtr() + position,
329 getFieldPtr() + position + count,
330 sizeof(Field) * (fLength - index - count));
331 fLength -= count;
332 return position;
333}
334
335UnicodeString NumberStringBuilder::toUnicodeString() const {
336 return UnicodeString(getCharPtr() + fZero, fLength);
337}
338
339const UnicodeString NumberStringBuilder::toTempUnicodeString() const {
340 // Readonly-alias constructor:
341 return UnicodeString(FALSE, getCharPtr() + fZero, fLength);
342}
343
344UnicodeString NumberStringBuilder::toDebugString() const {
345 UnicodeString sb;
346 sb.append(u"<NumberStringBuilder [", -1);
347 sb.append(toUnicodeString());
348 sb.append(u"] [", -1);
349 for (int i = 0; i < fLength; i++) {
350 if (fieldAt(i) == UNUM_FIELD_COUNT) {
351 sb.append(u'n');
352 } else {
353 char16_t c;
354 switch (fieldAt(i)) {
355 case UNUM_SIGN_FIELD:
356 c = u'-';
357 break;
358 case UNUM_INTEGER_FIELD:
359 c = u'i';
360 break;
361 case UNUM_FRACTION_FIELD:
362 c = u'f';
363 break;
364 case UNUM_EXPONENT_FIELD:
365 c = u'e';
366 break;
367 case UNUM_EXPONENT_SIGN_FIELD:
368 c = u'+';
369 break;
370 case UNUM_EXPONENT_SYMBOL_FIELD:
371 c = u'E';
372 break;
373 case UNUM_DECIMAL_SEPARATOR_FIELD:
374 c = u'.';
375 break;
376 case UNUM_GROUPING_SEPARATOR_FIELD:
377 c = u',';
378 break;
379 case UNUM_PERCENT_FIELD:
380 c = u'%';
381 break;
382 case UNUM_PERMILL_FIELD:
383 c = u'‰';
384 break;
385 case UNUM_CURRENCY_FIELD:
386 c = u'$';
387 break;
388 default:
389 c = u'?';
390 break;
391 }
392 sb.append(c);
393 }
394 }
395 sb.append(u"]>", -1);
396 return sb;
397}
398
399const char16_t *NumberStringBuilder::chars() const {
400 return getCharPtr() + fZero;
401}
402
403bool NumberStringBuilder::contentEquals(const NumberStringBuilder &other) const {
404 if (fLength != other.fLength) {
405 return false;
406 }
407 for (int32_t i = 0; i < fLength; i++) {
408 if (charAt(i) != other.charAt(i) || fieldAt(i) != other.fieldAt(i)) {
409 return false;
410 }
411 }
412 return true;
413}
414
415bool NumberStringBuilder::nextFieldPosition(FieldPosition& fp, UErrorCode& status) const {
416 int32_t rawField = fp.getField();
417
418 if (rawField == FieldPosition::DONT_CARE) {
419 return FALSE;
420 }
421
422 if (rawField < 0 || rawField >= UNUM_FIELD_COUNT) {
423 status = U_ILLEGAL_ARGUMENT_ERROR;
424 return FALSE;
425 }
426
427 auto field = static_cast<Field>(rawField);
428
429 bool seenStart = false;
430 int32_t fractionStart = -1;
431 int32_t startIndex = fp.getEndIndex();
432 for (int i = fZero + startIndex; i <= fZero + fLength; i++) {
433 Field _field = UNUM_FIELD_COUNT;
434 if (i < fZero + fLength) {
435 _field = getFieldPtr()[i];
436 }
437 if (seenStart && field != _field) {
438 // Special case: GROUPING_SEPARATOR counts as an INTEGER.
439 if (field == UNUM_INTEGER_FIELD && _field == UNUM_GROUPING_SEPARATOR_FIELD) {
440 continue;
441 }
442 fp.setEndIndex(i - fZero);
443 break;
444 } else if (!seenStart && field == _field) {
445 fp.setBeginIndex(i - fZero);
446 seenStart = true;
447 }
448 if (_field == UNUM_INTEGER_FIELD || _field == UNUM_DECIMAL_SEPARATOR_FIELD) {
449 fractionStart = i - fZero + 1;
450 }
451 }
452
453 // Backwards compatibility: FRACTION needs to start after INTEGER if empty.
454 // Do not return that a field was found, though, since there is not actually a fraction part.
455 if (field == UNUM_FRACTION_FIELD && !seenStart && fractionStart != -1) {
456 fp.setBeginIndex(fractionStart);
457 fp.setEndIndex(fractionStart);
458 }
459
460 return seenStart;
461}
462
463void NumberStringBuilder::getAllFieldPositions(FieldPositionIteratorHandler& fpih,
464 UErrorCode& status) const {
465 Field current = UNUM_FIELD_COUNT;
466 int32_t currentStart = -1;
467 for (int32_t i = 0; i < fLength; i++) {
468 Field field = fieldAt(i);
469 if (current == UNUM_INTEGER_FIELD && field == UNUM_GROUPING_SEPARATOR_FIELD) {
470 // Special case: GROUPING_SEPARATOR counts as an INTEGER.
471 fpih.addAttribute(UNUM_GROUPING_SEPARATOR_FIELD, i, i + 1);
472 } else if (current != field) {
473 if (current != UNUM_FIELD_COUNT) {
474 fpih.addAttribute(current, currentStart, i);
475 }
476 current = field;
477 currentStart = i;
478 }
479 if (U_FAILURE(status)) {
480 return;
481 }
482 }
483 if (current != UNUM_FIELD_COUNT) {
484 fpih.addAttribute(current, currentStart, fLength);
485 }
486}
487
488#endif /* #if !UCONFIG_NO_FORMATTING */