LLVM 23.0.0git
OnDiskGraphDB.h
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9/// \file
10/// This declares OnDiskGraphDB, an ondisk CAS database with a fixed length
11/// hash. This is the class that implements the database storage scheme without
12/// exposing the hashing algorithm.
13//
14//===----------------------------------------------------------------------===//
15
16#ifndef LLVM_CAS_ONDISKGRAPHDB_H
17#define LLVM_CAS_ONDISKGRAPHDB_H
18
23#include <atomic>
24
25namespace llvm::cas::ondisk {
26
27/// Standard 8 byte reference inside OnDiskGraphDB.
28class InternalRef {
29public:
30 FileOffset getFileOffset() const { return FileOffset(Data); }
31 uint64_t getRawData() const { return Data; }
32
33 static InternalRef getFromRawData(uint64_t Data) { return InternalRef(Data); }
34 static InternalRef getFromOffset(FileOffset Offset) {
35 return InternalRef(Offset.get());
36 }
37
38 friend bool operator==(InternalRef LHS, InternalRef RHS) {
39 return LHS.Data == RHS.Data;
40 }
41
42private:
44 InternalRef(uint64_t Data) : Data(Data) {}
46};
47
48/// Compact 4 byte reference inside OnDiskGraphDB for smaller references.
49class InternalRef4B {
50public:
51 FileOffset getFileOffset() const { return FileOffset(Data); }
52 uint32_t getRawData() const { return Data; }
53
54 /// Shrink to 4B reference.
55 static std::optional<InternalRef4B> tryToShrink(InternalRef Ref) {
56 uint64_t Offset = Ref.getRawData();
57 if (Offset > UINT32_MAX)
58 return std::nullopt;
59 return InternalRef4B(Offset);
60 }
61
62 operator InternalRef() const {
64 }
65
66private:
67 friend class InternalRef;
68 InternalRef4B(uint32_t Data) : Data(Data) {}
70};
71
72/// Array of internal node references.
74public:
75 size_t size() const { return Size; }
76 bool empty() const { return !Size; }
77
79 : public iterator_facade_base<iterator, std::random_access_iterator_tag,
80 const InternalRef> {
81 public:
82 bool operator==(const iterator &RHS) const { return I == RHS.I; }
85 return *Ref;
87 }
103 if (auto *Ref = dyn_cast<const InternalRef *>(I))
104 I = Ref + N;
105 else
107 return *this;
108 }
110 if (auto *Ref = dyn_cast<const InternalRef *>(I))
111 I = Ref - N;
112 else
114 return *this;
115 }
116 InternalRef operator[](ptrdiff_t N) const { return *(this->operator+(N)); }
117
118 iterator() = default;
119
120 uint64_t getOpaqueData() const { return uintptr_t(I.getOpaqueValue()); }
121
123 return iterator(
125 const InternalRef4B *>::getFromOpaqueValue((void *)
126 Opaque));
127 }
128
129 private:
131 explicit iterator(
133 : I(I) {}
135 };
136
137 bool operator==(const InternalRefArrayRef &RHS) const {
138 return size() == RHS.size() && std::equal(begin(), end(), RHS.begin());
139 }
140
141 iterator begin() const { return iterator(Begin); }
142 iterator end() const { return begin() + Size; }
143
144 /// Array accessor.
145 InternalRef operator[](ptrdiff_t N) const { return begin()[N]; }
146
147 bool is4B() const { return isa<const InternalRef4B *>(Begin); }
148 bool is8B() const { return isa<const InternalRef *>(Begin); }
149
151 if (is4B()) {
152 auto *B = cast<const InternalRef4B *>(Begin);
153 return ArrayRef((const uint8_t *)B, sizeof(InternalRef4B) * Size);
154 }
155 auto *B = cast<const InternalRef *>(Begin);
156 return ArrayRef((const uint8_t *)B, sizeof(InternalRef) * Size);
157 }
158
159 InternalRefArrayRef(std::nullopt_t = std::nullopt) {
160 // This is useful so that all the casts in the \p iterator functions can
161 // operate without needing to check for a null value.
162 static InternalRef PlaceHolder = InternalRef::getFromRawData(0);
163 Begin = &PlaceHolder;
164 }
165
167 : Begin(Refs.begin()), Size(Refs.size()) {}
168
170 : Begin(Refs.begin()), Size(Refs.size()) {}
171
172private:
174 size_t Size = 0;
175};
176
177/// Reference to a node. The node's data may not be stored in the database.
178/// An \p ObjectID instance can only be used with the \p OnDiskGraphDB instance
179/// it came from. \p ObjectIDs from different \p OnDiskGraphDB instances are not
180/// comparable.
181class ObjectID {
182public:
183 uint64_t getOpaqueData() const { return Opaque; }
184
185 static ObjectID fromOpaqueData(uint64_t Opaque) { return ObjectID(Opaque); }
186
187 friend bool operator==(const ObjectID &LHS, const ObjectID &RHS) {
188 return LHS.Opaque == RHS.Opaque;
189 }
190 friend bool operator!=(const ObjectID &LHS, const ObjectID &RHS) {
191 return !(LHS == RHS);
192 }
193
194private:
195 explicit ObjectID(uint64_t Opaque) : Opaque(Opaque) {}
196 uint64_t Opaque;
197};
198
199/// Handle for a loaded node object.
201public:
202 explicit ObjectHandle(uint64_t Opaque) : Opaque(Opaque) {}
203 uint64_t getOpaqueData() const { return Opaque; }
204
206 static ObjectHandle fromMemory(uintptr_t Ptr);
207
208 friend bool operator==(const ObjectHandle &LHS, const ObjectHandle &RHS) {
209 return LHS.Opaque == RHS.Opaque;
210 }
211 friend bool operator!=(const ObjectHandle &LHS, const ObjectHandle &RHS) {
212 return !(LHS == RHS);
213 }
214
215private:
216 uint64_t Opaque;
217};
218
219/// Iterator for ObjectID.
221 : public iterator_facade_base<object_refs_iterator,
222 std::random_access_iterator_tag, ObjectID> {
223public:
224 bool operator==(const object_refs_iterator &RHS) const { return I == RHS.I; }
226 return ObjectID::fromOpaqueData((*I).getRawData());
227 }
228 bool operator<(const object_refs_iterator &RHS) const { return I < RHS.I; }
230 return I - RHS.I;
231 }
233 I += N;
234 return *this;
235 }
237 I -= N;
238 return *this;
239 }
240 ObjectID operator[](ptrdiff_t N) const { return *(this->operator+(N)); }
241
244
245 uint64_t getOpaqueData() const { return I.getOpaqueData(); }
246
250
251private:
253};
254
256
257/// On-disk CAS nodes database, independent of a particular hashing algorithm.
258class OnDiskGraphDB {
259public:
260 /// Associate data & references with a particular object ID. If there is
261 /// already a record for this object the operation is a no-op. \param ID the
262 /// object ID to associate the data & references with. \param Refs references
263 /// \param Data data buffer.
266
267 /// Associates the data of a file with a particular object ID. If there is
268 /// already a record for this object the operation is a no-op.
269 ///
270 /// This is more than a convenience variant of \c store(), \c storeFile() can
271 /// perform optimizations that reduce I/O and disk space consumption.
272 ///
273 /// If there are any concurrent modifications to the file, the contents in the
274 /// CAS may be corrupt.
275 ///
276 /// \param ID the object ID to associate the data with.
277 /// \param FilePath the path of the file data.
279
280 /// \returns \p nullopt if the object associated with \p Ref does not exist.
282
283 /// \returns the hash bytes digest for the object reference.
285 // ObjectID should be valid to fetch Digest.
286 return cantFail(getDigest(getInternalRef(Ref)));
287 }
288
289 /// Form a reference for the provided hash. The reference can be used as part
290 /// of a CAS object even if it's not associated with an object yet.
292
293 /// Get an existing reference to the object \p Digest.
294 ///
295 /// Returns \p nullopt if the object is not stored in this CAS.
296 LLVM_ABI_FOR_TEST std::optional<ObjectID>
297 getExistingReference(ArrayRef<uint8_t> Digest, bool CheckUpstream = true);
298
299 /// Check whether the object associated with \p Ref is stored in the CAS.
300 /// Note that this function will fault-in according to the policy.
302
303 /// Check whether the object associated with \p Ref is stored in the CAS.
304 /// Note that this function does not fault-in.
305 bool containsObject(ObjectID Ref, bool CheckUpstream = true) const {
306 auto Presence = getObjectPresence(Ref, CheckUpstream);
307 if (!Presence) {
308 consumeError(Presence.takeError());
309 return false;
310 }
311 switch (*Presence) {
312 case ObjectPresence::Missing:
313 return false;
314 case ObjectPresence::InPrimaryDB:
315 return true;
316 case ObjectPresence::OnlyInUpstreamDB:
317 return true;
318 }
319 llvm_unreachable("Unknown ObjectPresence enum");
320 }
321
322 /// \returns the data part of the provided object handle.
324
325 /// \returns the object referenced by the provided object handle.
327 InternalRefArrayRef Refs = getInternalRefs(Node);
328 return make_range(Refs.begin(), Refs.end());
329 }
330
331 /// Encapsulates file info for an underlying object node.
333 /// The data of the object node.
335
336 struct FileInfoTy {
337 /// The file path of the object node.
338 std::string FilePath;
339 /// Whether the file of the object leaf node has an extra nul appended at
340 /// the end. If the file is copied the extra nul needs to be removed.
342 };
343 /// File information for the object, if available.
344 std::optional<FileInfoTy> FileInfo;
345 };
346
347 /// Provides access to the underlying file path, that represents an object
348 /// leaf node, when available.
349 ///
350 /// This enables reducing I/O and disk space consumption, i.e. instead of
351 /// loading the data in memory and then writing it to a file, the client could
352 /// clone the underlying file directly. The client *must not* write to or
353 /// delete the underlying file, the path is provided only for reading/copying.
355
356 /// \returns Total size of stored objects.
357 ///
358 /// NOTE: There's a possibility that the returned size is not including a
359 /// large object if the process crashed right at the point of inserting it.
360 LLVM_ABI_FOR_TEST size_t getStorageSize() const;
361
362 /// \returns The precentage of space utilization of hard space limits.
363 ///
364 /// Return value is an integer between 0 and 100 for percentage.
365 unsigned getHardStorageLimitUtilization() const;
366
367 void print(raw_ostream &OS) const;
368
369 /// Hashing function type for validation.
372
373 /// Validate the OnDiskGraphDB.
374 ///
375 /// \param Deep if true, rehash all the objects to ensure no data
376 /// corruption in stored objects, otherwise just validate the structure of
377 /// CAS database.
378 /// \param Hasher is the hashing function used for objects inside CAS.
379 Error validate(bool Deep, HashingFuncT Hasher) const;
380
381 /// Checks that \p ID exists in the index. It is allowed to not have data
382 /// associated with it.
384
385 /// How to fault-in nodes if an upstream database is used.
386 enum class FaultInPolicy {
387 /// Copy only the requested node.
389 /// Copy the the entire graph of a node.
391 };
392
393 /// Open the on-disk store from a directory.
394 ///
395 /// \param Path directory for the on-disk store. The directory will be created
396 /// if it doesn't exist.
397 /// \param HashName Identifier name for the hashing algorithm that is going to
398 /// be used.
399 /// \param HashByteSize Size for the object digest hash bytes.
400 /// \param UpstreamDB Optional on-disk store to be used for faulting-in nodes
401 /// if they don't exist in the primary store. The upstream store is only used
402 /// for reading nodes, new nodes are only written to the primary store. User
403 /// need to make sure \p UpstreamDB outlives current instance of
404 /// OnDiskGraphDB and the common usage is to have an \p UnifiedOnDiskCache to
405 /// manage both.
406 /// \param Policy If \p UpstreamDB is provided, controls how nodes are copied
407 /// to primary store. This is recorded at creation time and subsequent opens
408 /// need to pass the same policy otherwise the \p open will fail.
410 open(StringRef Path, StringRef HashName, unsigned HashByteSize,
411 OnDiskGraphDB *UpstreamDB = nullptr,
412 std::shared_ptr<OnDiskCASLogger> Logger = nullptr,
413 FaultInPolicy Policy = FaultInPolicy::FullTree);
414
416
417private:
418 /// Forward declaration for a proxy for an ondisk index record.
419 struct IndexProxy;
420
421 enum class ObjectPresence {
422 Missing,
423 InPrimaryDB,
424 OnlyInUpstreamDB,
425 };
426
427 /// Check if object exists and if it is on upstream only.
429 getObjectPresence(ObjectID Ref, bool CheckUpstream) const;
430
431 /// When \p load is called for a node that doesn't exist, this function tries
432 /// to load it from the upstream store and copy it to the primary one.
433 Expected<std::optional<ObjectHandle>> faultInFromUpstream(ObjectID PrimaryID);
434
435 /// Import the entire tree from upstream with \p UpstreamNode as root.
436 Error importFullTree(ObjectID PrimaryID, ObjectHandle UpstreamNode);
437 /// Import only the \param UpstreamNode.
438 Error importSingleNode(ObjectID PrimaryID, ObjectHandle UpstreamNode);
439 Error importUpstreamData(ObjectID PrimaryID, ArrayRef<ObjectID> PrimaryRefs,
440 ObjectHandle UpstreamNode);
441
442 enum class InternalUpstreamImportKind { Leaf, Leaf0 };
443 /// Private \c storeFile than optimizes internal upstream database imports.
444 Error storeFile(ObjectID ID, StringRef FilePath,
445 std::optional<InternalUpstreamImportKind> ImportKind);
446
447 /// Found the IndexProxy for the hash.
449
450 /// Get path for creating standalone data file.
451 void getStandalonePath(StringRef FileSuffix, FileOffset IndexOffset,
452 SmallVectorImpl<char> &Path) const;
453 /// Create a standalone leaf file.
454 Error createStandaloneLeaf(IndexProxy &I, ArrayRef<char> Data);
455
456 /// \name Helper functions for internal data structures.
457 /// \{
458 static InternalRef getInternalRef(ObjectID Ref) {
459 return InternalRef::getFromRawData(Ref.getOpaqueData());
460 }
461
462 static ObjectID getExternalReference(InternalRef Ref) {
463 return ObjectID::fromOpaqueData(Ref.getRawData());
464 }
465
466 static ObjectID getExternalReference(const IndexProxy &I);
467
468 static InternalRef makeInternalRef(FileOffset IndexOffset);
469
470 LLVM_ABI_FOR_TEST Expected<ArrayRef<uint8_t>>
471 getDigest(InternalRef Ref) const;
472
473 ArrayRef<uint8_t> getDigest(const IndexProxy &I) const;
474
475 Expected<IndexProxy> getIndexProxyFromRef(InternalRef Ref) const;
476
478 getIndexProxyFromPointer(OnDiskTrieRawHashMap::ConstOnDiskPtr P) const;
479
480 LLVM_ABI_FOR_TEST InternalRefArrayRef
481 getInternalRefs(ObjectHandle Node) const;
482 /// \}
483
484 /// Get the atomic variable that keeps track of the standalone data storage
485 /// size.
486 std::atomic<uint64_t> &standaloneStorageSize() const;
487
488 /// Increase the standalone data size.
489 void recordStandaloneSizeIncrease(size_t SizeIncrease);
490 /// Get the standalone data size.
491 uint64_t getStandaloneStorageSize() const;
492
493 // Private constructor.
494 OnDiskGraphDB(StringRef RootPath, OnDiskTrieRawHashMap Index,
495 OnDiskDataAllocator DataPool, OnDiskGraphDB *UpstreamDB,
496 FaultInPolicy Policy, std::shared_ptr<OnDiskCASLogger> Logger);
497
498 /// Mapping from hash to object reference.
499 ///
500 /// Data type is TrieRecord.
501 OnDiskTrieRawHashMap Index;
502
503 /// Storage for most objects.
504 ///
505 /// Data type is DataRecordHandle.
506 OnDiskDataAllocator DataPool;
507
508 /// A StandaloneDataMap.
509 void *StandaloneData = nullptr;
510
511 /// Path to the root directory.
512 std::string RootPath;
513
514 /// Optional on-disk store to be used for faulting-in nodes.
515 OnDiskGraphDB *UpstreamDB = nullptr;
516
517 /// The policy used to fault in data from upstream.
518 FaultInPolicy FIPolicy;
519
520 /// Debug Logger.
521 std::shared_ptr<OnDiskCASLogger> Logger;
522};
523
524} // namespace llvm::cas::ondisk
525
526#endif // LLVM_CAS_ONDISKGRAPHDB_H
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
AMDGPU Mark last scratch load
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
#define LLVM_ABI_FOR_TEST
Definition Compiler.h:218
#define I(x, y, z)
Definition MD5.cpp:57
This file declares interface for OnDiskCASLogger, an interface that can be used to log CAS events to ...
This file declares interface for OnDiskDataAllocator, a file backed data pool can be used to allocate...
static void getStandalonePath(StringRef RootPath, StringRef Prefix, FileOffset IndexOffset, SmallVectorImpl< char > &Path)
This file declares interface for OnDiskTrieRawHashMap, a thread-safe and (mostly) lock-free hash map ...
#define P(N)
This file defines the PointerUnion class, which is a discriminated union of pointer types.
Value * RHS
Value * LHS
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
Tagged union holding either a T or a Error.
Definition Error.h:485
Logging utility - given an ordered specification of features, and assuming a scalar reward,...
A discriminated union of two or more pointer types, with the discriminator in the low bit of the poin...
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
FileOffset is a wrapper around uint64_t to represent the offset of data from the beginning of the fil...
Definition FileOffset.h:24
Handle to a loaded object in a ObjectStore instance.
Compact 4 byte reference inside OnDiskGraphDB for smaller references.
static std::optional< InternalRef4B > tryToShrink(InternalRef Ref)
Shrink to 4B reference.
ptrdiff_t operator-(const iterator &RHS) const
bool operator==(const iterator &RHS) const
static iterator fromOpaqueData(uint64_t Opaque)
bool operator<(const iterator &RHS) const
Array of internal node references.
InternalRef operator[](ptrdiff_t N) const
Array accessor.
ArrayRef< uint8_t > getBuffer() const
InternalRefArrayRef(std::nullopt_t=std::nullopt)
InternalRefArrayRef(ArrayRef< InternalRef4B > Refs)
bool operator==(const InternalRefArrayRef &RHS) const
InternalRefArrayRef(ArrayRef< InternalRef > Refs)
Standard 8 byte reference inside OnDiskGraphDB.
friend bool operator==(InternalRef LHS, InternalRef RHS)
FileOffset getFileOffset() const
static InternalRef getFromRawData(uint64_t Data)
static InternalRef getFromOffset(FileOffset Offset)
Handle for a loaded node object.
static ObjectHandle fromFileOffset(FileOffset Offset)
static ObjectHandle fromMemory(uintptr_t Ptr)
friend bool operator!=(const ObjectHandle &LHS, const ObjectHandle &RHS)
friend bool operator==(const ObjectHandle &LHS, const ObjectHandle &RHS)
Reference to a node.
friend bool operator!=(const ObjectID &LHS, const ObjectID &RHS)
uint64_t getOpaqueData() const
friend bool operator==(const ObjectID &LHS, const ObjectID &RHS)
static ObjectID fromOpaqueData(uint64_t Opaque)
On-disk CAS nodes database, independent of a particular hashing algorithm.
FaultInPolicy
How to fault-in nodes if an upstream database is used.
@ FullTree
Copy the the entire graph of a node.
@ SingleNode
Copy only the requested node.
void print(raw_ostream &OS) const
LLVM_ABI_FOR_TEST Error validateObjectID(ObjectID ID) const
Checks that ID exists in the index.
Expected< bool > isMaterialized(ObjectID Ref)
Check whether the object associated with Ref is stored in the CAS.
Error validate(bool Deep, HashingFuncT Hasher) const
Validate the OnDiskGraphDB.
object_refs_range getObjectRefs(ObjectHandle Node) const
unsigned getHardStorageLimitUtilization() const
LLVM_ABI_FOR_TEST Error store(ObjectID ID, ArrayRef< ObjectID > Refs, ArrayRef< char > Data)
Associate data & references with a particular object ID.
ArrayRef< uint8_t > getDigest(ObjectID Ref) const
FileBackedData getInternalFileBackedObjectData(ObjectHandle Node) const
Provides access to the underlying file path, that represents an object leaf node, when available.
LLVM_ABI_FOR_TEST Error storeFile(ObjectID ID, StringRef FilePath)
Associates the data of a file with a particular object ID.
LLVM_ABI_FOR_TEST size_t getStorageSize() const
static LLVM_ABI_FOR_TEST Expected< std::unique_ptr< OnDiskGraphDB > > open(StringRef Path, StringRef HashName, unsigned HashByteSize, OnDiskGraphDB *UpstreamDB=nullptr, std::shared_ptr< OnDiskCASLogger > Logger=nullptr, FaultInPolicy Policy=FaultInPolicy::FullTree)
Open the on-disk store from a directory.
bool containsObject(ObjectID Ref, bool CheckUpstream=true) const
Check whether the object associated with Ref is stored in the CAS.
LLVM_ABI_FOR_TEST Expected< ObjectID > getReference(ArrayRef< uint8_t > Hash)
Form a reference for the provided hash.
function_ref< void( ArrayRef< ArrayRef< uint8_t > >, ArrayRef< char >, SmallVectorImpl< uint8_t > &)> HashingFuncT
Hashing function type for validation.
LLVM_ABI_FOR_TEST ArrayRef< char > getObjectData(ObjectHandle Node) const
LLVM_ABI_FOR_TEST std::optional< ObjectID > getExistingReference(ArrayRef< uint8_t > Digest, bool CheckUpstream=true)
Get an existing reference to the object Digest.
object_refs_iterator & operator-=(ptrdiff_t N)
bool operator<(const object_refs_iterator &RHS) const
object_refs_iterator & operator+=(ptrdiff_t N)
ptrdiff_t operator-(const object_refs_iterator &RHS) const
bool operator==(const object_refs_iterator &RHS) const
ObjectID operator[](ptrdiff_t N) const
static object_refs_iterator fromOpaqueData(uint64_t Opaque)
object_refs_iterator(InternalRefArrayRef::iterator I)
An efficient, type-erasing, non-owning reference to a callable.
CRTP base class which implements the entire standard iterator facade in terms of a minimal subset of ...
Definition iterator.h:80
A range adaptor for a pair of iterators.
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition raw_ostream.h:53
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
llvm::iterator_range< object_refs_iterator > object_refs_range
@ Offset
Definition DWP.cpp:532
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
iterator_range< T > make_range(T x, T y)
Convenience function for iterating over sub-ranges.
decltype(auto) get(const PointerIntPair< PointerTy, IntBits, IntType, PtrTraits, Info > &Pair)
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
@ Ref
The access may reference the value stored in memory.
Definition ModRef.h:32
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition Error.h:769
FunctionAddr VTableAddr uintptr_t uintptr_t Data
Definition InstrProf.h:189
ArrayRef(const T &OneElt) -> ArrayRef< T >
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
void consumeError(Error Err)
Consume a Error without doing anything.
Definition Error.h:1083
#define N
Proxy for an on-disk index record.
std::string FilePath
The file path of the object node.
bool IsFileNulTerminated
Whether the file of the object leaf node has an extra nul appended at the end.
Encapsulates file info for an underlying object node.
std::optional< FileInfoTy > FileInfo
File information for the object, if available.
ArrayRef< char > Data
The data of the object node.