yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
4c76b2759
master
1// slang-riff.cpp 2#include "slang-riff.h" 3 4#include "slang-blob.h" 5#include "slang-com-helper.h" 6 7namespace Slang 8{ 9namespace RIFF 10{ 11 12Size _roundUpToChunkAlignment (Size size ) 13{ 14auto alignmentMask = Size (Chunk ::kChunkAlignment )- 1 ; 15return (size + alignmentMask )& ~alignmentMask ; 16} 17 18// 19// RIFF::Chunk 20// 21 22// 23// RIFF::DataChunk 24// 25 26void DataChunk ::writePayloadInto (void * outData ,Size size )const 27{ 28SLANG_ASSERT (size <=getPayloadSize ()); 29 ::memcpy (outData ,getPayload (),size ); 30} 31 32// 33// RIFF::BoundsCheckedChunkPtr 34// 35 36void BoundsCheckedChunkPtr ::_set (Chunk const * chunk ,Size sizeLimit ) 37{ 38// We start by clearing out the state of this 39// pointer, so that we can early-out if any 40// validation checks fail, and be sure we 41// have a null pointer. 42// 43_ptr = nullptr ; 44_sizeLimit = 0 ; 45 46// If there's nothing to point to, then the pointer 47// should be null anyway. 48// 49if (!chunk || !sizeLimit ) 50 { 51return ; 52 } 53 54// Because this type can be used to traverse RIFF 55// chunks that were loaded into memory from in-theory 56// untrusted sources, we try to provide some validation 57// checks to make sure that access to the chunk will 58// be safe (or as safe as we can easily ensure). 59 60// If the available size isn't even enough for the 61// header of a RIFF chunk, then something is wrong. 62// 63if (sizeLimit < sizeof (Chunk ::Header )) 64 { 65SLANG_UNEXPECTED ("invalid RIFF" ); 66return ; 67 } 68 69// Once we've checked that there is enough space 70// for a valid RIFF header, we can read the 71// size that the `chunk` reports itself as having. 72// 73auto reportedSize = chunk -> getTotalSize (); 74 75// If the reported size is too small, then something 76// is wrong. 77// 78if (reportedSize < sizeof (Chunk ::Header )) 79 { 80SLANG_UNEXPECTED ("invalid RIFF" ); 81return ; 82 } 83 84// If the reported size is bigger than the size limit, 85// then it must be invalid (it is reporting itself as 86// bigger than the region of memory that is supposed 87// to contain it). 88// 89if (reportedSize > sizeLimit ) 90 { 91SLANG_UNEXPECTED ("invalid RIFF" ); 92return ; 93 } 94 95// If the chunk claims to be a list chunk, then it 96// must be big enough to hold the larger header 97// that list chunks use. 98// 99if (as < ListChunk > (chunk )) 100 { 101if (reportedSize < sizeof (ListChunk ::Header )) 102 { 103SLANG_UNEXPECTED ("invalid RIFF" ); 104return ; 105 } 106 } 107 108// At this point we've performed some basic validation 109// telling us that the chunk header appears plausible. 110// This does not mean that we've fully validated the 111// hierarchy of child chunks under it (in the case of 112// a list chunk), but that validation can be performed 113// on-demand while descending the hierarchy. 114 115_ptr = chunk ; 116_sizeLimit = sizeLimit ; 117} 118 119void BoundsCheckedChunkPtr ::_set (Chunk const * chunk ) 120{ 121// In the case where we are being set to point to a 122// single chunk, we have to assume that whatever 123// code derived the `chunk` pointer has validated 124// that it is safe to access its header. 125// 126// We will simply set up a pointer that can reference 127// the `chunk` itself, as well as any of its children 128// (if it has any), but that cannot be used to access 129// further sibling chunks under the same parent. 130// 131_set (chunk ,chunk -> getTotalSize ()); 132} 133 134BoundsCheckedChunkPtr BoundsCheckedChunkPtr ::getNextSibling ()const 135{ 136SLANG_ASSERT (_ptr != nullptr ); 137if (!_ptr ) 138return nullptr ; 139 140// The RIFF chunk reports its own size, and when navigating 141// the children of a list chunk, each child chunk starts 142// at the next (aligned) offset after the previous one. 143// 144auto chunkSize = _ptr -> getTotalSize (); 145 146// As a simple validation check, we check for a chunk that 147// reports its size as something bigger than the available 148// size; that would represent an invalid input. 149// 150if (chunkSize > _sizeLimit ) 151 { 152SLANG_UNEXPECTED ("invalid RIFF chunk size" ); 153UNREACHABLE_RETURN (nullptr ); 154 } 155 156// The next chunk (if there is one) would start at the 157// next offset after this chunk, rounded up to the minimum 158// alignment for a chunk. Thus, we round up the reported 159// size of this chunk to compute the offset to the next 160// chunk. 161// 162auto offsetToNextChunk = _roundUpToChunkAlignment (chunkSize ); 163 164// If stepping forward by the given number of bytes would 165// cause us to exceed our size limit, then we have reached 166// the end of the list of sibling chunks, and should 167// return a null pointer. 168// 169if (offsetToNextChunk >=_sizeLimit ) 170return nullptr ; 171 172auto nextChunk = (RIFF ::Chunk const * )(offsetToNextChunk + (Byte const * )_ptr ); 173auto nextSizeLimit = _sizeLimit - offsetToNextChunk ; 174 175return BoundsCheckedChunkPtr (nextChunk ,nextSizeLimit ); 176} 177 178 179// 180// RIFF::ListChunk 181// 182 183BoundsCheckedChunkPtr ListChunk ::getFirstChild ()const 184{ 185// Because this type could be used to navigate 186// an untrusted RIFF that has been loaded into memory, 187// we make some efforts to validate that things 188// seem okay as we navigate it. 189 190// The first child of a list chunk (if it has any) 191// comes right after the list header. 192// 193Size firstChildOffset = sizeof (ListChunk ::Header ); 194 195// The size that the parent chunk reports should 196// be appropriate to store the header. 197// 198// Note that in order to compute the reported size 199// we are *accessing* the header, so if there really 200// are too few bytes available, it is up to whatever 201// code computed this `ListChunk*` to have done 202// their own validation checks. 203// 204Size reportedParentSize = getTotalSize (); 205if (reportedParentSize < firstChildOffset ) 206 { 207SLANG_UNEXPECTED ("invalid RIFF" ); 208UNREACHABLE_RETURN (nullptr ); 209 } 210 211// The total size that the childen of this chunk can 212// consume is all of the reported size of the parent, 213// after the `ListChunk::Header`. 214// 215Size availableSizeForChildren = reportedParentSize - firstChildOffset ; 216 217// The available size can be zero, in the case where 218// the parent chunk has no children. 219// 220if (availableSizeForChildren == 0 ) 221return nullptr ; 222 223// If the parent chunk has a non-zero size, then it should 224// have at least one child, and the available size had better 225// be big enough to at least hold the *header* of that first 226// child. 227// 228if (availableSizeForChildren < sizeof (Chunk ::Header )) 229 { 230SLANG_UNEXPECTED ("invalid RIFF" ); 231UNREACHABLE_RETURN (nullptr ); 232 } 233 234// At this point we've convinced ourselves that there is 235// conceivably enough space for at least one child chunk, 236// so we will form a `BoundsCheckedChunkPtr` to it, which 237// will trigger further validity checks on that child chunk. 238// 239auto firstChild = (Chunk const * )(firstChildOffset + (Byte const * )this ); 240return BoundsCheckedChunkPtr (firstChild ,availableSizeForChildren ); 241} 242 243DataChunk const * ListChunk ::findDataChunk (Chunk ::Type type )const 244{ 245for (auto chunk :getChildren ()) 246 { 247auto dataChunk = as < DataChunk > (chunk ); 248if (!dataChunk ) 249continue ; 250 251if (dataChunk -> getType ()!= type ) 252continue ; 253 254return dataChunk ; 255 } 256return nullptr ; 257} 258 259ListChunk const * ListChunk ::findListChunk (Chunk ::Type type )const 260{ 261for (auto chunk :getChildren ()) 262 { 263auto listChunk = as < ListChunk > (chunk ); 264if (!listChunk ) 265continue ; 266 267if (listChunk -> getType ()!= type ) 268continue ; 269 270return listChunk ; 271 } 272return nullptr ; 273} 274 275ListChunk const * ListChunk ::findListChunkRec (Chunk ::Type type )const 276{ 277// Note: The search being performed here could 278// be implemented without any need for recursion 279// (or a stack), by taking advantage of the way 280// that RIFF chunks are laid out. If we have some 281// chunk C, then the next aligned offset in memory 282// after C is either at the end of the hierarchy, 283// or it is the next sibling of one of C's ancestors 284// (where C is being counted as its own ancestor). 285// 286// However, it's not really clear if there's enough 287// of a benefit to justify that more subtle implementation. 288 289if (getType ()== type ) 290return this ; 291 292for (auto chunk :getChildren ()) 293 { 294auto listChunk = as < ListChunk > (chunk ); 295if (!listChunk ) 296continue ; 297 298auto found = listChunk -> findListChunkRec (type ); 299if (!found ) 300continue ; 301 302return found ; 303 } 304return nullptr ; 305} 306 307 308// 309// RIFF::RootChunk 310// 311 312RootChunk const * RootChunk ::getFromBlob (void const * data ,size_t dataSize ) 313{ 314// Our goal is to determine whether the given 315// blob superficially looks like a RIFF. 316 317// The data pointer should be non-null if there 318// was any data passed in. 319// 320SLANG_ASSERT (data || !dataSize ); 321 322// If there's no data, then it's obvious not usable. 323// 324if (!data ) 325return nullptr ; 326 327// If there isn't even enough data to store the header 328// for a root, chunk, then the blob is too small. 329// 330if (dataSize < sizeof (RootChunk ::Header )) 331return nullptr ; 332 333// We cast the data pointer to a root chunk here, so that 334// we can access the fields in the header, but we are not 335// yet convinced it is actually a valid RIFF, so we may 336// still return `nullptr`. 337// 338auto rootChunk = reinterpret_cast < RootChunk const *> (data ); 339 340// The root chunk of a valid RIFF should have the `"RIFF"` 341// tag. This acts as a kind of "magic number" to mark the 342// start of a RIFF. 343// 344if (rootChunk -> getTag ()!= RootChunk ::kTag ) 345return nullptr ; 346 347// By reading the size field from the root chunk, we can 348// determine how big of a file the root chunk claims that 349// we have. 350// 351auto reportedSize = rootChunk -> getTotalSize (); 352 353// If the size implied by the RIFF header is larger than the 354// blob, then we do not have a properly structured RIFF, 355// and we would be at risk of reading past the end of the 356// buffer if we attempted to use it. 357// 358if (reportedSize > dataSize ) 359return nullptr ; 360 361// Note: It is possible that the `reportedSize` is strictly 362// *less than* the `dataSize` that was passed in, and there 363// is a policy choice to be made about how to handle that case. 364// 365// We err on the side of leniency here, because the client who 366// is calling this function might intentionally be storing 367// additional data in the same blob, after the RIFF, and could 368// use the RIFF's ability to report its own size as a way to 369// locate that data. 370 371// Note: At this point we could recursively walk the hierarchy 372// of the RIFF and validate that all the contained chunks appear 373// valid in terms of the sizes they report, but doing so would 374// take an amount of time that scales with the size of the RIFF, 375// and our goal here is to be efficient. 376// 377// Access to the data through the `RIFF::Chunk` API will do its 378// best to validate the information in chunks as they are 379// accessed. The code is attempting to be able to catch 380// corrupted or accidentally malformed input, but is not aspiring 381// to anything like proper security. 382 383return rootChunk ; 384} 385 386RootChunk const * RootChunk ::getFromBlob (ISlangBlob * blob ) 387{ 388SLANG_ASSERT (blob ); 389return getFromBlob (blob -> getBufferPointer (),blob -> getBufferSize ()); 390} 391 392// 393// RIFF::ChunkBuilder 394// 395 396Size ChunkBuilder ::_updateCachedTotalSize ()const 397{ 398Size totalSize = 0 ; 399if (auto dataChunk = as < DataChunkBuilder > (this )) 400 { 401// Every chunk starts with a header. 402// 403totalSize += sizeof (DataChunk ::Header ); 404 405// After the header comes the payload of 406// the chunk, which for a data chunk 407// will be the concatenation of all its 408// shards. 409// 410for (auto shard :dataChunk -> getShards ()) 411 { 412totalSize += shard -> getPayloadSize (); 413 } 414 } 415else if (auto listChunk = as < ListChunkBuilder > (this )) 416 { 417// A list chunk starts with a header, just 418// like a data chunk, although its header 419// is larger. 420// 421totalSize += sizeof (ListChunk ::Header ); 422 423// After the header come the child chunks, in order. 424// 425for (auto child :listChunk -> getChildren ()) 426 { 427// We recursively poke the child chunks to 428// update their cached total size, so that 429// we can be sure we are getting correct 430// information. 431// 432auto childSize = child -> _updateCachedTotalSize (); 433 434// We cannot simply add the size of the child 435// chunk directly to `totalSize`, because a 436// RIFF guarantees that every chunk must start 437// on a suitably aligned boundary. Thus, we 438// first round `totalSize` up to the necessary 439// alignment, and then add the child's size. 440// 441totalSize = _roundUpToChunkAlignment (totalSize ); 442totalSize += childSize ; 443 } 444 } 445else 446 { 447SLANG_UNREACHABLE ("RIFF chunk must be data or list" ); 448 } 449 450_cachedTotalSize = totalSize ; 451return totalSize ; 452} 453 454Result ChunkBuilder ::_writeTo (Stream * stream )const 455{ 456// The size information that gets written into 457// the chunk header will be based on the cached 458// size information for this chunk. 459// 460// If nobody has called `_updateCachedTotalSize()` 461// at all, then that is a problem. 462// 463SLANG_ASSERT (_getCachedTotalSize () >=sizeof (Chunk ::Header )); 464 465// The size that gets written into the chunk header 466// is the total size of the chunk, ecluding the chunk 467// header. Note that this size will *include* the 468// additional field of the list chunk header. 469// 470Size totalSizeExcludingChunkHeader = _getCachedTotalSize ()- sizeof (Chunk ::Header ); 471 472// Because the size field in the header is only 32 bits, 473// we want to double-check that it can actually represent 474// the size of the data to be written into it. 475// 476UInt32 sizeToWriteInHeader = UInt32 (totalSizeExcludingChunkHeader ); 477SLANG_ASSERT (Size (sizeToWriteInHeader )== totalSizeExcludingChunkHeader ); 478 479if (auto dataChunk = as < DataChunkBuilder > (this )) 480 { 481// We start by writing the header. 482// 483DataChunk ::Header header ; 484header .size = sizeToWriteInHeader ; 485 486// The tag of a data chunk is its type `FourCC`. 487// 488header .tag = dataChunk -> getType (); 489 490// Once we've filled in the header fields, we 491// can write it to the output stream. 492// 493SLANG_RETURN_ON_FAIL (stream -> write (& header ,sizeof (header ))); 494 495// Now we can simply write the payload bytes, 496// which are the concatenation of the payloads 497// of all the shards. 498// 499for (auto shard :dataChunk -> getShards ()) 500 { 501auto payload = shard -> getPayload (); 502auto payloadSize = shard -> getPayloadSize (); 503SLANG_RETURN_ON_FAIL (stream -> write (payload ,payloadSize )); 504 } 505 } 506else if (auto listChunk = as < ListChunkBuilder > (this )) 507 { 508// We start by writing the header. 509// 510ListChunk ::Header header ; 511header .chunkHeader .size = sizeToWriteInHeader ; 512 513// The tag of a list chunk is either `"RIFF"` 514// (for a root chunk) or `"LIST"` (for any 515// other list chunk). 516// 517header .chunkHeader .tag = 518listChunk -> getKind ()== Chunk ::Kind ::Root ?RootChunk ::kTag :ListChunk ::kTag ; 519 520// The type of a list chunk is stored in the 521// additional header field after the base 522// chunk header. 523// 524header .type = listChunk -> getType (); 525 526// Once we've filled in the header fields, we 527// can write it to the output stream. 528// 529SLANG_RETURN_ON_FAIL (stream -> write (& header ,sizeof (header ))); 530 531// Now we recursively write each of the child chunks, 532// keeping track of the total size so far, so that 533// we can insert padding as needing to bring things 534// up to alignment. 535// 536Size totalSize = sizeof (header ); 537for (auto child :listChunk -> getChildren ()) 538 { 539// We note the total size written so far, 540// as well as the size after rounding up 541// to the required alignment. 542// 543auto unalignedSize = totalSize ; 544auto alignedSize = _roundUpToChunkAlignment (unalignedSize ); 545SLANG_ASSERT (alignedSize >=unalignedSize ); 546 547// If the aligned size is greater than the 548// unaligned size, then we may need to write 549// some padding bytes into the output stream. 550// 551auto paddingSize = alignedSize - unalignedSize ; 552 553// The amount of padding to be inserted must 554// always be less than the minimum chunk alignment. 555// 556SLANG_ASSERT (paddingSize < Chunk ::kChunkAlignment ); 557 558// We'll write padding bytes to get things up 559// to the necessary alignment. 560// 561auto remainingPaddingToWrite = paddingSize ; 562while (remainingPaddingToWrite -- ) 563 { 564static const Byte kPadding [1 ]= {0 }; 565stream -> write (kPadding ,1 ); 566 } 567 568// Now we are at a suitably aligned offset. 569// 570totalSize = alignedSize ; 571 572// With the alignment concern dealt with, 573// we can simply recursively write the 574// child chunk and update the total size. 575// 576SLANG_RETURN_ON_FAIL (child -> _writeTo (stream )); 577totalSize += child -> _getCachedTotalSize (); 578 } 579 580// As a validation check, we expect the total number 581// of bytes we've written here to match the total 582// size that was cached on this chunk (and that was 583// written into the chunk header). 584// 585SLANG_ASSERT (totalSize == _getCachedTotalSize ()); 586 } 587else 588 { 589SLANG_UNREACHABLE ("RIFF chunk must be data or list" ); 590 } 591return SLANG_OK ; 592} 593 594MemoryArena & ChunkBuilder ::_getMemoryArena ()const 595{ 596return getRIFFBuilder ()-> _getMemoryArena (); 597} 598 599// 600// RIFF::ListChunkBuilder 601// 602 603DataChunkBuilder * ListChunkBuilder ::addDataChunk (Chunk ::Type type ) 604{ 605auto chunk = new (_getMemoryArena ())DataChunkBuilder (type ,this ); 606_children .add (chunk ); 607return chunk ; 608} 609 610ListChunkBuilder * ListChunkBuilder ::addListChunk (Chunk ::Type type ) 611{ 612auto chunk = new (_getMemoryArena ())ListChunkBuilder (type ,this ); 613_children .add (chunk ); 614return chunk ; 615} 616 617// 618// RIFF::DataChunkBuilder 619// 620 621void DataChunkBuilder ::addData (void const * data ,Size size ) 622{ 623// Adding no data should be a no-op. 624// 625if (size == 0 ) 626return ; 627 628// The most interesting implementation detail here 629// is that we will try to detect cases where we 630// can re-use an existing `Shard` by adding the data 631// to the end of that shard's allocation. 632// 633// This is only possible because of the way that 634// we are using a single `MemoryArena` to allocate 635// everything, which makes it possible that the 636// next address the arena would return for an allocation 637// of `size` bytes is the same as the ending address 638// of the payload for the last shard of this chunk. 639// 640auto & arena = _getMemoryArena (); 641 642// We start by checking if this chunk already has 643// a last shard that we could consider appending to. 644// 645auto lastShard = _shards .getLast (); 646if (lastShard ) 647 { 648// If there is a last shard, then we can compute 649// the end address of its payload, and see if 650// it is the same as the cursor of the arena 651// we are allocating from. 652// 653auto payload = lastShard -> getPayload (); 654auto payloadSize = lastShard -> getPayloadSize (); 655auto payloadEnd = (Byte * )payload + payloadSize ; 656if (payloadEnd == arena .getCursor ()) 657 { 658// Now that we've confirmed that the shard's 659// payload ends at an address the arena could 660// conceivably allocate from, we need to ask 661// the arena to allocate `size` bytes from 662// the current block it is using, and see if 663// doing so succeeds. 664// 665if (arena .allocateCurrentUnaligned (size )) 666 { 667// At this point, we've confirmed that we 668// are in our special case, and the relevant 669// bytes have been allocated from the arena. 670// 671// Now we can simply write the new data at 672// what used to be the end address for the 673// shard's payload, and adjust its state 674// to account for the new allocation. 675// 676 ::memcpy (payloadEnd ,data ,size ); 677lastShard -> setPayload (payload ,payloadSize + size ); 678return ; 679 } 680 } 681 } 682 683// If we didn't land in our special case, we 684// will simply allocate a new shard to hold 685// the data. 686// 687// Note that the order of allocation here is 688// intentional, and supports the optimized special 689// case that we checked for above. We make the 690// allocation for the payload *last*, so that 691// it is possible that the arena's next allocation 692// could come right after the payload allocation 693// in memory. 694// 695// If we allocated the payload first and the 696// `Shard` second, then there would already be 697// another allocation after the payload, and 698// the optimized case would never trigger. 699// 700auto shard = _addShard (); 701auto payload = arena .allocateUnaligned (size ); 702 ::memcpy (payload ,data ,size ); 703shard -> setPayload (payload ,size ); 704} 705 706void DataChunkBuilder ::addUnownedData (void const * data ,size_t size ) 707{ 708// Unowned data will always have to be added as its own shard. 709// 710auto shard = _addShard (); 711shard -> setPayload (data ,size ); 712} 713 714DataChunkBuilder ::Shard * DataChunkBuilder ::_addShard () 715{ 716auto shard = new (_getMemoryArena ())Shard (); 717_shards .add (shard ); 718return shard ; 719} 720 721// 722// RIFF::Builder 723// 724 725Builder ::Builder () 726 :_arena (4096 ) 727{ 728} 729 730Result Builder ::writeTo (Stream * stream ) 731{ 732// If there's no root chunk, then this isn't 733// a well-formed RIFF. 734// 735if (!_rootChunk ) 736return SLANG_FAIL ; 737 738// The `ChunkBuilder::_writeTo()` method requires size 739// information for each of the chunks in the hierarchy. 740// Rather than try to keep size information up-to-date 741// during the building process, we simply compute it 742// all at once, right before writing the output. 743// 744_rootChunk -> _updateCachedTotalSize (); 745_rootChunk -> _writeTo (stream ); 746return SLANG_OK ; 747} 748 749Result Builder ::writeToBlob (ISlangBlob ** outBlob ) 750{ 751OwnedMemoryStream stream (FileAccess ::Write ); 752SLANG_RETURN_ON_FAIL (writeTo (& stream )); 753 754List < uint8_t > data ; 755stream .swapContents (data ); 756 757* outBlob = ListBlob ::moveCreate (data ).detach (); 758return SLANG_OK ; 759} 760 761ListChunkBuilder * Builder ::addRootChunk (Chunk ::Type type ) 762{ 763// There must not already be a root chunk set. 764SLANG_ASSERT (getRootChunk ()== nullptr ); 765 766auto chunk = new (_getMemoryArena ())ListChunkBuilder (type ,this ); 767_rootChunk = chunk ; 768return chunk ; 769} 770 771 772// 773// RIFF::BuildCursor 774// 775 776BuildCursor ::BuildCursor () {} 777 778BuildCursor ::BuildCursor (Builder & builder ) 779 :_riffBuilder (& builder ) 780{ 781} 782 783BuildCursor ::BuildCursor (ChunkBuilder * chunk ) 784{ 785setCurrentChunk (chunk ); 786} 787 788void BuildCursor ::setCurrentChunk (ChunkBuilder * chunk ) 789{ 790_currentChunk = chunk ; 791_riffBuilder = chunk ?chunk -> getRIFFBuilder () :nullptr ; 792} 793 794DataChunkBuilder * BuildCursor ::addDataChunk (Chunk ::Type type ) 795{ 796// The current chunk must be a list chunk, so that 797// we can add children to it. 798// 799auto parentChunk = as < ListChunkBuilder > (getCurrentChunk ()); 800SLANG_ASSERT (parentChunk ); 801 802return parentChunk -> addDataChunk (type ); 803} 804 805void BuildCursor ::addDataChunk (Chunk ::Type type ,void const * data ,size_t size ) 806{ 807beginDataChunk (type ); 808addData (data ,size ); 809endChunk (); 810} 811 812ListChunkBuilder * BuildCursor ::addListChunk (Chunk ::Type type ) 813{ 814// If there is no current chunk being written into, 815// then an attempt to add a new chunk should set 816// the root chunk of the entire RIFF. 817// 818auto currentChunk = getCurrentChunk (); 819if (!currentChunk ) 820 { 821SLANG_ASSERT (getRIFFBuilder ()); 822return _riffBuilder -> addRootChunk (type ); 823 } 824 825// Otherwise, the current chunk must be a list 826// chunk, and we add a new child to it. 827// 828auto parentChunk = as < ListChunkBuilder > (currentChunk ); 829SLANG_ASSERT (parentChunk ); 830 831return parentChunk -> addListChunk (type ); 832} 833 834void BuildCursor ::beginDataChunk (Chunk ::Type type ) 835{ 836auto chunk = addDataChunk (type ); 837setCurrentChunk (chunk ); 838} 839 840void BuildCursor ::beginListChunk (Chunk ::Type type ) 841{ 842auto chunk = addListChunk (type ); 843setCurrentChunk (chunk ); 844} 845 846void BuildCursor ::endChunk () 847{ 848SLANG_ASSERT (getCurrentChunk ()!= nullptr ); 849 850auto chunk = getCurrentChunk (); 851setCurrentChunk (chunk -> getParent ()); 852} 853 854void BuildCursor ::addData (void const * data ,Size size ) 855{ 856// The current chunk must be a data chunk, so that 857// we can add data to it. 858// 859auto dataChunk = as < DataChunkBuilder > (getCurrentChunk ()); 860SLANG_ASSERT (dataChunk ); 861 862dataChunk -> addData (data ,size ); 863} 864 865void BuildCursor ::addUnownedData (void const * data ,Size size ) 866{ 867// The current chunk must be a data chunk, so that 868// we can add data to it. 869// 870auto dataChunk = as < DataChunkBuilder > (getCurrentChunk ()); 871SLANG_ASSERT (dataChunk ); 872 873dataChunk -> addUnownedData (data ,size ); 874} 875 876}// namespace RIFF 877 878}// namespace Slang