1 // © 2017 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 #include "unicode/utypes.h"
6 #if !UCONFIG_NO_FORMATTING
8 #include "number_stringbuilder.h"
9 #include "unicode/utf16.h"
12 using namespace icu::number
;
13 using namespace icu::number::impl
;
17 // A version of uprv_memcpy that checks for length 0.
18 // By default, uprv_memcpy requires a length of at least 1.
19 inline void uprv_memcpy2(void* dest
, const void* src
, size_t len
) {
21 uprv_memcpy(dest
, src
, len
);
25 // A version of uprv_memmove that checks for length 0.
26 // By default, uprv_memmove requires a length of at least 1.
27 inline void uprv_memmove2(void* dest
, const void* src
, size_t len
) {
29 uprv_memmove(dest
, src
, len
);
35 NumberStringBuilder::NumberStringBuilder() = default;
37 NumberStringBuilder::~NumberStringBuilder() {
39 uprv_free(fChars
.heap
.ptr
);
40 uprv_free(fFields
.heap
.ptr
);
44 NumberStringBuilder::NumberStringBuilder(const NumberStringBuilder
&other
) {
48 NumberStringBuilder
&NumberStringBuilder::operator=(const NumberStringBuilder
&other
) {
49 // Check for self-assignment
54 // Continue with deallocation and copying
56 uprv_free(fChars
.heap
.ptr
);
57 uprv_free(fFields
.heap
.ptr
);
61 int32_t capacity
= other
.getCapacity();
62 if (capacity
> DEFAULT_CAPACITY
) {
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.
71 *this = NumberStringBuilder(); // can't fail
76 fChars
.heap
.capacity
= capacity
;
77 fChars
.heap
.ptr
= newChars
;
78 fFields
.heap
.capacity
= capacity
;
79 fFields
.heap
.ptr
= newFields
;
82 uprv_memcpy2(getCharPtr(), other
.getCharPtr(), sizeof(char16_t) * capacity
);
83 uprv_memcpy2(getFieldPtr(), other
.getFieldPtr(), sizeof(Field
) * capacity
);
86 fLength
= other
.fLength
;
90 int32_t NumberStringBuilder::length() const {
94 int32_t NumberStringBuilder::codePointCount() const {
95 return u_countChar32(getCharPtr() + fZero
, fLength
);
98 UChar32
NumberStringBuilder::getFirstCodePoint() const {
103 U16_GET(getCharPtr() + fZero
, 0, 0, fLength
, cp
);
107 UChar32
NumberStringBuilder::getLastCodePoint() const {
111 int32_t offset
= fLength
;
112 U16_BACK_1(getCharPtr() + fZero
, 0, offset
);
114 U16_GET(getCharPtr() + fZero
, 0, offset
, fLength
, cp
);
118 UChar32
NumberStringBuilder::codePointAt(int32_t index
) const {
120 U16_GET(getCharPtr() + fZero
, 0, index
, fLength
, cp
);
124 UChar32
NumberStringBuilder::codePointBefore(int32_t index
) const {
125 int32_t offset
= index
;
126 U16_BACK_1(getCharPtr() + fZero
, 0, offset
);
128 U16_GET(getCharPtr() + fZero
, 0, offset
, fLength
, cp
);
132 NumberStringBuilder
&NumberStringBuilder::clear() {
133 // TODO: Reset the heap here?
134 fZero
= getCapacity() / 2;
139 int32_t NumberStringBuilder::appendCodePoint(UChar32 codePoint
, Field field
, UErrorCode
&status
) {
140 return insertCodePoint(fLength
, codePoint
, field
, 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
)) {
151 getCharPtr()[position
] = (char16_t) codePoint
;
152 getFieldPtr()[position
] = field
;
154 getCharPtr()[position
] = U16_LEAD(codePoint
);
155 getCharPtr()[position
+ 1] = U16_TRAIL(codePoint
);
156 getFieldPtr()[position
] = getFieldPtr()[position
+ 1] = field
;
161 int32_t NumberStringBuilder::append(const UnicodeString
&unistr
, Field field
, UErrorCode
&status
) {
162 return insert(fLength
, unistr
, field
, 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.
170 } else if (unistr
.length() == 1) {
171 // Fast path: insert using insertCodePoint.
172 return insertCodePoint(index
, unistr
.charAt(0), field
, status
);
174 return insert(index
, unistr
, 0, unistr
.length(), field
, 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
)) {
186 for (int32_t i
= 0; i
< count
; i
++) {
187 getCharPtr()[position
+ i
] = unistr
.charAt(start
+ i
);
188 getFieldPtr()[position
+ i
] = field
;
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
;
201 // Overall, chars need to be added.
202 position
= prepareForInsert(startThis
, count
, status
);
204 // Overall, chars need to be removed or kept the same.
205 position
= remove(startThis
, -count
);
207 if (U_FAILURE(status
)) {
210 for (int32_t i
= 0; i
< otherLength
; i
++) {
211 getCharPtr()[position
+ i
] = unistr
.charAt(startOther
+ i
);
212 getFieldPtr()[position
+ i
] = field
;
217 int32_t NumberStringBuilder::append(const NumberStringBuilder
&other
, UErrorCode
&status
) {
218 return insert(fLength
, other
, status
);
222 NumberStringBuilder::insert(int32_t index
, const NumberStringBuilder
&other
, UErrorCode
&status
) {
223 if (this == &other
) {
224 status
= U_ILLEGAL_ARGUMENT_ERROR
;
227 int32_t count
= other
.fLength
;
229 // Nothing to insert.
232 int32_t position
= prepareForInsert(index
, count
, status
);
233 if (U_FAILURE(status
)) {
236 for (int32_t i
= 0; i
< count
; i
++) {
237 getCharPtr()[position
+ i
] = other
.charAt(i
);
238 getFieldPtr()[position
+ i
] = other
.fieldAt(i
);
243 int32_t NumberStringBuilder::prepareForInsert(int32_t index
, int32_t count
, UErrorCode
&status
) {
244 if (index
== 0 && fZero
- count
>= 0) {
249 } else if (index
== fLength
&& fZero
+ fLength
+ count
< getCapacity()) {
252 return fZero
+ fLength
- count
;
254 // Move chars around and/or allocate more space
255 return prepareForInsertHelper(index
, count
, status
);
259 int32_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;
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) {
273 uprv_free(newFields
);
274 status
= U_MEMORY_ALLOCATION_ERROR
;
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
));
292 uprv_free(oldFields
);
295 fChars
.heap
.ptr
= newChars
;
296 fChars
.heap
.capacity
= newCapacity
;
297 fFields
.heap
.ptr
= newFields
;
298 fFields
.heap
.capacity
= newCapacity
;
302 int32_t newZero
= oldCapacity
/ 2 - (fLength
+ count
) / 2;
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
));
319 return fZero
+ index
;
322 int32_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
));
335 UnicodeString
NumberStringBuilder::toUnicodeString() const {
336 return UnicodeString(getCharPtr() + fZero
, fLength
);
339 const UnicodeString
NumberStringBuilder::toTempUnicodeString() const {
340 // Readonly-alias constructor:
341 return UnicodeString(FALSE
, getCharPtr() + fZero
, fLength
);
344 UnicodeString
NumberStringBuilder::toDebugString() const {
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
) {
354 switch (fieldAt(i
)) {
355 case UNUM_SIGN_FIELD
:
358 case UNUM_INTEGER_FIELD
:
361 case UNUM_FRACTION_FIELD
:
364 case UNUM_EXPONENT_FIELD
:
367 case UNUM_EXPONENT_SIGN_FIELD
:
370 case UNUM_EXPONENT_SYMBOL_FIELD
:
373 case UNUM_DECIMAL_SEPARATOR_FIELD
:
376 case UNUM_GROUPING_SEPARATOR_FIELD
:
379 case UNUM_PERCENT_FIELD
:
382 case UNUM_PERMILL_FIELD
:
385 case UNUM_CURRENCY_FIELD
:
395 sb
.append(u
"]>", -1);
399 const char16_t *NumberStringBuilder::chars() const {
400 return getCharPtr() + fZero
;
403 bool NumberStringBuilder::contentEquals(const NumberStringBuilder
&other
) const {
404 if (fLength
!= other
.fLength
) {
407 for (int32_t i
= 0; i
< fLength
; i
++) {
408 if (charAt(i
) != other
.charAt(i
) || fieldAt(i
) != other
.fieldAt(i
)) {
415 bool NumberStringBuilder::nextFieldPosition(FieldPosition
& fp
, UErrorCode
& status
) const {
416 int32_t rawField
= fp
.getField();
418 if (rawField
== FieldPosition::DONT_CARE
) {
422 if (rawField
< 0 || rawField
>= UNUM_FIELD_COUNT
) {
423 status
= U_ILLEGAL_ARGUMENT_ERROR
;
427 auto field
= static_cast<Field
>(rawField
);
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
];
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
) {
442 fp
.setEndIndex(i
- fZero
);
444 } else if (!seenStart
&& field
== _field
) {
445 fp
.setBeginIndex(i
- fZero
);
448 if (_field
== UNUM_INTEGER_FIELD
|| _field
== UNUM_DECIMAL_SEPARATOR_FIELD
) {
449 fractionStart
= i
- fZero
+ 1;
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
);
463 void 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
);
479 if (U_FAILURE(status
)) {
483 if (current
!= UNUM_FIELD_COUNT
) {
484 fpih
.addAttribute(current
, currentStart
, fLength
);
488 #endif /* #if !UCONFIG_NO_FORMATTING */