1 //===- MinidumpYAML.cpp - Minidump YAMLIO implementation ------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "llvm/ObjectYAML/MinidumpYAML.h"
10 #include "llvm/Support/Allocator.h"
11 #include "llvm/Support/ConvertUTF.h"
14 using namespace llvm::MinidumpYAML
;
15 using namespace llvm::minidump
;
18 /// A helper class to manage the placement of various structures into the final
19 /// minidump binary. Space for objects can be allocated via various allocate***
20 /// methods, while the final minidump file is written by calling the writeTo
21 /// method. The plain versions of allocation functions take a reference to the
22 /// data which is to be written (and hence the data must be available until
23 /// writeTo is called), while the "New" versions allocate the data in an
24 /// allocator-managed buffer, which is available until the allocator object is
25 /// destroyed. For both kinds of functions, it is possible to modify the
26 /// data for which the space has been "allocated" until the final writeTo call.
27 /// This is useful for "linking" the allocated structures via their offsets.
30 size_t tell() const { return NextOffset
; }
32 size_t allocateCallback(size_t Size
,
33 std::function
<void(raw_ostream
&)> Callback
) {
34 size_t Offset
= NextOffset
;
36 Callbacks
.push_back(std::move(Callback
));
40 size_t allocateBytes(ArrayRef
<uint8_t> Data
) {
41 return allocateCallback(
42 Data
.size(), [Data
](raw_ostream
&OS
) { OS
<< toStringRef(Data
); });
45 size_t allocateBytes(yaml::BinaryRef Data
) {
46 return allocateCallback(Data
.binary_size(), [Data
](raw_ostream
&OS
) {
47 Data
.writeAsBinary(OS
);
51 template <typename T
> size_t allocateArray(ArrayRef
<T
> Data
) {
52 return allocateBytes({reinterpret_cast<const uint8_t *>(Data
.data()),
53 sizeof(T
) * Data
.size()});
56 template <typename T
, typename RangeType
>
57 std::pair
<size_t, MutableArrayRef
<T
>>
58 allocateNewArray(const iterator_range
<RangeType
> &Range
);
60 template <typename T
> size_t allocateObject(const T
&Data
) {
61 return allocateArray(makeArrayRef(Data
));
64 template <typename T
, typename
... Types
>
65 std::pair
<size_t, T
*> allocateNewObject(Types
&&... Args
) {
66 T
*Object
= new (Temporaries
.Allocate
<T
>()) T(std::forward
<Types
>(Args
)...);
67 return {allocateObject(*Object
), Object
};
70 size_t allocateString(StringRef Str
);
72 void writeTo(raw_ostream
&OS
) const;
75 size_t NextOffset
= 0;
77 BumpPtrAllocator Temporaries
;
78 std::vector
<std::function
<void(raw_ostream
&)>> Callbacks
;
82 template <typename T
, typename RangeType
>
83 std::pair
<size_t, MutableArrayRef
<T
>>
84 BlobAllocator::allocateNewArray(const iterator_range
<RangeType
> &Range
) {
85 size_t Num
= std::distance(Range
.begin(), Range
.end());
86 MutableArrayRef
<T
> Array(Temporaries
.Allocate
<T
>(Num
), Num
);
87 std::uninitialized_copy(Range
.begin(), Range
.end(), Array
.begin());
88 return {allocateArray(Array
), Array
};
91 size_t BlobAllocator::allocateString(StringRef Str
) {
92 SmallVector
<UTF16
, 32> WStr
;
93 bool OK
= convertUTF8ToUTF16String(Str
, WStr
);
94 assert(OK
&& "Invalid UTF8 in Str?");
97 // The utf16 string is null-terminated, but the terminator is not counted in
101 allocateNewObject
<support::ulittle32_t
>(2 * (WStr
.size() - 1)).first
;
102 allocateNewArray
<support::ulittle16_t
>(make_range(WStr
.begin(), WStr
.end()));
106 void BlobAllocator::writeTo(raw_ostream
&OS
) const {
107 size_t BeginOffset
= OS
.tell();
108 for (const auto &Callback
: Callbacks
)
110 assert(OS
.tell() == BeginOffset
+ NextOffset
&&
111 "Callbacks wrote an unexpected number of bytes.");
115 /// Perform an optional yaml-mapping of an endian-aware type EndianType. The
116 /// only purpose of this function is to avoid casting the Default value to the
118 template <typename EndianType
>
119 static inline void mapOptional(yaml::IO
&IO
, const char *Key
, EndianType
&Val
,
120 typename
EndianType::value_type Default
) {
121 IO
.mapOptional(Key
, Val
, EndianType(Default
));
124 /// Yaml-map an endian-aware type EndianType as some other type MapType.
125 template <typename MapType
, typename EndianType
>
126 static inline void mapRequiredAs(yaml::IO
&IO
, const char *Key
,
128 MapType Mapped
= static_cast<typename
EndianType::value_type
>(Val
);
129 IO
.mapRequired(Key
, Mapped
);
130 Val
= static_cast<typename
EndianType::value_type
>(Mapped
);
133 /// Perform an optional yaml-mapping of an endian-aware type EndianType as some
134 /// other type MapType.
135 template <typename MapType
, typename EndianType
>
136 static inline void mapOptionalAs(yaml::IO
&IO
, const char *Key
, EndianType
&Val
,
138 MapType Mapped
= static_cast<typename
EndianType::value_type
>(Val
);
139 IO
.mapOptional(Key
, Mapped
, Default
);
140 Val
= static_cast<typename
EndianType::value_type
>(Mapped
);
144 /// Return the appropriate yaml Hex type for a given endian-aware type.
145 template <typename EndianType
> struct HexType
;
146 template <> struct HexType
<support::ulittle16_t
> { using type
= yaml::Hex16
; };
147 template <> struct HexType
<support::ulittle32_t
> { using type
= yaml::Hex32
; };
148 template <> struct HexType
<support::ulittle64_t
> { using type
= yaml::Hex64
; };
151 /// Yaml-map an endian-aware type as an appropriately-sized hex value.
152 template <typename EndianType
>
153 static inline void mapRequiredHex(yaml::IO
&IO
, const char *Key
,
155 mapRequiredAs
<typename HexType
<EndianType
>::type
>(IO
, Key
, Val
);
158 /// Perform an optional yaml-mapping of an endian-aware type as an
159 /// appropriately-sized hex value.
160 template <typename EndianType
>
161 static inline void mapOptionalHex(yaml::IO
&IO
, const char *Key
,
163 typename
EndianType::value_type Default
) {
164 mapOptionalAs
<typename HexType
<EndianType
>::type
>(IO
, Key
, Val
, Default
);
167 Stream::~Stream() = default;
169 Stream::StreamKind
Stream::getKind(StreamType Type
) {
171 case StreamType::ModuleList
:
172 return StreamKind::ModuleList
;
173 case StreamType::SystemInfo
:
174 return StreamKind::SystemInfo
;
175 case StreamType::LinuxCPUInfo
:
176 case StreamType::LinuxProcStatus
:
177 case StreamType::LinuxLSBRelease
:
178 case StreamType::LinuxCMDLine
:
179 case StreamType::LinuxMaps
:
180 case StreamType::LinuxProcStat
:
181 case StreamType::LinuxProcUptime
:
182 return StreamKind::TextContent
;
184 return StreamKind::RawContent
;
188 std::unique_ptr
<Stream
> Stream::create(StreamType Type
) {
189 StreamKind Kind
= getKind(Type
);
191 case StreamKind::ModuleList
:
192 return llvm::make_unique
<ModuleListStream
>();
193 case StreamKind::RawContent
:
194 return llvm::make_unique
<RawContentStream
>(Type
);
195 case StreamKind::SystemInfo
:
196 return llvm::make_unique
<SystemInfoStream
>();
197 case StreamKind::TextContent
:
198 return llvm::make_unique
<TextContentStream
>(Type
);
200 llvm_unreachable("Unhandled stream kind!");
203 void yaml::ScalarEnumerationTraits
<ProcessorArchitecture
>::enumeration(
204 IO
&IO
, ProcessorArchitecture
&Arch
) {
205 #define HANDLE_MDMP_ARCH(CODE, NAME) \
206 IO.enumCase(Arch, #NAME, ProcessorArchitecture::NAME);
207 #include "llvm/BinaryFormat/MinidumpConstants.def"
208 IO
.enumFallback
<Hex16
>(Arch
);
211 void yaml::ScalarEnumerationTraits
<OSPlatform
>::enumeration(IO
&IO
,
213 #define HANDLE_MDMP_PLATFORM(CODE, NAME) \
214 IO.enumCase(Plat, #NAME, OSPlatform::NAME);
215 #include "llvm/BinaryFormat/MinidumpConstants.def"
216 IO
.enumFallback
<Hex32
>(Plat
);
219 void yaml::ScalarEnumerationTraits
<StreamType
>::enumeration(IO
&IO
,
221 #define HANDLE_MDMP_STREAM_TYPE(CODE, NAME) \
222 IO.enumCase(Type, #NAME, StreamType::NAME);
223 #include "llvm/BinaryFormat/MinidumpConstants.def"
224 IO
.enumFallback
<Hex32
>(Type
);
227 void yaml::MappingTraits
<CPUInfo::ArmInfo
>::mapping(IO
&IO
,
228 CPUInfo::ArmInfo
&Info
) {
229 mapRequiredHex(IO
, "CPUID", Info
.CPUID
);
230 mapOptionalHex(IO
, "ELF hwcaps", Info
.ElfHWCaps
, 0);
234 template <std::size_t N
> struct FixedSizeHex
{
235 FixedSizeHex(uint8_t (&Storage
)[N
]) : Storage(Storage
) {}
237 uint8_t (&Storage
)[N
];
243 template <std::size_t N
> struct ScalarTraits
<FixedSizeHex
<N
>> {
244 static void output(const FixedSizeHex
<N
> &Fixed
, void *, raw_ostream
&OS
) {
245 OS
<< toHex(makeArrayRef(Fixed
.Storage
));
248 static StringRef
input(StringRef Scalar
, void *, FixedSizeHex
<N
> &Fixed
) {
249 if (!all_of(Scalar
, isHexDigit
))
250 return "Invalid hex digit in input";
251 if (Scalar
.size() < 2 * N
)
252 return "String too short";
253 if (Scalar
.size() > 2 * N
)
254 return "String too long";
255 copy(fromHex(Scalar
), Fixed
.Storage
);
259 static QuotingType
mustQuote(StringRef S
) { return QuotingType::None
; }
263 void yaml::MappingTraits
<CPUInfo::OtherInfo
>::mapping(
264 IO
&IO
, CPUInfo::OtherInfo
&Info
) {
265 FixedSizeHex
<sizeof(Info
.ProcessorFeatures
)> Features(Info
.ProcessorFeatures
);
266 IO
.mapRequired("Features", Features
);
270 /// A type which only accepts strings of a fixed size for yaml conversion.
271 template <std::size_t N
> struct FixedSizeString
{
272 FixedSizeString(char (&Storage
)[N
]) : Storage(Storage
) {}
280 template <std::size_t N
> struct ScalarTraits
<FixedSizeString
<N
>> {
281 static void output(const FixedSizeString
<N
> &Fixed
, void *, raw_ostream
&OS
) {
282 OS
<< StringRef(Fixed
.Storage
, N
);
285 static StringRef
input(StringRef Scalar
, void *, FixedSizeString
<N
> &Fixed
) {
286 if (Scalar
.size() < N
)
287 return "String too short";
288 if (Scalar
.size() > N
)
289 return "String too long";
290 copy(Scalar
, Fixed
.Storage
);
294 static QuotingType
mustQuote(StringRef S
) { return needsQuotes(S
); }
299 void yaml::MappingTraits
<CPUInfo::X86Info
>::mapping(IO
&IO
,
300 CPUInfo::X86Info
&Info
) {
301 FixedSizeString
<sizeof(Info
.VendorID
)> VendorID(Info
.VendorID
);
302 IO
.mapRequired("Vendor ID", VendorID
);
304 mapRequiredHex(IO
, "Version Info", Info
.VersionInfo
);
305 mapRequiredHex(IO
, "Feature Info", Info
.FeatureInfo
);
306 mapOptionalHex(IO
, "AMD Extended Features", Info
.AMDExtendedFeatures
, 0);
309 void yaml::MappingTraits
<VSFixedFileInfo
>::mapping(IO
&IO
,
310 VSFixedFileInfo
&Info
) {
311 mapOptionalHex(IO
, "Signature", Info
.Signature
, 0);
312 mapOptionalHex(IO
, "Struct Version", Info
.StructVersion
, 0);
313 mapOptionalHex(IO
, "File Version High", Info
.FileVersionHigh
, 0);
314 mapOptionalHex(IO
, "File Version Low", Info
.FileVersionLow
, 0);
315 mapOptionalHex(IO
, "Product Version High", Info
.ProductVersionHigh
, 0);
316 mapOptionalHex(IO
, "Product Version Low", Info
.ProductVersionLow
, 0);
317 mapOptionalHex(IO
, "File Flags Mask", Info
.FileFlagsMask
, 0);
318 mapOptionalHex(IO
, "File Flags", Info
.FileFlags
, 0);
319 mapOptionalHex(IO
, "File OS", Info
.FileOS
, 0);
320 mapOptionalHex(IO
, "File Type", Info
.FileType
, 0);
321 mapOptionalHex(IO
, "File Subtype", Info
.FileSubtype
, 0);
322 mapOptionalHex(IO
, "File Date High", Info
.FileDateHigh
, 0);
323 mapOptionalHex(IO
, "File Date Low", Info
.FileDateLow
, 0);
326 void yaml::MappingTraits
<ModuleListStream::ParsedModule
>::mapping(
327 IO
&IO
, ModuleListStream::ParsedModule
&M
) {
328 mapRequiredHex(IO
, "Base of Image", M
.Module
.BaseOfImage
);
329 mapRequiredHex(IO
, "Size of Image", M
.Module
.SizeOfImage
);
330 mapOptionalHex(IO
, "Checksum", M
.Module
.Checksum
, 0);
331 IO
.mapOptional("Time Date Stamp", M
.Module
.TimeDateStamp
,
332 support::ulittle32_t(0));
333 IO
.mapRequired("Module Name", M
.Name
);
334 IO
.mapOptional("Version Info", M
.Module
.VersionInfo
, VSFixedFileInfo());
335 IO
.mapRequired("CodeView Record", M
.CvRecord
);
336 IO
.mapOptional("Misc Record", M
.MiscRecord
, yaml::BinaryRef());
337 mapOptionalHex(IO
, "Reserved0", M
.Module
.Reserved0
, 0);
338 mapOptionalHex(IO
, "Reserved1", M
.Module
.Reserved1
, 0);
341 static void streamMapping(yaml::IO
&IO
, RawContentStream
&Stream
) {
342 IO
.mapOptional("Content", Stream
.Content
);
343 IO
.mapOptional("Size", Stream
.Size
, Stream
.Content
.binary_size());
346 static StringRef
streamValidate(RawContentStream
&Stream
) {
347 if (Stream
.Size
.value
< Stream
.Content
.binary_size())
348 return "Stream size must be greater or equal to the content size";
352 static void streamMapping(yaml::IO
&IO
, ModuleListStream
&Stream
) {
353 IO
.mapRequired("Modules", Stream
.Modules
);
356 static void streamMapping(yaml::IO
&IO
, SystemInfoStream
&Stream
) {
357 SystemInfo
&Info
= Stream
.Info
;
358 IO
.mapRequired("Processor Arch", Info
.ProcessorArch
);
359 mapOptional(IO
, "Processor Level", Info
.ProcessorLevel
, 0);
360 mapOptional(IO
, "Processor Revision", Info
.ProcessorRevision
, 0);
361 IO
.mapOptional("Number of Processors", Info
.NumberOfProcessors
, 0);
362 IO
.mapOptional("Product type", Info
.ProductType
, 0);
363 mapOptional(IO
, "Major Version", Info
.MajorVersion
, 0);
364 mapOptional(IO
, "Minor Version", Info
.MinorVersion
, 0);
365 mapOptional(IO
, "Build Number", Info
.BuildNumber
, 0);
366 IO
.mapRequired("Platform ID", Info
.PlatformId
);
367 IO
.mapOptional("CSD Version", Stream
.CSDVersion
, "");
368 mapOptionalHex(IO
, "Suite Mask", Info
.SuiteMask
, 0);
369 mapOptionalHex(IO
, "Reserved", Info
.Reserved
, 0);
370 switch (static_cast<ProcessorArchitecture
>(Info
.ProcessorArch
)) {
371 case ProcessorArchitecture::X86
:
372 case ProcessorArchitecture::AMD64
:
373 IO
.mapOptional("CPU", Info
.CPU
.X86
);
375 case ProcessorArchitecture::ARM
:
376 case ProcessorArchitecture::ARM64
:
377 IO
.mapOptional("CPU", Info
.CPU
.Arm
);
380 IO
.mapOptional("CPU", Info
.CPU
.Other
);
385 static void streamMapping(yaml::IO
&IO
, TextContentStream
&Stream
) {
386 IO
.mapOptional("Text", Stream
.Text
);
389 void yaml::MappingTraits
<std::unique_ptr
<Stream
>>::mapping(
390 yaml::IO
&IO
, std::unique_ptr
<MinidumpYAML::Stream
> &S
) {
394 IO
.mapRequired("Type", Type
);
396 if (!IO
.outputting())
397 S
= MinidumpYAML::Stream::create(Type
);
399 case MinidumpYAML::Stream::StreamKind::ModuleList
:
400 streamMapping(IO
, llvm::cast
<ModuleListStream
>(*S
));
402 case MinidumpYAML::Stream::StreamKind::RawContent
:
403 streamMapping(IO
, llvm::cast
<RawContentStream
>(*S
));
405 case MinidumpYAML::Stream::StreamKind::SystemInfo
:
406 streamMapping(IO
, llvm::cast
<SystemInfoStream
>(*S
));
408 case MinidumpYAML::Stream::StreamKind::TextContent
:
409 streamMapping(IO
, llvm::cast
<TextContentStream
>(*S
));
414 StringRef
yaml::MappingTraits
<std::unique_ptr
<Stream
>>::validate(
415 yaml::IO
&IO
, std::unique_ptr
<MinidumpYAML::Stream
> &S
) {
417 case MinidumpYAML::Stream::StreamKind::RawContent
:
418 return streamValidate(cast
<RawContentStream
>(*S
));
419 case MinidumpYAML::Stream::StreamKind::ModuleList
:
420 case MinidumpYAML::Stream::StreamKind::SystemInfo
:
421 case MinidumpYAML::Stream::StreamKind::TextContent
:
424 llvm_unreachable("Fully covered switch above!");
427 void yaml::MappingTraits
<Object
>::mapping(IO
&IO
, Object
&O
) {
428 IO
.mapTag("!minidump", true);
429 mapOptionalHex(IO
, "Signature", O
.Header
.Signature
, Header::MagicSignature
);
430 mapOptionalHex(IO
, "Version", O
.Header
.Version
, Header::MagicVersion
);
431 mapOptionalHex(IO
, "Flags", O
.Header
.Flags
, 0);
432 IO
.mapRequired("Streams", O
.Streams
);
435 static Directory
layout(BlobAllocator
&File
, Stream
&S
) {
437 Result
.Type
= S
.Type
;
438 Result
.Location
.RVA
= File
.tell();
439 Optional
<size_t> DataEnd
;
441 case Stream::StreamKind::ModuleList
: {
442 ModuleListStream
&List
= cast
<ModuleListStream
>(S
);
444 File
.allocateNewObject
<support::ulittle32_t
>(List
.Modules
.size());
445 for (ModuleListStream::ParsedModule
&M
: List
.Modules
)
446 File
.allocateObject(M
.Module
);
448 // Module names and CodeView/Misc records are not a part of the stream.
449 DataEnd
= File
.tell();
450 for (ModuleListStream::ParsedModule
&M
: List
.Modules
) {
451 M
.Module
.ModuleNameRVA
= File
.allocateString(M
.Name
);
453 M
.Module
.CvRecord
.RVA
= File
.allocateBytes(M
.CvRecord
);
454 M
.Module
.CvRecord
.DataSize
= M
.CvRecord
.binary_size();
456 M
.Module
.MiscRecord
.RVA
= File
.allocateBytes(M
.MiscRecord
);
457 M
.Module
.MiscRecord
.DataSize
= M
.MiscRecord
.binary_size();
461 case Stream::StreamKind::RawContent
: {
462 RawContentStream
&Raw
= cast
<RawContentStream
>(S
);
463 File
.allocateCallback(Raw
.Size
, [&Raw
](raw_ostream
&OS
) {
464 Raw
.Content
.writeAsBinary(OS
);
465 assert(Raw
.Content
.binary_size() <= Raw
.Size
);
466 OS
<< std::string(Raw
.Size
- Raw
.Content
.binary_size(), '\0');
470 case Stream::StreamKind::SystemInfo
: {
471 SystemInfoStream
&SystemInfo
= cast
<SystemInfoStream
>(S
);
472 File
.allocateObject(SystemInfo
.Info
);
473 // The CSD string is not a part of the stream.
474 DataEnd
= File
.tell();
475 SystemInfo
.Info
.CSDVersionRVA
= File
.allocateString(SystemInfo
.CSDVersion
);
478 case Stream::StreamKind::TextContent
:
479 File
.allocateArray(arrayRefFromStringRef(cast
<TextContentStream
>(S
).Text
));
482 // If DataEnd is not set, we assume everything we generated is a part of the
484 Result
.Location
.DataSize
=
485 DataEnd
.getValueOr(File
.tell()) - Result
.Location
.RVA
;
489 void MinidumpYAML::writeAsBinary(Object
&Obj
, raw_ostream
&OS
) {
491 File
.allocateObject(Obj
.Header
);
493 std::vector
<Directory
> StreamDirectory(Obj
.Streams
.size());
494 Obj
.Header
.StreamDirectoryRVA
=
495 File
.allocateArray(makeArrayRef(StreamDirectory
));
496 Obj
.Header
.NumberOfStreams
= StreamDirectory
.size();
498 for (auto &Stream
: enumerate(Obj
.Streams
))
499 StreamDirectory
[Stream
.index()] = layout(File
, *Stream
.value());
504 Error
MinidumpYAML::writeAsBinary(StringRef Yaml
, raw_ostream
&OS
) {
505 yaml::Input
Input(Yaml
);
508 if (std::error_code EC
= Input
.error())
509 return errorCodeToError(EC
);
511 writeAsBinary(Obj
, OS
);
512 return Error::success();
515 Expected
<std::unique_ptr
<Stream
>>
516 Stream::create(const Directory
&StreamDesc
, const object::MinidumpFile
&File
) {
517 StreamKind Kind
= getKind(StreamDesc
.Type
);
519 case StreamKind::ModuleList
: {
520 auto ExpectedList
= File
.getModuleList();
522 return ExpectedList
.takeError();
523 std::vector
<ModuleListStream::ParsedModule
> Modules
;
524 for (const Module
&M
: *ExpectedList
) {
525 auto ExpectedName
= File
.getString(M
.ModuleNameRVA
);
527 return ExpectedName
.takeError();
528 auto ExpectedCv
= File
.getRawData(M
.CvRecord
);
530 return ExpectedCv
.takeError();
531 auto ExpectedMisc
= File
.getRawData(M
.MiscRecord
);
533 return ExpectedMisc
.takeError();
535 {M
, std::move(*ExpectedName
), *ExpectedCv
, *ExpectedMisc
});
537 return llvm::make_unique
<ModuleListStream
>(std::move(Modules
));
539 case StreamKind::RawContent
:
540 return llvm::make_unique
<RawContentStream
>(StreamDesc
.Type
,
541 File
.getRawStream(StreamDesc
));
542 case StreamKind::SystemInfo
: {
543 auto ExpectedInfo
= File
.getSystemInfo();
545 return ExpectedInfo
.takeError();
546 auto ExpectedCSDVersion
= File
.getString(ExpectedInfo
->CSDVersionRVA
);
547 if (!ExpectedCSDVersion
)
548 return ExpectedInfo
.takeError();
549 return llvm::make_unique
<SystemInfoStream
>(*ExpectedInfo
,
550 std::move(*ExpectedCSDVersion
));
552 case StreamKind::TextContent
:
553 return llvm::make_unique
<TextContentStream
>(
554 StreamDesc
.Type
, toStringRef(File
.getRawStream(StreamDesc
)));
556 llvm_unreachable("Unhandled stream kind!");
559 Expected
<Object
> Object::create(const object::MinidumpFile
&File
) {
560 std::vector
<std::unique_ptr
<Stream
>> Streams
;
561 Streams
.reserve(File
.streams().size());
562 for (const Directory
&StreamDesc
: File
.streams()) {
563 auto ExpectedStream
= Stream::create(StreamDesc
, File
);
565 return ExpectedStream
.takeError();
566 Streams
.push_back(std::move(*ExpectedStream
));
568 return Object(File
.header(), std::move(Streams
));