1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3/*
4*******************************************************************************
5*   Copyright (C) 2010-2015, International Business Machines
6*   Corporation and others.  All Rights Reserved.
7*******************************************************************************
8*   file name:  charstr.cpp
9*   encoding:   UTF-8
10*   tab size:   8 (not used)
11*   indentation:4
12*
13*   created on: 2010may19
14*   created by: Markus W. Scherer
15*/
16
17#include <cstdlib>
18
19#include "unicode/utypes.h"
20#include "unicode/putil.h"
21#include "charstr.h"
22#include "cmemory.h"
23#include "cstring.h"
24#include "uinvchar.h"
25#include "ustr_imp.h"
26
27U_NAMESPACE_BEGIN
28
29CharString::CharString(CharString&& src) noexcept
30        : buffer(std::move(src.buffer)), len(src.len) {
31    src.len = 0;  // not strictly necessary because we make no guarantees on the source string
32}
33
34CharString& CharString::operator=(CharString&& src) noexcept {
35    buffer = std::move(src.buffer);
36    len = src.len;
37    src.len = 0;  // not strictly necessary because we make no guarantees on the source string
38    return *this;
39}
40
41char *CharString::cloneData(UErrorCode &errorCode) const {
42    if (U_FAILURE(errorCode)) { return nullptr; }
43    char *p = static_cast<char *>(uprv_malloc(len + 1));
44    if (p == nullptr) {
45        errorCode = U_MEMORY_ALLOCATION_ERROR;
46        return nullptr;
47    }
48    uprv_memcpy(p, buffer.getAlias(), len + 1);
49    return p;
50}
51
52int32_t CharString::extract(char *dest, int32_t capacity, UErrorCode &errorCode) const {
53    if (U_FAILURE(errorCode)) { return len; }
54    if (capacity < 0 || (capacity > 0 && dest == nullptr)) {
55        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
56        return len;
57    }
58    const char *src = buffer.getAlias();
59    if (0 < len && len <= capacity && src != dest) {
60        uprv_memcpy(dest, src, len);
61    }
62    return u_terminateChars(dest, capacity, len, &errorCode);
63}
64
65CharString &CharString::copyFrom(const CharString &s, UErrorCode &errorCode) {
66    if(U_SUCCESS(errorCode) && this!=&s && ensureCapacity(s.len+1, 0, errorCode)) {
67        len=s.len;
68        uprv_memcpy(buffer.getAlias(), s.buffer.getAlias(), len+1);
69    }
70    return *this;
71}
72
73int32_t CharString::lastIndexOf(char c) const {
74    for(int32_t i=len; i>0;) {
75        if(buffer[--i]==c) {
76            return i;
77        }
78    }
79    return -1;
80}
81
82bool CharString::contains(StringPiece s) const {
83    if (s.empty()) { return false; }
84    const char *p = buffer.getAlias();
85    int32_t lastStart = len - s.length();
86    for (int32_t i = 0; i <= lastStart; ++i) {
87        if (uprv_memcmp(p + i, s.data(), s.length()) == 0) {
88            return true;
89        }
90    }
91    return false;
92}
93
94CharString &CharString::truncate(int32_t newLength) {
95    if(newLength<0) {
96        newLength=0;
97    }
98    if(newLength<len) {
99        buffer[len=newLength]=0;
100    }
101    return *this;
102}
103
104CharString &CharString::append(char c, UErrorCode &errorCode) {
105    if(ensureCapacity(len+2, 0, errorCode)) {
106        buffer[len++]=c;
107        buffer[len]=0;
108    }
109    return *this;
110}
111
112CharString &CharString::append(const char *s, int32_t sLength, UErrorCode &errorCode) {
113    if(U_FAILURE(errorCode)) {
114        return *this;
115    }
116    if(sLength<-1 || (s==nullptr && sLength!=0)) {
117        errorCode=U_ILLEGAL_ARGUMENT_ERROR;
118        return *this;
119    }
120    if(sLength<0) {
121        sLength= static_cast<int32_t>(uprv_strlen(s));
122    }
123    if(sLength>0) {
124        if(s==(buffer.getAlias()+len)) {
125            // The caller wrote into the getAppendBuffer().
126            if(sLength>=(buffer.getCapacity()-len)) {
127                // The caller wrote too much.
128                errorCode=U_INTERNAL_PROGRAM_ERROR;
129            } else {
130                buffer[len+=sLength]=0;
131            }
132        } else if(buffer.getAlias()<=s && s<(buffer.getAlias()+len) &&
133                  sLength>=(buffer.getCapacity()-len)
134        ) {
135            // (Part of) this string is appended to itself which requires reallocation,
136            // so we have to make a copy of the substring and append that.
137            return append(CharString(s, sLength, errorCode), errorCode);
138        } else if(ensureCapacity(len+sLength+1, 0, errorCode)) {
139            uprv_memcpy(buffer.getAlias()+len, s, sLength);
140            buffer[len+=sLength]=0;
141        }
142    }
143    return *this;
144}
145
146CharString &CharString::appendNumber(int32_t number, UErrorCode &status) {
147    if (number < 0) {
148        this->append('-', status);
149        if (U_FAILURE(status)) {
150            return *this;
151        }
152    }
153
154    if (number == 0) {
155        this->append('0', status);
156        return *this;
157    }
158
159    int32_t numLen = 0;
160    while (number != 0) {
161        int32_t residue = number % 10;
162        number /= 10;
163        this->append(std::abs(residue) + '0', status);
164        numLen++;
165        if (U_FAILURE(status)) {
166            return *this;
167        }
168    }
169
170    int32_t start = this->length() - numLen, end = this->length() - 1;
171    while(start < end) {
172        std::swap(this->data()[start++], this->data()[end--]);
173    }
174
175    return *this;
176}
177
178char *CharString::getAppendBuffer(int32_t minCapacity,
179                                  int32_t desiredCapacityHint,
180                                  int32_t &resultCapacity,
181                                  UErrorCode &errorCode) {
182    if(U_FAILURE(errorCode)) {
183        resultCapacity=0;
184        return nullptr;
185    }
186    int32_t appendCapacity=buffer.getCapacity()-len-1;  // -1 for NUL
187    if(appendCapacity>=minCapacity) {
188        resultCapacity=appendCapacity;
189        return buffer.getAlias()+len;
190    }
191    if(ensureCapacity(len+minCapacity+1, len+desiredCapacityHint+1, errorCode)) {
192        resultCapacity=buffer.getCapacity()-len-1;
193        return buffer.getAlias()+len;
194    }
195    resultCapacity=0;
196    return nullptr;
197}
198
199CharString &CharString::appendInvariantChars(const UnicodeString &s, UErrorCode &errorCode) {
200    return appendInvariantChars(s.getBuffer(), s.length(), errorCode);
201}
202
203CharString &CharString::appendInvariantChars(const char16_t* uchars, int32_t ucharsLen, UErrorCode &errorCode) {
204    if(U_FAILURE(errorCode)) {
205        return *this;
206    }
207    if (!uprv_isInvariantUString(uchars, ucharsLen)) {
208        errorCode = U_INVARIANT_CONVERSION_ERROR;
209        return *this;
210    }
211    if(ensureCapacity(len+ucharsLen+1, 0, errorCode)) {
212        u_UCharsToChars(uchars, buffer.getAlias()+len, ucharsLen);
213        len += ucharsLen;
214        buffer[len] = 0;
215    }
216    return *this;
217}
218
219UBool CharString::ensureCapacity(int32_t capacity,
220                                 int32_t desiredCapacityHint,
221                                 UErrorCode &errorCode) {
222    if(U_FAILURE(errorCode)) {
223        return false;
224    }
225    if(capacity>buffer.getCapacity()) {
226        if(desiredCapacityHint==0) {
227            desiredCapacityHint=capacity+buffer.getCapacity();
228        }
229        if( (desiredCapacityHint<=capacity || buffer.resize(desiredCapacityHint, len+1)==nullptr) &&
230            buffer.resize(capacity, len+1)==nullptr
231        ) {
232            errorCode=U_MEMORY_ALLOCATION_ERROR;
233            return false;
234        }
235    }
236    return true;
237}
238
239CharString &CharString::appendPathPart(StringPiece s, UErrorCode &errorCode) {
240    if(U_FAILURE(errorCode)) {
241        return *this;
242    }
243    if(s.length()==0) {
244        return *this;
245    }
246    char c;
247    if(len>0 && (c=buffer[len-1])!=U_FILE_SEP_CHAR && c!=U_FILE_ALT_SEP_CHAR) {
248        append(getDirSepChar(), errorCode);
249    }
250    append(s, errorCode);
251    return *this;
252}
253
254CharString &CharString::ensureEndsWithFileSeparator(UErrorCode &errorCode) {
255    char c;
256    if(U_SUCCESS(errorCode) && len>0 &&
257            (c=buffer[len-1])!=U_FILE_SEP_CHAR && c!=U_FILE_ALT_SEP_CHAR) {
258        append(getDirSepChar(), errorCode);
259    }
260    return *this;
261}
262
263char CharString::getDirSepChar() const {
264    char dirSepChar = U_FILE_SEP_CHAR;
265#if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR)
266    // We may need to return a different directory separator when building for Cygwin or MSYS2.
267    if(len>0 && !uprv_strchr(data(), U_FILE_SEP_CHAR) && uprv_strchr(data(), U_FILE_ALT_SEP_CHAR))
268        dirSepChar = U_FILE_ALT_SEP_CHAR;
269#endif
270    return dirSepChar;
271}
272
273U_NAMESPACE_END
274