// 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; using unique_const_buffer = unique_buffer; using unique_mutable_buffer = unique_buffer; using shared_const_buffer = shared_buffer; using shared_mutable_buffer = shared_buffer; // Preconstructed null buffers extern const mutable_buffer null_buffer; extern const ilist null_buffers; // Alignment constant expressions constexpr bool aligned(const uintptr_t &, size_t alignment); constexpr size_t padding(const size_t &size, size_t alignment); constexpr size_t pad_to(const size_t &size, const size_t &alignment); constexpr uintptr_t align(uintptr_t, size_t alignment); constexpr uintptr_t align_up(uintptr_t, size_t alignment); // Alignment inline tools bool aligned(const void *const &, const size_t &alignment); template const T *align(const void *const &, const size_t &alignment); template T *align(void *const &, const size_t &alignment); template const T *align_up(const void *const &, const size_t &alignment); template T *align_up(void *const &, const size_t &alignment); // 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 observer utils template bool null(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 padded(const buffer &buffer, const size_t &alignment); template bool aligned(const buffer &buffer, const size_t &alignment); template buffer operator+(const buffer &buffer, const size_t &bytes); size_t overlap_count(const const_buffer &, const const_buffer &); bool overlap(const const_buffer &, const const_buffer &); // Single buffer mutator utils template size_t consume(buffer &buffer, const size_t &bytes); template buffer &operator+=(buffer &buffer, const size_t &bytes); // other tools 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); } #include "buffer_base.h" #include "mutable_buffer.h" #include "const_buffer.h" #include "copy.h" #include "move.h" #include "fixed_buffer.h" #include "window_buffer.h" #include "parse_buffer.h" #include "unique_buffer.h" #include "shared_buffer.h" #include "buffers.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::unique_const_buffer; using buffer::unique_mutable_buffer; using buffer::shared_const_buffer; using buffer::shared_mutable_buffer; using buffer::const_buffers; using buffer::mutable_buffers; using buffer::aligned; using buffer::align; using buffer::align_up; using buffer::padded; using buffer::padding; using buffer::pad_to; using buffer::size; using buffer::data; using buffer::copy; using buffer::move; using buffer::consume; using buffer::begin; using buffer::end; } 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 inline size_t __attribute__((always_inline)) ircd::buffer::zero(const mutable_buffer &buf) { std::memset(data(buf), 0x0, size(buf)); return size(buf); } #endif inline void __attribute__((always_inline)) ircd::buffer::reverse(const mutable_buffer &buf) { std::reverse(data(buf), data(buf) + size(buf)); } inline size_t __attribute__((always_inline)) 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 inline ircd::buffer::buffer & __attribute__((always_inline)) ircd::buffer::operator+=(buffer &buffer, const size_t &bytes) { consume(buffer, bytes); return buffer; } template inline size_t __attribute__((always_inline)) ircd::buffer::consume(buffer &b, const size_t &bytes) { assert(!null(b)); assert(bytes <= size(b)); const auto &advance { std::min(bytes, size(b)) }; std::get<0>(b) += advance; assert(std::get<0>(b) <= std::get<1>(b)); return advance; } inline bool __attribute__((always_inline)) ircd::buffer::overlap(const const_buffer &a, const const_buffer &b) { return overlap_count(a, b) > 0UL; } inline size_t __attribute__((always_inline)) ircd::buffer::overlap_count(const const_buffer &a, const const_buffer &b) { const char *const res[2] { std::max(begin(a), begin(b)), std::min(end(a), end(b)), }; return std::max(res[1] - res[0], 0L); } template inline ircd::buffer::buffer __attribute__((always_inline)) ircd::buffer::operator+(const buffer &buffer, const size_t &bytes) { auto ret(buffer); ret += bytes; return ret; } template inline bool __attribute__((always_inline)) ircd::buffer::aligned(const buffer &buffer, const size_t &a) { return likely(a)? aligned(data(buffer), a) && padded(buffer, a): true; } template inline bool __attribute__((always_inline)) ircd::buffer::padded(const buffer &buffer, const size_t &a) { return likely(a)? size(buffer) % a == 0: true; } template inline const it & __attribute__((always_inline)) ircd::buffer::data(const buffer &buffer) { return get<0>(buffer); } template inline size_t __attribute__((always_inline)) 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 inline bool __attribute__((always_inline)) ircd::buffer::operator!(const buffer &buffer) { return empty(buffer); } template inline bool __attribute__((always_inline)) ircd::buffer::empty(const buffer &buffer) { return null(buffer) || std::distance(get<0>(buffer), get<1>(buffer)) == 0; } template inline bool __attribute__((always_inline)) ircd::buffer::null(const buffer &buffer) { return get<0>(buffer) == nullptr; } template inline std::reverse_iterator __attribute__((always_inline)) ircd::buffer::rend(const buffer &buffer) { return std::reverse_iterator(get<0>(buffer)); } template inline std::reverse_iterator __attribute__((always_inline)) ircd::buffer::rbegin(const buffer &buffer) { return std::reverse_iterator(get<0>(buffer) + size(buffer)); } template inline it & __attribute__((always_inline)) ircd::buffer::end(buffer &buffer) { return get<1>(buffer); } template inline it & __attribute__((always_inline)) ircd::buffer::begin(buffer &buffer) { return get<0>(buffer); } template inline const it & __attribute__((always_inline)) ircd::buffer::end(const buffer &buffer) { return get<1>(buffer); } template inline const it & __attribute__((always_inline)) ircd::buffer::begin(const buffer &buffer) { return get<0>(buffer); } template [[gnu::always_inline]] inline T * ircd::buffer::align(void *const &ptr, const size_t &alignment) { return reinterpret_cast ( align(uintptr_t(ptr), alignment) ); } template [[gnu::always_inline]] inline const T * ircd::buffer::align(const void *const &ptr, const size_t &alignment) { return reinterpret_cast ( align(uintptr_t(ptr), alignment) ); } template [[gnu::always_inline]] inline T * ircd::buffer::align_up(void *const &ptr, const size_t &alignment) { return reinterpret_cast ( align_up(uintptr_t(ptr), alignment) ); } template [[gnu::always_inline]] inline const T * ircd::buffer::align_up(const void *const &ptr, const size_t &alignment) { return reinterpret_cast ( align_up(uintptr_t(ptr), alignment) ); } [[gnu::always_inline]] inline bool ircd::buffer::aligned(const void *const &ptr, const size_t &alignment) { return aligned(uintptr_t(ptr), alignment); } constexpr uintptr_t ircd::buffer::align_up(uintptr_t ptr, size_t alignment) { alignment = std::max(alignment, 1UL); ptr += (alignment - (ptr % alignment)) % alignment; return ptr; } constexpr uintptr_t ircd::buffer::align(uintptr_t ptr, size_t alignment) { alignment = std::max(alignment, 1UL); ptr -= (ptr % alignment); return ptr; } constexpr size_t ircd::buffer::pad_to(const size_t &size, const size_t &alignment) { return size + padding(size, alignment); } constexpr size_t ircd::buffer::padding(const size_t &size, size_t alignment) { alignment = std::max(alignment, 1UL); return (alignment - (size % alignment)) % alignment; } constexpr bool ircd::buffer::aligned(const uintptr_t &ptr, size_t alignment) { alignment = std::max(alignment, 1UL); return ptr % alignment == 0; }