// Matrix Construct // // Copyright (C) Matrix Construct Developers, Authors & Contributors // Copyright (C) 2016-2018 Jason Volk // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice is present in all copies. The // full license for this software is available in the LICENSE file. #pragma once #define HAVE_IRCD_BUFFER_H // Forward declarations from boost::asio because it is not included here. IRCd // buffers are not based directly on the boost ones but are easily converted // when passing our buffer to an asio function. namespace boost::asio { struct const_buffer; struct mutable_buffer; } /// Lightweight buffer interface compatible with boost::asio IO buffers and vectors /// /// A const_buffer is a pair of iterators like `const char *` meant for sending /// data; a mutable_buffer is a pair of iterators meant for receiving. /// /// These templates offer tools for individual buffers as well as tools for /// iterations of buffers. An iteration of buffers is an iovector that is /// passed to our sockets etc. The ircd::iov template can host an iteration of /// buffers. The `template template` functions are tools for a container of /// buffers of any permutation. /// namespace ircd::buffer { template struct buffer; struct const_buffer; struct mutable_buffer; struct window_buffer; struct parse_buffer; template struct fixed_buffer; template struct unique_buffer; template struct shared_buffer; template using fixed_const_buffer = fixed_buffer; template using fixed_mutable_buffer = fixed_buffer; template class I> using const_buffers = I; template class I> using mutable_buffers = I; // Preconstructed null buffers extern const mutable_buffer null_buffer; extern const ilist null_buffers; // Single buffer iteration of contents template const it &begin(const buffer &buffer); template const it &end(const buffer &buffer); template it &begin(buffer &buffer); template it &end(buffer &buffer); template std::reverse_iterator rbegin(const buffer &buffer); template std::reverse_iterator rend(const buffer &buffer); // Single buffer tools template bool null(const buffer &buffer); template bool full(const buffer &buffer); template bool empty(const buffer &buffer); template bool operator!(const buffer &buffer); template size_t size(const buffer &buffer); template const it &data(const buffer &buffer); template bool aligned(const buffer &buffer, const size_t &alignment); template size_t consume(buffer &buffer, const size_t &bytes); template buffer operator+(const buffer &buffer, const size_t &bytes); template it copy(it &dest, const it &stop, const const_buffer &); template it move(it &dest, const it &stop, const const_buffer &); template size_t copy(const mutable_buffer &dst, const char (&buf)[SIZE]); template size_t move(const mutable_buffer &dst, const char (&buf)[SIZE]); size_t copy(const mutable_buffer &dst, const const_buffer &src); size_t move(const mutable_buffer &dst, const const_buffer &src); size_t reverse(const mutable_buffer &dst, const const_buffer &src); void reverse(const mutable_buffer &buf); size_t zero(const mutable_buffer &buf); // Convenience copy to std stream template std::ostream &operator<<(std::ostream &s, const buffer &buffer); } namespace ircd::buffer::buffers { // Iterable of buffers tools template class I, class T> size_t size(const I &buffers); template class I, class T> size_t copy(const mutable_buffer &, const I &buffer); template class I, class T> size_t consume(I &buffers, const size_t &bytes); template class I, class T> std::ostream &operator<<(std::ostream &s, const I &buffers); } #include "buffer_base.h" #include "mutable_buffer.h" #include "const_buffer.h" #include "fixed_buffer.h" #include "window_buffer.h" #include "parse_buffer.h" #include "unique_buffer.h" #include "shared_buffer.h" // Export these important aliases down to main ircd namespace namespace ircd { namespace buffers = buffer::buffers; using buffer::const_buffer; using buffer::mutable_buffer; using buffer::fixed_buffer; using buffer::unique_buffer; using buffer::shared_buffer; using buffer::null_buffer; using buffer::window_buffer; using buffer::fixed_const_buffer; using buffer::fixed_mutable_buffer; using buffer::const_buffers; using buffer::mutable_buffers; using buffer::size; using buffer::data; using buffer::copy; using buffer::consume; using buffer::begin; using buffer::end; } template class buffers, class T> std::ostream & ircd::buffer::buffers::operator<<(std::ostream &s, const buffers &b) { using it = typename T::iterator; std::for_each(std::begin(b), std::end(b), [&s] (const buffer &b) { s << b; }); return s; } template class buffers, class T> size_t ircd::buffer::buffers::consume(buffers &b, const size_t &bytes) { ssize_t remain(bytes); for(auto it(std::begin(b)); it != std::end(b) && remain > 0; ++it) { using buffer = typename buffers::value_type; using iterator = typename buffer::iterator; using ircd::buffer::size; using ircd::buffer::consume; buffer &b(const_cast(*it)); const ssize_t bsz(size(b)); const ssize_t csz{std::min(remain, bsz)}; remain -= consume(b, csz); assert(remain >= 0); } assert(ssize_t(bytes) >= remain); return bytes - remain; } template class buffers, class T> size_t ircd::buffer::buffers::copy(const mutable_buffer &dest, const buffers &b) { using it = typename T::iterator; using ircd::buffer::copy; using ircd::buffer::size; size_t ret(0); for(const buffer &b : b) ret += copy(data(dest) + ret, size(dest) - ret, b); return ret; } template class buffers, class T> size_t ircd::buffer::buffers::size(const buffers &b) { using it = typename T::iterator; using ircd::buffer::size; return std::accumulate(std::begin(b), std::end(b), size_t(0), [] (auto ret, const buffer &b) { return ret += size(b); }); } template std::ostream & ircd::buffer::operator<<(std::ostream &s, const buffer &buffer) { assert(!null(buffer) || get<1>(buffer) == nullptr); s.write(data(buffer), size(buffer)); return s; } // We use the sodium_memzero() from libsodium in ircd/sodium.cc if available // to ensure cross-platform guarantees the zero'ing doesn't get optimized away. #ifndef HAVE_SODIUM_H inline size_t ircd::buffer::zero(const mutable_buffer &buf) { std::memset(data(buf), 0x0, size(buf)); return size(buf); } #endif inline void ircd::buffer::reverse(const mutable_buffer &buf) { std::reverse(data(buf), data(buf) + size(buf)); } inline size_t ircd::buffer::reverse(const mutable_buffer &dst, const const_buffer &src) { const size_t ret{std::min(size(dst), size(src))}; std::reverse_copy(data(src), data(src) + ret, data(dst)); return ret; } template __attribute__((error ( "Move source is an array. Is this a string literal? Do you want to move the \\0?" " Disambiguate this by typing the source string_view or const_buffer." ))) inline size_t ircd::buffer::move(const mutable_buffer &dst, const char (&buf)[SIZE]) { return move(dst, const_buffer{buf}); } template __attribute__((error ( "Copy source is an array. Is this a string literal? Do you want to copy the \\0?" " Disambiguate this by typing the source string_view or const_buffer." ))) inline size_t ircd::buffer::copy(const mutable_buffer &dst, const char (&buf)[SIZE]) { return copy(dst, const_buffer{buf}); } inline size_t ircd::buffer::move(const mutable_buffer &dst, const const_buffer &src) { auto e{begin(dst)}; move(e, end(dst), src); assert(std::distance(begin(dst), e) >= 0); return std::distance(begin(dst), e); } inline size_t ircd::buffer::copy(const mutable_buffer &dst, const const_buffer &src) { auto e{begin(dst)}; copy(e, end(dst), src); assert(std::distance(begin(dst), e) >= 0); return std::distance(begin(dst), e); } template it ircd::buffer::move(it &dest, const it &stop, const const_buffer &src) { assert(dest <= stop); const it ret(dest); const size_t &srcsz(size(src)); const size_t &remain(std::distance(ret, stop)); const size_t &mvsz(std::min(srcsz, remain)); dest += mvsz; assert(dest <= stop); assert(mvsz <= srcsz); assert(mvsz <= remain); assert(remain >= 0); memmove(ret, data(src), mvsz); return ret; } template it ircd::buffer::copy(it &dest, const it &stop, const const_buffer &src) { assert(dest <= stop); const it ret(dest); const size_t &srcsz(size(src)); const size_t &remain(std::distance(ret, stop)); const size_t &cpsz(std::min(srcsz, remain)); dest += cpsz; assert(dest <= stop); assert(cpsz <= srcsz); assert(cpsz <= remain); assert(remain >= 0); memcpy(ret, data(src), cpsz); return ret; } template ircd::buffer::buffer ircd::buffer::operator+(const buffer &buffer, const size_t &bytes) { const size_t advance { std::min(bytes, size(buffer)) }; return { begin(buffer) + advance, size(buffer) - advance }; } template size_t ircd::buffer::consume(buffer &buffer, const size_t &bytes) { assert(!null(buffer)); assert(bytes <= size(buffer)); get<0>(buffer) += bytes; assert(get<0>(buffer) <= get<1>(buffer)); return size(buffer); } template bool ircd::buffer::aligned(const buffer &buffer, const size_t &a) { return likely(a)? uintptr_t(data(buffer)) % a == 0 && size(buffer) % a == 0: true; } template const it & ircd::buffer::data(const buffer &buffer) { return get<0>(buffer); } template size_t ircd::buffer::size(const buffer &buffer) { assert(get<0>(buffer) <= get<1>(buffer)); assert(!null(buffer) || get<1>(buffer) == nullptr); return std::distance(get<0>(buffer), get<1>(buffer)); } template bool ircd::buffer::operator!(const buffer &buffer) { return empty(buffer); } template bool ircd::buffer::empty(const buffer &buffer) { return null(buffer) || std::distance(get<0>(buffer), get<1>(buffer)) == 0; } template bool ircd::buffer::full(const buffer &buffer) { return std::distance(get<0>(buffer), get<1>(buffer)) == 0; } template bool ircd::buffer::null(const buffer &buffer) { return get<0>(buffer) == nullptr; } template std::reverse_iterator ircd::buffer::rend(const buffer &buffer) { return std::reverse_iterator(get<0>(buffer)); } template std::reverse_iterator ircd::buffer::rbegin(const buffer &buffer) { return std::reverse_iterator(get<0>(buffer) + size(buffer)); } template it & ircd::buffer::end(buffer &buffer) { return get<1>(buffer); } template it & ircd::buffer::begin(buffer &buffer) { return get<0>(buffer); } template const it & ircd::buffer::end(const buffer &buffer) { return get<1>(buffer); } template const it & ircd::buffer::begin(const buffer &buffer) { return get<0>(buffer); }