Ark Server API (ASA) - Wiki
Loading...
Searching...
No Matches
String.cpp
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#include "Containers/Array.h"
4#include "Containers/StringConv.h"
5#include "Containers/UnrealString.h"
6#include "CoreGlobals.h"
7#include "CoreTypes.h"
8#include "HAL/PlatformString.h"
9#include "HAL/UnrealMemory.h"
10//#include "Logging/LogCategory.h"
11//#include "Logging/LogMacros.h"
12#include "Math/NumericLimits.h"
13#include "Math/UnrealMathUtility.h"
14#include "Misc/AssertionMacros.h"
15#include "Misc/ByteSwap.h"
16#include "Misc/CString.h"
17#include "Misc/Char.h"
18#include "Misc/VarArgs.h"
19#include "Serialization/Archive.h"
20#include "String/HexToBytes.h"
21#include "Templates/MemoryOps.h"
22#include "Templates/RemoveReference.h"
23#include "Templates/UnrealTemplate.h"
24#include "UObject/NameTypes.h"
25#include "API/Fields.h"
26
27/* FString implementation
28 *****************************************************************************/
29
30namespace UE::Core::String::Private
31{
33 {
34 static FORCEINLINE bool Compare(TCHAR Lhs, TCHAR Rhs)
35 {
36 return Lhs == Rhs;
37 }
38 };
39
41 {
42 static FORCEINLINE bool Compare(TCHAR Lhs, TCHAR Rhs)
43 {
44 return FChar::ToLower(Lhs) == FChar::ToLower(Rhs);
45 }
46 };
47
48 template <typename CompareType>
49 bool MatchesWildcardRecursive(const TCHAR* Target, int32 TargetLength, const TCHAR* Wildcard, int32 WildcardLength)
50 {
51 // Skip over common initial non-wildcard-char sequence of Target and Wildcard
52 for (;;)
53 {
54 if (WildcardLength == 0)
55 {
56 return TargetLength == 0;
57 }
58
60 if (WCh == TEXT('*') || WCh == TEXT('?'))
61 {
62 break;
63 }
64
66 {
67 return false;
68 }
69
70 ++Target;
71 ++Wildcard;
74 }
75
76 // Test for common suffix
77 const TCHAR* TPtr = Target + TargetLength;
79 for (;;)
80 {
81 --TPtr;
82 --WPtr;
83
84 TCHAR WCh = *WPtr;
85 if (WCh == TEXT('*') || WCh == TEXT('?'))
86 {
87 break;
88 }
89
90 if (!CompareType::Compare(*TPtr, WCh))
91 {
92 return false;
93 }
94
97
98 if (TargetLength == 0)
99 {
100 break;
101 }
102 }
103
104 // Match * against anything and ? against single (and zero?) chars
106 if (WildcardLength == 1 && (FirstWild == TEXT('*') || TargetLength < 2))
107 {
108 return true;
109 }
110 ++Wildcard;
112
113 // This routine is very slow, though it does ok with one wildcard
115 if (FirstWild == TEXT('?') && MaxNum > 1)
116 {
117 MaxNum = 1;
118 }
119
120 for (int32 Index = 0; Index <= MaxNum; ++Index)
121 {
123 {
124 return true;
125 }
126 }
127 return false;
128 }
129}
130
131template<typename CharType>
132void AppendCharacters(TArray<TCHAR>& Out, const CharType* Str, int32 Count)
133{
134 check(Count >= 0);
135
136 if (!Count)
137 {
138 return;
139 }
140
141 checkSlow(Str);
142
143 int32 OldEnd = Out.Num();
144
145 // Try to reserve enough space by guessing that the new length will be the same as the input length.
146 // Include an extra gap for a null terminator if we don't already have a string allocated
147 Out.AddUninitialized(Count + (OldEnd ? 0 : 1));
148 OldEnd -= OldEnd ? 1 : 0;
149
150 TCHAR* Dest = Out.GetData() + OldEnd;
151
152 // Try copying characters to end of string, overwriting null terminator if we already have one
153 TCHAR* NewEnd = FPlatformString::Convert(Dest, Count, Str, Count);
154 if (!NewEnd)
155 {
156 // If that failed, it will have meant that conversion likely contained multi-code unit characters
157 // and so the buffer wasn't long enough, so calculate it properly.
158 int32 Length = FPlatformString::ConvertedLength<TCHAR>(Str, Count);
159
160 // Add the extra bytes that we need
161 Out.AddUninitialized(Length - Count);
162
163 // Restablish destination pointer in case a realloc happened
164 Dest = Out.GetData() + OldEnd;
165
166 NewEnd = FPlatformString::Convert(Dest, Length, Str, Count);
167 checkSlow(NewEnd);
168 }
169 else
170 {
171 int32 NewEndIndex = (int32)(NewEnd - Dest);
172 if (NewEndIndex < Count)
173 {
174 Out.SetNumUninitialized(OldEnd + NewEndIndex + 1, /*bAllowShrinking=*/false);
175 }
176 }
177
178 // (Re-)establish the null terminator
179 *NewEnd = '\0';
180}
181
182namespace UE::String::Private
183{
184
185template<typename CharType>
186FORCEINLINE void ConstructFromCString(/* Out */ TArray<TCHAR>& Data, const CharType* Src)
187{
188 if (Src && *Src)
189 {
194
196 }
197}
198
199template<typename CharType>
200FORCEINLINE void ConstructWithLength(/* Out */ TArray<TCHAR>& Data, int32 InCount, const CharType* InSrc)
201{
202 if (InSrc)
203 {
205 if (DestLen > 0 && *InSrc)
206 {
207 Data.Reserve(DestLen + 1);
209
211 *(Data.GetData() + Data.Num() - 1) = TEXT('\0');
212 }
213 }
214}
215
216template<typename CharType>
217FORCEINLINE void ConstructWithSlack(/* Out */ TArray<TCHAR>& Data, const CharType* Src, int32 ExtraSlack)
218{
219 if (Src && *Src)
220 {
225
227 }
228 else if (ExtraSlack > 0)
229 {
231 }
232}
233
234} // namespace UE::String::Private
235
236/*FString::FString(const ANSICHAR* Str) { UE::String::Private::ConstructFromCString(Data, Str); }
237FString::FString(const WIDECHAR* Str) { UE::String::Private::ConstructFromCString(Data, Str); }
238FString::FString(const UTF8CHAR* Str) { UE::String::Private::ConstructFromCString(Data, Str); }
239FString::FString(const UCS2CHAR* Str) { UE::String::Private::ConstructFromCString(Data, Str); }
240FString::FString(int32 Len, const ANSICHAR* Str) { UE::String::Private::ConstructWithLength(Data, Len, Str); }
241FString::FString(int32 Len, const WIDECHAR* Str) { UE::String::Private::ConstructWithLength(Data, Len, Str); }
242FString::FString(int32 Len, const UTF8CHAR* Str) { UE::String::Private::ConstructWithLength(Data, Len, Str); }
243FString::FString(int32 Len, const UCS2CHAR* Str) { UE::String::Private::ConstructWithLength(Data, Len, Str); }
244FString::FString(const ANSICHAR* Str, int32 ExtraSlack) { UE::String::Private::ConstructWithSlack(Data, Str, ExtraSlack); }
245FString::FString(const WIDECHAR* Str, int32 ExtraSlack) { UE::String::Private::ConstructWithSlack(Data, Str, ExtraSlack); }
246FString::FString(const UTF8CHAR* Str, int32 ExtraSlack) { UE::String::Private::ConstructWithSlack(Data, Str, ExtraSlack); }
247FString::FString(const UCS2CHAR* Str, int32 ExtraSlack) { UE::String::Private::ConstructWithSlack(Data, Str, ExtraSlack); }*/
248
249FString::FString(const ANSICHAR* Str) { NativeCall<void, const ANSICHAR*>(this, "FString.FString(char*)", Str); }
250FString::FString(const WIDECHAR* Str) { NativeCall<void, const WIDECHAR*>(this, "FString.FString(wchar_t*)", Str); }
251FString::FString(const UTF8CHAR* Str) { UE::String::Private::ConstructFromCString(Data, Str); }
252FString::FString(const UCS2CHAR* Str) { UE::String::Private::ConstructFromCString(Data, Str); }
253FString::FString(int32 Len, const ANSICHAR* Str) { UE::String::Private::ConstructWithLength(Data, Len, Str); }
254FString::FString(int32 Len, const WIDECHAR* Str) { UE::String::Private::ConstructWithLength(Data, Len, Str); }
255FString::FString(int32 Len, const UTF8CHAR* Str) { UE::String::Private::ConstructWithLength(Data, Len, Str); }
256FString::FString(int32 Len, const UCS2CHAR* Str) { UE::String::Private::ConstructWithLength(Data, Len, Str); }
257FString::FString(const ANSICHAR* Str, int32 ExtraSlack) { UE::String::Private::ConstructWithSlack(Data, Str, ExtraSlack); }
258FString::FString(const WIDECHAR* Str, int32 ExtraSlack) { UE::String::Private::ConstructWithSlack(Data, Str, ExtraSlack); }
259FString::FString(const UTF8CHAR* Str, int32 ExtraSlack) { UE::String::Private::ConstructWithSlack(Data, Str, ExtraSlack); }
260FString::FString(const UCS2CHAR* Str, int32 ExtraSlack) { UE::String::Private::ConstructWithSlack(Data, Str, ExtraSlack); }
261
262FString& FString::operator=( const TCHAR* Other )
263{
264 if (Data.GetData() != Other)
265 {
266 int32 Len = (Other && *Other) ? FCString::Strlen(Other)+1 : 0;
267 Data.Empty(Len);
268 Data.AddUninitialized(Len);
269
270 if( Len )
271 {
272 FMemory::Memcpy( Data.GetData(), Other, Len * sizeof(TCHAR) );
273 }
274 }
275 return *this;
276}
277
278
279ARK_API void FString::AssignRange(const TCHAR* OtherData, int32 OtherLen)
280{
281 if (OtherLen == 0)
282 {
283 Empty();
284 }
285 else
286 {
287 const int32 ThisLen = Len();
288 if (OtherLen <= ThisLen)
289 {
290 // Unless the input is longer, this might be assigned from a view of itself.
291 TCHAR* DataPtr = Data.GetData();
292 FMemory::Memmove(DataPtr, OtherData, OtherLen * sizeof(TCHAR));
293 DataPtr[OtherLen] = TEXT('\0');
294 Data.RemoveAt(OtherLen + 1, ThisLen - OtherLen);
295 }
296 else
297 {
298 Data.Empty(OtherLen + 1);
299 Data.AddUninitialized(OtherLen + 1);
300 TCHAR* DataPtr = Data.GetData();
301 FMemory::Memcpy(DataPtr, OtherData, OtherLen * sizeof(TCHAR));
302 DataPtr[OtherLen] = TEXT('\0');
303 }
304 }
305}
306
307void FString::Reserve(int32 CharacterCount)
308{
309 checkSlow(CharacterCount >= 0 && CharacterCount < MAX_int32);
310 if (CharacterCount > 0)
311 {
312 Data.Reserve(CharacterCount + 1);
313 }
314}
315
316void FString::Empty(int32 Slack)
317{
318 Data.Empty(Slack ? Slack + 1 : 0);
319}
320
322{
323 Data.Empty(0);
324}
325
326
328{
329 Data.Shrink();
330}
331
332#ifdef __OBJC__
333/** Convert FString to Objective-C NSString */
334NSString* FString::GetNSString() const
335{
336#if PLATFORM_TCHAR_IS_4_BYTES
337 return [[[NSString alloc] initWithBytes:Data.GetData() length:Len() * sizeof(TCHAR) encoding:NSUTF32LittleEndianStringEncoding] autorelease];
338#else
339 return [[[NSString alloc] initWithBytes:Data.GetData() length:Len() * sizeof(TCHAR) encoding:NSUTF16LittleEndianStringEncoding] autorelease];
340#endif
341}
342#endif
343
344
345void FString::AppendChars(const ANSICHAR* Str, int32 Count)
346{
348 AppendCharacters(Data, Str, Count);
349}
350
351void FString::AppendChars(const WIDECHAR* Str, int32 Count)
352{
354 AppendCharacters(Data, Str, Count);
355}
356
357void FString::AppendChars(const UCS2CHAR* Str, int32 Count)
358{
360 AppendCharacters(Data, Str, Count);
361}
362
363void FString::AppendChars(const UTF8CHAR* Str, int32 Count)
364{
366 AppendCharacters(Data, Str, Count);
367}
368
370{
371 if( Data.Num() )
372 {
373 int32 DataLen = FCString::Strlen(Data.GetData());
374 check(DataLen == 0 || DataLen < Data.Num());
375 int32 Len = DataLen > 0 ? DataLen+1 : 0;
376
377 check(Len <= Data.Num());
378 Data.RemoveAt(Len, Data.Num()-Len);
379 }
380}
381
382
383
384bool FString::Split(const FString& InS, FString* LeftS, FString* RightS, ESearchCase::Type SearchCase, ESearchDir::Type SearchDir) const
385{
386 check(LeftS != RightS || LeftS == nullptr);
387
388 int32 InPos = Find(InS, SearchCase, SearchDir);
389
390 if (InPos < 0) { return false; }
391
392 if (LeftS)
393 {
394 if (LeftS != this)
395 {
396 *LeftS = Left(InPos);
397 if (RightS) { *RightS = Mid(InPos + InS.Len()); }
398 }
399 else
400 {
401 // we know that RightS can't be this so we can safely modify it before we deal with LeftS
402 if (RightS) { *RightS = Mid(InPos + InS.Len()); }
403 *LeftS = Left(InPos);
404 }
405 }
406 else if (RightS)
407 {
408 *RightS = Mid(InPos + InS.Len());
409 }
410
411 return true;
412}
413
414bool FString::Split(const FString& InS, FString* LeftS, FString* RightS) const
415{
416 return Split(InS, LeftS, RightS, ESearchCase::IgnoreCase);
417}
418
419
420
421
422
423
424
426{
427 const int32 StringLength = Len();
428 if (StringLength == 0)
429 {
430 return;
431 }
432
433 TCHAR* RawData = Data.GetData();
434 int32 CopyToIndex = 0;
435 for (int32 CopyFromIndex = 0; CopyFromIndex < StringLength; ++CopyFromIndex)
436 {
437 if (RawData[CopyFromIndex] != ' ')
438 { // Copy any character OTHER than space.
439 RawData[CopyToIndex] = RawData[CopyFromIndex];
440 ++CopyToIndex;
441 }
442 }
443
444 // Copy null-terminating character.
445 if (CopyToIndex <= StringLength)
446 {
447 RawData[CopyToIndex] = TEXT('\0');
448 Data.SetNum(CopyToIndex + 1, false);
449 }
450}
451
452
453
454void FString::InsertAt(int32 Index, TCHAR Character)
455{
456 if (Character != 0)
457 {
458 if (Data.Num() == 0)
459 {
460 *this += Character;
461 }
462 else
463 {
464 Data.Insert(Character, Index);
465 }
466 }
467}
468
469void FString::InsertAt(int32 Index, const FString& Characters)
470{
471 if (Characters.Len())
472 {
473 if (Data.Num() == 0)
474 {
475 *this += Characters;
476 }
477 else
478 {
479 Data.Insert(Characters.Data.GetData(), Characters.Len(), Index);
480 }
481 }
482}
483
484void FString::RemoveAt(int32 Index, int32 Count, bool bAllowShrinking)
485{
486 Data.RemoveAt(Index, FMath::Clamp(Count, 0, Len()-Index), bAllowShrinking);
487}
488
489ARK_API bool FString::RemoveFromStart(const TCHAR* InPrefix, int32 InPrefixLen, ESearchCase::Type SearchCase)
490{
491 if (InPrefixLen == 0 )
492 {
493 return false;
494 }
495
496 if (StartsWith(InPrefix, InPrefixLen, SearchCase ))
497 {
498 RemoveAt(0, InPrefixLen);
499 return true;
500 }
501
502 return false;
503}
504
505bool FString::RemoveFromEnd(const TCHAR* InSuffix, int32 InSuffixLen, ESearchCase::Type SearchCase)
506{
507 if (InSuffixLen == 0)
508 {
509 return false;
510 }
511
512 if (EndsWith(InSuffix, InSuffixLen, SearchCase))
513 {
514 RemoveAt( Len() - InSuffixLen, InSuffixLen );
515 return true;
516 }
517
518 return false;
519}
520
521namespace UE::String::Private
522{
523
524template <typename LhsType, typename RhsType>
525UE_NODISCARD FORCEINLINE FString ConcatFStrings(LhsType&& Lhs, RhsType&& Rhs)
526{
529
530 if (Lhs.IsEmpty())
531 {
532 return Forward<RhsType>(Rhs);
533 }
534
535 int32 RhsLen = Rhs.Len();
536
537 FString Result(Forward<LhsType>(Lhs), /* extra slack */ RhsLen);
539
540 return Result;
541}
542
543template <typename RhsType>
544UE_NODISCARD FORCEINLINE FString ConcatRangeFString(const TCHAR* Lhs, int32 LhsLen, RhsType&& Rhs)
545{
546 checkSlow(LhsLen >= 0);
548 if (LhsLen == 0)
549 {
550 return Forward<RhsType>(Rhs);
551 }
552
553 int32 RhsLen = Rhs.Len();
554
555 // This is not entirely optimal, as if the Rhs is an rvalue and has enough slack space to hold Lhs, then
556 // the memory could be reused here without constructing a new object. However, until there is proof otherwise,
557 // I believe this will be relatively rare and isn't worth making the code a lot more complex right now.
561
565 *(ResultData + LhsLen + RhsLen) = TEXT('\0');
566
567 return Result;
568}
569
570template <typename LhsType>
571UE_NODISCARD FORCEINLINE FString ConcatFStringRange(LhsType&& Lhs, const TCHAR* Rhs, int32 RhsLen)
572{
574 checkSlow(RhsLen >= 0);
575 if (RhsLen == 0)
576 {
577 return Forward<LhsType>(Lhs);
578 }
579
580 FString Result(Forward<LhsType>(Lhs), /* extra slack */ RhsLen);
582
583 return Result;
584}
585
586template <typename RhsType>
587UE_NODISCARD FORCEINLINE FString ConcatCStringFString(const TCHAR* Lhs, RhsType&& Rhs)
588{
589 checkSlow(Lhs);
590 if (!Lhs)
591 {
592 return Forward<RhsType>(Rhs);
593 }
594
596}
597
598template <typename LhsType>
599UE_NODISCARD FORCEINLINE FString ConcatFStringCString(LhsType&& Lhs, const TCHAR* Rhs)
600{
601 checkSlow(Rhs);
602 if (!Rhs)
603 {
604 return Forward<LhsType>(Lhs);
605 }
607}
608
609} // namespace UE::String::Private
610
611FString FString::ConcatFF(const FString& Lhs, const FString& Rhs) { return UE::String::Private::ConcatFStrings(Lhs, Rhs); }
612FString FString::ConcatFF(FString&& Lhs, const FString& Rhs) { return UE::String::Private::ConcatFStrings(MoveTemp(Lhs), Rhs); }
613FString FString::ConcatFF(const FString& Lhs, FString&& Rhs) { return UE::String::Private::ConcatFStrings(Lhs, MoveTemp(Rhs)); }
614FString FString::ConcatFF(FString&& Lhs, FString&& Rhs) { return UE::String::Private::ConcatFStrings(MoveTemp(Lhs), MoveTemp(Rhs)); }
615FString FString::ConcatFC(const FString& Lhs, const TCHAR* Rhs) { return UE::String::Private::ConcatFStringCString(Lhs, Rhs); }
616FString FString::ConcatFC(FString&& Lhs, const TCHAR* Rhs) { return UE::String::Private::ConcatFStringCString(MoveTemp(Lhs), Rhs); }
617FString FString::ConcatCF(const TCHAR* Lhs, const FString& Rhs) { return UE::String::Private::ConcatCStringFString(Lhs, Rhs); }
618FString FString::ConcatCF(const TCHAR* Lhs, FString&& Rhs) { return UE::String::Private::ConcatCStringFString(Lhs, MoveTemp(Rhs)); }
619FString FString::ConcatFR(const FString& Lhs, const TCHAR* Rhs, int32 RhsLen) { return UE::String::Private::ConcatFStringRange(Lhs, Rhs, RhsLen); }
620FString FString::ConcatFR(FString&& Lhs, const TCHAR* Rhs, int32 RhsLen) { return UE::String::Private::ConcatFStringRange(MoveTemp(Lhs), Rhs, RhsLen); }
621FString FString::ConcatRF(const TCHAR* Lhs, int32 LhsLen, const FString& Rhs) { return UE::String::Private::ConcatRangeFString(Lhs, LhsLen, Rhs); }
622FString FString::ConcatRF(const TCHAR* Lhs, int32 LhsLen, FString&& Rhs) { return UE::String::Private::ConcatRangeFString(Lhs, LhsLen, MoveTemp(Rhs)); }
623
624/**
625 * Concatenate this path with given path ensuring the / character is used between them
626 *
627 * @param Str Pointer to an array of TCHARs (not necessarily null-terminated) to be concatenated onto the end of this.
628 * @param StrLength Exact number of characters from Str to append.
629 */
630void FString::PathAppend(const TCHAR* Str, int32 StrLength)
631{
632 int32 DataNum = Data.Num();
633 if (StrLength == 0)
634 {
635 if (DataNum > 1 && Data[DataNum - 2] != TEXT('/') && Data[DataNum - 2] != TEXT('\\'))
636 {
637 Data[DataNum - 1] = TEXT('/');
638 Data.Add(TEXT('\0'));
639 }
640 }
641 else
642 {
643 if (DataNum > 0)
644 {
645 if (DataNum > 1 && Data[DataNum - 2] != TEXT('/') && Data[DataNum - 2] != TEXT('\\') && *Str != TEXT('/'))
646 {
647 Data[DataNum - 1] = TEXT('/');
648 }
649 else
650 {
651 Data.Pop(false);
652 --DataNum;
653 }
654 }
655
656 Reserve(DataNum + StrLength);
657 Data.Append(Str, StrLength);
658 Data.Add(TEXT('\0'));
659 }
660}
661
662
663
664
665void FString::ReplaceCharInlineCaseSensitive(const TCHAR SearchChar, const TCHAR ReplacementChar)
666{
667 for (TCHAR& Character : Data)
668 {
669 Character = Character == SearchChar ? ReplacementChar : Character;
670 }
671}
672
673void FString::ReplaceCharInlineIgnoreCase(const TCHAR SearchChar, const TCHAR ReplacementChar)
674{
675 TCHAR OtherCaseSearchChar = TChar<TCHAR>::IsUpper(SearchChar) ? TChar<TCHAR>::ToLower(SearchChar) : TChar<TCHAR>::ToUpper(SearchChar);
676 ReplaceCharInlineCaseSensitive(OtherCaseSearchChar, ReplacementChar);
677 ReplaceCharInlineCaseSensitive(SearchChar, ReplacementChar);
678}
679
681{
684}
685
687{
689 return MoveTemp(*this);
690}
691
693{
694 int32 Pos = 0;
695 while(Pos < Len() && FChar::IsWhitespace((*this)[Pos]))
696 {
697 Pos++;
698 }
699 RemoveAt(0, Pos);
700}
701
703{
704 FString Result(*this);
705 Result.TrimStartInline();
706 return Result;
707}
708
710{
712 return MoveTemp(*this);
713}
714
716{
717 int32 End = Len();
718 while(End > 0 && FChar::IsWhitespace((*this)[End - 1]))
719 {
720 End--;
721 }
722 RemoveAt(End, Len() - End);
723}
724
726{
727 FString Result(*this);
728 Result.TrimEndInline();
729 return Result;
730}
731
733{
735 return MoveTemp(*this);
736}
737
738void FString::TrimCharInline(const TCHAR CharacterToTrim, bool* bCharRemoved)
739{
740 bool bQuotesWereRemoved=false;
741 int32 Start = 0, Count = Len();
742 if ( Count > 0 )
743 {
744 if ( (*this)[0] == CharacterToTrim )
745 {
746 Start++;
747 Count--;
748 bQuotesWereRemoved=true;
749 }
750
751 if ( Len() > 1 && (*this)[Len() - 1] == CharacterToTrim )
752 {
753 Count--;
754 bQuotesWereRemoved=true;
755 }
756 }
757
758 if ( bCharRemoved != nullptr )
759 {
760 *bCharRemoved = bQuotesWereRemoved;
761 }
762 MidInline(Start, Count, false);
763}
764
765void FString::TrimQuotesInline(bool* bQuotesRemoved)
766{
767 TrimCharInline(TCHAR('"'), bQuotesRemoved);
768}
769
770FString FString::TrimQuotes(bool* bQuotesRemoved) const &
771{
772 FString Result(*this);
773 Result.TrimQuotesInline(bQuotesRemoved);
774 return Result;
775}
776
777FString FString::TrimQuotes(bool* bQuotesRemoved) &&
778{
779 TrimQuotesInline(bQuotesRemoved);
780 return MoveTemp(*this);
781}
782
783FString FString::TrimChar(const TCHAR CharacterToTrim, bool* bCharRemoved) const &
784{
785 FString Result(*this);
786 Result.TrimCharInline(CharacterToTrim, bCharRemoved);
787 return Result;
788}
789
790FString FString::TrimChar(const TCHAR CharacterToTrim, bool* bCharRemoved) &&
791{
792 TrimCharInline(CharacterToTrim, bCharRemoved);
793 return MoveTemp(*this);
794}
795
796int32 FString::CullArray( TArray<FString>* InArray )
797{
798 check(InArray);
799 FString Empty;
800 InArray->Remove(Empty);
801 return InArray->Num();
802}
803
805{
806 FString New(*this);
808 return New;
809}
810
812{
814 return MoveTemp(*this);
815}
816
818{
819 if ( Len() > 0 )
820 {
821 TCHAR* StartChar = &(*this)[0];
822 TCHAR* EndChar = &(*this)[Len()-1];
823 TCHAR TempChar;
824 do
825 {
826 TempChar = *StartChar; // store the current value of StartChar
827 *StartChar = *EndChar; // change the value of StartChar to the value of EndChar
828 *EndChar = TempChar; // change the value of EndChar to the character that was previously at StartChar
829
830 StartChar++;
831 EndChar--;
832
833 } while( StartChar < EndChar ); // repeat until we've reached the midpoint of the string
834 }
835}
836
838{
839 FString Number = FString::FromInt( InNumber ), Result;
840
841 int32 dec = 0;
842 for( int32 x = Number.Len()-1 ; x > -1 ; --x )
843 {
844 Result += Number.Mid(x,1);
845
846 dec++;
847 if( dec == 3 && x > 0 )
848 {
849 Result += TEXT(",");
850 dec = 0;
851 }
852 }
853
854 return Result.Reverse();
855}
856
857/**
858 * Serializes a string as ANSI char array.
859 *
860 * @param String String to serialize
861 * @param Ar Archive to serialize with
862 * @param MinCharacters Minimum number of characters to serialize.
863 */
864void FString::SerializeAsANSICharArray( FArchive& Ar, int32 MinCharacters ) const
865{
866 int32 Length = FMath::Max( Len(), MinCharacters );
867 Ar << Length;
868
869 for( int32 CharIndex=0; CharIndex<Len(); CharIndex++ )
870 {
871 ANSICHAR AnsiChar = CharCast<ANSICHAR>( (*this)[CharIndex] );
872 Ar << AnsiChar;
873 }
874
875 // Zero pad till minimum number of characters are written.
876 for( int32 i=Len(); i<Length; i++ )
877 {
878 ANSICHAR NullChar = 0;
879 Ar << NullChar;
880 }
881}
882
883
884FString FString::FromBlob(const uint8* SrcBuffer,const uint32 SrcSize)
885{
886 FString Result;
887 Result.Reserve( SrcSize * 3 );
888 // Convert and append each byte in the buffer
889 for (uint32 Count = 0; Count < SrcSize; Count++)
890 {
891 Result += FString::Printf(TEXT("%03d"),(uint8)SrcBuffer[Count]);
892 }
893 return Result;
894}
895
896bool FString::ToBlob(const FString& Source,uint8* DestBuffer,const uint32 DestSize)
897{
898 // Make sure the buffer is at least half the size and that the string is an
899 // even number of characters long
900 if (DestSize >= (uint32)(Source.Len() / 3) &&
901 (Source.Len() % 3) == 0)
902 {
903 TCHAR ConvBuffer[4];
904 ConvBuffer[3] = TEXT('\0');
905 int32 WriteIndex = 0;
906 // Walk the string 3 chars at a time
907 for (int32 Index = 0; Index < Source.Len(); Index += 3, WriteIndex++)
908 {
909 ConvBuffer[0] = Source[Index];
910 ConvBuffer[1] = Source[Index + 1];
911 ConvBuffer[2] = Source[Index + 2];
912 DestBuffer[WriteIndex] = (uint8)FCString::Atoi(ConvBuffer);
913 }
914 return true;
915 }
916 return false;
917}
918
919FString FString::FromHexBlob( const uint8* SrcBuffer, const uint32 SrcSize )
920{
921 FString Result;
922 Result.Reserve( SrcSize * 2 );
923 // Convert and append each byte in the buffer
924 for (uint32 Count = 0; Count < SrcSize; Count++)
925 {
926 Result += FString::Printf( TEXT( "%02X" ), (uint8)SrcBuffer[Count] );
927 }
928 return Result;
929}
930
931bool FString::ToHexBlob( const FString& Source, uint8* DestBuffer, const uint32 DestSize )
932{
933 // Make sure the buffer is at least half the size and that the string is an
934 // even number of characters long
935 if (DestSize >= (uint32)(Source.Len() / 2) &&
936 (Source.Len() % 2) == 0)
937 {
938 TCHAR ConvBuffer[3];
939 ConvBuffer[2] = TEXT( '\0' );
940 int32 WriteIndex = 0;
941 // Walk the string 2 chars at a time
942 TCHAR* End = nullptr;
943 for (int32 Index = 0; Index < Source.Len(); Index += 2, WriteIndex++)
944 {
945 ConvBuffer[0] = Source[Index];
946 ConvBuffer[1] = Source[Index + 1];
947 DestBuffer[WriteIndex] = (uint8)FCString::Strtoi( ConvBuffer, &End, 16 );
948 }
949 return true;
950 }
951 return false;
952}
953
955void StripNegativeZero(double& InFloat)
956{
957 // This works for translating a negative zero into a positive zero,
958 // but if optimizations are enabled when compiling with -ffast-math
959 // or /fp:fast, the compiler can strip it out.
960 InFloat += 0.0f;
961}
963
964FString FString::SanitizeFloat( double InFloat, const int32 InMinFractionalDigits )
965{
966 // Avoids negative zero
967 StripNegativeZero(InFloat);
968
969 // First create the string
970 FString TempString = FString::Printf(TEXT("%f"), InFloat);
971 if (!TempString.IsNumeric())
972 {
973 // String did not format as a valid decimal number so avoid messing with it
974 return TempString;
975 }
976
977 // Trim all trailing zeros (up-to and including the decimal separator) from the fractional part of the number
978 int32 TrimIndex = INDEX_NONE;
979 int32 DecimalSeparatorIndex = INDEX_NONE;
980 for (int32 CharIndex = TempString.Len() - 1; CharIndex >= 0; --CharIndex)
981 {
982 const TCHAR Char = TempString[CharIndex];
983 if (Char == TEXT('.'))
984 {
985 DecimalSeparatorIndex = CharIndex;
986 TrimIndex = FMath::Max(TrimIndex, DecimalSeparatorIndex);
987 break;
988 }
989 if (TrimIndex == INDEX_NONE && Char != TEXT('0'))
990 {
991 TrimIndex = CharIndex + 1;
992 }
993 }
994 check(TrimIndex != INDEX_NONE && DecimalSeparatorIndex != INDEX_NONE);
995 TempString.RemoveAt(TrimIndex, TempString.Len() - TrimIndex, /*bAllowShrinking*/false);
996
997 // Pad the number back to the minimum number of fractional digits
998 if (InMinFractionalDigits > 0)
999 {
1000 if (TrimIndex == DecimalSeparatorIndex)
1001 {
1002 // Re-add the decimal separator
1003 TempString.AppendChar(TEXT('.'));
1004 }
1005
1006 const int32 NumFractionalDigits = (TempString.Len() - DecimalSeparatorIndex) - 1;
1007 const int32 FractionalDigitsToPad = InMinFractionalDigits - NumFractionalDigits;
1008 if (FractionalDigitsToPad > 0)
1009 {
1010 TempString.Reserve(TempString.Len() + FractionalDigitsToPad);
1011 for (int32 Cx = 0; Cx < FractionalDigitsToPad; ++Cx)
1012 {
1013 TempString.AppendChar(TEXT('0'));
1014 }
1015 }
1016 }
1017
1018 return TempString;
1019}
1020
1021FString FString::Chr( TCHAR Ch )
1022{
1023 TCHAR Temp[2]= { Ch, TEXT('\0') };
1024 return FString(Temp);
1025}
1026
1027
1028FString FString::ChrN( int32 NumCharacters, TCHAR Char )
1029{
1030 check( NumCharacters >= 0 );
1031
1032 FString Temp;
1033 Temp.Data.AddUninitialized(NumCharacters+1);
1034 for( int32 Cx = 0; Cx < NumCharacters; ++Cx )
1035 {
1036 Temp[Cx] = Char;
1037 }
1038 Temp.Data[NumCharacters] = TEXT('\0');
1039 return Temp;
1040}
1041
1042FString FString::LeftPad( int32 ChCount ) const
1043{
1044 int32 Pad = ChCount - Len();
1045
1046 if (Pad > 0)
1047 {
1048 return ChrN(Pad, TEXT(' ')) + *this;
1049 }
1050 else
1051 {
1052 return *this;
1053 }
1054}
1055FString FString::RightPad( int32 ChCount ) const
1056{
1057 int32 Pad = ChCount - Len();
1058
1059 if (Pad > 0)
1060 {
1061 return *this + ChrN(Pad, TEXT(' '));
1062 }
1063 else
1064 {
1065 return *this;
1066 }
1067}
1068
1069
1070
1071/**
1072 * Breaks up a delimited string into elements of a string array.
1073 *
1074 * @param InArray The array to fill with the string pieces
1075 * @param pchDelim The string to delimit on
1076 * @param InCullEmpty If 1, empty strings are not added to the array
1077 *
1078 * @return The number of elements in InArray
1079 */
1080ARK_API int32 FString::ParseIntoArray( TArray<FString>& OutArray, const TCHAR* pchDelim, const bool InCullEmpty ) const
1081{
1082 // Make sure the delimit string is not null or empty
1083 check(pchDelim);
1084 OutArray.Reset();
1085 const TCHAR *Start = **this;
1086 const int32 DelimLength = FCString::Strlen(pchDelim);
1087 if (Start && *Start != TEXT('\0') && DelimLength)
1088 {
1089 while( const TCHAR *At = FCString::Strstr(Start,pchDelim) )
1090 {
1091 if (!InCullEmpty || At-Start)
1092 {
1093 OutArray.Emplace(UE_PTRDIFF_TO_INT32(At-Start),Start);
1094 }
1095 Start = At + DelimLength;
1096 }
1097 if (!InCullEmpty || *Start)
1098 {
1099 OutArray.Emplace(Start);
1100 }
1101
1102 }
1103 return OutArray.Num();
1104}
1105
1106bool FString::MatchesWildcard(const TCHAR* InWildcard, int32 InWildcardLen, ESearchCase::Type SearchCase) const
1107{
1108 const TCHAR* Target = **this;
1109 int32 TargetLength = Len();
1110
1111 if (SearchCase == ESearchCase::CaseSensitive)
1112 {
1113 return UE::Core::String::Private::MatchesWildcardRecursive<UE::Core::String::Private::FCompareCharsCaseSensitive>(Target, TargetLength, InWildcard, InWildcardLen);
1114 }
1115 else
1116 {
1117 return UE::Core::String::Private::MatchesWildcardRecursive<UE::Core::String::Private::FCompareCharsCaseInsensitive>(Target, TargetLength, InWildcard, InWildcardLen);
1118 }
1119}
1120
1121
1122/** Caution!! this routine is O(N^2) allocations...use it for parsing very short text or not at all */
1123ARK_API int32 FString::ParseIntoArrayWS( TArray<FString>& OutArray, const TCHAR* pchExtraDelim, bool InCullEmpty ) const
1124{
1125 // default array of White Spaces, the last entry can be replaced with the optional pchExtraDelim string
1126 // (if you want to split on white space and another character)
1127 const TCHAR* WhiteSpace[] =
1128 {
1129 TEXT(" "),
1130 TEXT("\t"),
1131 TEXT("\r"),
1132 TEXT("\n"),
1133 TEXT(""),
1134 };
1135
1136 // start with just the standard whitespaces
1137 int32 NumWhiteSpaces = UE_ARRAY_COUNT(WhiteSpace) - 1;
1138 // if we got one passed in, use that in addition
1139 if (pchExtraDelim && *pchExtraDelim)
1140 {
1141 WhiteSpace[NumWhiteSpaces++] = pchExtraDelim;
1142 }
1143
1144 return ParseIntoArray(OutArray, WhiteSpace, NumWhiteSpaces, InCullEmpty);
1145}
1146
1147ARK_API int32 FString::ParseIntoArrayLines(TArray<FString>& OutArray, bool InCullEmpty) const
1148{
1149 // default array of LineEndings
1150 static const TCHAR* LineEndings[] =
1151 {
1152 TEXT("\r\n"),
1153 TEXT("\r"),
1154 TEXT("\n"),
1155 };
1156
1157 // start with just the standard line endings
1158 int32 NumLineEndings = UE_ARRAY_COUNT(LineEndings);
1159 return ParseIntoArray(OutArray, LineEndings, NumLineEndings, InCullEmpty);
1160}
1161
1162ARK_API int32 FString::ParseIntoArray(TArray<FString>& OutArray, const TCHAR* const * DelimArray, int32 NumDelims, bool InCullEmpty) const
1163{
1164 // Make sure the delimit string is not null or empty
1165 check(DelimArray);
1166 OutArray.Reset();
1167 const TCHAR *Start = Data.GetData();
1168 const int32 Length = Len();
1169 if (Start)
1170 {
1171 int32 SubstringBeginIndex = 0;
1172
1173 // Iterate through string.
1174 for(int32 i = 0; i < Len();)
1175 {
1176 int32 SubstringEndIndex = INDEX_NONE;
1177 int32 DelimiterLength = 0;
1178
1179 // Attempt each delimiter.
1180 for(int32 DelimIndex = 0; DelimIndex < NumDelims; ++DelimIndex)
1181 {
1182 DelimiterLength = FCString::Strlen(DelimArray[DelimIndex]);
1183
1184 // If we found a delimiter...
1185 if (FCString::Strncmp(Start + i, DelimArray[DelimIndex], DelimiterLength) == 0)
1186 {
1187 // Mark the end of the substring.
1188 SubstringEndIndex = i;
1189 break;
1190 }
1191 }
1192
1193 if (SubstringEndIndex != INDEX_NONE)
1194 {
1195 const int32 SubstringLength = SubstringEndIndex - SubstringBeginIndex;
1196 // If we're not culling empty strings or if we are but the string isn't empty anyways...
1197 if(!InCullEmpty || SubstringLength != 0)
1198 {
1199 // ... add new string from substring beginning up to the beginning of this delimiter.
1200 new (OutArray) FString(SubstringEndIndex - SubstringBeginIndex, Start + SubstringBeginIndex);
1201 }
1202 // Next substring begins at the end of the discovered delimiter.
1203 SubstringBeginIndex = SubstringEndIndex + DelimiterLength;
1204 i = SubstringBeginIndex;
1205 }
1206 else
1207 {
1208 ++i;
1209 }
1210 }
1211
1212 // Add any remaining characters after the last delimiter.
1213 const int32 SubstringLength = Length - SubstringBeginIndex;
1214 // If we're not culling empty strings or if we are but the string isn't empty anyways...
1215 if(!InCullEmpty || SubstringLength != 0)
1216 {
1217 // ... add new string from substring beginning up to the beginning of this delimiter.
1218 new (OutArray) FString(Start + SubstringBeginIndex);
1219 }
1220 }
1221
1222 return OutArray.Num();
1223}
1224
1225
1226
1227
1228
1229/**
1230 * Returns a copy of this string with all quote marks escaped (unless the quote is already escaped)
1231 */
1233{
1235 {
1236 FString Copy(MoveTemp(*this));
1237
1238 const TCHAR* pChar = *Copy;
1239
1240 bool bEscaped = false;
1241 while ( *pChar != 0 )
1242 {
1243 if ( bEscaped )
1244 {
1245 bEscaped = false;
1246 }
1247 else if ( *pChar == TCHAR('\\') )
1248 {
1249 bEscaped = true;
1250 }
1251 else if ( *pChar == TCHAR('"') )
1252 {
1253 *this += TCHAR('\\');
1254 }
1255
1256 *this += *pChar++;
1257 }
1258 }
1259
1260 return MoveTemp(*this);
1261}
1262
1263static const TCHAR* CharToEscapeSeqMap[][2] =
1264{
1265 // Always replace \\ first to avoid double-escaping characters
1266 { TEXT("\\"), TEXT("\\\\") },
1267 { TEXT("\n"), TEXT("\\n") },
1268 { TEXT("\r"), TEXT("\\r") },
1269 { TEXT("\t"), TEXT("\\t") },
1270 { TEXT("\'"), TEXT("\\'") },
1271 { TEXT("\""), TEXT("\\\"") }
1272};
1273//int32 FString::ReplaceInline(const TCHAR* SearchText, const TCHAR* ReplacementText, ESearchCase::Type SearchCase);
1275
1276void FString::ReplaceEscapedCharWithCharInline(const TArray<TCHAR>* Chars/*=nullptr*/)
1277{
1278 if ( Len() > 0 && (Chars == nullptr || Chars->Num() > 0) )
1279 {
1280 // Spin CharToEscapeSeqMap backwards to ensure we're doing the inverse of ReplaceCharWithEscapedChar
1281 for ( int32 ChIdx = MaxSupportedEscapeChars - 1; ChIdx >= 0; ChIdx-- )
1282 {
1283 if ( Chars == nullptr || Chars->Contains(*(CharToEscapeSeqMap[ChIdx][0])) )
1284 {
1285 // use ReplaceInline as that won't create a copy of the string if the character isn't found
1286 ReplaceInline(CharToEscapeSeqMap[ChIdx][1], CharToEscapeSeqMap[ChIdx][0]);
1287 }
1288 }
1289 }
1290}
1291
1292/**
1293 * Replaces all instances of '\t' with TabWidth number of spaces
1294 * @param InSpacesPerTab - Number of spaces that a tab represents
1295 */
1296void FString::ConvertTabsToSpacesInline(const int32 InSpacesPerTab)
1297{
1298 //must call this with at least 1 space so the modulus operation works
1299 check(InSpacesPerTab > 0);
1300
1301 int32 TabIndex;
1302 while ((TabIndex = Find(TEXT("\t"), ESearchCase::CaseSensitive)) != INDEX_NONE )
1303 {
1304 FString RightSide = Mid(TabIndex+1);
1305 LeftInline(TabIndex, false);
1306
1307 //for a tab size of 4,
1308 int32 LineBegin = Find(TEXT("\n"), ESearchCase::CaseSensitive, ESearchDir::FromEnd, TabIndex);
1309 if (LineBegin == INDEX_NONE)
1310 {
1311 LineBegin = 0;
1312 }
1313 const int32 CharactersOnLine = (Len()-LineBegin);
1314
1315 int32 NumSpacesForTab = InSpacesPerTab - (CharactersOnLine % InSpacesPerTab);
1316 for (int32 i = 0; i < NumSpacesForTab; ++i)
1317 {
1318 AppendChar(TEXT(' '));
1319 }
1320 Append(RightSide);
1321 }
1322}
1323
1324// This starting size catches 99.97% of printf calls - there are about 700k printf calls per level
1325#define STARTING_BUFFER_SIZE 512
1326
1327//FString FString::PrintfImpl(const TCHAR* Fmt, ...);
1328
1329void FString::AppendfImpl(FString& AppendToMe, const TCHAR* Fmt, ...)
1330{
1331 int32 BufferSize = STARTING_BUFFER_SIZE;
1332 TCHAR StartingBuffer[STARTING_BUFFER_SIZE];
1333 TCHAR* Buffer = StartingBuffer;
1334 int32 Result = -1;
1335
1336 // First try to print to a stack allocated location
1337 GET_VARARGS_RESULT(Buffer, BufferSize, BufferSize - 1, Fmt, Fmt, Result);
1338
1339 // If that fails, start allocating regular memory
1340 if (Result == -1)
1341 {
1342 Buffer = nullptr;
1343 while (Result == -1)
1344 {
1345 BufferSize *= 2;
1346 Buffer = (TCHAR*)FMemory::Realloc(Buffer, BufferSize * sizeof(TCHAR));
1347 GET_VARARGS_RESULT(Buffer, BufferSize, BufferSize - 1, Fmt, Fmt, Result);
1348 };
1349 }
1350
1351 Buffer[Result] = TEXT('\0');
1352
1353 AppendToMe += Buffer;
1354
1355 if (BufferSize != STARTING_BUFFER_SIZE)
1356 {
1357 FMemory::Free(Buffer);
1358 }
1359}
1360
1361static_assert(PLATFORM_LITTLE_ENDIAN, "FString serialization needs updating to support big-endian platforms!");
1362
1363FArchive& operator<<( FArchive& Ar, FString& A )
1364{
1365 // > 0 for ANSICHAR, < 0 for UTF16CHAR serialization
1366 static_assert(sizeof(UTF16CHAR) == sizeof(UCS2CHAR), "UTF16CHAR and UCS2CHAR are assumed to be the same size!");
1367
1368 if (Ar.IsLoading())
1369 {
1370 int32 SaveNum = 0;
1371 Ar << SaveNum;
1372
1373 bool bLoadUnicodeChar = SaveNum < 0;
1374 if (bLoadUnicodeChar)
1375 {
1376 // If SaveNum cannot be negated due to integer overflow, Ar is corrupted.
1377 if (SaveNum == MIN_int32)
1378 {
1380 //UE_LOG(LogCore, Error, TEXT("Archive is corrupted"));
1381 return Ar;
1382 }
1383
1384 SaveNum = -SaveNum;
1385 }
1386
1387 int64 MaxSerializeSize = Ar.GetMaxSerializeSize();
1388 // Protect against network packets allocating too much memory
1389 if ((MaxSerializeSize > 0) && (SaveNum > MaxSerializeSize))
1390 {
1392 //UE_LOG(LogCore, Error, TEXT("String is too large (Size: %i, Max: %i)"), SaveNum, MaxSerializeSize);
1393 return Ar;
1394 }
1395
1396 // Resize the array only if it passes the above tests to prevent rogue packets from crashing
1397 A.Data.Empty (SaveNum);
1398 A.Data.AddUninitialized(SaveNum);
1399
1400 if (SaveNum)
1401 {
1402 if (bLoadUnicodeChar)
1403 {
1404 // read in the unicode string
1405 auto Passthru = StringMemoryPassthru<UCS2CHAR>(A.Data.GetData(), SaveNum, SaveNum);
1406 Ar.Serialize(Passthru.Get(), SaveNum * sizeof(UCS2CHAR));
1407 if (Ar.IsByteSwapping())
1408 {
1409 for (int32 CharIndex = 0; CharIndex < SaveNum; ++CharIndex)
1410 {
1411 Passthru.Get()[CharIndex] = ByteSwap(Passthru.Get()[CharIndex]);
1412 }
1413 }
1414 // Ensure the string has a null terminator
1415 Passthru.Get()[SaveNum-1] = '\0';
1416 Passthru.Apply();
1417
1418 // Inline combine any surrogate pairs in the data when loading into a UTF-32 string
1420
1421 // Since Microsoft's vsnwprintf implementation raises an invalid parameter warning
1422 // with a character of 0xffff, scan for it and terminate the string there.
1423 // 0xffff isn't an actual Unicode character anyway.
1424 int Index = 0;
1425 if(A.FindChar(0xffff, Index))
1426 {
1427 A[Index] = TEXT('\0');
1429 }
1430 }
1431 else
1432 {
1433 auto Passthru = StringMemoryPassthru<ANSICHAR>(A.Data.GetData(), SaveNum, SaveNum);
1434 Ar.Serialize(Passthru.Get(), SaveNum * sizeof(ANSICHAR));
1435 // Ensure the string has a null terminator
1436 Passthru.Get()[SaveNum-1] = '\0';
1437 Passthru.Apply();
1438 }
1439
1440 // Throw away empty string.
1441 if (SaveNum == 1)
1442 {
1443 A.Data.Empty();
1444 }
1445 }
1446 }
1447 else
1448 {
1449 A.Data.CountBytes(Ar);
1450
1451 const bool bSaveUnicodeChar = Ar.IsForcingUnicode() || !FCString::IsPureAnsi(*A);
1452 if (bSaveUnicodeChar)
1453 {
1454 // Note: This is a no-op on platforms that are using a 16-bit TCHAR
1455 FTCHARToUTF16 UTF16String(*A, A.Len() + 1); // include the null terminator
1456 int32 Num = UTF16String.Length() + 1; // include the null terminator
1457
1458 int32 SaveNum = -Num;
1459 Ar << SaveNum;
1460
1461 if (Num)
1462 {
1463 if (!Ar.IsByteSwapping())
1464 {
1465 Ar.Serialize((void*)UTF16String.Get(), sizeof(UTF16CHAR) * Num);
1466 }
1467 else
1468 {
1469 TArray<UTF16CHAR> Swapped(UTF16String.Get(), Num);
1470 for (int32 CharIndex = 0; CharIndex < Num; ++CharIndex)
1471 {
1472 Swapped[CharIndex] = ByteSwap(Swapped[CharIndex]);
1473 }
1474 Ar.Serialize((void*)Swapped.GetData(), sizeof(UTF16CHAR) * Num);
1475 }
1476 }
1477 }
1478 else
1479 {
1480 int32 Num = A.Data.Num();
1481 Ar << Num;
1482
1483 if (Num)
1484 {
1485 Ar.Serialize((void*)StringCast<ANSICHAR>(A.Data.GetData(), Num).Get(), sizeof(ANSICHAR) * Num);
1486 }
1487 }
1488 }
1489
1490 return Ar;
1491}
1492template <typename CharType>
1493int32 HexToBytes(const FString& HexString, uint8* OutBytes)
1494{
1495 FWideStringView Hex(HexString);
1496 const int32 HexCount = Hex.Len();
1497 const CharType* HexPos = Hex.GetData();
1498 const CharType* HexEnd = HexPos + HexCount;
1499 uint8* OutPos = OutBytes;
1500 if (const bool bPadNibble = (HexCount % 2) == 1)
1501 {
1502 *OutPos++ = TCharToNibble(*HexPos++);
1503 }
1504 while (HexPos != HexEnd)
1505 {
1506 const uint8 HiNibble = uint8(TCharToNibble(*HexPos++) << 4);
1507 *OutPos++ = HiNibble | TCharToNibble(*HexPos++);
1508 }
1509 return static_cast<int32>(OutPos - OutBytes);
1510}
1511
1512int32 FindMatchingClosingParenthesis(const FString& TargetString, const int32 StartSearch)
1513{
1514 check(StartSearch >= 0 && StartSearch <= TargetString.Len());// Check for usage, we do not accept INDEX_NONE like other string functions
1515
1516 const TCHAR* const StartPosition = (*TargetString) + StartSearch;
1517 const TCHAR* CurrPosition = StartPosition;
1518 int32 ParenthesisCount = 0;
1519
1520 // Move to first open parenthesis
1521 while (*CurrPosition != 0 && *CurrPosition != TEXT('('))
1522 {
1523 ++CurrPosition;
1524 }
1525
1526 // Did we find the open parenthesis
1527 if (*CurrPosition == TEXT('('))
1528 {
1529 ++ParenthesisCount;
1530 ++CurrPosition;
1531
1532 while (*CurrPosition != 0 && ParenthesisCount > 0)
1533 {
1534 if (*CurrPosition == TEXT('('))
1535 {
1536 ++ParenthesisCount;
1537 }
1538 else if (*CurrPosition == TEXT(')'))
1539 {
1540 --ParenthesisCount;
1541 }
1542 ++CurrPosition;
1543 }
1544
1545 // Did we find the matching close parenthesis
1546 if (ParenthesisCount == 0 && *(CurrPosition - 1) == TEXT(')'))
1547 {
1548 return StartSearch + UE_PTRDIFF_TO_INT32((CurrPosition - 1) - StartPosition);
1549 }
1550 }
1551
1552 return INDEX_NONE;
1553}
1554
1555FString SlugStringForValidName(const FString& DisplayString, const TCHAR* ReplaceWith /*= TEXT("")*/)
1556{
1557 FString GeneratedName = DisplayString;
1558
1559 // Convert the display label, which may consist of just about any possible character, into a
1560 // suitable name for a UObject (remove whitespace, certain symbols, etc.)
1561 {
1562 for (int32 BadCharacterIndex = 0; BadCharacterIndex < UE_ARRAY_COUNT(TEXT("\"' ,/.:|&!~\n\r\t@#(){}[]=;^%$`")) - 1; ++BadCharacterIndex)
1563 {
1564 const TCHAR TestChar[2] = { TEXT("\"' ,/.:|&!~\n\r\t@#(){}[]=;^%$`")[BadCharacterIndex], TEXT('\0') };
1565 const int32 NumReplacedChars = GeneratedName.ReplaceInline(TestChar, ReplaceWith);
1567 }
1568
1569 return GeneratedName;
1570}
1571
1572void FTextRange::CalculateLineRangesFromString(const FString& Input, TArray<FTextRange>& LineRanges)
1573{
1574 int32 LineBeginIndex = 0;
1575
1576 // Loop through splitting at new-lines
1577 const TCHAR* const InputStart = *Input;
1578 for (const TCHAR* CurrentChar = InputStart; CurrentChar && *CurrentChar; ++CurrentChar)
1579 {
1580 // Handle a chain of \r\n slightly differently to stop the FChar::IsLinebreak adding two separate new-lines
1581 const bool bIsWindowsNewLine = (*CurrentChar == '\r' && *(CurrentChar + 1) == '\n');
1582 if (bIsWindowsNewLine || FChar::IsLinebreak(*CurrentChar))
1583 {
1584 const int32 LineEndIndex = UE_PTRDIFF_TO_INT32(CurrentChar - InputStart);
1585 check(LineEndIndex >= LineBeginIndex);
1586 LineRanges.Emplace(FTextRange(LineBeginIndex, LineEndIndex));
1587
1588 if (bIsWindowsNewLine)
1589 {
1590 ++CurrentChar; // skip the \n of the \r\n chain
1591 }
1592 LineBeginIndex = UE_PTRDIFF_TO_INT32(CurrentChar - InputStart) + 1; // The next line begins after the end of the current line
1593 }
1594 }
1595
1596 // Process any remaining string after the last new-line
1597 if (LineBeginIndex <= Input.Len())
1598 {
1599 LineRanges.Emplace(FTextRange(LineBeginIndex, Input.Len()));
1600 }
1601}
1602
1604{
1605 InlineCombineSurrogates_Array(Str.GetCharArray());
1606}
#define ARK_API
Definition Ark.h:16
#define checkSlow(expr)
#define check(expr)
@ INDEX_NONE
#define UE_PTRDIFF_TO_INT32(argument)
#define UE_DISABLE_OPTIMIZATION_SHIP
#define UE_ENABLE_OPTIMIZATION_SHIP
#define MAX_int32
#define MIN_int32
FPlatformTypes::CHAR16 UCS2CHAR
A 16-bit character containing a UCS2 (Unicode, 16-bit, fixed-width) code unit, used for compatibility...
Definition Platform.h:975
#define PLATFORM_LITTLE_ENDIAN
Definition Platform.h:144
#define TEXT(x)
Definition Platform.h:1108
#define FORCEINLINE
Definition Platform.h:644
#define UE_NODISCARD
Definition Platform.h:660
FPlatformTypes::CHAR16 UTF16CHAR
A 16-bit character containing a UTF16 (Unicode, 16-bit, variable-width) code unit.
Definition Platform.h:977
int32 HexToBytes(const FString &HexString, uint8 *OutBytes)
Definition String.cpp:1493
static const uint32 MaxSupportedEscapeChars
Definition String.cpp:1274
static const TCHAR * CharToEscapeSeqMap[][2]
Definition String.cpp:1263
UE_DISABLE_OPTIMIZATION_SHIP void StripNegativeZero(double &InFloat)
Definition String.cpp:955
#define STARTING_BUFFER_SIZE
Definition String.cpp:1325
FString SlugStringForValidName(const FString &DisplayString, const TCHAR *ReplaceWith)
Definition String.cpp:1555
void AppendCharacters(TArray< TCHAR > &Out, const CharType *Str, int32 Count)
Definition String.cpp:132
TStringPointer< TCHAR, UTF16CHAR > FTCHARToUTF16
Definition StringConv.h:994
int32 FindMatchingClosingParenthesis(const FString &TargetString, const int32 StartSearch=0)
Definition String.cpp:1512
#define UE_ARRAY_COUNT(array)
#define GET_VARARGS_RESULT(msg, msgsize, len, lastarg, fmt, result)
Definition VarArgs.h:39
virtual void Serialize(void *V, int64 Length)
Definition Archive.h:1569
ARK_API void AppendChars(const UCS2CHAR *Str, int32 Count)
Definition String.cpp:357
ARK_API FString(const UTF8CHAR *Str)
Definition String.cpp:251
FORCEINLINE const TCHAR & operator[](int32 Index) const UE_LIFETIMEBOUND
FORCEINLINE void MidInline(int32 Start, int32 Count=MAX_int32, bool bAllowShrinking=true)
UE_NODISCARD FString Reverse() const &
Definition String.cpp:804
UE_NODISCARD FString TrimStart() const &
Definition String.cpp:702
UE_NODISCARD FORCEINLINE FString Mid(int32 Start) const &
ARK_API void AppendChars(const ANSICHAR *Str, int32 Count)
Definition String.cpp:345
UE_NODISCARD static ARK_API FString ConcatFF(const FString &Lhs, const FString &Rhs)
Definition String.cpp:611
static UE_NODISCARD FString FromHexBlob(const uint8 *SrcBuffer, const uint32 SrcSize)
Definition String.cpp:919
FORCEINLINE TCHAR & operator[](int32 Index) UE_LIFETIMEBOUND
UE_NODISCARD FString TrimEnd() const &
Definition String.cpp:725
void TrimStartInline()
Definition String.cpp:692
UE_NODISCARD static ARK_API FString ConcatFF(FString &&Lhs, FString &&Rhs)
Definition String.cpp:614
ARK_API FString(int32 Len, const WIDECHAR *Str)
Definition String.cpp:254
ARK_API void AppendChars(const UTF8CHAR *Str, int32 Count)
Definition String.cpp:363
UE_NODISCARD FString TrimStart() &&
Definition String.cpp:709
UE_NODISCARD static ARK_API FString ConcatFF(FString &&Lhs, const FString &Rhs)
Definition String.cpp:612
UE_NODISCARD FString TrimStartAndEnd() &&
Definition String.cpp:686
UE_NODISCARD FString TrimQuotes(bool *bQuotesRemoved=nullptr) const &
Definition String.cpp:770
static UE_NODISCARD FORCEINLINE FString FromInt(int32 Num)
FString & operator=(FString &&)=default
ARK_API void RemoveAt(int32 Index, int32 Count=1, bool bAllowShrinking=true)
Definition String.cpp:484
UE_NODISCARD FString Mid(int32 Start, int32 Count) const &
static bool ToHexBlob(const FString &Source, uint8 *DestBuffer, const uint32 DestSize)
Definition String.cpp:931
void ConvertTabsToSpacesInline(const int32 InSpacesPerTab)
Definition String.cpp:1296
ARK_API int32 ParseIntoArrayLines(TArray< FString > &OutArray, bool InCullEmpty=true) const
Definition String.cpp:1147
UE_NODISCARD FString RightPad(int32 ChCount) const
Definition String.cpp:1055
UE_NODISCARD FORCEINLINE int32 Len() const
void TrimQuotesInline(bool *bQuotesRemoved=nullptr)
Definition String.cpp:765
void TrimEndInline()
Definition String.cpp:715
UE_NODISCARD FORCEINLINE friend FString operator+(const FString &Lhs, FString &&Rhs)
UE_NODISCARD FString TrimEnd() &&
Definition String.cpp:732
bool Split(const FString &InS, FString *LeftS, FString *RightS, ESearchCase::Type SearchCase, ESearchDir::Type SearchDir=ESearchDir::FromStart) const
Definition String.cpp:384
static UE_NODISCARD FString FromBlob(const uint8 *SrcBuffer, const uint32 SrcSize)
Definition String.cpp:884
ARK_API void AppendChars(const WIDECHAR *Str, int32 Count)
Definition String.cpp:351
ARK_API FString(const ANSICHAR *Str, int32 ExtraSlack)
Definition String.cpp:257
UE_NODISCARD bool IsNumeric() const
ARK_API FString(const UCS2CHAR *Str, int32 ExtraSlack)
Definition String.cpp:260
FString(const FString &)=default
static UE_NODISCARD FString FormatAsNumber(int32 InNumber)
Definition String.cpp:837
UE_NODISCARD FORCEINLINE const TCHAR * operator*() const UE_LIFETIMEBOUND
UE_NODISCARD FString TrimQuotes(bool *bQuotesRemoved=nullptr) &&
Definition String.cpp:777
ARK_API FString(const WIDECHAR *Str)
Definition String.cpp:250
void TrimStartAndEndInline()
Definition String.cpp:680
ARK_API FString(const UCS2CHAR *Str)
Definition String.cpp:252
void SerializeAsANSICharArray(FArchive &Ar, int32 MinCharacters=0) const
Definition String.cpp:864
UE_NODISCARD static ARK_API FString ConcatFF(const FString &Lhs, FString &&Rhs)
Definition String.cpp:613
UE_NODISCARD int32 Find(const FString &SubStr, ESearchCase::Type SearchCase=ESearchCase::IgnoreCase, ESearchDir::Type SearchDir=ESearchDir::FromStart, int32 StartPosition=INDEX_NONE) const
static bool ToBlob(const FString &Source, uint8 *DestBuffer, const uint32 DestSize)
Definition String.cpp:896
void RemoveSpacesInline()
Definition String.cpp:425
void Shrink()
Definition String.cpp:327
ARK_API void Empty()
Definition String.cpp:321
void InsertAt(int32 Index, const FString &Characters)
Definition String.cpp:469
ARK_API FString(const WIDECHAR *Str, int32 ExtraSlack)
Definition String.cpp:258
UE_NODISCARD FORCEINLINE bool Contains(const FString &SubStr, ESearchCase::Type SearchCase=ESearchCase::IgnoreCase, ESearchDir::Type SearchDir=ESearchDir::FromStart) const
void ReverseString()
Definition String.cpp:817
bool Split(const FString &InS, FString *LeftS, FString *RightS) const
Definition String.cpp:414
ARK_API FString(int32 Len, const UCS2CHAR *Str)
Definition String.cpp:256
ARK_API FString(const ANSICHAR *Str)
Definition String.cpp:249
ARK_API FString(const UTF8CHAR *Str, int32 ExtraSlack)
Definition String.cpp:259
ARK_API FString(int32 Len, const ANSICHAR *Str)
Definition String.cpp:253
void Reserve(int32 CharacterCount)
Definition String.cpp:307
static UE_NODISCARD FString SanitizeFloat(double InFloat, const int32 InMinFractionalDigits=1)
Definition String.cpp:964
UE_NODISCARD FString ReplaceQuotesWithEscapedQuotes() &&
Definition String.cpp:1232
ARK_API void Empty(int32 Slack)
Definition String.cpp:316
UE_NODISCARD FString LeftPad(int32 ChCount) const
Definition String.cpp:1042
UE_NODISCARD FORCEINLINE friend FString operator+(FString &&Lhs, const FString &Rhs)
UE_NODISCARD FString Reverse() &&
Definition String.cpp:811
UE_NODISCARD FORCEINLINE DataType & GetCharArray() UE_LIFETIMEBOUND
ARK_API FString(int32 Len, const UTF8CHAR *Str)
Definition String.cpp:255
void TrimToNullTerminator()
Definition String.cpp:369
UE_NODISCARD FORCEINLINE FString Left(int32 Count) const &
FORCEINLINE void CheckInvariants() const
FORCEINLINE void LeftInline(int32 Count, bool bAllowShrinking=true)
FString & operator=(const FString &)=default
void ReplaceEscapedCharWithCharInline(const TArray< TCHAR > *Chars=nullptr)
Definition String.cpp:1276
@ CaseSensitive
Definition CString.h:25
void InlineCombineSurrogates(FString &Str)
Definition String.cpp:1603
bool MatchesWildcardRecursive(const TCHAR *Target, int32 TargetLength, const TCHAR *Wildcard, int32 WildcardLength)
Definition String.cpp:49
FORCEINLINE void ConstructWithSlack(TArray< TCHAR > &Data, const CharType *Src, int32 ExtraSlack)
Definition String.cpp:217
UE_NODISCARD FORCEINLINE FString ConcatFStringRange(LhsType &&Lhs, const TCHAR *Rhs, int32 RhsLen)
Definition String.cpp:571
UE_NODISCARD FORCEINLINE FString ConcatFStrings(LhsType &&Lhs, RhsType &&Rhs)
Definition String.cpp:525
UE_NODISCARD FORCEINLINE FString ConcatRangeFString(const TCHAR *Lhs, int32 LhsLen, RhsType &&Rhs)
Definition String.cpp:544
UE_NODISCARD FORCEINLINE FString ConcatCStringFString(const TCHAR *Lhs, RhsType &&Rhs)
Definition String.cpp:587
UE_NODISCARD FORCEINLINE FString ConcatFStringCString(LhsType &&Lhs, const TCHAR *Rhs)
Definition String.cpp:599
FORCEINLINE void ConstructFromCString(TArray< TCHAR > &Data, const CharType *Src)
Definition String.cpp:186
FORCEINLINE void ConstructWithLength(TArray< TCHAR > &Data, int32 InCount, const CharType *InSrc)
Definition String.cpp:200
Definition Vector.h:40
void SetCriticalError()
Definition Archive.h:113
FORCEINLINE bool IsByteSwapping()
Definition Archive.h:165
FORCEINLINE int64 GetMaxSerializeSize() const
Definition Archive.h:481
FORCEINLINE bool IsLoading() const
Definition Archive.h:249
static void Free(void *Original)
static FORCEINLINE void * Memmove(void *Dest, const void *Src, SIZE_T Count)
static void * Realloc(void *Original, SIZE_T Size, uint32 Alignment=DEFAULT_ALIGNMENT)
static FORCEINLINE void * Memcpy(void *Dest, const void *Src, SIZE_T Count)
static FORCEINLINE bool Compare(TCHAR Lhs, TCHAR Rhs)
Definition String.cpp:42
static FORCEINLINE bool Compare(TCHAR Lhs, TCHAR Rhs)
Definition String.cpp:34