-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #263 from OutpostUniverse/addDynamicStreamWriter
Add DynamicMemoryWriter class
- Loading branch information
Showing
9 changed files
with
193 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
#include "DynamicMemoryWriter.h" | ||
#include <cstring> // memcpy | ||
#include <limits> | ||
#include <stdexcept> | ||
|
||
namespace Stream | ||
{ | ||
DynamicMemoryWriter::DynamicMemoryWriter() { | ||
} | ||
|
||
DynamicMemoryWriter::DynamicMemoryWriter(std::size_t preallocateSize) { | ||
streamBuffer.reserve(preallocateSize); | ||
} | ||
|
||
void DynamicMemoryWriter::WriteImplementation(const void* buffer, std::size_t size) | ||
{ | ||
auto streamSize = streamBuffer.size(); | ||
streamBuffer.resize(streamSize + size); | ||
std::memcpy(streamBuffer.data() + streamSize, buffer, size); | ||
} | ||
|
||
uint64_t DynamicMemoryWriter::Length() | ||
{ | ||
return streamBuffer.size(); | ||
} | ||
|
||
uint64_t DynamicMemoryWriter::Position() | ||
{ | ||
return streamBuffer.size(); | ||
} | ||
|
||
void DynamicMemoryWriter::SeekForward(uint64_t offset) | ||
{ | ||
auto streamSize = streamBuffer.size(); | ||
if (offset > std::numeric_limits<SizeType>::max() - streamSize) { | ||
throw std::runtime_error("Seek forward beyond stream size limit"); | ||
} | ||
// Zero fill to new size | ||
streamBuffer.resize(static_cast<SizeType>(streamSize + offset), 0); | ||
} | ||
|
||
void DynamicMemoryWriter::SeekBackward(uint64_t offset) | ||
{ | ||
auto streamSize = streamBuffer.size(); | ||
if (offset > streamSize) { | ||
throw std::runtime_error("Seek backward before beginning of stream"); | ||
} | ||
streamBuffer.resize(static_cast<SizeType>(streamSize - offset), 0); | ||
} | ||
|
||
void DynamicMemoryWriter::Seek(uint64_t offset) | ||
{ | ||
streamBuffer.resize(static_cast<SizeType>(offset), 0); | ||
} | ||
|
||
MemoryReader DynamicMemoryWriter::GetReader() { | ||
return MemoryReader(streamBuffer.data(), streamBuffer.size()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
#pragma once | ||
|
||
#include "BidirectionalWriter.h" | ||
#include "MemoryReader.h" | ||
#include <cstddef> | ||
#include <cstdint> | ||
#include <vector> | ||
|
||
namespace Stream | ||
{ | ||
/** | ||
* \brief Memory backed stream, which grows dynamically in size as data is added | ||
* | ||
* This stream can be used as a destination when the output size is not known exactly ahead of time. | ||
* Written data can later be accessed by calling GetReader to retrieve a MemoryReader for the written data. | ||
* The DynamicMemoryWriter is particularly useful when writing test code for stream serializaation, | ||
* as it does not need to write to disk, nor cleanup temporary files, during round-trip testing. | ||
* | ||
* An internal memory buffer is managed by DynamicMemoryWriter, which grows as needed to accommodate new data. | ||
* This is in contrast to MemoryWriter, which is a view to a non-owned pre-set sized buffer, which can not grow. | ||
* | ||
* \note Adding additional data to the stream after calling GetReader invalidates existing readers. | ||
* (They may be left pointing at old memory after a re-allocation). | ||
*/ | ||
class DynamicMemoryWriter : public BidirectionalWriter | ||
{ | ||
public: | ||
DynamicMemoryWriter(); | ||
DynamicMemoryWriter(std::size_t preallocateSize); | ||
|
||
uint64_t Length() override; | ||
uint64_t Position() override; | ||
|
||
// SeekForward will set a new stream size and 0 fill the buffer gap | ||
void SeekForward(uint64_t offset) override; | ||
// SeekBackward will set a new stream size and truncate the stream | ||
void SeekBackward(uint64_t offset) override; | ||
// Seek will set a new stream size (either by 0 filling or by truncating) | ||
void Seek(uint64_t position) override; | ||
|
||
// Get read access to the written data | ||
MemoryReader GetReader(); | ||
|
||
protected: | ||
void WriteImplementation(const void* buffer, std::size_t size) override; | ||
|
||
private: | ||
std::vector<uint8_t> streamBuffer; | ||
|
||
using SizeType = decltype(streamBuffer)::size_type; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
#include "Stream/DynamicMemoryWriter.h" | ||
#include <gtest/gtest.h> | ||
#include <cstdint> | ||
#include <array> | ||
|
||
|
||
TEST(DynamicMemoryWriter, LengthAutoExpands) { | ||
const std::array<uint8_t, 4> data = {0, 1, 2, 3}; | ||
Stream::DynamicMemoryWriter writer; | ||
|
||
// Initially stream has 0 length | ||
EXPECT_EQ(0, writer.Length()); | ||
|
||
// Length grows as data is added | ||
EXPECT_NO_THROW(writer.Write(data)); | ||
EXPECT_EQ(4, writer.Length()); | ||
|
||
// Seeking forward past the end expands the stream | ||
EXPECT_NO_THROW(writer.SeekForward(1)); | ||
EXPECT_EQ(5, writer.Length()); | ||
|
||
// Additional data is added after the expansion gap | ||
EXPECT_NO_THROW(writer.Write(data)); | ||
EXPECT_EQ(9, writer.Length()); | ||
} | ||
|
||
TEST(DynamicMemoryWriter, ReadsBackStoredData) { | ||
const std::array<uint8_t, 4> data = {0, 1, 2, 3}; | ||
Stream::DynamicMemoryWriter writer; | ||
|
||
// Add data to stream | ||
EXPECT_NO_THROW(writer.Write(data)); | ||
// Seeking forward 0 fills | ||
EXPECT_NO_THROW(writer.SeekForward(1)); | ||
// Write more data after the gap | ||
EXPECT_NO_THROW(writer.Write(data)); | ||
|
||
// Get stream to read back data | ||
auto reader = writer.GetReader(); | ||
std::array<uint8_t, 4> readData; | ||
uint8_t readDataByte; | ||
|
||
// Read data back, and ensure it matches up | ||
EXPECT_NO_THROW(reader.Read(readData)); | ||
EXPECT_EQ(data, readData); | ||
EXPECT_NO_THROW(reader.Read(readDataByte)); | ||
EXPECT_EQ(0, readDataByte); | ||
EXPECT_NO_THROW(reader.Read(readData)); | ||
EXPECT_EQ(data, readData); | ||
} |