Skip to content

Commit

Permalink
add size-constrained string sink (#135)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsteemann committed Oct 19, 2022
1 parent 772095d commit 2d0837a
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 42 deletions.
1 change: 0 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ if (IPO_ENABLED)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION True)
endif()

option(BuildVelocyPackExamples "Build examples" ON)
option(Maintainer "Build maintainer tools" OFF)
option(BuildSseOpt "Build with SSE optimization instructions" ON)

Expand Down
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# options

set(CMAKE_INTERPROCEDURAL_OPTIMIZATION False)
option(BuildVelocyPackExamples "Build examples" ON)

message(STATUS "VelocyPack building examples: ${BuildVelocyPackExamples}")
if(BuildVelocyPackExamples)
Expand Down
122 changes: 94 additions & 28 deletions include/velocypack/Sink.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,89 +48,154 @@ struct Sink {

template<typename T>
struct ByteBufferSinkImpl final : public Sink {
explicit ByteBufferSinkImpl(Buffer<T>* buffer) : buffer(buffer) {}
explicit ByteBufferSinkImpl(Buffer<T>* buffer) : _buffer(buffer) {}

void push_back(char c) override final { buffer->push_back(c); }
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());
_buffer->append(p.c_str(), p.size());
}

void append(char const* p) override final { buffer->append(p, strlen(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, len);
_buffer->append(p, len);
}

void reserve(ValueLength len) override final { buffer->reserve(len); }
void reserve(ValueLength len) override final { _buffer->reserve(len); }

Buffer<T>* buffer;
private:
Buffer<T>* _buffer;
};

typedef ByteBufferSinkImpl<char> CharBufferSink;

template<typename T>
struct StringSinkImpl final : public Sink {
explicit StringSinkImpl(T* buffer) : buffer(buffer) {}
explicit StringSinkImpl(T* buffer) : _buffer(buffer) {}

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

void append(std::string const& p) override final { buffer->append(p); }
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) override final { _buffer->append(p, strlen(p)); }

void append(char const* p, ValueLength len) override final {
buffer->append(p, checkOverflow(len));
_buffer->append(p, checkOverflow(len));
}

void reserve(ValueLength len) override final {
ValueLength length = len + buffer->size();
if (length <= buffer->capacity()) {
ValueLength length = len + _buffer->size();
if (length <= _buffer->capacity()) {
return;
}
buffer->reserve(checkOverflow(length));
_buffer->reserve(checkOverflow(len));
}

T* buffer;
private:
T* _buffer;
};

typedef StringSinkImpl<std::string> StringSink;

// a sink with an upper bound for the generated output value
template<typename T>
struct SizeConstrainedStringSinkImpl final : public Sink {
explicit SizeConstrainedStringSinkImpl(T* buffer, ValueLength maxLength)
: _buffer(buffer), _maxLength(maxLength), _overflowed(false) {}

void push_back(char c) override final {
if (_buffer->size() < _maxLength) {
_buffer->push_back(c);
} 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 {
if (_buffer->size() < _maxLength) {
ValueLength total = checkOverflow(_buffer->size() + checkOverflow(len));
if (total <= _maxLength) {
_buffer->append(p, len);
return;
}
ValueLength left = _maxLength - _buffer->size();
if (len > left) {
len = left;
}
_buffer->append(p, len);
}
_overflowed = true;
}

void reserve(ValueLength len) override final {
ValueLength total = checkOverflow(_buffer->size() + checkOverflow(len));
if (total <= _buffer->capacity()) {
return;
}
ValueLength left = _maxLength - _buffer->size();
if (len > left) {
len = left;
}
_buffer->reserve(checkOverflow(len));
}

bool overflowed() const noexcept { return _overflowed; }

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

typedef SizeConstrainedStringSinkImpl<std::string> SizeConstrainedStringSink;

// only tracks the length of the generated output
struct StringLengthSink final : public Sink {
StringLengthSink() : length(0) {}
StringLengthSink() : _length(0) {}

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

void append(std::string const& p) override final { length += p.size(); }
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* p) override final { _length += strlen(p); }

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

void reserve(ValueLength) override final {}

ValueLength length;
ValueLength length() const noexcept { return _length; }

private:
ValueLength _length;
};

template<typename T>
struct StreamSinkImpl final : public Sink {
explicit StreamSinkImpl(T* stream) : stream(stream) {}
explicit StreamSinkImpl(T* stream) : _stream(stream) {}

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

void append(std::string const& p) override final { *stream << p; }
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)));
_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));
_stream->write(p, static_cast<std::streamsize>(len));
}

