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 
27 U_NAMESPACE_BEGIN
28 
CharString(CharString && src)29 CharString::CharString(CharString&& src) U_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 
operator =(CharString && src)34 CharString& CharString::operator=(CharString&& src) U_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 
cloneData(UErrorCode & errorCode) const41 char *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 
extract(char * dest,int32_t capacity,UErrorCode & errorCode) const52 int32_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 
copyFrom(const CharString & s,UErrorCode & errorCode)65 CharString &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 
lastIndexOf(char c) const73 int32_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 
contains(StringPiece s) const82 bool 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 
truncate(int32_t newLength)94 CharString &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 
append(char c,UErrorCode & errorCode)104 CharString &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 
append(const char * s,int32_t sLength,UErrorCode & errorCode)112 CharString &CharString::append(const char *s, int32_t sLength, UErrorCode &errorCode) {
113     if(U_FAILURE(errorCode)) {
114         return *this;
115     }
116     if(sLength<-1 || (s==NULL && 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 
appendNumber(int32_t number,UErrorCode & status)146 CharString &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 
getAppendBuffer(int32_t minCapacity,int32_t desiredCapacityHint,int32_t & resultCapacity,UErrorCode & errorCode)178 char *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 NULL;
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 NULL;
197 }
198 
appendInvariantChars(const UnicodeString & s,UErrorCode & errorCode)199 CharString &CharString::appendInvariantChars(const UnicodeString &s, UErrorCode &errorCode) {
200     return appendInvariantChars(s.getBuffer(), s.length(), errorCode);
201 }
202 
appendInvariantChars(const UChar * uchars,int32_t ucharsLen,UErrorCode & errorCode)203 CharString &CharString::appendInvariantChars(const UChar* 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 
ensureCapacity(int32_t capacity,int32_t desiredCapacityHint,UErrorCode & errorCode)219 UBool 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)==NULL) &&
230             buffer.resize(capacity, len+1)==NULL
231         ) {
232             errorCode=U_MEMORY_ALLOCATION_ERROR;
233             return FALSE;
234         }
235     }
236     return TRUE;
237 }
238 
appendPathPart(StringPiece s,UErrorCode & errorCode)239 CharString &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 
ensureEndsWithFileSeparator(UErrorCode & errorCode)254 CharString &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 
getDirSepChar() const263 char 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 
273 U_NAMESPACE_END
274