Ark Server API (ASA) - Wiki
Loading...
Searching...
No Matches
CompactBinaryPackage.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "Compression/CompressedBuffer.h"
6#include "Containers/Array.h"
7#include "Containers/ContainersFwd.h"
8#include "CoreTypes.h"
9#include "IO/IoHash.h"
10#include "Memory/CompositeBuffer.h"
11#include "Memory/SharedBuffer.h"
12#include "Misc/AssertionMacros.h"
13#include "Misc/TVariant.h"
14#include "Serialization/CompactBinary.h"
15#include "Templates/Function.h"
16#include "Templates/UnrealTemplate.h"
17
18class FArchive;
19class FCbWriter;
20template <typename FuncType> class TFunctionRef;
21
22///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
23
24/**
25 * An attachment is either null, raw binary, compressed binary, or an object.
26 *
27 * Attachments are always identified by their raw hash, even when stored compressed.
28 *
29 * An attachment is serialized as a sequence of compact binary fields with no name. It is invalid
30 * to serialize a null attachment. A raw binary attachment is serialized as an empty Binary field
31 * when empty, and otherwise as a BinaryAttachment field containing the raw hash, followed by the
32 * raw binary data in a Binary field. A compressed binary attachment is serialized as Binary with
33 * a compressed buffer as the value. An object is serialized as an empty Object field when empty,
34 * and otherwise as an ObjectAttachment field containing the hash, followed by the object data in
35 * an Object field.
36 */
38{
40 {
43
44 FObjectValue(FCbObject&& Object, const FIoHash* Hash);
45 };
46
48 {
51
52 template <typename BufferType, decltype(FCompositeBuffer(DeclVal<BufferType>().MakeOwned()))* = nullptr>
53 inline explicit FBinaryValue(BufferType&& InBuffer)
56 {
57 }
58
59 template <typename BufferType, decltype(FCompositeBuffer(DeclVal<BufferType>().MakeOwned()))* = nullptr>
60 inline explicit FBinaryValue(BufferType&& InBuffer, const FIoHash& InHash)
62 , Hash(InHash)
63 {
65 }
66 };
67
68 template <typename... ArgTypes, decltype(FBinaryValue(DeclVal<ArgTypes>()...))* = nullptr>
69 inline FCbAttachment(TInPlaceType<FBinaryValue>, ArgTypes&&... Args)
71 {
73 {
75 }
76 }
77
78public:
79 /** Construct a null attachment. */
80 FCbAttachment() = default;
81
82 /** Construct an object attachment. Value is cloned if not owned. */
83 inline explicit FCbAttachment(const FCbObject& InValue)
85 inline explicit FCbAttachment(FCbObject&& InValue)
87 inline explicit FCbAttachment(const FCbObject& InValue, const FIoHash& Hash)
89 inline explicit FCbAttachment(FCbObject&& InValue, const FIoHash& Hash)
91
92 /** Construct a raw binary attachment from a shared/composite buffer. Value is cloned if not owned. */
93 template <typename BufferType, decltype(FBinaryValue(DeclVal<BufferType>().MakeOwned()))* = nullptr>
94 inline explicit FCbAttachment(BufferType&& InValue)
96 template <typename BufferType, decltype(FBinaryValue(DeclVal<BufferType>().MakeOwned()))* = nullptr>
97 inline explicit FCbAttachment(BufferType&& InValue, const FIoHash& InHash)
99
100 /** Construct a compressed binary attachment. Value is cloned if not owned. */
101 inline explicit FCbAttachment(const FCompressedBuffer& InValue)
103 {
104 if (Value.Get<FCompressedBuffer>().IsNull())
105 {
106 Value.Emplace<TYPE_OF_NULLPTR>();
107 }
108 }
109
110 /** Construct a compressed binary attachment. Value is cloned if not owned. */
111 inline explicit FCbAttachment(FCompressedBuffer&& InValue)
113 {
114 if (Value.Get<FCompressedBuffer>().IsNull())
115 {
116 Value.Emplace<TYPE_OF_NULLPTR>();
117 }
118 }
119
120 /** Reset this to a null attachment. */
121 inline void Reset() { *this = FCbAttachment(); }
122
123 /** Access the attachment as an object. Defaults to an empty object on error. */
124 inline FCbObject AsObject() const;
125
126 /** Access the attachment as raw binary in a single contiguous buffer. Defaults to a null buffer on error. */
127 inline FSharedBuffer AsBinary() const;
128
129 /** Access the attachment as raw binary. Defaults to a null buffer on error. */
130 inline const FCompositeBuffer& AsCompositeBinary() const;
131
132 /** Access the attachment as compressed binary. Defaults to a null buffer on error. */
133 inline const FCompressedBuffer& AsCompressedBinary() const;
134
135 /** Whether the attachment has a non-null value. */
136 inline explicit operator bool() const { return !IsNull(); }
137
138 /** Whether the attachment has a null value. */
139 inline bool IsNull() const { return Value.IsType<TYPE_OF_NULLPTR>(); }
140
141 /** Returns whether the attachment is an object. */
142 inline bool IsObject() const { return Value.IsType<FObjectValue>(); }
143
144 /** Returns whether the attachment is raw binary. */
145 inline bool IsBinary() const { return Value.IsType<FBinaryValue>(); }
146
147 /** Returns whether the attachment is compressed binary. */
148 inline bool IsCompressedBinary() const { return Value.IsType<FCompressedBuffer>(); }
149
150 /** Returns the hash of the attachment value. */
152
153 /** Compares attachments by their hash. Any discrepancy in type must be handled externally. */
154 inline bool operator==(const FCbAttachment& Attachment) const { return GetHash() == Attachment.GetHash(); }
155 inline bool operator!=(const FCbAttachment& Attachment) const { return GetHash() != Attachment.GetHash(); }
156 inline bool operator<(const FCbAttachment& Attachment) const { return GetHash() < Attachment.GetHash(); }
157
158 /**
159 * Load the attachment from compact binary as written by Save.
160 *
161 * The attachment references the input iterator if it is owned, and otherwise clones the value.
162 *
163 * The iterator is advanced as attachment fields are consumed from it.
164 */
166
167 /**
168 * Load the attachment from compact binary as written by Save.
169 *
170 * The attachments value will be loaded into an owned buffer.
171 *
172 * @param Ar Archive to read the attachment from. An error state is set on failure.
173 * @param Allocator Allocator for the attachment value buffer.
174 * @note Allocated buffers will be cloned if they are not owned.
175 */
176 bool TryLoad(FArchive& Ar, FCbBufferAllocator Allocator = FUniqueBuffer::Alloc);
177
178 /** Save the attachment into the writer as a stream of compact binary fields. */
179 void Save(FCbWriter& Writer) const;
180
181 /** Save the attachment into the archive as a stream of compact binary fields. */
182 void Save(FArchive& Ar) const;
183
184private:
186};
187
188/** Hashes attachments by their hash. Any discrepancy in type must be handled externally. */
189inline uint32 GetTypeHash(const FCbAttachment& Attachment)
190{
191 return GetTypeHash(Attachment.GetHash());
192}
193
194///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
195
196/**
197 * A package is an object with a tree of referenced attachments.
198 *
199 * A package is a Merkle tree with an object as its root and non-leaf nodes, and either binary or
200 * objects as its leaf nodes. Nodes reference their children from fields of type BinaryAttachment
201 * or ObjectAttachment, which store the raw hash of the referenced attachment.
202 *
203 * It is invalid for a package to include attachments that are not referenced by its object or by
204 * one of its referenced object attachments. This invariant needs to be maintained if attachments
205 * are added explicitly instead of being discovered by the attachment resolver. If any attachment
206 * is not referenced, it may not survive a round-trip through certain storage systems.
207 *
208 * It is valid for a package to exclude referenced attachments, but then it is the responsibility
209 * of the package consumer to have a mechanism for resolving those references when necessary.
210 *
211 * A package is serialized as a sequence of compact binary fields with no name. The object may be
212 * both preceded and followed by attachments. The object is only serialized when it is non-empty,
213 * starting with its hash, in a Hash field, followed by the object, in an Object field. A package
214 * ends with a Null field. The canonical order of components is the object and its hash, followed
215 * by the attachments ordered by hash, followed by a Null field. It is valid for the a package to
216 * have its attachments serialized in any order relative to each other and to the object.
217 */
219{
220public:
221 /**
222 * A function that resolves a hash to a buffer containing the data matching that hash.
223 *
224 * The resolver may return a null buffer to skip resolving an attachment for the hash.
225 */
227
228 /** Construct a null package. */
229 FCbPackage() = default;
230
231 /**
232 * Construct a package from a root object without gathering attachments.
233 *
234 * @param InObject The root object, which will be cloned unless it is owned.
235 */
236 inline explicit FCbPackage(FCbObject InObject)
237 {
238 SetObject(MoveTemp(InObject));
239 }
240
241 /**
242 * Construct a package from a root object and gather attachments using the resolver.
243 *
244 * @param InObject The root object, which will be cloned unless it is owned.
245 * @param InResolver A function that is invoked for every reference and binary reference field.
246 */
247 inline explicit FCbPackage(FCbObject InObject, FAttachmentResolver InResolver)
248 {
249 SetObject(MoveTemp(InObject), InResolver);
250 }
251
252 /**
253 * Construct a package from a root object without gathering attachments.
254 *
255 * @param InObject The root object, which will be cloned unless it is owned.
256 * @param InObjectHash The hash of the object, which must match to avoid validation errors.
257 */
258 inline explicit FCbPackage(FCbObject InObject, const FIoHash& InObjectHash)
259 {
260 SetObject(MoveTemp(InObject), InObjectHash);
261 }
262
263 /**
264 * Construct a package from a root object and gather attachments using the resolver.
265 *
266 * @param InObject The root object, which will be cloned unless it is owned.
267 * @param InObjectHash The hash of the object, which must match to avoid validation errors.
268 * @param InResolver A function that is invoked for every reference and binary reference field.
269 */
270 inline explicit FCbPackage(FCbObject InObject, const FIoHash& InObjectHash, FAttachmentResolver InResolver)
271 {
272 SetObject(MoveTemp(InObject), InObjectHash, InResolver);
273 }
274
275 /** Reset this to a null package. */
276 inline void Reset() { *this = FCbPackage(); }
277
278 /** Whether the package has a non-empty object or attachments. */
279 inline explicit operator bool() const { return !IsNull(); }
280
281 /** Whether the package has an empty object and no attachments. */
282 inline bool IsNull() const { return !Object && Attachments.IsEmpty(); }
283
284 /** Returns the object for the package. */
285 inline const FCbObject& GetObject() const { return Object; }
286
287 /** Returns the hash of the object for the package. */
288 inline const FIoHash& GetObjectHash() const { return ObjectHash; }
289
290 /**
291 * Set the root object without gathering attachments.
292 *
293 * @param InObject The root object, which will be cloned unless it is owned.
294 */
295 inline void SetObject(FCbObject InObject)
296 {
297 SetObject(MoveTemp(InObject), nullptr, nullptr);
298 }
299
300 /**
301 * Set the root object and gather attachments using the resolver.
302 *
303 * @param InObject The root object, which will be cloned unless it is owned.
304 * @param InResolver A function that is invoked for every reference and binary reference field.
305 */
306 inline void SetObject(FCbObject InObject, FAttachmentResolver InResolver)
307 {
308 SetObject(MoveTemp(InObject), nullptr, &InResolver);
309 }
310
311 /**
312 * Set the root object without gathering attachments.
313 *
314 * @param InObject The root object, which will be cloned unless it is owned.
315 * @param InObjectHash The hash of the object, which must match to avoid validation errors.
316 */
317 inline void SetObject(FCbObject InObject, const FIoHash& InObjectHash)
318 {
319 SetObject(MoveTemp(InObject), &InObjectHash, nullptr);
320 }
321
322 /**
323 * Set the root object and gather attachments using the resolver.
324 *
325 * @param InObject The root object, which will be cloned unless it is owned.
326 * @param InObjectHash The hash of the object, which must match to avoid validation errors.
327 * @param InResolver A function that is invoked for every reference and binary reference field.
328 */
329 inline void SetObject(FCbObject InObject, const FIoHash& InObjectHash, FAttachmentResolver InResolver)
330 {
331 SetObject(MoveTemp(InObject), &InObjectHash, &InResolver);
332 }
333
334 /** Returns the attachments in this package. */
335 inline TConstArrayView<FCbAttachment> GetAttachments() const { return Attachments; }
336
337 /**
338 * Find an attachment by its hash.
339 *
340 * @return The attachment, or null if the attachment is not found.
341 * @note The returned pointer is only valid until the attachments on this package are modified.
342 */
343 const FCbAttachment* FindAttachment(const FIoHash& Hash) const;
344
345 /** Find an attachment if it exists in the package. */
346 inline const FCbAttachment* FindAttachment(const FCbAttachment& Attachment) const
347 {
348 return FindAttachment(Attachment.GetHash());
349 }
350
351 /** Add the attachment to this package. */
352 inline void AddAttachment(const FCbAttachment& Attachment)
353 {
354 AddAttachment(Attachment, nullptr);
355 }
356
357 /** Add the attachment to this package, along with any references that can be resolved. */
358 inline void AddAttachment(const FCbAttachment& Attachment, FAttachmentResolver Resolver)
359 {
360 AddAttachment(Attachment, &Resolver);
361 }
362
363 /**
364 * Remove an attachment by hash.
365 *
366 * @return Number of attachments removed, which will be either 0 or 1.
367 */
368 int32 RemoveAttachment(const FIoHash& Hash);
369 inline int32 RemoveAttachment(const FCbAttachment& Attachment) { return RemoveAttachment(Attachment.GetHash()); }
370
371 /** Compares packages by their object and attachment hashes. */
372 bool Equals(const FCbPackage& Package) const;
373 inline bool operator==(const FCbPackage& Package) const { return Equals(Package); }
374 inline bool operator!=(const FCbPackage& Package) const { return !Equals(Package); }
375
376 /**
377 * Load the object and attachments from compact binary as written by Save.
378 *
379 * The object and attachments reference the input iterator, if it is owned, and otherwise clones
380 * the object and attachments individually to make owned copies.
381 *
382 * The iterator is advanced as object and attachment fields are consumed from it.
383 */
385
386 /**
387 * Load the object and attachments from compact binary as written by Save.
388 *
389 * The object and attachments will be individually loaded into owned buffers.
390 *
391 * @param Ar Archive to read the package from. An error state is set on failure.
392 * @param Allocator Allocator for object and attachment buffers.
393 * @note Allocated buffers will be cloned if they are not owned.
394 */
395 bool TryLoad(FArchive& Ar, FCbBufferAllocator Allocator = FUniqueBuffer::Alloc);
396
397 /** Save the object and attachments into the writer as a stream of compact binary fields. */
398 void Save(FCbWriter& Writer) const;
399
400 /** Save the object and attachments into the archive as a stream of compact binary fields. */
401 void Save(FArchive& Ar) const;
402
403private:
404 void SetObject(FCbObject Object, const FIoHash* Hash, FAttachmentResolver* Resolver);
405 void AddAttachment(const FCbAttachment& Attachment, FAttachmentResolver* Resolver);
406
407 void GatherAttachments(const FCbObject& Object, FAttachmentResolver Resolver);
408
409 /** Attachments ordered by their hash. */
413};
414
415///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
416
418{
419 if (const FObjectValue* ObjectValue = Value.TryGet<FObjectValue>())
420 {
421 return ObjectValue->Object;
422 }
423 return FCbObject();
424}
425
427{
428 if (const FBinaryValue* BinaryValue = Value.TryGet<FBinaryValue>())
429 {
430 return BinaryValue->Buffer.ToShared();
431 }
432 return FSharedBuffer();
433}
434
436{
437 if (const FBinaryValue* BinaryValue = Value.TryGet<FBinaryValue>())
438 {
439 return BinaryValue->Buffer;
440 }
441 return FCompositeBuffer::Null;
442}
443
445{
446 if (const FCompressedBuffer* CompressedBuffer = Value.TryGet<FCompressedBuffer>())
447 {
448 return *CompressedBuffer;
449 }
450 return FCompressedBuffer::Null;
451}
#define checkSlow(expr)
uint32 GetTypeHash(const FCbAttachment &Attachment)
FCbAttachment(FCompressedBuffer &&InValue)
TVariant< TYPE_OF_NULLPTR, FObjectValue, FBinaryValue, FCompressedBuffer > Value
void Save(FCbWriter &Writer) const
FCbAttachment(const FCbObject &InValue, const FIoHash &Hash)
const FCompositeBuffer & AsCompositeBinary() const
FCbAttachment()=default
bool operator==(const FCbAttachment &Attachment) const
const FCompressedBuffer & AsCompressedBinary() const
FCbAttachment(TInPlaceType< FBinaryValue >, ArgTypes &&... Args)
FCbAttachment(FCbObject &&InValue)
FCbObject AsObject() const
bool operator<(const FCbAttachment &Attachment) const
FIoHash GetHash() const
FSharedBuffer AsBinary() const
void Save(FArchive &Ar) const
bool TryLoad(FCbFieldIterator &Fields)
bool operator!=(const FCbAttachment &Attachment) const
bool TryLoad(FArchive &Ar, FCbBufferAllocator Allocator=FUniqueBuffer::Alloc)
FCbAttachment(const FCompressedBuffer &InValue)
FCbAttachment(BufferType &&InValue, const FIoHash &InHash)
FCbAttachment(const FCbObject &InValue)
FCbAttachment(BufferType &&InValue)
bool IsCompressedBinary() const
FCbAttachment(FCbObject &&InValue, const FIoHash &Hash)
FCbPackage(FCbObject InObject, FAttachmentResolver InResolver)
bool TryLoad(FCbFieldIterator &Fields)
bool Equals(const FCbPackage &Package) const
bool operator==(const FCbPackage &Package) const
void AddAttachment(const FCbAttachment &Attachment, FAttachmentResolver *Resolver)
int32 RemoveAttachment(const FCbAttachment &Attachment)
void SetObject(FCbObject Object, const FIoHash *Hash, FAttachmentResolver *Resolver)
FCbPackage()=default
const FCbAttachment * FindAttachment(const FCbAttachment &Attachment) const
void SetObject(FCbObject InObject, const FIoHash &InObjectHash)
TArray< FCbAttachment > Attachments
FCbPackage(FCbObject InObject)
FCbPackage(FCbObject InObject, const FIoHash &InObjectHash)
bool IsNull() const
void SetObject(FCbObject InObject)
TConstArrayView< FCbAttachment > GetAttachments() const
void SetObject(FCbObject InObject, const FIoHash &InObjectHash, FAttachmentResolver InResolver)
void Save(FArchive &Ar) const
const FIoHash & GetObjectHash() const
int32 RemoveAttachment(const FIoHash &Hash)
const FCbAttachment * FindAttachment(const FIoHash &Hash) const
FCbPackage(FCbObject InObject, const FIoHash &InObjectHash, FAttachmentResolver InResolver)
bool operator!=(const FCbPackage &Package) const
bool TryLoad(FArchive &Ar, FCbBufferAllocator Allocator=FUniqueBuffer::Alloc)
void AddAttachment(const FCbAttachment &Attachment)
void GatherAttachments(const FCbObject &Object, FAttachmentResolver Resolver)
void Save(FCbWriter &Writer) const
void SetObject(FCbObject InObject, FAttachmentResolver InResolver)
void AddAttachment(const FCbAttachment &Attachment, FAttachmentResolver Resolver)
FBinaryValue(BufferType &&InBuffer)
FBinaryValue(BufferType &&InBuffer, const FIoHash &InHash)
FObjectValue(FCbObject &&Object, const FIoHash *Hash)