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 "unicode/utypes.h"
18 #include "unicode/putil.h"
19 #include "charstr.h"
20 #include "cmemory.h"
21 #include "cstring.h"
22 #include "uinvchar.h"
23 #include "ustr_imp.h"
24 
25 U_NAMESPACE_BEGIN
26 
CharString(CharString && src)27 CharString::CharString(CharString&& src) U_NOEXCEPT
28         : buffer(std::move(src.buffer)), len(src.len) {
29     src.len = 0;  // not strictly necessary because we make no guarantees on the source string
30 }
31 
operator =(CharString && src)32 CharString& CharString::operator=(CharString&& src) U_NOEXCEPT {
33     buffer = std::move(src.buffer);
34     len = src.len;
35     src.len = 0;  // not strictly necessary because we make no guarantees on the source string
36     return *this;
37 }
38 
cloneData(UErrorCode & errorCode) const39 char *CharString::cloneData(UErrorCode &errorCode) const {
40     if (U_FAILURE(errorCode)) { return nullptr; }
41     char *p = static_cast<char *>(uprv_malloc(len + 1));
42     if (p == nullptr) {
43         errorCode = U_MEMORY_ALLOCATION_ERROR;
44         return nullptr;
45     }
46     uprv_memcpy(p, buffer.getAlias(), len + 1);
47     return p;
48 }
49 
extract(char * dest,int32_t capacity,UErrorCode & errorCode) const50 int32_t CharString::extract(char *dest, int32_t capacity, UErrorCode &errorCode) const {
51     if (U_FAILURE(errorCode)) { return len; }
52     if (capacity < 0 || (capacity > 0 && dest == nullptr)) {
53         errorCode = U_ILLEGAL_ARGUMENT_ERROR;
54         return len;
55     }
56     const char *src = buffer.getAlias();
57     if (0 < len && len <= capacity && src != dest) {
58         uprv_memcpy(dest, src, len);
59     }
60     return u_terminateChars(dest, capacity, len, &errorCode);
61 }
62 
copyFrom(const CharString & s,UErrorCode & errorCode)63 CharString &CharString::copyFrom(const CharString &s, UErrorCode &errorCode) {
64     if(U_SUCCESS(errorCode) && this!=&s && ensureCapacity(s.len+1, 0, errorCode)) {
65         len=s.len;
66         uprv_memcpy(buffer.getAlias(), s.buffer.getAlias(), len+1);
67     }
68     return *this;
69 }
70 
lastIndexOf(char c) const71 int32_t CharString::lastIndexOf(char c) const {
72     for(int32_t i=len; i>0;) {
73         if(buffer[--i]==c) {
74             return i;
75         }
76     }
77     return -1;
78 }
79 
contains(StringPiece s) const80 bool CharString::contains(StringPiece s) const {
81     if (s.empty()) { return false; }
82     const char *p = buffer.getAlias();
83     int32_t lastStart = len - s.length();
84     for (int32_t i = 0; i <= lastStart; ++i) {
85         if (uprv_memcmp(p + i, s.data(), s.length()) == 0) {
86             return true;
87         }
88     }
89     return false;
90 }
91 
truncate(int32_t newLength)92 CharString &CharString::truncate(int32_t newLength) {
93     if(newLength<0) {
94         newLength=0;
95     }
96     if(newLength<len) {
97         buffer[len=newLength]=0;
98     }
99     return *this;
100 }
101 
append(char c,UErrorCode & errorCode)102 CharString &CharString::append(char c, UErrorCode &errorCode) {
103     if(ensureCapacity(len+2, 0, errorCode)) {
104         buffer[len++]=c;
105         buffer[len]=0;
106     }
107     return *this;
108 }
109 
append(const char * s,int32_t sLength,UErrorCode & errorCode)110 CharString &CharString::append(const char *s, int32_t sLength, UErrorCode &errorCode) {
111     if(U_FAILURE(errorCode)) {
112         return *this;
113     }
114     if(sLength<-1 || (s==NULL && sLength!=0)) {
115         errorCode=U_ILLEGAL_ARGUMENT_ERROR;
116         return *this;
117     }
118     if(sLength<0) {
119         sLength= static_cast<int32_t>(uprv_strlen(s));
120     }
121     if(sLength>0) {
122         if(s==(buffer.getAlias()+len)) {
123             // The caller wrote into the getAppendBuffer().
124             if(sLength>=(buffer.getCapacity()-len)) {
125                 // The caller wrote too much.
126                 errorCode=U_INTERNAL_PROGRAM_ERROR;
127             } else {
128                 buffer[len+=sLength]=0;
129             }
130         } else if(buffer.getAlias()<=s && s<(buffer.getAlias()+len) &&
131                   sLength>=(buffer.getCapacity()-len)
132         ) {
133             // (Part of) this string is appended to itself which requires reallocation,
134             // so we have to make a copy of the substring and append that.
135             return append(CharString(s, sLength, errorCode), errorCode);
136         } else if(ensureCapacity(len+sLength+1, 0, errorCode)) {
137             uprv_memcpy(buffer.getAlias()+len, s, sLength);
138             buffer[len+=sLength]=0;
139         }
140     }
141     return *this;
142 }
143 
getAppendBuffer(int32_t minCapacity,int32_t desiredCapacityHint,int32_t & resultCapacity,UErrorCode & errorCode)144 char *CharString::getAppendBuffer(int32_t minCapacity,
145                                   int32_t desiredCapacityHint,
146                                   int32_t &resultCapacity,
147                                   UErrorCode &errorCode) {
148     if(U_FAILURE(errorCode)) {
149         resultCapacity=0;
150         return NULL;
151     }
152     int32_t appendCapacity=buffer.getCapacity()-len-1;  // -1 for NUL
153     if(appendCapacity>=minCapacity) {
154         resultCapacity=appendCapacity;
155         return buffer.getAlias()+len;
156     }
157     if(ensureCapacity(len+minCapacity+1, len+desiredCapacityHint+1, errorCode)) {
158         resultCapacity=buffer.getCapacity()-len-1;
159         return buffer.getAlias()+len;
160     }
161     resultCapacity=0;
162     return NULL;
163 }
164 
appendInvariantChars(const UnicodeString & s,UErrorCode & errorCode)165 CharString &CharString::appendInvariantChars(const UnicodeString &s, UErrorCode &errorCode) {
166     return appendInvariantChars(s.getBuffer(), s.length(), errorCode);
167 }
168 
appendInvariantChars(const UChar * uchars,int32_t ucharsLen,UErrorCode & errorCode)169 CharString &CharString::appendInvariantChars(const UChar* uchars, int32_t ucharsLen, UErrorCode &errorCode) {
170     if(U_FAILURE(errorCode)) {
171         return *this;
172     }
173     if (!uprv_isInvariantUString(uchars, ucharsLen)) {
174         errorCode = U_INVARIANT_CONVERSION_ERROR;
175         return *this;
176     }
177     if(ensureCapacity(len+ucharsLen+1, 0, errorCode)) {
178         u_UCharsToChars(uchars, buffer.getAlias()+len, ucharsLen);
179         len += ucharsLen;
180         buffer[len] = 0;
181     }
182     return *this;
183 }
184 
ensureCapacity(int32_t capacity,int32_t desiredCapacityHint,UErrorCode & errorCode)185 UBool CharString::ensureCapacity(int32_t capacity,
186                                  int32_t desiredCapacityHint,
187                                  UErrorCode &errorCode) {
188     if(U_FAILURE(errorCode)) {
189         return FALSE;
190     }
191     if(capacity>buffer.getCapacity()) {
192         if(desiredCapacityHint==0) {
193             desiredCapacityHint=capacity+buffer.getCapacity();
194         }
195         if( (desiredCapacityHint<=capacity || buffer.resize(desiredCapacityHint, len+1)==NULL) &&
196             buffer.resize(capacity, len+1)==NULL
197         ) {
198             errorCode=U_MEMORY_ALLOCATION_ERROR;
199             return FALSE;
200         }
201     }
202     return TRUE;
203 }
204 
appendPathPart(StringPiece s,UErrorCode & errorCode)205 CharString &CharString::appendPathPart(StringPiece s, UErrorCode &errorCode) {
206     if(U_FAILURE(errorCode)) {
207         return *this;
208     }
209     if(s.length()==0) {
210         return *this;
211     }
212     char c;
213     if(len>0 && (c=buffer[len-1])!=U_FILE_SEP_CHAR && c!=U_FILE_ALT_SEP_CHAR) {
214         append(getDirSepChar(), errorCode);
215     }
216     append(s, errorCode);
217     return *this;
218 }
219 
ensureEndsWithFileSeparator(UErrorCode & errorCode)220 CharString &CharString::ensureEndsWithFileSeparator(UErrorCode &errorCode) {
221     char c;
222     if(U_SUCCESS(errorCode) && len>0 &&
223             (c=buffer[len-1])!=U_FILE_SEP_CHAR && c!=U_FILE_ALT_SEP_CHAR) {
224         append(getDirSepChar(), errorCode);
225     }
226     return *this;
227 }
228 
getDirSepChar() const229 char CharString::getDirSepChar() const {
230     char dirSepChar = U_FILE_SEP_CHAR;
231 #if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR)
232     // We may need to return a different directory separator when building for Cygwin or MSYS2.
233     if(len>0 && !uprv_strchr(data(), U_FILE_SEP_CHAR) && uprv_strchr(data(), U_FILE_ALT_SEP_CHAR))
234         dirSepChar = U_FILE_ALT_SEP_CHAR;
235 #endif
236     return dirSepChar;
237 }
238 
239 U_NAMESPACE_END
240