Skip to content

Commit

Permalink
Implements a span stream, using a external buffer provided at constru…
Browse files Browse the repository at this point in the history
…ction time
  • Loading branch information
guillaumeblanc committed Nov 22, 2023
1 parent 43136d0 commit 1fb71ad
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 76 deletions.
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
Release version next
----------------------

* Library
- [base] Implements a span stream, using a external buffer provided at construction time.

Release version 0.14.2
----------------------

Expand Down
81 changes: 48 additions & 33 deletions include/ozz/base/io/stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@

#include <cstddef>

#include "ozz/base/containers/vector.h"
#include "ozz/base/platform.h"
#include "ozz/base/span.h"

namespace ozz {
namespace io {
Expand Down Expand Up @@ -103,86 +105,99 @@ class OZZ_BASE_DLL File : public Stream {
explicit File(void* _file);

// Close the file if it is opened.
virtual ~File();
virtual ~File() override;

// Close the file if it is opened.
void Close();

// See Stream::opened for details.
virtual bool opened() const;
virtual bool opened() const override;

// See Stream::Read for details.
virtual size_t Read(void* _buffer, size_t _size);
virtual size_t Read(void* _buffer, size_t _size) override;

// See Stream::Write for details.
virtual size_t Write(const void* _buffer, size_t _size);
virtual size_t Write(const void* _buffer, size_t _size) override;

// See Stream::Seek for details.
virtual int Seek(int _offset, Origin _origin);
virtual int Seek(int _offset, Origin _origin) override;

// See Stream::Tell for details.
virtual int Tell() const;
virtual int Tell() const override;

// See Stream::Tell for details.
virtual size_t Size() const;
virtual size_t Size() const override;

private:
// The CRT file pointer.
void* file_;
};

// Implements an in-memory Stream. Allows to use a memory buffer as a Stream.
// The opening mode is equivalent to fopen w+b (binary read/write).
class OZZ_BASE_DLL MemoryStream : public Stream {
// Implements an in-memory Stream. Buffer is provided at construction time and
// can be reallocated if overriden. The opening mode is equivalent to fopen w+b
// (binary read/write).
class OZZ_BASE_DLL SpanStream : public Stream {
public:
// Construct an empty memory stream opened in w+b mode.
MemoryStream();
SpanStream(ozz::span<byte> _buffer);

// Closes the stream and deallocates memory buffer.
virtual ~MemoryStream();
virtual ~SpanStream();

// See Stream::opened for details.
virtual bool opened() const;
virtual bool opened() const override;

// See Stream::Read for details.
virtual size_t Read(void* _buffer, size_t _size);
virtual size_t Read(void* _buffer, size_t _size) override;

// See Stream::Write for details.
virtual size_t Write(const void* _buffer, size_t _size);
virtual size_t Write(const void* _buffer, size_t _size) override;

// See Stream::Seek for details.
virtual int Seek(int _offset, Origin _origin);
virtual int Seek(int _offset, Origin _origin) override;

// See Stream::Tell for details.
virtual int Tell() const;
virtual int Tell() const override;

// See Stream::Tell for details.
virtual size_t Size() const;
virtual size_t Size() const override;

private:
// Resizes buffers size to _size bytes. If _size is less than the actual
// buffer size, then it remains unchanged.
// Returns true if the buffer can contains _size bytes.
bool Resize(size_t _size);

// Size of the buffer increment.
static const size_t kBufferSizeIncrement;

// Maximum stream size.
static const size_t kMaxSize;

// Buffer of data.
byte* buffer_;
// Asked to resizes buffers _size bytes.
// Returns true if the buffer can contains _size bytes.
virtual bool Resize(size_t _size, ozz::span<byte>& _buffer);

// The size of the buffer, which is greater or equal to the size of the data
// it contains (end_).
size_t alloc_size_;
// Buffer of data.
ozz::span<byte> buffer_;

// The effective size of the data in the buffer.
int end_;
int end_ = 0;

// The cursor position in the buffer of data.
int tell_;
int tell_ = 0;
};

// Implements a self-allocated in-memory Stream.
// The opening mode is equivalent to fopen w+b (binary read/write).
class OZZ_BASE_DLL MemoryStream : public SpanStream {
public:
// Construct an empty memory stream opened in w+b mode.
MemoryStream();

// Closes the stream and deallocates memory buffer.
virtual ~MemoryStream();

private:
// Resizes buffers size to _size bytes. If _size is less than the actual
// buffer size, then it remains unchanged.
// Returns true if the buffer can contains _size bytes.
virtual bool Resize(size_t _size, ozz::span<byte>& _buffer) override;

// Buffer storage
ozz::vector<byte> allocation_;
};
} // namespace io
} // namespace ozz
Expand Down
74 changes: 35 additions & 39 deletions src/base/io/stream.cc
Original file line number Diff line number Diff line change
Expand Up @@ -107,34 +107,31 @@ size_t File::Size() const {
return static_cast<size_t>(end);
}

// Starts MemoryStream implementation.
const size_t MemoryStream::kBufferSizeIncrement = 16 << 10;
const size_t MemoryStream::kMaxSize = std::numeric_limits<int>::max();
// SpanStream implementation.
// ----------------------------------------------------------------------------

MemoryStream::MemoryStream()
: buffer_(nullptr), alloc_size_(0), end_(0), tell_(0) {}
const size_t SpanStream::kMaxSize = std::numeric_limits<int>::max();

MemoryStream::~MemoryStream() {
ozz::memory::default_allocator()->Deallocate(buffer_);
buffer_ = nullptr;
}
SpanStream::SpanStream(ozz::span<byte> _buffer) : buffer_(_buffer) {}

SpanStream::~SpanStream() {}

bool MemoryStream::opened() const { return true; }
bool SpanStream::opened() const { return true; }

size_t MemoryStream::Read(void* _buffer, size_t _size) {
size_t SpanStream::Read(void* _buffer, size_t _size) {
// A read cannot set file position beyond the end of the file.
// A read cannot exceed the maximum Stream size.
if (tell_ > end_ || _size > kMaxSize) {
return 0;
}

const int read_size = math::Min(end_ - tell_, static_cast<int>(_size));
std::memcpy(_buffer, buffer_ + tell_, read_size);
std::memcpy(_buffer, buffer_.data() + tell_, read_size);
tell_ += read_size;
return read_size;
}

size_t MemoryStream::Write(const void* _buffer, size_t _size) {
size_t SpanStream::Write(const void* _buffer, size_t _size) {
if (_size > kMaxSize || tell_ > static_cast<int>(kMaxSize - _size)) {
// A write cannot exceed the maximum Stream size.
return 0;
Expand All @@ -144,27 +141,27 @@ size_t MemoryStream::Write(const void* _buffer, size_t _size) {
// beyond the end of existing data in the file. If data is later written at
// this point, subsequent reads of data in the gap shall return bytes with
// the value 0 until data is actually written into the gap.
if (!Resize(tell_)) {
if (!Resize(tell_, buffer_)) {
return 0;
}
// Fills the gap with 0's.
const size_t gap = tell_ - end_;
std::memset(buffer_ + end_, 0, gap);
std::memset(buffer_.data() + end_, 0, gap);
end_ = tell_;
}

const int size = static_cast<int>(_size);
const int tell_end = tell_ + size;
if (Resize(tell_end)) {
if (Resize(tell_end, buffer_)) {
end_ = math::Max(tell_end, end_);
std::memcpy(buffer_ + tell_, _buffer, _size);
std::memcpy(buffer_.data() + tell_, _buffer, _size);
tell_ += size;
return _size;
}
return 0;
}

int MemoryStream::Seek(int _offset, Origin _origin) {
int SpanStream::Seek(int _offset, Origin _origin) {
int origin;
switch (_origin) {
case kCurrent:
Expand Down Expand Up @@ -192,28 +189,27 @@ int MemoryStream::Seek(int _offset, Origin _origin) {
return 0;
}

int MemoryStream::Tell() const { return tell_; }

size_t MemoryStream::Size() const { return static_cast<size_t>(end_); }

bool MemoryStream::Resize(size_t _size) {
if (_size > alloc_size_) {
// Resize to the next multiple of kBufferSizeIncrement, requires
// kBufferSizeIncrement to be a power of 2.
static_assert(
(MemoryStream::kBufferSizeIncrement & (kBufferSizeIncrement - 1)) == 0,
"kBufferSizeIncrement must be a power of 2");
const size_t new_size = ozz::Align(_size, kBufferSizeIncrement);
byte* new_buffer = reinterpret_cast<byte*>(
ozz::memory::default_allocator()->Allocate(new_size, 16));
if (buffer_ != nullptr) {
std::memcpy(new_buffer, buffer_, alloc_size_);
}
ozz::memory::default_allocator()->Deallocate(buffer_);
buffer_ = new_buffer;
alloc_size_ = new_size;
int SpanStream::Tell() const { return tell_; }

size_t SpanStream::Size() const { return static_cast<size_t>(end_); }

bool SpanStream::Resize(size_t _size, ozz::span<byte>& _buffer) {
return _size <= _buffer.size();
}

// MemoryStream implementation.
// ----------------------------------------------------------------------------

MemoryStream::MemoryStream() : SpanStream({}) {}

MemoryStream::~MemoryStream() {}

bool MemoryStream::Resize(size_t _size, ozz::span<byte>& _buffer) {
if (_size > _buffer.size()) {
allocation_.resize(_size);
_buffer = ozz::make_span(allocation_);
}
return _size == 0 || buffer_ != nullptr;
return true;
}
} // namespace io
} // namespace ozz
24 changes: 20 additions & 4 deletions test/base/io/stream_tests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@
// //
//----------------------------------------------------------------------------//

#include "ozz/base/io/stream.h"

#include <stdint.h>

#include <limits>

#include "gtest/gtest.h"

#include "ozz/base/containers/array.h"
#include "ozz/base/io/stream.h"
#include "ozz/base/platform.h"

void TestStream(ozz::io::Stream* _stream) {
Expand Down Expand Up @@ -79,7 +79,7 @@ void TestSeek(ozz::io::Stream* _stream) {
EXPECT_EQ(_stream->Tell(), static_cast<int>(sizeof(Type)));
EXPECT_EQ(_stream->Size(), sizeof(Type));

const int64_t kEnd = 465827;
const int64_t kEnd = 46582;
// Force file length to kEnd but do not write to the stream.
EXPECT_EQ(_stream->Seek(kEnd - _stream->Tell(), ozz::io::Stream::kCurrent),
0);
Expand Down Expand Up @@ -171,6 +171,22 @@ TEST(File, Stream) {
{ EXPECT_TRUE(ozz::io::File::Exist("test.bin")); }
}

TEST(SpanStream, Stream) {
ozz::array<ozz::byte, 634627> buffer;
{
ozz::io::SpanStream stream(ozz::make_span(buffer));
TestStream(&stream);
}
{
ozz::io::SpanStream stream(ozz::make_span(buffer));
TestSeek(&stream);
}
{
ozz::io::SpanStream stream(ozz::make_span(buffer));
TestTooBigStream(&stream);
}
}

TEST(MemoryStream, Stream) {
{
ozz::io::MemoryStream stream;
Expand Down

0 comments on commit 1fb71ad

Please sign in to comment.