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