Skip to content

Commit

Permalink
Add new helper class for no-copy istream
Browse files Browse the repository at this point in the history
  • Loading branch information
Yankes committed Dec 10, 2023
1 parent f2f1af2 commit 4daad85
Show file tree
Hide file tree
Showing 2 changed files with 228 additions and 0 deletions.
95 changes: 95 additions & 0 deletions src/Engine/CrossPlatform.cpp
Expand Up @@ -1822,6 +1822,101 @@ static auto dummy = ([]

return 0;
})();

static auto dummyRawFile = ([]
{
{
char text[] = "test";
StreamData raw(RawData{text, std::strlen(text), +[](void*){}});

assert(raw.get() == 't');
assert(raw.get() == 'e');
assert(raw.get() == 's');
assert(raw.get() == 't');
assert(raw.get() == std::char_traits<char>::eof());
}

{
char text[] = "test123";
StreamData raw(RawData{text, std::strlen(text), +[](void*){}});

char dummy1[10] = { };
assert(raw.read(dummy1, 4) && std::strcmp(dummy1, "test") == 0);

char dummy2[10] = { };
assert(raw.read(dummy2, 3) && std::strcmp(dummy2, "123") == 0);

}

{
char text[] = "test123";
StreamData raw(RawData{text, std::strlen(text), +[](void*){}});

char dummy1[10] = { };
assert(!raw.read(dummy1, 10) && std::strcmp(dummy1, "test123") == 0);
}

{
char text[] = "test123";
StreamData raw(RawData{text, std::strlen(text), +[](void*){}});

raw.seekg(0, std::ios::end);
std::streamoff end = raw.tellg();
raw.seekg(0, std::ios::beg);
std::streamoff begin = raw.tellg();

assert(end-begin == (int)std::strlen(text));
}

{
static int calledDelete = 0;
char text[] = "test123";
StreamData raw(RawData{text, std::strlen(text), +[](void*){ ++calledDelete; }});

assert(raw.get() == 't');

raw.extractRawData();

assert(raw.get() == std::char_traits<char>::eof());
assert(calledDelete == 1);
}

{
static int calledDelete = 0;
char text[] = "0123";
StreamData raw(RawData{text, std::strlen(text), +[](void*){ ++calledDelete; }});


assert(raw.get() == '0');

StreamData raw2 = std::move(raw);

assert(raw.get() == std::char_traits<char>::eof());
assert(!!raw2);
assert(raw2.get() == '1');

raw = std::move(raw2);

assert(raw.get() == '2');
assert(raw2.get() == std::char_traits<char>::eof());

assert(raw.get() == '3');
assert(!!raw);
assert(raw.get() == std::char_traits<char>::eof());
assert(!raw);

{
StreamData raw3 = std::move(raw);
assert(!raw3);

assert(calledDelete == 0);
}

assert(calledDelete == 1);
}

return 0;
})();
#endif


Expand Down
133 changes: 133 additions & 0 deletions src/Engine/CrossPlatform.h
Expand Up @@ -27,6 +27,139 @@
namespace OpenXcom
{

using RawDataDeleteFun = void(*)(void*);

/**
* Unique pointer with size to raw data buffer.
*/
class RawData
{
std::unique_ptr<void, RawDataDeleteFun> _data;
std::size_t _size;


public:

/// Default constructor.
RawData() : _data{ nullptr, +[](void*){} }, _size{ }
{

}

/// Create data from pointer and size.
RawData(void* data, std::size_t size, RawDataDeleteFun del) : _data{ data, del }, _size{ size }
{

}

/// Move constructor.
RawData(RawData&& d) : RawData()
{
*this = std::move(d);
}

/// Move assignment.
RawData& operator=(RawData&& d)
{
_data = std::exchange(d._data, std::unique_ptr<void, RawDataDeleteFun>{ nullptr, +[](void*){} });
_size = std::exchange(d._size, 0u);

return *this;
}


/// Size of buffer.
std::size_t size() const { return _size; }

/// Data of buffer.
const void* data() const { return _data.get(); }

/// Data of buffer.
void* data() { return _data.get(); }
};

/**
* Stream to raw data buffer, owning its data buffer.
*/
class StreamData : public std::istream, private std::streambuf
{
RawData _data;


public:

/// Default constructor.
StreamData()
{
this->rdbuf(this);
}

/// Constructor from raw data.
StreamData(RawData data) : StreamData()
{
insertRawData(std::move(data));
}

/// Move constructor, move whole state to new object.
StreamData(StreamData&& f) : StreamData()
{
*this = std::move(f);
}

/// Move assignment, move whole state to new object.
StreamData& operator=(StreamData&& f)
{
auto state = f.rdstate();
auto pos = f.tellg();
insertRawData(f.extractRawData());
this->seekg(pos);
this->clear(state);

return *this;
}


/// Move out raw data from object.
RawData extractRawData()
{
this->setg(nullptr, nullptr, nullptr);
return std::move(_data);
}

/// Insert raw data into object.
void insertRawData(RawData data)
{
_data = std::move(data);
this->setg((char*)_data.data(), (char*)_data.data(), (char*)_data.data() + _data.size());
this->clear();
}


protected:

/// https://stackoverflow.com/questions/35066207/how-to-implement-custom-stdstreambufs-seekoff
virtual std::streambuf::pos_type seekoff(
std::streambuf::off_type off, std::ios_base::seekdir dir,
std::ios_base::openmode) override
{
if (dir == std::ios_base::cur)
gbump(off);
else if (dir == std::ios_base::end)
setg(eback(), egptr() + off, egptr());
else if (dir == std::ios_base::beg)
setg(eback(), eback() + off, egptr());
return gptr() - eback();
}

/// https://stackoverflow.com/a/46069245/1938348
virtual std::streambuf::pos_type seekpos(
std::streambuf::pos_type pos,
std::ios_base::openmode which) override
{
return seekoff(pos - std::streambuf::pos_type(std::streambuf::off_type(0)), std::ios_base::beg, which);
}
};

/**
* Generic purpose functions that need different
* implementations for different platforms.
Expand Down

0 comments on commit 4daad85

Please sign in to comment.