1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "common_components/base/c_string.h"
16 #include "common_components/log/log.h"
17
18 #include <libgen.h>
19
20 namespace common {
21 // The last two characters are units, such as "kb" or "ms".
22 constexpr int8_t LAST_CHARACTERS_SIZE = 2;
23 // nDecimal = ceil(sizeof(int32_t) * 8 * log(2)) = ceil(2.41 * sizeof(int32_t))
24 // consider overhead for sign & '\0' and alignment, replace 2.41 by 4
25 constexpr int8_t MIN_ALIGN_SPACE = 4;
26
CString()27 CString::CString()
28 {
29 str_ = reinterpret_cast<char*>(malloc(capacity_));
30 LOGF_IF(str_ == nullptr) << "CString::Init failed";
31 *str_ = '\0';
32 length_ = 0;
33 }
34
CString(const char * initStr)35 CString::CString(const char* initStr)
36 {
37 if (initStr != nullptr) {
38 size_t initLen = strlen(initStr);
39 while (capacity_ < initLen + 1) {
40 capacity_ <<= 1;
41 }
42 str_ = reinterpret_cast<char*>(malloc(capacity_));
43 LOGF_IF(str_ == nullptr) << "CString::Init failed";
44 if (*initStr != '\0') {
45 LOGF_IF(memcpy_s(str_, capacity_, initStr, initLen) != EOK) << "CString::CString memcpy_s failed";
46 }
47 length_ = initLen;
48 str_[length_] = '\0';
49 }
50 }
51
CString(char c)52 CString::CString(char c)
53 {
54 str_ = reinterpret_cast<char*>(malloc(capacity_));
55 LOGF_IF(str_ == nullptr) << "CString::Init failed";
56 str_[0] = c;
57 str_[1] = '\0';
58 length_ = 1;
59 }
60
61 // nDecimal = ceil(sizeof(int32_t) * 8 * log(2)) = ceil(2.41 * sizeof(int32_t))
62 // consider overhead for sign & '\0' and alignment, replace 2.41 by 4
CString(int32_t number)63 CString::CString(int32_t number)
64 {
65 capacity_ = sizeof(int32_t) * MIN_ALIGN_SPACE;
66 str_ = reinterpret_cast<char*>(malloc(capacity_));
67 LOGF_IF(str_ == nullptr) << "CString::Init failed";
68 int ret = sprintf_s(str_, capacity_, "%d", number);
69 LOGF_IF(ret == -1) << "CString::Init failed";
70 length_ = static_cast<size_t>(ret);
71 }
72
CString(int64_t number)73 CString::CString(int64_t number)
74 {
75 capacity_ = sizeof(int64_t) * MIN_ALIGN_SPACE;
76 str_ = reinterpret_cast<char*>(malloc(capacity_));
77 LOGF_IF(str_ == nullptr) << "CString::Init failed";
78 int ret = sprintf_s(str_, capacity_, "%lld", number);
79 LOGF_IF(ret == -1) << "CString::Init failed";
80 length_ = static_cast<size_t>(ret);
81 }
82
CString(uint32_t number)83 CString::CString(uint32_t number)
84 {
85 capacity_ = sizeof(uint32_t) * MIN_ALIGN_SPACE;
86 str_ = reinterpret_cast<char*>(malloc(capacity_));
87 LOGF_IF(str_ == nullptr) << "CString::Init failed";
88 int ret = sprintf_s(str_, capacity_, "%u", number);
89 LOGF_IF(ret == -1) << "CString::Init failed";
90 length_ = static_cast<size_t>(ret);
91 }
92
CString(uint64_t number)93 CString::CString(uint64_t number)
94 {
95 capacity_ = sizeof(uint64_t) * MIN_ALIGN_SPACE;
96 str_ = reinterpret_cast<char*>(malloc(capacity_));
97 LOGF_IF(str_ == nullptr) << "CString::Init failed";
98 int ret = sprintf_s(str_, capacity_, "%lu", number);
99 LOGF_IF(ret == -1) << "CString::Init failed";
100 length_ = static_cast<size_t>(ret);
101 }
102
CString(const CString & other)103 CString::CString(const CString& other)
104 {
105 size_t initLen = other.Length();
106 while (capacity_ < initLen + 1) {
107 capacity_ <<= 1;
108 }
109 str_ = reinterpret_cast<char*>(malloc(capacity_));
110 LOGF_IF(str_ == nullptr) << "CString::Init failed";
111 if (!other.IsEmpty()) {
112 LOGF_IF(memcpy_s(str_, capacity_, other.Str(), initLen) != EOK) << "CString::CString memcpy_s failed";
113 }
114 length_ = initLen;
115 str_[length_] = '\0';
116 }
117
CString(size_t number,char ch)118 CString::CString(size_t number, char ch)
119 {
120 while (capacity_ < number + 1) {
121 capacity_ <<= 1;
122 }
123 str_ = reinterpret_cast<char*>(malloc(capacity_));
124 LOGF_IF(str_ == nullptr) << "CString::Init failed";
125 LOGF_IF(memset_s(str_, capacity_, ch, number) != EOK) << "CString::CString memset_s failed";
126 length_ = number;
127 str_[length_] = '\0';
128 }
129
operator =(const CString & other)130 CString& CString::operator=(const CString& other)
131 {
132 if (this == &other) {
133 return *this;
134 }
135 size_t initLen = other.Length();
136 while (capacity_ < initLen + 1) {
137 capacity_ <<= 1;
138 }
139 if (str_ != nullptr) {
140 free(str_);
141 }
142 str_ = reinterpret_cast<char*>(malloc(capacity_));
143 LOGF_IF(str_ == nullptr) << "CString::operator= malloc failed";
144 if (!other.IsEmpty()) {
145 LOGF_IF(memcpy_s(str_, capacity_, other.Str(), initLen) != EOK) << "CString::operator= memcpy_s failed";
146 }
147 length_ = initLen;
148 str_[length_] = '\0';
149 return *this;
150 }
151
~CString()152 CString::~CString()
153 {
154 if (str_ != nullptr) {
155 free(str_);
156 str_ = nullptr;
157 }
158 }
159
operator [](size_t index) const160 const char& CString::operator[](size_t index) const
161 {
162 if (index >= length_) {
163 LOG_COMMON(FATAL) << "CString[index] failed index=" << index;
164 }
165 return str_[index];
166 }
167
operator [](size_t index)168 char& CString::operator[](size_t index)
169 {
170 if (index >= length_) {
171 LOG_COMMON(FATAL) << "CString[index] failed index=" << index;
172 }
173 return str_[index];
174 }
175
EnsureSpace(size_t addLen)176 void CString::EnsureSpace(size_t addLen)
177 {
178 if (addLen == 0 || capacity_ >= length_ + addLen + 1) {
179 return;
180 }
181 while (capacity_ < length_ + addLen + 1) {
182 capacity_ <<= 1;
183 }
184 char* newStr = reinterpret_cast<char*>(malloc(capacity_));
185 LOGF_IF(newStr == nullptr) << "CString::Init failed";
186 LOGF_IF(memcpy_s(newStr, capacity_, str_, length_) != EOK) << "CString::EnsureSpace memcpy_s failed";
187 if (str_ != nullptr) {
188 free(str_);
189 }
190 str_ = newStr;
191 }
192
Append(const CString & addStr,size_t addLen)193 CString& CString::Append(const CString& addStr, size_t addLen)
194 {
195 if (addStr.IsEmpty()) {
196 return *this;
197 }
198 if (addLen == 0) {
199 addLen = strlen(addStr.str_);
200 }
201 EnsureSpace(addLen);
202 DCHECK_CC(addLen <= addStr.length_);
203 LOGF_IF(memcpy_s(str_ + length_, capacity_ - length_, addStr.str_, addLen) != EOK) <<
204 "CString::Append memcpy_s failed";
205 length_ += addLen;
206 DCHECK_CC(str_ != nullptr);
207 str_[length_] = '\0';
208 return *this;
209 }
210
Append(const char * addStr,size_t addLen)211 CString& CString::Append(const char* addStr, size_t addLen)
212 {
213 if (addStr == nullptr || *addStr == '\0') {
214 return *this;
215 }
216 if (addLen == 0) {
217 addLen = strlen(addStr);
218 }
219 EnsureSpace(addLen);
220 DCHECK_CC(addLen <= strlen(addStr));
221 LOGF_IF(memcpy_s(str_ + length_, capacity_ - length_, addStr, addLen) != EOK) <<
222 "CString::Append memcpy_s failed";
223 length_ += addLen;
224 str_[length_] = '\0';
225 return *this;
226 }
227
Combine(const char * addStr) const228 CString CString::Combine(const char* addStr) const
229 {
230 CString newStr;
231 newStr.Append(*this);
232 newStr.Append(addStr);
233 return newStr;
234 }
235
Combine(const CString & addStr) const236 CString CString::Combine(const CString& addStr) const
237 {
238 CString newStr;
239 newStr.Append(*this);
240 newStr.Append(addStr);
241 return newStr;
242 }
243
Length() const244 size_t CString::Length() const { return length_; }
245
Str() const246 const char* CString::Str() const noexcept { return str_; }
247
GetStr() const248 char* CString::GetStr() const noexcept { return str_; }
249
Truncate(size_t index)250 CString& CString::Truncate(size_t index)
251 {
252 if (index >= length_) {
253 LOG_COMMON(ERROR) << "CString::Truncate input parameter error";
254 return *this;
255 }
256 length_ = index;
257 str_[index] = '\0';
258 return *this;
259 }
260
Insert(size_t index,const char * addStr)261 CString& CString::Insert(size_t index, const char* addStr)
262 {
263 if (index >= length_ || *addStr == '\0') {
264 LOG_COMMON(ERROR) << "CString::Insert input parameter error";
265 return *this;
266 }
267 CString subStr = SubStr(index);
268 Truncate(index);
269 Append(addStr);
270 Append(subStr);
271 return *this;
272 }
273
Find(const char * subStr,size_t begin) const274 int CString::Find(const char* subStr, size_t begin) const
275 {
276 if (begin >= length_) {
277 LOG_COMMON(ERROR) << "CString::Find input parameter error";
278 return -1;
279 }
280 char* ret = strstr(str_ + begin, subStr);
281 return ret != nullptr ? ret - str_ : -1;
282 }
283
Find(const char subStr,size_t begin) const284 int CString::Find(const char subStr, size_t begin) const
285 {
286 if (begin >= length_) {
287 LOG_COMMON(ERROR) << "CString::Find input parameter error";
288 return -1;
289 }
290 char* ret = strchr(str_ + begin, subStr);
291 return ret != nullptr ? ret - str_ : -1;
292 }
293
RFind(const char * subStr) const294 int CString::RFind(const char* subStr) const
295 {
296 int index = -1;
297 int ret = Find(subStr);
298 while (ret != -1) {
299 index = ret;
300 if (ret + strlen(subStr) == length_) {
301 break;
302 }
303 ret = Find(subStr, ret + strlen(subStr));
304 }
305 return index;
306 }
307
SubStr(size_t index,size_t len) const308 CString CString::SubStr(size_t index, size_t len) const
309 {
310 CString newStr;
311 if (index + len > length_) {
312 LOG_COMMON(ERROR) << "CString::SubStr input parameter error\n";
313 return newStr;
314 }
315 newStr.EnsureSpace(len);
316 if (memcpy_s(newStr.str_, newStr.capacity_, str_ + index, len) != EOK) {
317 LOG_COMMON(ERROR) << "CString::SubStr memcpy_s failed";
318 return newStr;
319 }
320 newStr.length_ = len;
321 DCHECK_CC(newStr.str_ != nullptr);
322 newStr.str_[newStr.length_] = '\0';
323 return newStr;
324 }
325
SubStr(size_t index) const326 CString CString::SubStr(size_t index) const
327 {
328 if (index >= length_) {
329 LOG_COMMON(ERROR) << "CString::SubStr input parameter error\n";
330 return CString();
331 }
332 return SubStr(index, length_ - index);
333 }
334
Split(CString & source,char separator)335 std::vector<CString> CString::Split(CString& source, char separator)
336 {
337 std::vector<CString> tokens;
338 const char s[2] = { separator, '\0' };
339 char* tmpSave = nullptr;
340
341 CString tmp = source;
342 CString token = strtok_s(tmp.str_, s, &tmpSave);
343 while (!token.IsEmpty()) {
344 tokens.push_back(token);
345 token = strtok_s(nullptr, s, &tmpSave);
346 }
347 return tokens;
348 }
349
BaseName(const CString & path)350 char* CString::BaseName(const CString& path) { return basename(path.str_); }
351
352 // helpers for logging one line with no more than 256 characters
FormatString(const char * format,...)353 CString CString::FormatString(const char* format, ...)
354 {
355 constexpr size_t defaultBufferSize = 256;
356 char buf[defaultBufferSize];
357
358 va_list argList;
359 va_start(argList, format);
360 if (vsprintf_s(buf, sizeof(buf), format, argList) == -1) {
361 return "invalid arguments for FormatString";
362 }
363 va_end(argList);
364
365 return CString(buf);
366 }
367
368 // Check whether `s` can convert to a positive number.
IsPosNumber(const CString & s)369 bool CString::IsPosNumber(const CString& s)
370 {
371 if (s.Length() == 0 || s == "+" || s == "0") {
372 return false;
373 }
374 size_t i = 0;
375 char it = s.Str()[i];
376 if (it == '+') {
377 i++;
378 }
379 for (; i < s.Length(); ++i) {
380 it = s.Str()[i];
381 if (it < '0' || it > '9') {
382 return false;
383 }
384 }
385 return true;
386 }
IsPosDecimal(const CString & s)387 bool CString::IsPosDecimal(const CString& s)
388 {
389 if (s.Length() == 0 || s == "+" || s == "0") {
390 return false;
391 }
392 char* endptr;
393 double number = std::strtod(s.str_, &endptr);
394 if (*endptr != '\0') {
395 return false;
396 }
397 if (number > 0) {
398 return true;
399 }
400 return false;
401 }
402 // Check whether `s` can convert to a number.
IsNumber(const CString & s)403 bool CString::IsNumber(const CString& s)
404 {
405 if (s.Length() == 0) {
406 return false;
407 }
408 size_t i = 0;
409 char it = s.Str()[i];
410 if (it == '-') {
411 i++;
412 }
413 for (; i < s.Length(); ++i) {
414 it = s.Str()[i];
415 if (it < '0' || it > '9') {
416 return false;
417 }
418 }
419 return true;
420 }
421
422 // If `s` is "1" or "true" or "TRUE", return true.
423 // Otherwise return false.
ParseFlagFromEnv(const CString & s)424 bool CString::ParseFlagFromEnv(const CString& s) { return s == "1" || s == "true" || s == "TRUE"; }
425
RemoveBlankSpace() const426 CString CString::RemoveBlankSpace() const
427 {
428 size_t index = 0;
429 CString noBlankSpaceStr(this->Str());
430 if (length_ == 0) {
431 return noBlankSpaceStr;
432 }
433 DCHECK_CC(noBlankSpaceStr.str_ != nullptr);
434 for (size_t i = 0; i < length_; i++) {
435 if (str_[i] != ' ') {
436 noBlankSpaceStr.str_[index++] = str_[i];
437 }
438 }
439 noBlankSpaceStr.str_[index] = '\0';
440 noBlankSpaceStr.length_ = index;
441 return noBlankSpaceStr;
442 }
443
ParseSizeFromEnv(const CString & env)444 size_t CString::ParseSizeFromEnv(const CString& env)
445 {
446 size_t tempSize = 0;
447 CString noBlankStr = env.RemoveBlankSpace();
448 size_t len = noBlankStr.Length();
449 if (len <= LAST_CHARACTERS_SIZE) {
450 return 0;
451 }
452 // Split size and unit.
453 CString num = noBlankStr.SubStr(0, len - 2);
454 CString unit = noBlankStr.SubStr(len - 2, 2);
455
456 if (IsPosNumber(num)) {
457 tempSize = std::strtoul(num.Str(), nullptr, 0);
458 } else {
459 return 0;
460 }
461
462 unit.ToLowerCase();
463
464 if (unit == "kb") {
465 return tempSize;
466 } else if (unit == "mb") {
467 tempSize *= 1024; // unit: 1024 * 1KB = 1MB
468 } else if (unit == "gb") {
469 tempSize *= 1024UL * 1024; // unit: 1024 * 1024 * 1KB = 1GB
470 } else {
471 return 0;
472 }
473
474 return tempSize;
475 }
476
ParseTimeFromEnv(const CString & env)477 size_t CString::ParseTimeFromEnv(const CString& env)
478 {
479 size_t tempTime = 0;
480 CString noBlankStr = env.RemoveBlankSpace();
481 size_t len = noBlankStr.Length();
482 if (len <= 1) { // The last one characters are units, such as "s".
483 return 0;
484 }
485 // Split size and unit.
486 CString num = noBlankStr.SubStr(0, len - 1);
487 CString unit = noBlankStr.SubStr(len - 1, 1);
488 if (unit == "s" && IsPosNumber(num)) {
489 tempTime = std::strtoul(num.Str(), nullptr, 0);
490 tempTime *= 1000UL * 1000 * 1000; // unit: 1000 * 1000 * 1000 * 1ns= 1s
491 return tempTime;
492 }
493 num = noBlankStr.SubStr(0, len - LAST_CHARACTERS_SIZE); // The last two characters are units, such as "ms".
494 unit = noBlankStr.SubStr(len - LAST_CHARACTERS_SIZE, LAST_CHARACTERS_SIZE);
495 if (IsPosNumber(num)) {
496 tempTime = std::strtoul(num.Str(), nullptr, 0);
497 } else {
498 return 0;
499 }
500 unit.ToLowerCase();
501 if (unit == "ns") {
502 return tempTime;
503 } else if (unit == "us") {
504 tempTime *= 1000; // unit: 1000 * 1ns= 1us
505 } else if (unit == "ms") {
506 tempTime *= 1000 * 1000; // unit: 1000 * 1000 * 1ns= 1ms
507 } else {
508 return 0;
509 }
510
511 return tempTime;
512 }
513
ParseNumFromEnv(const CString & env)514 int CString::ParseNumFromEnv(const CString& env)
515 {
516 int temp = 0;
517 CString noBlankStr = env.RemoveBlankSpace();
518 if (IsNumber(noBlankStr)) {
519 temp = std::atoi(noBlankStr.Str());
520 } else {
521 return 0;
522 }
523
524 return temp;
525 }
526
ParsePosNumFromEnv(const CString & env)527 size_t CString::ParsePosNumFromEnv(const CString& env)
528 {
529 size_t temp = 0;
530 CString noBlankStr = env.RemoveBlankSpace();
531 if (IsPosNumber(noBlankStr)) {
532 temp = static_cast<size_t>(std::atoi(noBlankStr.Str()));
533 } else {
534 return 0;
535 }
536
537 return temp;
538 }
539
ParsePosDecFromEnv(const CString & env)540 double CString::ParsePosDecFromEnv(const CString& env)
541 {
542 double temp = 0;
543 CString noBlankStr = env.RemoveBlankSpace();
544 if (IsPosDecimal(noBlankStr)) {
545 temp = std::atof(noBlankStr.Str());
546 } else {
547 return 0;
548 }
549 return temp;
550 }
551
Replace(size_t pos,CString cStr)552 void CString::Replace(size_t pos, CString cStr)
553 {
554 size_t repLen = cStr.Length();
555 LOGF_IF(pos + repLen > length_) << "CString::Replace failed, input is too long";
556 LOGF_IF(memcpy_s(str_ + pos, repLen, cStr.Str(), repLen) != EOK) << "CString::Replace memcpy_s failed";
557 }
558
ReplaceAll(CString replacement,CString target)559 void CString::ReplaceAll(CString replacement, CString target)
560 {
561 int index = -1;
562 int ret = Find(target.Str());
563 while (ret != -1) {
564 index = ret;
565 Replace(index, replacement);
566 ret = Find(target.Str(), ret + target.length_);
567 }
568 return;
569 }
570
571 } // namespace common
572