void reserve(ValueLength) override final {}

T* stream;
private:
T* _stream;
};

typedef StreamSinkImpl<std::ostringstream> StringStreamSink;
Expand All @@ -141,6 +206,7 @@ typedef StreamSinkImpl<std::ofstream> OutputFileStreamSink;
using VPackSink = arangodb::velocypack::Sink;
using VPackCharBufferSink = arangodb::velocypack::CharBufferSink;
using VPackStringSink = arangodb::velocypack::StringSink;
using VPackSizeConstrainedStringSink = arangodb::velocypack::SizeConstrainedStringSink;
using VPackStringLengthSink = arangodb::velocypack::StringLengthSink;
using VPackStringStreamSink = arangodb::velocypack::StringStreamSink;
using VPackOutputFileStreamSink = arangodb::velocypack::OutputFileStreamSink;
18 changes: 9 additions & 9 deletions tests/testsDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1812,7 +1812,7 @@ TEST(DumperLengthTest, Null) {
StringLengthSink sink;
Dumper dumper(&sink, &options);
dumper.dump(s);
ASSERT_EQ(strlen("null"), sink.length);
ASSERT_EQ(strlen("null"), sink.length());
}

TEST(DumperLengthTest, True) {
Expand All @@ -1829,7 +1829,7 @@ TEST(DumperLengthTest, True) {
StringLengthSink sink;
Dumper dumper(&sink, &options);
dumper.dump(s);
ASSERT_EQ(strlen("true"), sink.length);
ASSERT_EQ(strlen("true"), sink.length());
}

TEST(DumperLengthTest, False) {
Expand All @@ -1846,7 +1846,7 @@ TEST(DumperLengthTest, False) {
StringLengthSink sink;
Dumper dumper(&sink, &options);
dumper.dump(s);
ASSERT_EQ(strlen("false"), sink.length);
ASSERT_EQ(strlen("false"), sink.length());
}

TEST(DumperLengthTest, String) {
Expand All @@ -1863,7 +1863,7 @@ TEST(DumperLengthTest, String) {
StringLengthSink sink;
Dumper dumper(&sink, &options);
dumper.dump(s);
ASSERT_EQ(strlen("\"abcdefgjfjhhgh\""), sink.length);
ASSERT_EQ(strlen("\"abcdefgjfjhhgh\""), sink.length());
;
}

Expand All @@ -1881,7 +1881,7 @@ TEST(DumperLengthTest, EmptyObject) {
StringLengthSink sink;
Dumper dumper(&sink, &options);
dumper.dump(s);
ASSERT_EQ(strlen("{}"), sink.length);
ASSERT_EQ(strlen("{}"), sink.length());
}

TEST(DumperLengthTest, SimpleObject) {
Expand All @@ -1898,7 +1898,7 @@ TEST(DumperLengthTest, SimpleObject) {
StringLengthSink sink;
Dumper dumper(&sink, &options);
dumper.dump(s);
ASSERT_EQ(strlen("{\"foo\":\"bar\"}"), sink.length);
ASSERT_EQ(strlen("{\"foo\":\"bar\"}"), sink.length());
}

TEST(DumperLengthTest, SimpleArray) {
Expand All @@ -1915,7 +1915,7 @@ TEST(DumperLengthTest, SimpleArray) {
StringLengthSink sink;
Dumper dumper(&sink, &options);
dumper.dump(s);
ASSERT_EQ(strlen("[1,2,3,4,5,6,7,\"abcdef\"]"), sink.length);
ASSERT_EQ(strlen("[1,2,3,4,5,6,7,\"abcdef\"]"), sink.length());
}

TEST(DumperLengthTest, EscapeUnicodeOn) {
Expand All @@ -1933,7 +1933,7 @@ TEST(DumperLengthTest, EscapeUnicodeOn) {
StringLengthSink sink;
Dumper dumper(&sink, &options);
dumper.dump(s);
ASSERT_EQ(strlen("\"m\\uxxxxt\\uxxxxr\""), sink.length);
ASSERT_EQ(strlen("\"m\\uxxxxt\\uxxxxr\""), sink.length());
}

TEST(DumperLengthTest, EscapeUnicodeOff) {
Expand All @@ -1951,7 +1951,7 @@ TEST(DumperLengthTest, EscapeUnicodeOff) {
StringLengthSink sink;
Dumper dumper(&sink, &options);
dumper.dump(s);
ASSERT_EQ(strlen("\"mötör\""), sink.length);
ASSERT_EQ(strlen("\"mötör\""), sink.length());
}

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

0 comments on commit 2d0837a

Please sign in to comment.