/src/icu/source/common/charstr.cpp
Line | Count | Source (jump to first uncovered line) |
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) U_NOEXCEPT |
30 | 0 | : buffer(std::move(src.buffer)), len(src.len) { |
31 | 0 | src.len = 0; // not strictly necessary because we make no guarantees on the source string |
32 | 0 | } |
33 | | |
34 | 0 | CharString& CharString::operator=(CharString&& src) U_NOEXCEPT { |
35 | 0 | buffer = std::move(src.buffer); |
36 | 0 | len = src.len; |
37 | 0 | src.len = 0; // not strictly necessary because we make no guarantees on the source string |
38 | 0 | return *this; |
39 | 0 | } |
40 | | |
41 | 0 | char *CharString::cloneData(UErrorCode &errorCode) const { |
42 | 0 | if (U_FAILURE(errorCode)) { return nullptr; } |
43 | 0 | char *p = static_cast<char *>(uprv_malloc(len + 1)); |
44 | 0 | if (p == nullptr) { |
45 | 0 | errorCode = U_MEMORY_ALLOCATION_ERROR; |
46 | 0 | return nullptr; |
47 | 0 | } |
48 | 0 | uprv_memcpy(p, buffer.getAlias(), len + 1); |
49 | 0 | return p; |
50 | 0 | } |
51 | | |
52 | 0 | int32_t CharString::extract(char *dest, int32_t capacity, UErrorCode &errorCode) const { |
53 | 0 | if (U_FAILURE(errorCode)) { return len; } |
54 | 0 | if (capacity < 0 || (capacity > 0 && dest == nullptr)) { |
55 | 0 | errorCode = U_ILLEGAL_ARGUMENT_ERROR; |
56 | 0 | return len; |
57 | 0 | } |
58 | 0 | const char *src = buffer.getAlias(); |
59 | 0 | if (0 < len && len <= capacity && src != dest) { |
60 | 0 | uprv_memcpy(dest, src, len); |
61 | 0 | } |
62 | 0 | return u_terminateChars(dest, capacity, len, &errorCode); |
63 | 0 | } |
64 | | |
65 | 0 | CharString &CharString::copyFrom(const CharString &s, UErrorCode &errorCode) { |
66 | 0 | if(U_SUCCESS(errorCode) && this!=&s && ensureCapacity(s.len+1, 0, errorCode)) { |
67 | 0 | len=s.len; |
68 | 0 | uprv_memcpy(buffer.getAlias(), s.buffer.getAlias(), len+1); |
69 | 0 | } |
70 | 0 | return *this; |
71 | 0 | } |
72 | | |
73 | 0 | int32_t CharString::lastIndexOf(char c) const { |
74 | 0 | for(int32_t i=len; i>0;) { |
75 | 0 | if(buffer[--i]==c) { |
76 | 0 | return i; |
77 | 0 | } |
78 | 0 | } |
79 | 0 | return -1; |
80 | 0 | } |
81 | | |
82 | 0 | bool CharString::contains(StringPiece s) const { |
83 | 0 | if (s.empty()) { return false; } |
84 | 0 | const char *p = buffer.getAlias(); |
85 | 0 | int32_t lastStart = len - s.length(); |
86 | 0 | for (int32_t i = 0; i <= lastStart; ++i) { |
87 | 0 | if (uprv_memcmp(p + i, s.data(), s.length()) == 0) { |
88 | 0 | return true; |
89 | 0 | } |
90 | 0 | } |
91 | 0 | return false; |
92 | 0 | } |
93 | | |
94 | 0 | CharString &CharString::truncate(int32_t newLength) { |
95 | 0 | if(newLength<0) { |
96 | 0 | newLength=0; |
97 | 0 | } |
98 | 0 | if(newLength<len) { |
99 | 0 | buffer[len=newLength]=0; |
100 | 0 | } |
101 | 0 | return *this; |
102 | 0 | } |
103 | | |
104 | 0 | CharString &CharString::append(char c, UErrorCode &errorCode) { |
105 | 0 | if(ensureCapacity(len+2, 0, errorCode)) { |
106 | 0 | buffer[len++]=c; |
107 | 0 | buffer[len]=0; |
108 | 0 | } |
109 | 0 | return *this; |
110 | 0 | } |
111 | | |
112 | 0 | CharString &CharString::append(const char *s, int32_t sLength, UErrorCode &errorCode) { |
113 | 0 | if(U_FAILURE(errorCode)) { |
114 | 0 | return *this; |
115 | 0 | } |
116 | 0 | if(sLength<-1 || (s==NULL && sLength!=0)) { |
117 | 0 | errorCode=U_ILLEGAL_ARGUMENT_ERROR; |
118 | 0 | return *this; |
119 | 0 | } |
120 | 0 | if(sLength<0) { |
121 | 0 | sLength= static_cast<int32_t>(uprv_strlen(s)); |
122 | 0 | } |
123 | 0 | if(sLength>0) { |
124 | 0 | if(s==(buffer.getAlias()+len)) { |
125 | | // The caller wrote into the getAppendBuffer(). |
126 | 0 | if(sLength>=(buffer.getCapacity()-len)) { |
127 | | // The caller wrote too much. |
128 | 0 | errorCode=U_INTERNAL_PROGRAM_ERROR; |
129 | 0 | } else { |
130 | 0 | buffer[len+=sLength]=0; |
131 | 0 | } |
132 | 0 | } else if(buffer.getAlias()<=s && s<(buffer.getAlias()+len) && |
133 | 0 | sLength>=(buffer.getCapacity()-len) |
134 | 0 | ) { |
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 | 0 | return append(CharString(s, sLength, errorCode), errorCode); |
138 | 0 | } else if(ensureCapacity(len+sLength+1, 0, errorCode)) { |
139 | 0 | uprv_memcpy(buffer.getAlias()+len, s, sLength); |
140 | 0 | buffer[len+=sLength]=0; |
141 | 0 | } |
142 | 0 | } |
143 | 0 | return *this; |
144 | 0 | } |
145 | | |
146 | 0 | CharString &CharString::appendNumber(int32_t number, UErrorCode &status) { |
147 | 0 | if (number < 0) { |
148 | 0 | this->append('-', status); |
149 | 0 | if (U_FAILURE(status)) { |
150 | 0 | return *this; |
151 | 0 | } |
152 | 0 | } |
153 | | |
154 | 0 | if (number == 0) { |
155 | 0 | this->append('0', status); |
156 | 0 | return *this; |
157 | 0 | } |
158 | | |
159 | 0 | int32_t numLen = 0; |
160 | 0 | while (number != 0) { |
161 | 0 | int32_t residue = number % 10; |
162 | 0 | number /= 10; |
163 | 0 | this->append(std::abs(residue) + '0', status); |
164 | 0 | numLen++; |
165 | 0 | if (U_FAILURE(status)) { |
166 | 0 | return *this; |
167 | 0 | } |
168 | 0 | } |
169 | | |
170 | 0 | int32_t start = this->length() - numLen, end = this->length() - 1; |
171 | 0 | while(start < end) { |
172 | 0 | std::swap(this->data()[start++], this->data()[end--]); |
173 | 0 | } |
174 | |
|
175 | 0 | return *this; |
176 | 0 | } |
177 | | |
178 | | char *CharString::getAppendBuffer(int32_t minCapacity, |
179 | | int32_t desiredCapacityHint, |
180 | | int32_t &resultCapacity, |
181 | 0 | UErrorCode &errorCode) { |
182 | 0 | if(U_FAILURE(errorCode)) { |
183 | 0 | resultCapacity=0; |
184 | 0 | return NULL; |
185 | 0 | } |
186 | 0 | int32_t appendCapacity=buffer.getCapacity()-len-1; // -1 for NUL |
187 | 0 | if(appendCapacity>=minCapacity) { |
188 | 0 | resultCapacity=appendCapacity; |
189 | 0 | return buffer.getAlias()+len; |
190 | 0 | } |
191 | 0 | if(ensureCapacity(len+minCapacity+1, len+desiredCapacityHint+1, errorCode)) { |
192 | 0 | resultCapacity=buffer.getCapacity()-len-1; |
193 | 0 | return buffer.getAlias()+len; |
194 | 0 | } |
195 | 0 | resultCapacity=0; |
196 | 0 | return NULL; |
197 | 0 | } |
198 | | |
199 | 0 | CharString &CharString::appendInvariantChars(const UnicodeString &s, UErrorCode &errorCode) { |
200 | 0 | return appendInvariantChars(s.getBuffer(), s.length(), errorCode); |
201 | 0 | } |
202 | | |
203 | 0 | CharString &CharString::appendInvariantChars(const UChar* uchars, int32_t ucharsLen, UErrorCode &errorCode) { |
204 | 0 | if(U_FAILURE(errorCode)) { |
205 | 0 | return *this; |
206 | 0 | } |
207 | 0 | if (!uprv_isInvariantUString(uchars, ucharsLen)) { |
208 | 0 | errorCode = U_INVARIANT_CONVERSION_ERROR; |
209 | 0 | return *this; |
210 | 0 | } |
211 | 0 | if(ensureCapacity(len+ucharsLen+1, 0, errorCode)) { |
212 | 0 | u_UCharsToChars(uchars, buffer.getAlias()+len, ucharsLen); |
213 | 0 | len += ucharsLen; |
214 | 0 | buffer[len] = 0; |
215 | 0 | } |
216 | 0 | return *this; |
217 | 0 | } |
218 | | |
219 | | UBool CharString::ensureCapacity(int32_t capacity, |
220 | | int32_t desiredCapacityHint, |
221 | 0 | UErrorCode &errorCode) { |
222 | 0 | if(U_FAILURE(errorCode)) { |
223 | 0 | return FALSE; |
224 | 0 | } |
225 | 0 | if(capacity>buffer.getCapacity()) { |
226 | 0 | if(desiredCapacityHint==0) { |
227 | 0 | desiredCapacityHint=capacity+buffer.getCapacity(); |
228 | 0 | } |
229 | 0 | if( (desiredCapacityHint<=capacity || buffer.resize(desiredCapacityHint, len+1)==NULL) && |
230 | 0 | buffer.resize(capacity, len+1)==NULL |
231 | 0 | ) { |
232 | 0 | errorCode=U_MEMORY_ALLOCATION_ERROR; |
233 | 0 | return FALSE; |
234 | 0 | } |
235 | 0 | } |
236 | 0 | return TRUE; |
237 | 0 | } |
238 | | |
239 | 0 | CharString &CharString::appendPathPart(StringPiece s, UErrorCode &errorCode) { |
240 | 0 | if(U_FAILURE(errorCode)) { |
241 | 0 | return *this; |
242 | 0 | } |
243 | 0 | if(s.length()==0) { |
244 | 0 | return *this; |
245 | 0 | } |
246 | 0 | char c; |
247 | 0 | if(len>0 && (c=buffer[len-1])!=U_FILE_SEP_CHAR && c!=U_FILE_ALT_SEP_CHAR) { |
248 | 0 | append(getDirSepChar(), errorCode); |
249 | 0 | } |
250 | 0 | append(s, errorCode); |
251 | 0 | return *this; |
252 | 0 | } |
253 | | |
254 | 0 | CharString &CharString::ensureEndsWithFileSeparator(UErrorCode &errorCode) { |
255 | 0 | char c; |
256 | 0 | if(U_SUCCESS(errorCode) && len>0 && |
257 | 0 | (c=buffer[len-1])!=U_FILE_SEP_CHAR && c!=U_FILE_ALT_SEP_CHAR) { |
258 | 0 | append(getDirSepChar(), errorCode); |
259 | 0 | } |
260 | 0 | return *this; |
261 | 0 | } |
262 | | |
263 | 0 | char CharString::getDirSepChar() const { |
264 | 0 | 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 | 0 | return dirSepChar; |
271 | 0 | } |
272 | | |
273 | | U_NAMESPACE_END |