summaryrefslogtreecommitdiff
path: root/source/core
diff options
context:
space:
mode:
Diffstat (limited to 'source/core')
-rw-r--r--source/core/slang-hex-dump-util.cpp15
-rw-r--r--source/core/slang-hex-dump-util.h3
-rw-r--r--source/core/slang-riff.cpp772
-rw-r--r--source/core/slang-riff.h286
-rw-r--r--source/core/slang-stream.cpp33
-rw-r--r--source/core/slang-stream.h14
6 files changed, 1054 insertions, 69 deletions
diff --git a/source/core/slang-hex-dump-util.cpp b/source/core/slang-hex-dump-util.cpp
index 908f6c1b6..4a3d4ce06 100644
--- a/source/core/slang-hex-dump-util.cpp
+++ b/source/core/slang-hex-dump-util.cpp
@@ -12,6 +12,7 @@ namespace Slang
static const UnownedStringSlice s_start = UnownedStringSlice::fromLiteral("--START--");
static const UnownedStringSlice s_end = UnownedStringSlice::fromLiteral("--END--");
+static const char s_hex[] = "0123456789abcdef";
/* static */SlangResult HexDumpUtil::dumpWithMarkers(const List<uint8_t>& data, int maxBytesPerLine, ISlangWriter* writer)
{
@@ -26,6 +27,17 @@ static const UnownedStringSlice s_end = UnownedStringSlice::fromLiteral("--END--
return SLANG_OK;
}
+/* static */void HexDumpUtil::dump(uint32_t value, ISlangWriter* writer)
+{
+ char c[9];
+ for (int i = 0; i < 8; ++i)
+ {
+ c[i] = s_hex[value >> 28];
+ value <<= 4;
+ }
+ writer->write(c, 8);
+}
+
/* static */SlangResult HexDumpUtil::dump(const List<uint8_t>& data, int maxBytesPerLine, ISlangWriter* writer)
{
int maxCharsPerLine = 2 * maxBytesPerLine + 1 + maxBytesPerLine + 1;
@@ -33,8 +45,7 @@ static const UnownedStringSlice s_end = UnownedStringSlice::fromLiteral("--END--
const uint8_t* cur = data.begin();
const uint8_t* end = data.end();
- static char s_hex[] = "0123456789abcdef";
-
+
while (cur < end)
{
size_t count = size_t(end - cur);
diff --git a/source/core/slang-hex-dump-util.h b/source/core/slang-hex-dump-util.h
index 580f5a120..fefe84a17 100644
--- a/source/core/slang-hex-dump-util.h
+++ b/source/core/slang-hex-dump-util.h
@@ -15,6 +15,9 @@ struct HexDumpUtil
/// Dump data to writer, with lines starting with hex data
static SlangResult dump(const List<uint8_t>& data, int numBytesPerLine, ISlangWriter* writer);
+ /// Dump a single value
+ static void dump(uint32_t value, ISlangWriter* writer);
+
static SlangResult dumpWithMarkers(const List<uint8_t>& data, int numBytesPerLine, ISlangWriter* writer);
/// Parses lines formatted by dump, back into bytes
diff --git a/source/core/slang-riff.cpp b/source/core/slang-riff.cpp
index 0b0519b48..9849cfc26 100644
--- a/source/core/slang-riff.cpp
+++ b/source/core/slang-riff.cpp
@@ -2,16 +2,18 @@
#include "../../slang-com-helper.h"
+#include "slang-hex-dump-util.h"
+
namespace Slang
{
-/* static */int64_t RiffUtil::calcChunkTotalSize(const RiffChunk& chunk)
+/* static */int64_t RiffUtil::calcChunkTotalSize(const RiffHeader& chunk)
{
- int64_t size = chunk.m_size + sizeof(RiffChunk);
- return (size + 3) & ~int64_t(3);
+ size_t size = chunk.size + sizeof(RiffHeader);
+ return getPadSize(size);
}
-/* static */SlangResult RiffUtil::skip(const RiffChunk& chunk, Stream* stream, int64_t* remainingBytesInOut)
+/* static */SlangResult RiffUtil::skip(const RiffHeader& chunk, Stream* stream, int64_t* remainingBytesInOut)
{
int64_t chunkSize = calcChunkTotalSize(chunk);
if (remainingBytesInOut)
@@ -20,16 +22,15 @@ namespace Slang
}
// Skip the payload (we don't need to skip the Chunk because that was already read
- stream->seek(SeekOrigin::Current, chunkSize - sizeof(RiffChunk));
+ stream->seek(SeekOrigin::Current, chunkSize - sizeof(RiffHeader));
return SLANG_OK;
}
-
-/* static */SlangResult RiffUtil::readChunk(Stream* stream, RiffChunk& outChunk)
+/* static */SlangResult RiffUtil::readChunk(Stream* stream, RiffHeader& outChunk)
{
try
{
- stream->read(&outChunk, sizeof(RiffChunk));
+ stream->read(&outChunk, sizeof(RiffHeader));
}
catch (IOException&)
{
@@ -42,32 +43,38 @@ namespace Slang
}
-/* static */SlangResult RiffUtil::writeData(const RiffChunk* header, size_t headerSize, const void* payload, size_t payloadSize, Stream* out)
+/* static */SlangResult RiffUtil::writeData(const RiffHeader* header, size_t headerSize, const void* payload, size_t payloadSize, Stream* out)
{
SLANG_ASSERT(uint64_t(payloadSize) <= uint64_t(0xfffffffff));
- SLANG_ASSERT(headerSize >= sizeof(RiffChunk));
- SLANG_ASSERT((headerSize & 3) == 0);
+ SLANG_ASSERT(headerSize >= sizeof(RiffHeader));
// TODO(JS): Could handle endianness here
- RiffChunk chunk;
- chunk.m_type = header->m_type;
- chunk.m_size = uint32_t(headerSize - sizeof(RiffChunk) + payloadSize);
+ RiffHeader chunk;
+ chunk.type = header->type;
+ chunk.size = uint32_t(headerSize - sizeof(RiffHeader) + payloadSize);
try
{
// The chunk
- out->write(&chunk, sizeof(RiffChunk));
- // The rest of the header
- out->write(header + 1, headerSize - sizeof(RiffChunk));
+ out->write(&chunk, sizeof(RiffHeader));
+
+ // Remainder of header
+ if (headerSize > sizeof(RiffHeader))
+ {
+ // The rest of the header
+ out->write(header + 1, headerSize - sizeof(RiffHeader));
+ }
+ // Write the payload
out->write(payload, payloadSize);
- size_t remaining = payloadSize & 3;
- if (remaining)
+ // The riff spec requires all chunks are 4 byte aligned (even if size is not)
+ size_t padSize = getPadSize(payloadSize);
+ if (padSize - payloadSize)
{
- uint8_t end[4] = { 0, 0, 0, 0};
- out->write(end, 4 - remaining);
+ uint8_t end[kRiffPadSize] = { 0 };
+ out->write(end, padSize - payloadSize);
}
}
catch (IOException&)
@@ -78,11 +85,33 @@ namespace Slang
return SLANG_OK;
}
-/* static */SlangResult RiffUtil::readData(Stream* stream, RiffChunk* outHeader, size_t headerSize, List<uint8_t>& data)
+/* static */SlangResult RiffUtil::readPayload(Stream* stream, size_t size, void* outData, size_t& outReadSize)
{
- RiffChunk chunk;
+ outReadSize = 0;
+ try
+ {
+ stream->read(outData, size);
+ const size_t alignedSize = getPadSize(size);
+ // Skip to the alignment
+ if (alignedSize > size)
+ {
+ stream->seek(SeekOrigin::Current, alignedSize - size);
+ }
+ outReadSize = alignedSize;
+ }
+ catch (IOException&)
+ {
+ return SLANG_FAIL;
+ }
+
+ return SLANG_OK;
+}
+
+/* static */SlangResult RiffUtil::readData(Stream* stream, RiffHeader* outHeader, size_t headerSize, List<uint8_t>& data)
+{
+ RiffHeader chunk;
SLANG_RETURN_ON_FAIL(readChunk(stream, chunk));
- if (chunk.m_size < headerSize)
+ if (chunk.size < headerSize)
{
return SLANG_FAIL;
}
@@ -92,27 +121,702 @@ namespace Slang
try
{
// Read the header
- stream->read(outHeader + 1, headerSize - sizeof(RiffChunk));
+ if (headerSize > sizeof(RiffHeader))
+ {
+ stream->read(outHeader + 1, headerSize - sizeof(RiffHeader));
+ }
+ }
+ catch (IOException&)
+ {
+ return SLANG_FAIL;
+ }
- const size_t payloadSize = chunk.m_size - (headerSize - sizeof(RiffChunk));
+ const size_t payloadSize = chunk.size - (headerSize - sizeof(RiffHeader));
+ size_t readSize;
+ data.setCount(payloadSize);
+ return readPayload(stream, payloadSize, data.getBuffer(), readSize);
+}
- data.setCount(payloadSize);
+/* static */SlangResult RiffUtil::readHeader(Stream* stream, RiffListHeader& outHeader)
+{
+ // Need to read the chunk header
+ SLANG_RETURN_ON_FAIL(readChunk(stream, outHeader.chunk));
+ outHeader.subType = 0;
- stream->read(data.getBuffer(), payloadSize);
+ if (isListType(outHeader.chunk.type))
+ {
+ // Read the sub type
+ try
+ {
+ stream->read(&outHeader.subType, sizeof(RiffListHeader) - sizeof(RiffHeader));
+ }
+ catch (const IOException&)
+ {
+ return SLANG_FAIL;
+ }
+ }
- // Skip to the alignment
- uint32_t remaining = payloadSize & 3;
- if (remaining)
+ return SLANG_OK;
+}
+
+namespace { // anonymous
+
+struct DumpVisitor : public RiffContainer::Visitor
+{
+ typedef RiffContainer::Chunk Chunk;
+ typedef RiffContainer::ListChunk ListChunk;
+ typedef RiffContainer::DataChunk DataChunk;
+
+
+ // Visitor
+ virtual SlangResult enterList(ListChunk* list) SLANG_OVERRIDE
+ {
+ _dumpIndent();
+ // If it's the root it's 'riff'
+ _dumpRiffType(list == m_rootChunk ? RiffFourCC::kRiff : RiffFourCC::kList);
+ m_writer.put(" ");
+ _dumpRiffType(list->m_subType);
+ m_writer.put("\n");
+ m_indent++;
+ return SLANG_OK;
+ }
+ virtual SlangResult handleData(DataChunk* data) SLANG_OVERRIDE
+ {
+ _dumpIndent();
+ // Write out the name
+ _dumpRiffType(data->m_type);
+ m_writer.put(" ");
+
+
+ int hash = data->calcHash();
+
+ // We don't know in general what the contents is or means... but we can display a hash
+ HexDumpUtil::dump(uint32_t(hash), m_writer.getWriter());
+ m_writer.put(" ");
+
+ m_writer.put("\n");
+ return SLANG_OK;
+ }
+ virtual SlangResult leaveList(ListChunk* list) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(list);
+ m_indent--;
+ return SLANG_OK;
+ }
+
+ DumpVisitor(WriterHelper writer, Chunk* rootChunk) :
+ m_writer(writer),
+ m_indent(0),
+ m_rootChunk(rootChunk)
+ {
+ }
+
+ void _dumpIndent()
+ {
+ for (int i = 0; i < m_indent; ++i)
{
- stream->seek(SeekOrigin::Current, 4 - remaining);
+ m_writer.put(" ");
}
}
- catch (IOException&)
+ void _dumpRiffType(FourCC fourCC)
{
- return SLANG_FAIL;
+ char c[5];
+ for (int i = 0; i < 4; ++i)
+ {
+ c[i] = char(fourCC);
+ fourCC >>= 8;
+ }
+ c[4] = 0;
+ m_writer.put(c);
+ }
+
+ Chunk* m_rootChunk;
+
+ int m_indent;
+ WriterHelper m_writer;
+};
+
+}
+
+/* static */void RiffUtil::dump(RiffContainer::Chunk* chunk, WriterHelper writer)
+{
+ DumpVisitor visitor(writer, chunk);
+ chunk->visit(&visitor);
+}
+
+/* static */SlangResult RiffUtil::write(RiffContainer::ListChunk* list, bool isRoot, Stream* stream)
+{
+ RiffListHeader listHeader;
+
+ listHeader.chunk.type = isRoot ? RiffFourCC::kRiff : RiffFourCC::kList;
+ listHeader.chunk.size = uint32_t(list->m_payloadSize);
+ listHeader.subType = list->m_subType;
+
+ try
+ {
+ // Write the header
+ stream->write(&listHeader, sizeof(listHeader));
+
+ // Write the contained chunks
+ Chunk* chunk = list->m_containedChunks;
+ while (chunk)
+ {
+ switch (chunk->m_kind)
+ {
+ case Chunk::Kind::List:
+ {
+ auto listChunk = static_cast<ListChunk*>(chunk);
+ // It's a container
+ SLANG_RETURN_ON_FAIL(write(listChunk, false, stream));
+ break;
+ }
+ case Chunk::Kind::Data:
+ {
+ auto dataChunk = static_cast<DataChunk*>(chunk);
+
+ // Must be a regular chunk with data
+ RiffHeader chunkHeader;
+ chunkHeader.type = dataChunk->m_type;
+ chunkHeader.size = uint32_t(dataChunk->m_payloadSize);
+
+ stream->write(&chunkHeader, sizeof(chunkHeader));
+
+ RiffContainer::Data* data = dataChunk->m_dataList;
+ while (data)
+ {
+ stream->write(data->getPayload(), data->getSize());
+
+ // Next but of data
+ data = data->m_next;
+ }
+
+ // Need to write for alignment
+ const size_t remainingSize = getPadSize(dataChunk->m_payloadSize) - dataChunk->m_payloadSize;
+
+ if (remainingSize)
+ {
+ static const uint8_t trailing[kRiffPadSize] = { 0 };
+ stream->write(trailing, remainingSize);
+ }
+ }
+ default: break;
+ }
+
+ // Next
+ chunk = chunk->m_next;
+ }
+ }
+ catch (const IOException&)
+ {
+ return SLANG_FAIL;
}
return SLANG_OK;
}
+/* static */SlangResult RiffUtil::read(Stream* stream, RiffContainer& outContainer)
+{
+ typedef RiffContainer::ScopeChunk ScopeChunk;
+ typedef RiffContainer::ScopeChunk ScopeContainer;
+ outContainer.reset();
+
+ size_t remaining;
+ {
+ RiffListHeader header;
+
+ SLANG_RETURN_ON_FAIL(readHeader(stream, header));
+ if (!isListType(header.chunk.type))
+ {
+ return SLANG_FAIL;
+ }
+
+ remaining = getPadSize(header.chunk.size) - (sizeof(RiffListHeader) - sizeof(RiffHeader));
+ outContainer.startChunk(Chunk::Kind::List, header.subType);
+ }
+
+ List<size_t> remainingStack;
+ while (true)
+ {
+ // It must be the end
+ if (remaining == 0)
+ {
+ // If it's a container then we pop container
+ outContainer.endChunk();
+ if (remainingStack.getCount() <= 0)
+ {
+ break;
+ }
+
+ remaining = remainingStack.getLast();
+ remainingStack.removeLast();
+ }
+ else
+ {
+ RiffListHeader header;
+ SLANG_RETURN_ON_FAIL(readHeader(stream, header));
+
+ // The amount of data can't be larger than what remains
+ if (header.chunk.size > remaining)
+ {
+ return SLANG_FAIL;
+ }
+
+ if (header.chunk.type == RiffFourCC::kList)
+ {
+ if (header.chunk.size & kRiffPadMask)
+ {
+ SLANG_ASSERT(!"A list chunk can only have divisible by 2 size");
+ return SLANG_FAIL;
+ }
+
+ // Work out the pad size
+ const size_t padSize = getPadSize(header.chunk.size);
+
+ // Subtract the size of this chunk from remaining of the current chunk
+ remaining -= sizeof(RiffHeader) + padSize;
+ // Push it, for when we hit the end
+ remainingStack.add(remaining);
+
+ // Work out how much remains in this container
+ remaining = padSize - (sizeof(RiffListHeader) - sizeof(RiffHeader));
+
+ // Start a container
+ outContainer.startChunk(Chunk::Kind::List, header.subType);
+ }
+ else
+ {
+ ScopeChunk scopeChunk(&outContainer, Chunk::Kind::Data, header.chunk.type);
+ RiffContainer::Data* data = outContainer.addData(header.chunk.size);
+
+ size_t readSize;
+ SLANG_RETURN_ON_FAIL(readPayload(stream, header.chunk.size, data->getPayload(), readSize));
+
+ // All read sizes must end up aligned
+ SLANG_ASSERT((readSize & kRiffPadMask) == 0);
+
+ // Correct remaining
+ remaining -= sizeof(RiffHeader) + readSize;
+ }
+ }
+ }
+
+ return outContainer.isFullyConstructed() ? SLANG_OK : SLANG_FAIL;
+}
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! RiffContainer::Chunk !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+SlangResult RiffContainer::Chunk::visit(Visitor* visitor)
+{
+ switch (m_kind)
+ {
+ case Kind::Data:
+ {
+ return visitor->handleData(static_cast<DataChunk*>(this));
+ }
+ case Kind::List:
+ {
+ auto list = static_cast<ListChunk*>(this);
+ SLANG_RETURN_ON_FAIL(visitor->enterList(list));
+
+ Chunk* chunk = list->m_containedChunks;
+ while (chunk)
+ {
+ SLANG_RETURN_ON_FAIL(chunk->visit(visitor));
+
+ chunk = chunk->m_next;
+ }
+
+ SLANG_RETURN_ON_FAIL(visitor->leaveList(list));
+ return SLANG_OK;
+ }
+ default: return SLANG_FAIL;
+ }
+}
+
+SlangResult RiffContainer::Chunk::visitPreOrder(VisitorCallback callback, void* data)
+{
+ switch (m_kind)
+ {
+ case Kind::Data:
+ {
+ return callback(this, data);
+ }
+ case Kind::List:
+ {
+ auto list = static_cast<ListChunk*>(this);
+ // Do this containing node first
+ SLANG_RETURN_ON_FAIL(callback(this, data));
+
+ // Do the contents next
+ Chunk* chunk = list->m_containedChunks;
+ while (chunk)
+ {
+ SLANG_RETURN_ON_FAIL(chunk->visitPreOrder(callback, data));
+ chunk = chunk->m_next;
+ }
+ return SLANG_OK;
+ }
+ default: return SLANG_FAIL;
+ }
+}
+
+SlangResult RiffContainer::Chunk::visitPostOrder(VisitorCallback callback, void* data)
+{
+ switch (m_kind)
+ {
+ case Kind::Data:
+ {
+ return callback(this, data);
+ }
+ case Kind::List:
+ {
+ auto list = static_cast<ListChunk*>(this);
+
+ // Do the contents first
+ Chunk* chunk = list->m_containedChunks;
+ while (chunk)
+ {
+ SLANG_RETURN_ON_FAIL(chunk->visitPostOrder(callback, data));
+ chunk = chunk->m_next;
+ }
+ // Then the list node (so a post order)
+ SLANG_RETURN_ON_FAIL(callback(this, data));
+ return SLANG_OK;
+ }
+ default: return SLANG_FAIL;
+ }
+}
+
+size_t RiffContainer::Chunk::calcPayloadSize()
+{
+ switch (m_kind)
+ {
+ case Kind::Data: return static_cast<DataChunk*>(this)->calcPayloadSize();
+ case Kind::List: return static_cast<ListChunk*>(this)->calcPayloadSize();
+ default: return 0;
+ }
+}
+
+RiffContainer::Data* RiffContainer::Chunk::getSingleData() const
+{
+ return (m_kind == Kind::Data) ? static_cast<const DataChunk*>(this)->getSingleData(): nullptr;
+}
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!! RiffContainer::ListChunk !!!!!!!!!!!!!!!!!!!!!!
+
+size_t RiffContainer::ListChunk::calcPayloadSize()
+{
+ // Have to include the part of the header not taken up by the RiffHeader
+ size_t size = sizeof(RiffListHeader) - sizeof(RiffHeader);
+ Chunk* chunk = m_containedChunks;
+ while (chunk)
+ {
+ size_t chunkSize = chunk->m_payloadSize + sizeof(RiffHeader);
+ // Align the contained chunk size
+ size += RiffUtil::getPadSize(chunkSize);
+
+ chunk = chunk->m_next;
+ }
+ return size;
+}
+
+RiffContainer::Chunk* RiffContainer::ListChunk::findContained(FourCC type) const
+{
+ Chunk* chunk = m_containedChunks;
+ while (chunk)
+ {
+ const FourCC checkType = (chunk->m_kind == Chunk::Kind::Data) ?
+ static_cast<RiffContainer::DataChunk*>(chunk)->m_type :
+ static_cast<RiffContainer::ListChunk*>(chunk)->m_subType;
+ if (checkType == type)
+ {
+ return chunk;
+ }
+ chunk = chunk->m_next;
+ }
+ return nullptr;
+}
+
+RiffContainer::Data* RiffContainer::ListChunk::findContainedData(FourCC type) const
+{
+ Chunk* found = findContained(type);
+ if (found && found->m_kind == Kind::Data)
+ {
+ DataChunk* dataChunk = static_cast<DataChunk*>(found);
+ // Assumes that there is a single data chunk
+
+ Data* data = dataChunk->m_dataList;
+ if (data && data->m_next == nullptr)
+ {
+ return data;
+ }
+ }
+ return nullptr;
+}
+
+void* RiffContainer::ListChunk::findContainedData(FourCC type, size_t minSize) const
+{
+ Data* data = findContainedData(type);
+ return (data && data->m_size >= minSize) ? data->getPayload() : nullptr;
+}
+
+static RiffContainer::ListChunk* _findListRec(RiffContainer::ListChunk* list, FourCC subType)
+{
+ RiffContainer::Chunk* chunk = list->m_containedChunks;
+ while (chunk)
+ {
+ if (auto childList = as<RiffContainer::ListChunk>(chunk))
+ {
+ // Test if the child is the subtype, if so we are done
+ if (childList->m_subType == subType)
+ {
+ return childList;
+ }
+ auto found = _findListRec(childList, subType);
+ if (found)
+ {
+ return found;
+ }
+ }
+ chunk = chunk->m_next;
+ }
+ return nullptr;
+}
+
+/* static */RiffContainer::ListChunk* RiffContainer::ListChunk::findListRec(FourCC subType)
+{
+ return (m_subType == subType) ? this : _findListRec(this, subType);
+}
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!! RiffContainer::DataChunk !!!!!!!!!!!!!!!!!!!!!!
+
+RiffContainer::Data* RiffContainer::DataChunk::getSingleData() const
+{
+ if (m_kind == Kind::Data)
+ {
+ auto dataChunk = static_cast<const DataChunk*>(this);
+ Data* data = dataChunk->m_dataList;
+ if (data && data->m_next == nullptr)
+ {
+ return data;
+ }
+ }
+ return nullptr;
+}
+
+int RiffContainer::DataChunk::calcHash() const
+{
+ int hash = 0;
+
+ Data* data = m_dataList;
+ while (data)
+ {
+ // This is a little contrived (in that we don't use the function GetHashCode), but the
+ // reason to be careful is we want the same result however many Data blocks there are.
+ const char* buffer = (const char*)data->getPayload();
+ const size_t size = data->getSize();
+
+ for (size_t i = 0; i < size; ++i)
+ {
+ hash = int(buffer[i]) + (hash << 6) + (hash << 16) - hash;
+ }
+
+ data = data->m_next;
+ }
+
+ return hash;
+}
+
+size_t RiffContainer::DataChunk::calcPayloadSize() const
+{
+ size_t size = 0;
+ Data* data = m_dataList;
+ while (data)
+ {
+ size += data->getSize();
+ data = data->m_next;
+ }
+ return size;
+}
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! RiffContainer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+RiffContainer::RiffContainer() :
+ m_arena(4096)
+{
+ m_rootList = nullptr;
+ m_listChunk = nullptr;
+ m_dataChunk = nullptr;
+}
+
+void RiffContainer::reset()
+{
+ m_arena.reset();
+
+ m_rootList = nullptr;
+ m_listChunk = nullptr;
+ m_dataChunk = nullptr;
+}
+
+RiffContainer::ListChunk* RiffContainer::_newListChunk(FourCC subType)
+{
+ SLANG_ASSERT(!RiffUtil::isListType(subType));
+
+ ListChunk* chunk = (ListChunk*)m_arena.allocate(sizeof(ListChunk));
+ chunk->init(subType);
+ return chunk;
+}
+
+RiffContainer::DataChunk* RiffContainer::_newDataChunk(FourCC type)
+{
+ SLANG_ASSERT(!RiffUtil::isListType(type));
+
+ DataChunk* chunk = (DataChunk*)m_arena.allocate(sizeof(DataChunk));
+ chunk->init(type);
+ return chunk;
+}
+
+void RiffContainer::_addChunk(Chunk* chunk)
+{
+ if (m_listChunk)
+ {
+ chunk->m_parent = m_listChunk;
+ Chunk*& next = m_listChunk->m_endChunk ? m_listChunk->m_endChunk->m_next : m_listChunk->m_containedChunks;
+ SLANG_ASSERT(next == nullptr);
+ next = chunk;
+ m_listChunk->m_endChunk = chunk;
+ }
+}
+
+void RiffContainer::startChunk(Chunk::Kind kind, FourCC fourCC)
+{
+ SLANG_ASSERT(m_listChunk || m_rootList == nullptr);
+
+ switch (kind)
+ {
+ case Chunk::Kind::Data:
+ {
+ // We can only start a data chunk if we are in a container, and we can't already be in data chunk
+ SLANG_ASSERT(m_listChunk && m_dataChunk == nullptr);
+
+ DataChunk* chunk = _newDataChunk(fourCC);
+ _addChunk(chunk);
+ m_dataChunk = chunk;
+ break;
+ }
+ case Chunk::Kind::List:
+ {
+ // We can't be in a data chunk
+ SLANG_ASSERT(m_dataChunk == nullptr);
+
+ ListChunk* list = _newListChunk(fourCC);
+
+ // If this is the first, make it the root
+ if (!m_rootList)
+ {
+ m_rootList = list;
+ }
+
+ _addChunk(list);
+
+ m_listChunk = list;
+ break;
+ }
+ }
+}
+
+void RiffContainer::endChunk()
+{
+ size_t chunkPayloadSize;
+
+ // The chunk we are popping
+ Chunk* chunk = nullptr;
+
+ ListChunk* parent;
+ if (m_dataChunk)
+ {
+ chunk = m_dataChunk;
+
+ parent = m_dataChunk->m_parent;
+ chunkPayloadSize = m_dataChunk->m_payloadSize;
+
+ m_dataChunk = nullptr;
+ }
+ else
+ {
+ chunk = m_listChunk;
+
+ SLANG_ASSERT(m_listChunk && m_dataChunk == nullptr);
+ parent = m_listChunk->m_parent;
+ chunkPayloadSize = m_listChunk->m_payloadSize;
+ }
+
+ m_listChunk = parent;
+
+ if (parent)
+ {
+ // Fix the size taking into account padding bytes requirement
+ chunkPayloadSize = RiffUtil::getPadSize(chunkPayloadSize);
+ // Update the parents size
+ parent->m_payloadSize += sizeof(RiffHeader) + chunkPayloadSize;
+ }
+
+ // Check it's size seems ok
+ SLANG_ASSERT(isChunkOk(chunk));
+}
+
+RiffContainer::Data* RiffContainer::addData(size_t size)
+{
+ // We must be in a chunk
+ SLANG_ASSERT(m_dataChunk);
+
+ // Add current chunks data
+ m_dataChunk->m_payloadSize += size;
+
+ Data* data = (Data*)m_arena.allocate(sizeof(Data) + size);
+
+ data->m_next = nullptr;
+ data->m_size = size;
+
+ Data*& next = m_dataChunk->m_endData ? m_dataChunk->m_endData->m_next : m_dataChunk->m_dataList;
+ SLANG_ASSERT(next == nullptr);
+
+ // Add to linked list
+ next = data;
+ // Make this the new end
+ m_dataChunk->m_endData = data;
+ return data;
+}
+
+void RiffContainer::write(const void* inData, size_t size)
+{
+ auto data = addData(size);
+ ::memcpy(data->getPayload(), inData, size);
+}
+
+static SlangResult _isChunkOk(RiffContainer::Chunk* chunk, void* data)
+{
+ SLANG_UNUSED(data);
+ return chunk->calcPayloadSize() == chunk->m_payloadSize ? SLANG_OK : SLANG_FAIL;
+}
+
+/* static */bool RiffContainer::isChunkOk(Chunk* chunk)
+{
+ return SLANG_SUCCEEDED(chunk->visitPostOrder(&_isChunkOk, nullptr));
+}
+
+static SlangResult _calcAndSetSize(RiffContainer::Chunk* chunk, void* data)
+{
+ SLANG_UNUSED(data);
+ chunk->m_payloadSize = chunk->calcPayloadSize();
+ return SLANG_OK;
+}
+
+/* static */void RiffContainer::calcAndSetSize(Chunk* chunk)
+{
+ chunk->visitPostOrder(&_calcAndSetSize, nullptr);
+}
+
+
+
}
diff --git a/source/core/slang-riff.h b/source/core/slang-riff.h
index f681d6d68..9dfe655b6 100644
--- a/source/core/slang-riff.h
+++ b/source/core/slang-riff.h
@@ -1,8 +1,10 @@
#ifndef SLANG_RIFF_H
#define SLANG_RIFF_H
-#include "../core/slang-basic.h"
-#include "../core/slang-stream.h"
+#include "slang-basic.h"
+#include "slang-stream.h"
+#include "slang-memory-arena.h"
+#include "slang-writer.h"
namespace Slang
{
@@ -10,11 +12,37 @@ namespace Slang
// http://fileformats.archiveteam.org/wiki/RIFF
// http://www.fileformat.info/format/riff/egff.htm
-#define SLANG_FOUR_CC(c0, c1, c2, c3) ((uint32_t(c0) << 0) | (uint32_t(c1) << 8) | (uint32_t(c2) << 16) | (uint32_t(c3) << 24))
+typedef uint32_t FourCC;
+
+#define SLANG_FOUR_CC(c0, c1, c2, c3) FourCC((uint32_t(c0) << 0) | (uint32_t(c1) << 8) | (uint32_t(c2) << 16) | (uint32_t(c3) << 24))
+
+enum
+{
+ kRiffPadSize = 2, ///< We only align to 2 bytes
+ kRiffPadMask = kRiffPadSize - 1,
+};
+
+struct RiffHeader
+{
+ FourCC type; ///< The FourCC code that identifies this chunk
+ uint32_t size; ///< Size does *NOT* include the riff chunk size. The size can be byte sized, but on storage it will always be treated as aligned up by 4.
+};
+
+struct RiffListHeader
+{
+ RiffHeader chunk;
+ FourCC subType;
+ // This is then followed by the contained subchunk/s
+};
struct RiffFourCC
{
- static const uint32_t kRiff = SLANG_FOUR_CC('R', 'I', 'F', 'F');
+ /// A 'riff' is the high level file container. It is followed by a subtype and then the contained chunks.
+ static const FourCC kRiff = SLANG_FOUR_CC('R', 'I', 'F', 'F');
+ /// A list is the same as a 'riff' except can be placed anywhere in hierarchy.
+ static const FourCC kList = SLANG_FOUR_CC('L', 'I', 'S', 'T');
+private:
+ RiffFourCC() = delete;
};
// Follows semantic version rules
@@ -70,22 +98,256 @@ struct RiffSemanticVersion
RawType m_raw;
};
-struct RiffChunk
+/* A container for data in RIFF format. Holds the contents in memory.
+
+With the data held in memory allows for adding or removing chunks at will.
+
+A future implementation does not necessarily have to be backed by memory when construction,
+as data could be written to stream, and the chunk sizes written by seeking back over the file and setting the value.
+
+In normal usage the chunk sizes are calculated during construction. If the structure is changed, the sizes may
+need to be recalculated, before serialization.
+*/
+class RiffContainer
{
- uint32_t m_type; ///< The FourCC code that identifies this chunk
- uint32_t m_size; ///< Size does *NOT* include the riff chunk size. The size can be byte sized, but on storage it will always be treated as aligned up by 4.
+public:
+
+ struct ListChunk;
+ struct DataChunk;
+
+ struct Data
+ {
+ /// Get the payload
+ void* getPayload() { return (void*)(this + 1); }
+ size_t getSize() { return m_size; }
+
+ void init()
+ {
+ m_size = 0;
+ m_next = nullptr;
+ }
+
+ size_t m_size;
+ Data* m_next;
+ // Followed by the payload
+ };
+
+ struct Chunk;
+ typedef SlangResult(*VisitorCallback)(Chunk* chunk, void* data);
+
+ class Visitor;
+ struct Chunk
+ {
+ enum class Kind
+ {
+ List, ///< Strictly speaking this can be a 'LIST' or a 'RIFF' as they have the same structure
+ Data,
+ };
+
+ void init(Kind kind)
+ {
+ m_kind = kind;
+ m_payloadSize = 0;
+ m_next = nullptr;
+ m_parent = nullptr;
+ }
+
+ SlangResult visit(Visitor* visitor);
+ SlangResult visitPostOrder(VisitorCallback callback, void* data);
+ SlangResult visitPreOrder(VisitorCallback callback, void* data);
+
+ /// Returns a single data chunk
+ Data* getSingleData() const;
+
+ /// Get the unique type
+ FourCC getUniqueType() const;
+
+ size_t calcPayloadSize();
+
+ // TODO(JS):
+ // We might want to consider moving subType/type to the Chunk, because in practice they typically
+ // mean the same thing, and as it has been arranged, for list chunk the overall chunk type is implied
+ // by the kind.
+ Kind m_kind; ///< Kind of chunk
+ size_t m_payloadSize; ///< The payload size (ie does NOT include RiffChunk header).
+ Chunk* m_next; ///< Next chunk in this list
+ ListChunk* m_parent; ///< The chunk this belongs to
+ };
+
+ struct ListChunk : public Chunk
+ {
+ typedef Chunk Super;
+ SLANG_FORCE_INLINE static bool isType(const Chunk* chunk) { return chunk->m_kind == Kind::List; }
+
+ void init(FourCC subType)
+ {
+ Super::init(Kind::List);
+ m_subType = subType;
+ m_containedChunks = nullptr;
+ m_endChunk = nullptr;
+
+ m_payloadSize = uint32_t(sizeof(RiffListHeader) - sizeof(RiffHeader));
+ }
+
+ /// Finds chunk (list or data) that matches type. For List/Riff, type is the subtype
+ Chunk* findContained(FourCC type) const;
+
+ void* findContainedData(FourCC type, size_t minSize) const;
+
+ /// Finds the contained data. NOTE! Assumes that there is only as single data block, and will return nullptr if there is not
+ Data* findContainedData(FourCC type) const;
+
+ template <typename T>
+ T* findContainedData(FourCC type) const { return (T*)findContainedData(type, sizeof(T)); }
+
+ /// Find the list (including self) that matches subtype recursively
+ ListChunk* findListRec(FourCC subType);
+
+ /// NOTE! Assumes all contained chunks have correct payload sizes
+ size_t calcPayloadSize();
+
+ FourCC m_subType; ///< The subtype of this contained
+ Chunk* m_containedChunks; ///< The contained chunks
+ Chunk* m_endChunk; ///< The last chunk (only set when pushed, and used when popped)
+ };
+
+ struct DataChunk : public Chunk
+ {
+ typedef Chunk Super;
+ SLANG_FORCE_INLINE static bool isType(const Chunk* chunk) { return chunk->m_kind == Kind::Data; }
+
+ /// Calculate a hash (not necessarily very fast)
+ int calcHash() const;
+ /// Calculate the payload size
+ size_t calcPayloadSize() const;
+
+ /// Get single data payload.
+ Data* getSingleData() const;
+
+ void init(FourCC type)
+ {
+ Super::init(Kind::Data);
+ m_type = type;
+ m_dataList = nullptr;
+ m_endData = nullptr;
+ }
+
+ FourCC m_type; ///< Type of chunk
+ Data* m_dataList; ///< List of 0 or more data items
+ Data* m_endData; ///< The last data point
+ };
+
+ class ScopeChunk
+ {
+ public:
+ ScopeChunk(RiffContainer* container, Chunk::Kind kind, FourCC fourCC) :
+ m_container(container)
+ {
+ container->startChunk(kind, fourCC);
+ }
+ ~ScopeChunk()
+ {
+ m_container->endChunk();
+ }
+ private:
+ RiffContainer* m_container;
+ };
+
+ class Visitor
+ {
+ public:
+ virtual SlangResult enterList(ListChunk* list) = 0;
+ virtual SlangResult handleData(DataChunk* data) = 0;
+ virtual SlangResult leaveList(ListChunk* list) = 0;
+ };
+
+ /// Start a chunk
+ void startChunk(Chunk::Kind kind, FourCC type);
+
+ /// Write data into a chunk (can only be inside a Kind::Data)
+ void write(const void* data, size_t size);
+ Data* addData(size_t size);
+
+ /// End a chunk
+ void endChunk();
+
+ /// Get the root
+ ListChunk* getRoot() const { return m_rootList; }
+
+ /// Reset the container
+ void reset();
+
+ /// true if has a root container, and nothing remains open
+ bool isFullyConstructed() { return m_rootList && m_listChunk == nullptr && m_dataChunk == nullptr; }
+
+ /// The if the list and sublists appear correct
+ static bool isChunkOk(Chunk* chunk);
+
+ /// Traverses over chunk hierarchy and sets the sizes
+ static void calcAndSetSize(Chunk* chunk);
+
+ /// Ctor
+ RiffContainer();
+
+protected:
+ void _addChunk(Chunk* chunk);
+ ListChunk* _newListChunk(FourCC subType);
+ DataChunk* _newDataChunk(FourCC type);
+
+ ListChunk* m_rootList; ///< Root list
+
+ ListChunk* m_listChunk;
+ DataChunk* m_dataChunk;
+
+ MemoryArena m_arena;
};
+// -----------------------------------------------------------------------------
+SLANG_FORCE_INLINE FourCC RiffContainer::Chunk::getUniqueType() const
+{
+ return (m_kind == Kind::Data) ? static_cast<const DataChunk*>(this)->m_type : static_cast<const ListChunk*>(this)->m_subType;
+}
+// -----------------------------------------------------------------------------
+template <typename T>
+T* as(RiffContainer::Chunk* chunk)
+{
+ return T::isType(chunk) ? static_cast<T*>(chunk) : nullptr;
+}
+
+
struct RiffUtil
{
- static int64_t calcChunkTotalSize(const RiffChunk& chunk);
+ typedef RiffContainer::Chunk Chunk;
+ typedef RiffContainer::ListChunk ListChunk;
+ typedef RiffContainer::DataChunk DataChunk;
+
+ static int64_t calcChunkTotalSize(const RiffHeader& chunk);
+
+ static SlangResult skip(const RiffHeader& chunk, Stream* stream, int64_t* remainingBytesInOut);
+
+ static SlangResult readChunk(Stream* stream, RiffHeader& outChunk);
+
+ static SlangResult writeData(const RiffHeader* header, size_t headerSize, const void* payload, size_t payloadSize, Stream* out);
+ static SlangResult readData(Stream* stream, RiffHeader* outHeader, size_t headerSize, List<uint8_t>& data);
+
+ static SlangResult readPayload(Stream* stream, size_t size, void* outData, size_t& outReadSize);
+
+ /// Read a header. Handles special case of list/riff types
+ static SlangResult readHeader(Stream* stream, RiffListHeader& outHeader);
+
+ /// True if the type is a container type
+ static bool isListType(FourCC type) { return type == RiffFourCC::kRiff || type == RiffFourCC::kList; }
- static SlangResult skip(const RiffChunk& chunk, Stream* stream, int64_t* remainingBytesInOut);
+ /// Dump the chunk structure
+ static void dump(Chunk* chunk, WriterHelper writer);
- static SlangResult readChunk(Stream* stream, RiffChunk& outChunk);
+ /// Get the size taking into account padding
+ static size_t getPadSize(size_t in) { return (in + kRiffPadMask) & ~size_t(kRiffPadMask); }
- static SlangResult writeData(const RiffChunk* header, size_t headerSize, const void* payload, size_t payloadSize, Stream* out);
- static SlangResult readData(Stream* stream, RiffChunk* outHeader, size_t headerSize, List<uint8_t>& data);
+ /// Write a container and contents to a stream
+ static SlangResult write(ListChunk* container, bool isRoot, Stream* stream);
+ /// Read the stream into the container
+ static SlangResult read(Stream* stream, RiffContainer& outContainer);
};
}
diff --git a/source/core/slang-stream.cpp b/source/core/slang-stream.cpp
index b5eef1d9b..bfbe43b0c 100644
--- a/source/core/slang-stream.cpp
+++ b/source/core/slang-stream.cpp
@@ -181,9 +181,10 @@ void FileStream::seek(SeekOrigin origin, Int64 offset)
throw IOException("FileStream seek failed.");
}
}
-Int64 FileStream::read(void* buffer, Int64 length)
+
+size_t FileStream::read(void* buffer, size_t length)
{
- auto bytes = fread_s(buffer, (size_t)length, 1, (size_t)length, m_handle);
+ auto bytes = fread_s(buffer, length, 1, length, m_handle);
if (bytes == 0 && length > 0)
{
if (!feof(m_handle))
@@ -192,25 +193,29 @@ Int64 FileStream::read(void* buffer, Int64 length)
throw EndOfStreamException("End of file is reached.");
m_endReached = true;
}
- return (int)bytes;
+ return bytes;
}
-Int64 FileStream::write(const void* buffer, Int64 length)
+
+size_t FileStream::write(const void* buffer, size_t length)
{
- auto bytes = (Int64)fwrite(buffer, 1, (size_t)length, m_handle);
+ auto bytes = fwrite(buffer, 1, length, m_handle);
if (bytes < length)
{
throw IOException("FileStream write failed.");
}
return bytes;
}
+
bool FileStream::canRead()
{
return ((int)m_fileAccess & (int)FileAccess::Read) != 0;
}
+
bool FileStream::canWrite()
{
return ((int)m_fileAccess & (int)FileAccess::Write) != 0;
}
+
void FileStream::close()
{
if (m_handle)
@@ -219,6 +224,7 @@ void FileStream::close()
m_handle = 0;
}
}
+
bool FileStream::isEnd()
{
return m_endReached;
@@ -251,18 +257,17 @@ void MemoryStreamBase::seek(SeekOrigin origin, Int64 offset)
pos = (pos < 0) ? 0 : pos;
pos = (pos > Int64(m_contentsSize)) ? Int64(m_contentsSize) : pos;
- m_position = UInt(pos);
+ m_position = ptrdiff_t(pos);
}
-Int64 MemoryStreamBase::read(void* buffer, Int64 length)
+size_t MemoryStreamBase::read(void* buffer, size_t length)
{
if (!canRead())
{
throw IOException("Cannot read this stream.");
}
- const Int64 maxRead = Int64(m_contentsSize - m_position);
-
+ const size_t maxRead = size_t(m_contentsSize - m_position);
if (maxRead == 0 && length > 0)
{
m_atEnd = true;
@@ -271,14 +276,14 @@ Int64 MemoryStreamBase::read(void* buffer, Int64 length)
length = length > maxRead ? maxRead : length;
- ::memcpy(buffer, m_contents + m_position, size_t(length));
- m_position += UInt(length);
+ ::memcpy(buffer, m_contents + m_position, length);
+ m_position += ptrdiff_t(length);
return maxRead;
}
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!! OwnedMemoryStream !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-Int64 OwnedMemoryStream::write(const void * buffer, Int64 length)
+size_t OwnedMemoryStream::write(const void * buffer, size_t length)
{
if (!canWrite())
{
@@ -287,11 +292,11 @@ Int64 OwnedMemoryStream::write(const void * buffer, Int64 length)
if (m_position == m_ownedContents.getCount())
{
- m_ownedContents.addRange((const uint8_t*)buffer, UInt(length));
+ m_ownedContents.addRange((const uint8_t*)buffer, Index(length));
}
else
{
- m_ownedContents.insertRange(m_position, (const uint8_t*)buffer, UInt(length));
+ m_ownedContents.insertRange(m_position, (const uint8_t*)buffer, Index(length));
}
m_contents = m_ownedContents.getBuffer();
diff --git a/source/core/slang-stream.h b/source/core/slang-stream.h
index 8b8a6decf..212437ab5 100644
--- a/source/core/slang-stream.h
+++ b/source/core/slang-stream.h
@@ -39,8 +39,8 @@ public:
virtual ~Stream() {}
virtual Int64 getPosition()=0;
virtual void seek(SeekOrigin origin, Int64 offset)=0;
- virtual Int64 read(void * buffer, Int64 length) = 0;
- virtual Int64 write(const void * buffer, Int64 length) = 0;
+ virtual size_t read(void * buffer, size_t length) = 0;
+ virtual size_t write(const void * buffer, size_t length) = 0;
virtual bool isEnd() = 0;
virtual bool canRead() = 0;
virtual bool canWrite() = 0;
@@ -70,8 +70,8 @@ public:
virtual Int64 getPosition() SLANG_OVERRIDE { return m_position; }
virtual void seek(SeekOrigin origin, Int64 offset) SLANG_OVERRIDE;
- virtual Int64 read(void * buffer, Int64 length) SLANG_OVERRIDE;
- virtual Int64 write(const void * buffer, Int64 length) SLANG_OVERRIDE { SLANG_UNUSED(buffer); SLANG_UNUSED(length); return 0; }
+ virtual size_t read(void * buffer, size_t length) SLANG_OVERRIDE;
+ virtual size_t write(const void * buffer, size_t length) SLANG_OVERRIDE { SLANG_UNUSED(buffer); SLANG_UNUSED(length); return 0; }
virtual bool isEnd() SLANG_OVERRIDE { return m_atEnd; }
virtual bool canRead() SLANG_OVERRIDE { return (int(m_access) & int(FileAccess::Read)) != 0; }
virtual bool canWrite() SLANG_OVERRIDE { return (int(m_access) & int(FileAccess::Write)) != 0; }
@@ -117,7 +117,7 @@ class OwnedMemoryStream : public MemoryStreamBase
public:
typedef MemoryStreamBase Super;
- virtual Int64 write(const void* buffer, Int64 length) SLANG_OVERRIDE;
+ virtual size_t write(const void* buffer, size_t length) SLANG_OVERRIDE;
/// Set the contents
void setContent(const void* contents, size_t contentsSize)
@@ -150,8 +150,8 @@ public:
// Stream interface
virtual Int64 getPosition();
virtual void seek(SeekOrigin origin, Int64 offset);
- virtual Int64 read(void* buffer, Int64 length);
- virtual Int64 write(const void* buffer, Int64 length);
+ virtual size_t read(void* buffer, size_t length);
+ virtual size_t write(const void* buffer, size_t length);
virtual bool canRead();
virtual bool canWrite();
virtual void close();