diff --git a/include/ircd/buffer.h b/include/ircd/buffer/buffer.h similarity index 53% rename from include/ircd/buffer.h rename to include/ircd/buffer/buffer.h index 18b76d9d6..e70d13274 100644 --- a/include/ircd/buffer.h +++ b/include/ircd/buffer/buffer.h @@ -45,6 +45,10 @@ namespace ircd::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); @@ -77,12 +81,15 @@ namespace ircd::buffer // Convenience copy to std stream template std::ostream &operator<<(std::ostream &s, const buffer &buffer); template class I, class T> std::ostream &operator<<(std::ostream &s, const I &buffers); - - // Preconstructed null buffers - extern const mutable_buffer null_buffer; - extern const ilist null_buffers; } +#include "buffer_base.h" +#include "mutable_buffer.h" +#include "const_buffer.h" +#include "fixed_buffer.h" +#include "window_buffer.h" +#include "unique_buffer.h" + // Export these important aliases down to main ircd namespace namespace ircd { @@ -104,342 +111,6 @@ namespace ircd using buffer::consume; } -/// Base for all buffer types -/// -template -struct ircd::buffer::buffer -:std::tuple -{ - using iterator = it; - using value_type = typename std::remove_pointer::type; - - operator string_view() const; - explicit operator std::string_view() const; - explicit operator std::string() const; - - auto &begin() const { return std::get<0>(*this); } - auto &begin() { return std::get<0>(*this); } - auto &end() const { return std::get<1>(*this); } - auto &end() { return std::get<1>(*this); } - - auto &operator[](const size_t &i) const - { - return *(begin() + i); - } - - auto &operator[](const size_t &i) - { - return *(begin() + i); - } - - buffer(const it &start, const it &stop) - :std::tuple{start, stop} - {} - - buffer(const it &start, const size_t &size) - :buffer{start, start + size} - {} - - buffer() - :buffer{nullptr, nullptr} - {} -}; - -/// Base for mutable buffers, or buffers which can be written to because they -/// are not const. -/// -struct ircd::buffer::mutable_buffer -:buffer -{ - // Conversion offered for the analogous asio buffer - operator boost::asio::mutable_buffer() const; - - // Allows boost::spirit to append to the buffer; this means the size() of - // this buffer becomes a consumption counter and the real size of the buffer - // must be kept separately. This is the lowlevel basis for a stream buffer. - void insert(char *const &it, const value_type &v) - { - assert(it >= this->begin() && it <= this->end()); - memmove(it + 1, it, std::distance(it, this->end())); - *it = v; - ++std::get<1>(*this); - } - - using buffer::buffer; - - mutable_buffer() - :buffer{} - {} - - mutable_buffer(const buffer &b) - :buffer{b} - {} - - template - mutable_buffer(char (&buf)[SIZE]) - :buffer{buf, SIZE} - {} - - template - mutable_buffer(std::array &buf) - :buffer{buf.data(), SIZE} - {} - - // lvalue string reference offered to write through to a std::string as - // the buffer. not explicit; should be hard to bind by accident... - mutable_buffer(std::string &buf) - :mutable_buffer{const_cast(buf.data()), buf.size()} - {} - - mutable_buffer(const std::function &closure) - { - closure(*this); - } -}; - -struct ircd::buffer::const_buffer -:buffer -{ - operator boost::asio::const_buffer() const; - - using buffer::buffer; - - const_buffer() - :buffer{} - {} - - const_buffer(const buffer &b) - :buffer{b} - {} - - const_buffer(const buffer &b) - :buffer{data(b), size(b)} - {} - - template - const_buffer(const char (&buf)[SIZE]) - :buffer{buf, SIZE} - {} - - template - const_buffer(const std::array &buf) - :buffer{reinterpret_cast(buf.data()), SIZE} - {} - - const_buffer(const mutable_buffer &b) - :buffer{data(b), size(b)} - {} - - const_buffer(const string_view &s) - :buffer{data(s), size(s)} - {} -}; - -/// fixed_buffer wraps an std::array with construction and conversions apropos -/// the ircd::buffer suite. fixed_buffer should be punnable. Its only memory -/// footprint is the array itself and -/// -template -struct ircd::buffer::fixed_buffer -:std::array::type, SIZE> -{ - using mutable_type = typename std::remove_const::type; - using const_type = typename std::add_const::type; - using array_type = std::array; - - operator buffer() const - { - return { std::begin(*this), std::end(*this) }; - } - - operator buffer() - { - return { std::begin(*this), std::end(*this) }; - } - - using array_type::array_type; - fixed_buffer(const nullptr_t &) - :array_type{{0}} - {} - - fixed_buffer(const std::function &closure) - { - closure(mutable_buffer{reinterpret_cast(this->data()), this->size()}); - } - - fixed_buffer(buffer b) - :array_type{std::begin(b), std::end(b)} - {} - - fixed_buffer() = default; -}; - -static_assert -( - // Assertion over an arbitrary but common template configuration. - std::is_standard_layout>::value, - "ircd::buffer::fixed_buffer must be standard layout" -); - -/// The window_buffer is just two mutable_buffers. One of the two buffers -/// just spans an underlying space and the other buffer is a window of the -/// remaining space which shrinks toward the end as the space is consumed. -/// The window_buffer object inherits from the latter, so it always has the -/// appearance of a mutable_buffer windowing on the the next place to write. -/// -/// The recommended usage of this device is actually through the operator() -/// closure, which will automatically resize the window based on the return -/// value in the closure. -/// -struct ircd::buffer::window_buffer -:mutable_buffer -{ - mutable_buffer base; - - /// Bytes remaining for writes to the stream buffer (same as size(*this)) - size_t remaining() const - { - assert(begin() <= base.end()); - const size_t ret(std::distance(begin(), base.end())); - assert(ret == size(*this)); - return ret; - } - - /// Bytes used by writes to the stream buffer - size_t consumed() const - { - assert(begin() >= base.begin()); - assert(begin() <= base.end()); - return std::distance(base.begin(), begin()); - } - - /// View the completed portion of the stream - const_buffer completed() const - { - assert(base.begin() <= begin()); - assert(base.begin() + consumed() <= base.end()); - return { base.begin(), base.begin() + consumed() }; - } - - /// View the completed portion of the stream - mutable_buffer completed() - { - assert(base.begin() <= begin()); - assert(base.begin() + consumed() <= base.end()); - return { base.begin(), base.begin() + consumed() }; - } - - /// Convenience conversion to get the completed portion - explicit operator const_buffer() const - { - return completed(); - } - - /// Convenience closure presenting the writable window and advancing the - /// window with a consume() for the bytes written in the closure. - using closure = std::function; - void operator()(const closure &closure) - { - consume(*this, closure(*this)); - } - - window_buffer(const mutable_buffer &base) - :mutable_buffer{base} - ,base{base} - {} -}; - -/// Like unique_ptr, this template holds ownership of an allocated buffer -/// -template -struct ircd::buffer::unique_buffer -:buffer -{ - unique_buffer(std::unique_ptr &&, const size_t &size); - unique_buffer(const size_t &size); - unique_buffer(); - unique_buffer(unique_buffer &&) noexcept; - unique_buffer(const unique_buffer &) = delete; - unique_buffer &operator=(unique_buffer &&) noexcept; - unique_buffer &operator=(const unique_buffer &) = delete; - ~unique_buffer() noexcept; -}; - -template -ircd::buffer::unique_buffer::unique_buffer() -:buffer -{ - nullptr, nullptr -} -{} - -template -ircd::buffer::unique_buffer::unique_buffer(std::unique_ptr &&b, - const size_t &size) -:buffer -{ - typename buffer::iterator(b.release()), size -} -{} - -template -ircd::buffer::unique_buffer::unique_buffer(const size_t &size) -:unique_buffer -{ - std::unique_ptr - { - //TODO: Can't use a template parameter to the attribute even though - // it's known at compile time. Hardcoding this until fixed with better - // aligned dynamic memory. - //new __attribute__((aligned(alignment))) uint8_t[size] - new __attribute__((aligned(16))) uint8_t[size] - }, - size -} -{ - // Alignment can only be 16 bytes for now - assert(alignment == 16); -} - -template -ircd::buffer::unique_buffer::unique_buffer(unique_buffer &&other) -noexcept -:buffer -{ - std::move(static_cast(other)) -} -{ - get<0>(other) = nullptr; -} - -template -ircd::buffer::unique_buffer & -ircd::buffer::unique_buffer::operator=(unique_buffer &&other) -noexcept -{ - this->~unique_buffer(); - - static_cast(*this) = std::move(static_cast(other)); - get<0>(other) = nullptr; - - return *this; -} - -template -ircd::buffer::unique_buffer::~unique_buffer() -noexcept -{ - delete[] data(*this); -} - template class buffers, class T> diff --git a/include/ircd/buffer/buffer_base.h b/include/ircd/buffer/buffer_base.h new file mode 100644 index 000000000..5133fee7d --- /dev/null +++ b/include/ircd/buffer/buffer_base.h @@ -0,0 +1,70 @@ +// 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_BASE_H + +/// Base for all buffer types +/// +template +struct ircd::buffer::buffer +:std::tuple +{ + using iterator = it; + using value_type = typename std::remove_pointer::type; + + operator string_view() const; + explicit operator std::string_view() const; + explicit operator std::string() const; + + auto &begin() const { return std::get<0>(*this); } + auto &begin() { return std::get<0>(*this); } + auto &end() const { return std::get<1>(*this); } + auto &end() { return std::get<1>(*this); } + + auto &operator[](const size_t &i) const; + auto &operator[](const size_t &i); + + buffer(const it &start, const it &stop); + buffer(const it &start, const size_t &size); + buffer(); +}; + +template +ircd::buffer::buffer::buffer() +:buffer{nullptr, nullptr} +{} + +template +ircd::buffer::buffer::buffer(const it &start, + const size_t &size) +:buffer{start, start + size} +{} + +template +ircd::buffer::buffer::buffer(const it &start, + const it &stop) +:std::tuple{start, stop} +{} + +template +auto & +ircd::buffer::buffer::operator[](const size_t &i) +const +{ + return *(begin() + i); +} + +template +auto & +ircd::buffer::buffer::operator[](const size_t &i) +{ + return *(begin() + i); +} diff --git a/include/ircd/buffer/const_buffer.h b/include/ircd/buffer/const_buffer.h new file mode 100644 index 000000000..11f193a8b --- /dev/null +++ b/include/ircd/buffer/const_buffer.h @@ -0,0 +1,58 @@ +// 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_CONST_BUFFER_H + +struct ircd::buffer::const_buffer +:buffer +{ + // Definition for this is somewhere in the .cc files where boost is incl. + operator boost::asio::const_buffer() const; + + using buffer::buffer; + template const_buffer(const char (&buf)[SIZE]); + template const_buffer(const std::array &buf); + const_buffer(const buffer &b); + const_buffer(const buffer &b); + const_buffer(const mutable_buffer &b); + const_buffer(const string_view &s); + const_buffer() = default; +}; + +inline +ircd::buffer::const_buffer::const_buffer(const buffer &b) +:buffer{b} +{} + +inline +ircd::buffer::const_buffer::const_buffer(const buffer &b) +:buffer{data(b), size(b)} +{} + +inline +ircd::buffer::const_buffer::const_buffer(const mutable_buffer &b) +:buffer{data(b), size(b)} +{} + +inline +ircd::buffer::const_buffer::const_buffer(const string_view &s) +:buffer{data(s), size(s)} +{} + +template +ircd::buffer::const_buffer::const_buffer(const char (&buf)[SIZE]) +:buffer{buf, SIZE} +{} + +template +ircd::buffer::const_buffer::const_buffer(const std::array &buf) +:buffer{reinterpret_cast(buf.data()), SIZE} +{} diff --git a/include/ircd/buffer/fixed_buffer.h b/include/ircd/buffer/fixed_buffer.h new file mode 100644 index 000000000..975b82bfd --- /dev/null +++ b/include/ircd/buffer/fixed_buffer.h @@ -0,0 +1,78 @@ +// 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_FIXED_BUFFER_H + +/// fixed_buffer wraps an std::array with construction and conversions apropos +/// the ircd::buffer suite. fixed_buffer should be punnable. Its only memory +/// footprint is the array itself and +/// +template +struct ircd::buffer::fixed_buffer +:std::array::type, SIZE> +{ + using mutable_type = typename std::remove_const::type; + using const_type = typename std::add_const::type; + using array_type = std::array; + + operator buffer() const; + operator buffer(); + + using array_type::array_type; + fixed_buffer(const nullptr_t &); + fixed_buffer(const std::function &closure); + fixed_buffer(buffer b); + fixed_buffer() = default; +}; + +static_assert +( + // Assertion over an arbitrary but common template configuration. + std::is_standard_layout>::value, + "ircd::buffer::fixed_buffer must be standard layout" +); + +template +ircd::buffer::fixed_buffer::fixed_buffer(const nullptr_t &) +:array_type{{0}} +{} + +template +ircd::buffer::fixed_buffer::fixed_buffer(const std::function &closure) +{ + closure(mutable_buffer{reinterpret_cast(this->data()), this->size()}); +} + +template +ircd::buffer::fixed_buffer::fixed_buffer(buffer b) +:array_type{std::begin(b), std::end(b)} +{} + +template +ircd::buffer::fixed_buffer::operator +buffer() +{ + return { std::begin(*this), std::end(*this) }; +} + +template +ircd::buffer::fixed_buffer::operator +buffer() +const +{ + return { std::begin(*this), std::end(*this) }; +} diff --git a/include/ircd/buffer/mutable_buffer.h b/include/ircd/buffer/mutable_buffer.h new file mode 100644 index 000000000..226dd894e --- /dev/null +++ b/include/ircd/buffer/mutable_buffer.h @@ -0,0 +1,75 @@ +// 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_MUTABLE_BUFFER_H + +/// Base for mutable buffers, or buffers which can be written to because they +/// are not const. +/// +struct ircd::buffer::mutable_buffer +:buffer +{ + // The definition for this is somewhere in one of the .cc files. + /// Conversion offered for the analogous asio buffer. + operator boost::asio::mutable_buffer() const; + + /// Allows boost::spirit to append to the buffer; this means the size() of + /// this buffer becomes a consumption counter and the real size of the buffer + /// must be kept separately. This is the lowlevel basis for a stream buffer. + void insert(char *const &it, const value_type &v); + + using buffer::buffer; + template mutable_buffer(char (&buf)[SIZE]); + template mutable_buffer(std::array &buf); + mutable_buffer(const std::function &); + explicit mutable_buffer(std::string &buf); + mutable_buffer(const buffer &b); + mutable_buffer() = default; +}; + +inline +ircd::buffer::mutable_buffer::mutable_buffer(const buffer &b) +:buffer{b} +{} + +/// lvalue string reference offered to write through to a std::string as +/// the buffer. should be hard to bind by accident... +inline +ircd::buffer::mutable_buffer::mutable_buffer(std::string &buf) +:mutable_buffer{const_cast(buf.data()), buf.size()} +{} + +inline +ircd::buffer::mutable_buffer::mutable_buffer(const std::function &closure) +{ + closure(*this); +} + +template +ircd::buffer::mutable_buffer::mutable_buffer(char (&buf)[SIZE]) +:buffer{buf, SIZE} +{} + +template +ircd::buffer::mutable_buffer::mutable_buffer(std::array &buf) +:buffer{buf.data(), SIZE} +{} + +inline void +ircd::buffer::mutable_buffer::insert(char *const &it, + const value_type &v) +{ + assert(it >= this->begin() && it <= this->end()); + memmove(it + 1, it, std::distance(it, this->end())); + *it = v; + ++std::get<1>(*this); +} + diff --git a/include/ircd/buffer/unique_buffer.h b/include/ircd/buffer/unique_buffer.h new file mode 100644 index 000000000..63a4713e0 --- /dev/null +++ b/include/ircd/buffer/unique_buffer.h @@ -0,0 +1,102 @@ +// 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_UNIQUE_BUFFER_H + +/// Like unique_ptr, this template holds ownership of an allocated buffer +/// +template +struct ircd::buffer::unique_buffer +:buffer +{ + unique_buffer(std::unique_ptr &&, const size_t &size); + unique_buffer(const size_t &size); + unique_buffer(); + unique_buffer(unique_buffer &&) noexcept; + unique_buffer(const unique_buffer &) = delete; + unique_buffer &operator=(unique_buffer &&) noexcept; + unique_buffer &operator=(const unique_buffer &) = delete; + ~unique_buffer() noexcept; +}; + +template +ircd::buffer::unique_buffer::unique_buffer() +:buffer +{ + nullptr, nullptr +} +{} + +template +ircd::buffer::unique_buffer::unique_buffer(std::unique_ptr &&b, + const size_t &size) +:buffer +{ + typename buffer::iterator(b.release()), size +} +{} + +template +ircd::buffer::unique_buffer::unique_buffer(const size_t &size) +:unique_buffer +{ + std::unique_ptr + { + //TODO: Can't use a template parameter to the attribute even though + // it's known at compile time. Hardcoding this until fixed with better + // aligned dynamic memory. + //new __attribute__((aligned(alignment))) uint8_t[size] + new __attribute__((aligned(16))) uint8_t[size] + }, + size +} +{ + // Alignment can only be 16 bytes for now + assert(alignment == 16); +} + +template +ircd::buffer::unique_buffer::unique_buffer(unique_buffer &&other) +noexcept +:buffer +{ + std::move(static_cast(other)) +} +{ + get<0>(other) = nullptr; +} + +template +ircd::buffer::unique_buffer & +ircd::buffer::unique_buffer::operator=(unique_buffer &&other) +noexcept +{ + this->~unique_buffer(); + + static_cast(*this) = std::move(static_cast(other)); + get<0>(other) = nullptr; + + return *this; +} + +template +ircd::buffer::unique_buffer::~unique_buffer() +noexcept +{ + delete[] data(*this); +} diff --git a/include/ircd/buffer/window_buffer.h b/include/ircd/buffer/window_buffer.h new file mode 100644 index 000000000..a0b56d6fe --- /dev/null +++ b/include/ircd/buffer/window_buffer.h @@ -0,0 +1,101 @@ +// 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_WINDOW_BUFFER_H + +/// The window_buffer is just two mutable_buffers. One of the two buffers +/// just spans an underlying space and the other buffer is a window of the +/// remaining space which shrinks toward the end as the space is consumed. +/// The window_buffer object inherits from the latter, so it always has the +/// appearance of a mutable_buffer windowing on the the next place to write. +/// +/// The recommended usage of this device is actually through the operator() +/// closure, which will automatically resize the window based on the return +/// value in the closure. +/// +struct ircd::buffer::window_buffer +:mutable_buffer +{ + using closure = std::function; + + mutable_buffer base; + + size_t remaining() const; + size_t consumed() const; + + const_buffer completed() const; + explicit operator const_buffer() const; + mutable_buffer completed(); + + void operator()(const closure &closure); + + window_buffer(const mutable_buffer &base); +}; + +inline +ircd::buffer::window_buffer::window_buffer(const mutable_buffer &base) +:mutable_buffer{base} +,base{base} +{} + +inline void +ircd::buffer::window_buffer::operator()(const closure &closure) +{ + consume(*this, closure(*this)); +} + +/// View the completed portion of the stream +inline ircd::buffer::mutable_buffer +ircd::buffer::window_buffer::completed() +{ + assert(base.begin() <= begin()); + assert(base.begin() + consumed() <= base.end()); + return { base.begin(), base.begin() + consumed() }; +} + +/// Convenience conversion to get the completed portion +inline ircd::buffer::window_buffer::operator +const_buffer() +const +{ + return completed(); +} + +/// View the completed portion of the stream +inline ircd::buffer::const_buffer +ircd::buffer::window_buffer::completed() +const +{ + assert(base.begin() <= begin()); + assert(base.begin() + consumed() <= base.end()); + return { base.begin(), base.begin() + consumed() }; +} + +/// Bytes used by writes to the stream buffer +inline size_t +ircd::buffer::window_buffer::consumed() +const +{ + assert(begin() >= base.begin()); + assert(begin() <= base.end()); + return std::distance(base.begin(), begin()); +} + +/// Bytes remaining for writes to the stream buffer (same as size(*this)) +inline size_t +ircd::buffer::window_buffer::remaining() +const +{ + assert(begin() <= base.end()); + const size_t ret(std::distance(begin(), base.end())); + assert(ret == size(*this)); + return ret; +} diff --git a/include/ircd/stdinc.h b/include/ircd/stdinc.h index 6c45e556c..3558e51dd 100644 --- a/include/ircd/stdinc.h +++ b/include/ircd/stdinc.h @@ -193,7 +193,7 @@ namespace ircd #include "string_view.h" #include "vector_view.h" #include "byte_view.h" -#include "buffer.h" +#include "buffer/buffer.h" #include "allocator.h" #include "util/util.h" #include "exception.h"