yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
ec7ab914f
master
1// slang-blob-builder.h 2#ifndef SLANG_BLOB_BUILDER_H 3#define SLANG_BLOB_BUILDER_H 4 5// This file provides utilities for building "blobs" of data 6// where, for purposes, a blob is a contiguous sequence of 7// bytes where the interpretation *of* those bytes depends 8// only on the bytes themselves, and not other factors like 9// the in-memory address of the blob, or the address/contents 10// of memory not in the blob. 11// 12// Superficially, the task seems simple: just maintain a 13// dynamically-sized array of bytes and append to it until 14// you're done. If that's what you need, you're probably 15// better off just using an `OwnedMemoryStream`. 16// 17// The utilities in this file deal with the case where you 18// want to build some kind of offset-based data structure, 19// so that parts of the blob will store byte offsets to 20// other parts, while also being able to build parts of 21// that structure "out of order", so that the final offset 22// of a particular piece of data in the blob may not be 23// known until everything *before* it has been fully built. 24 25#include "slang-basic.h" 26#include "slang-internally-linked-list.h" 27#include "slang-io.h" 28#include "slang-memory-arena.h" 29 30namespace Slang 31{ 32 33inline constexprbool isPowerOfTwo (Size value ) 34{ 35return value > 0 && (value - 1 & value )== 0 ; 36} 37 38inline constexprSize roundUpToAlignment (Size size ,Size alignment ) 39{ 40SLANG_ASSERT (isPowerOfTwo (alignment )); 41 42 autoalignmentMask = Size (alignment) - 1 ; 43return (size + alignmentMask) & ~alignmentMask; 44} 45 46class ShardBuilder; 47class ChunkBuilder; 48struct BlobBuilder ; 49 50 51/// A utility type for composing a binary blob. 52/// 53/// A blob builder allows a blob to be composed as a sequence of discrete 54/// chunks, allowing chunks to be added and written to in any order. 55/// 56/// Chunks can contain (relative) pointers to one another, with the correct 57/// relative offsets being computed as part of writing the entire blob out. 58/// 59struct BlobBuilder 60{ 61public : 62/// Construct an empty blob builder. 63BlobBuilder (); 64 65/// Write the contents of the blob to the given `stream`. 66void writeTo ( Stream * stream); 67 68/// Create a copy of the contents of the blob and assign to `outBlob`. 69void writeToBlob ( ISlangBlob ** outBlob); 70 71/// Add a new empty chunk to the end of the blob. 72ChunkBuilder * addChunk (); 73 74/// Add a new empty chunk after the given `chunk`. 75ChunkBuilder * addChunkAfter ( ChunkBuilder * chunk); 76 77/// Create a chunk that is not initially part of the blob. 78/// 79/// The contents of the returned chunk will only become 80/// part of the full blob if `addChunk()` is called later, 81/// *or* if the contents of the new chunk are moved into 82/// another chunk that gets added. 83/// 84ChunkBuilder * createUnparentedChunk (); 85 86/// Add a chunk to the blob that was initially not part of the blob. 87void addChunk ( ChunkBuilder * chunk); 88 89private : 90InternallyLinkedList < ChunkBuilder > _chunks ; 91MemoryArena _arena; 92 93friend class ChunkBuilder; 94friend class ShardBuilder; 95MemoryArena & _getArena() { return _arena; } 96 97Size _calcSizeAndSetCachedChunkOffsets (); 98Size _writeChunksTo ( Stream * stream ); 99}; 100 101/// A chunk is a logically contiguous unit, such that data can 102/// only ever be appended to it. As a result, offsets that are 103/// relative to the start of a chunk are meaningful, and can be 104/// used to encode relative offsets for pointers. 105/// 106/// Every `ChunkBuilder` is owned by its parent `BlobBuilder`. 107/// A pointer to a `ChunkBuilder` will only be valid during the 108/// lifetime of the parent `BlobBuilder`. 109/// 110/// Conceptually, a `ChunkBuilder` has the following: 111/// 112/// * A minimum *alignment* in bytes (initially 1) 113/// 114/// * Zero or more bytes of *content* data (initially empty) 115/// 116/// * An optional *prefix* consisting of zero or more bytes (initially absent) 117/// 118/// The content may be a mix of raw data (e.g., added via `writeData()`), 119/// relative pointers to other chunks (`writeRelativePtr()`) and padding 120/// bytes (`writePaddingToAlignTo()`). 121/// 122/// The prefix may only be either a single relative pointer, or a 123/// single range of raw data bytes. 124/// 125/// When the parent blob written out as a flat buffer of bytes, the 126/// following are guaranteed: 127/// 128/// * The content of the chunk will be a contiguous range of bytes 129/// starting at some offset, and will not overlap any other chunks. 130/// 131/// * The byte offset where the chunk contents start will be a multiple 132/// of the chunk's minimum alignment. 133/// 134/// * The bytes of the prefix (if any) will immediately precede the 135/// bytes of the content. 136/// 137class ChunkBuilder : public InternallyLinkedList < ChunkBuilder > :: Node 138{ 139public : 140/// Get the blob builder that this chunk belongs to. 141BlobBuilder * getParentBlob () const { return _parentBlob ; } 142 143/// Get the required alignment of this chunk. 144/// 145/// The minimum alignment for a chunk starts at 1, 146/// and may be increased by operations such as 147/// `setAlignmentToAtLeast()` and `writePaddingToAlinTo()`. 148/// 149Size getAlignment () const ; 150 151/// Potentially increases the required alignment of this chunk. 152/// 153/// If the alignment of the chunk is less than `alignment`, 154/// then it will be increased to `alignment`. 155/// 156/// The `alignment` passed in must be a power of two. 157/// 158void setAlignmentToAtLeast ( Size alignment ); 159 160/// Get the size in bytes of the content of this chunk. 161/// 162/// Note that the size is not necessarily a multiple of the 163/// alignment of the chunk. 164/// 165Size getContentSize () const ; 166 167/// Write data into the chunk. 168/// 169/// The chunk will retain a copy of the data passed in, 170/// so the `data` pointer only needs to be valid for 171/// the duration of the call. 172/// 173/// Note that this operation does *not* adjust the 174/// alignment of the chunk in any way. 175/// 176/// The data must only contain types that can be copied 177/// bit-for-bit, and that do not depend on addresses 178/// in meory. In particular no pointers (absolute or 179/// relative) should be written. 180/// 181void writeData ( void const * data , Size size ); 182 183/// Append padding bytes to this chunk until its content size 184/// is a multiple of `alignment`. 185/// 186/// May also increase the alignment of the chunk, as if 187/// calling `setAlignmentToAtLeast(alignment)`. 188/// 189/// The padding bytes will all be zero. 190/// 191void writePaddingToAlignTo ( Size alignment ); 192 193/// Write a relative pointer to the given `targetChunk`. 194/// 195/// The type parameter `T` is used to determine the size 196/// of the relative pointer (should be either 4 or 8 bytes). 197/// 198/// The bytes that eventually get written will contain 199/// the computed offset of `targetChunk` minus the computed 200/// offset of the first byte of the relative pointer itself. 201/// 202/// Acts as is `writePaddingToAlignTo(sizeof(T))` were 203/// called immediately before. 204/// 205template < typename T > 206void writeRelativePtr ( ChunkBuilder * targetChunk ) 207{ 208_writeRelativePtr ( targetChunk , sizeof ( T )); 209} 210 211/// Append the contents of another chunk to this one. 212/// 213/// This *moves* all of the contents of `chunk` into `this`. 214/// After the operation completes `chunk` will be an empty 215/// chunk with one-byte alignment. 216/// 217/// This operation is useful when accumulating data that 218/// needs to be appended to the chunk, but where the correct 219/// alignment for that data is not yet known; a use can 220/// effectively create a temporary "sub-chunk" and then append 221/// it to the main chunk once its correct alignment is known. 222/// 223void addContentsOf ( ChunkBuilder * chunk ); 224 225/// Get the size in bytes of the prefix of this chunk, if any. 226/// 227/// The prefix will be written so that the *end* offset of the 228/// prefix data is the same as the starting offset of the 229/// chunk's content. 230/// 231Size getPrefixSize () const ; 232 233/// Add a prefix to this chunk, consisting of raw data. 234/// 235/// This chunk must not already have a prefix. 236/// 237void addPrefixData ( void const * data , Size size ); 238 239/// Add a prefix to this chunk, consisting of a relative pointer. 240/// 241/// This chunk must not already have a prefix. 242/// 243/// Updates the alignment of the chunk as if making a call to 244/// `setAlignmentToAtLeast(sizeof(T))`. 245/// 246template < typename T > 247void addPrefixRelativePtr ( ChunkBuilder * targetChunk ) 248{ 249_addPrefixRelativePtr ( targetChunk , sizeof ( T )); 250} 251 252/// Insert a new chunk into the blob, immediately after this chunk. 253/// 254ChunkBuilder * addChunkAfter (); 255 256private : 257ChunkBuilder () = delete ; 258ChunkBuilder ( ChunkBuilder const & ) = delete ; 259ChunkBuilder ( ChunkBuilder && ) = delete ; 260 261friend struct BlobBuilder ; 262friend class ShardBuilder ; 263 264ChunkBuilder ( BlobBuilder * parentBlob ) ; 265 266Size _contentSize = 0 ; 267Size _contentAlignment = 1 ; 268 269InternallyLinkedList < ShardBuilder > _childShards; 270BlobBuilder * _parentBlob = nullptr ; 271 272Size _cachedOffset = ~ Size ( 0 ); 273Size _getCachedOffset () { return _cachedOffset; } 274void _setCachedOffset ( Size offset) { _cachedOffset = offset; } 275 276void _writeRelativePtr ( ChunkBuilder * targetChunk, Size ptrSize); 277 278ShardBuilder * _createDataShard ( void const * data, Size size); 279ShardBuilder * _createRelativePtrShard ( ChunkBuilder * targetChunk, Size ptrSize); 280 281ShardBuilder * _prefixShard = nullptr ; 282 283void _writeTo ( Stream * stream); 284 285void _addPrefixRelativePtr ( ChunkBuilder * targetChunk, Size ptrSize); 286}; 287 288/// A shard is a unit of contiguously-allocated data that makes 289/// up part of a chunk. 290/// 291/// Shards are *not* meant to be directly manipulated by users; 292/// they are an implementation detail of `ChunkBuilder`. 293/// 294/// Every `ShardBuilder` is owned by its parent `ChunkBuilder`. 295/// A pointer to a `ShardBuilder` will only be valid during the 296/// lifetime of the parent `ChunkBuilder`. 297/// 298class ShardBuilder : public InternallyLinkedList < ShardBuilder > ::Node 299{ 300public : 301// There are two kinds of shards that may appear in a chunk: 302// 303// * Shards that hold plain data that will be part of the 304// serialized chunk. 305// 306// * Shards that represent a relative pointer to some chunk, 307// which cannot have their exact binary value determined 308// until the offsets of chunk/shards have been finalized. 309 310enum class Kind 311{ 312Data, 313RelativePtr, 314}; 315 316Size getSize () const { return _size; } 317 318private : 319ShardBuilder () = delete; 320ShardBuilder( ShardBuilder const & ) = delete; 321ShardBuilder (ShardBuilder && ) = delete; 322 323friend class ChunkBuilder; 324ShardBuilder( Kind kind ); 325 326void _writeTo ( Stream * stream, Size selfOffset); 327 328/// Kind of this shard (data or relative pointer) 329Kind _kind = Kind::Data; 330 331union 332{ 333/// Used when `_kind == Kind::Data` 334struct 335{ 336void const * ptr ; 337} _data ; 338 339/// Used when `_kind == Kind::RelativePtr` 340struct 341{ 342ChunkBuilder * targetChunk ; 343} _relativePtr ; 344}; 345 346// Size of this shard in bytes. 347Size _size = 0 ; 348}; 349 350} // namespace Slang 351 352#endif