• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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