0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-12-28 16:34:13 +01:00
construct/include/ircd/buffer/buffer.h
2020-06-07 01:51:19 -07:00

546 lines
15 KiB
C++

// Matrix Construct
//
// Copyright (C) Matrix Construct Developers, Authors & Contributors
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
//
// 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<class it> struct buffer;
struct const_buffer;
struct mutable_buffer;
struct window_buffer;
struct parse_buffer;
template<class buffer, size_t SIZE> struct fixed_buffer;
template<class buffer> struct unique_buffer;
template<class buffer> struct shared_buffer;
template<size_t SIZE> using fixed_const_buffer = fixed_buffer<const_buffer, SIZE>;
template<size_t SIZE> using fixed_mutable_buffer = fixed_buffer<mutable_buffer, SIZE>;
template<template<class> class I> using const_buffers = I<const_buffer>;
template<template<class> class I> using mutable_buffers = I<mutable_buffer>;
using unique_const_buffer = unique_buffer<const_buffer>;
using unique_mutable_buffer = unique_buffer<mutable_buffer>;
using shared_const_buffer = shared_buffer<const_buffer>;
using shared_mutable_buffer = shared_buffer<mutable_buffer>;
// Preconstructed null buffers
extern const mutable_buffer null_buffer;
extern const ilist<mutable_buffer> null_buffers;
// Single buffer iteration of contents
template<class it> const it &begin(const buffer<it> &buffer);
template<class it> const it &end(const buffer<it> &buffer);
template<class it> it &begin(buffer<it> &buffer);
template<class it> it &end(buffer<it> &buffer);
template<class it> std::reverse_iterator<it> rbegin(const buffer<it> &buffer);
template<class it> std::reverse_iterator<it> rend(const buffer<it> &buffer);
// Single buffer observers
template<class it> bool null(const buffer<it> &buffer);
template<class it> bool full(const buffer<it> &buffer);
template<class it> bool empty(const buffer<it> &buffer);
template<class it> bool operator!(const buffer<it> &buffer);
template<class it> size_t size(const buffer<it> &buffer);
template<class it> const it &data(const buffer<it> &buffer);
template<class it> bool aligned(const buffer<it> &buffer, const size_t &alignment);
template<class it> buffer<it> operator+(const buffer<it> &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 mutators
template<class it> size_t consume(buffer<it> &buffer, const size_t &bytes);
template<class it> buffer<it> &operator+=(buffer<it> &buffer, const size_t &bytes);
char *&copy(char *&dest, char *const &stop, char src);
char *&copy(char *&dest, char *const &stop, const const_buffer &src);
char *&move(char *&dest, char *const &stop, const const_buffer &src);
size_t copy(const mutable_buffer &dst, const char src);
size_t copy(const mutable_buffer &dst, const const_buffer &src);
size_t move(const mutable_buffer &dst, const const_buffer &src);
template<size_t SIZE> size_t copy(const mutable_buffer &dst, const char (&)[SIZE]);
template<size_t SIZE> size_t move(const mutable_buffer &dst, const char (&)[SIZE]);
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<class it> std::ostream &operator<<(std::ostream &s, const buffer<it> &buffer);
}
namespace ircd::buffer::buffers
{
// Iterable of buffers tools
template<template<class> class I, class T> size_t size(const I<T> &buffers);
template<template<class> class I, class T> size_t copy(const mutable_buffer &, const I<T> &buffer);
template<template<class> class I, class T> size_t consume(I<T> &buffers, const size_t &bytes);
template<template<class> class I, class T> std::ostream &operator<<(std::ostream &s, const I<T> &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::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::size;
using buffer::data;
using buffer::copy;
using buffer::consume;
using buffer::begin;
using buffer::end;
}
template<template<class>
class buffers,
class T>
std::ostream &
ircd::buffer::buffers::operator<<(std::ostream &s, const buffers<T> &b)
{
using it = typename T::iterator;
std::for_each(std::begin(b), std::end(b), [&s]
(const buffer<it> &b)
{
s << b;
});
return s;
}
template<template<class>
class buffers,
class T>
size_t
ircd::buffer::buffers::consume(buffers<T> &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<T>::value_type;
using iterator = typename buffer::iterator;
using ircd::buffer::size;
using ircd::buffer::consume;
buffer &b(const_cast<buffer &>(*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<template<class>
class buffers,
class T>
size_t
ircd::buffer::buffers::copy(const mutable_buffer &dest,
const buffers<T> &b)
{
using it = typename T::iterator;
using ircd::buffer::copy;
using ircd::buffer::size;
size_t ret(0);
for(const buffer<it> &b : b)
ret += copy(dest + ret, b);
return ret;
}
template<template<class>
class buffers,
class T>
size_t
ircd::buffer::buffers::size(const buffers<T> &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<it> &b)
{
return ret += size(b);
});
}
template<class it>
std::ostream &
ircd::buffer::operator<<(std::ostream &s, const buffer<it> &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<size_t SIZE>
#ifndef __clang__
__attribute__((error
#else
__attribute__((unavailable
#endif
(
"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<size_t SIZE>
#ifndef __clang__
__attribute__((error
#else
__attribute__((unavailable
#endif
(
"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)
{
char *const &s(begin(dst)), *e(s);
e = move(e, end(dst), src);
assert(std::distance(s, e) >= 0);
return std::distance(s, e);
}
inline size_t
ircd::buffer::copy(const mutable_buffer &dst,
const const_buffer &src)
{
char *const &s(begin(dst)), *e(s);
e = copy(e, end(dst), src);
assert(std::distance(s, e) >= 0);
return std::distance(s, e);
}
inline size_t
ircd::buffer::copy(const mutable_buffer &dst,
const char src)
{
char *const &s(begin(dst)), *e(s);
e = copy(e, end(dst), src);
assert(std::distance(s, e) >= 0);
return std::distance(s, e);
}
inline char *&
__attribute__((always_inline))
ircd::buffer::move(char *&dest,
char *const &stop,
const const_buffer &src)
{
assert(dest <= stop);
const size_t remain(std::distance(dest, stop));
const size_t cpsz(std::min(size(src), remain));
assert(cpsz <= size(src));
assert(cpsz <= remain);
#if __has_builtin(__builtin_memmove_inline) && !defined(RB_GENERIC)
__builtin_memmove_inline(dest, data(src), cpsz);
#else
__builtin_memmove(dest, data(src), cpsz);
#endif
dest += cpsz;
assert(dest <= stop);
return dest;
}
inline char *&
__attribute__((always_inline))
ircd::buffer::copy(char *&dest,
char *const &stop,
const const_buffer &src)
{
assert(dest <= stop);
const size_t remain(std::distance(dest, stop));
const size_t cpsz(std::min(size(src), remain));
assert(!overlap(const_buffer(dest, cpsz), src));
assert(cpsz <= size(src));
assert(cpsz <= remain);
#if __has_builtin(__builtin_memcpy_inline) && !defined(RB_GENERIC)
__builtin_memcpy_inline(dest, data(src), cpsz);
#else
__builtin_memcpy(dest, data(src), cpsz);
#endif
dest += cpsz;
assert(dest <= stop);
return dest;
}
inline char *&
ircd::buffer::copy(char *&dest,
char *const &stop,
char src)
{
assert(dest <= stop);
const bool cpsz(dest != stop);
(cpsz? *dest : src) = src;
dest += cpsz;
assert(dest <= stop);
return dest;
}
template<class it>
inline ircd::buffer::buffer<it> &
__attribute__((always_inline))
ircd::buffer::operator+=(buffer<it> &buffer,
const size_t &bytes)
{
const size_t &advance(std::min(bytes, size(buffer)));
consume(buffer, advance);
return buffer;
}
template<class it>
inline size_t
__attribute__((always_inline))
ircd::buffer::consume(buffer<it> &buffer,
const size_t &bytes)
{
assert(!null(buffer));
assert(bytes <= size(buffer));
get<0>(buffer) += bytes;
assert(get<0>(buffer) <= get<1>(buffer));
return bytes;
}
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<class it>
inline ircd::buffer::buffer<it>
__attribute__((always_inline))
ircd::buffer::operator+(const buffer<it> &buffer,
const size_t &bytes)
{
const size_t advance{std::min(bytes, size(buffer))};
return { begin(buffer) + advance, size(buffer) - advance };
}
template<class it>
inline bool
__attribute__((always_inline))
ircd::buffer::aligned(const buffer<it> &buffer,
const size_t &a)
{
return likely(a)?
uintptr_t(data(buffer)) % a == 0 && size(buffer) % a == 0:
true;
}
template<class it>
inline const it &
__attribute__((always_inline))
ircd::buffer::data(const buffer<it> &buffer)
{
return get<0>(buffer);
}
template<class it>
inline size_t
__attribute__((always_inline))
ircd::buffer::size(const buffer<it> &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<class it>
inline bool
__attribute__((always_inline))
ircd::buffer::operator!(const buffer<it> &buffer)
{
return empty(buffer);
}
template<class it>
inline bool
__attribute__((always_inline))
ircd::buffer::empty(const buffer<it> &buffer)
{
return null(buffer) || std::distance(get<0>(buffer), get<1>(buffer)) == 0;
}
template<class it>
inline bool
__attribute__((always_inline))
ircd::buffer::full(const buffer<it> &buffer)
{
return std::distance(get<0>(buffer), get<1>(buffer)) == 0;
}
template<class it>
inline bool
__attribute__((always_inline))
ircd::buffer::null(const buffer<it> &buffer)
{
return get<0>(buffer) == nullptr;
}
template<class it>
inline std::reverse_iterator<it>
__attribute__((always_inline))
ircd::buffer::rend(const buffer<it> &buffer)
{
return std::reverse_iterator<it>(get<0>(buffer));
}
template<class it>
inline std::reverse_iterator<it>
__attribute__((always_inline))
ircd::buffer::rbegin(const buffer<it> &buffer)
{
return std::reverse_iterator<it>(get<0>(buffer) + size(buffer));
}
template<class it>
inline it &
__attribute__((always_inline))
ircd::buffer::end(buffer<it> &buffer)
{
return get<1>(buffer);
}
template<class it>
inline it &
__attribute__((always_inline))
ircd::buffer::begin(buffer<it> &buffer)
{
return get<0>(buffer);
}
template<class it>
inline const it &
__attribute__((always_inline))
ircd::buffer::end(const buffer<it> &buffer)
{
return get<1>(buffer);
}
template<class it>
inline const it &
__attribute__((always_inline))
ircd::buffer::begin(const buffer<it> &buffer)
{
return get<0>(buffer);
}