UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
UTF8StringBuilder.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
7#include <stdio.h> // snprintf
8#include <cstring> // strlen
9
10namespace uLang
11{
12
13// Indent defaults
14static const char * DefaultIndentCString = " ";
15static const uint32_t DefaultIndentCStringByteCount = 4u; // Number of bytes for DefaultIndentCString
16static const uint32_t DefaultIndentEffectiveSpaceCount = 4u;
17static const CUTF8StringView DefaultIndentString = CUTF8StringView(DefaultIndentCString);
18
19
22template<class AllocatorType, typename... AllocatorArgsType>
24{
25public:
26
27 using StringType = TUTF8String<AllocatorType, AllocatorArgsType...>;
28
29 // Construction
30
31 TUTF8StringBuilder(uint32_t ReserveBytes = 0) : _AllocatedBytes(0) { if (ReserveBytes) { EnsureAllocated(ReserveBytes + 1); } }
34 template<typename... FormatterArgsType>
38
39 ~TUTF8StringBuilder() {} // Memory will be released via the embedded string object
40
41 void Reset() { _String._String._End = _String._String._Begin; } // Just reset length but hold on to memory
42 void EnsureAllocatedExtra(size_t ExtraBytes) { EnsureAllocated(_String.ByteLen() + ExtraBytes); }
43
44
45 // Accessors
46
47 ULANG_FORCEINLINE int32_t ByteLen() const { return _String.ByteLen(); }
48 ULANG_FORCEINLINE bool IsEmpty() const { return _String.IsEmpty(); }
49 ULANG_FORCEINLINE bool IsFilled() const { return _String.IsFilled(); }
50
53 {
54 return _String[ByteIndex];
55 }
56
59
60 // Comparison operators
61
62 template<class OtherAllocatorType, typename... OtherAllocatorArgsType>
64 template<class OtherAllocatorType, typename... OtherAllocatorArgsType>
66 template<class OtherAllocatorType, typename... OtherAllocatorArgsType>
68 template<class OtherAllocatorType, typename... OtherAllocatorArgsType>
70 ULANG_FORCEINLINE bool operator==(const CUTF8StringView& StringView) const { return _String == StringView; }
71 ULANG_FORCEINLINE bool operator!=(const CUTF8StringView& StringView) const { return _String != StringView; }
72
73 // Assignment
74
78
79 // Conversions
80
81 ULANG_FORCEINLINE operator const CUTF8StringView&() const { return _String; }
82 ULANG_FORCEINLINE const CUTF8StringView& ToStringView() const { return _String; }
83 ULANG_FORCEINLINE const char* AsCString() const { return _String.AsCString(); }
84 ULANG_FORCEINLINE const char* operator*() const { return _String.AsCString(); }
85
86 StringType MoveToString(); // Move string and reset the builder (most efficient)
87 StringType CopyToString() const { return _String; } // Make copy of string and keep builder as-is (less efficient)
88
89 // Modifications
90
93 template<typename... FormatterArgsType>
104 UTF8Char* AppendBuffer(size_t ByteSize);
105
108
111
114
117
120
137 int32_t LineIndentEditor(int32_t Idx = 0, int32_t SpanCount = -1, int32_t SpaceCount = DefaultIndentEffectiveSpaceCount);
138
139protected:
140
143
144private:
145
147 ULANG_FORCEINLINE static size_t CalculateBytesToAllocate(size_t RequestedBytes)
148 {
149 // For now, we just grow in fixed increments of big chunks
150 // Exponential growth of small chunks does not seem like a good idea
151 constexpr size_t Alignment = 1 << 11; // 2K
152 return (RequestedBytes + (Alignment - 1)) & ~(Alignment - 1);
153 }
154
156 ULANG_FORCEINLINE void Construct(const UTF8Char* String, size_t ByteLength, const AllocatorType& Allocator)
157 {
158 size_t BytesToAllocate = CalculateBytesToAllocate(ByteLength + 1);
160 _String._String._Begin = Memory;
161 _String._String._End = Memory + ByteLength;
162 if (ByteLength)
163 {
164 memcpy(Memory, String, ByteLength);
165 }
166 Memory[ByteLength] = 0; // Add null termination
167 _String.GetAllocator() = Allocator; // Remember the allocator
168 _AllocatedBytes = uint32_t(BytesToAllocate);
169 }
170
172 ULANG_FORCEINLINE void EnsureAllocated(size_t BytesNeeded)
173 {
174 // Do we need to grow?
175 BytesNeeded = CalculateBytesToAllocate(BytesNeeded);
176 if (BytesNeeded > _AllocatedBytes)
177 {
178 // Yes, grow our allocation
179 UTF8Char* Memory = (UTF8Char*)_String.GetAllocator().Reallocate((void*)_String._String._Begin, BytesNeeded);
180 _String._String._End = Memory + _String.ByteLen();
181 _String._String._Begin = Memory;
182 _AllocatedBytes = uint32_t(BytesNeeded);
183 }
184 }
185
186 // We repurpose a TUTF8String to store the string
187 StringType _String;
188
189 // How many bytes we have actually allocated
190 uint32_t _AllocatedBytes;
191};
192
195
198
199//=======================================================================================
200// TUTF8StringBuilder Inline Methods
201//=======================================================================================
202
203template<class AllocatorType, typename... AllocatorArgsType>
209
210template<class AllocatorType, typename... AllocatorArgsType>
212 : _String(NoInit)
213{
214 Construct(StringView._Begin, StringView.ByteLen(), AllocatorType(uLang::ForwardArg<AllocatorArgsType>(AllocatorArgs)...));
215}
216
218template<class AllocatorType, typename... AllocatorArgsType>
219template<typename... FormatterArgsType>
221 : _String(NoInit)
222{
223 // Compute length of string
224 size_t ByteLength = ::snprintf(nullptr, 0, NullTerminatedFormat, FormatterArgs...);
225
226 // Allocate memory
227 AllocatorType Allocator(AllocatorArgs...);
228 size_t BytesToAllocate = CalculateBytesToAllocate(ByteLength + 1);
230
231 // Create string
233
234 // Store string and allocator
235 _String._String = CUTF8StringView(Text, Text + ByteLength);
236 _String.GetAllocator() = Allocator;
237 _AllocatedBytes = uint32_t(BytesToAllocate);
238}
240
241template<class AllocatorType, typename... AllocatorArgsType>
243 : _String(NoInit)
244{
245 Construct(Other._String._String._Begin, Other.ByteLen(), Other._String.GetAllocator());
246}
247
248template<class AllocatorType, typename... AllocatorArgsType>
250 : _String(ForwardArg<StringType>(Other._String))
251 , _AllocatedBytes(Other._AllocatedBytes)
252{
253 Other._AllocatedBytes = 0;
254}
255
256template<class AllocatorType, typename... AllocatorArgsType>
258{
260 if (_String.IsFilled())
261 {
262 size_t ByteLength = ByteLen();
263 UTF8Char* Memory = (UTF8Char*)_String.GetAllocator().Reallocate((void*)_String._String._Begin, ByteLength + 1);
264 String._String._Begin = Memory;
265 String._String._End = Memory + ByteLength;
266 _String._String.Reset();
267 }
268 else
269 {
270 _String.Reset(); // Free potential slack memory
271 String._String.Reset();
272 }
273
274 String.GetAllocator() = _String.GetAllocator();
275 _AllocatedBytes = 0;
276
277 return String;
278}
279
280template<class AllocatorType, typename... AllocatorArgsType>
282{
283 int32_t ByteLen = Other.ByteLen();
284 if (ByteLen)
285 {
286 EnsureAllocated(ByteLen + 1u);
287 memcpy(const_cast<UTF8Char*>(_String._String._Begin), Other._String._String._Begin, ByteLen + 1u);
288 }
289 // Note, no need to write null terminator if empty if _Begin == _End then "" returned by AsCString()
290 _String._String._End = (UTF8Char*)_String._String._Begin + ByteLen;
291 _String.GetAllocator() = Other._String.GetAllocator();
292 return *this;
293}
294
295template<class AllocatorType, typename... AllocatorArgsType>
297{
298 _String = Move(Other._String);
299 _AllocatedBytes = Other._AllocatedBytes;
300 Other._AllocatedBytes = 0;
301 return *this;
302}
303
304template<class AllocatorType, typename... AllocatorArgsType>
306{
307 int32_t ByteLen = StringView.ByteLen();
308 if (ByteLen)
309 {
310 EnsureAllocated(ByteLen + 1u);
311 memcpy((UTF8Char*)_String._String._Begin, StringView._Begin, ByteLen);
312 UTF8Char* WritableEnd = (UTF8Char*)_String._String._Begin + ByteLen;
313 *WritableEnd = 0;
314 _String._String._End = WritableEnd;
315 return *this;
316 }
317 // No need to write null terminator if empty if _Begin == _End then "" returned by AsCString()
318 _String._String._End = (UTF8Char*)_String._String._Begin;
319 return *this;
320}
321
322template<class AllocatorType, typename... AllocatorArgsType>
324{
325 EnsureAllocated(ByteLen() + 2);
326 UTF8Char* WritableEnd = (UTF8Char*)_String._String._End; // Local variable to avoid LHS
327 *WritableEnd++ = Char;
328 *WritableEnd = 0;
329 _String._String._End = WritableEnd;
330 return *this;
331}
332
333template<class AllocatorType, typename... AllocatorArgsType>
335{
336 size_t OtherLength = String.ByteLen();
337 if (OtherLength)
338 {
339 EnsureAllocated(ByteLen() + OtherLength + 1);
340 UTF8Char* WritableEnd = (UTF8Char*)_String._String._End; // Local variable to avoid LHS
343 *WritableEnd = 0;
344 _String._String._End = WritableEnd;
345 }
346 return *this;
347}
348
350template<class AllocatorType, typename... AllocatorArgsType>
351template<typename... FormatterArgsType>
353{
354 // Compute length of string
355 size_t ByteLength = ::snprintf(nullptr, 0, NullTerminatedFormat, FormatterArgs...);
356
357 // Allocate memory
358 EnsureAllocated(ByteLen() + ByteLength + 1);
359
360 // Create string
361 ::snprintf((char*)_String._String._End, _AllocatedBytes, NullTerminatedFormat, FormatterArgs...);
362
363 // Increase length
364 _String._String._End += ByteLength;
365
366 return *this;
367}
369
370template<class AllocatorType, typename... AllocatorArgsType>
372{
373 EnsureAllocated(ByteLen() + ByteSize + 1);
374 UTF8Char* BufferStart = (UTF8Char*)_String._String._End;
375
376 // We're growing the string, such that it can be serialized into -- in that regard,
377 // we want the _End to point to the end of the buffer
378 UTF8Char* NewEnd = (UTF8Char*)( ((uint8_t*)BufferStart) + ByteSize );
379 _String._String._End = NewEnd;
380
381 // Zero, just to guard against users failing to serialize into this buffer immediately
382 *BufferStart = 0;
383 *NewEnd = 0;
384
385 return BufferStart;
386}
387
388template<class AllocatorType, typename... AllocatorArgsType>
390{
391 ULANG_ASSERTF(ToBeReplaced._Begin <= uint32_t(ByteLen()) && ToBeReplaced._End <= uint32_t(ByteLen()) && ToBeReplaced._Begin <= ToBeReplaced._End, "Malformed index range.");
392
393 size_t NewLength = ByteLen() - ToBeReplaced.GetLength() + Replacement.ByteLen();
394 EnsureAllocated(NewLength + 1);
395 memmove(const_cast<UTF8Char*>(_String._String._Begin) + ToBeReplaced._Begin + Replacement.ByteLen(), _String._String._Begin + ToBeReplaced._End, ByteLen() + 1 - ToBeReplaced._End);
396 if (Replacement.ByteLen())
397 {
398 memcpy(const_cast<UTF8Char*>(_String._String._Begin) + ToBeReplaced._Begin, Replacement._Begin, Replacement.ByteLen());
399 }
400 _String._String._End = _String._String._Begin + NewLength;
401 return *this;
402}
403
404template<class AllocatorType, typename... AllocatorArgsType>
406{
407 ULANG_ASSERTF(Index >= 0 && Index <= ByteLen(), "Out-of-bounds index.");
408 const_cast<UTF8Char*>(_String._String._Begin)[Index] = Replacement;
409 return *this;
410}
411
412template <class AllocatorType, typename... AllocatorArgsType>
414{
415 for (UTF8Char* Ch = const_cast<UTF8Char*>(_String._String._Begin); Ch < _String._String._End; ++Ch)
416 {
417 if (*Ch == Search)
418 {
419 *Ch = Replacement;
420 }
421 }
422 return *this;
423}
424
425template<class AllocatorType, typename... AllocatorArgsType>
427{
428 ULANG_ASSERTF(Index >= 0 && Index <= ByteLen(), "Out-of-bounds index.");
429
430 size_t NewLength = ByteLen() + StringToInsert.ByteLen();
431 EnsureAllocated(NewLength + 1);
432 memmove(const_cast<UTF8Char*>(_String._String._Begin) + Index + StringToInsert.ByteLen(), _String._String._Begin + Index, ByteLen() + 1 - Index);
433 if (StringToInsert.ByteLen())
434 {
435 memcpy(const_cast<UTF8Char*>(_String._String._Begin) + Index, StringToInsert._Begin, StringToInsert.ByteLen());
436 }
437 _String._String._End = _String._String._Begin + NewLength;
438 return *this;
439}
440
441template<class AllocatorType, typename... AllocatorArgsType>
443{
444 if (_String._String.IsFilled() && _String._String._End[-1] == Ch)
445 {
446 ((UTF8Char*)(--_String._String._End))[0] = 0;
447 }
448 return *this;
449}
450
451template<class AllocatorType, typename... AllocatorArgsType>
453{
454 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
455 // Resolve span and determine if there is anything to do
456 if (!InputByteIdxSpan(Idx, SpanCount))
457 {
458 return 0;
459 }
460
461 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
462 // Determine number of indented lines to determine how much more space needed
463 UTF8Char* CStr = const_cast<UTF8Char*>(_String._String._Begin) + Idx;
465 int32_t LineCount = 1; // First line counts as #1
466 int32_t IndentCount = 0; // Not every line may be indented
467 bool bNonWS = false; // Found preceding non-whitespace before newline
468
469 while (CStr < CStrEnd)
470 {
471 switch (*CStr)
472 {
473 case '\n':
474 if (bNonWS)
475 {
476 IndentCount++;
477 bNonWS = false;
478 }
479
480 LineCount++;
481 break;
482
483 case ' ':
484 case '\t':
485 case '\r':
486 // Ignored whitespace other than newline
487 break;
488
489 default:
490 // Only indent lines that have non-whitespace in them
491 bNonWS = true;
492 }
493
494 CStr++;
495 }
496
497 if (bNonWS)
498 {
499 IndentCount++;
500 }
501
502 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
503 // Ensure enough memory for indenting
504 int32_t ExtraBytes = IndentCount * SpaceCount;
505
506 EnsureAllocatedExtra(ExtraBytes);
507
508 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
509 // Move mem from: range end - old end to: range end new - end new
510 size_t Bytes = ByteLen() - (Idx + SpanCount) + 1u; // Extra char for null terminator
511 UTF8Char* CStrStart = const_cast<UTF8Char*>(_String._String._Begin);
512 CStrEnd = CStrStart + Idx + SpanCount;
515
516 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
517 // Work in reverse over selected section (so bytes only copied once)
519 UTF8Char* CStrBegin = CStrStart + Idx; // Beginning of selection
520 CStr = CStrEnd - 1;
521 do
522 {
523 // Read reverse until \n or start of selection found
524 bNonWS = false;
525 while ((CStr > CStrBegin) && (*CStr != '\n'))
526 {
527 if ((*CStr != ' ') && (*CStr != '\t') && (*CStr != '\r'))
528 {
529 bNonWS = true;
530 }
531
532 CStr--;
533 }
534
536
537 if (*CStr == '\n')
538 {
539 // Skip over newline
540 CStr++;
541 }
542
544
545 // Move section to end
546 CStrDest -= Bytes;
547 CStrEnd -= Bytes;
549
550 // Don't indent rows with no content
551 if (bNonWS)
552 {
553 // Write indent chars
555 if (SpaceCount)
556 {
558 }
559 }
560
561 // Keep scanning backward
562 CStr = CStrResume - 1;
563 } while (CStr >= CStrBegin);
564
565 // Fix up the internals
566 _String._String._End += ExtraBytes;
567
568 return LineCount;
569}
570
571
572} // namespace uLang
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
@ Char
Character type.
#define ULANG_FORCEINLINE
Definition Common.h:188
#define ULANG_SILENCE_SECURITY_WARNING_END
Definition Common.h:136
#define ULANG_SILENCE_SECURITY_WARNING_START
Definition Common.h:133
#define ULANG_ASSERTF(expr, format,...)
Definition Common.h:290
memcpy(InputBufferBase, BinkBlocksData, BinkBlocksSize)
Definition UTF8StringView.h:15
const UTF8Char * _Begin
Points to first byte.
Definition UTF8StringView.h:19
ULANG_FORCEINLINE int32_t InputByteIdxToDirectIdx(int32_t InIdx) const
Definition UTF8StringView.h:372
ULANG_FORCEINLINE bool InputByteIdxSpan(int32_t &InOutIdx, int32_t &InOutSpan) const
Definition UTF8StringView.h:392
ULANG_FORCEINLINE UTF8Char LastByte() const
Definition UTF8StringView.h:59
const UTF8Char * _End
Points to the byte after the last byte.
Definition UTF8StringView.h:20
ULANG_FORCEINLINE int32_t ByteLen() const
Definition UTF8StringView.h:35
Definition UTF8StringBuilder.h:24
void Reset()
Definition UTF8StringBuilder.h:41
void EnsureAllocatedExtra(size_t ExtraBytes)
Definition UTF8StringBuilder.h:42
TUTF8StringBuilder & operator=(const TUTF8StringBuilder &Other)
Definition UTF8StringBuilder.h:281
TUTF8StringBuilder & TrimEnd(UTF8Char Ch)
Definition UTF8StringBuilder.h:442
ULANG_FORCEINLINE bool operator!=(const CUTF8StringView &StringView) const
Definition UTF8StringBuilder.h:71
StringType CopyToString() const
Definition UTF8StringBuilder.h:87
TUTF8StringBuilder & Append(UTF8Char Char)
Definition UTF8StringBuilder.h:323
ULANG_FORCEINLINE bool IsEmpty() const
Definition UTF8StringBuilder.h:48
TUTF8StringBuilder(const CUTF8StringView &StringView, AllocatorArgsType &&... AllocatorArgs)
Definition UTF8StringBuilder.h:211
TUTF8StringBuilder(const TUTF8StringBuilder &Other)
Definition UTF8StringBuilder.h:242
TUTF8StringBuilder & AppendFormat(const char *NullTerminatedFormat, FormatterArgsType &&... FormatterArgs)
int32_t LineIndentEditor(int32_t Idx=0, int32_t SpanCount=-1, int32_t SpaceCount=DefaultIndentEffectiveSpaceCount)
Definition UTF8StringBuilder.h:452
TUTF8StringBuilder(AllocatorArgsType &&... AllocatorArgs, const char *NullTerminatedFormat, FormatterArgsType &&... FormatterArgs)
ULANG_FORCEINLINE const UTF8Char & operator[](int32_t ByteIndex) const
Definition UTF8StringBuilder.h:52
ULANG_FORCEINLINE bool InputByteIdxSpan(int32_t &InOutIdx, int32_t &InOutSpan) const
Definition UTF8StringBuilder.h:142
ULANG_FORCEINLINE int32_t InputByteIdxToDirectIdx(int32_t InIdx) const
Definition UTF8StringBuilder.h:141
ULANG_FORCEINLINE const CUTF8StringView & ToStringView() const
Definition UTF8StringBuilder.h:82
TUTF8StringBuilder & operator=(TUTF8StringBuilder &&Other)
Definition UTF8StringBuilder.h:296
TUTF8StringBuilder(uint32_t ReserveBytes=0)
Definition UTF8StringBuilder.h:31
ULANG_FORCEINLINE bool operator==(const TUTF8String< OtherAllocatorType, OtherAllocatorArgsType... > &Other) const
Definition UTF8StringBuilder.h:67
UTF8Char * AppendBuffer(size_t ByteSize)
Definition UTF8StringBuilder.h:371
~TUTF8StringBuilder()
Definition UTF8StringBuilder.h:39
ULANG_FORCEINLINE const char * operator*() const
Definition UTF8StringBuilder.h:84
TUTF8StringBuilder(const char *NullTerminatedString, AllocatorArgsType &&... AllocatorArgs)
Definition UTF8StringBuilder.h:204
TUTF8StringBuilder & ReplaceRange(SIdxRange ToBeReplaced, const CUTF8StringView &Replacement)
Definition UTF8StringBuilder.h:389
ULANG_FORCEINLINE bool operator!=(const TUTF8StringBuilder< OtherAllocatorType, OtherAllocatorArgsType... > &Other) const
Definition UTF8StringBuilder.h:65
ULANG_FORCEINLINE const char * AsCString() const
Definition UTF8StringBuilder.h:83
ULANG_FORCEINLINE bool operator!=(const TUTF8String< OtherAllocatorType, OtherAllocatorArgsType... > &Other) const
Definition UTF8StringBuilder.h:69
ULANG_FORCEINLINE bool operator==(const TUTF8StringBuilder< OtherAllocatorType, OtherAllocatorArgsType... > &Other) const
Definition UTF8StringBuilder.h:63
TUTF8StringBuilder & ReplaceAll(const UTF8Char Search, const UTF8Char Replacement)
Definition UTF8StringBuilder.h:413
StringType MoveToString()
Definition UTF8StringBuilder.h:257
TUTF8StringBuilder & ReplaceAt(int32_t Index, const UTF8Char Replacement)
Definition UTF8StringBuilder.h:405
ULANG_FORCEINLINE UTF8Char LastByte() const
Definition UTF8StringBuilder.h:58
ULANG_FORCEINLINE int32_t ByteLen() const
Definition UTF8StringBuilder.h:47
ULANG_FORCEINLINE bool IsFilled() const
Definition UTF8StringBuilder.h:49
TUTF8StringBuilder & InsertAt(int32_t Index, const CUTF8StringView &StringToInsert)
Definition UTF8StringBuilder.h:426
TUTF8StringBuilder & operator=(const CUTF8StringView &StringView)
Definition UTF8StringBuilder.h:305
TUTF8StringBuilder & Append(const CUTF8StringView &String)
Definition UTF8StringBuilder.h:334
TUTF8String< AllocatorType, AllocatorArgsType... > StringType
Definition UTF8StringBuilder.h:27
ULANG_FORCEINLINE bool operator==(const CUTF8StringView &StringView) const
Definition UTF8StringBuilder.h:70
TUTF8StringBuilder(TUTF8StringBuilder &&Other)
Definition UTF8StringBuilder.h:249
ULANG_FORCEINLINE bool IsEmpty() const
Definition UTF8String.h:47
ULANG_FORCEINLINE const CUTF8StringView & ToStringView() const
Definition UTF8String.h:96
ULANG_FORCEINLINE bool IsFilled() const
Definition UTF8String.h:48
ULANG_FORCEINLINE const char * AsCString() const
Definition UTF8String.h:50
ULANG_FORCEINLINE int32_t ByteLen() const
Definition UTF8String.h:46
uLang::CUTF8StringView CUTF8StringView
Definition VstNode.h:51
Definition VVMEngineEnvironment.h:23
@ NoInit
Definition Common.h:375
uint8_t UTF8Char
UTF-8 octet.
Definition Unicode.h:20
ULANG_FORCEINLINE T && ForwardArg(typename TRemoveReference< T >::Type &Obj)
Definition References.h:115
ULANG_FORCEINLINE TRemoveReference< T >::Type && Move(T &&Obj)
Definition References.h:86
U16 Index
Definition radfft.cpp:71
Definition IdxRange.h:16