summaryrefslogtreecommitdiff
path: root/source/slang/slang-serialize-riff.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-serialize-riff.cpp')
-rw-r--r--source/slang/slang-serialize-riff.cpp897
1 files changed, 897 insertions, 0 deletions
diff --git a/source/slang/slang-serialize-riff.cpp b/source/slang/slang-serialize-riff.cpp
new file mode 100644
index 000000000..01b39e825
--- /dev/null
+++ b/source/slang/slang-serialize-riff.cpp
@@ -0,0 +1,897 @@
+// slang-serialize-riff.cpp
+#include "slang-serialize-riff.h"
+
+namespace Slang
+{
+
+//
+// RIFFSerialWriter
+//
+
+RIFFSerialWriter::RIFFSerialWriter(RIFF::ChunkBuilder* chunk, FourCC type)
+ : _cursor(chunk)
+{
+ _initialize(type);
+}
+
+RIFFSerialWriter::RIFFSerialWriter(RIFF::Builder& riff, FourCC type)
+ : _cursor(riff)
+{
+ _initialize(type);
+}
+
+RIFFSerialWriter::~RIFFSerialWriter()
+{
+ // We need to flush any pending operations to
+ // write objects into the object definition list chunk.
+ //
+ _flush();
+}
+
+SerializationMode RIFFSerialWriter::getMode()
+{
+ return SerializationMode::Write;
+}
+
+void RIFFSerialWriter::handleBool(bool& value)
+{
+ _cursor.addDataChunk(value ? RIFFSerial::kTrueFourCC : RIFFSerial::kFalseFourCC, nullptr, 0);
+}
+
+void RIFFSerialWriter::handleInt8(int8_t& value)
+{
+ _writeInt(value);
+}
+
+void RIFFSerialWriter::handleInt16(int16_t& value)
+{
+ _writeInt(value);
+}
+
+void RIFFSerialWriter::handleInt32(Int32& value)
+{
+ _writeInt(value);
+}
+
+void RIFFSerialWriter::handleInt64(Int64& value)
+{
+ _writeInt(value);
+}
+
+void RIFFSerialWriter::handleUInt8(uint8_t& value)
+{
+ _writeUInt(value);
+}
+
+void RIFFSerialWriter::handleUInt16(uint16_t& value)
+{
+ _writeUInt(value);
+}
+
+void RIFFSerialWriter::handleUInt32(UInt32& value)
+{
+ _writeUInt(value);
+}
+
+void RIFFSerialWriter::handleUInt64(UInt64& value)
+{
+ _writeUInt(value);
+}
+
+void RIFFSerialWriter::handleFloat32(float& value)
+{
+ _writeFloat(value);
+}
+
+void RIFFSerialWriter::handleFloat64(double& value)
+{
+ _writeFloat(value);
+}
+
+void RIFFSerialWriter::handleString(String& value)
+{
+ _cursor.addDataChunk(RIFFSerial::kStringFourCC, value.getBuffer(), value.getLength());
+}
+
+void RIFFSerialWriter::_writeInt(Int64 value)
+{
+ // We pick a 32-bit representation if it can
+ // faithfully represent the value, and a 64-bit
+ // representation otherwise.
+ //
+ if (Int32(value) == value)
+ {
+ auto v = Int32(value);
+ _cursor.addDataChunk(RIFFSerial::kInt32FourCC, &v, sizeof(v));
+ }
+ else
+ {
+ _cursor.addDataChunk(RIFFSerial::kInt64FourCC, &value, sizeof(value));
+ }
+}
+
+void RIFFSerialWriter::_writeUInt(UInt64 value)
+{
+ // We pick a 32-bit representation if it can
+ // faithfully represent the value, and a 64-bit
+ // representation otherwise.
+ //
+ if (UInt32(value) == value)
+ {
+ auto v = UInt32(value);
+ _cursor.addDataChunk(RIFFSerial::kUInt32FourCC, &v, sizeof(v));
+ }
+ else
+ {
+ _cursor.addDataChunk(RIFFSerial::kUInt64FourCC, &value, sizeof(value));
+ }
+}
+
+void RIFFSerialWriter::_writeFloat(double value)
+{
+ // We pick a 32-bit representation if it can
+ // faithfully represent the value, and a 64-bit
+ // representation otherwise.
+ //
+ if (float(value) == value)
+ {
+ auto v = float(value);
+ _cursor.addDataChunk(RIFFSerial::kFloat32FourCC, &v, sizeof(v));
+ }
+ else
+ {
+ _cursor.addDataChunk(RIFFSerial::kFloat64FourCC, &value, sizeof(value));
+ }
+}
+
+void RIFFSerialWriter::beginArray()
+{
+ _cursor.beginListChunk(RIFFSerial::kArrayFourCC);
+}
+
+void RIFFSerialWriter::endArray()
+{
+ _cursor.endChunk();
+}
+
+void RIFFSerialWriter::beginDictionary()
+{
+ _cursor.beginListChunk(RIFFSerial::kDictionaryFourCC);
+}
+
+void RIFFSerialWriter::endDictionary()
+{
+ _cursor.endChunk();
+}
+
+bool RIFFSerialWriter::hasElements()
+{
+ return false;
+}
+
+void RIFFSerialWriter::beginStruct()
+{
+ _cursor.beginListChunk(RIFFSerial::kStructFourCC);
+}
+
+void RIFFSerialWriter::handleFieldKey(char const* name, Int index)
+{
+ // For now we are ignoring field keys, and treating
+ // structs as basically equivalent to tuples.
+ SLANG_UNUSED(name);
+ SLANG_UNUSED(index);
+}
+
+void RIFFSerialWriter::endStruct()
+{
+ _cursor.endChunk();
+}
+
+void RIFFSerialWriter::beginTuple()
+{
+ _cursor.beginListChunk(RIFFSerial::kTupleFourCC);
+}
+
+void RIFFSerialWriter::endTuple()
+{
+ _cursor.endChunk();
+}
+
+void RIFFSerialWriter::beginOptional()
+{
+ _cursor.beginListChunk(RIFFSerial::kOptionalFourCC);
+}
+
+void RIFFSerialWriter::endOptional()
+{
+ _cursor.endChunk();
+}
+
+void RIFFSerialWriter::handleSharedPtr(void*& value, Callback callback, void* userData)
+{
+ // Because we are writing, we only care about the
+ // pointer that is already present in `value`.
+ //
+ void* ptr = value;
+
+ // The first special case we check for is a null pointer,
+ // which we can serialize as an inline value.
+ //
+ if (ptr == nullptr)
+ {
+ _cursor.addDataChunk(RIFFSerial::kNullFourCC, nullptr, 0);
+ return;
+ }
+
+ // Next, we check to see if we have encountered this
+ // pointer before, in which case we've already allocated
+ // an index for it in the object definition list, and
+ // we can simply write a reference to that index.
+ //
+ if (auto found = _mapPtrToObjectIndex.tryGetValue(ptr))
+ {
+ auto objectIndex = *found;
+ _writeObjectReference(objectIndex);
+ return;
+ }
+
+ // If we have a non-null pointer that we haven't seen
+ // before, then we will allocate a new entry in the
+ // object definition list, and the pointer itself
+ // will be written as a reference to that entry.
+ //
+ auto objectIndex = ObjectIndex(_objects.getCount());
+ _mapPtrToObjectIndex.add(ptr, objectIndex);
+ _writeObjectReference(objectIndex);
+
+ // At this point we've correctly written the *reference*
+ // to the object (and will be able to write further
+ // references later if we see an identical pointer),
+ // but we also need to make sure that the *definition*
+ // of the object gets written into the object definition
+ // list chunk.
+ //
+ // The `callback` that was passed in can be used to
+ // write out the members of the object, but if we
+ // simply invoked it here and now we would be at risk
+ // of introducing unbounded recursion in cases where
+ // the object graph contains very long pointer chains.
+ //
+ // (Note that we are not at risk of *infinite* recursion,
+ // because we have already cached the index for the
+ // object into `_mapPtrToObjectIndex`)
+ //
+ // We will simply add an entry to our `_objects` array
+ // to represent the to-be-written object, and store
+ // the pointer and callback there so that we can write
+ // everything out later, in `_flush()`.
+ //
+ ObjectInfo objectInfo;
+ objectInfo.ptr = ptr;
+ objectInfo.callback = callback;
+ objectInfo.userData = userData;
+ _objects.add(objectInfo);
+}
+
+void RIFFSerialWriter::handleUniquePtr(void*& value, Callback callback, void* userData)
+{
+ // We treat all pointers as shared pointers, because there isn't really
+ // an optimized representation we would want to use for the unique case.
+ //
+ handleSharedPtr(value, callback, userData);
+}
+
+void RIFFSerialWriter::handleDeferredObjectContents(
+ void* valuePtr,
+ Callback callback,
+ void* userData)
+{
+ // Because we are already deferring writing of the *entirety* of
+ // an object's members as part of how `handleSharedPtr()` works,
+ // we don't need to implement deferral at this juncture.
+ //
+ // (In practice the `handleDeferredObjectContents()` operation is
+ // more for the benefit of reading than writing).
+ //
+ callback(valuePtr, userData);
+}
+
+void RIFFSerialWriter::_writeObjectReference(ObjectIndex index)
+{
+ _cursor.addDataChunk(RIFFSerial::kObjectReferenceFourCC, &index, sizeof(index));
+}
+
+void RIFFSerialWriter::_initialize(FourCC type)
+{
+ // The entire content that we write will be nested
+ // in a single list chunk, with the type that was
+ // passed in.
+ //
+ _cursor.beginListChunk(type);
+
+ // The first child chunk needs to be the object
+ // definition list chunk, so we create it up front.
+ //
+ _objectDefinitionListChunk = _cursor.addListChunk(RIFFSerial::kObjectDefinitionListFourCC);
+}
+
+void RIFFSerialWriter::_flush()
+{
+ // At this point we might have zero or more object
+ // waiting to be written into the object definition list
+ // chunk, and we need to make sure that they all
+ // get a chance to write their content out.
+ //
+ _cursor.setCurrentChunk(_objectDefinitionListChunk);
+
+ // Note that we do *not* compute `_objects.getCount()` outside
+ // of the loop here, because writing out one object definition
+ // could cause other objects to be referenced, which could
+ // in turn add more entries to `_objects` that need to be
+ // written out.
+ //
+ while (_writtenObjectDefinitionCount < _objects.getCount())
+ {
+ auto objectIndex = _writtenObjectDefinitionCount++;
+ auto objectInfo = _objects[objectIndex];
+
+ // We shouldn't ever be putting a null pointer into the
+ // object definition list; there is logic in `handleSharedPtr()`
+ // that explicitly checks for a null pointer and does an
+ // early-exit in that case.
+ //
+ SLANG_ASSERT(objectInfo.ptr);
+
+ // The callback that was passed into `handleSharedPtr()` should
+ // be able to write out the value of the pointed-to object.
+ //
+ // Note that we are passing the *address* of `objectInfo.ptr`
+ // and not just its *value*, because this callback is used
+ // for both reading and writing, and in the reading case it
+ // needs to be invoked on a pointer-pointer (e.g., a `T**` when
+ // serializing an object pointer `T*`) so that the callee
+ // can set the pointed-to pointer to whatever object it
+ // allocates or finds.
+ //
+ objectInfo.callback(&objectInfo.ptr, objectInfo.userData);
+
+ // TODO(tfoley): There is an important invariant here that
+ // the callback had better only write *one* value, but
+ // that is not currently being enforced.
+ }
+}
+
+//
+// RIFFSerialReader
+//
+
+RIFFSerialReader::RIFFSerialReader(RIFF::Chunk const* chunk, FourCC type)
+ : _cursor(chunk)
+{
+ _initialize(type);
+}
+
+RIFFSerialReader::~RIFFSerialReader()
+{
+ _flush();
+}
+
+SerializationMode RIFFSerialReader::getMode()
+{
+ return SerializationMode::Read;
+}
+
+void RIFFSerialReader::handleBool(bool& value)
+{
+ switch (_peekChunkType())
+ {
+ case RIFFSerial::kTrueFourCC:
+ _advanceCursor();
+ value = true;
+ break;
+
+ case RIFFSerial::kFalseFourCC:
+ _advanceCursor();
+ value = false;
+ break;
+
+ default:
+ SLANG_UNEXPECTED("invalid format in RIFF");
+ break;
+ }
+}
+
+void RIFFSerialReader::handleInt8(int8_t& value)
+{
+ value = int8_t(_readInt());
+}
+
+void RIFFSerialReader::handleInt16(int16_t& value)
+{
+ value = int16_t(_readInt());
+}
+
+void RIFFSerialReader::handleInt32(Int32& value)
+{
+ value = Int32(_readInt());
+}
+
+void RIFFSerialReader::handleInt64(Int64& value)
+{
+ value = Int64(_readInt());
+}
+
+void RIFFSerialReader::handleUInt8(uint8_t& value)
+{
+ value = uint8_t(_readUInt());
+}
+
+void RIFFSerialReader::handleUInt16(uint16_t& value)
+{
+ value = uint16_t(_readUInt());
+}
+
+void RIFFSerialReader::handleUInt32(UInt32& value)
+{
+ value = UInt32(_readUInt());
+}
+
+void RIFFSerialReader::handleUInt64(UInt64& value)
+{
+ value = UInt64(_readUInt());
+}
+
+void RIFFSerialReader::handleFloat32(float& value)
+{
+ value = float(_readFloat());
+}
+
+void RIFFSerialReader::handleFloat64(double& value)
+{
+ value = double(_readFloat());
+}
+
+void RIFFSerialReader::handleString(String& value)
+{
+ if (_peekChunkType() != RIFFSerial::kStringFourCC)
+ {
+ SLANG_UNEXPECTED("invalid format in RIFF");
+ return;
+ }
+
+ auto dataChunk = as<RIFF::DataChunk>(_cursor);
+ if (!dataChunk)
+ {
+ SLANG_UNEXPECTED("invalid format in RIFF");
+ return;
+ }
+
+ auto size = dataChunk->getPayloadSize();
+
+ value = String();
+ value.appendRepeatedChar(' ', size);
+ dataChunk->writePayloadInto((char*)value.getBuffer(), size);
+
+ _advanceCursor();
+}
+
+void RIFFSerialReader::beginArray()
+{
+ _beginListChunk(RIFFSerial::kArrayFourCC);
+}
+
+void RIFFSerialReader::endArray()
+{
+ _endListChunk();
+}
+
+
+void RIFFSerialReader::beginDictionary()
+{
+ _beginListChunk(RIFFSerial::kDictionaryFourCC);
+}
+
+void RIFFSerialReader::endDictionary()
+{
+ _endListChunk();
+}
+
+bool RIFFSerialReader::hasElements()
+{
+ return _cursor.get() != nullptr;
+}
+
+void RIFFSerialReader::beginStruct()
+{
+ _beginListChunk(RIFFSerial::kStructFourCC);
+}
+
+void RIFFSerialReader::handleFieldKey(char const* name, Int index)
+{
+ // For now we are ignoring field keys, and treating
+ // structs as basically equivalent to tuples.
+ SLANG_UNUSED(name);
+ SLANG_UNUSED(index);
+}
+
+void RIFFSerialReader::endStruct()
+{
+ _endListChunk();
+}
+
+void RIFFSerialReader::beginTuple()
+{
+ _beginListChunk(RIFFSerial::kTupleFourCC);
+}
+
+void RIFFSerialReader::endTuple()
+{
+ _endListChunk();
+}
+
+void RIFFSerialReader::beginOptional()
+{
+ _beginListChunk(RIFFSerial::kOptionalFourCC);
+}
+
+void RIFFSerialReader::endOptional()
+{
+ _endListChunk();
+}
+
+RIFFSerialReader::ObjectIndex RIFFSerialReader::_readObjectReference()
+{
+ if (_peekChunkType() != RIFFSerial::kObjectReferenceFourCC)
+ {
+ SLANG_UNEXPECTED("invalid format in RIFF");
+ UNREACHABLE_RETURN(false);
+ }
+
+ auto objectIndex = _readDataChunk<ObjectIndex>();
+ SLANG_ASSERT(objectIndex >= 0 && objectIndex < _objects.getCount());
+ return objectIndex;
+}
+
+void RIFFSerialReader::handleSharedPtr(void*& value, Callback callback, void* userData)
+{
+ // The logic here largely mirrors what appears in
+ // `RIFFSerialWriter::handleSharedPtr`.
+ //
+ // We first check for an explicitly written null pointer.
+ // If we find one our work is very easy.
+ //
+ if (_peekChunkType() == RIFFSerial::kNullFourCC)
+ {
+ _advanceCursor();
+ value = nullptr;
+ return;
+ }
+
+ // Otherwise, we expect to find a reference to
+ // an object index.
+ //
+ // Note that `_readObjectReference()` already asserts
+ // that the index is in-bounds, so we don't repeat
+ // that test here.
+ //
+ auto objectIndex = _readObjectReference();
+
+ // Now we need to check if we've previously read in
+ // a reference to the same object.
+ //
+ auto& objectInfo = _objects[objectIndex];
+ if (objectInfo.state != ObjectState::Unread)
+ {
+ // We've seen this object before, although it
+ // is still possible that we are in the middle
+ // of reading it as part of an invocation
+ // of `handleSharedPtr()` further up the call
+ // stack.
+ //
+ // If a non-nullpointer value has already been
+ // written into the `objectInfo`, then that means
+ // the callback that was run for the prior (or
+ // in-flight) read operation has already allocated
+ // or found an object and written it out.
+ // In that case we will trust the value.
+ //
+ if (objectInfo.ptr == nullptr)
+ {
+ // It is possible that the pointer is null because
+ // the callback that was invoked explicitly *chose*
+ // to yield a null pointer (perhaps the application
+ // is choosing not to deserialize some optional
+ // piece of state).
+ //
+ // However, if there is still a callback in-flight
+ // to read this object, and the pointer is null,
+ // then we have reached a circular reference,
+ // and need to signal an error.
+ //
+ if (objectInfo.state == ObjectState::ReadingInProgress)
+ {
+ SLANG_UNEXPECTED("circularity detected in RIFF deserialization");
+ }
+ }
+ value = objectInfo.ptr;
+ return;
+ }
+
+ // At this point we are reading a reference to an
+ // object index that has not yet been read at all.
+ //
+ SLANG_ASSERT(objectInfo.state == ObjectState::Unread);
+
+ // We cannot return from this function until we have
+ // stored a pointer into `value`, to represent the
+ // deserialized object.
+ //
+ // Thus we will set ourselves up to start reading
+ // from the relevant object definition, and invoke
+ // the callback that was passed in.
+ //
+ // Calling into user-defined serialization logic from
+ // within this function creates the possibility of
+ // unbounded/infinite recursion, so it is vital that
+ // the user is properly using `deferSerializeObjectContents()`
+ // to delay reading data that isn't immediately
+ // necessary.
+ //
+ // We will still set the `objectInfo.state` to reflect
+ // this in-flight operation so that we can detect
+ // a cirularity if one occurs at runtime.
+ //
+ objectInfo.state = ObjectState::ReadingInProgress;
+
+ // We save/restore the current cursor around
+ // the callback, because we need to be able
+ // to return to the current state to continue
+ // reading whatever comes after the pointer
+ // we were invoked to read.
+ //
+ _pushCursor();
+ _cursor = objectInfo.definitionChunk;
+
+ // Note that we are passing the address of `objectInfo.ptr`,
+ // and `objectInfo` is a reference to an element of the
+ // `_objects` array. Thus whenever the `callback` stores
+ // a pointer into that output parameter, the value it writes
+ // will automatically be visible to any subsequent calls
+ // to `handleSharedPtr()`, even if they occur before
+ // `callback` returns.
+ //
+ // Thus a "true" circularity can only occur if the callback
+ // recursively reads a reference to the same object again
+ // *before* it allocates the in-memory representation of
+ // that objects and stores a pointer to it into the output
+ // parameter.
+ //
+ callback(&objectInfo.ptr, userData);
+
+ _popCursor();
+
+ objectInfo.state = ObjectState::ReadingComplete;
+
+ value = objectInfo.ptr;
+}
+
+void RIFFSerialReader::handleUniquePtr(void*& value, Callback callback, void* userData)
+{
+ // We treat all pointers as shared pointers, because there isn't really
+ // an optimized representation we would want to use for the unique case.
+ //
+ handleSharedPtr(value, callback, userData);
+}
+
+void RIFFSerialReader::handleDeferredObjectContents(
+ void* valuePtr,
+ Callback callback,
+ void* userData)
+{
+ // Unlike the case in `RIFFSerialWriter::handleDeferredObjectContents()`,
+ // we very much *do* want to delay invoking the callback until later.
+ //
+ // There is a kind of symmetry going on, where the writer delays the
+ // callback passed to `handleSharedPtr()`, but *not* the callback
+ // passed to `handleDeferredObjectContents()`, while the reader
+ // does the opposite: immediately calls the callback in `handleSharedPtr()`
+ // but delays calling it here.
+
+ // We make sure to save the current `_cursor` value along with
+ // the arguments that will be passed into the callback, so that
+ // we can restore the reader to this state before invoking
+ // the callbak in `_flush()`.
+
+ DeferredAction deferredAction;
+ deferredAction.savedCursor = _cursor;
+ deferredAction.valuePtr = valuePtr;
+ deferredAction.callback = callback;
+ deferredAction.userData = userData;
+
+ _deferredActions.add(deferredAction);
+}
+
+void RIFFSerialReader::_initialize(FourCC type)
+{
+ // All of the content will have been serialized as a single RIFF
+ // list chunk (possibly a root chunk if this content comprises
+ // an entire file), with the given `type`.
+ //
+ _beginListChunk(type);
+
+ // The first child chunk should be the object definition list
+ // chunk, and we will proactively read through all of the
+ // entries in that chunk to build up the `_objects` array.
+ //
+ // This operation takes linear time in the number of serialized
+ // objects, independent of their size, because the RIFF chunk
+ // headers allow us to skip over the content of each of these
+ // object-definition chunks.
+ //
+ _beginListChunk(RIFFSerial::kObjectDefinitionListFourCC);
+ while (auto objectDefinitionChunk = _cursor.get())
+ {
+ ObjectInfo objectInfo;
+ objectInfo.definitionChunk = objectDefinitionChunk;
+ _objects.add(objectInfo);
+
+ _advanceCursor();
+ }
+ _endListChunk();
+}
+
+void RIFFSerialReader::_flush()
+{
+ // We need to flush any actions that were deferred
+ // and are still pending.
+ //
+ while (_deferredActions.getCount() != 0)
+ {
+ // TODO: For simplicity we are using the `_deferredActions`
+ // array as a stack (LIFO), but it would be good to
+ // check whether there is a menaingful difference in how
+ // large the array would need to grow for a FIFO vs. LIFO,
+ // and pick the better option.
+ //
+ auto deferredAction = _deferredActions.getLast();
+ _deferredActions.removeLast();
+
+ _cursor = deferredAction.savedCursor;
+ deferredAction.callback(deferredAction.valuePtr, deferredAction.userData);
+ }
+}
+
+FourCC RIFFSerialReader::_peekChunkType()
+{
+ auto chunk = _cursor.get();
+ if (!chunk)
+ return 0;
+ return chunk->getType();
+}
+
+Int64 RIFFSerialReader::_readInt()
+{
+ switch (_peekChunkType())
+ {
+ case RIFFSerial::kInt64FourCC:
+ return _readDataChunk<Int64>();
+ case RIFFSerial::kInt32FourCC:
+ return _readDataChunk<Int32>();
+
+ case RIFFSerial::kUInt32FourCC:
+ return _readDataChunk<UInt32>();
+
+ case RIFFSerial::kUInt64FourCC:
+ {
+ auto uintValue = _readDataChunk<UInt64>();
+ if (Int64(uintValue) < 0)
+ {
+ SLANG_UNEXPECTED("signed/unsigned mismatch in RIFF");
+ }
+ return Int64(uintValue);
+ }
+
+ default:
+ SLANG_UNEXPECTED("invalid format in RIFF");
+ UNREACHABLE_RETURN(0);
+ }
+}
+
+UInt64 RIFFSerialReader::_readUInt()
+{
+ switch (_peekChunkType())
+ {
+ case RIFFSerial::kUInt64FourCC:
+ return _readDataChunk<UInt64>();
+ case RIFFSerial::kUInt32FourCC:
+ return _readDataChunk<UInt32>();
+
+ case RIFFSerial::kInt32FourCC:
+ case RIFFSerial::kInt64FourCC:
+ {
+ auto intValue = _readInt();
+ if (intValue < 0)
+ {
+ SLANG_UNEXPECTED("signed/unsigned mismatch in RIFF");
+ }
+ return UInt64(intValue);
+ }
+
+ default:
+ SLANG_UNEXPECTED("invalid format in RIFF");
+ UNREACHABLE_RETURN(0);
+ }
+}
+
+double RIFFSerialReader::_readFloat()
+{
+ switch (_peekChunkType())
+ {
+ case RIFFSerial::kFloat32FourCC:
+ return _readDataChunk<float>();
+ case RIFFSerial::kFloat64FourCC:
+ return _readDataChunk<double>();
+
+ default:
+ SLANG_UNEXPECTED("invalid format in RIFF");
+ UNREACHABLE_RETURN(0);
+ }
+}
+
+void RIFFSerialReader::_readDataChunk(void* outData, size_t dataSize)
+{
+ auto dataChunk = as<RIFF::DataChunk>(_cursor);
+ if (!dataChunk)
+ {
+ SLANG_UNEXPECTED("invalid format in RIFF");
+ return;
+ }
+ auto size = dataChunk->getPayloadSize();
+ if (size < dataSize)
+ {
+ SLANG_UNEXPECTED("invalid format in RIFF");
+ return;
+ }
+ dataChunk->writePayloadInto(outData, dataSize);
+ _advanceCursor();
+}
+
+
+void RIFFSerialReader::_beginListChunk(FourCC type)
+{
+ auto listChunk = as<RIFF::ListChunk>(_cursor);
+ if (!listChunk || listChunk->getType() != type)
+ {
+ SLANG_UNEXPECTED("invalid format in RIFF");
+ }
+
+ _advanceCursor();
+ _pushCursor();
+
+ _cursor = listChunk->getFirstChild();
+}
+
+void RIFFSerialReader::_endListChunk()
+{
+ _popCursor();
+}
+
+void RIFFSerialReader::_advanceCursor()
+{
+ _cursor = _cursor.getNextSibling();
+}
+
+void RIFFSerialReader::_pushCursor()
+{
+ _stack.add(_cursor);
+}
+
+void RIFFSerialReader::_popCursor()
+{
+ SLANG_ASSERT(_stack.getCount() != 0);
+ _cursor = _stack.getLast();
+ _stack.removeLast();
+}
+
+
+} // namespace Slang