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
11 using namespace icu;
12 using namespace icu::number;
13 using namespace icu::number::impl;
14
15 namespace {
16
17 // A version of uprv_memcpy that checks for length 0.
18 // By default, uprv_memcpy requires a length of at least 1.
uprv_memcpy2(void * dest,const void * src,size_t len)19 inline 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.
uprv_memmove2(void * dest,const void * src,size_t len)27 inline 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
35 NumberStringBuilder::NumberStringBuilder() = default;
36
~NumberStringBuilder()37 NumberStringBuilder::~NumberStringBuilder() {
38 if (fUsingHeap) {
39 uprv_free(fChars.heap.ptr);
40 uprv_free(fFields.heap.ptr);
41 }
42 }
43
NumberStringBuilder(const NumberStringBuilder & other)44 NumberStringBuilder::NumberStringBuilder(const NumberStringBuilder &other) {
45 *this = other;
46 }
47
operator =(const NumberStringBuilder & other)48 NumberStringBuilder &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
length() const90 int32_t NumberStringBuilder::length() const {
91 return fLength;
92 }
93
codePointCount() const94 int32_t NumberStringBuilder::codePointCount() const {
95 return u_countChar32(getCharPtr() + fZero, fLength);
96 }
97
getFirstCodePoint() const98 UChar32 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
getLastCodePoint() const107 UChar32 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
codePointAt(int32_t index) const118 UChar32 NumberStringBuilder::codePointAt(int32_t index) const {
119 UChar32 cp;
120 U16_GET(getCharPtr() + fZero, 0, index, fLength, cp);
121 return cp;
122 }
123
codePointBefore(int32_t index) const124 UChar32 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
clear()132 NumberStringBuilder &NumberStringBuilder::clear() {
133 // TODO: Reset the heap here?
134 fZero = getCapacity() / 2;
135 fLength = 0;
136 return *this;
137 }
138
appendCodePoint(UChar32 codePoint,Field field,UErrorCode & status)139 int32_t NumberStringBuilder::appendCodePoint(UChar32 codePoint, Field field, UErrorCode &status) {
140 return insertCodePoint(fLength, codePoint, field, status);
141 }
142
143 int32_t
insertCodePoint(int32_t index,UChar32 codePoint,Field field,UErrorCode & status)144 NumberStringBuilder::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
append(const UnicodeString & unistr,Field field,UErrorCode & status)161 int32_t NumberStringBuilder::append(const UnicodeString &unistr, Field field, UErrorCode &status) {
162 return insert(fLength, unistr, field, status);
163 }
164
insert(int32_t index,const UnicodeString & unistr,Field field,UErrorCode & status)165 int32_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
178 int32_t
insert(int32_t index,const UnicodeString & unistr,int32_t start,int32_t end,Field field,UErrorCode & status)179 NumberStringBuilder::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
193 int32_t
splice(int32_t startThis,int32_t endThis,const UnicodeString & unistr,int32_t startOther,int32_t endOther,Field field,UErrorCode & status)194 NumberStringBuilder::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
append(const NumberStringBuilder & other,UErrorCode & status)217 int32_t NumberStringBuilder::append(const NumberStringBuilder &other, UErrorCode &status) {
218 return insert(fLength, other, status);
219 }
220
221 int32_t
insert(int32_t index,const NumberStringBuilder & other,UErrorCode & status)222 NumberStringBuilder::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
prepareForInsert(int32_t index,int32_t count,UErrorCode & status)243 int32_t NumberStringBuilder::prepareForInsert(int32_t index, int32_t count, UErrorCode &status) {
244 U_ASSERT(index >= 0);
245 U_ASSERT(index <= fLength);
246 U_ASSERT(count >= 0);
247 if (index == 0 && fZero - count >= 0) {
248 // Append to start
249 fZero -= count;
250 fLength += count;
251 return fZero;
252 } else if (index == fLength && fZero + fLength + count < getCapacity()) {
253 // Append to end
254 fLength += count;
255 return fZero + fLength - count;
256 } else {
257 // Move chars around and/or allocate more space
258 return prepareForInsertHelper(index, count, status);
259 }
260 }
261
prepareForInsertHelper(int32_t index,int32_t count,UErrorCode & status)262 int32_t NumberStringBuilder::prepareForInsertHelper(int32_t index, int32_t count, UErrorCode &status) {
263 int32_t oldCapacity = getCapacity();
264 int32_t oldZero = fZero;
265 char16_t *oldChars = getCharPtr();
266 Field *oldFields = getFieldPtr();
267 if (fLength + count > oldCapacity) {
268 int32_t newCapacity = (fLength + count) * 2;
269 int32_t newZero = newCapacity / 2 - (fLength + count) / 2;
270
271 // C++ note: malloc appears in two places: here and in the assignment operator.
272 auto newChars = static_cast<char16_t *> (uprv_malloc(sizeof(char16_t) * newCapacity));
273 auto newFields = static_cast<Field *>(uprv_malloc(sizeof(Field) * newCapacity));
274 if (newChars == nullptr || newFields == nullptr) {
275 uprv_free(newChars);
276 uprv_free(newFields);
277 status = U_MEMORY_ALLOCATION_ERROR;
278 return -1;
279 }
280
281 // First copy the prefix and then the suffix, leaving room for the new chars that the
282 // caller wants to insert.
283 // C++ note: memcpy is OK because the src and dest do not overlap.
284 uprv_memcpy2(newChars + newZero, oldChars + oldZero, sizeof(char16_t) * index);
285 uprv_memcpy2(newChars + newZero + index + count,
286 oldChars + oldZero + index,
287 sizeof(char16_t) * (fLength - index));
288 uprv_memcpy2(newFields + newZero, oldFields + oldZero, sizeof(Field) * index);
289 uprv_memcpy2(newFields + newZero + index + count,
290 oldFields + oldZero + index,
291 sizeof(Field) * (fLength - index));
292
293 if (fUsingHeap) {
294 uprv_free(oldChars);
295 uprv_free(oldFields);
296 }
297 fUsingHeap = true;
298 fChars.heap.ptr = newChars;
299 fChars.heap.capacity = newCapacity;
300 fFields.heap.ptr = newFields;
301 fFields.heap.capacity = newCapacity;
302 fZero = newZero;
303 fLength += count;
304 } else {
305 int32_t newZero = oldCapacity / 2 - (fLength + count) / 2;
306
307 // C++ note: memmove is required because src and dest may overlap.
308 // First copy the entire string to the location of the prefix, and then move the suffix
309 // to make room for the new chars that the caller wants to insert.
310 uprv_memmove2(oldChars + newZero, oldChars + oldZero, sizeof(char16_t) * fLength);
311 uprv_memmove2(oldChars + newZero + index + count,
312 oldChars + newZero + index,
313 sizeof(char16_t) * (fLength - index));
314 uprv_memmove2(oldFields + newZero, oldFields + oldZero, sizeof(Field) * fLength);
315 uprv_memmove2(oldFields + newZero + index + count,
316 oldFields + newZero + index,
317 sizeof(Field) * (fLength - index));
318
319 fZero = newZero;
320 fLength += count;
321 }
322 return fZero + index;
323 }
324
remove(int32_t index,int32_t count)325 int32_t NumberStringBuilder::remove(int32_t index, int32_t count) {
326 // TODO: Reset the heap here? (If the string after removal can fit on stack?)
327 int32_t position = index + fZero;
328 uprv_memmove2(getCharPtr() + position,
329 getCharPtr() + position + count,
330 sizeof(char16_t) * (fLength - index - count));
331 uprv_memmove2(getFieldPtr() + position,
332 getFieldPtr() + position + count,
333 sizeof(Field) * (fLength - index - count));
334 fLength -= count;
335 return position;
336 }
337
toUnicodeString() const338 UnicodeString NumberStringBuilder::toUnicodeString() const {
339 return UnicodeString(getCharPtr() + fZero, fLength);
340 }
341
toTempUnicodeString() const342 const UnicodeString NumberStringBuilder::toTempUnicodeString() const {
343 // Readonly-alias constructor:
344 return UnicodeString(FALSE, getCharPtr() + fZero, fLength);
345 }
346
toDebugString() const347 UnicodeString NumberStringBuilder::toDebugString() const {
348 UnicodeString sb;
349 sb.append(u"<NumberStringBuilder [", -1);
350 sb.append(toUnicodeString());
351 sb.append(u"] [", -1);
352 for (int i = 0; i < fLength; i++) {
353 if (fieldAt(i) == UNUM_FIELD_COUNT) {
354 sb.append(u'n');
355 } else {
356 char16_t c;
357 switch (fieldAt(i)) {
358 case UNUM_SIGN_FIELD:
359 c = u'-';
360 break;
361 case UNUM_INTEGER_FIELD:
362 c = u'i';
363 break;
364 case UNUM_FRACTION_FIELD:
365 c = u'f';
366 break;
367 case UNUM_EXPONENT_FIELD:
368 c = u'e';
369 break;
370 case UNUM_EXPONENT_SIGN_FIELD:
371 c = u'+';
372 break;
373 case UNUM_EXPONENT_SYMBOL_FIELD:
374 c = u'E';
375 break;
376 case UNUM_DECIMAL_SEPARATOR_FIELD:
377 c = u'.';
378 break;
379 case UNUM_GROUPING_SEPARATOR_FIELD:
380 c = u',';
381 break;
382 case UNUM_PERCENT_FIELD:
383 c = u'%';
384 break;
385 case UNUM_PERMILL_FIELD:
386 c = u'‰';
387 break;
388 case UNUM_CURRENCY_FIELD:
389 c = u'$';
390 break;
391 default:
392 c = u'?';
393 break;
394 }
395 sb.append(c);
396 }
397 }
398 sb.append(u"]>", -1);
399 return sb;
400 }
401
chars() const402 const char16_t *NumberStringBuilder::chars() const {
403 return getCharPtr() + fZero;
404 }
405
contentEquals(const NumberStringBuilder & other) const406 bool NumberStringBuilder::contentEquals(const NumberStringBuilder &other) const {
407 if (fLength != other.fLength) {
408 return false;
409 }
410 for (int32_t i = 0; i < fLength; i++) {
411 if (charAt(i) != other.charAt(i) || fieldAt(i) != other.fieldAt(i)) {
412 return false;
413 }
414 }
415 return true;
416 }
417
nextFieldPosition(FieldPosition & fp,UErrorCode & status) const418 bool NumberStringBuilder::nextFieldPosition(FieldPosition& fp, UErrorCode& status) const {
419 int32_t rawField = fp.getField();
420
421 if (rawField == FieldPosition::DONT_CARE) {
422 return FALSE;
423 }
424
425 if (rawField < 0 || rawField >= UNUM_FIELD_COUNT) {
426 status = U_ILLEGAL_ARGUMENT_ERROR;
427 return FALSE;
428 }
429
430 auto field = static_cast<Field>(rawField);
431
432 bool seenStart = false;
433 int32_t fractionStart = -1;
434 int32_t startIndex = fp.getEndIndex();
435 for (int i = fZero + startIndex; i <= fZero + fLength; i++) {
436 Field _field = UNUM_FIELD_COUNT;
437 if (i < fZero + fLength) {
438 _field = getFieldPtr()[i];
439 }
440 if (seenStart && field != _field) {
441 // Special case: GROUPING_SEPARATOR counts as an INTEGER.
442 if (field == UNUM_INTEGER_FIELD && _field == UNUM_GROUPING_SEPARATOR_FIELD) {
443 continue;
444 }
445 fp.setEndIndex(i - fZero);
446 break;
447 } else if (!seenStart && field == _field) {
448 fp.setBeginIndex(i - fZero);
449 seenStart = true;
450 }
451 if (_field == UNUM_INTEGER_FIELD || _field == UNUM_DECIMAL_SEPARATOR_FIELD) {
452 fractionStart = i - fZero + 1;
453 }
454 }
455
456 // Backwards compatibility: FRACTION needs to start after INTEGER if empty.
457 // Do not return that a field was found, though, since there is not actually a fraction part.
458 if (field == UNUM_FRACTION_FIELD && !seenStart && fractionStart != -1) {
459 fp.setBeginIndex(fractionStart);
460 fp.setEndIndex(fractionStart);
461 }
462
463 return seenStart;
464 }
465
getAllFieldPositions(FieldPositionIteratorHandler & fpih,UErrorCode & status) const466 void NumberStringBuilder::getAllFieldPositions(FieldPositionIteratorHandler& fpih,
467 UErrorCode& status) const {
468 Field current = UNUM_FIELD_COUNT;
469 int32_t currentStart = -1;
470 for (int32_t i = 0; i < fLength; i++) {
471 Field field = fieldAt(i);
472 if (current == UNUM_INTEGER_FIELD && field == UNUM_GROUPING_SEPARATOR_FIELD) {
473 // Special case: GROUPING_SEPARATOR counts as an INTEGER.
474 fpih.addAttribute(UNUM_GROUPING_SEPARATOR_FIELD, i, i + 1);
475 } else if (current != field) {
476 if (current != UNUM_FIELD_COUNT) {
477 fpih.addAttribute(current, currentStart, i);
478 }
479 current = field;
480 currentStart = i;
481 }
482 if (U_FAILURE(status)) {
483 return;
484 }
485 }
486 if (current != UNUM_FIELD_COUNT) {
487 fpih.addAttribute(current, currentStart, fLength);
488 }
489 }
490
containsField(Field field) const491 bool NumberStringBuilder::containsField(Field field) const {
492 for (int32_t i = 0; i < fLength; i++) {
493 if (field == fieldAt(i)) {
494 return true;
495 }
496 }
497 return false;
498 }
499
500 #endif /* #if !UCONFIG_NO_FORMATTING */
501