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 | |
29 | CharString::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 | |
34 | CharString& 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 | |
41 | 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 | |
52 | int32_t CharString::(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 | |
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 | |
73 | 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 | |
82 | 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 | |
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 | |
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 | |
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==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 | |
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 | |
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 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 | |
199 | CharString &CharString::appendInvariantChars(const UnicodeString &s, UErrorCode &errorCode) { |
200 | return appendInvariantChars(s.getBuffer(), s.length(), errorCode); |
201 | } |
202 | |
203 | CharString &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 | |
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)==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 | |
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 | |
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 | |
263 | 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 | |