yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Theresa FoleyAdd a memory-mappable binary serialization format (#7222)ec7ab914f

master
27.5 KiB1035 linesraw
1// slang-riff.h
2#ifndef SLANG_RIFF_H
3#define SLANG_RIFF_H
4
5// This file defines an API for reading and writing files in the
6// RIFF file format.
7//
8// Some references on the RIFF format include:
9//
10// * http://fileformats.archiveteam.org/wiki/RIFF
11// * http://www.fileformat.info/format/riff/egff.htm
12//
13// RIFF files, and formats inspired by it, are commonly used as
14// binary interchange formats in cases where ad hoc extensibility
15// is needed.
16//
17
18#include "slang-basic.h"
19#include "slang-internally-linked-list.h"
20#include "slang-memory-arena.h"
21#include "slang-stream.h"
22#include "slang-writer.h"
23
24namespace Slang
25{
26
27//
28// An important concept in the RIFF format, as well as
29// many derived formats, is a *four-character code*, usually
30// referred to as a "FourCC" or "FOURCC".
31//
32
33/// A 32-bit value that comprises four ASCII characters.
34///
35/// A `FourCC` can be used as a kind of "extensible `enum`" in situations
36/// where different developers or groups may want to independently add
37/// cases, while minimizing the chances of accidental collisions.
38///
39/// A `FourCC` can be efficienctly compared, or used in `switch`
40/// statements, which can be an advantage compared to alternative
41/// extensible formats like strings or UUIDs.
42///
43/// In memory, the characters of a `FourCC` come in the
44/// usual order that they would for an array of four `char`s;
45/// that is, the first character occupies the byte with the
46/// lowest address, and so forth. When that same memory
47/// is read as a 32-bit integer, the integer value read will
48/// depend on the endianness of the architecture.
49///
50struct FourCC
51{
52public:
53    /// The value of a `FourCC`, represented as single integer.
54    using RawValue = UInt32;
55
56    FourCC() { _rawValue = 0; }
57
58    FourCC(RawValue rawValue) { _rawValue = rawValue; }
59
60    void operator=(RawValue rawValue) { _rawValue = rawValue; }
61
62    RawValue getRawValue() const { return _rawValue; }
63
64    operator RawValue() const { return _rawValue; }
65
66private:
67    //
68    // The storage for a `FourCC` is defined in a
69    // way that makes the textual form more visible
70    // when debugging.
71    //
72    union
73    {
74        char _text[4];
75        RawValue _rawValue;
76    };
77};
78
79//
80// Because the integer representation of a `FourCC` depends
81// on the endianness of the architecture, we define a macro
82// to turn a sequence of four independent characters into
83// a single `FourCC::RawValue`, based on the target
84// architecture.
85//
86
87#if SLANG_LITTLE_ENDIAN
88
89#define SLANG_FOUR_CC(c0, c1, c2, c3)                                                           \
90    ((FourCC::RawValue(c0) << 0) | (FourCC::RawValue(c1) << 8) | (FourCC::RawValue(c2) << 16) | \
91     (FourCC::RawValue(c3) << 24))
92
93#else
94
95#define SLANG_FOUR_CC(c0, c1, c2, c3)                                                            \
96    ((FourCC::RawValue(c0) << 24) | (FourCC::RawValue(c1) << 16) | (FourCC::RawValue(c2) << 8) | \
97     (FourCC::RawValue(c3) << 0))
98#endif
99
100
101namespace RIFF
102{
103
104struct Chunk;
105struct DataChunk;
106struct ListChunk;
107struct RootChunk;
108class ChunkBuilder;
109class ListChunkBuilder;
110class DataChunkBuilder;
111struct Builder;
112
113//
114// A RIFF file is organized as a tree of *chunks*.
115//
116
117/// A chunk in a RIFF file.
118///
119struct Chunk
120{
121public:
122    //
123    // The starting offset of a chunk in a RIFF file
124    // is only guaranteed to be 2-byte aligned.
125    // Code that reads from a chunk must be cautious about
126    // the possibility of performing unaligned loads.
127    //
128
129    /// Required alignment for the starting offset of a chunk.
130    static const UInt32 kChunkAlignment = 2;
131
132    //
133    // Every chunk starts with a *header*, which includes
134    // a *tag* used to identify the kind/type of chunk
135    // as well a representation of the size of the chunk
136    // in bytes.
137    //
138    struct Header
139    {
140        //
141        // Note that when loading a RIFF file into memory,
142        // chunks may not start on 4-byte-aligned boundaries,
143        // so code should not directly read the following
144        // fields unless preconditions exist to guarantee
145        // higher alignment.
146        //
147
148        /// Tag for this chunk.
149        ///
150        /// * For a data chunk, this will be its type.
151        /// * For a list chunk this will be `"LIST"`.
152        /// * For a root chunk this will be `"RIFF"`.
153        ///
154        FourCC tag;
155
156        /// Size in bytes of this chunk, not including this header.
157        UInt32 size;
158    };
159
160    /// Get the header for this chunk.
161    Header const* getHeader() const { return (Header const*)this; }
162
163    /// Get the tag from the header of this chunk.
164    FourCC getTag() const { return _readTagFromHeader(); }
165
166    /// Get the total size of this chunk, in bytes.
167    ///
168    /// This size includes the chunk header.
169    ///
170    UInt32 getTotalSize() const { return sizeof(RIFF::Chunk) + _readSizeFromHeader(); }
171
172    //
173    // There are three *kinds* of chunks that can appear in a RIFF:
174    //
175    // * *data chunks* contain zero or more bytes of data.
176    //
177    // * *list chunks* contain a sequence of other chunks
178    //
179    // * a *root chunk* is a special case of list chunk that
180    //   is used as the root of the chunk hierarchy in a RIFF file.
181    //
182    // List chunks are identified by having a tag of `"LIST"`
183    // in their header, while root chunks have a tag of `"RIFF"`
184    //
185
186    /// Kind of a chunk.
187    enum class Kind
188    {
189        Data,
190        List,
191        Root,
192    };
193
194    /// Get the kind of this chunk.
195    Kind getKind() const;
196
197    //
198    // Every chunk has a *type*, which is a `FourCC`.
199    //
200    // For data chunks, the type is stored as the tag
201    // of the chunk header, while for list and root
202    // chunks the type is stored immediately after
203    // the chunk header.
204    //
205
206    /// Type of a chunk.
207    using Type = FourCC;
208
209    /// Get the type of this chunk.
210    Type getType() const;
211
212private:
213    Header _header;
214
215protected:
216    //
217    // Rather than directly reading the `_tag` or `_size`
218    // members, code should use the following accessors,
219    // which account for possible alignment issues.
220    //
221
222    FourCC _readTagFromHeader() const
223    {
224        auto header = getHeader();
225        FourCC result;
226        memcpy(&result, &header->tag, sizeof(header->tag));
227        return result;
228    }
229
230    UInt32 _readSizeFromHeader() const
231    {
232        auto header = getHeader();
233        UInt32 result;
234        memcpy(&result, &header->size, sizeof(header->size));
235        return result;
236    }
237};
238
239/// A chunk that contains zero or more bytes of payload data.
240struct DataChunk : Chunk
241{
242public:
243    /// Get the size in bytes of the payload data of this chunk.
244    UInt32 getPayloadSize() const { return _readSizeFromHeader(); }
245
246    /// Get a pointer to the payload data of this chunk.
247    ///
248    /// Note that this pointer is only guaranteed to be aligned
249    /// up to `RIFF::Chunk::kAlignment`, for chunks of a RIFF
250    /// file loaded directly into memory.
251    ///
252    void const* getPayload() const { return static_cast<void const*>(this + 1); }
253
254    /// Write the payload data of this chunk into the given buffer.
255    ///
256    /// The payload must be at least `size` bytes.
257    /// If the payload is larger than `size` bytes, only the
258    /// first `size` bytes will be written to the buffer.
259    ///
260    void writePayloadInto(void* outData, Size size) const;
261
262    /// Get the payload data of this chunk.
263    ///
264    template<typename T>
265    void writePayloadInto(T& outValue) const
266    {
267        writePayloadInto(&outValue, sizeof(outValue));
268    }
269
270    /// Get the payload data of this chunk.
271    ///
272    template<typename T>
273    T readPayloadAs() const
274    {
275        T result;
276        writePayloadInto(result);
277        return result;
278    }
279
280    /// Get the type of this chunk.
281    Type getType() const
282    {
283        // The type of a data chunk is just the tag
284        // from the chunk header.
285
286        return getTag();
287    }
288
289    /// Determine if a chunk is an instance of this kind.
290    static bool _isChunkOfThisKind(Chunk const* chunk)
291    {
292        return chunk->getKind() == Chunk::Kind::Data;
293    }
294};
295
296/// A pointer to a `RIFF::Chunk` that is dynamically
297/// checked to make sure access doesn't go past a
298/// certain size bound.
299///
300struct BoundsCheckedChunkPtr
301{
302public:
303    /// Initialize a null pointer
304    BoundsCheckedChunkPtr() {}
305
306    /// Initialize a null pointer
307    BoundsCheckedChunkPtr(std::nullptr_t) {}
308
309
310    /// Initialize a pointer to a chunk, with a size limit.
311    BoundsCheckedChunkPtr(Chunk const* chunk, Size sizeLimit) { _set(chunk, sizeLimit); }
312
313    /// Initialize a pointer to a chunk, with a limit based on its reported size.
314    BoundsCheckedChunkPtr(Chunk const* chunk) { _set(chunk); }
315
316    /// Get the underlying chunk pointer.
317    Chunk const* get() const { return _ptr; }
318
319    operator Chunk const*() const { return get(); }
320    Chunk const* operator->() const { return get(); }
321
322    BoundsCheckedChunkPtr getNextSibling() const;
323
324private:
325    Chunk const* _ptr = nullptr;
326    Size _sizeLimit = 0;
327
328    void _set(Chunk const* chunk, Size sizeLimit);
329    void _set(Chunk const* chunk);
330};
331
332
333template<typename T = Chunk>
334struct ChunkList
335{
336public:
337    ChunkList() {}
338
339    ChunkList(BoundsCheckedChunkPtr firstChunk)
340        : _firstChunk(firstChunk)
341    {
342    }
343
344    struct Iterator
345    {
346    public:
347        Iterator() {}
348
349        Iterator(BoundsCheckedChunkPtr chunk)
350            : _chunk(chunk)
351        {
352        }
353
354        T const* operator*() const { return static_cast<T const*>(_chunk.get()); }
355
356        void operator++() { _chunk = _chunk.getNextSibling(); }
357
358        bool operator!=(Iterator const& that) const { return _chunk != that._chunk; }
359
360    private:
361        BoundsCheckedChunkPtr _chunk;
362    };
363
364    Iterator begin() const { return Iterator(_firstChunk); }
365    Iterator end() const { return Iterator(); }
366
367    template<typename U>
368    ChunkList<U> cast() const
369    {
370        return ChunkList<U>(_firstChunk);
371    }
372
373    T const* getFirst() const { return *begin(); }
374
375private:
376    friend struct ListChunk;
377
378    BoundsCheckedChunkPtr _firstChunk;
379};
380
381struct ListChunk : Chunk
382{
383public:
384    //
385    // A (non-root) list chunk has a tag of `"LIST"`
386    // in its header.
387    //
388
389    static const FourCC::RawValue kTag = SLANG_FOUR_CC('L', 'I', 'S', 'T');
390
391    //
392    // A list chunk starts with a header, as all chunks do,
393    // but for a list chunk the ordinary `Chunk::Header`
394    // is followed by an additional `FourCC`, to specify
395    // the type of the list chunk.
396    //
397
398    struct Header
399    {
400        //
401        // As is the case with any other chunk, code that
402        // wants to access these fields should be mindful
403        // of the way that a RIFF does not guarantee 4-byte
404        // alignment for chunks.
405        //
406
407        /// The base chunk header.
408        Chunk::Header chunkHeader;
409
410        /// The type of this list chunk.
411        Type type;
412    };
413
414    /// Get the header for this list chunk.
415    Header const* getHeader() const { return (Header const*)this; }
416
417    //
418    // The content of a list chunk comprises zero or more
419    // child chunks, organized as a kind of linked list.
420    //
421    // The starting offset for each successive child chunk
422    // is the end offset of the previous child chunk, rounded
423    // up to the required alignment (`RIFF::Chunk::kAlignment`).
424    //
425
426    /// List of child chunks.
427    using ChildList = ChunkList<>;
428
429    /// Get the list of children of this chunk.
430    ChildList getChildren() const { return ChildList(getFirstChild()); }
431
432    /// Get the first child chunk (if any) of this chunk.
433    ///
434    /// The list of child chunks can be navigated using
435    /// the `BoundCheckedChunkPtr::getNextSibling` operation.
436    ///
437    BoundsCheckedChunkPtr getFirstChild() const;
438
439    /// Find a child data chunk of the given `type`.
440    DataChunk const* findDataChunk(Chunk::Type type) const;
441
442    /// Find a child list chunk of the given `type`.
443    ListChunk const* findListChunk(Chunk::Type type) const;
444
445    /// Recursively search for a list chunk of the given `type`.
446    ///
447    /// Will consider this chunk itself as a possible match.
448    ///
449    ListChunk const* findListChunkRec(Chunk::Type type) const;
450
451    /// Get the type of this chunk.
452    Type getType() const { return _readTypeFromHeader(); }
453
454    /// Determine if a chunk is an instance of this kind.
455    static bool _isChunkOfThisKind(Chunk const* chunk)
456    {
457        // Anything that isn't a data chunk is a list.
458        return chunk->getKind() != Chunk::Kind::Data;
459    }
460
461private:
462    //
463    // Because we are inheriting from `Chunk`, we do not
464    // declare a full `ListChunk::Header` here, and instead
465    // just declare the additional field that appears after
466    // the base header.
467    //
468
469    Type _type;
470
471    //
472    // The `_type` field is mostly just there for debugging
473    // purposes; when actually reading from the header, we
474    // make use of a cast.
475    //
476
477    Type _readTypeFromHeader() const
478    {
479        auto header = getHeader();
480        Type result;
481        memcpy(&result, &header->type, sizeof(header->type));
482        return result;
483    }
484};
485
486struct RootChunk : ListChunk
487{
488public:
489    //
490    // A root chunk has a tag of `"RIFF"` in its header.
491    //
492
493    static const FourCC::RawValue kTag = SLANG_FOUR_CC('R', 'I', 'F', 'F');
494
495    /// Get a pointer to the root chunk of a RIFF hierarchy stored in a data blob.
496    ///
497    /// Performs some minimal validity checks, and returns `nullptr` if
498    /// the blob provided does not superficially appear to be a valid RIFF.
499    ///
500    static RootChunk const* getFromBlob(void const* data, size_t dataSize);
501
502    /// Get a pointer to the root chunk of a RIFF hierarchy stored in a data blob.
503    ///
504    /// Performs some minimal validity checks, and returns `nullptr` if
505    /// the blob provided does not superficially appear to be a valid RIFF.
506    ///
507    static RootChunk const* getFromBlob(ISlangBlob* blob);
508
509    /// Determine if a chunk is an instance of this kind.
510    static bool _isChunkOfThisKind(Chunk const* chunk)
511    {
512        return chunk->getKind() == Chunk::Kind::Root;
513    }
514
515private:
516    static bool _isTagForThisKind(FourCC tag) { return tag == kTag; }
517};
518
519inline Chunk::Kind Chunk::getKind() const
520{
521    switch (getTag())
522    {
523    case RootChunk::kTag:
524        return Chunk::Kind::Root;
525    case ListChunk::kTag:
526        return Chunk::Kind::List;
527    default:
528        return Chunk::Kind::Data;
529    }
530}
531
532inline Chunk::Type Chunk::getType() const
533{
534    auto tag = getTag();
535    switch (tag)
536    {
537    case RootChunk::kTag:
538    case ListChunk::kTag:
539        return static_cast<ListChunk const*>(this)->getType();
540
541    default:
542        return tag;
543    }
544}
545
546/// Cast a `Chunk` to a sub-type of `Chunk`.
547template<typename T>
548T* as(Chunk* chunk)
549{
550    if (!chunk)
551        return nullptr;
552    if (!T::_isChunkOfThisKind(chunk))
553        return nullptr;
554    return static_cast<T*>(chunk);
555}
556
557/// Cast a `Chunk` to a sub-type of `Chunk`.
558template<typename T>
559T const* as(Chunk const* chunk)
560{
561    if (!chunk)
562        return nullptr;
563    if (!T::_isChunkOfThisKind(chunk))
564        return nullptr;
565    return static_cast<T const*>(chunk);
566}
567
568/// A builder for a chunk in a RIFF.
569class ChunkBuilder : public InternallyLinkedList<ChunkBuilder>::Node
570{
571public:
572    /// Get the kind of the chunk being built.
573    Chunk::Kind getKind() const { return _kind; }
574
575    /// Get the type of the chunk being built.
576    Chunk::Type getType() const { return _type; }
577
578    /// Set the type of the chunk being built.
579    void setType(Chunk::Type type) { _type = type; }
580
581    /// Get the parent chunk of this chunk in the RIFF hierarchy.
582    ///
583    ListChunkBuilder* getParent() const { return _parent; }
584
585    /// Get the RIFF builder that this chunk belongs to.
586    ///
587    RIFF::Builder* getRIFFBuilder() const { return _riffBuilder; }
588
589protected:
590    ChunkBuilder(
591        Chunk::Kind kind,
592        Chunk::Type type,
593        ListChunkBuilder* parent,
594        RIFF::Builder* riffBuilder)
595        : _kind(kind), _type(type), _parent(parent), _riffBuilder(riffBuilder)
596    {
597    }
598
599    ChunkBuilder(ChunkBuilder const&) = delete;
600    void operator=(ChunkBuilder const&) = delete;
601
602    MemoryArena& _getMemoryArena() const;
603
604private:
605    Chunk::Kind _kind = Chunk::Kind(-1);
606    Chunk::Type _type = 0;
607    ListChunkBuilder* _parent = nullptr;
608    Builder* _riffBuilder = nullptr;
609
610    // A cached total size for this chunk. This
611    // is only valid after `_updateCachedTotalSize()`
612    // has been called, and before any subsequent
613    // changes to the content of this chunk or any
614    // of its descendents in the hierarchy.
615    //
616    mutable Size _cachedTotalSize = 0;
617
618    Size _updateCachedTotalSize() const;
619
620    Size _getCachedTotalSize() const { return _cachedTotalSize; }
621
622    /// Write the binary representation of this chunk to the given `stream`
623    ///
624    /// Assumes that `_updateCachedTotalSize` has been used
625    /// so that the cached total size of this chunk is valid.
626    ///
627    Result _writeTo(Stream* stream) const;
628
629    friend struct Builder;
630};
631
632class ListChunkBuilder : public ChunkBuilder
633{
634public:
635    /// A list of child chunks.
636    using ChildList = InternallyLinkedList<ChunkBuilder>;
637
638    /// Get the child chunks of this list.
639    ChildList getChildren() const { return _children; }
640
641    /// Append a new data chunk to the current list chunk.
642    DataChunkBuilder* addDataChunk(Chunk::Type type);
643
644    /// Append a new data chunk to the current list chunk.
645    ListChunkBuilder* addListChunk(Chunk::Type type);
646
647    /// Determine if a chunk is an instance of this kind.
648    static bool _isChunkOfThisKind(ChunkBuilder const* chunk)
649    {
650        return chunk->getKind() != Chunk::Kind::Data;
651    }
652
653private:
654    ListChunkBuilder(Chunk::Type type, ListChunkBuilder* parent)
655        : ChunkBuilder(Chunk::Kind::List, type, parent, parent->getRIFFBuilder())
656    {
657    }
658
659    friend struct RIFF::Builder;
660
661    ListChunkBuilder(Chunk::Type type, RIFF::Builder* riffBuilder)
662        : ChunkBuilder(Chunk::Kind::Root, type, nullptr, riffBuilder)
663    {
664    }
665
666    ChildList _children;
667};
668
669
670/// A builder for a data chunk in a RIFF.
671class DataChunkBuilder : public ChunkBuilder
672{
673public:
674    /// Append data to this chunk.
675    void addData(void const* data, Size size);
676
677    /// Append data to this chunk.
678    template<typename T>
679    void addData(T const& value)
680    {
681        addData(&value, sizeof(value));
682    }
683
684    /// Append existing data to this chunk.
685    ///
686    /// The caller takes responsibility for ensuring that
687    /// the passed-in data pointer will remain valid for
688    /// the rest of the lifetime of the enclosing RIFF
689    /// builder.
690    ///
691    void addUnownedData(void const* data, size_t size);
692
693    //
694    // While the payload of a chunk in a RIFF file is
695    // contiguous, the payload of a `DataChunkBuilder`
696    // can span multiple different allocations, which
697    // this implementation refers to as *shards*.
698    //
699    // Each shard has a contiguous payload, and the
700    // `DataChunkBuilder` owns a list of shards. The
701    // logical payload of the data chunk is the
702    // concatenation of the payloads of its shards.
703    //
704
705    /// A contiguous range of bytes in a `RIFF::DataChunkBuilder`
706    class Shard : public InternallyLinkedList<Shard>::Node
707    {
708    public:
709        /// Get the payload of this shard.
710        void const* getPayload() const { return _payload; }
711
712        /// Get the size of the payload of this shard.
713        Size getPayloadSize() const { return _payloadSize; }
714
715    private:
716        friend class DataChunkBuilder;
717
718        Shard() {}
719
720        void setPayload(void const* data, Size size)
721        {
722            _payload = data;
723            _payloadSize = size;
724        }
725
726        void const* _payload = nullptr;
727        Size _payloadSize = 0;
728    };
729
730    /// List of shards in a data chunk.
731    using ShardList = InternallyLinkedList<Shard>;
732
733    /// Get the list of shards that make up this chunk.
734    ShardList getShards() const { return _shards; }
735
736    /// Determine if a chunk is an instance of this kind.
737    static bool _isChunkOfThisKind(ChunkBuilder const* chunk)
738    {
739        return chunk->getKind() == Chunk::Kind::Data;
740    }
741
742private:
743    friend class ListChunkBuilder;
744
745    DataChunkBuilder(Chunk::Type type, ListChunkBuilder* parent)
746        : ChunkBuilder(Chunk::Kind::Data, type, parent, parent->getRIFFBuilder())
747    {
748    }
749
750    Shard* _addShard();
751
752    ShardList _shards;
753};
754
755template<typename T>
756T* as(ChunkBuilder* chunk)
757{
758    if (!chunk)
759        return nullptr;
760    if (!T::_isChunkOfThisKind(chunk))
761        return nullptr;
762    return static_cast<T*>(chunk);
763}
764
765template<typename T>
766T const* as(ChunkBuilder const* chunk)
767{
768    if (!chunk)
769        return nullptr;
770    if (!T::_isChunkOfThisKind(chunk))
771        return nullptr;
772    return static_cast<T const*>(chunk);
773}
774
775/// A builder for a RIFF-structured file.
776///
777struct Builder
778{
779public:
780    /// Initialize a builder with an empty tree of chunks.
781    Builder();
782
783    /// Write the built hierarchy out to the given `stream`.
784    Result writeTo(Stream* stream);
785
786    /// Write the built hierarchy out as a blob.
787    Result writeToBlob(ISlangBlob** outBlob);
788
789    /// Get the root chunk of the RIFF being built.
790    ///
791    /// If a root chunk has not yet been added, returns `nullptr`.
792    ///
793    ListChunkBuilder* getRootChunk() const { return _rootChunk; }
794
795    /// Add a root chunk to the RIFF being built.
796    ///
797    /// There must not already be a root chunk.
798    ///
799    /// Returns the root chunk that was added.
800    ///
801    ListChunkBuilder* addRootChunk(Chunk::Type type);
802
803    /// Get the memory arena used for allocation.
804    ///
805    /// This arena is used for allocating all of the chunk
806    /// builders, as well as their data.
807    ///
808    /// Note: typical use cases should never need to
809    /// access this; it is part of the public API
810    /// primarily to enable some of the unit tests.
811    ///
812    MemoryArena& _getMemoryArena() { return _arena; }
813
814private:
815    Builder(Builder const&) = delete;
816    void operator=(Builder const&) = delete;
817
818    /// The root chunk of the RIFF.
819    ListChunkBuilder* _rootChunk = nullptr;
820
821    /// Arena to use for all allocations.
822    MemoryArena _arena;
823};
824
825/// A stateful cursor for a RIFF::Builder.
826///
827/// Represents a kind of pointer to a location in
828/// the hierarchy of RIFF chunks, and allows for
829/// new chunks to be added at that location.
830///
831struct BuildCursor
832{
833public:
834    /// Construct a cursor writing into no chunk.
835    BuildCursor();
836
837    /// Construct a cursor writing into the given `chunk`.
838    BuildCursor(ChunkBuilder* chunk);
839
840    /// Construct a cursor writing at the root of the given `builder`.
841    ///
842    /// Note that this is not the same as constructing a
843    /// cursor for the root chunk of `builder`. Instead, adding
844    /// a chunk via this cursor will add/create the root chunk
845    /// of the entire RIFF hierarchy.
846    ///
847    BuildCursor(Builder& builder);
848
849    /// Get the RIFF being written into, if any.
850    RIFF::Builder* getRIFFBuilder() const { return _riffBuilder; }
851
852    /// Get the current chunk being written into, if any.
853    ChunkBuilder* getCurrentChunk() const { return _currentChunk; }
854
855    /// Set the current chunk to write into.
856    void setCurrentChunk(ChunkBuilder* chunk);
857
858    /// Append a new data chunk to the current list chunk.
859    DataChunkBuilder* addDataChunk(Chunk::Type type);
860
861    /// Append a complete data chunk to the current list chunk.
862    void addDataChunk(Chunk::Type type, void const* data, size_t size);
863
864    /// Append a new data chunk to the current list chunk.
865    ListChunkBuilder* addListChunk(Chunk::Type type);
866
867    /// Begin a new data chunk as a child of the current list chunk.
868    ///
869    /// On return, the cursor will be set to write into the new chunk.
870    ///
871    void beginDataChunk(Chunk::Type type);
872
873    /// Begin a new list chunk as a child of the current list chunk.
874    ///
875    /// On return, the cursor will be set to write into the new chunk.
876    ///
877    void beginListChunk(Chunk::Type type);
878
879    /// End the current chunk.
880    ///
881    /// Sets the cursor to write to the parent of the chunk that was ended.
882    ///
883    void endChunk();
884
885    /// Append data onto the current data chunk.
886    void addData(void const* data, Size size);
887
888    /// Write data onto the current data chunk.
889    template<typename T>
890    void addData(T const& value)
891    {
892        addData(&value, sizeof(value));
893    }
894
895    /// Append existing data to the current data chunk.
896    ///
897    /// The caller takes responsibility for ensuring that
898    /// the passed-in data pointer will remain valid for
899    /// the rest of the lifetime of the enclosing RIFF
900    /// builder.
901    ///
902    void addUnownedData(void const* data, Size size);
903
904    /// Base type for RAII helpers to pair begin/end chunk calls.
905    struct ScopedChunk
906    {
907    protected:
908        ScopedChunk(BuildCursor& cursor)
909            : _cursor(cursor)
910        {
911        }
912
913        ~ScopedChunk() { _cursor.endChunk(); }
914
915    private:
916        BuildCursor& _cursor;
917    };
918
919    struct ScopedDataChunk : ScopedChunk
920    {
921    public:
922        ScopedDataChunk(BuildCursor& cursor, Chunk::Type type)
923            : ScopedChunk(cursor)
924        {
925            cursor.beginDataChunk(type);
926        }
927    };
928
929    struct ScopedListChunk : ScopedChunk
930    {
931    public:
932        ScopedListChunk(BuildCursor& cursor, Chunk::Type type)
933            : ScopedChunk(cursor)
934        {
935            cursor.beginListChunk(type);
936        }
937    };
938
939private:
940    RIFF::Builder* _riffBuilder = nullptr;
941    ChunkBuilder* _currentChunk = nullptr;
942};
943
944#define SLANG_SCOPED_RIFF_BUILDER_DATA_CHUNK(CURSOR, TYPE)    \
945    ::Slang::RIFF::BuildCursor::ScopedDataChunk SLANG_CONCAT( \
946        _scopedRIFFBuilderDataChunk,                          \
947        __LINE__)(CURSOR, TYPE)
948
949#define SLANG_SCOPED_RIFF_BUILDER_LIST_CHUNK(CURSOR, TYPE)    \
950    ::Slang::RIFF::BuildCursor::ScopedListChunk SLANG_CONCAT( \
951        _scopedRIFFBuilderListChunk,                          \
952        __LINE__)(CURSOR, TYPE)
953
954} // namespace RIFF
955
956/// A simple helper for reading from a blob.
957///
958struct MemoryReader
959{
960    //
961    // TODO: This type should eventually either find
962    // a home somewhere that has nothing to do with
963    // RIFF files, or its usage in RIFF-related contexts
964    // should be replaced with other types.
965    //
966
967public:
968    /// Initialize a reader with no bytes remaining.
969    ///
970    MemoryReader() {}
971
972    /// Initialize a reader for the given blob.
973    MemoryReader(void const* data, Size size)
974        : _cursor(static_cast<Byte const*>(data)), _remainingSize(size)
975    {
976    }
977
978    /// Read data into the given buffer.
979    ///
980    /// Fails if `size` is greater than the
981    /// amount of data remaining.
982    ///
983    SlangResult read(void* dst, Size size)
984    {
985        if (size > getRemainingSize())
986        {
987            return SLANG_FAIL;
988        }
989        ::memcpy(dst, _cursor, size);
990        _cursor += size;
991        _remainingSize -= size;
992        return SLANG_OK;
993    }
994
995    /// Read data into the given value.
996    ///
997    /// Fails if `sizeof(dst)` is greater than the
998    /// amount of data remaining.
999    ///
1000    template<typename T>
1001    SlangResult read(T& dst)
1002    {
1003        return read(&dst, sizeof(dst));
1004    }
1005
1006    /// Skip over the given number of bytes.
1007    ///
1008    /// Fails if `size` is greater than the
1009    /// amount of data remaining.
1010    ///
1011    SlangResult skip(Size size)
1012    {
1013        if (size > getRemainingSize())
1014        {
1015            return SLANG_FAIL;
1016        }
1017        _cursor += size;
1018        _remainingSize -= size;
1019        return SLANG_OK;
1020    }
1021
1022    /// Get a pointer to the data that remains to be read.
1023    Byte const* getRemainingData() const { return _cursor; }
1024
1025    /// Get the size of the data that remains to be read.
1026    Size getRemainingSize() const { return _remainingSize; }
1027
1028private:
1029    Byte const* _cursor = nullptr;
1030    Size _remainingSize = 0;
1031};
1032
1033} // namespace Slang
1034
1035#endif