UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
Misc.inl
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5namespace UE::IoStore::HTTP
6{
7
8// {{{1 trace ..................................................................
9
29
31#if UE_TRACE_ENABLED
32
33static void Trace(const struct FActivity*, ETrace, uint32);
34static void Trace(UPTRINT, ETrace Action, const class FOutcome* =nullptr);
35
36static void Trace(ETrace Action) {}
37static void Trace(const void*, ETrace Action, ...) {}
38
41{
43 return &Iax;
44}
45
46#else
47
48static void Trace(...) {}
49IOSTOREHTTPCLIENT_API const void* GetIaxTraceChannel() { return nullptr; }
50
51#endif // UE_TRACE_ENABLED
52
53
54
55// {{{1 misc ...................................................................
56
58#define IAS_CVAR(Type, Name, Default, Desc, ...) \
59 Type G##Name = Default; \
60 static FAutoConsoleVariableRef CVar_Ias##Name( \
61 TEXT("ias.Http" #Name), \
62 G##Name, \
63 TEXT(Desc) \
64 __VA_ARGS__ \
65 )
66
68static IAS_CVAR(int32, RecvWorkThresholdKiB,80, "Threshold of data remaining at which next request is sent (in KiB)");
69static IAS_CVAR(int32, IdleMs, 50'000, "Time in milliseconds to close idle connections or fail waits");
70
73{
74public:
75 static FOutcome Ok(int32 Result=0);
76 static FOutcome Waiting(int32 Result=0);
79 static FOutcome Error(const char* Message, int32 Code=-1);
80 static FOutcome None() { return Error(""); }
81 bool IsError() const { return Message < 0x8000'0000'0000; }
82 bool IsWaiting() const { return (Tag & WaitTag) == WaitTag; }
83 bool IsWaitData() const { check(IsWaiting()); return (Tag & ~WaitTag) == 0; }
84 bool IsWaitBuffer() const{ check(IsWaiting()); return (Tag & BufferTag) != 0; }
85 bool IsWaitStream() const{ check(IsWaiting()); return (Tag & StreamTag) != 0; }
86 bool IsOk() const { return Tag == OkTag; }
87 FAnsiStringView GetMessage() const { check(IsError()); return (const char*)UPTRINT(Message); }
88 int32 GetErrorCode() const{ check(IsError()); return int32(Code); }
89 uint32 GetResult() const { check(!IsError()); return Result; }
90
91private:
92 FOutcome() = default;
93
94 static uint32 const OkTag = 0x0000'8000;
95 static uint32 const WaitTag = 0x0001'8000;
96 static uint32 const BufferTag = 0x0002'0000;
97 static uint32 const StreamTag = 0x0004'0000;
98
99 union {
100 struct {
103 };
104 struct {
107 };
108 };
109};
110static_assert(sizeof(FOutcome) == sizeof(void*));
111
114{
116 Outcome.Tag = OkTag;
117 Outcome.Result = Result;
118 return Outcome;
119}
120
123{
125 Outcome.Tag = WaitTag;
126 Outcome.Result = Result;
127 return Outcome;
128}
129
132{
133 // Given buffer space exhausted
135 Outcome.Tag = WaitTag|BufferTag;
136 Outcome.Result = Result;
137 return Outcome;
138}
139
142{
143 // Need to switch to another stream
145 Outcome.Tag = WaitTag|StreamTag;
146 Outcome.Result = Result;
147 return Outcome;
148}
149
151FOutcome FOutcome::Error(const char* Message, int32 Code)
152{
153 check(Message != nullptr);
155
158 Outcome.Code = int16(Code);
159 return Outcome;
160}
161
162
163
165template <uint32 Base=10>
166static int64 CrudeToInt(FAnsiStringView View)
167{
168 static_assert(Base == 10 || Base == 16);
169
170 // FCStringAnsi::* is not used to mitigate any locale hiccups. By
171 // initialising 'Value' with MSB set we can detect cases where View did not
172 // start with digits. This works as we won't be using this on huge numbers.
173 int64 Value = 0x8000'0000'0000'0000ll;
174 for (int32 c : View)
175 {
176 uint32 Digit = c - '0';
177 if (Digit > 9u)
178 {
179 if constexpr (Base != 16)
180 {
181 break;
182 }
183 else
184 {
185 Digit = (c | 0x20) - 'a';
186 if (Digit > uint32('f' - 'a'))
187 {
188 break;
189 }
190 Digit += 10;
191 }
192 }
193 Value *= Base;
194 Value += Digit;
195 }
196 return Value;
197};
198
199template <uint32 Base=10> static int64 CrudeToInt(const char*) = delete;
200
203{
204 struct Slice
205 {
206 Slice() = default;
207 Slice(int32 l, int32 r) : Left(uint8(l)), Right(uint8(r)) {}
208 FAnsiStringView Get(FAnsiStringView Url) const { return Url.Mid(Left, Right - Left); }
209 operator bool () const { return Left > 0; }
210 int32 Len() const { return Right - Left; }
213 };
219};
220
221static int32 ParseUrl(FAnsiStringView Url, FUrlOffsets& Out)
222{
223 if (Url.Len() < 5)
224 {
225 return -1;
226 }
227
228 Out = {};
229
230 const char* Start = Url.GetData();
231 const char* Cursor = Start;
232
233 // Scheme
234 int32 i = 0;
235 for (; i < 5; ++i)
236 {
237 if (uint32(Cursor[i] - 'a') > uint32('z' - 'a'))
238 {
239 break;
240 }
241 }
242
243 Out.SchemeLength = uint8(i);
244 FAnsiStringView Scheme = Url.Left(i);
245 if (Scheme != "http" && Scheme != "https")
246 {
247 return -1;
248 }
249
250 // Separator and authority
251 if (Cursor[i] != ':' || Cursor[i + 1] != '/' || Cursor[i + 2] != '/')
252 {
253 return -1;
254 }
255 i += 3;
256
257 struct { int32 c; int32 i; } Seps[2];
258 int32 SepCount = 0;
259 for (; i < Url.Len(); ++i)
260 {
261 int32 c = Cursor[i];
262 if (c < '-') break;
263 if (c != ':' && c != '@' && c != '/') continue;
264 if (c == '/' || SepCount >= 2) break;
265
266 if (c == '@' && SepCount)
267 {
268 SepCount -= (Seps[SepCount - 1].c == ':');
269 }
270 Seps[SepCount++] = { c, i };
271 }
272
273 if (i > 0xff || i <= Scheme.Len() + 3)
274 {
275 return -1;
276 }
277
278 if (i < Url.Len())
279 {
280 Out.Path = uint8(i);
281 }
282
283 Out.HostName = { uint8(Scheme.Len() + 3), uint8(i) };
284
285 switch (SepCount)
286 {
287 case 0:
288 break;
289
290 case 1:
291 if (Seps[0].c == ':')
292 {
293 Out.Port = { Seps[0].i + 1, i };
294 Out.HostName.Right = uint8(Seps[0].i);
295 }
296 else
297 {
298 Out.UserInfo = { Out.HostName.Left, Seps[0].i };
299 Out.HostName.Left += uint8(Seps[0].i + 1);
300 Out.HostName.Right += uint8(Seps[0].i + 1);
301 }
302 break;
303
304 case 2:
305 if ((Seps[0].c != '@') | (Seps[1].c != ':'))
306 {
307 return -1;
308 }
309 Out.UserInfo = { Out.HostName.Left, Seps[0].i };
310 Out.Port.Left = uint8(Seps[1].i + 1);
311 Out.Port.Right = Out.HostName.Right;
312 Out.HostName.Left = Out.UserInfo.Right + 1;
313 Out.HostName.Right = Out.Port.Left - 1;
314 break;
315
316 default:
317 return -1;
318 }
319
320 bool Bad = false;
321 Bad |= (Out.HostName.Len() == 0);
322 Bad |= bool(int32(Out.UserInfo) & (Out.UserInfo.Len() == 0));
323
324 if (Out.Port.Left)
325 {
326 Bad |= (Out.Port.Len() == 0);
327 for (int32 j = 0, n = Out.Port.Len(); j < n; ++j)
328 {
329 Bad |= (uint32(Start[Out.Port.Left + j] - '0') > 9);
330 }
331 }
332
333 return Bad ? -1 : 1;
334}
335
336
337
338// {{{1 buffer .................................................................
339
342{
343public:
345 {
346 char* Data;
348 };
349
350 FBuffer() = default;
351 FBuffer(char* InData, uint32 InMax);
352 ~FBuffer();
353 void Resize(uint32 Size);
354 const char* GetData() const;
355 uint32 GetSize() const;
356 template <typename T> T* Alloc(uint32 Count=1);
359
360private:
361 uint32 GetCapacity() const;
362 char* GetDataPtr();
363 void Extend(uint32 AtLeast, uint32 PageSize);
364 UPTRINT Data;
365 uint32 Max = 0;
366 union
367 {
368 struct
369 {
372 };
374 };
375
376private:
377 FBuffer(FBuffer&&) = delete;
378 FBuffer(const FBuffer&) = delete;
379 FBuffer& operator = (const FBuffer&) = delete;
380 FBuffer& operator = (FBuffer&& Rhs) = delete;
381};
382
385: Data(UPTRINT(InData))
386, Max(InMax)
387{
388 Inline = 1;
389}
390
393{
394 if (Data && !Inline)
395 {
396 FMemory::Free(GetDataPtr());
397 }
398}
399
402{
403 check(Size <= Max);
404 Used = Size;
405}
406
408char* FBuffer::GetDataPtr()
409{
410 return (char*)Data;
411}
412
414const char* FBuffer::GetData() const
415{
416 return (char*)Data;
417}
418
421{
422 return Used;
423}
424
426uint32 FBuffer::GetCapacity() const
427{
428 return Max;
429}
430
432template <typename T>
434{
435 uint32 AlignBias = uint32(Data + Used) & (alignof(T) - 1);
436 if (AlignBias)
437 {
438 AlignBias = alignof(T) - AlignBias;
439 }
440
441 uint32 PotentialUsed = Used + AlignBias + (sizeof(T) * Count);
442 if (PotentialUsed > Max)
443 {
444 Extend(PotentialUsed, 256);
445 }
446
447 void* Ret = GetDataPtr() + Used + AlignBias;
449 return (T*)Ret;
450}
451
454{
455 MinSize = (MinSize == 0 && Used == Max) ? PageSize : MinSize;
456
458 if (PotentialUsed > Max)
459 {
460 Extend(PotentialUsed, PageSize);
461 }
462
463 return FMutableSection{ GetDataPtr() + Used, Max - Used };
464}
465
468{
469 Used += Delta;
470 check(Used <= Max);
471}
472
474void FBuffer::Extend(uint32 AtLeast, uint32 PageSize)
475{
476 checkSlow(((PageSize - 1) & PageSize) == 0);
477
478 --PageSize;
479 Max = (AtLeast + PageSize) & ~PageSize;
480
481 if (!Inline)
482 {
483 Data = UPTRINT(FMemory::Realloc(GetDataPtr(), Max, alignof(FBuffer)));
484 return;
485 }
486
487 const char* PrevData = GetDataPtr();
488 Data = UPTRINT(FMemory::Malloc(Max, alignof(FBuffer)));
489 ::memcpy(GetDataPtr(), PrevData, Used);
490 Inline = 0;
491}
492
493
494
495// {{{1 throttler ..............................................................
496
498static void ThrottleTest(FAnsiStringView);
499
502{
503public:
504 FThrottler();
507 void ReturnUnused(int32 Unused);
508
509private:
510 friend void ThrottleTest(FAnsiStringView);
512 int64 CycleFreq;
513 int64 CycleLast = 0;
514 int64 CyclePeriod = 0;
515 uint32 Limit = 0;
516
517 enum : uint32 {
518 LIMITLESS = MAX_int32,
519 SLICES_POW2 = 5,
520 };
521};
522
525{
526 CycleFreq = int64(1.0 / FPlatformTime::GetSecondsPerCycle64());
527 check(CycleFreq >> SLICES_POW2);
528}
529
532{
533 // 512MiB/s might as well be limitless.
534 KiBPerSec = (KiBPerSec < (512 << 10)) ? KiBPerSec : 0;
535 if (KiBPerSec)
536 {
537 KiBPerSec = FMath::Max(KiBPerSec, 1u << SLICES_POW2);
538 }
539 Limit = KiBPerSec << 10;
540}
541
544{
546 int64 CycleDelta = Cycle - CycleLast;
547 CycleLast = Cycle;
548 return GetAllowance(CycleDelta);
549}
550
553{
554 if (Limit == 0)
555 {
556 return LIMITLESS;
557 }
558
559 int64 CycleSlice = CycleFreq >> SLICES_POW2;
560 CycleDelta = FMath::Min(CycleDelta, CycleSlice);
561 CyclePeriod -= CycleDelta;
562 if (CyclePeriod > 0)
563 {
564 return 0 - int32((CyclePeriod * 1000ll) / CycleFreq);
565 }
566 CyclePeriod += CycleSlice;
567
568 int32 Released = Limit >> SLICES_POW2;
569 return Released;
570}
571
574{
575 if (Limit == 0 || Unused == 0)
576 {
577 return;
578 }
579
580 int64 CycleReturn = (CycleFreq * Unused) / Limit;
581 CycleLast -= CycleReturn;
582}
583
584// }}}
585
586} // namespace UE::IoStore::HTTP
#define checkSlow(expr)
Definition AssertionMacros.h:332
#define check(expr)
Definition AssertionMacros.h:314
FPlatformTypes::int16 int16
A 16-bit signed integer.
Definition Platform.h:1123
FPlatformTypes::PTRINT PTRINT
A signed integer the same size as a pointer.
Definition Platform.h:1148
FPlatformTypes::int64 int64
A 64-bit signed integer.
Definition Platform.h:1127
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
FPlatformTypes::UPTRINT UPTRINT
An unsigned integer the same size as a pointer.
Definition Platform.h:1146
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
#define IAS_CVAR(Type, Name, Default, Desc,...)
Definition Misc.inl:58
const bool
Definition NetworkReplayStreaming.h:178
#define MAX_int32
Definition NumericLimits.h:25
#define UE_TRACE_CHANNEL(ChannelName,...)
Definition Trace.h:446
uint32 Size
Definition VulkanMemory.cpp:4034
memcpy(InputBufferBase, BinkBlocksData, BinkBlocksSize)
uint8_t uint8
Definition binka_ue_file_header.h:8
uint32_t uint32
Definition binka_ue_file_header.h:6
ViewType Mid(int32 Position, int32 CharCount=MAX_int32) const
Definition StringView.h:606
constexpr int32 Len() const
Definition StringView.h:174
ViewType Left(int32 CharCount) const
Definition StringView.h:580
constexpr const CharType * GetData() const
Definition StringView.h:160
Definition Misc.inl:342
void AdvanceUsed(uint32 Delta)
Definition Misc.inl:467
T * Alloc(uint32 Count=1)
Definition Misc.inl:433
FMutableSection GetMutableFree(uint32 MinSize, uint32 PageSize=256)
Definition Misc.inl:453
void Resize(uint32 Size)
Definition Misc.inl:401
uint32 GetSize() const
Definition Misc.inl:420
uint32 UsedInline
Definition Misc.inl:373
~FBuffer()
Definition Misc.inl:392
const char * GetData() const
Definition Misc.inl:414
uint32 Used
Definition Misc.inl:370
uint32 Inline
Definition Misc.inl:371
Definition Misc.inl:73
PTRINT Code
Definition Misc.inl:102
bool IsWaiting() const
Definition Misc.inl:82
static FOutcome Ok(int32 Result=0)
Definition Misc.inl:113
bool IsError() const
Definition Misc.inl:81
static FOutcome WaitStream(int32 Result=0)
Definition Misc.inl:141
uint32 Tag
Definition Misc.inl:106
int32 GetErrorCode() const
Definition Misc.inl:88
int32 Result
Definition Misc.inl:105
bool IsOk() const
Definition Misc.inl:86
UPTRINT Message
Definition Misc.inl:101
bool IsWaitBuffer() const
Definition Misc.inl:84
static FOutcome Error(const char *Message, int32 Code=-1)
Definition Misc.inl:151
FAnsiStringView GetMessage() const
Definition Misc.inl:87
bool IsWaitStream() const
Definition Misc.inl:85
static FOutcome WaitBuffer(int32 Result=0)
Definition Misc.inl:131
static FOutcome None()
Definition Misc.inl:80
static FOutcome Waiting(int32 Result=0)
Definition Misc.inl:122
bool IsWaitData() const
Definition Misc.inl:83
uint32 GetResult() const
Definition Misc.inl:89
Definition Misc.inl:502
void ReturnUnused(int32 Unused)
Definition Misc.inl:573
friend void ThrottleTest(FAnsiStringView)
Definition Test.inl:171
FThrottler()
Definition Misc.inl:524
int32 GetAllowance()
Definition Misc.inl:543
void SetLimit(uint32 KiBPerSec)
Definition Misc.inl:531
@ Trace
Definition NetTraceConfig.h:23
Definition Client.h:20
ETrace
Definition Misc.inl:12
IOSTOREHTTPCLIENT_API const void * GetIaxTraceChannel()
Definition Misc.inl:49
static uint64 Cycles64()
Definition AndroidPlatformTime.h:34
static double GetSecondsPerCycle64()
Definition GenericPlatformTime.h:196
static FORCENOINLINE CORE_API void Free(void *Original)
Definition UnrealMemory.cpp:685
uint32 Size
Definition Misc.inl:347
char * Data
Definition Misc.inl:346
Slice(int32 l, int32 r)
Definition Misc.inl:207
FAnsiStringView Get(FAnsiStringView Url) const
Definition Misc.inl:208
uint8 Left
Definition Misc.inl:211
int32 Len() const
Definition Misc.inl:210
uint8 Right
Definition Misc.inl:212
Definition Misc.inl:203
Slice Port
Definition Misc.inl:216
Slice HostName
Definition Misc.inl:215
uint8 SchemeLength
Definition Misc.inl:218
uint8 Path
Definition Misc.inl:217
Slice UserInfo
Definition Misc.inl:214