UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
VVMArrayBase.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#if WITH_VERSE_VM || defined(__INTELLISENSE__)
6
9#include "Misc/Optional.h"
10#include "VerseVM/VVMAtomics.h"
11#include "VerseVM/VVMAux.h"
12#include "VerseVM/VVMCell.h"
15#include "VerseVM/VVMHeap.h"
16#include "VerseVM/VVMLog.h"
17#include "VerseVM/VVMVerse.h"
19
20namespace Verse
21{
22
23enum class ECompares : uint8;
24enum class EValueStringFormat;
25struct VInt;
26
27enum class EArrayType : uint8
28{
29 None,
30 VValue,
31 Int32,
32 Char8,
33 Char32
34};
35
36inline bool IsNullTerminatedString(EArrayType Type)
37{
38 return Type == EArrayType::Char8;
39}
40
41inline size_t ByteLength(EArrayType ArrayType)
42{
43 switch (ArrayType)
44 {
45 case EArrayType::None:
46 return 0; // Empty-Untyped VMutableArray
47 case EArrayType::VValue:
48 return sizeof(TWriteBarrier<VValue>);
49 case EArrayType::Int32:
50 return sizeof(int32);
51 case EArrayType::Char8:
52 return sizeof(UTF8CHAR);
53 case EArrayType::Char32:
54 return sizeof(UTF32CHAR);
55 default:
56 V_DIE("Unhandled EArrayType encountered!");
57 }
58}
59
60struct VBuffer : TAux<void>
61{
62 // Note: We don't need to align char/char32 arrays this way. However, that will
63 // make accessing the data branch on Type. So it might never be worth doing.
64 struct alignas(sizeof(VValue)) Header
65 {
66 uint32 NumValues;
67 // These are immutable per VBuffer. This means that if the GC sees a buffer with
68 // a particular type, it's type can't change while it's scanning the buffer.
69 const uint32 Capacity;
70 const EArrayType Type;
71 };
72
73 VBuffer() = default;
74
75 VBuffer(FAllocationContext Context, uint32 NumValues, uint32 Capacity, EArrayType Type)
76 {
77 V_DIE_IF(Type == EArrayType::None);
78 V_DIE_UNLESS(Capacity >= NumValues);
79
80 // If we are UTF8, add +1 to capacity and set a null-terminator
81 uint32 AllocationCapacity = Capacity + (IsNullTerminatedString(Type) ? 1 : 0);
83
84 Ptr = Context.AllocateAuxCell(sizeof(Header) + ByteLength(Type) * AllocationCapacity);
85 new (GetHeader()) Header{NumValues, Capacity, Type};
86
87 if (IsNullTerminatedString(Type))
88 {
90 }
91 }
92
93 VBuffer(FAllocationContext Context, uint32 NumValues, EArrayType Type)
94 : VBuffer(Context, NumValues, NumValues, Type)
95 {
96 }
97
98 Header* GetHeader() const
99 {
100 return static_cast<Header*>(Ptr);
101 }
102
103 void* GetDataStart()
104 {
105 return Ptr ? static_cast<char*>(Ptr) + sizeof(Header) : nullptr;
106 }
107
108 const void* GetDataStart() const
109 {
110 return const_cast<VBuffer*>(this)->GetDataStart();
111 }
112
113 EArrayType GetArrayType() const
114 {
115 if (Header* Header = GetHeader())
116 {
117 checkSlow(Header->Type != EArrayType::None);
118 return Header->Type;
119 }
120 return EArrayType::None;
121 }
122
123 uint32 Num() const
124 {
125 if (Header* Header = GetHeader())
126 {
127 return Header->NumValues;
128 }
129 return 0;
130 }
131
132 uint32 Capacity() const
133 {
134 if (Header* Header = GetHeader())
135 {
136 return Header->Capacity;
137 }
138 return 0;
139 }
140
141 void SetNullTerminator()
142 {
143 SetChar(Num(), static_cast<UTF8CHAR>(0));
144 }
145
146 template <bool bTransactional>
147 void SetVValue(FAllocationContext Context, uint32 Index, VValue Value)
148 {
149 checkSlow(GetArrayType() == EArrayType::VValue);
150 if constexpr (bTransactional)
151 {
153 }
154 else
155 {
157 }
158 }
160 {
161 checkSlow(GetArrayType() == EArrayType::Int32);
163 }
165 {
166 checkSlow(GetArrayType() == EArrayType::Char8);
168 }
170 {
171 checkSlow(GetArrayType() == EArrayType::Char32);
173 }
174
175 template <typename T = void>
176 T* GetData() { return BitCast<T*>(GetDataStart()); }
177
178 template <typename T = void>
179 const T* GetData() const { return BitCast<T*>(GetDataStart()); }
180};
181
182static_assert(IsTAux<VBuffer>);
183
184struct VArrayBase : VHeapValue
185{
187
188protected:
190
191 template <bool bTransactional = false>
192 void SetBufferWithoutStoreBarrier(FAllocationContext Context, VBuffer NewBuffer)
193 {
194 if constexpr (bTransactional)
195 {
196 reinterpret_cast<TWriteBarrier<TAux<void>>&>(Buffer).SetTransactionally(Context, NewBuffer);
197 }
198 else
199 {
200 Buffer.Set(Context, NewBuffer);
201 }
202 }
203 template <bool bTransactional = false>
204 void SetBufferWithStoreBarrier(FAllocationContext Context, VBuffer NewBuffer)
205 {
208 }
209
210 static EArrayType DetermineArrayType(VValue Value)
211 {
212 if (Value.IsInt32())
213 {
214 return EArrayType::Int32;
215 }
216 if (Value.IsChar())
217 {
218 return EArrayType::Char8;
219 }
220 if (Value.IsChar32())
221 {
222 return EArrayType::Char32;
223 }
224 return EArrayType::VValue;
225 }
226
227 static EArrayType DetermineCombinedType(EArrayType A, EArrayType B)
228 {
229 if (B == EArrayType::None)
230 {
231 return A;
232 }
233 else if (A == EArrayType::None)
234 {
235 return B;
236 }
237 else if (A == B)
238 {
239 return A;
240 }
241 else
242 {
243 return EArrayType::VValue;
244 }
245 }
246
247 VArrayBase(FAllocationContext Context, uint32 NumValues, uint32 Capacity, EArrayType ArrayType, VEmergentType* Type)
249 {
251
252 V_DIE_UNLESS(Capacity >= NumValues);
253 if (ArrayType != EArrayType::None && Capacity)
254 {
255 SetBufferWithoutStoreBarrier(Context, VBuffer(Context, NumValues, Capacity, ArrayType));
256 }
257 else
258 {
259 V_DIE_IF(NumValues);
260 }
261 }
262
263 VArrayBase(FAllocationContext Context, std::initializer_list<VValue> InitList, VEmergentType* Type)
265 {
267
268 if (InitList.size())
269 {
271 uint32 Index = 0;
272 for (VValue Value : InitList)
273 {
276 }
277 }
278 }
279
280 template <typename InitIndexFunc, typename = std::enable_if_t<std::is_same_v<VValue, std::invoke_result_t<InitIndexFunc, uint32>>>>
281 VArrayBase(FAllocationContext Context, uint32 NumValues, InitIndexFunc&& InitFunc, VEmergentType* Type)
283 {
284 AutoRTFM::UnreachableIfClosed("#jira SOL-8415");
285
287
288 if (NumValues)
289 {
290 // If an uninitialized VValue is returned from the InitFunc, the caller is requesting an immediate halt.
291 // Stop processing and leave the array with an empty buffer.
292 if (VValue Elem0 = InitFunc(0); Elem0)
293 {
294 // Initialize the VBuffer based on the type of the initial VValue.
297 }
298 else
299 {
300 return;
301 }
302
303 // Populate the rest of the buffer. As before, stop immediately (and jettison the buffer) if an
304 // uninitialized VValue is returned.
305 for (uint32 Index = 1; Index < NumValues; ++Index)
306 {
307 if (VValue ElemN = InitFunc(Index); ElemN)
308 {
310 }
311 else
312 {
314 return;
315 }
316 }
317 }
318 }
319
320 VArrayBase(FAllocationContext Context, FUtf8StringView String, VEmergentType* Type)
322 , Buffer(Context, VBuffer(Context, String.Len(), EArrayType::Char8))
323 {
325 if (String.Len())
326 {
327 FMemory::Memcpy(GetData(), String.GetData(), String.Len());
328 }
329 }
330
331 void SetNullTerminator()
332 {
333 Buffer.Get().SetNullTerminator();
334 }
335
336 template <bool bTransactional = false>
337 void ConvertDataToVValues(FAllocationContext Context, uint32 NewCapacity);
338
339public:
340 uint32 Num() const { return Buffer.Get().Num(); }
341 uint32 Capacity() const { return Buffer.Get().Capacity(); }
342 bool IsInBounds(uint32 Index) const;
343 bool IsInBounds(const VInt& Index, const uint32 Bounds) const;
344 VValue GetValue(uint32 Index);
345 const VValue GetValue(uint32 Index) const;
346
347protected:
348 template <bool bTransactional>
349 void SetValueImpl(FAllocationContext Context, uint32 Index, VValue Value);
350
351public:
352 void SetValue(FAllocationContext Context, uint32 Index, VValue Value);
353 void SetValueTransactionally(FAllocationContext Context, uint32 Index, VValue Value);
354 template <bool bTransactional = false>
355 void SetVValue(FAllocationContext Context, uint32 Index, VValue Value)
356 {
357 Buffer.Get().SetVValue<bTransactional>(Context, Index, Value);
358 }
360 {
361 Buffer.Get().SetInt32(Index, Value);
362 }
364 {
365 Buffer.Get().SetChar(Index, Value);
366 }
368 {
369 Buffer.Get().SetChar32(Index, Value);
370 }
371
372 void* GetData() { return Buffer.Get().GetData(); };
373 const void* GetData() const { return Buffer.Get().GetData(); };
374
375 template <typename T>
376 T* GetData() { return Buffer.Get().GetData<T>(); }
377 template <typename T>
378 const T* GetData() const { return Buffer.Get().GetData<T>(); }
379
380 EArrayType GetArrayType() const { return Buffer.Get().GetArrayType(); }
381
382 size_t ByteLength() const
383 {
384 return Num() * ::Verse::ByteLength(GetArrayType());
385 }
386
388 {
389 if (GetArrayType() == EArrayType::None || GetArrayType() == EArrayType::VValue)
390 {
391 FUtf8String String = FUtf8String::ConstructWithSlack(UTF8TEXT(""), Num());
392 for (uint32 Index = 0, End = Num(); Index < End; ++Index)
393 {
394 if (!GetValue(Index).IsChar())
395 {
396 return {};
397 }
398 String[Index] = GetValue(Index).AsChar();
399 }
400 return String;
401 }
402 if (::Verse::IsNullTerminatedString(GetArrayType()))
403 {
405 }
406 return {};
407 }
408
409 FString AsString() const
410 {
412 {
413 return FString(*Utf8String);
414 }
415 V_DIE("Couldn't convert Array to String!");
416 }
417
418 FUtf8StringView AsStringView() const
419 {
420 if (GetArrayType() == EArrayType::None)
421 {
422 return FUtf8StringView();
423 }
424 if (::Verse::IsNullTerminatedString(GetArrayType()))
425 {
427 }
428 V_DIE("Couldn't convert Array to String!");
429 }
430
431 bool Equals(const FUtf8StringView String) const
432 {
433 if (GetArrayType() == EArrayType::VValue)
434 {
435 for (uint32 Index = 0, End = Num(); Index < End; ++Index)
436 {
437 VValue Val = GetValue(Index);
438 if (!Val.IsChar() || Val.AsChar() != String[Index])
439 {
440 return false;
441 }
442 }
443 return true;
444 }
445 else if (::Verse::IsNullTerminatedString(GetArrayType()))
446 {
447 return AsStringView().Equals(String);
448 }
449 return false;
450 }
451
452 COREUOBJECT_API ECompares EqualImpl(FAllocationContext Context, VCell* Other, const TFunction<void(::Verse::VValue, ::Verse::VValue)>& HandlePlaceholder);
453
455
456 COREUOBJECT_API VValue MeltImpl(FAllocationContext Context);
457
458 void VisitMembersImpl(FAllocationContext Context, FDebuggerVisitor& Visitor);
459
460 COREUOBJECT_API void AppendToStringImpl(FAllocationContext Context, FUtf8StringBuilderBase& Builder, EValueStringFormat Format, TSet<const void*>& VisitedObjects, uint32 RecursionDepth);
462
463 template <typename ArrayType>
464 static void SerializeLayoutImpl(FAllocationContext Context, ArrayType*& This, FStructuredArchiveVisitor& Visitor);
465 void SerializeImpl(FAllocationContext Context, FStructuredArchiveVisitor& Visitor);
466
467 // C++ ranged-based iteration
468 class FConstIterator
469 {
470 union
471 {
473 const int32* Int32;
474 const UTF8CHAR* Char;
475 const UTF32CHAR* Char32;
476 const void* None;
477 };
478 EArrayType ArrayType;
479
480 public:
481 inline VValue operator*() const
482 {
483 switch (ArrayType)
484 {
485 case EArrayType::VValue:
486 return Barrier->Get().Follow();
487 case EArrayType::Int32:
488 return VValue::FromInt32(*Int32);
489 case EArrayType::Char8:
490 return VValue::Char(*Char);
491 case EArrayType::Char32:
492 return VValue::Char32(*Char32);
493 case EArrayType::None:
494 default:
495 V_DIE("Unhandled EArrayType (%u) encountered!", static_cast<uint32>(ArrayType));
496 }
497 }
498
499 // Don't need to worry about the data-type here as we are just doing pointer comparison
500 V_FORCEINLINE bool operator==(const FConstIterator& Rhs) const { return Barrier == Rhs.Barrier; }
501 V_FORCEINLINE bool operator!=(const FConstIterator& Rhs) const { return Barrier != Rhs.Barrier; }
502
503 inline FConstIterator& operator++()
504 {
505 switch (ArrayType)
506 {
507 case EArrayType::VValue:
508 ++Barrier;
509 break;
510 case EArrayType::Int32:
511 ++Int32;
512 break;
513 case EArrayType::Char8:
514 ++Char;
515 break;
516 case EArrayType::Char32:
517 ++Char32;
518 break;
519 case EArrayType::None:
520 default:
521 V_DIE("Unhandled EArrayType (%u) encountered!", static_cast<uint32>(ArrayType));
522 }
523 return *this;
524 }
525
526 private:
527 friend struct VArrayBase;
528 inline FConstIterator(const TWriteBarrier<VValue>* InCurrentValue)
530 , ArrayType(EArrayType::VValue) {}
531 inline FConstIterator(const int32* InCurrentValue)
533 , ArrayType(EArrayType::Int32) {}
534 inline FConstIterator(const UTF8CHAR* InCurrentValue)
536 , ArrayType(EArrayType::Char8) {}
537 inline FConstIterator(const UTF32CHAR* InCurrentValue)
538 : Char32(InCurrentValue)
539 , ArrayType(EArrayType::Char32) {}
540 inline FConstIterator(const void* InCurrentValue)
542 , ArrayType(EArrayType::None) {}
543 };
544
545 COREUOBJECT_API FConstIterator begin() const;
546 COREUOBJECT_API FConstIterator end() const;
547};
548
549} // namespace Verse
550#endif // WITH_VERSE_VM
#define checkSlow(expr)
Definition AssertionMacros.h:332
UE_FORCEINLINE_HINT FLinearColor operator*(float Scalar, const FLinearColor &Color)
Definition Color.h:473
FPlatformTypes::UTF32CHAR UTF32CHAR
A 32-bit character containing a UTF32 (Unicode, 32-bit, fixed-width) code unit.
Definition Platform.h:1143
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
#define UTF8TEXT(x)
Definition Platform.h:1286
FPlatformTypes::UTF8CHAR UTF8CHAR
An 8-bit character containing a UTF8 (Unicode, 8-bit, variable-width) code unit.
Definition Platform.h:1137
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
UE_FORCEINLINE_HINT bool operator!=(const FIndexedPointer &Other) const
Definition LockFreeList.h:76
@ Num
Definition MetalRHIPrivate.h:234
@ Char
Character type.
auto GetData(const TStringConversion< Converter, DefaultConversionSize > &Conversion) -> decltype(Conversion.Get())
Definition StringConv.h:802
TStringView< UTF8CHAR > FUtf8StringView
Definition StringFwd.h:48
float Val(const FString &Value)
Definition UnrealMath.cpp:3163
#define V_FORCEINLINE
Definition VVMVerse.h:30
uint8_t uint8
Definition binka_ue_file_header.h:8
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition JsonObject.h:23
Definition AndroidPlatformMisc.h:14
Definition UnrealString.h.inl:34
Definition SharedPointer.h:692
Definition StringBuilder.h:79
IAnalyticsPropertyStore::EStatusCode SetValue(TGetter &&GetterFn, TSetter &&SetterFn, const T &ProposedValue, TCompare &&ConditionFn)
Definition AnalyticsPropertyStore.cpp:34
Type
Definition PawnAction_Move.h:11
T::FDataType GetValue(const UBlackboardComponent &Blackboard, const FName &Name, FBlackboard::FKey &InOutCachedKey, const typename T::FDataType &DefaultValue)
Definition ValueOrBBKey.h:51
bool SerializeImpl(const UScriptStruct *InSourceEventType, const void *InSourceEventData, FLiveLinkSerializedFrameData &OutSerializedData)
Definition LiveLinkCompression.cpp:126
bool operator==(const FCachedAssetKey &A, const FCachedAssetKey &B)
Definition AssetDataMap.h:501
FORCEINLINE FStridedReferenceIterator begin(FStridedReferenceView View)
Definition FastReferenceCollector.h:490
FORCEINLINE FStridedReferenceIterator end(FStridedReferenceView View)
Definition FastReferenceCollector.h:491
bool IsInBounds(const FIntVector3 &Point, const FIntVector3 &Min, const FIntVector3 &Max)
Definition SparseVolumeTextureUtility.cpp:202
FORCEINLINE UE_STRING_CLASS RhsType && Rhs
Definition String.cpp.inl:718
Definition Archive.h:36
EValueStringFormat
Definition VVMValuePrinting.h:17
U16 Index
Definition radfft.cpp:71
static UE_FORCEINLINE_HINT void * Memcpy(void *Dest, const void *Src, SIZE_T Count)
Definition UnrealMemory.h:160
Definition Optional.h:131