Skip to content

Commit

Permalink
Merge 41319b6 into 1e9b1ab
Browse files Browse the repository at this point in the history
  • Loading branch information
jsteemann committed Nov 3, 2022
2 parents 1e9b1ab + 41319b6 commit 0440944
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 30 deletions.
61 changes: 31 additions & 30 deletions include/velocypack/Sink.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#pragma once

#include <string>
#include <string_view>
#include <fstream>
#include <sstream>

Expand All @@ -40,24 +41,23 @@ struct Sink {

virtual ~Sink() = default;
virtual void push_back(char c) = 0;
virtual void append(std::string const& p) = 0;
virtual void append(char const* p) = 0;
virtual void append(char const* p, ValueLength len) = 0;
virtual void reserve(ValueLength len) = 0;

// default implementations, can be overridden
virtual void append(std::string const& p) { append(p.data(), p.size()); }
virtual void append(std::string_view p) { append(p.data(), p.size()); }
[[deprecated]] virtual void append(char const* p) { append(p, std::strlen(p)); }
};

template<typename T>
struct ByteBufferSinkImpl final : public Sink {
using Sink::append;

explicit ByteBufferSinkImpl(Buffer<T>* buffer) : _buffer(buffer) {}

void push_back(char c) override final { _buffer->push_back(c); }

void append(std::string const& p) override final {
_buffer->append(p.c_str(), p.size());
}

void append(char const* p) override final { _buffer->append(p, strlen(p)); }

void append(char const* p, ValueLength len) override final {
_buffer->append(p, len);
}
Expand All @@ -72,14 +72,12 @@ typedef ByteBufferSinkImpl<char> CharBufferSink;

template<typename T>
struct StringSinkImpl final : public Sink {
using Sink::append;

explicit StringSinkImpl(T* buffer) : _buffer(buffer) {}

void push_back(char c) override final { _buffer->push_back(c); }

void append(std::string const& p) override final { _buffer->append(p); }

void append(char const* p) override final { _buffer->append(p, strlen(p)); }

void append(char const* p, ValueLength len) override final {
_buffer->append(p, checkOverflow(len));
}
Expand All @@ -101,35 +99,36 @@ typedef StringSinkImpl<std::string> StringSink;
// a sink with an upper bound for the generated output value
template<typename T>
struct SizeConstrainedStringSinkImpl final : public Sink {
using Sink::append;

explicit SizeConstrainedStringSinkImpl(T* buffer, ValueLength maxLength)
: _buffer(buffer), _maxLength(maxLength), _overflowed(false) {}
: _maxLength(maxLength), _buffer(buffer), _length(0), _overflowed(false) {}

void push_back(char c) override final {
++_length;
if (_buffer->size() < _maxLength) {
_buffer->push_back(c);
_buffer->push_back(c);
VELOCYPACK_ASSERT(_buffer->size() <= _maxLength);
} else {
_overflowed = true;
}
}

void append(std::string const& p) override final {
append(p.data(), p.size());
}

void append(char const* p) override final { append(p, strlen(p)); }

void append(char const* p, ValueLength len) override final {
_length += len;
if (_buffer->size() < _maxLength) {
ValueLength total = checkOverflow(_buffer->size() + checkOverflow(len));
if (total <= _maxLength) {
_buffer->append(p, len);
VELOCYPACK_ASSERT(_buffer->size() <= _maxLength);
return;
}
ValueLength left = _maxLength - _buffer->size();
if (len > left) {
len = left;
}
_buffer->append(p, len);
VELOCYPACK_ASSERT(_buffer->size() <= _maxLength);
}
_overflowed = true;
}
Expand All @@ -139,33 +138,37 @@ struct SizeConstrainedStringSinkImpl final : public Sink {
if (total <= _buffer->capacity()) {
return;
}
VELOCYPACK_ASSERT(_buffer->size() <= _maxLength);
ValueLength left = _maxLength - _buffer->size();
if (len > left) {
len = left;
}
_buffer->reserve(checkOverflow(len));
if (len > 0) {
_buffer->reserve(checkOverflow(len));
}
}

ValueLength maxLength() const noexcept { return _maxLength; }
ValueLength unconstrainedLength() const noexcept { return _length; }
bool overflowed() const noexcept { return _overflowed; }

private:
T* _buffer;
std::size_t const _maxLength;
T* _buffer;
ValueLength _length;
bool _overflowed;
};

typedef SizeConstrainedStringSinkImpl<std::string> SizeConstrainedStringSink;

// only tracks the length of the generated output
struct StringLengthSink final : public Sink {
using Sink::append;

StringLengthSink() : _length(0) {}

void push_back(char) override final { ++_length; }

void append(std::string const& p) override final { _length += p.size(); }

void append(char const* p) override final { _length += strlen(p); }

void append(char const*, ValueLength len) override final { _length += len; }

void reserve(ValueLength) override final {}
Expand All @@ -178,16 +181,14 @@ struct StringLengthSink final : public Sink {

template<typename T>
struct StreamSinkImpl final : public Sink {
using Sink::append;

explicit StreamSinkImpl(T* stream) : _stream(stream) {}

void push_back(char c) override final { *_stream << c; }

void append(std::string const& p) override final { *_stream << p; }

void append(char const* p) override final {
_stream->write(p, static_cast<std::streamsize>(strlen(p)));
}

void append(char const* p, ValueLength len) override final {
_stream->write(p, static_cast<std::streamsize>(len));
}
Expand Down
54 changes: 54 additions & 0 deletions tests/testsSink.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#include <ostream>
#include <string>
#include <string_view>

#include "tests-common.h"

Expand All @@ -41,6 +42,10 @@ TEST(SinkTest, CharBufferSink) {
out.clear();
s.append(std::string("foobarbaz"));
ASSERT_EQ(9, out.size());

out.clear();
s.append(std::string_view("foobarbaz"));
ASSERT_EQ(9, out.size());

out.clear();
s.append("foobarbaz");
Expand All @@ -65,6 +70,11 @@ TEST(SinkTest, StringSink) {
s.append(std::string("foobarbaz"));
ASSERT_EQ(9, out.size());
ASSERT_EQ("foobarbaz", out);

out.clear();
s.append(std::string_view("foobarbaz"));
ASSERT_EQ(9, out.size());
ASSERT_EQ("foobarbaz", out);

out.clear();
s.append("foobarbaz");
Expand All @@ -81,85 +91,123 @@ TEST(SinkTest, SizeConstrainedStringSinkAlwaysEmpty) {
std::string out;
SizeConstrainedStringSink s(&out, 0);

ASSERT_EQ(0, s.maxLength());
ASSERT_TRUE(out.empty());
ASSERT_FALSE(s.overflowed());

s.push_back('x');
ASSERT_EQ(0, s.maxLength());
ASSERT_TRUE(out.empty());
ASSERT_TRUE(s.overflowed());
ASSERT_EQ(1, s.unconstrainedLength());

s.append("foobarbaz");
ASSERT_EQ(0, s.maxLength());
ASSERT_TRUE(out.empty());
ASSERT_TRUE(s.overflowed());
ASSERT_EQ(10, s.unconstrainedLength());

s.append(std::string_view("foobarbaz"));
ASSERT_EQ(0, s.maxLength());
ASSERT_TRUE(out.empty());
ASSERT_TRUE(s.overflowed());
ASSERT_EQ(19, s.unconstrainedLength());

s.append("123", 3);
ASSERT_EQ(0, s.maxLength());
ASSERT_TRUE(out.empty());
ASSERT_TRUE(s.overflowed());
ASSERT_EQ(22, s.unconstrainedLength());
}

TEST(SinkTest, SizeConstrainedStringSinkSmall) {
std::string out;
SizeConstrainedStringSink s(&out, 15);

ASSERT_EQ(15, s.maxLength());
ASSERT_TRUE(out.empty());
ASSERT_FALSE(s.overflowed());

s.push_back('x');
ASSERT_EQ(15, s.maxLength());
ASSERT_EQ("x", out);
ASSERT_FALSE(s.overflowed());
ASSERT_EQ(1, s.unconstrainedLength());

s.append("foobarbaz");
ASSERT_EQ(15, s.maxLength());
ASSERT_EQ("xfoobarbaz", out);
ASSERT_FALSE(s.overflowed());
ASSERT_EQ(10, s.unconstrainedLength());

s.append("123", 3);
ASSERT_EQ(15, s.maxLength());
ASSERT_EQ("xfoobarbaz123", out);
ASSERT_FALSE(s.overflowed());
ASSERT_EQ(13, s.unconstrainedLength());

s.push_back('y');
ASSERT_EQ(15, s.maxLength());
ASSERT_EQ("xfoobarbaz123y", out);
ASSERT_FALSE(s.overflowed());
ASSERT_EQ(14, s.unconstrainedLength());

s.append("123", 3);
ASSERT_EQ(15, s.maxLength());
ASSERT_EQ("xfoobarbaz123y1", out);
ASSERT_TRUE(s.overflowed());
ASSERT_EQ(17, s.unconstrainedLength());

s.append(std::string_view("fuchs"));
ASSERT_EQ(15, s.maxLength());
ASSERT_EQ("xfoobarbaz123y1", out);
ASSERT_TRUE(s.overflowed());
ASSERT_EQ(22, s.unconstrainedLength());
}

TEST(SinkTest, SizeConstrainedStringSinkLarger) {
std::string out;
SizeConstrainedStringSink s(&out, 2048);

ASSERT_EQ(2048, s.maxLength());
ASSERT_TRUE(out.empty());
ASSERT_FALSE(s.overflowed());

for (std::size_t i = 0; i < 4096; ++i) {
s.push_back('x');
ASSERT_EQ(2048, s.maxLength());
if (i >= 2048) {
ASSERT_EQ(2048, out.size());
ASSERT_TRUE(s.overflowed());
} else {
ASSERT_EQ(i + 1, out.size());
ASSERT_FALSE(s.overflowed());
}
ASSERT_EQ(i + 1, s.unconstrainedLength());
}
}

TEST(SinkTest, SizeConstrainedStringSinkLongStringAppend) {
std::string out;
SizeConstrainedStringSink s(&out, 2092);

ASSERT_EQ(2092, s.maxLength());
ASSERT_TRUE(out.empty());
ASSERT_FALSE(s.overflowed());

s.append("meow");
ASSERT_EQ(2092, s.maxLength());
ASSERT_EQ(4, out.size());
ASSERT_FALSE(s.overflowed());
ASSERT_EQ(4, s.unconstrainedLength());

std::string append(16384, 'x');
s.append(append);
ASSERT_EQ(2092, s.maxLength());
ASSERT_EQ(2092, out.size());
ASSERT_EQ(std::string("meow") + append.substr(0, 2088), out);
ASSERT_TRUE(s.overflowed());
ASSERT_EQ(16388, s.unconstrainedLength());
}

TEST(SinkTest, SizeConstrainedStringSinkReserve) {
Expand Down Expand Up @@ -225,6 +273,9 @@ TEST(SinkTest, StringLengthSink) {

s.append("foobarbaz", 9);
ASSERT_EQ(28, s.length());

s.append(std::string_view("foobarbaz"));
ASSERT_EQ(37, s.length());
}

TEST(SinkTest, StringStreamSink) {
Expand All @@ -242,6 +293,9 @@ TEST(SinkTest, StringStreamSink) {

s.append("foobarbaz", 9);
ASSERT_EQ("xfoobarbazfoobarbazfoobarbaz", out.str());

s.append(std::string_view("boofar"));
ASSERT_EQ("xfoobarbazfoobarbazfoobarbazboofar", out.str());
}

int main(int argc, char* argv[]) {
Expand Down

0 comments on commit 0440944

Please sign in to comment.