Coverage Report

Created: 2018-09-25 14:53

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