1 /* Copyright (C) 2021 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
18 #include "precompiled.h"
20 #ifndef CStr_CPP_FIRST
21 #define CStr_CPP_FIRST
23 #include "lib/fnv_hash.h"
25 #include "lib/byte_order.h"
26 #include "network/Serialization.h"
32 #include <type_traits>
36 // Use a knowingly false expression, as we can't use
37 // static_assert(false, ...) directly, because it's an ill-formed program
38 // with a value (false) which doesn't depend on any input parameter.
39 // We don't use constexpr bool AlwaysFalse = false, because some compilers
40 // complain about an unused constant.
42 struct AlwaysFalse
: std::false_type
45 template<typename StrBase
>
46 using tstringstream
= std::basic_stringstream
<typename
StrBase::value_type
>;
48 template<typename Char
>
49 bool istspace(const Char chr
)
51 if constexpr (std::is_same_v
<Char
, char>)
52 return static_cast<bool>(std::isspace(chr
));
54 return static_cast<bool>(std::iswspace(chr
));
57 template<typename Char
>
58 Char
totlower(const Char chr
)
60 if constexpr (std::is_same_v
<Char
, char>)
61 return std::tolower(chr
);
63 return std::towlower(chr
);
66 template<typename Char
>
67 Char
totupper(const Char chr
)
69 if constexpr (std::is_same_v
<Char
, char>)
70 return std::toupper(chr
);
72 return std::towupper(chr
);
75 template<typename StrBase
>
76 u8
* SerializeImpl(const StrBase
& str
, u8
* buffer
)
78 using Char
= typename
StrBase::value_type
;
80 if constexpr (std::is_same_v
<Char
, char>)
82 // CStr8 is always serialized to / from ASCII(or whatever 8 - bit codepage stored
84 size_t len
= str
.length();
85 Serialize_int_4(buffer
, (u32
)len
);
87 for (i
= 0; i
< len
; i
++)
91 else if constexpr (std::is_same_v
<Char
, wchar_t>)
93 // CStrW is always serialized to / from UTF - 16.
94 size_t len
= str
.length();
96 for (i
= 0; i
< len
; i
++)
98 const u16 bigEndian
= to_be16(str
[i
]);
99 *(u16
*)(buffer
+ i
* 2) = bigEndian
;
101 *(u16
*)(buffer
+ i
* 2) = 0;
102 return buffer
+ len
* 2 + 2;
105 static_assert(AlwaysFalse
<Char
>::value
, "Not implemented.");
108 template<typename StrBase
>
109 const u8
* DeserializeImpl(const u8
* buffer
, const u8
* bufferend
, StrBase
& str
)
111 using Char
= typename
StrBase::value_type
;
114 if constexpr (std::is_same_v
<Char
, char>)
117 Deserialize_int_4(buffer
, len
);
118 if (buffer
+ len
> bufferend
)
120 str
= StrBase(buffer
, buffer
+ len
);
123 else if constexpr (std::is_same_v
<Char
, wchar_t>)
125 const u16
*strend
= (const u16
*)buffer
;
126 while ((const u8
*)strend
< bufferend
&& *strend
)
128 if ((const u8
*)strend
>= bufferend
)
131 str
.resize(strend
- (const u16
*)buffer
);
132 const u16
*ptr
= (const u16
*)buffer
;
134 typename
StrBase::iterator it
= str
.begin();
137 const u16 native
= to_be16(*(ptr
++)); // we want from_be16, but that's the same
138 *(it
++) = (Char
)native
;
141 return (const u8
*)(strend
+ 1);
144 static_assert(AlwaysFalse
<Char
>::value
, "Not implemented.");
147 template<typename StrBase
>
148 size_t GetSerializedLengthImpl(const StrBase
& str
)
150 using Char
= typename
StrBase::value_type
;
151 if constexpr (std::is_same_v
<Char
, char>)
152 return str
.length() + 4;
153 else if constexpr (std::is_same_v
<Char
, wchar_t>)
154 return str
.length() * 2 + 2;
156 static_assert(AlwaysFalse
<Char
>::value
, "Not implemented.");
159 } // anonymous namespace
161 #define UNIDOUBLER_HEADER "CStr.cpp"
162 #include "UniDoubler.h"
165 // Only include these function definitions in the first instance of CStr.cpp:
168 * Convert CStr to UTF-8
170 * @return CStr8 converted string
172 CStr8
CStrW::ToUTF8() const
175 return utf8_from_wstring(*this, &err
);
179 * Convert UTF-8 to CStr
181 * @return CStrW converted string
183 CStrW
CStr8::FromUTF8() const
186 return wstring_from_utf8(*this, &err
);
191 // The following code is compiled twice, as CStrW then as CStr8:
195 CStr
CStr::Repeat(const CStr
& str
, size_t reps
)
198 ret
.reserve(str
.length() * reps
);
199 while (reps
--) ret
+= str
;
203 // Construction from numbers:
205 CStr
CStr::FromInt(int n
)
207 tstringstream
<StrBase
> ss
;
212 CStr
CStr::FromUInt(unsigned int n
)
214 tstringstream
<StrBase
> ss
;
219 CStr
CStr::FromInt64(i64 n
)
221 tstringstream
<StrBase
> ss
;
226 CStr
CStr::FromDouble(double n
)
228 tstringstream
<StrBase
> ss
;
233 // Conversion to numbers:
235 int CStr::ToInt() const
238 tstringstream
<StrBase
> str(*this);
243 unsigned int CStr::ToUInt() const
245 unsigned int ret
= 0;
246 tstringstream
<StrBase
> str(*this);
251 long CStr::ToLong() const
254 tstringstream
<StrBase
> str(*this);
259 unsigned long CStr::ToULong() const
261 unsigned long ret
= 0;
262 tstringstream
<StrBase
> str(*this);
268 * libc++ and libstd++ differ on how they handle string-to-number parsing for floating-points numbers.
269 * See https://trac.wildfiregames.com/ticket/2780#comment:4 for details.
270 * To prevent this, only consider [0-9.-+], replace the others in-place with a neutral character.
272 CStr
ParseableAsNumber(CStr cleaned_copy
)
274 for (CStr::Char
& c
: cleaned_copy
)
275 if (!std::isdigit(c
) && c
!= '.' && c
!= '-' && c
!= '+')
281 float CStr::ToFloat() const
284 tstringstream
<StrBase
> str(ParseableAsNumber(*this));
289 double CStr::ToDouble() const
292 tstringstream
<StrBase
> str(ParseableAsNumber(*this));
297 // Search the string for another string
298 long CStr::Find(const CStr
& str
) const
300 size_t pos
= find(str
, 0);
303 return static_cast<long>(pos
);
308 // Search the string for another string
309 long CStr::Find(const Char chr
) const
311 size_t pos
= find(chr
, 0);
314 return static_cast<long>(pos
);
319 // Search the string for another string
320 long CStr::Find(const int start
, const Char chr
) const
322 size_t pos
= find(chr
, start
);
325 return static_cast<long>(pos
);
330 long CStr::FindInsensitive(const int start
, const Char chr
) const { return LowerCase().Find(start
, totlower(chr
)); }
331 long CStr::FindInsensitive(const Char chr
) const { return LowerCase().Find(totlower(chr
)); }
332 long CStr::FindInsensitive(const CStr
& str
) const { return LowerCase().Find(str
.LowerCase()); }
334 long CStr::ReverseFind(const CStr
& str
) const
336 size_t pos
= rfind(str
, length() );
339 return static_cast<long>(pos
);
344 // Lowercase and uppercase
345 CStr
CStr::LowerCase() const
347 StrBase newStr
= *this;
348 for (size_t i
= 0; i
< length(); i
++)
349 newStr
[i
] = (Char
)totlower((*this)[i
]);
354 CStr
CStr::UpperCase() const
356 StrBase newStr
= *this;
357 for (size_t i
= 0; i
< length(); i
++)
358 newStr
[i
] = (Char
)totupper((*this)[i
]);
364 // Retrieve the substring of the first n characters
365 CStr
CStr::Left(size_t len
) const
367 ENSURE(len
<= length());
368 return substr(0, len
);
371 // Retrieve the substring of the last n characters
372 CStr
CStr::Right(size_t len
) const
374 ENSURE(len
<= length());
375 return substr(length()-len
, len
);
378 // Retrieve the substring following the last occurrence of Str
379 // (or the whole string if it doesn't contain Str)
380 CStr
CStr::AfterLast(const CStr
& str
, size_t startPos
) const
382 size_t pos
= rfind(str
, startPos
);
386 return substr(pos
+ str
.length());
389 // Retrieve the substring preceding the last occurrence of Str
390 // (or the whole string if it doesn't contain Str)
391 CStr
CStr::BeforeLast(const CStr
& str
, size_t startPos
) const
393 size_t pos
= rfind(str
, startPos
);
397 return substr(0, pos
);
400 // Retrieve the substring following the first occurrence of Str
401 // (or the whole string if it doesn't contain Str)
402 CStr
CStr::AfterFirst(const CStr
& str
, size_t startPos
) const
404 size_t pos
= find(str
, startPos
);
408 return substr(pos
+ str
.length());
411 // Retrieve the substring preceding the first occurrence of Str
412 // (or the whole string if it doesn't contain Str)
413 CStr
CStr::BeforeFirst(const CStr
& str
, size_t startPos
) const
415 size_t pos
= find(str
, startPos
);
419 return substr(0, pos
);
422 // Remove all occurrences of some character or substring
423 void CStr::Remove(const CStr
& str
)
426 while (foundAt
!= npos
)
428 foundAt
= find(str
, 0);
431 erase(foundAt
, str
.length());
435 // Replace all occurrences of some substring by another
436 void CStr::Replace(const CStr
& toReplace
, const CStr
& replaceWith
)
441 pos
= find(toReplace
, pos
);
444 erase(pos
, toReplace
.length());
445 insert(pos
, replaceWith
);
446 pos
+= replaceWith
.length();
451 std::string
CStr::EscapeToPrintableASCII() const
454 for (size_t i
= 0; i
< length(); i
++)
456 Char ch
= (*this)[i
];
458 if (ch
== '"') newStr
+= "\\\"";
459 else if (ch
== '\\') newStr
+= "\\\\";
460 else if (ch
== '\b') newStr
+= "\\b";
461 else if (ch
== '\f') newStr
+= "\\f";
462 else if (ch
== '\n') newStr
+= "\\n";
463 else if (ch
== '\r') newStr
+= "\\r";
464 else if (ch
== '\t') newStr
+= "\\t";
465 else if (ch
>= 32 && ch
<= 126)
469 std::stringstream ss
;
470 ss
<< "\\u" << std::hex
<< std::setfill('0') << std::setw(4) << (int)(unsigned char)ch
;
477 // Returns a trimmed string, removes whitespace from the left/right/both
478 CStr
CStr::Trim(PS_TRIM_MODE mode
) const
480 size_t left
= 0, right
= 0;
486 for (left
= 0; left
< length(); left
++)
487 if (istspace((*this)[left
]) == false)
488 break; // end found, trim 0 to Left-1 inclusive
495 if (istspace((*this)[right
]) == false)
496 break; // end found, trim len-1 to Right+1 inclusive
501 for (left
= 0; left
< length(); left
++)
502 if (istspace((*this)[left
]) == false)
503 break; // end found, trim 0 to Left-1 inclusive
507 if (istspace((*this)[right
]) == false)
508 break; // end found, trim len-1 to Right+1 inclusive
512 debug_warn(L
"CStr::Trim: invalid Mode");
516 return substr(left
, right
- left
+ 1);
519 CStr
CStr::Pad(PS_TRIM_MODE mode
, size_t len
) const
521 size_t left
= 0, right
= 0;
526 // From here: Length-length() >= 1
531 left
= len
- length();
535 right
= len
- length();
539 left
= (len
- length() + 1) / 2;
540 right
= (len
- length() - 1) / 2; // cannot be negative
544 debug_warn(L
"CStr::Trim: invalid Mode");
547 return StrBase(left
, ' ') + *this + StrBase(right
, ' ');
550 size_t CStr::GetHashCode() const
552 return (size_t)fnv_hash(data(), length()*sizeof(value_type
));
553 // janwas 2005-03-18: now use 32-bit version; 64 is slower and
554 // the result was truncated down to 32 anyway.
557 u8
* CStr::Serialize(u8
* buffer
) const
559 return SerializeImpl(*this, buffer
);
562 const u8
* CStr::Deserialize(const u8
* buffer
, const u8
* bufferend
)
564 return DeserializeImpl(buffer
, bufferend
, *this);
567 size_t CStr::GetSerializedLength() const
569 return GetSerializedLengthImpl(*this);
572 #endif // CStr_CPP_FIRST