yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
ec7ab914f
master
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. 54using RawValue = UInt32 ; 55 56FourCC () {_rawValue = 0 ; } 57 58FourCC (RawValue rawValue ) {_rawValue = rawValue ; } 59 60void operator = (RawValue rawValue ) {_rawValue = rawValue ; } 61 62RawValue getRawValue ()const {return _rawValue ; } 63 64operator 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// 72union 73 { 74char _text [4 ]; 75RawValue _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. 130static 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// 138struct 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/// 154FourCC tag; 155 156/// Size in bytes of this chunk, not including this header. 157UInt32 size; 158}; 159 160/// Get the header for this chunk. 161Header const * getHeader() const { return ( Header const * ) this ; } 162 163/// Get the tag from the header of this chunk. 164FourCC getTag () const { return _readTagFromHeader (); } 165 166/// Get the total size of this chunk, in bytes. 167/// 168/// This size includes the chunk header. 169/// 170UInt32 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. 187enum class Kind 188{ 189Data , 190List , 191Root , 192}; 193 194/// Get the kind of this chunk. 195Kind 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. 207using Type = FourCC ; 208 209/// Get the type of this chunk. 210Type getType () const ; 211 212private : 213Header _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 222FourCC _readTagFromHeader () const 223{ 224auto header = getHeader (); 225FourCC result ; 226memcpy ( & result , & header -> tag, sizeof ( header -> tag)); 227return result ; 228} 229 230UInt32 _readSizeFromHeader () const 231{ 232auto header = getHeader (); 233UInt32 result ; 234memcpy ( & result , & header -> size , sizeof ( header -> size )); 235return 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. 244UInt32 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/// 252void 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/// 260void writePayloadInto ( void * outData , Size size ) const ; 261 262/// Get the payload data of this chunk. 263/// 264template < typename T > 265void writePayloadInto ( T & outValue ) const 266{ 267writePayloadInto ( & outValue , sizeof ( outValue )); 268} 269 270/// Get the payload data of this chunk. 271/// 272template < typename T > 273T readPayloadAs () const 274{ 275T result ; 276writePayloadInto ( result ); 277return result ; 278} 279 280/// Get the type of this chunk. 281Type getType () const 282{ 283// The type of a data chunk is just the tag 284// from the chunk header. 285 286return getTag (); 287} 288 289/// Determine if a chunk is an instance of this kind. 290static bool _isChunkOfThisKind ( Chunk const * chunk ) 291{ 292return 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 304BoundsCheckedChunkPtr() {} 305 306/// Initialize a null pointer 307BoundsCheckedChunkPtr( std :: nullptr_t ) {} 308 309 310/// Initialize a pointer to a chunk, with a size limit. 311BoundsCheckedChunkPtr( Chunk const * chunk , Size sizeLimit ) { _set( chunk , sizeLimit ); } 312 313/// Initialize a pointer to a chunk, with a limit based on its reported size. 314BoundsCheckedChunkPtr ( Chunk const * chunk ) { _set ( chunk ); } 315 316/// Get the underlying chunk pointer. 317Chunk const * get () const { return _ptr ; } 318 319operator Chunk const * () const { return get (); } 320Chunk const * operator -> () const { return get (); } 321 322BoundsCheckedChunkPtr getNextSibling () const ; 323 324private : 325Chunk const * _ptr = nullptr ; 326Size _sizeLimit = 0 ; 327 328void _set ( Chunk const * chunk, Size sizeLimit); 329void _set ( Chunk const * chunk); 330}; 331 332 333template < typename T = Chunk > 334struct ChunkList 335{ 336public : 337ChunkList () {} 338 339ChunkList( BoundsCheckedChunkPtr firstChunk ) 340: _firstChunk ( firstChunk ) 341{ 342} 343 344struct Iterator 345{ 346public : 347Iterator () {} 348 349Iterator ( BoundsCheckedChunkPtr chunk) 350: _chunk (chunk) 351{ 352} 353 354T const * operator * () const { return static_cast < T const *> (_chunk. get ()); } 355 356void operator ++ () { _chunk = _chunk. getNextSibling (); } 357 358bool operator != ( Iterator const & that ) const { return _chunk != that. _chunk ; } 359 360private: 361BoundsCheckedChunkPtr _chunk; 362}; 363 364Iterator begin () const { return Iterator (_firstChunk); } 365Iterator end () const { return Iterator (); } 366 367template < typename U > 368ChunkList < U > cast () const 369{ 370return ChunkList < U > (_firstChunk); 371} 372 373T const * getFirst () const { return * begin (); } 374 375private: 376friend struct ListChunk ; 377 378BoundsCheckedChunkPtr _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 389static 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 398struct 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. 408Chunk :: Header chunkHeader ; 409 410/// The type of this list chunk. 411Type type ; 412}; 413 414/// Get the header for this list chunk. 415Header 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. 427using ChildList = ChunkList <> ; 428 429/// Get the list of children of this chunk. 430ChildList 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/// 437BoundsCheckedChunkPtr getFirstChild () const ; 438 439/// Find a child data chunk of the given `type`. 440DataChunk const * findDataChunk ( Chunk ::Type type) const; 441 442/// Find a child list chunk of the given `type`. 443ListChunk 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/// 449ListChunk const * findListChunkRec ( Chunk ::Type type) const; 450 451/// Get the type of this chunk. 452Type getType () const { return _readTypeFromHeader (); } 453 454/// Determine if a chunk is an instance of this kind. 455static bool _isChunkOfThisKind ( Chunk const * chunk) 456{ 457// Anything that isn't a data chunk is a list. 458return 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 469Type _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 477Type _readTypeFromHeader () const 478{ 479auto header = getHeader (); 480Type result; 481memcpy( & result , & header -> type, sizeof (header -> type)); 482return result; 483} 484}; 485 486struct RootChunk : ListChunk 487{ 488public : 489// 490// A root chunk has a tag of `"RIFF"` in its header. 491// 492 493static 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/// 500static 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/// 507static RootChunk const * getFromBlob ( ISlangBlob * blob); 508 509/// Determine if a chunk is an instance of this kind. 510static bool _isChunkOfThisKind ( Chunk const * chunk) 511{ 512return chunk -> getKind () == Chunk::Kind::Root; 513} 514 515private : 516static bool _isTagForThisKind ( FourCC tag) { return tag == kTag; } 517}; 518 519inline Chunk ::Kind Chunk :: getKind () const 520{ 521switch ( getTag ()) 522{ 523case RootChunk:: kTag : 524return Chunk::Kind::Root; 525case ListChunk:: kTag : 526return Chunk::Kind::List; 527default : 528return Chunk::Kind::Data; 529} 530} 531 532inline Chunk ::Type Chunk :: getType () const 533{ 534auto tag = getTag (); 535switch (tag) 536{ 537case RootChunk:: kTag : 538case ListChunk:: kTag : 539return static_cast < ListChunk const *> (this) -> getType (); 540 541default : 542return tag; 543} 544} 545 546/// Cast a `Chunk` to a sub-type of `Chunk`. 547template < typename T > 548T * as (Chunk * chunk) 549{ 550if (!chunk) 551return nullptr ; 552if (! T :: _isChunkOfThisKind (chunk)) 553return nullptr ; 554return 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{ 561if (!chunk) 562return nullptr ; 563if (! T :: _isChunkOfThisKind (chunk)) 564return nullptr ; 565return 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. 573Chunk :: Kind getKind () const { return _kind; } 574 575/// Get the type of the chunk being built. 576Chunk :: Type getType () const { return _type; } 577 578/// Set the type of the chunk being built. 579void setType ( Chunk ::Type type) { _type = type; } 580 581/// Get the parent chunk of this chunk in the RIFF hierarchy. 582/// 583ListChunkBuilder * getParent () const { return _parent; } 584 585/// Get the RIFF builder that this chunk belongs to. 586/// 587RIFF :: Builder * getRIFFBuilder () const { return _riffBuilder; } 588 589protected : 590ChunkBuilder ( 591Chunk :: Kind kind, 592Chunk:: Type type, 593ListChunkBuilder * parent, 594RIFF :: Builder * riffBuilder) 595: _kind (kind), _type (type), _parent (parent), _riffBuilder (riffBuilder) 596{ 597} 598 599ChunkBuilder( ChunkBuilder const & ) = delete; 600void operator = ( ChunkBuilder const & ) = delete; 601 602MemoryArena & _getMemoryArena () const; 603 604private : 605Chunk :: Kind _kind = Chunk:: Kind ( -1 ); 606Chunk :: Type _type = 0 ; 607ListChunkBuilder * _parent = nullptr ; 608Builder * _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// 616mutable Size _cachedTotalSize = 0 ; 617 618Size _updateCachedTotalSize () const ; 619 620Size _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/// 627Result _writeTo ( Stream * stream) const; 628 629friend struct Builder; 630}; 631 632class ListChunkBuilder : public ChunkBuilder 633{ 634public : 635/// A list of child chunks. 636using ChildList = InternallyLinkedList < ChunkBuilder > ; 637 638/// Get the child chunks of this list. 639ChildList getChildren () const { return _children; } 640 641/// Append a new data chunk to the current list chunk. 642DataChunkBuilder * addDataChunk (Chunk::Type type); 643 644/// Append a new data chunk to the current list chunk. 645ListChunkBuilder * addListChunk (Chunk::Type type); 646 647/// Determine if a chunk is an instance of this kind. 648static bool _isChunkOfThisKind ( ChunkBuilder const * chunk) 649{ 650return chunk -> getKind () != Chunk::Kind::Data; 651} 652 653private : 654ListChunkBuilder ( Chunk :: Type type, ListChunkBuilder * parent) 655: ChunkBuilder ( Chunk :: Kind ::List, type, parent, parent -> getRIFFBuilder ()) 656{ 657} 658 659friend struct RIFF ::Builder; 660 661ListChunkBuilder ( Chunk :: Type type, RIFF :: Builder * riffBuilder) 662: ChunkBuilder ( Chunk :: Kind ::Root, type, nullptr , riffBuilder) 663{ 664} 665 666ChildList _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. 675void addData ( void const * data, Size size); 676 677/// Append data to this chunk. 678template < typename T > 679void addData( T const & value) 680{ 681addData ( & 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/// 691void 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` 706class Shard : public InternallyLinkedList < Shard > ::Node 707{ 708public : 709/// Get the payload of this shard. 710void const * getPayload () const { return _payload; } 711 712/// Get the size of the payload of this shard. 713Size getPayloadSize () const { return _payloadSize; } 714 715private : 716friend class DataChunkBuilder; 717 718Shard () {} 719 720void setPayload ( void const * data, Size size) 721{ 722_payload = data; 723_payloadSize = size; 724} 725 726void const * _payload = nullptr ; 727Size _payloadSize = 0 ; 728}; 729 730/// List of shards in a data chunk. 731using ShardList = InternallyLinkedList < Shard > ; 732 733/// Get the list of shards that make up this chunk. 734ShardList getShards () const { return _shards; } 735 736/// Determine if a chunk is an instance of this kind. 737static bool _isChunkOfThisKind ( ChunkBuilder const * chunk) 738{ 739return chunk -> getKind () == Chunk::Kind::Data; 740} 741 742private : 743friend class ListChunkBuilder; 744 745DataChunkBuilder ( Chunk :: Type type, ListChunkBuilder * parent) 746: ChunkBuilder ( Chunk :: Kind ::Data, type, parent, parent -> getRIFFBuilder ()) 747{ 748} 749 750Shard * _addShard (); 751 752ShardList _shards; 753}; 754 755template < typename T > 756T * as (ChunkBuilder * chunk) 757{ 758if (!chunk) 759return nullptr ; 760if (! T :: _isChunkOfThisKind (chunk)) 761return nullptr ; 762return static_cast < T *> (chunk); 763} 764 765template < typename T > 766T const * as (ChunkBuilder const * chunk) 767{ 768if (!chunk) 769return nullptr ; 770if (! T :: _isChunkOfThisKind (chunk)) 771return nullptr ; 772return 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. 781Builder (); 782 783/// Write the built hierarchy out to the given `stream`. 784Result writeTo ( Stream * stream); 785 786/// Write the built hierarchy out as a blob. 787Result 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/// 793ListChunkBuilder * 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/// 801ListChunkBuilder * 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/// 812MemoryArena & _getMemoryArena () { return _arena; } 813 814private : 815Builder( Builder const & ) = delete; 816void operator = ( Builder const & ) = delete; 817 818/// The root chunk of the RIFF. 819ListChunkBuilder * _rootChunk = nullptr ; 820 821/// Arena to use for all allocations. 822MemoryArena _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. 835BuildCursor (); 836 837/// Construct a cursor writing into the given `chunk`. 838BuildCursor( 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/// 847BuildCursor( Builder & builder); 848 849/// Get the RIFF being written into, if any. 850RIFF :: Builder * getRIFFBuilder () const { return _riffBuilder; } 851 852/// Get the current chunk being written into, if any. 853ChunkBuilder * getCurrentChunk () const { return _currentChunk; } 854 855/// Set the current chunk to write into. 856void setCurrentChunk ( ChunkBuilder * chunk); 857 858/// Append a new data chunk to the current list chunk. 859DataChunkBuilder * addDataChunk (Chunk::Type type); 860 861/// Append a complete data chunk to the current list chunk. 862void addDataChunk ( Chunk ::Type type, void const * data, size_t size); 863 864/// Append a new data chunk to the current list chunk. 865ListChunkBuilder * 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/// 871void 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/// 877void 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/// 883void endChunk (); 884 885/// Append data onto the current data chunk. 886void addData ( void const * data, Size size); 887 888/// Write data onto the current data chunk. 889template < typename T > 890void addData( T const & value) 891{ 892addData ( & 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/// 902void addUnownedData ( void const * data, Size size); 903 904/// Base type for RAII helpers to pair begin/end chunk calls. 905struct ScopedChunk 906{ 907protected : 908ScopedChunk ( BuildCursor & cursor ) 909: _cursor ( cursor ) 910{ 911} 912 913~ ScopedChunk () { _cursor . endChunk (); } 914 915private : 916BuildCursor & _cursor ; 917}; 918 919struct ScopedDataChunk : ScopedChunk 920{ 921public : 922ScopedDataChunk ( BuildCursor & cursor , Chunk :: Type type ) 923: ScopedChunk ( cursor ) 924{ 925cursor . beginDataChunk ( type ); 926} 927}; 928 929struct ScopedListChunk : ScopedChunk 930{ 931public : 932ScopedListChunk ( BuildCursor & cursor , Chunk :: Type type ) 933: ScopedChunk ( cursor ) 934{ 935cursor . beginListChunk ( type ); 936} 937}; 938 939private : 940RIFF :: Builder * _riffBuilder = nullptr ; 941ChunkBuilder * _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/// 970MemoryReader() {} 971 972/// Initialize a reader for the given blob. 973MemoryReader( 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/// 983SlangResult read( void * dst , Size size ) 984{ 985if ( size > getRemainingSize ()) 986{ 987return SLANG_FAIL; 988} 989:: memcpy ( dst , _cursor , size ); 990_cursor += size ; 991_remainingSize -= size ; 992return 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/// 1000template < typename T > 1001SlangResult read ( T & dst ) 1002{ 1003return 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/// 1011SlangResult skip ( Size size ) 1012{ 1013if ( size > getRemainingSize ()) 1014{ 1015return SLANG_FAIL ; 1016} 1017_cursor += size ; 1018_remainingSize -= size ; 1019return SLANG_OK ; 1020} 1021 1022/// Get a pointer to the data that remains to be read. 1023Byte const * getRemainingData () const { return _cursor ; } 1024 1025/// Get the size of the data that remains to be read. 1026Size getRemainingSize () const { return _remainingSize ; } 1027 1028private : 1029Byte const * _cursor = nullptr ; 1030Size _remainingSize = 0 ; 1031}; 1032 1033} // namespace Slang 1034 1035#endif