/* * Copyright (C) 2014 ScyllaDB */ /* * This file is part of Scylla. * * Scylla is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Scylla is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Scylla. If not, see . */ #ifndef UTILS_DATA_OUTPUT_HH_ #define UTILS_DATA_OUTPUT_HH_ #include "bytes.hh" #include "net/byteorder.hh" /** * Data output interface for Java-esqe wire serialization of * basic type, including strings and bytes. The latter I've opted * to keep for a few reasons: * 1.) It matches better with data_input.read, which in turn * is somewhat meaningful because it can create a string more efficiently * without adding another slightly contrived abstraction (iterators?) * 2.) At some point this interface should maybe be extended to support stream-like * underlying mediums (such as a back_insert iterator etc), at which point * having to know the exact size of what it being serialized becomes less of an * issue. */ class data_output { public: data_output(char* p, char* e) : _ptr(p), _end(e) { } data_output(char* p, size_t n) : data_output(p, p + n) { } data_output(bytes& b) : data_output(reinterpret_cast(b.begin()), b.size()) { } data_output(bytes& b, size_t off, size_t n = bytes::npos) : data_output(reinterpret_cast(b.begin()) + off, reinterpret_cast(b.begin()) + off + std::min(b.size() - off, n)) { if (off > b.size()) { throw std::out_of_range("Offset out of range"); } } template static inline std::enable_if_t::value, size_t> serialized_size(const T&); template static inline std::enable_if_t::value, size_t> serialized_size(); template static inline size_t serialized_size(const sstring& s) { if (s.size() > std::numeric_limits::max()) { throw std::out_of_range("String too large"); } return sizeof(SizeType) + s.size(); } template static inline size_t serialized_size(const bytes_view& s) { if (s.size() > std::numeric_limits::max()) { throw std::out_of_range("Buffer too large"); } return sizeof(SizeType) + s.size(); } template static inline size_t serialized_size(const bytes& s) { return serialized_size(bytes_view(s)); } size_t avail() const { return _end - _ptr; } void ensure(size_t s) const { if (avail() < s) { throw std::out_of_range("Buffer overflow"); } } data_output& skip(size_t s) { ensure(s); _ptr += s; return *this; } char* reserve(size_t s) { auto p = _ptr; skip(s); return p; } template inline std::enable_if_t::value, data_output&> write(T t); template data_output& write(const sstring& s) { ensure(serialized_size(s)); write(SizeType(s.size())); _ptr = std::copy(s.begin(), s.end(), _ptr); return *this; } template data_output& write(const bytes_view& s) { ensure(serialized_size(s)); write(SizeType(s.size())); _ptr = std::copy(s.begin(), s.end(), _ptr); return *this; } template data_output& write(const bytes & s) { return write(bytes_view(s)); } template data_output& write(Iter s, Iter e) { while (s != e) { write(*s++); } return *this; } data_output& write(const char* s, const char* e) { ensure(e - s); _ptr = std::copy(s, e, _ptr); return *this; } template data_output& write(T t, size_t n) { while (n-- > 0) { write(t); } return *this; } data_output& write_view(bytes_view v) { return write(v.begin(), v.end()); } private: char * _ptr; char * _end; }; template<> inline size_t data_output::serialized_size() { return sizeof(uint8_t); } template inline std::enable_if_t::value, size_t> data_output::serialized_size() { return sizeof(T); } template inline std::enable_if_t::value, size_t> data_output::serialized_size(const T&) { return serialized_size(); } template<> inline data_output& data_output::write(bool b) { return write(b); } template<> inline data_output& data_output::write(char c) { ensure(1); *_ptr++ = c; return *this; } template inline std::enable_if_t::value, data_output&> data_output::write( T t) { ensure(sizeof(T)); *reinterpret_cast *>(_ptr) = net::hton(t); _ptr += sizeof(T); return *this; } #endif /* UTILS_DATA_OUTPUT_HH_ */