Apply clang-format

This commit is contained in:
Kurt Sassenrath 2023-10-20 00:34:44 -07:00
parent c46f68e759
commit 6874da27a3
33 changed files with 2546 additions and 2625 deletions

View File

@ -1,11 +1,11 @@
#include <algorithm>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/strand.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/strand.hpp>
#include <boost/config.hpp>
#include <algorithm>
#include <cstdlib>
#include <functional>
#include <iostream>
@ -14,69 +14,57 @@
#include <thread>
#include <vector>
namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http; // from <boost/beast/http.hpp>
namespace net = boost::asio; // from <boost/asio.hpp>
using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http; // from <boost/beast/http.hpp>
namespace net = boost::asio; // from <boost/asio.hpp>
using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
// Return a reasonable mime type based on the extension of a file.
beast::string_view
mime_type(beast::string_view path)
{
beast::string_view mime_type(beast::string_view path) {
using beast::iequals;
auto const ext = [&path]
{
auto const ext = [&path] {
auto const pos = path.rfind(".");
if(pos == beast::string_view::npos)
return beast::string_view{};
if (pos == beast::string_view::npos) return beast::string_view{};
return path.substr(pos);
}();
if(iequals(ext, ".htm")) return "text/html";
if(iequals(ext, ".html")) return "text/html";
if(iequals(ext, ".php")) return "text/html";
if(iequals(ext, ".css")) return "text/css";
if(iequals(ext, ".txt")) return "text/plain";
if(iequals(ext, ".js")) return "application/javascript";
if(iequals(ext, ".json")) return "application/json";
if(iequals(ext, ".xml")) return "application/xml";
if(iequals(ext, ".swf")) return "application/x-shockwave-flash";
if(iequals(ext, ".flv")) return "video/x-flv";
if(iequals(ext, ".png")) return "image/png";
if(iequals(ext, ".jpe")) return "image/jpeg";
if(iequals(ext, ".jpeg")) return "image/jpeg";
if(iequals(ext, ".jpg")) return "image/jpeg";
if(iequals(ext, ".gif")) return "image/gif";
if(iequals(ext, ".bmp")) return "image/bmp";
if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon";
if(iequals(ext, ".tiff")) return "image/tiff";
if(iequals(ext, ".tif")) return "image/tiff";
if(iequals(ext, ".svg")) return "image/svg+xml";
if(iequals(ext, ".svgz")) return "image/svg+xml";
if (iequals(ext, ".htm")) return "text/html";
if (iequals(ext, ".html")) return "text/html";
if (iequals(ext, ".php")) return "text/html";
if (iequals(ext, ".css")) return "text/css";
if (iequals(ext, ".txt")) return "text/plain";
if (iequals(ext, ".js")) return "application/javascript";
if (iequals(ext, ".json")) return "application/json";
if (iequals(ext, ".xml")) return "application/xml";
if (iequals(ext, ".swf")) return "application/x-shockwave-flash";
if (iequals(ext, ".flv")) return "video/x-flv";
if (iequals(ext, ".png")) return "image/png";
if (iequals(ext, ".jpe")) return "image/jpeg";
if (iequals(ext, ".jpeg")) return "image/jpeg";
if (iequals(ext, ".jpg")) return "image/jpeg";
if (iequals(ext, ".gif")) return "image/gif";
if (iequals(ext, ".bmp")) return "image/bmp";
if (iequals(ext, ".ico")) return "image/vnd.microsoft.icon";
if (iequals(ext, ".tiff")) return "image/tiff";
if (iequals(ext, ".tif")) return "image/tiff";
if (iequals(ext, ".svg")) return "image/svg+xml";
if (iequals(ext, ".svgz")) return "image/svg+xml";
return "application/text";
}
// Append an HTTP rel-path to a local filesystem path.
// The returned path is normalized for the platform.
std::string
path_cat(
beast::string_view base,
beast::string_view path)
{
if(base.empty())
return std::string(path);
std::string path_cat(beast::string_view base, beast::string_view path) {
if (base.empty()) return std::string(path);
std::string result(base);
#ifdef BOOST_MSVC
char constexpr path_separator = '\\';
if(result.back() == path_separator)
result.resize(result.size() - 1);
if (result.back() == path_separator) result.resize(result.size() - 1);
result.append(path.data(), path.size());
for(auto& c : result)
if(c == '/')
c = path_separator;
for (auto& c : result)
if (c == '/') c = path_separator;
#else
char constexpr path_separator = '/';
if(result.back() == path_separator)
result.resize(result.size() - 1);
if (result.back() == path_separator) result.resize(result.size() - 1);
result.append(path.data(), path.size());
#endif
return result;
@ -87,16 +75,12 @@ path_cat(
// The concrete type of the response message (which depends on the
// request), is type-erased in message_generator.
template <class Body, class Allocator>
http::message_generator
handle_request(
beast::string_view doc_root,
http::request<Body, http::basic_fields<Allocator>>&& req)
{
http::message_generator handle_request(beast::string_view doc_root,
http::request<Body, http::basic_fields<Allocator>>&& req) {
// Returns a bad request response
auto const bad_request =
[&req](beast::string_view why)
{
http::response<http::string_body> res{http::status::bad_request, req.version()};
auto const bad_request = [&req](beast::string_view why) {
http::response<http::string_body> res{
http::status::bad_request, req.version()};
res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
res.set(http::field::content_type, "text/html");
res.keep_alive(req.keep_alive());
@ -106,23 +90,22 @@ handle_request(
};
// Returns a not found response
auto const not_found =
[&req](beast::string_view target)
{
http::response<http::string_body> res{http::status::not_found, req.version()};
auto const not_found = [&req](beast::string_view target) {
http::response<http::string_body> res{
http::status::not_found, req.version()};
res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
res.set(http::field::content_type, "text/html");
res.keep_alive(req.keep_alive());
res.body() = "The resource '" + std::string(target) + "' was not found.";
res.body() =
"The resource '" + std::string(target) + "' was not found.";
res.prepare_payload();
return res;
};
// Returns a server error response
auto const server_error =
[&req](beast::string_view what)
{
http::response<http::string_body> res{http::status::internal_server_error, req.version()};
auto const server_error = [&req](beast::string_view what) {
http::response<http::string_body> res{
http::status::internal_server_error, req.version()};
res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
res.set(http::field::content_type, "text/html");
res.keep_alive(req.keep_alive());
@ -132,20 +115,17 @@ handle_request(
};
// Make sure we can handle the method
if( req.method() != http::verb::get &&
req.method() != http::verb::head)
if (req.method() != http::verb::get && req.method() != http::verb::head)
return bad_request("Unknown HTTP-method");
// Request path must be absolute and not contain "..".
if( req.target().empty() ||
req.target()[0] != '/' ||
req.target().find("..") != beast::string_view::npos)
if (req.target().empty() || req.target()[0] != '/'
|| req.target().find("..") != beast::string_view::npos)
return bad_request("Illegal request-target");
// Build the path to the requested file
std::string path = path_cat(doc_root, req.target());
if(req.target().back() == '/')
path.append("index.html");
if (req.target().back() == '/') path.append("index.html");
// Attempt to open the file
beast::error_code ec;
@ -153,19 +133,17 @@ handle_request(
body.open(path.c_str(), beast::file_mode::scan, ec);
// Handle the case where the file doesn't exist
if(ec == beast::errc::no_such_file_or_directory)
if (ec == beast::errc::no_such_file_or_directory)
return not_found(req.target());
// Handle an unknown error
if(ec)
return server_error(ec.message());
if (ec) return server_error(ec.message());
// Cache the size since we need it after the move
auto const size = body.size();
// Respond to HEAD request
if(req.method() == http::verb::head)
{
if (req.method() == http::verb::head) {
http::response<http::empty_body> res{http::status::ok, req.version()};
res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
res.set(http::field::content_type, mime_type(path));
@ -175,10 +153,9 @@ handle_request(
}
// Respond to GET request
http::response<http::file_body> res{
std::piecewise_construct,
std::make_tuple(std::move(body)),
std::make_tuple(http::status::ok, req.version())};
http::response<http::file_body> res{std::piecewise_construct,
std::make_tuple(std::move(body)),
std::make_tuple(http::status::ok, req.version())};
res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
res.set(http::field::content_type, mime_type(path));
res.content_length(size);
@ -189,15 +166,12 @@ handle_request(
//------------------------------------------------------------------------------
// Report a failure
void
fail(beast::error_code ec, char const* what)
{
void fail(beast::error_code ec, char const* what) {
std::cerr << what << ": " << ec.message() << "\n";
}
// Handles an HTTP server connection
class session : public std::enable_shared_from_this<session>
{
class session : public std::enable_shared_from_this<session> {
beast::tcp_stream stream_;
beast::flat_buffer buffer_;
std::shared_ptr<std::string const> doc_root_;
@ -205,31 +179,23 @@ class session : public std::enable_shared_from_this<session>
public:
// Take ownership of the stream
session(
tcp::socket&& socket,
std::shared_ptr<std::string const> const& doc_root)
: stream_(std::move(socket))
, doc_root_(doc_root)
{
}
session(tcp::socket&& socket,
std::shared_ptr<std::string const> const& doc_root)
: stream_(std::move(socket))
, doc_root_(doc_root) {}
// Start the asynchronous operation
void
run()
{
void run() {
// We need to be executing within a strand to perform async operations
// on the I/O objects in this session. Although not strictly necessary
// for single-threaded contexts, this example code is written to be
// thread-safe by default.
net::dispatch(stream_.get_executor(),
beast::bind_front_handler(
&session::do_read,
shared_from_this()));
beast::bind_front_handler(
&session::do_read, shared_from_this()));
}
void
do_read()
{
void do_read() {
// Make the request empty before reading,
// otherwise the operation behavior is undefined.
req_ = {};
@ -239,56 +205,38 @@ public:
// Read a request
http::async_read(stream_, buffer_, req_,
beast::bind_front_handler(
&session::on_read,
shared_from_this()));
beast::bind_front_handler(
&session::on_read, shared_from_this()));
}
void
on_read(
beast::error_code ec,
std::size_t bytes_transferred)
{
void on_read(beast::error_code ec, std::size_t bytes_transferred) {
boost::ignore_unused(bytes_transferred);
// This means they closed the connection
if(ec == http::error::end_of_stream)
return do_close();
if (ec == http::error::end_of_stream) return do_close();
if(ec)
return fail(ec, "read");
if (ec) return fail(ec, "read");
// Send the response
send_response(
handle_request(*doc_root_, std::move(req_)));
send_response(handle_request(*doc_root_, std::move(req_)));
}
void
send_response(http::message_generator&& msg)
{
void send_response(http::message_generator&& msg) {
bool keep_alive = msg.keep_alive();
// Write the response
beast::async_write(
stream_,
std::move(msg),
beast::bind_front_handler(
&session::on_write, shared_from_this(), keep_alive));
beast::async_write(stream_, std::move(msg),
beast::bind_front_handler(
&session::on_write, shared_from_this(), keep_alive));
}
void
on_write(
bool keep_alive,
beast::error_code ec,
std::size_t bytes_transferred)
{
void on_write(bool keep_alive, beast::error_code ec,
std::size_t bytes_transferred) {
boost::ignore_unused(bytes_transferred);
if(ec)
return fail(ec, "write");
if (ec) return fail(ec, "write");
if(! keep_alive)
{
if (!keep_alive) {
// This means we should close the connection, usually because
// the response indicated the "Connection: close" semantic.
return do_close();
@ -298,9 +246,7 @@ public:
do_read();
}
void
do_close()
{
void do_close() {
// Send a TCP shutdown
beast::error_code ec;
stream_.socket().shutdown(tcp::socket::shutdown_send, ec);
@ -312,90 +258,66 @@ public:
//------------------------------------------------------------------------------
// Accepts incoming connections and launches the sessions
class listener : public std::enable_shared_from_this<listener>
{
class listener : public std::enable_shared_from_this<listener> {
net::io_context& ioc_;
tcp::acceptor acceptor_;
std::shared_ptr<std::string const> doc_root_;
public:
listener(
net::io_context& ioc,
tcp::endpoint endpoint,
std::shared_ptr<std::string const> const& doc_root)
: ioc_(ioc)
, acceptor_(net::make_strand(ioc))
, doc_root_(doc_root)
{
listener(net::io_context& ioc, tcp::endpoint endpoint,
std::shared_ptr<std::string const> const& doc_root)
: ioc_(ioc)
, acceptor_(net::make_strand(ioc))
, doc_root_(doc_root) {
beast::error_code ec;
// Open the acceptor
acceptor_.open(endpoint.protocol(), ec);
if(ec)
{
if (ec) {
fail(ec, "open");
return;
}
// Allow address reuse
acceptor_.set_option(net::socket_base::reuse_address(true), ec);
if(ec)
{
if (ec) {
fail(ec, "set_option");
return;
}
// Bind to the server address
acceptor_.bind(endpoint, ec);
if(ec)
{
if (ec) {
fail(ec, "bind");
return;
}
// Start listening for connections
acceptor_.listen(
net::socket_base::max_listen_connections, ec);
if(ec)
{
acceptor_.listen(net::socket_base::max_listen_connections, ec);
if (ec) {
fail(ec, "listen");
return;
}
}
// Start accepting incoming connections
void
run()
{
do_accept();
}
void run() { do_accept(); }
private:
void
do_accept()
{
void do_accept() {
// The new connection gets its own strand
acceptor_.async_accept(
net::make_strand(ioc_),
beast::bind_front_handler(
&listener::on_accept,
shared_from_this()));
acceptor_.async_accept(net::make_strand(ioc_),
beast::bind_front_handler(
&listener::on_accept, shared_from_this()));
}
void
on_accept(beast::error_code ec, tcp::socket socket)
{
if(ec)
{
void on_accept(beast::error_code ec, tcp::socket socket) {
if (ec) {
fail(ec, "accept");
return; // To avoid infinite loop
}
else
{
} else {
// Create the session and run it
std::make_shared<session>(
std::move(socket),
doc_root_)->run();
std::make_shared<session>(std::move(socket), doc_root_)->run();
}
// Accept another connection
@ -404,5 +326,3 @@ private:
};
//------------------------------------------------------------------------------

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: Logging
//
@ -14,21 +14,20 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifndef logging_982a89e400976f59
#define logging_982a89e400976f59
#include "logging/formatters.h"
#include "logging/theme.h"
#include "logging/traits.h"
#include <chrono>
#include <memory>
#include <span>
#include <string_view>
#include <vector>
#include "logging/formatters.h"
#include "logging/theme.h"
#include "logging/traits.h"
namespace parselink {
namespace logging {
@ -51,7 +50,9 @@ struct message {
struct endpoint {
virtual ~endpoint() = default;
virtual std::span<char> buffer() { return {}; }
virtual void write(message const& msg) = 0;
virtual bool colored() const noexcept = 0;
level threshold{default_threshold};
@ -64,27 +65,28 @@ public:
// This constructor allows for an arbitrary number of logging endpoints
// to be passed in.
logger(std::string_view name,
std::vector<std::shared_ptr<endpoint>> eps);
logger(std::string_view name, std::vector<std::shared_ptr<endpoint>> eps);
template <typename... Endpoints>
explicit logger(std::string_view name, Endpoints&&... eps)
: logger(name, {std::forward<Endpoints>(eps)...}) {}
template<level Level, typename... Args>
requires (Level <= static_threshold)
template <level Level, typename... Args>
requires(Level <= static_threshold)
void log(fmt::format_string<Args...> format, Args&&... args) const {
try_write(Level, format.get(), std::forward<Args>(args)...);
do_write(Level, format.get(), std::forward<Args>(args)...);
}
template<level Level, typename... Args>
requires (Level > static_threshold)
template <level Level, typename... Args>
requires(Level > static_threshold)
[[gnu::flatten]] void log(fmt::format_string<Args...>, Args&&...) const {}
#define LOG_API(lvl) \
template <typename... Args> \
[[gnu::always_inline]] void lvl(fmt::format_string<Args...>&& format, Args&&... args) const { \
log<level::lvl>(std::forward<decltype(format)>(format), std::forward<Args>(args)...); \
#define LOG_API(lvl) \
template <typename... Args> \
[[gnu::always_inline]] void lvl( \
fmt::format_string<Args...>&& format, Args&&... args) const { \
log<level::lvl>(std::forward<decltype(format)>(format), \
std::forward<Args>(args)...); \
}
LOG_API(critical);
@ -100,8 +102,7 @@ public:
void set_threshold(level new_threshold) noexcept;
private:
template<typename... Args>
template <typename... Args>
void write_endpoint(std::shared_ptr<endpoint> const& endpoint, message msg,
fmt::string_view format, Args&&... args) const {
auto buff = endpoint->buffer();
@ -114,16 +115,15 @@ private:
endpoint->write(msg);
} else {
// Fill the static buffer.
fmt::vformat_to(buff.begin(), format,
fmt::make_format_args(args...));
fmt::vformat_to(
buff.begin(), format, fmt::make_format_args(args...));
msg.message = std::string_view{buff.data(), buff.size()};
endpoint->write(msg);
}
}
template<typename... Args>
void try_write(level level, fmt::string_view format,
Args&&... args) const {
template <typename... Args>
void do_write(level level, fmt::string_view format, Args&&... args) const {
message msg{level, std::chrono::system_clock::now(), name_};
for (auto const& ep : endpoints_) {
if (level > ep->threshold) continue;

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: Logging
//
@ -15,23 +15,22 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifndef logging_formatters_d22a64b1645a8134
#define logging_formatters_d22a64b1645a8134
#include "traits.h"
#include "theme.h"
#include "traits.h"
#include <magic_enum.hpp>
#include <tl/expected.hpp>
#include <magic_enum.hpp>
// Simple wrapper for error strings to be formatted like any other string_view.
template <>
struct fmt::formatter<parselink::logging::error_str>
: fmt::formatter<std::string_view> {
template<typename FormatContext>
template <typename FormatContext>
constexpr auto format(auto const& v, FormatContext& ctx) const {
return fmt::formatter<std::string_view>::format(v.v, ctx);
}
@ -45,14 +44,13 @@ struct fmt::formatter<parselink::logging::error_str>
//
// Logging `color` will yield "Color::Green"
template <typename E>
requires std::is_enum_v<E>
requires std::is_enum_v<E>
struct fmt::formatter<E> : fmt::formatter<std::string_view> {
template<typename FormatContext>
template <typename FormatContext>
auto format(E const v, FormatContext& ctx) const {
auto str = [v]{
return fmt::format("{}::{}",
magic_enum::enum_type_name<E>(),
magic_enum::enum_name(v));
auto str = [v] {
return fmt::format("{}::{}", magic_enum::enum_type_name<E>(),
magic_enum::enum_name(v));
}();
return fmt::formatter<std::string_view>::format(str, ctx);
}
@ -66,11 +64,12 @@ struct fmt::formatter<E> : fmt::formatter<std::string_view> {
//
// Logging `enum_name_only{color}` will yield "Green"
template <typename E>
requires std::is_enum_v<E>
requires std::is_enum_v<E>
struct fmt::formatter<parselink::logging::enum_name_only<E>>
: fmt::formatter<std::string_view> {
using enum_name_only = parselink::logging::enum_name_only<E>;
template<typename FormatContext>
template <typename FormatContext>
auto format(enum_name_only const v, FormatContext& ctx) const {
return fmt::formatter<std::string_view>::format(
magic_enum::enum_name(v.v), ctx);
@ -79,7 +78,7 @@ struct fmt::formatter<parselink::logging::enum_name_only<E>>
template <>
struct fmt::formatter<std::error_code> : fmt::formatter<std::string_view> {
template<typename FormatContext>
template <typename FormatContext>
auto format(auto const& v, FormatContext& ctx) const {
return fmt::formatter<std::string_view>::format(v.message(), ctx);
}
@ -89,7 +88,7 @@ struct fmt::formatter<std::error_code> : fmt::formatter<std::string_view> {
// string.
template <>
struct fmt::formatter<std::errc> : fmt::formatter<std::error_code> {
template<typename FormatContext>
template <typename FormatContext>
auto format(std::errc const& v, FormatContext& ctx) const {
return fmt::formatter<std::error_code>::format(
std::make_error_code(v), ctx);
@ -104,7 +103,7 @@ struct fmt::formatter<std::byte> {
return ctx.begin();
}
template<typename FormatContext>
template <typename FormatContext>
auto format(std::byte const v, FormatContext& ctx) const {
return fmt::format_to(ctx.out(), "0x{:0x}", std::uint8_t(v));
}
@ -113,7 +112,7 @@ struct fmt::formatter<std::byte> {
// Support printing raw/smart pointers without needing to wrap them in fmt::ptr
template <parselink::logging::detail::printable_pointer T>
struct fmt::formatter<T> : fmt::formatter<void const*> {
template<typename FormatContext>
template <typename FormatContext>
auto format(T const& v, FormatContext& ctx) const {
return fmt::formatter<void const*>::format(fmt::ptr(v), ctx);
}
@ -127,7 +126,7 @@ struct fmt::formatter<tl::expected<T, Err>> {
return ctx.begin();
}
template<typename FormatContext>
template <typename FormatContext>
auto format(tl::expected<T, Err> const& v, FormatContext& ctx) const {
if (v) {
return fmt::format_to(ctx.out(), "{}",
@ -141,8 +140,8 @@ struct fmt::formatter<tl::expected<T, Err>> {
// Support format_arg wrappers, which will be used to colorize output.
template <typename T>
struct fmt::formatter<parselink::logging::format_arg<T>> :
fmt::formatter<typename parselink::logging::format_arg<T>::type> {
struct fmt::formatter<parselink::logging::format_arg<T>>
: fmt::formatter<typename parselink::logging::format_arg<T>::type> {
using format_arg_type = parselink::logging::format_arg<T>;
using resolved_type = typename format_arg_type::type;

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: Logging
//
@ -16,7 +16,7 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifndef level_9f090ff308e53a57
#define level_9f090ff308e53a57
@ -25,14 +25,14 @@ namespace parselink {
namespace logging {
enum class level {
silent, // "Virtual" level used to suppress all logging output.
critical, // Indicates a fatal error occurred. Crash likely.
error, // Indicates a non-fatal error occurred.
warning, // Indicates potentially incorrect/unintentional behavior.
info, // Indicates general information.
verbose, // Noisier/potentially unimportant information.
debug, // Information intended for debugging purposes only.
trace // Tracer-like levels of verbosity may impact performance.
silent, // "Virtual" level used to suppress all logging output.
critical, // Indicates a fatal error occurred. Crash likely.
error, // Indicates a non-fatal error occurred.
warning, // Indicates potentially incorrect/unintentional behavior.
info, // Indicates general information.
verbose, // Noisier/potentially unimportant information.
debug, // Information intended for debugging purposes only.
trace // Tracer-like levels of verbosity may impact performance.
};
} // namespace logging

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: Logging
//
@ -24,42 +24,49 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifndef log_theme_8e9601cc066b20bf
#define log_theme_8e9601cc066b20bf
#include "level.h"
#include "traits.h"
#include <tl/expected.hpp>
#include <fmt/color.h>
#include <system_error>
#include <type_traits>
#include <fmt/color.h>
#include <tl/expected.hpp>
namespace parselink {
namespace logging {
template <typename T>
struct format_arg{
struct format_arg {
using type = std::decay_t<T>;
constexpr format_arg(type const& t) : v(t) {}
constexpr format_arg(type const& t)
: v(t) {}
type const& v;
};
template <std::convertible_to<std::string_view> T>
struct format_arg<T> {
using type = std::string_view;
constexpr format_arg(T&& t) noexcept : v(t) {}
constexpr format_arg(T&& t) noexcept
: v(t) {}
type v;
};
template <detail::smart_pointer T>
struct format_arg<T> {
using type = void const*;
constexpr format_arg(T const& t) noexcept : v(fmt::ptr(t)) {}
constexpr format_arg(T const& t) noexcept
: v(fmt::ptr(t)) {}
void const* v;
};
@ -72,7 +79,7 @@ struct static_theme {
};
template <std::integral T>
requires (!std::same_as<T, bool>)
requires(!std::same_as<T, bool>)
struct theme<T> : static_theme<fmt::color::light_sky_blue> {};
template <>
@ -85,7 +92,7 @@ template <detail::printable_pointer T>
struct theme<T> : static_theme<fmt::color::pale_violet_red> {};
template <typename E>
requires std::is_enum_v<E>
requires std::is_enum_v<E>
struct theme<E> : static_theme<fmt::color::gray> {};
// Errors
@ -98,7 +105,6 @@ struct theme<std::errc> : static_theme<fmt::color::fire_brick> {};
template <>
struct theme<error_str> : static_theme<fmt::color::fire_brick> {};
template <>
struct theme<bool> {
static constexpr auto style(bool l) noexcept {
@ -108,37 +114,40 @@ struct theme<bool> {
template <>
struct theme<enum_name_only<logging::level>> {
// clang-format off
constexpr static fmt::color colors[] = {
fmt::color::black,
fmt::color::red,
fmt::color::fire_brick,
fmt::color::golden_rod,
fmt::color::light_sky_blue,
fmt::color::lime_green,
fmt::color::pink,
fmt::color::slate_gray
};
fmt::color::black,
fmt::color::red,
fmt::color::fire_brick,
fmt::color::golden_rod,
fmt::color::light_sky_blue,
fmt::color::lime_green,
fmt::color::pink,
fmt::color::slate_gray };
// clang-format on
static constexpr auto style(auto l) noexcept {
return fmt::fg(*std::next(std::begin(colors), size_t(l.v)));
}
};
template <typename T>
concept has_static_theme =
std::convertible_to<fmt::text_style,
decltype(theme<std::remove_cvref_t<T>>::style)>;
concept has_static_theme = std::convertible_to<fmt::text_style,
decltype(theme<std::remove_cvref_t<T>>::style)>;
template <typename T>
concept has_dynamic_theme = requires (T const& t) {
{ theme<std::remove_cvref_t<T>>::style(t) }
-> std::convertible_to<fmt::text_style>;
concept has_dynamic_theme = requires(T const& t) {
{
theme<std::remove_cvref_t<T>>::style(t)
} -> std::convertible_to<fmt::text_style>;
};
template <typename T>
concept has_theme = has_static_theme<T> || has_dynamic_theme<T>;
template <typename T>
requires (has_theme<T>)
requires(has_theme<T>)
struct theme<std::span<T>> : theme<T> {};
template <typename T>
@ -156,12 +165,12 @@ constexpr auto get_theme(T const& value) {
return theme<T>::style(value);
}
[[gnu::always_inline]] constexpr auto styled(fmt::text_style const& style,
auto out) {
[[gnu::always_inline]] constexpr auto styled(
fmt::text_style const& style, auto out) {
if (style.has_foreground()) {
auto foreground =
fmt::detail::make_foreground_color<char>(style.get_foreground());
out = std::copy(foreground.begin(), foreground.end(), out);
auto foreground = fmt::detail::make_foreground_color<char>(
style.get_foreground());
out = std::copy(foreground.begin(), foreground.end(), out);
}
return out;
}

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: Logging
//
@ -14,13 +14,13 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifndef logging_traits_34e410874c0179c6
#define logging_traits_34e410874c0179c6
#include <type_traits>
#include <memory>
#include <type_traits>
namespace parselink {
namespace logging {
@ -33,7 +33,7 @@ namespace logging {
// logger.log<...>("value: {}", v); // logs "value: Foo::Bar"
// logger.log<...>("value: {}", enum_name_only{v}); // logs "value: Bar"
template <typename E>
requires std::is_enum_v<E>
requires std::is_enum_v<E>
struct enum_name_only {
E v;
};
@ -45,7 +45,9 @@ enum_name_only(E) -> enum_name_only<E>;
// a normal string.
struct error_str {
template <std::convertible_to<std::string_view> T>
error_str(T&& t) : v(std::forward<T>(t)) {}
error_str(T&& t)
: v(std::forward<T>(t)) {}
std::string_view v;
};

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: msgpack
//
@ -14,7 +14,7 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifndef msgpack_error_3fae420b0427164e
#define msgpack_error_3fae420b0427164e

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: msgpack
//
@ -14,7 +14,7 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifndef msgpack_format_849b5c5238d8212
#define msgpack_format_849b5c5238d8212
@ -27,327 +27,366 @@
namespace msgpack {
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Supporting types/tags for formats
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
struct nil {
nil() = default;
// This constructor is used by the reader implementation.
nil(auto) {};
nil(auto){};
};
struct invalid {};
using boolean = bool;
struct array_desc {
constexpr array_desc() noexcept = default;
constexpr array_desc(auto sz) noexcept : count(std::size_t(sz)) {};
constexpr array_desc(auto sz) noexcept
: count(std::size_t(sz)){};
// This constructor is implemented for use by reader
constexpr array_desc(auto, auto sz) noexcept : count(std::size_t(sz)) {};
constexpr array_desc(auto, auto sz) noexcept
: count(std::size_t(sz)){};
constexpr bool operator==(array_desc const& other) const noexcept {
return count == other.count;
}
std::size_t count = 0;
};
struct map_desc {
constexpr map_desc() noexcept = default;
constexpr map_desc(auto sz) noexcept : count(std::size_t(sz)) {};
constexpr map_desc(auto sz) noexcept
: count(std::size_t(sz)){};
// This constructor is implemented for use by reader
constexpr map_desc(auto, auto sz) noexcept : count(std::size_t(sz)) {};
constexpr map_desc(auto, auto sz) noexcept
: count(std::size_t(sz)){};
std::size_t count = 0;
};
namespace format {
// Classification of the format type. Used by the token API to distinguish
// different formats.
enum class type : std::uint8_t {
invalid,
unsigned_int,
signed_int,
string,
binary,
nil,
boolean,
array,
map,
array_view,
map_view,
};
// Classification of the format type. Used by the token API to distinguish
// different formats.
enum class type : std::uint8_t {
invalid,
unsigned_int,
signed_int,
string,
binary,
nil,
boolean,
array,
map,
array_view,
map_view,
};
// Flags that may control the behavior of readers/writers.
enum flag : std::uint8_t {
apply_mask = 1,
fixed_size = 2,
};
// Flags that may control the behavior of readers/writers.
enum flag : std::uint8_t {
apply_mask = 1,
fixed_size = 2,
};
// MessagePack formats break down into one of the following schemes:
// Marker byte + Fixed-length value
// Marker byte + Fixed-length payload length + payload
//
// In addition, there are "fix" types that embed the literal value or the
// payload length within the marker byte.
enum payload : std::uint8_t {
fixed,
variable
};
// MessagePack formats break down into one of the following schemes:
// Marker byte + Fixed-length value
// Marker byte + Fixed-length payload length + payload
//
// In addition, there are "fix" types that embed the literal value or the
// payload length within the marker byte.
enum payload : std::uint8_t { fixed, variable };
// Base structure for all MessagePack formats to inherit from.
struct base_format {
constexpr static flag flags = {};
};
// Base structure for all MessagePack formats to inherit from.
struct base_format {
constexpr static flag flags = {};
};
// Traits describing a particular format.
struct traits {
std::uint8_t flags{};
std::uint8_t size{};
std::byte mask{};
type token_type{};
constexpr auto operator<=>(traits const&) const noexcept = default;
};
// Traits describing a particular format.
struct traits {
std::uint8_t flags{};
std::uint8_t size{};
std::byte mask{};
type token_type{};
constexpr auto operator<=>(traits const&) const noexcept = default;
};
// "Sentinel" traits instance indicating no trait found
constexpr static traits no_traits {};
// "Sentinel" traits instance indicating no trait found
constexpr static traits no_traits{};
struct fixtype_format : base_format {};
struct fixtype_format : base_format {};
template <class>
struct resolve_token_type {
constexpr static type value = type::invalid;
};
template <class>
struct resolve_token_type {
constexpr static type value = type::invalid;
};
template <std::integral T>
struct resolve_token_type<T> {
constexpr static type value = std::is_signed_v<T> ?
type::signed_int : type::unsigned_int;
};
template <std::integral T>
struct resolve_token_type<T> {
constexpr static type value =
std::is_signed_v<T> ? type::signed_int : type::unsigned_int;
};
template <>
struct resolve_token_type<std::string_view> {
constexpr static type value = type::string;
};
template <>
struct resolve_token_type<std::string_view> {
constexpr static type value = type::string;
};
template <>
struct resolve_token_type<std::span<std::byte const>> {
constexpr static type value = type::binary;
};
template <>
struct resolve_token_type<std::span<std::byte const>> {
constexpr static type value = type::binary;
};
template <>
struct resolve_token_type<bool> {
constexpr static type value = type::boolean;
};
template <>
struct resolve_token_type<bool> {
constexpr static type value = type::boolean;
};
template <>
struct resolve_token_type<nil> {
constexpr static type value = type::nil;
};
template <>
struct resolve_token_type<nil> {
constexpr static type value = type::nil;
};
template <>
struct resolve_token_type<invalid> {
constexpr static type value = type::invalid;
};
template <>
struct resolve_token_type<invalid> {
constexpr static type value = type::invalid;
};
template <>
struct resolve_token_type<array_desc> {
constexpr static type value = type::array;
};
template <>
struct resolve_token_type<array_desc> {
constexpr static type value = type::array;
};
template <>
struct resolve_token_type<map_desc> {
constexpr static type value = type::map;
};
template <>
struct resolve_token_type<map_desc> {
constexpr static type value = type::map;
};
template <typename T>
constexpr static auto resolve_type_v = resolve_token_type<T>::value;
template <typename T>
constexpr static auto resolve_type_v = resolve_token_type<T>::value;
// The library's representation of certain data types does not always
// match the underlying data serialized in the message. For example,
// arrays and maps are encoded as a marker byte + fixed length value, but
// msgpack will present them wrapped in a structure for stronger type
// semantics.
// The library's representation of certain data types does not always
// match the underlying data serialized in the message. For example,
// arrays and maps are encoded as a marker byte + fixed length value, but
// msgpack will present them wrapped in a structure for stronger type
// semantics.
template <std::uint8_t Marker, typename First, typename Value = First>
struct format : base_format {
using first_type = First;
using value_type = Value; // Can be overridden
constexpr static std::byte marker{Marker};
constexpr static payload payload_type{payload::fixed};
constexpr static std::uint8_t flags{};
};
template <std::uint8_t Marker, typename First, typename Value = First>
struct format : base_format {
using first_type = First;
using value_type = Value; // Can be overridden
constexpr static std::byte marker{Marker};
constexpr static payload payload_type{payload::fixed};
constexpr static std::uint8_t flags{};
};
template <std::uint8_t Marker, std::uint8_t Mask, typename First = std::uint8_t>
struct fixtype : fixtype_format {
using first_type = First;
using value_type = first_type; // Can be overridden
constexpr static std::byte marker{Marker};
constexpr static std::byte mask{Mask};
constexpr static payload payload_type{payload::fixed};
constexpr static auto flags{flag::apply_mask};
};
template <std::uint8_t Marker, std::uint8_t Mask, typename First = std::uint8_t>
struct fixtype : fixtype_format {
using first_type = First;
using value_type = first_type; // Can be overridden
constexpr static std::byte marker{Marker};
constexpr static std::byte mask{Mask};
constexpr static payload payload_type{payload::fixed};
constexpr static auto flags{flag::apply_mask};
};
/*
* Integral types
*/
/*
* Integral types
*/
// Positive/negative fixint represent the literal value specified, do not
// need to mask it off.
struct positive_fixint : fixtype<0x00, 0x7f> {
constexpr static flag flags{};
};
struct negative_fixint : fixtype<0xe0, 0x1f, std::int8_t> {
constexpr static flag flags{};
};
// Positive/negative fixint represent the literal value specified, do not
// need to mask it off.
struct positive_fixint : fixtype<0x00, 0x7f> {
constexpr static flag flags{};
};
struct uint8 : format<0xcc, std::uint8_t> {};
struct uint16 : format<0xcd, std::uint16_t> {};
struct uint32 : format<0xce, std::uint32_t> {};
struct uint64 : format<0xcf, std::uint64_t> {};
struct negative_fixint : fixtype<0xe0, 0x1f, std::int8_t> {
constexpr static flag flags{};
};
struct int8 : format<0xd0, std::int8_t> {};
struct int16 : format<0xd1, std::int16_t> {};
struct int32 : format<0xd2, std::int32_t> {};
struct int64 : format<0xd3, std::int64_t> {};
struct uint8 : format<0xcc, std::uint8_t> {};
/*
* Other primitive types
*/
struct nil : fixtype<0xc0, 0x00> {
using value_type = msgpack::nil;
constexpr static flag flags{flag::fixed_size | flag::apply_mask};
};
struct uint16 : format<0xcd, std::uint16_t> {};
struct invalid : fixtype<0xc1, 0x00> {
using value_type = msgpack::invalid;
constexpr static flag flags{flag::fixed_size | flag::apply_mask};
};
struct uint32 : format<0xce, std::uint32_t> {};
struct boolean : fixtype<0xc2, 0x01> {
using value_type = bool;
constexpr static flag flags{flag::fixed_size | flag::apply_mask};
};
struct uint64 : format<0xcf, std::uint64_t> {};
/*
* Maps
*/
struct int8 : format<0xd0, std::int8_t> {};
template <typename Fmt> struct map_format : Fmt {
using value_type = map_desc;
};
struct int16 : format<0xd1, std::int16_t> {};
struct fixmap : map_format<fixtype<0x80, 0x0f>> {};
struct map16 : map_format<format<0xde, std::uint16_t>> {};
struct map32 : map_format<format<0xdf, std::uint32_t>> {};
struct int32 : format<0xd2, std::int32_t> {};
/*
* Arrays
*/
struct int64 : format<0xd3, std::int64_t> {};
template <typename Fmt> struct array_format : Fmt {
using value_type = array_desc;
};
/*
* Other primitive types
*/
struct nil : fixtype<0xc0, 0x00> {
using value_type = msgpack::nil;
constexpr static flag flags{flag::fixed_size | flag::apply_mask};
};
struct fixarray : array_format<fixtype<0x90, 0x0f>> {};
struct array16 : array_format<format<0xdc, std::uint16_t>> {};
struct array32 : array_format<format<0xdd, std::uint32_t>> {};
struct invalid : fixtype<0xc1, 0x00> {
using value_type = msgpack::invalid;
constexpr static flag flags{flag::fixed_size | flag::apply_mask};
};
/*
* Strings
*/
struct boolean : fixtype<0xc2, 0x01> {
using value_type = bool;
constexpr static flag flags{flag::fixed_size | flag::apply_mask};
};
template <typename Fmt> struct string_format : Fmt {
using value_type = std::string_view;
constexpr static auto payload_type = payload::variable;
};
/*
* Maps
*/
template <typename Fmt>
struct map_format : Fmt {
using value_type = map_desc;
};
struct fixstr : string_format<fixtype<0xa0, 0x1f>> {};
struct str8 : string_format<format<0xd9, std::uint8_t>> {};
struct str16 : string_format<format<0xda, std::uint16_t>> {};
struct str32 : string_format<format<0xdb, std::uint32_t>> {};
struct fixmap : map_format<fixtype<0x80, 0x0f>> {};
/*
* Binary arrays
*/
struct map16 : map_format<format<0xde, std::uint16_t>> {};
template <typename Fmt> struct bin_format : Fmt {
using value_type = std::span<std::byte const>;
constexpr static auto payload_type = payload::variable;
};
struct map32 : map_format<format<0xdf, std::uint32_t>> {};
struct bin8 : bin_format<format<0xc4, std::uint8_t>> {};
struct bin16 : bin_format<format<0xc5, std::uint16_t>> {};
struct bin32 : bin_format<format<0xc6, std::uint32_t>> {};
/*
* Arrays
*/
/*
* Extension types, not yet supported.
*/
template <typename Fmt>
struct array_format : Fmt {
using value_type = array_desc;
};
template <typename Fmt> struct unimplemented_format : Fmt {};
struct fixarray : array_format<fixtype<0x90, 0x0f>> {};
struct fixext1 : unimplemented_format<fixtype<0xd4, 0x00>> {};
struct fixext2 : unimplemented_format<fixtype<0xd5, 0x00>> {};
struct fixext4 : unimplemented_format<fixtype<0xd6, 0x00>> {};
struct fixext8 : unimplemented_format<fixtype<0xd7, 0x00>> {};
struct fixext16 : unimplemented_format<fixtype<0xd8, 0x00>> {};
struct ext8 : unimplemented_format<format<0xc7, std::uint8_t>> {};
struct ext16 : unimplemented_format<format<0xc8, std::uint16_t>> {};
struct ext32 : unimplemented_format<format<0xc9, std::uint32_t>> {};
struct array16 : array_format<format<0xdc, std::uint16_t>> {};
template <typename T>
struct unimplemented : std::false_type {};
struct array32 : array_format<format<0xdd, std::uint32_t>> {};
template <typename Fmt>
struct unimplemented<unimplemented_format<Fmt>> : std::true_type {};
/*
* Strings
*/
template <typename Fmt>
struct string_format : Fmt {
using value_type = std::string_view;
constexpr static auto payload_type = payload::variable;
};
struct fixstr : string_format<fixtype<0xa0, 0x1f>> {};
struct str8 : string_format<format<0xd9, std::uint8_t>> {};
struct str16 : string_format<format<0xda, std::uint16_t>> {};
struct str32 : string_format<format<0xdb, std::uint32_t>> {};
/*
* Binary arrays
*/
template <typename Fmt>
struct bin_format : Fmt {
using value_type = std::span<std::byte const>;
constexpr static auto payload_type = payload::variable;
};
struct bin8 : bin_format<format<0xc4, std::uint8_t>> {};
struct bin16 : bin_format<format<0xc5, std::uint16_t>> {};
struct bin32 : bin_format<format<0xc6, std::uint32_t>> {};
/*
* Extension types, not yet supported.
*/
template <typename Fmt>
struct unimplemented_format : Fmt {};
struct fixext1 : unimplemented_format<fixtype<0xd4, 0x00>> {};
struct fixext2 : unimplemented_format<fixtype<0xd5, 0x00>> {};
struct fixext4 : unimplemented_format<fixtype<0xd6, 0x00>> {};
struct fixext8 : unimplemented_format<fixtype<0xd7, 0x00>> {};
struct fixext16 : unimplemented_format<fixtype<0xd8, 0x00>> {};
struct ext8 : unimplemented_format<format<0xc7, std::uint8_t>> {};
struct ext16 : unimplemented_format<format<0xc8, std::uint16_t>> {};
struct ext32 : unimplemented_format<format<0xc9, std::uint32_t>> {};
template <typename T>
struct unimplemented : std::false_type {};
template <typename Fmt>
struct unimplemented<unimplemented_format<Fmt>> : std::true_type {};
namespace detail {
// Simple typelist for looking up compatible types.
// TODO: Use traits to generate the compatibility list automatically?
template <std::size_t I, typename T>
struct type_list_entry { using type = T; };
// Simple typelist for looking up compatible types.
// TODO: Use traits to generate the compatibility list automatically?
template <std::size_t I, typename T>
struct type_list_entry {
using type = T;
};
template <typename...>
struct type_list_impl;
template <typename...>
struct type_list_impl;
template <std::size_t... Is, typename... Ts>
struct type_list_impl<std::index_sequence<Is...>, Ts...> :
type_list_entry<Is, Ts>... {
static constexpr auto size = sizeof...(Ts);
};
template <std::size_t... Is, typename... Ts>
struct type_list_impl<std::index_sequence<Is...>, Ts...>
: type_list_entry<Is, Ts>... {
static constexpr auto size = sizeof...(Ts);
};
template <typename T> struct typelist_lookup_tag { using type = T; };
template <typename T>
struct typelist_lookup_tag {
using type = T;
};
template <std::size_t I, typename T>
typelist_lookup_tag<T> typelist_lookup(type_list_entry<I, T> const&);
template <std::size_t I, typename T>
typelist_lookup_tag<T> typelist_lookup(type_list_entry<I, T> const&);
template <std::size_t I, typename List>
using type_list_at = typename decltype(
typelist_lookup<I>(std::declval<List>()))::type;
template <std::size_t I, typename List>
using type_list_at =
typename decltype(typelist_lookup<I>(std::declval<List>()))::type;
template <typename... Ts>
using type_list = detail::type_list_impl<
std::make_index_sequence<sizeof...(Ts)>, Ts...>;
template <typename... Ts>
using type_list =
detail::type_list_impl<std::make_index_sequence<sizeof...(Ts)>, Ts...>;
template <typename, typename, template <typename> typename>
struct filter_type_list;
template <typename, typename, template <typename> typename>
struct filter_type_list;
template <typename... Ts, template <typename> typename Predicate>
struct filter_type_list<type_list<>, type_list<Ts...>, Predicate> {
using type = type_list<Ts...>;
};
template <typename... Ts, template <typename> typename Predicate>
struct filter_type_list<type_list<>, type_list<Ts...>, Predicate> {
using type = type_list<Ts...>;
};
template <typename Head, typename... Tail, typename... Ts, template <typename> typename Predicate>
struct filter_type_list<type_list<Head, Tail...>, type_list<Ts...>, Predicate> {
using type = typename std::conditional_t<
Predicate<Head>::value,
filter_type_list<type_list<Tail...>, type_list<Ts..., Head>, Predicate>,
filter_type_list<type_list<Tail...>, type_list<Ts...>, Predicate>>::type;
};
template <typename Head, typename... Tail, typename... Ts,
template <typename> typename Predicate>
struct filter_type_list<type_list<Head, Tail...>, type_list<Ts...>, Predicate> {
using type = typename std::conditional_t<Predicate<Head>::value,
filter_type_list<type_list<Tail...>, type_list<Ts..., Head>,
Predicate>,
filter_type_list<type_list<Tail...>, type_list<Ts...>,
Predicate>>::type;
};
template <typename List, template <typename> typename Predicate>
using filter = filter_type_list<List, type_list<>, Predicate>;
template <typename List, template <typename> typename Predicate>
using filter = filter_type_list<List, type_list<>, Predicate>;
template <typename List, template <typename> typename Predicate>
using filter_t = typename filter<List, Predicate>::type;
template <typename List, template <typename> typename Predicate>
using filter_t = typename filter<List, Predicate>::type;
} // namespace detail
} // namespace format
@ -358,35 +397,33 @@ template <typename T>
concept is_fixtype = std::is_base_of_v<format::fixtype_format, T>;
namespace format {
// This template instantiates a format::traits object for a given format.
// This should only occur once per format type, and should happen at
// compile time.
//
// This isn't the prettiest way to go about it.
template <format_type Format>
inline traits const& get_traits() noexcept {
constexpr static auto inst = []{
// Fixtypes define the size within the identifier byte, so the
// trait size must be zero.
auto size = is_fixtype<Format> ?
0 : sizeof(typename Format::first_type);
// This template instantiates a format::traits object for a given format.
// This should only occur once per format type, and should happen at
// compile time.
//
// This isn't the prettiest way to go about it.
template <format_type Format>
inline traits const& get_traits() noexcept {
constexpr static auto inst = [] {
// Fixtypes define the size within the identifier byte, so the
// trait size must be zero.
auto size =
is_fixtype<Format> ? 0 : sizeof(typename Format::first_type);
traits inst{
.flags = Format::flags,
traits inst{.flags = Format::flags,
.size = std::uint8_t(size),
.token_type = resolve_type_v<typename Format::value_type>
};
// Add in the fixed_size flag for integral types.
if constexpr (std::is_integral_v<typename Format::value_type>) {
inst.flags |= flag::fixed_size;
}
if constexpr (is_fixtype<Format>) {
inst.mask = Format::mask;
}
return inst;
}();
.token_type = resolve_type_v<typename Format::value_type>};
// Add in the fixed_size flag for integral types.
if constexpr (std::is_integral_v<typename Format::value_type>) {
inst.flags |= flag::fixed_size;
}
if constexpr (is_fixtype<Format>) {
inst.mask = Format::mask;
}
return inst;
};
}();
return inst;
};
} // namespace format
template <format_type... Formats>

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: msgpack
//
@ -14,13 +14,13 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifndef msgpack_core_reader_6c2f66f02585565
#define msgpack_core_reader_6c2f66f02585565
#include "../util/endianness.h"
#include "error.h"
#include "format.h"
#include "../util/endianness.h"
#include <tl/expected.hpp>
@ -35,268 +35,265 @@ namespace msgpack {
*/
namespace reader_policy {
struct relaxed {}; // Similar formats are acceptable.
struct strict {}; // Formats must exactly match the caller's request.
} // namespace reader_policy
namespace detail {
// Generic mechanism for reading a number of bytes out of an interator.
template <std::size_t N, typename Itr>
constexpr inline decltype(auto) read_bytes(Itr& inp) noexcept {
std::array<std::byte, N> out;
auto const end = inp + N;
auto dst = out.begin();
while (inp != end) {
*dst = std::byte{*inp};
++dst; ++inp;
}
return out;
// Generic mechanism for reading a number of bytes out of an interator.
template <std::size_t N, typename Itr>
constexpr inline decltype(auto) read_bytes(Itr& inp) noexcept {
std::array<std::byte, N> out;
auto const end = inp + N;
auto dst = out.begin();
while (inp != end) {
*dst = std::byte{*inp};
++dst;
++inp;
}
return out;
}
// Read an integral type out of the iterator.
template <std::integral T, typename Itr>
constexpr inline decltype(auto) read_integral(Itr& inp) noexcept {
constexpr auto N = sizeof(T);
if constexpr (sizeof(T) == 1) {
return T(*inp++);
} else {
return be_to_host(::detail::value_cast<T>(read_bytes<N>(inp)));
}
}
// This helper structure provides a customization point for converting an
// iterator to some other type (e.g. a pointer) in the event that the
// final format does not have a suitable constructor. It's only currently
// used for string_view, but may be overridden on platforms that provide
// their own types.
template <typename T, typename Iter>
struct iterator_adapter {
static constexpr auto convert(Iter itr) { return itr; }
};
template <typename Iter>
struct iterator_adapter<std::string_view, Iter> {
static constexpr auto convert(Iter itr) {
return ::detail::value_cast<std::string_view::pointer>(&*itr);
}
};
template <typename T>
using expected = tl::expected<T, error>;
template <format_type F, typename Iter>
constexpr inline bool accept(Iter& itr) noexcept {
if constexpr (is_fixtype<F>) {
// Don't advance the iterator -- part of the format is locked away
// within the format byte.
return (std::byte{*itr} & ~F::mask) == F::marker;
} else {
// Advance the iterator past the format byte.
return std::byte{*itr++} == F::marker;
}
}
// Read a value from a MessagePack message. Assumes that itr != end to
// start with.
template <format_type F, typename Iter>
requires(!format::unimplemented<F>::value)
constexpr inline auto read(Iter& itr, Iter const end) noexcept
-> expected<typename F::value_type> {
// Copy off the iterator. Update it at the end _if_ everything goes
// smoothly.
auto cur = itr;
if (!accept<F>(cur)) return tl::make_unexpected(error::wrong_type);
// First thing's first. Read the format's "first_type", which is either
// the payload or the length of the payload. Ensure we have enough data
// from the span before we do so, though.
using first_type = typename F::first_type;
using value_type = typename F::value_type;
using diff_type = typename std::iterator_traits<Iter>::difference_type;
if (std::distance(cur, end) < diff_type{sizeof(first_type)}) {
return tl::make_unexpected(error::incomplete_message);
}
// Read an integral type out of the iterator.
template <std::integral T, typename Itr>
constexpr inline decltype(auto) read_integral(Itr& inp) noexcept {
constexpr auto N = sizeof(T);
if constexpr (sizeof(T) == 1) {
return T(*inp++);
} else {
return be_to_host(::detail::value_cast<T>(read_bytes<N>(inp)));
}
auto f = read_integral<first_type>(cur);
if constexpr (is_fixtype<F> && (F::flags & format::flag::apply_mask)) {
f &= decltype(f)(F::mask);
}
// This helper structure provides a customization point for converting an
// iterator to some other type (e.g. a pointer) in the event that the
// final format does not have a suitable constructor. It's only currently
// used for string_view, but may be overridden on platforms that provide
// their own types.
template <typename T, typename Iter>
struct iterator_adapter {
static constexpr auto convert(Iter itr) { return itr; }
};
template <typename Iter>
struct iterator_adapter<std::string_view, Iter> {
static constexpr auto convert(Iter itr) {
return ::detail::value_cast<std::string_view::pointer>(&*itr); }
};
template <typename T>
using expected = tl::expected<T, error>;
template <format_type F, typename Iter>
constexpr inline bool accept(Iter& itr) noexcept {
if constexpr (is_fixtype<F>) {
// Don't advance the iterator -- part of the format is locked away
// within the format byte.
return (std::byte{*itr} & ~F::mask) == F::marker;
} else {
// Advance the iterator past the format byte.
return std::byte{*itr++} == F::marker;
}
}
// Read a value from a MessagePack message. Assumes that itr != end to
// start with.
template <format_type F, typename Iter>
requires (!format::unimplemented<F>::value)
constexpr inline auto read(Iter& itr, Iter const end) noexcept ->
expected<typename F::value_type> {
// Copy off the iterator. Update it at the end _if_ everything goes
// smoothly.
auto cur = itr;
if (!accept<F>(cur)) return tl::make_unexpected(error::wrong_type);
// First thing's first. Read the format's "first_type", which is either
// the payload or the length of the payload. Ensure we have enough data
// from the span before we do so, though.
using first_type = typename F::first_type;
using value_type = typename F::value_type;
using diff_type = typename std::iterator_traits<Iter>::difference_type;
if (std::distance(cur, end) < diff_type{sizeof(first_type)}) {
if constexpr (F::payload_type == format::payload::fixed) {
// We've read all we need to read. Update itr and return the value.
itr = cur;
return value_type(f);
} else {
// We're reading a variable length payload. `f` is the length of the
// payload. Ensure that the span has enough data and construct the
// value_type accordingly.
if (std::distance(cur, end) < diff_type{f}) {
return tl::make_unexpected(error::incomplete_message);
}
auto f = read_integral<first_type>(cur);
if constexpr (is_fixtype<F> && (F::flags & format::flag::apply_mask)) {
f &= decltype(f)(F::mask);
}
itr = cur + f;
using adapt = iterator_adapter<value_type, Iter>;
return value_type(adapt::convert(cur), f);
}
}
if constexpr (F::payload_type == format::payload::fixed) {
// We've read all we need to read. Update itr and return the value.
itr = cur;
return value_type(f);
} else {
// We're reading a variable length payload. `f` is the length of the
// payload. Ensure that the span has enough data and construct the
// value_type accordingly.
if (std::distance(cur, end) < diff_type{f}) {
return tl::make_unexpected(error::incomplete_message);
}
// "Relaxed" reader policies allow for certain conversions to take place
// at runtime. For instance, any smaller type of the same category is
// allowed.
//
// TODO: Possible improvement to readability; specify a single format list
// containing all formats, and filter them down for each instantiated
// format. Note that this would probably have compile-time performance
// implications.
itr = cur + f;
using adapt = iterator_adapter<value_type, Iter>;
return value_type(adapt::convert(cur), f);
}
template <format_type>
struct relaxed_conversions {};
template <>
struct relaxed_conversions<format::uint8> {
using type_list = format_list<format::uint8, format::positive_fixint>;
};
template <>
struct relaxed_conversions<format::uint16> {
using type_list =
format_list<format::uint16, format::uint8, format::positive_fixint>;
};
template <>
struct relaxed_conversions<format::uint32> {
using type_list = format_list<format::uint32, format::uint16, format::uint8,
format::positive_fixint>;
};
template <>
struct relaxed_conversions<format::uint64> {
using type_list = format_list<format::uint64, format::uint32,
format::uint16, format::uint8, format::positive_fixint>;
};
template <>
struct relaxed_conversions<format::int8> {
using type_list = format_list<format::int8, format::negative_fixint>;
};
template <>
struct relaxed_conversions<format::int16> {
using type_list =
format_list<format::int16, format::int8, format::negative_fixint>;
};
template <>
struct relaxed_conversions<format::int32> {
using type_list = format_list<format::int32, format::int16, format::int8,
format::negative_fixint>;
};
template <>
struct relaxed_conversions<format::int64> {
using type_list = format_list<format::int64, format::int32, format::int16,
format::int8, format::negative_fixint>;
};
template <>
struct relaxed_conversions<format::str8> {
using type_list = format_list<format::str8, format::fixstr>;
};
template <>
struct relaxed_conversions<format::str16> {
using type_list = format_list<format::str16, format::str8, format::fixstr>;
};
template <>
struct relaxed_conversions<format::map16> {
using type_list = format_list<format::map16, format::fixmap>;
};
template <>
struct relaxed_conversions<format::map32> {
using type_list = format_list<format::map32, format::map16, format::fixmap>;
};
template <typename F>
concept has_conversions = format_type<F> && requires {
typename relaxed_conversions<F>::type_list;
};
template <typename policy>
struct reader_policy_traits {};
template <>
struct reader_policy_traits<reader_policy::strict> {
template <format_type F, typename Iter>
constexpr static bool accept(Iter itr) noexcept {
return detail::accept<F>(itr);
}
// "Relaxed" reader policies allow for certain conversions to take place
// at runtime. For instance, any smaller type of the same category is
// allowed.
//
// TODO: Possible improvement to readability; specify a single format list
// containing all formats, and filter them down for each instantiated
// format. Note that this would probably have compile-time performance
// implications.
template <format_type>
struct relaxed_conversions {};
template <>
struct relaxed_conversions<format::uint8> {
using type_list = format_list<format::uint8, format::positive_fixint>;
template <format_type F, typename Iter>
constexpr static decltype(auto) read(Iter& itr, Iter const end) noexcept {
return detail::read<F>(itr, end);
};
};
template <>
struct relaxed_conversions<format::uint16> {
using type_list = format_list<format::uint16, format::uint8,
format::positive_fixint>;
};
template <>
struct relaxed_conversions<format::uint32> {
using type_list = format_list<format::uint32, format::uint16,
format::uint8, format::positive_fixint>;
};
template <>
struct relaxed_conversions<format::uint64> {
using type_list = format_list<format::uint64, format::uint32,
format::uint16, format::uint8, format::positive_fixint>;
};
template <>
struct relaxed_conversions<format::int8> {
using type_list = format_list<format::int8, format::negative_fixint>;
};
template <>
struct relaxed_conversions<format::int16> {
using type_list = format_list<format::int16, format::int8,
format::negative_fixint>;
};
template <>
struct relaxed_conversions<format::int32> {
using type_list = format_list<format::int32, format::int16,
format::int8, format::negative_fixint>;
};
template <>
struct relaxed_conversions<format::int64> {
using type_list = format_list<format::int64, format::int32,
format::int16, format::int8, format::negative_fixint>;
};
template <>
struct relaxed_conversions<format::str8> {
using type_list = format_list<format::str8, format::fixstr>;
};
template <>
struct relaxed_conversions<format::str16> {
using type_list = format_list<format::str16, format::str8,
format::fixstr>;
};
template <>
struct relaxed_conversions<format::map16> {
using type_list = format_list<format::map16, format::fixmap>;
};
template <>
struct relaxed_conversions<format::map32> {
using type_list = format_list<format::map32, format::map16,
format::fixmap>;
};
template <typename F>
concept has_conversions = format_type<F> && requires {
typename relaxed_conversions<F>::type_list;
};
template <typename policy>
struct reader_policy_traits {};
template <>
struct reader_policy_traits<reader_policy::strict> {
template <format_type F, typename Iter>
constexpr static bool accept(Iter itr) noexcept {
return detail::accept<F>(itr);
template <format_type fmt, typename fmtlist, typename Iter, std::size_t I = 0>
constexpr static auto relaxed_read_impl(Iter& itr, Iter const end) noexcept
-> expected<typename fmt::value_type> {
if constexpr (I < fmtlist::size) {
using this_format = format_list_at<I, fmtlist>;
auto v = detail::read<this_format>(itr, end);
if (v.has_value()) {
return v.value();
} else if (v.error() == error::wrong_type) {
return relaxed_read_impl<fmt, fmtlist, Iter, I + 1>(itr, end);
} else {
return v;
}
} else {
return tl::make_unexpected(error::wrong_type);
}
}
template <format_type F, typename Iter>
constexpr static decltype(auto) read(Iter& itr, Iter const end)
noexcept {
template <>
struct reader_policy_traits<reader_policy::relaxed> {
template <format_type F, typename Iter>
constexpr static decltype(auto) read(Iter& itr, Iter const& end) noexcept {
if constexpr (has_conversions<F>) {
using format_list = typename relaxed_conversions<F>::type_list;
return relaxed_read_impl<F, format_list, Iter, 0>(itr, end);
} else {
// Fall back on strict policy
return detail::read<F>(itr, end);
};
};
template <format_type fmt, typename fmtlist, typename Iter,
std::size_t I = 0>
constexpr static auto relaxed_read_impl(Iter& itr, Iter const end)
noexcept -> expected<typename fmt::value_type> {
if constexpr (I < fmtlist::size) {
using this_format = format_list_at<I, fmtlist>;
auto v = detail::read<this_format>(itr, end);
if (v.has_value()) {
return v.value();
} else if (v.error() == error::wrong_type) {
return relaxed_read_impl<fmt, fmtlist, Iter, I + 1>(itr, end);
} else {
return v;
}
} else {
return tl::make_unexpected(error::wrong_type);
}
}
template <>
struct reader_policy_traits<reader_policy::relaxed> {
template <format_type F, typename Iter>
constexpr static decltype(auto) read(Iter& itr, Iter const& end)
noexcept {
if constexpr (has_conversions<F>) {
using format_list = typename relaxed_conversions<F>::type_list;
return relaxed_read_impl<F, format_list, Iter, 0>(itr, end);
} else {
// Fall back on strict policy
return detail::read<F>(itr, end);
}
}
};
};
} // namespace detail
template <typename policy = reader_policy::relaxed>
class reader {
public:
template <typename T>
using expected = detail::expected<T>;
constexpr reader(std::span<std::byte const> src) :
data(src), curr(std::begin(data)), end(std::end(data)) {}
constexpr reader(std::span<std::byte const> src)
: data(src)
, curr(std::begin(data))
, end(std::end(data)) {}
template <format_type F>
constexpr expected<typename F::value_type> read() noexcept {
if (curr == end) return tl::make_unexpected(error::end_of_message);
return detail::reader_policy_traits<policy>::template read<F>(curr, end);
return detail::reader_policy_traits<policy>::template read<F>(
curr, end);
}
constexpr auto pos() const noexcept {
return curr;
}
constexpr auto pos() const noexcept { return curr; }
constexpr auto subview() const noexcept {
return std::span<std::byte const>(curr, end);

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: msgpack
//
@ -14,13 +14,13 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifndef msgpack_core_writer_ce48a51aa6ed0858
#define msgpack_core_writer_ce48a51aa6ed0858
#include "../util/endianness.h"
#include "error.h"
#include "format.h"
#include "../util/endianness.h"
#include <limits>
#include <type_traits>
@ -37,15 +37,15 @@ enum class writer_error {
};
// Helper template for writing a non-integral datatype to an output.
//template <typename T>
//struct write_adapter {
//template <typename Itr>
//static constexpr tl::expected<Itr, error> write(T const& t);
// template <typename T>
// struct write_adapter {
// template <typename Itr>
// static constexpr tl::expected<Itr, error> write(T const& t);
//};
template <std::size_t N, typename Itr>
constexpr inline decltype(auto) write_bytes(std::array<std::byte, N>&& data,
Itr out) noexcept {
constexpr inline decltype(auto) write_bytes(
std::array<std::byte, N>&& data, Itr out) noexcept {
for (auto b : data) {
*out++ = b;
}
@ -57,9 +57,7 @@ struct write_adapter {};
template <std::integral T>
struct write_adapter<T> {
static constexpr auto size(T) noexcept {
return sizeof(T);
}
static constexpr auto size(T) noexcept { return sizeof(T); }
template <typename Itr>
static constexpr auto write(T t, Itr out) noexcept {
@ -75,7 +73,8 @@ struct write_adapter<std::string_view> {
template <typename Itr>
static constexpr auto write(std::string_view str, Itr out) noexcept {
std::byte const* beg = reinterpret_cast<std::byte const*>(&*str.begin());
std::byte const* beg =
reinterpret_cast<std::byte const*>(&*str.begin());
std::copy(beg, beg + str.size(), out);
return out += str.size();
}
@ -88,8 +87,8 @@ struct write_adapter<std::span<std::byte const>> {
}
template <typename Itr>
static constexpr auto write(std::span<std::byte const> bytes,
Itr out) noexcept {
static constexpr auto write(
std::span<std::byte const> bytes, Itr out) noexcept {
std::copy(bytes.begin(), bytes.end(), out);
return out += bytes.size();
}
@ -97,142 +96,137 @@ struct write_adapter<std::span<std::byte const>> {
template <>
struct write_adapter<map_desc> {
static constexpr auto value(map_desc desc) noexcept {
return desc.count;
}
static constexpr auto value(map_desc desc) noexcept { return desc.count; }
};
template <>
struct write_adapter<array_desc> {
static constexpr auto value(array_desc desc) noexcept {
return desc.count;
}
static constexpr auto value(array_desc desc) noexcept { return desc.count; }
};
// TODO: These could be optimized away because invalid/nil never contain real
// data.
template <>
struct write_adapter<invalid> {
static constexpr auto value(invalid) noexcept {
return 0;
}
static constexpr auto value(invalid) noexcept { return 0; }
};
template <>
struct write_adapter<nil> {
static constexpr auto value(nil) noexcept {
return 0;
}
static constexpr auto value(nil) noexcept { return 0; }
};
namespace detail {
template <typename T>
using expected = tl::expected<T, error>;
template <typename T>
using expected = tl::expected<T, error>;
template <format_type F>
constexpr inline std::size_t calculate_space(typename F::value_type val)
noexcept {
// At a minimum, one byte is needed to store the format.
std::size_t size = sizeof(typename F::first_type);
template <format_type F>
constexpr inline std::size_t calculate_space(
typename F::value_type val) noexcept {
// At a minimum, one byte is needed to store the format.
std::size_t size = sizeof(typename F::first_type);
if (!is_fixtype<F>) {
++size; // For format
}
if constexpr (F::payload_type == format::payload::variable) {
size += write_adapter<typename F::value_type>::size(val);
}
return size;
if (!is_fixtype<F>) {
++size; // For format
}
// The "first type" is either the size of the variable length payload or
// a fixed-length value. Additionally, this "first type" may be inlined
// into the marker byte if it's a fix type.
if constexpr (F::payload_type == format::payload::variable) {
size += write_adapter<typename F::value_type>::size(val);
}
template <format_type F>
constexpr inline expected<typename F::first_type> pack_first(
typename F::value_type value) noexcept {
using value_type = typename F::value_type;
if constexpr (F::payload_type == format::payload::variable) {
return typename F::first_type(write_adapter<value_type>::size(value));
return size;
}
// The "first type" is either the size of the variable length payload or
// a fixed-length value. Additionally, this "first type" may be inlined
// into the marker byte if it's a fix type.
template <format_type F>
constexpr inline expected<typename F::first_type> pack_first(
typename F::value_type value) noexcept {
using value_type = typename F::value_type;
if constexpr (F::payload_type == format::payload::variable) {
return typename F::first_type(write_adapter<value_type>::size(value));
} else {
if constexpr (requires { write_adapter<value_type>::value; }) {
return typename F::first_type(
write_adapter<value_type>::value(value));
} else {
if constexpr (requires { write_adapter<value_type>::value; }) {
return typename F::first_type(write_adapter<value_type>::value(value));
} else {
return typename F::first_type(value);
}
return typename F::first_type(value);
}
}
}
template <format_type F, typename Itr>
constexpr inline expected<Itr> write(
typename F::value_type&& value, Itr out, Itr const end) {
using diff_type = typename std::iterator_traits<Itr>::difference_type;
if (diff_type(calculate_space<F>(value)) > std::distance(out, end)) {
return tl::make_unexpected(error::out_of_space);
}
auto marker = F::marker;
auto result = pack_first<F>(value);
if (!result) {
return tl::make_unexpected(result.error());
}
if constexpr (is_fixtype<F>) {
if (*result > 0xff) {
return tl::make_unexpected(error::bad_value);
}
if constexpr (F::flags & format::flag::apply_mask) {
marker |= std::byte(*result);
} else {
marker = std::byte(*result);
}
if ((marker & ~F::mask) != F::marker) {
return tl::make_unexpected(error::bad_value);
}
}
template <format_type F, typename Itr>
constexpr inline expected<Itr> write(
typename F::value_type&& value, Itr out, Itr const end) {
using diff_type = typename std::iterator_traits<Itr>::difference_type;
if (diff_type(calculate_space<F>(value)) > std::distance(out, end)) {
return tl::make_unexpected(error::out_of_space);
}
*out++ = marker;
auto marker = F::marker;
auto result = pack_first<F>(value);
if (!result) {
return tl::make_unexpected(result.error());
}
if constexpr (is_fixtype<F>) {
if (*result > 0xff) {
return tl::make_unexpected(error::bad_value);
}
if constexpr (F::flags & format::flag::apply_mask) {
marker |= std::byte(*result);
} else {
marker = std::byte(*result);
}
if ((marker & ~F::mask) != F::marker) {
return tl::make_unexpected(error::bad_value);
}
}
*out++ = marker;
if constexpr (!is_fixtype<F>) {
out = write_adapter<typename decltype(result)::value_type>::write(*result, out);
}
if constexpr (F::payload_type == format::payload::variable) {
out = write_adapter<typename F::value_type>::write(value, out);
}
return out;
if constexpr (!is_fixtype<F>) {
out = write_adapter<typename decltype(result)::value_type>::write(
*result, out);
}
template <typename T>
struct format_hint;
if constexpr (F::payload_type == format::payload::variable) {
out = write_adapter<typename F::value_type>::write(value, out);
}
template <>
struct format_hint<std::uint8_t> {
using type = format::positive_fixint;
};
return out;
}
template <>
struct format_hint<std::uint16_t> {
using type = format::uint16;
};
template <typename T>
struct format_hint;
template <>
struct format_hint<std::uint8_t> {
using type = format::positive_fixint;
};
template <>
struct format_hint<std::uint16_t> {
using type = format::uint16;
};
} // namespace detail
class writer {
public:
template <typename T>
using expected = detail::expected<T>;
constexpr writer(std::span<std::byte> dest) :
data(dest), curr(std::begin(data)), end(std::end(data)) {}
constexpr writer(std::span<std::byte> dest)
: data(dest)
, curr(std::begin(data))
, end(std::end(data)) {}
template <format_type F>
constexpr expected<tl::monostate> write(typename F::value_type&& v)
noexcept {
constexpr expected<tl::monostate> write(
typename F::value_type&& v) noexcept {
using value_type = typename F::value_type;
if (curr == end) return tl::make_unexpected(error::out_of_space);
auto result = detail::write<F>(std::forward<value_type>(v), curr, end);
@ -245,14 +239,12 @@ public:
}
template <typename T>
requires requires { typename detail::format_hint<T>::type; }
requires requires { typename detail::format_hint<T>::type; }
constexpr expected<tl::monostate> write(T&& v) {
return write<typename detail::format_hint<T>::type>(std::forward<T>(v));
}
constexpr auto pos() const noexcept {
return curr;
}
constexpr auto pos() const noexcept { return curr; }
constexpr auto tell() const noexcept {
return std::distance(std::begin(data), curr);

View File

@ -1,8 +1,8 @@
#ifndef msgpack_object_f1f3a9e5c8be6a11
#define msgpack_object_f1f3a9e5c8be6a11
#include "token/type.h"
#include "token/reader.h"
#include "token/type.h"
#include "token/views.h"
#endif // msgpack_object_f1f3a9e5c8be6a11

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: msgpack
//
@ -17,14 +17,14 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifndef msgpack_token_reader_8daff350a0b1a519
#define msgpack_token_reader_8daff350a0b1a519
#include "type.h"
#include "../core/error.h"
#include "../util/endianness.h"
#include "../core/format.h"
#include "../util/endianness.h"
#include "type.h"
#include <tl/expected.hpp>
@ -44,8 +44,7 @@ constexpr std::int64_t sign_extend(std::size_t size, auto bytes) noexcept {
return {};
}
constexpr decltype(auto) read(std::size_t size, auto& inp)
noexcept {
constexpr decltype(auto) read(std::size_t size, auto& inp) noexcept {
std::array<std::byte, token::word_size> value{};
be_to_host(inp, inp + size, std::begin(value));
inp += size;
@ -65,7 +64,7 @@ constexpr inline format::traits const& traits_lookup(std::byte id) {
== format::negative_fixint::marker) {
return format::get_traits<format::negative_fixint>();
} else if ((id & ~format::positive_fixint::mask)
== format::positive_fixint::marker) {
== format::positive_fixint::marker) {
return format::get_traits<format::positive_fixint>();
} else if ((id & ~format::fixstr::mask) == format::fixstr::marker) {
return format::get_traits<format::fixstr>();
@ -76,8 +75,7 @@ constexpr inline format::traits const& traits_lookup(std::byte id) {
}
switch (id) {
case format::uint8::marker:
return format::get_traits<format::uint8>();
case format::uint8::marker: return format::get_traits<format::uint8>();
case format::uint16::marker:
return format::get_traits<format::uint16>();
case format::uint32::marker:
@ -85,47 +83,33 @@ constexpr inline format::traits const& traits_lookup(std::byte id) {
case format::uint64::marker:
return format::get_traits<format::uint64>();
case format::int8::marker:
return format::get_traits<format::int8>();
case format::int16::marker:
return format::get_traits<format::int16>();
case format::int32::marker:
return format::get_traits<format::int32>();
case format::int64::marker:
return format::get_traits<format::int64>();
case format::int8::marker: return format::get_traits<format::int8>();
case format::int16::marker: return format::get_traits<format::int16>();
case format::int32::marker: return format::get_traits<format::int32>();
case format::int64::marker: return format::get_traits<format::int64>();
case format::str8::marker:
return format::get_traits<format::str8>();
case format::str16::marker:
return format::get_traits<format::str16>();
case format::str32::marker:
return format::get_traits<format::str32>();
case format::bin8::marker:
return format::get_traits<format::bin8>();
case format::bin16::marker:
return format::get_traits<format::bin16>();
case format::bin32::marker:
return format::get_traits<format::bin32>();
case format::str8::marker: return format::get_traits<format::str8>();
case format::str16::marker: return format::get_traits<format::str16>();
case format::str32::marker: return format::get_traits<format::str32>();
case format::bin8::marker: return format::get_traits<format::bin8>();
case format::bin16::marker: return format::get_traits<format::bin16>();
case format::bin32::marker: return format::get_traits<format::bin32>();
case format::array16::marker:
return format::get_traits<format::array16>();
case format::array32::marker:
return format::get_traits<format::array32>();
case format::map16::marker:
return format::get_traits<format::map16>();
case format::map32::marker:
return format::get_traits<format::map32>();
case format::map16::marker: return format::get_traits<format::map16>();
case format::map32::marker: return format::get_traits<format::map32>();
case format::nil::marker:
return format::get_traits<format::nil>();
case format::nil::marker: return format::get_traits<format::nil>();
case format::invalid::marker:
return format::get_traits<format::invalid>();
case format::boolean::marker:
case format::boolean::marker | std::byte{1}:
return format::get_traits<format::boolean>();
default:
break;
default: break;
}
return format::no_traits;
@ -135,26 +119,22 @@ constexpr inline format::traits const& traits_lookup(std::byte id) {
class token_reader {
public:
constexpr token_reader(std::span<std::byte const> src) noexcept :
data_(src), curr_{} {}
constexpr token_reader(std::span<std::byte const> src) noexcept
: data_(src)
, curr_{} {}
constexpr auto current() const noexcept {
return std::next(data_.begin(), curr_);
}
constexpr auto end() const noexcept {
return data_.end();
}
constexpr auto end() const noexcept { return data_.end(); }
constexpr auto remaining(auto itr) noexcept {
using dist_type = decltype(std::distance(itr, data_.end()));
return std::max(dist_type(0), std::distance(itr, data_.end()));
}
constexpr auto remaining() noexcept {
return remaining(current());
}
constexpr auto remaining() noexcept { return remaining(current()); }
// Read the next token. If the reader currently points to the end of the
// byte buffer, then end_of_message is returned, and if there is still
@ -181,7 +161,7 @@ public:
}
// This is either the value of the format, or the size of the format.
auto first_bytes = [&]{
auto first_bytes = [&] {
if (traits.size) {
return detail::read(traits.size, curr);
} else {
@ -206,14 +186,11 @@ public:
case format::type::boolean:
tok = token{bool(first_bytes[0] & traits.mask)};
break;
case format::type::invalid:
tok = token{invalid{}};
break;
case format::type::nil:
tok = token{nil{}};
break;
case format::type::invalid: tok = token{invalid{}}; break;
case format::type::nil: tok = token{nil{}}; break;
case format::type::unsigned_int:
tok = detail::make_token<format::type::unsigned_int>(first_bytes);
tok = detail::make_token<format::type::unsigned_int>(
first_bytes);
break;
case format::type::signed_int: {
auto value = detail::sign_extend(traits.size, first_bytes);
@ -242,8 +219,7 @@ public:
tok = token{map_desc(var_size)};
break;
}
default:
return tl::make_unexpected(error::not_implemented);
default: return tl::make_unexpected(error::not_implemented);
}
curr_ = std::distance(data_.begin(), curr);
@ -261,9 +237,9 @@ public:
error err = error::end_of_message;
while (tok != token_buffer.end()) {
auto result = read_one().map([&tok](auto&& t){
*tok = t;
++tok;
auto result = read_one().map([&tok](auto&& t) {
*tok = t;
++tok;
});
if (!result) {
err = result.error();
@ -280,8 +256,7 @@ public:
}
template <typename T>
constexpr tl::expected<T, error> read() {
}
constexpr tl::expected<T, error> read() {}
private:
std::span<std::byte const> data_;

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: msgpack
//
@ -20,23 +20,22 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifndef msgpack_token_type_f43c22522692063f
#define msgpack_token_type_f43c22522692063f
#include "../core/error.h"
#include "../core/format.h"
#include <string_view>
#include <tl/expected.hpp>
#include <limits>
#include <memory>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <variant>
#include <vector>
#include <tl/expected.hpp>
namespace msgpack {
// This API is _currently_ optimizing on the fact that most desktop/server
@ -54,7 +53,8 @@ namespace msgpack {
// Of course, this means custom code!
template <std::integral Size, typename E>
requires (sizeof(Size) + sizeof(std::underlying_type_t<E>) <= sizeof(uintptr_t))
requires(sizeof(Size) + sizeof(std::underlying_type_t<E>)
<= sizeof(uintptr_t))
class size_and_enum {
public:
using size_type = Size;
@ -67,6 +67,7 @@ public:
// Constructors
constexpr size_and_enum() noexcept = default;
constexpr size_and_enum(size_type size, enum_type enum_value) noexcept {
set_both(size, enum_value);
}
@ -75,30 +76,34 @@ public:
constexpr auto get_size() const noexcept {
return static_cast<size_type>((value & size_mask) >> size_shift);
}
constexpr auto get_enum() const noexcept {
return static_cast<enum_type>((value & enum_mask));
}
constexpr auto rep() const noexcept { return value; }
// Mutators
constexpr auto set_size(size_type size) noexcept {
value = (static_cast<uintptr_t>(size) << size_shift) |
(value & enum_mask);
value = (static_cast<uintptr_t>(size) << size_shift)
| (value & enum_mask);
}
constexpr auto set_enum(enum_type enum_value) noexcept {
value = (static_cast<uintptr_t>(enum_value) & enum_mask) |
(value & size_mask);
value = (static_cast<uintptr_t>(enum_value) & enum_mask)
| (value & size_mask);
}
constexpr auto set_both(size_type size, enum_type enum_value) noexcept {
value = (static_cast<uintptr_t>(size) << size_shift) |
(static_cast<uintptr_t>(enum_value) & enum_mask);
value = (static_cast<uintptr_t>(size) << size_shift)
| (static_cast<uintptr_t>(enum_value) & enum_mask);
}
// Explicit conversion
constexpr explicit operator size_type() const noexcept {
return get_size();
}
constexpr explicit operator enum_type() const noexcept {
return get_enum();
}
@ -156,9 +161,10 @@ class token_base<8> {
public:
constexpr static std::size_t word_size = 8;
token_base() noexcept = default;
token_base(token_base const& other) noexcept
: value_(other.value_)
, size_and_type_(other.size_and_type_) {}
: value_(other.value_)
, size_and_type_(other.size_and_type_) {}
template <std::integral T>
explicit token_base(T value) noexcept {
@ -215,11 +221,12 @@ public:
template <typename T>
constexpr tl::expected<T, error> get() const noexcept;
template<std::integral T>
template <std::integral T>
constexpr tl::expected<T, error> get() const noexcept {
constexpr auto expected_type = std::is_same_v<T, bool> ?
format::type::boolean : std::is_signed_v<T> ?
format::type::signed_int : format::type::unsigned_int;
constexpr auto expected_type =
std::is_same_v<T, bool> ? format::type::boolean
: std::is_signed_v<T> ? format::type::signed_int
: format::type::unsigned_int;
if (type() != expected_type) {
return tl::make_unexpected(error::wrong_type);
@ -227,8 +234,8 @@ public:
if constexpr (expected_type == format::type::boolean) {
return T(value_.b);
} else if constexpr (expected_type == format::type::signed_int) {
if (std::numeric_limits<T>::max() < value_.i ||
std::numeric_limits<T>::lowest() > value_.i) {
if (std::numeric_limits<T>::max() < value_.i
|| std::numeric_limits<T>::lowest() > value_.i) {
return tl::make_unexpected(error::will_truncate);
}
return T(value_.i);
@ -266,94 +273,82 @@ private:
// TODO: What to store here for array/map types? Possibly a pointer
// to the end of the array/map, if/when known?
} value_;
size_and_enum<std::uint32_t, format::type> size_and_type_{};
};
template<>
inline tl::expected<std::string, error> token_base<8>::get()
const noexcept
{
template <>
inline tl::expected<std::string, error> token_base<8>::get() const noexcept {
if (type() != format::type::string) {
return tl::make_unexpected(error::wrong_type);
}
return std::string{value_.str, size_and_type_.get_size()};
}
template<>
constexpr tl::expected<std::string_view, error> token_base<8>::get()
const noexcept
{
template <>
constexpr tl::expected<std::string_view, error>
token_base<8>::get() const noexcept {
if (type() != format::type::string) {
return tl::make_unexpected(error::wrong_type);
}
return std::string_view{value_.str, size_and_type_.get_size()};
}
template <std::size_t N>
constexpr bool token_base<8>::operator==(char const (&t)[N]) const noexcept {
auto result = get<std::string_view>().map([&t](auto v) {
return v == std::string_view{t}; });
return result && *result;
}
template <std::size_t N>
constexpr bool token_base<8>::operator==(char const (&t)[N]) const noexcept {
auto result = get<std::string_view>().map(
[&t](auto v) { return v == std::string_view{t}; });
return result && *result;
}
template<>
inline tl::expected<std::vector<std::byte>, error> token_base<8>::get()
const noexcept
{
template <>
inline tl::expected<std::vector<std::byte>, error>
token_base<8>::get() const noexcept {
tl::expected<std::vector<std::byte>, error> result;
if (type() != format::type::binary) {
result = tl::make_unexpected(error::wrong_type);
} else {
result = std::vector<std::byte>(value_.bp,
value_.bp + size_and_type_.get_size());
result = std::vector<std::byte>(
value_.bp, value_.bp + size_and_type_.get_size());
}
return result;
}
template<>
template <>
constexpr tl::expected<std::span<std::byte const>, error>
token_base<8>::get() const noexcept
{
token_base<8>::get() const noexcept {
if (type() != format::type::binary) {
return tl::make_unexpected(error::wrong_type);
}
return std::span<std::byte const>(value_.bp, size_and_type_.get_size());
}
template<>
constexpr tl::expected<nil, error>
token_base<8>::get() const noexcept
{
template <>
constexpr tl::expected<nil, error> token_base<8>::get() const noexcept {
if (type() != format::type::nil) {
return tl::make_unexpected(error::wrong_type);
}
return nil{};
}
template<>
constexpr tl::expected<invalid, error>
token_base<8>::get() const noexcept
{
template <>
constexpr tl::expected<invalid, error> token_base<8>::get() const noexcept {
if (type() != format::type::invalid) {
return tl::make_unexpected(error::wrong_type);
}
return invalid{};
}
template<>
constexpr tl::expected<array_desc, error>
token_base<8>::get() const noexcept
{
template <>
constexpr tl::expected<array_desc, error> token_base<8>::get() const noexcept {
if (type() != format::type::array) {
return tl::make_unexpected(error::wrong_type);
}
return array_desc{size_and_type_.get_size()};
}
template<>
constexpr tl::expected<map_desc, error>
token_base<8>::get() const noexcept
{
template <>
constexpr tl::expected<map_desc, error> token_base<8>::get() const noexcept {
if (type() != format::type::map) {
return tl::make_unexpected(error::wrong_type);
}
@ -364,5 +359,4 @@ using token = token_base<sizeof(void*)>;
} // namespace msgpack
#endif // msgpack_token_type_f43c22522692063f

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: msgpack
//
@ -19,124 +19,119 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifndef msgpack_token_views_f19c250e782ed51c
#define msgpack_token_views_f19c250e782ed51c
#include "type.h"
#include <bits/iterator_concepts.h>
#include <fmt/format.h>
#include <ranges>
#include <fmt/format.h>
namespace msgpack {
template <std::ranges::view V>
requires std::ranges::input_range<V> && std::same_as<token,
std::ranges::range_value_t<V>>
struct map_view : public std::ranges::view_interface<map_view<V>> {
template <std::ranges::view V>
requires std::ranges::input_range<V>
&& std::same_as<token, std::ranges::range_value_t<V>>
struct map_view : public std::ranges::view_interface<map_view<V>> {
public:
class sentinel;
class iterator {
friend class sentinel;
using base_iterator = std::ranges::iterator_t<V>;
using base_sentinel = std::ranges::sentinel_t<V>;
using base_value_type = std::ranges::range_value_t<V>;
using base_reference = std::ranges::range_reference_t<V>;
base_iterator next(base_iterator current, std::size_t n = 1) {
while (n && current != std::ranges::end(*base_)) {
if (auto m = current->template get<map_desc>(); m) {
n += m->count * 2;
} else if (auto m = current->template get<array_desc>(); m) {
n += m->count;
}
++current;
--n;
}
return current;
}
public:
class sentinel;
using value_type = std::pair<base_value_type, base_value_type>;
using reference = std::pair<base_reference, base_reference>;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
class iterator {
friend class sentinel;
iterator() = default;
using base_iterator = std::ranges::iterator_t<V>;
using base_sentinel = std::ranges::sentinel_t<V>;
using base_value_type = std::ranges::range_value_t<V>;
using base_reference = std::ranges::range_reference_t<V>;
base_iterator next(base_iterator current, std::size_t n = 1) {
while (n && current != std::ranges::end(*base_)) {
if (auto m = current->template get<map_desc>(); m) {
n += m->count * 2;
} else if (auto m = current->template get<array_desc>(); m) {
n += m->count;
}
++current;
--n;
}
return current;
}
public:
using value_type = std::pair<base_value_type, base_value_type>;
using reference = std::pair<base_reference, base_reference>;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
iterator() = default;
iterator(V const& base)
: base_{&base}
, k_{std::ranges::begin(base)} {
// Ensure that k_ points to a map_desc. If not, then we
// effectively treat this as the end.
if (k_->type() == msgpack::format::type::map) {
remaining_ = k_->template get<msgpack::map_desc>()->count + 1;
// Advance to the first entry in the map.
++k_;
v_ = next(k_);
}
}
[[nodiscard]] reference operator*() const {
return { *k_, *v_ };
}
iterator& operator++() {
k_ = next(v_);
iterator(V const& base)
: base_{&base}
, k_{std::ranges::begin(base)} {
// Ensure that k_ points to a map_desc. If not, then we
// effectively treat this as the end.
if (k_->type() == msgpack::format::type::map) {
remaining_ = k_->template get<msgpack::map_desc>()->count + 1;
// Advance to the first entry in the map.
++k_;
v_ = next(k_);
--remaining_;
return *this;
}
[[nodiscard]] iterator operator++(int) {
auto tmp = *this;
++(*this);
return tmp;
}
[[nodiscard]] bool operator==(iterator const& rhs) const {
return k_ == rhs.remaining_ &&
base_ == rhs.base_;
}
private:
V const* base_{};
base_iterator k_{};
base_iterator v_{};
std::size_t remaining_{};
};
class sentinel {
public:
[[nodiscard]] bool operator==(sentinel const&) const {
return true;
}
[[nodiscard]] bool operator==(iterator const& rhs) const {
return rhs.remaining_ == 0
|| rhs.k_ == std::ranges::end(*rhs.base_);
}
};
constexpr map_view() noexcept = default;
constexpr map_view(V base) : base_{std::move(base)} {}
[[nodiscard]] constexpr iterator begin() const {
return { base_ };
}
[[nodiscard]] constexpr sentinel end() const {
return {};
[[nodiscard]] reference operator*() const { return {*k_, *v_}; }
iterator& operator++() {
k_ = next(v_);
v_ = next(k_);
--remaining_;
return *this;
}
[[nodiscard]] iterator operator++(int) {
auto tmp = *this;
++(*this);
return tmp;
}
[[nodiscard]] bool operator==(iterator const& rhs) const {
return k_ == rhs.remaining_ && base_ == rhs.base_;
}
private:
V base_;
V const* base_{};
base_iterator k_{};
base_iterator v_{};
std::size_t remaining_{};
};
template <class Range>
map_view(Range&&) -> map_view<std::views::all_t<Range>>;
class sentinel {
public:
[[nodiscard]] bool operator==(sentinel const&) const { return true; }
[[nodiscard]] bool operator==(iterator const& rhs) const {
return rhs.remaining_ == 0
|| rhs.k_ == std::ranges::end(*rhs.base_);
}
};
constexpr map_view() noexcept = default;
constexpr map_view(V base)
: base_{std::move(base)} {}
[[nodiscard]] constexpr iterator begin() const { return {base_}; }
[[nodiscard]] constexpr sentinel end() const { return {}; }
private:
V base_;
};
template <class Range>
map_view(Range&&) -> map_view<std::views::all_t<Range>>;
} // namespace msgpack
#endif // msgpack_token_views_f19c250e782ed51c

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: msgpack
//
@ -15,7 +15,7 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifndef msgpack_endianness_d8ae54f45851ed13
#define msgpack_endianness_d8ae54f45851ed13
@ -24,11 +24,7 @@
#include <cstdint>
#include <type_traits>
enum class endianness {
big,
little,
other
};
enum class endianness { big, little, other };
namespace detail {
@ -37,8 +33,8 @@ namespace detail {
*/
struct host_endianness_check {
constexpr static inline std::uint32_t impl = 0x01020304;
constexpr static inline auto test = static_cast<const unsigned char&>(impl);
constexpr static inline auto value = []{
constexpr static inline auto test = static_cast<unsigned char const&>(impl);
constexpr static inline auto value = [] {
switch (test) {
case 4: return endianness::little;
case 1: return endianness::big;
@ -66,7 +62,7 @@ constexpr void byte_swap(Iter begin, Iter end, OutIter dest) {
* byte_swap above should optimize to bswap or some equivalent assembly.
*/
template <endianness From, endianness To, std::size_t N>
requires (From != endianness::other && To != endianness::other)
requires(From != endianness::other && To != endianness::other)
constexpr auto maybe_swap(std::array<std::byte, N> val) noexcept {
if constexpr (From == To) {
return val;
@ -78,7 +74,7 @@ constexpr auto maybe_swap(std::array<std::byte, N> val) noexcept {
}
template <endianness From, endianness To, typename Iter, typename OutIter>
requires (From != endianness::other && To != endianness::other)
requires(From != endianness::other && To != endianness::other)
constexpr void maybe_swap_iter(Iter begin, Iter end, OutIter dest) noexcept {
if constexpr (From == To) {
std::copy(begin, end, dest);
@ -98,6 +94,7 @@ constexpr auto raw_cast(T val) noexcept {
T val;
std::array<std::byte, sizeof(T)> data;
};
trick_to_array u{val};
return u.data;
}
@ -112,6 +109,7 @@ constexpr auto value_cast(Array&& data) noexcept {
Array data;
T val;
};
trick_to_value u{std::forward<Array>(data)};
return u.val;
}
@ -120,14 +118,14 @@ constexpr auto value_cast(Array&& data) noexcept {
* Byte swap implementation for arbitrary endiannesses.
*/
template <endianness From, endianness To, typename T>
requires std::is_trivial_v<std::remove_reference_t<T>>
requires std::is_trivial_v<std::remove_reference_t<T>>
constexpr T byte_swap(T val) noexcept {
using array_type = std::array<std::byte, sizeof(T)>;
return std::bit_cast<T>(
maybe_swap<From, To>(std::bit_cast<array_type>(val)));
}
} // namespace detail;
} // namespace detail
/**
* Exposes endianness information about a target.

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: Proto
//
@ -14,16 +14,16 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifndef message_0c61530748b9f966
#define message_0c61530748b9f966
#include <cstdint>
#include <span>
#include <string_view>
#include <cstdint>
#include <variant>
#include <tl/expected.hpp>
#include <variant>
namespace parselink {
namespace proto {
@ -38,8 +38,8 @@ namespace proto {
// switch to something hand-crafted for saving bits.
struct error_message {
std::uint32_t code; // An error code
std::string_view what; // A string
std::uint32_t code; // An error code
std::string_view what; // A string
};
// C->S: Request to (re)connect.
@ -70,7 +70,7 @@ struct parser_data_message {
std::span<std::byte> payload;
};
} // namespace message
} // namespace proto
} // namespace parselink
#endif // message_0c61530748b9f966

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: Proto
//
@ -14,18 +14,17 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifndef session_07eae057feface79
#define session_07eae057feface79
#include <tl/expected.hpp>
#include "parselink/msgpack/token.h"
#include <cstdint>
#include <span>
#include <string>
#include <tl/expected.hpp>
namespace parselink {
namespace proto {
@ -59,8 +58,8 @@ public:
// the session is established or not.
constexpr static std::uint32_t max_message_size = 128 * 1024;
std::string user_id;
private:
};
@ -68,4 +67,3 @@ private:
} // namespace parselink
#endif // session_0c61530748b9f966

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: Server
//
@ -14,12 +14,12 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifndef server_5b46f075be3caa00
#define server_5b46f075be3caa00
#include <memory>
#include <cstdint>
#include <memory>
namespace parselink {

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: Utility
//
@ -15,16 +15,16 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifndef argparse_d2ddac0dab0d7b88
#define argparse_d2ddac0dab0d7b88
#include <chrono>
#include <charconv>
#include <chrono>
#include <initializer_list>
#include <optional>
#include <map>
#include <optional>
#include <span>
#include <stdexcept>
#include <string>
@ -37,210 +37,205 @@
namespace argparse {
namespace custom {
template <typename T>
struct argument_parser {};
namespace custom {
template <typename T>
struct argument_parser {};
template <typename T>
concept has_parser = requires {
{ argument_parser<std::decay_t<T>>::parse(std::string_view{}) }
-> std::same_as<T*>;
};
}
template <typename T>
concept has_parser = requires {
{
argument_parser<std::decay_t<T>>::parse(std::string_view{})
} -> std::same_as<T*>;
};
} // namespace custom
template <typename T>
struct argument_parser {};
template <typename T>
struct argument_parser {};
template <>
struct argument_parser<bool> {
static bool* parse(std::string_view value) noexcept {
if (value == "1" || value == "true") {
return new bool{true};
} else if (value == "0" || value == "false") {
return new bool{false};
}
return nullptr;
}
};
inline constexpr std::initializer_list<
std::tuple<char const*, std::chrono::nanoseconds>> unit_map = {
{"ns", std::chrono::nanoseconds{1}},
{"us", std::chrono::microseconds{1}},
{"ms", std::chrono::milliseconds{1}},
{"s", std::chrono::seconds{1}},
{"m", std::chrono::minutes{1}},
{"h", std::chrono::hours{1}},
};
template <typename rep, typename period>
struct argument_parser<std::chrono::duration<rep, period>> {
using duration = std::chrono::duration<rep, period>;
static duration* parse(std::string_view value) noexcept {
rep result;
auto err = std::from_chars(value.begin(), value.end(), result);
if (err.ec == std::errc{}) {
auto this_unit = std::string_view{err.ptr, value.end()};
for (auto const& [unit, dura] : unit_map) {
if (std::string_view{unit} == this_unit) {
auto v =
std::chrono::duration_cast<duration>(result * dura);
return new duration{v};
}
}
}
return nullptr;
}
};
template <typename T>
requires requires (T& t) {
std::from_chars(nullptr, nullptr, t);
}
struct argument_parser<T> {
static T* parse(std::string_view value) noexcept {
T result;
auto err = std::from_chars(value.begin(), value.end(), result);
return err.ec == std::errc{} ? new T{result} : nullptr;
}
};
template <>
struct argument_parser<std::string> {
static std::string* parse(std::string_view value) noexcept {
return new std::string{value};
}
};
template <typename T>
concept has_parser = requires {
{ argument_parser<std::decay_t<T>>::parse(std::string_view{}) }
-> std::same_as<T*>;
};
static_assert(has_parser<int>);
namespace detail {
// This wrapper acts similar to std::any, but also provides a method to
// parse a string_view for the value as well. Parsers can be implemented
// by creating an argparse::custom::argument_parser template specialization
// for a given type.
struct any_arg {
constexpr any_arg() = default;
template <typename T>
any_arg(T&& value) : iface(&dispatcher<std::decay_t<T>>::table) {
dispatcher<std::decay_t<T>>::create(*this, std::forward<T>(value));
}
any_arg(any_arg const& other) : iface(other.iface) {
if (other.iface) {
data = other.iface->copy(other.data);
}
}
any_arg(any_arg&& other) : data(std::move(other.data)), iface(other.iface) {
other.iface = nullptr;
}
any_arg& operator=(any_arg const& other) {
if (has_value()) {
iface->destroy(data);
}
iface = other.iface;
if (other.iface) {
data = other.iface->copy(other.data);
}
return *this;
}
~any_arg() {
if (has_value()) {
iface->destroy(data);
}
}
bool parse(std::string_view sv) {
return iface->parse(*this, sv);
}
bool has_value() const noexcept {
return static_cast<bool>(iface);
}
template <typename T>
bool holds() const noexcept {
return iface == &dispatcher<std::decay_t<T>>::table;
}
template <typename T>
friend T const* arg_cast(any_arg const*) noexcept;
void* data = nullptr;
struct interface {
void (*destroy)(void*);
void *(*copy)(void*);
bool (*parse)(any_arg&, std::string_view);
};
interface const* iface = nullptr;
template <typename T>
struct dispatcher {
template <typename... Args>
static void create(any_arg& self, Args&&... args) {
self.data = new T{std::forward<Args>(args)...};
}
static void* copy(void *ptr) {
return new T{*static_cast<T*>(ptr)};
}
static bool parse(any_arg& self, std::string_view sv) {
if constexpr (custom::has_parser<T>) {
self.data = custom::argument_parser<T>::parse(sv);
return static_cast<bool>(self.data);
} else if constexpr (has_parser<T>) {
self.data = argument_parser<T>::parse(sv);
return static_cast<bool>(self.data);
} else {
return false;
}
}
static void destroy(void* ptr) {
delete static_cast<T*>(ptr);
}
static T const* cast(void* ptr) {
return static_cast<T const*>(ptr);
}
static constexpr struct interface table {
&dispatcher::destroy, &dispatcher::copy, &dispatcher::parse };
};
};
template <typename T>
T const* arg_cast(any_arg const* ar) noexcept {
if (ar->holds<T>()) {
return any_arg::dispatcher<std::decay_t<T>>::cast(ar->data);
template <>
struct argument_parser<bool> {
static bool* parse(std::string_view value) noexcept {
if (value == "1" || value == "true") {
return new bool{true};
} else if (value == "0" || value == "false") {
return new bool{false};
}
return nullptr;
}
};
inline constexpr std::initializer_list<
std::tuple<char const*, std::chrono::nanoseconds>>
unit_map = {
{"ns", std::chrono::nanoseconds{1}},
{"us", std::chrono::microseconds{1}},
{"ms", std::chrono::milliseconds{1}},
{"s", std::chrono::seconds{1}},
{"m", std::chrono::minutes{1}},
{"h", std::chrono::hours{1}},
};
template <typename rep, typename period>
struct argument_parser<std::chrono::duration<rep, period>> {
using duration = std::chrono::duration<rep, period>;
static duration* parse(std::string_view value) noexcept {
rep result;
auto err = std::from_chars(value.begin(), value.end(), result);
if (err.ec == std::errc{}) {
auto this_unit = std::string_view{err.ptr, value.end()};
for (auto const& [unit, dura] : unit_map) {
if (std::string_view{unit} == this_unit) {
auto v =
std::chrono::duration_cast<duration>(result * dura);
return new duration{v};
}
}
}
return nullptr;
}
};
template <typename T>
requires requires(T& t) { std::from_chars(nullptr, nullptr, t); }
struct argument_parser<T> {
static T* parse(std::string_view value) noexcept {
T result;
auto err = std::from_chars(value.begin(), value.end(), result);
return err.ec == std::errc{} ? new T{result} : nullptr;
}
};
template <>
struct argument_parser<std::string> {
static std::string* parse(std::string_view value) noexcept {
return new std::string{value};
}
};
template <typename T>
concept has_parser = requires {
{
argument_parser<std::decay_t<T>>::parse(std::string_view{})
} -> std::same_as<T*>;
};
static_assert(has_parser<int>);
namespace detail {
// This wrapper acts similar to std::any, but also provides a method to
// parse a string_view for the value as well. Parsers can be implemented
// by creating an argparse::custom::argument_parser template specialization
// for a given type.
struct any_arg {
constexpr any_arg() = default;
template <typename T>
any_arg(T&& value)
: iface(&dispatcher<std::decay_t<T>>::table) {
dispatcher<std::decay_t<T>>::create(*this, std::forward<T>(value));
}
any_arg(any_arg const& other)
: iface(other.iface) {
if (other.iface) {
data = other.iface->copy(other.data);
}
}
any_arg(any_arg&& other)
: data(std::move(other.data))
, iface(other.iface) {
other.iface = nullptr;
}
any_arg& operator=(any_arg const& other) {
if (has_value()) {
iface->destroy(data);
}
iface = other.iface;
if (other.iface) {
data = other.iface->copy(other.data);
}
return *this;
}
~any_arg() {
if (has_value()) {
iface->destroy(data);
}
}
bool parse(std::string_view sv) { return iface->parse(*this, sv); }
bool has_value() const noexcept { return static_cast<bool>(iface); }
template <typename T>
bool holds() const noexcept {
return iface == &dispatcher<std::decay_t<T>>::table;
}
template <typename T>
friend T const* arg_cast(any_arg const*) noexcept;
void* data = nullptr;
struct interface {
void (*destroy)(void*);
void* (*copy)(void*);
bool (*parse)(any_arg&, std::string_view);
};
interface const* iface = nullptr;
template <typename T>
struct dispatcher {
template <typename... Args>
static void create(any_arg& self, Args&&... args) {
self.data = new T{std::forward<Args>(args)...};
}
static void* copy(void* ptr) { return new T{*static_cast<T*>(ptr)}; }
static bool parse(any_arg& self, std::string_view sv) {
if constexpr (custom::has_parser<T>) {
self.data = custom::argument_parser<T>::parse(sv);
return static_cast<bool>(self.data);
} else if constexpr (has_parser<T>) {
self.data = argument_parser<T>::parse(sv);
return static_cast<bool>(self.data);
} else {
return false;
}
}
static void destroy(void* ptr) { delete static_cast<T*>(ptr); }
static T const* cast(void* ptr) { return static_cast<T const*>(ptr); }
static constexpr struct interface table {
&dispatcher::destroy, &dispatcher::copy, &dispatcher::parse
};
};
};
template <typename T>
T const* arg_cast(any_arg const* ar) noexcept {
if (ar->holds<T>()) {
return any_arg::dispatcher<std::decay_t<T>>::cast(ar->data);
}
return nullptr;
}
} // namespace detail
class command_line_parser {
public:
using argument = detail::any_arg;
constexpr static char delimiter = '=';
using opt_list = std::initializer_list<
std::tuple<std::string_view, argument>>;
using opt_list =
std::initializer_list<std::tuple<std::string_view, argument>>;
struct result {
enum class code {
@ -261,8 +256,8 @@ public:
template <typename T>
T const* maybe_opt(std::string_view name) const noexcept {
auto entry = opts.find(name);
return entry != opts.end() ?
detail::arg_cast<T>(&entry->second) : nullptr;
return entry != opts.end() ? detail::arg_cast<T>(&entry->second)
: nullptr;
}
template <typename T>
@ -306,7 +301,6 @@ public:
}
private:
static auto split_option(std::string_view kv) {
auto delim = kv.find(delimiter);
if (delim != kv.npos) {
@ -350,7 +344,6 @@ private:
std::vector<std::string> arguments_;
};
}
} // namespace argparse
#endif // argparse_d2ddac0dab0d7b88

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: Logging
//
@ -14,7 +14,7 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#include "parselink/logging.h"
@ -26,7 +26,7 @@ namespace {
struct console_endpoint : public endpoint {
static constexpr std::string_view format_string =
"{:%Y-%m-%d %H:%M:%S}.{:03} [{:<8}] {:>20} | {}\n";
"{:%Y-%m-%d %H:%M:%S}.{:03} [{:<8}] {:>20} | {}\n";
bool colored() const noexcept override { return true; }
@ -38,23 +38,25 @@ struct console_endpoint : public endpoint {
}
};
}
} // namespace
auto& console() {
static auto console = std::make_shared<console_endpoint>();
return console;
}
logger::logger(std::string_view name) : name_{name} {
logger::logger(std::string_view name)
: name_{name} {
endpoints_.emplace_back(console());
}
logger::logger(std::string_view name, std::vector<std::shared_ptr<endpoint>> eps)
: name_{name}, endpoints_{std::move(eps)} {}
logger::logger(
std::string_view name, std::vector<std::shared_ptr<endpoint>> eps)
: name_{name}
, endpoints_{std::move(eps)} {}
void logger::set_threshold(level new_threshold) noexcept {
for (auto& ep : endpoints_) {
ep->threshold = new_threshold;
}
}

View File

@ -1,19 +1,19 @@
#include "parselink/logging.h"
#include <utility/argparse.h>
#include <server.h>
#include <utility/argparse.h>
namespace {
parselink::logging::logger logger("main");
parselink::logging::logger logger("main");
}
using level = parselink::logging::level;
int run(std::span<std::string_view> arg_list) {
argparse::command_line_parser parser({
{"address", {std::string{"0.0.0.0"}}},
{"user_port", {std::uint16_t{9001}}},
{"websocket_port", {std::uint16_t{10501}}},
{"verbose", {false}},
{"address", {std::string{"0.0.0.0"}}},
{"user_port", {std::uint16_t{9001}}},
{"websocket_port", {std::uint16_t{10501}}},
{"verbose", {false}},
});
auto args = parser.parse(arg_list);
@ -28,8 +28,6 @@ int run(std::span<std::string_view> arg_list) {
logger.set_threshold(level::trace);
}
auto server = parselink::make_server(args.opt<std::string>("address"),
args.opt<std::uint16_t>("user_port"),
args.opt<std::uint16_t>("websocket_port"));
@ -41,8 +39,7 @@ int run(std::span<std::string_view> arg_list) {
return 0;
}
int main(int argc, char* argv[])
{
int main(int argc, char* argv[]) {
// TODO(ksassenrath): Add configuration file to the mix.
std::vector<std::string_view> args;

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: Server
//
@ -16,15 +16,15 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#include "parselink/proto/session.h"
#include <fmt/ranges.h>
#include "parselink/logging.h"
#include "parselink/msgpack/token.h"
#include <fmt/ranges.h>
using namespace parselink;
using namespace parselink::proto;
@ -34,22 +34,27 @@ struct fmt::formatter<msgpack::token> {
constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template<typename FormatContext>
template <typename FormatContext>
auto format(msgpack::token const& v, FormatContext& ctx) const {
using parselink::logging::themed_arg;
auto out = fmt::format_to(ctx.out(), "<msgpack {} = ", themed_arg(v.type()));
auto out = fmt::format_to(
ctx.out(), "<msgpack {} = ", themed_arg(v.type()));
switch (v.type()) {
case msgpack::format::type::unsigned_int:
fmt::format_to(out, "{}", themed_arg(*(v.get<std::uint64_t>())));
fmt::format_to(
out, "{}", themed_arg(*(v.get<std::uint64_t>())));
break;
case msgpack::format::type::signed_int:
out = fmt::format_to(out, "{}", themed_arg(*(v.get<std::uint64_t>())));
out = fmt::format_to(
out, "{}", themed_arg(*(v.get<std::uint64_t>())));
break;
case msgpack::format::type::boolean:
out = fmt::format_to(out, "{}", themed_arg(*(v.get<bool>())));
break;
case msgpack::format::type::string:
out = fmt::format_to(out, "{}", themed_arg(*(v.get<std::string_view>())));
out = fmt::format_to(
out, "{}", themed_arg(*(v.get<std::string_view>())));
break;
case msgpack::format::type::binary:
out = fmt::format_to(out, "{}",
@ -69,14 +74,12 @@ struct fmt::formatter<msgpack::token> {
case msgpack::format::type::invalid:
out = fmt::format_to(out, "(invalid)");
break;
default:
break;
default: break;
}
return fmt::format_to(out, ">");
}
};
namespace {
logging::logger logger("session");
}
@ -91,7 +94,7 @@ tl::expected<header_info, error> session::parse_header(
}
auto size = reader.read_one().and_then(
[](auto t){ return t.template get<std::uint32_t>(); });
[](auto t) { return t.template get<std::uint32_t>(); });
if (!size || !*size) {
logger.error("Failed to get valid message size");
@ -133,13 +136,12 @@ tl::expected<std::monostate, error> session::handle_connect(
return tl::make_unexpected(error::bad_data);
}
if (k == "user_id") {
result = v.get<std::string>().map([this](auto uid){
user_id = std::move(uid);
});
result = v.get<std::string>().map(
[this](auto uid) { user_id = std::move(uid); });
}
if (!result) {
logger.error("connect failed: {} -> {}: {}", k, v,
result.error());
logger.error(
"connect failed: {} -> {}: {}", k, v, result.error());
return tl::make_unexpected(error::bad_data);
}
}

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: Server
//
@ -16,42 +16,38 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#include "parselink/logging.h"
#include "parselink/server.h"
#include "parselink/logging.h"
#include "parselink/msgpack/token/reader.h"
#include "parselink/msgpack/token/views.h"
#include "parselink/proto/session.h"
#include <fmt/ranges.h>
#include <boost/asio/io_context.hpp>
#include <boost/asio/signal_set.hpp>
#include <boost/asio/redirect_error.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/as_tuple.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/deferred.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/as_tuple.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/redirect_error.hpp>
#include <boost/asio/signal_set.hpp>
#include <boost/asio/write.hpp>
#include <chrono>
#include <map>
#include <fmt/ranges.h>
using namespace parselink;
using namespace std::chrono_literals;
namespace net = boost::asio;
using net::co_spawn;
using net::awaitable;
using net::use_awaitable;
using net::co_spawn;
using net::deferred;
using net::detached;
using net::use_awaitable;
//-----------------------------------------------------------------------------
// TODO(ksassenrath): These are logging formatters for various boost/asio types.
@ -65,7 +61,7 @@ struct parselink::logging::theme<boost::system::error_code>
template <>
struct fmt::formatter<boost::system::error_code>
: fmt::formatter<std::string_view> {
template<typename FormatContext>
template <typename FormatContext>
constexpr auto format(auto const& v, FormatContext& ctx) const {
return fmt::formatter<std::string_view>::format(v.message(), ctx);
}
@ -77,22 +73,27 @@ struct fmt::formatter<msgpack::token> {
constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template<typename FormatContext>
template <typename FormatContext>
auto format(msgpack::token const& v, FormatContext& ctx) const {
using parselink::logging::themed_arg;
auto out = fmt::format_to(ctx.out(), "<msgpack {} = ", themed_arg(v.type()));
auto out = fmt::format_to(
ctx.out(), "<msgpack {} = ", themed_arg(v.type()));
switch (v.type()) {
case msgpack::format::type::unsigned_int:
fmt::format_to(out, "{}", themed_arg(*(v.get<std::uint64_t>())));
fmt::format_to(
out, "{}", themed_arg(*(v.get<std::uint64_t>())));
break;
case msgpack::format::type::signed_int:
out = fmt::format_to(out, "{}", themed_arg(*(v.get<std::uint64_t>())));
out = fmt::format_to(
out, "{}", themed_arg(*(v.get<std::uint64_t>())));
break;
case msgpack::format::type::boolean:
out = fmt::format_to(out, "{}", themed_arg(*(v.get<bool>())));
break;
case msgpack::format::type::string:
out = fmt::format_to(out, "{}", themed_arg(*(v.get<std::string_view>())));
out = fmt::format_to(
out, "{}", themed_arg(*(v.get<std::string_view>())));
break;
case msgpack::format::type::binary:
out = fmt::format_to(out, "{}",
@ -112,8 +113,7 @@ struct fmt::formatter<msgpack::token> {
case msgpack::format::type::invalid:
out = fmt::format_to(out, "(invalid)");
break;
default:
break;
default: break;
}
return fmt::format_to(out, ">");
}
@ -121,8 +121,8 @@ struct fmt::formatter<msgpack::token> {
template <typename T>
concept endpoint = requires(T const& t) {
{t.address()};
{t.port()};
{ t.address() };
{ t.port() };
};
template <endpoint T>
@ -130,12 +130,11 @@ struct parselink::logging::theme<T>
: parselink::logging::static_theme<fmt::color::coral> {};
template <endpoint T>
struct fmt::formatter<T>
: fmt::formatter<std::string_view> {
template<typename FormatContext>
struct fmt::formatter<T> : fmt::formatter<std::string_view> {
template <typename FormatContext>
constexpr auto format(auto const& v, FormatContext& ctx) const {
return fmt::format_to(ctx.out(), "{}:{}", v.address().to_string(),
v.port());
return fmt::format_to(
ctx.out(), "{}:{}", v.address().to_string(), v.port());
}
};
@ -144,10 +143,10 @@ struct fmt::formatter<T>
//-----------------------------------------------------------------------------
namespace {
logging::logger logger("server");
constexpr auto no_ex_coro = net::as_tuple(use_awaitable);
constexpr auto no_ex_defer = net::as_tuple(deferred);
}
logging::logger logger("server");
constexpr auto no_ex_coro = net::as_tuple(use_awaitable);
constexpr auto no_ex_defer = net::as_tuple(deferred);
} // namespace
struct user_session {
std::string user_id;
@ -163,7 +162,6 @@ public:
std::error_code run() noexcept override;
private:
friend user_session;
awaitable<void> user_listen();
@ -181,8 +179,8 @@ private:
class user_connection : public std::enable_shared_from_this<user_connection> {
public:
user_connection(monolithic_server& server, net::ip::tcp::socket sock)
: server_(server)
, socket_(std::move(sock)) {}
: server_(server)
, socket_(std::move(sock)) {}
~user_connection() {
logger.debug("Connection to {} closed.", socket_.remote_endpoint());
@ -193,17 +191,17 @@ public:
void start() {
logger.debug("New connection from {}", socket_.remote_endpoint());
co_spawn(socket_.get_executor(), [self = shared_from_this()]{
return self->await_connect();
}, detached);
co_spawn(
socket_.get_executor(),
[self = shared_from_this()] { return self->await_connect(); },
detached);
}
tl::expected<std::vector<std::byte>, msgpack::error> parse_header(
std::span<std::byte> data) noexcept {
auto reader = msgpack::token_reader(data);
auto magic = reader.read_one().map(
[](auto t){ return t == "prs"; });
auto magic = reader.read_one().map([](auto t) { return t == "prs"; });
if (magic && *magic) {
logger.debug("Got magic from client");
} else {
@ -212,7 +210,7 @@ public:
}
auto sz = reader.read_one().and_then(
[](auto t){ return t.template get<std::size_t>(); });
[](auto t) { return t.template get<std::size_t>(); });
if (sz && *sz) {
logger.debug("Got packet size from client: {}", *sz);
} else {
@ -231,7 +229,6 @@ public:
awaitable<tl::expected<std::monostate, boost::system::error_code>>
buffer_message(std::span<std::byte> buffer) noexcept {
std::size_t amt = 0;
while (amt < buffer.size()) {
@ -276,12 +273,11 @@ public:
// Copy remaining portion of message in initial read to the message
// buffer.
std::copy(
std::next(buffer.begin(), maybe_hdr->bytes_read),
std::next(buffer.begin(), n),
msg.begin());
std::copy(std::next(buffer.begin(), maybe_hdr->bytes_read),
std::next(buffer.begin(), n), msg.begin());
auto msg_span = std::span(msg.begin() + n - maybe_hdr->bytes_read, msg.end());
auto msg_span =
std::span(msg.begin() + n - maybe_hdr->bytes_read, msg.end());
if (auto result = co_await buffer_message(msg_span); !result) {
logger.error("Unable to parse header: {}", result.error());
@ -301,14 +297,10 @@ public:
// Authenticate against database.
logger.debug("User {} established connection", session.user_id);
//session_ = server_.establish_session(std::move(*maybe_user));
// session_ = server_.establish_session(std::move(*maybe_user));
}
enum class state {
init,
authenticated,
active
};
enum class state { init, authenticated, active };
monolithic_server& server_;
user_session* session_{};
@ -322,15 +314,17 @@ monolithic_server::monolithic_server(std::string_view address,
, user_port_{user_port}
, websocket_port_{websocket_port} {
logger.debug("Creating monolithic_server(address = {}, user_port = {}, "
"websocket_port = {})", address, user_port_, websocket_port_);
"websocket_port = {})",
address, user_port_, websocket_port_);
}
awaitable<void> monolithic_server::user_listen() {
auto exec = co_await net::this_coro::executor;
net::ip::tcp::acceptor acceptor{exec, {addr_, user_port_}};
while (true) {
std::make_shared<user_connection>(*this,
co_await acceptor.async_accept(use_awaitable))->start();
std::make_shared<user_connection>(
*this, co_await acceptor.async_accept(use_awaitable))
->start();
}
}
@ -338,7 +332,7 @@ std::error_code monolithic_server::run() noexcept {
logger.info("Starting server.");
net::signal_set signals(io_context_, SIGINT, SIGTERM);
signals.async_wait([&](auto, auto){
signals.async_wait([&](auto, auto) {
logger.info("Received signal... Shutting down.");
io_context_.stop();
});
@ -354,8 +348,8 @@ user_session* monolithic_server::establish_session(user_session session) {
auto existing_session = active_user_sessions_.find(session.user_id);
if (existing_session == active_user_sessions_.end()) {
// No session exists with that user ID yet.
active_user_sessions_.emplace(session.user_id,
std::move(session.user_id));
active_user_sessions_.emplace(
session.user_id, std::move(session.user_id));
}
return {};
}

View File

@ -1,11 +1,11 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: Tests
//
@ -14,7 +14,7 @@
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#include "parselink/logging.h"
@ -32,16 +32,18 @@ struct test_endpoint_base : public endpoint {
void write(message const& msg) override {
if constexpr (Colored) {
buffer_.append(fmt::format("{} {} | {}",
themed_arg{enum_name_only{msg.lvl}}, msg.name, msg.message));
themed_arg{enum_name_only{msg.lvl}}, msg.name,
msg.message));
} else {
buffer_.append(fmt::format("{} {} | {}",
enum_name_only{msg.lvl}, msg.name, msg.message));
buffer_.append(fmt::format("{} {} | {}", enum_name_only{msg.lvl},
msg.name, msg.message));
}
}
bool colored() const noexcept override { return Colored; }
void clear() { buffer_.clear(); }
std::string_view contents() const { return buffer_; }
std::string buffer_;
@ -58,15 +60,19 @@ using colored_test_endpoint = test_endpoint_base<true>;
// In order to test themed (colorized) logging, we must be sure that theming
// gets correctly-styled content.
struct static_theme_test { int value; };
struct static_theme_test {
int value;
};
// In order to test themed (colorized) logging, we must be sure that theming
// gets correctly-styled content.
struct dynamic_theme_test { int value; };
struct dynamic_theme_test {
int value;
};
template <has_theme T>
auto styled(T&& v) {
if constexpr(has_static_theme<T>) {
if constexpr (has_static_theme<T>) {
return fmt::styled(v, theme<std::remove_cvref_t<T>>::style);
} else if constexpr (has_dynamic_theme<T>) {
return fmt::styled(v, theme<std::remove_cvref_t<T>>::style(v));
@ -75,7 +81,7 @@ auto styled(T&& v) {
}
}
}
} // namespace
template <>
struct fmt::formatter<static_theme_test> : public fmt::formatter<int> {
@ -104,7 +110,6 @@ struct parselink::logging::theme<dynamic_theme_test> {
}
};
// Begin tests!
using namespace boost::ut;
@ -226,18 +231,18 @@ suite logging = [] {
{
auto const& cptrref = ptr;
logger.log<level::info>("int const pointer&: {}", cptrref);
auto expected = fmt::format(
"info formatting | int const pointer&: {}",
fmt::ptr(cptrref));
auto expected =
fmt::format("info formatting | int const pointer&: {}",
fmt::ptr(cptrref));
expect(ep->contents() == expected);
ep->clear();
}
{
logger.log<level::info>("std::unique_ptr<int>: {}", uniq_ptr);
auto expected = fmt::format(
"info formatting | std::unique_ptr<int>: {}",
fmt::ptr(uniq_ptr));
auto expected =
fmt::format("info formatting | std::unique_ptr<int>: {}",
fmt::ptr(uniq_ptr));
expect(ep->contents() == expected);
ep->clear();
}
@ -245,17 +250,17 @@ suite logging = [] {
{
auto& uniq_ptrref = uniq_ptr;
logger.log<level::info>("std::unique_ptr<int>&: {}", uniq_ptrref);
auto expected = fmt::format(
"info formatting | std::unique_ptr<int>&: {}",
fmt::ptr(uniq_ptrref));
auto expected =
fmt::format("info formatting | std::unique_ptr<int>&: {}",
fmt::ptr(uniq_ptrref));
expect(ep->contents() == expected);
ep->clear();
}
{
auto const& cuniq_ptrref = uniq_ptr;
logger.log<level::info>("std::unique_ptr<int> const&: {}",
cuniq_ptrref);
logger.log<level::info>(
"std::unique_ptr<int> const&: {}", cuniq_ptrref);
auto expected = fmt::format(
"info formatting | std::unique_ptr<int> const&: {}",
fmt::ptr(cuniq_ptrref));
@ -265,9 +270,9 @@ suite logging = [] {
{
logger.log<level::info>("std::shared_ptr<int>: {}", shr_ptr);
auto expected = fmt::format(
"info formatting | std::shared_ptr<int>: {}",
fmt::ptr(shr_ptr));
auto expected =
fmt::format("info formatting | std::shared_ptr<int>: {}",
fmt::ptr(shr_ptr));
expect(ep->contents() == expected);
ep->clear();
}
@ -275,17 +280,17 @@ suite logging = [] {
{
auto& shr_ptrref = shr_ptr;
logger.log<level::info>("std::shared_ptr<int>&: {}", shr_ptrref);
auto expected = fmt::format(
"info formatting | std::shared_ptr<int>&: {}",
fmt::ptr(shr_ptrref));
auto expected =
fmt::format("info formatting | std::shared_ptr<int>&: {}",
fmt::ptr(shr_ptrref));
expect(ep->contents() == expected);
ep->clear();
}
{
auto const& cshr_ptrref = shr_ptr;
logger.log<level::info>("std::shared_ptr<int> const&: {}",
cshr_ptrref);
logger.log<level::info>(
"std::shared_ptr<int> const&: {}", cshr_ptrref);
auto expected = fmt::format(
"info formatting | std::shared_ptr<int> const&: {}",
fmt::ptr(cshr_ptrref));
@ -330,8 +335,8 @@ suite logging = [] {
dynamic_theme_test red{21}, green{202};
auto formatted = fmt::format("{}", styled(stt));
auto expected = fmt::format("{}",
fmt::styled(stt.value, fmt::fg(fmt::color::black)));
auto expected = fmt::format(
"{}", fmt::styled(stt.value, fmt::fg(fmt::color::black)));
expect(formatted == expected);
formatted = fmt::format("{}", themed_arg{stt});
expect(formatted == expected);
@ -351,8 +356,8 @@ suite logging = [] {
auto [logger, ep] = make_test_logger<colored_test_endpoint>("colored");
logger.log<level::info>("");
auto expected = fmt::format("{} colored | ",
styled(enum_name_only{level::info}));
auto expected = fmt::format(
"{} colored | ", styled(enum_name_only{level::info}));
expect(ep->contents() == expected);
ep->clear();
@ -387,12 +392,11 @@ suite logging = [] {
auto* int_ptr = &int_value;
logger.log<level::info>("pointer {} {}", int_ptr, *int_ptr);
expected = fmt::format("{} colored | pointer {} {}",
styled(enum_name_only{level::info}),
themed_arg{int_ptr}, themed_arg{*int_ptr});
styled(enum_name_only{level::info}), themed_arg{int_ptr},
themed_arg{*int_ptr});
expect(ep->contents() == expected);
ep->clear();
};
};
int main(int, char**) {
}
int main(int, char**) {}

View File

@ -5,16 +5,23 @@
#include <random>
struct rng {
rng(auto s) : seed(s), generator(seed) {}
rng() : rng([]{ return std::random_device{}(); }()) {}
rng(auto s)
: seed(s)
, generator(seed) {}
rng()
: rng([] { return std::random_device{}(); }()) {}
template <typename T>
requires std::is_integral_v<T>
requires std::is_integral_v<T>
T get() noexcept {
union {
std::array<std::mt19937::result_type, 1 + sizeof(T) / sizeof(std::mt19937::result_type)> data;
std::array<std::mt19937::result_type,
1 + sizeof(T) / sizeof(std::mt19937::result_type)>
data;
T v;
} u;
for (auto& d : u.data) {
d = generator();
}
@ -23,7 +30,7 @@ struct rng {
}
template <typename T, std::size_t N>
requires std::is_integral_v<T>
requires std::is_integral_v<T>
auto get() noexcept {
std::array<T, N> values;
for (auto& value : values) {

View File

@ -1,7 +1,5 @@
#include "parselink/msgpack/core/reader.h"
#include <boost/ut.hpp>
#include <string>
namespace {
@ -10,55 +8,56 @@ using namespace boost::ut;
namespace format = msgpack::format;
template <typename... Bytes>
constexpr std::array<std::byte, sizeof...(Bytes)> make_bytes(Bytes &&...bytes) {
return {std::byte(std::forward<Bytes>(bytes))...};
constexpr std::array<std::byte, sizeof...(Bytes)> make_bytes(Bytes&&... bytes) {
return {std::byte(std::forward<Bytes>(bytes))...};
}
template <typename T, std::size_t C = 1024> struct oversized_array {
std::array<T, C> data;
std::size_t size;
template <typename T, std::size_t C = 1024>
struct oversized_array {
std::array<T, C> data;
std::size_t size;
};
constexpr auto to_bytes_array_oversized(auto const &container) {
oversized_array<std::byte> arr;
std::copy(std::begin(container), std::end(container), std::begin(arr.data));
arr.size = std::distance(std::begin(container), std::end(container));
return arr;
constexpr auto to_bytes_array_oversized(auto const& container) {
oversized_array<std::byte> arr;
std::copy(std::begin(container), std::end(container), std::begin(arr.data));
arr.size = std::distance(std::begin(container), std::end(container));
return arr;
}
consteval auto generate_bytes(auto callable) {
constexpr auto oversized = to_bytes_array_oversized(callable());
std::array<std::byte, oversized.size> out;
std::copy(std::begin(oversized.data),
constexpr auto oversized = to_bytes_array_oversized(callable());
std::array<std::byte, oversized.size> out;
std::copy(std::begin(oversized.data),
std::next(std::begin(oversized.data), oversized.size),
std::begin(out));
return out;
return out;
}
template <std::size_t A, std::size_t B>
consteval auto cat(std::array<std::byte, A> const &a,
std::array<std::byte, B> const &b) {
std::array<std::byte, A + B> out;
std::copy(std::begin(a), std::next(std::begin(a), std::size(a)),
consteval auto cat(
std::array<std::byte, A> const& a, std::array<std::byte, B> const& b) {
std::array<std::byte, A + B> out;
std::copy(std::begin(a), std::next(std::begin(a), std::size(a)),
std::begin(out));
std::copy(std::begin(b), std::next(std::begin(b), std::size(b)),
std::copy(std::begin(b), std::next(std::begin(b), std::size(b)),
std::next(std::begin(out), std::size(a)));
return out;
return out;
}
template <typename T, typename Itr>
constexpr auto zip(T val, Itr begin, Itr end) {
std::vector<T> output;
for (auto itr = begin; itr != end; ++itr) {
output.emplace_back(val);
output.emplace_back(*itr);
}
return output;
std::vector<T> output;
for (auto itr = begin; itr != end; ++itr) {
output.emplace_back(val);
output.emplace_back(*itr);
}
return output;
}
template <typename T, typename Iterable>
constexpr auto zip(T val, Iterable const &itr) {
return zip(val, std::begin(itr), std::end(itr));
constexpr auto zip(T val, Iterable const& itr) {
return zip(val, std::begin(itr), std::end(itr));
}
using relaxed_reader = msgpack::reader<msgpack::reader_policy::relaxed>;
@ -66,65 +65,65 @@ using relaxed_reader = msgpack::reader<msgpack::reader_policy::relaxed>;
} // namespace
suite relaxed = [] {
"empty span"_test = [] {
using error = msgpack::error;
std::span<std::byte> bytes;
relaxed_reader reader(bytes);
auto v = reader.read<msgpack::format::uint8>();
expect(v.error() == error::end_of_message);
};
"empty span"_test = [] {
using error = msgpack::error;
std::span<std::byte> bytes;
relaxed_reader reader(bytes);
auto v = reader.read<msgpack::format::uint8>();
expect(v.error() == error::end_of_message);
};
////////////////////////////////////////////////////////////////////////////////
// Unsigned integers
////////////////////////////////////////////////////////////////////////////////
"reader<reader_policy::relaxed>::read<format::uint8>"_test = [] {
using fmt = format::uint8;
using error = msgpack::error;
/**
* All bytes 0x00->0x79 are effectively literal uint8s.
*/
{
constexpr auto payload = make_bytes(0x52, 0xcc, 0x84);
relaxed_reader reader(payload);
{
auto result = reader.read<fmt>();
expect(result == std::uint8_t(0x52));
}
{
auto result = reader.read<fmt>();
expect(result == std::uint8_t(0x84));
}
////////////////////////////////////////////////////////////////////////////////
// Unsigned integers
////////////////////////////////////////////////////////////////////////////////
"reader<reader_policy::relaxed>::read<format::uint8>"_test = [] {
using fmt = format::uint8;
using error = msgpack::error;
/**
* All bytes 0x00->0x79 are effectively literal uint8s.
*/
{
constexpr auto payload = make_bytes(0x52, 0xcc, 0x84);
relaxed_reader reader(payload);
{
auto result = reader.read<fmt>();
expect(result == std::uint8_t(0x52));
}
{
auto result = reader.read<fmt>();
expect(result == std::uint8_t(0x84));
}
auto result = reader.read<fmt>();
expect(result == tl::make_unexpected(error::end_of_message));
}
};
auto result = reader.read<fmt>();
expect(result == tl::make_unexpected(error::end_of_message));
}
};
"reader<reader_policy::relaxed>::read<formats::uint16>"_test = [] {
using fmt = msgpack::format::uint16;
using error = msgpack::error;
/**
* All bytes 0x00->0x79 are effectively literal uint8s.
*/
{
constexpr auto payload = make_bytes(0x52, 0xcc, 0x84, 0xcd, 0xaa, 0xcc);
relaxed_reader reader(payload);
{
auto result = reader.read<fmt>();
expect(result == std::uint16_t(0x52));
}
{
auto result = reader.read<fmt>();
expect(result == std::uint16_t(0x84));
}
{
auto result = reader.read<fmt>();
expect(result == std::uint16_t(0xaacc));
}
"reader<reader_policy::relaxed>::read<formats::uint16>"_test = [] {
using fmt = msgpack::format::uint16;
using error = msgpack::error;
/**
* All bytes 0x00->0x79 are effectively literal uint8s.
*/
{
constexpr auto payload =
make_bytes(0x52, 0xcc, 0x84, 0xcd, 0xaa, 0xcc);
relaxed_reader reader(payload);
{
auto result = reader.read<fmt>();
expect(result == std::uint16_t(0x52));
}
{
auto result = reader.read<fmt>();
expect(result == std::uint16_t(0x84));
}
{
auto result = reader.read<fmt>();
expect(result == std::uint16_t(0xaacc));
}
auto result = reader.read<fmt>();
expect(result == tl::make_unexpected(error::end_of_message));
}
};
auto result = reader.read<fmt>();
expect(result == tl::make_unexpected(error::end_of_message));
}
};
};

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,15 @@
#include "parselink/msgpack/token.h"
//#include "parselink/proto/message.h"
#include <fmt/format.h>
#include <fmt/ranges.h>
#include <magic_enum.hpp>
// #include "parselink/proto/message.h"
#include <chrono>
#include <cstdlib>
#include <fcntl.h>
#include <magic_enum.hpp>
#include <vector>
#include <fmt/format.h>
#include <fmt/ranges.h>
using namespace std::chrono_literals;
struct any {
@ -37,7 +37,6 @@ auto read_file(char const* filename) {
}
int main(int argc, char** argv) {
std::array<msgpack::token, 20> buf;
auto data = read_file(argc < 2 ? "output.bin" : argv[1]);
@ -47,42 +46,48 @@ int main(int argc, char** argv) {
auto before = std::chrono::steady_clock::now();
auto test = reader.read_some(buf);
auto after = std::chrono::steady_clock::now();
test.map([&](auto sp){
fmt::print("Read {} tokens\n", sp.size());
for (auto token : sp) {
fmt::print("token type: {} value: ", magic_enum::enum_name(token.type()));
switch (token.type()) {
case msgpack::format::type::unsigned_int:
fmt::print("{}", *(token.template get<std::uint64_t>()));
break;
case msgpack::format::type::signed_int:
fmt::print("{}", *(token.template get<std::int64_t>()));
break;
case msgpack::format::type::boolean:
fmt::print("{}", *(token.template get<bool>()));
break;
case msgpack::format::type::string:
fmt::print("{}", *(token.template get<std::string_view>()));
break;
case msgpack::format::type::binary:
fmt::print("{}", *(token.template get<std::span<std::byte const>>()));
break;
case msgpack::format::type::map:
fmt::print("map ({} entries)", token.template get<msgpack::map_desc>()->count);
break;
case msgpack::format::type::array:
fmt::print("array ({} entries)", token.template get<msgpack::array_desc>()->count);
break;
case msgpack::format::type::nil:
fmt::print("(nil)");
break;
case msgpack::format::type::invalid:
fmt::print("(invalid)");
break;
test.map([&](auto sp) {
fmt::print("Read {} tokens\n", sp.size());
for (auto token : sp) {
fmt::print("token type: {} value: ",
magic_enum::enum_name(token.type()));
switch (token.type()) {
case msgpack::format::type::unsigned_int:
fmt::print(
"{}", *(token.template get<std::uint64_t>()));
break;
case msgpack::format::type::signed_int:
fmt::print("{}", *(token.template get<std::int64_t>()));
break;
case msgpack::format::type::boolean:
fmt::print("{}", *(token.template get<bool>()));
break;
case msgpack::format::type::string:
fmt::print("{}",
*(token.template get<std::string_view>()));
break;
case msgpack::format::type::binary:
fmt::print(
"{}", *(token.template get<
std::span<std::byte const>>()));
break;
case msgpack::format::type::map:
fmt::print("map ({} entries)",
token.template get<msgpack::map_desc>()->count);
break;
case msgpack::format::type::array:
fmt::print("array ({} entries)",
token.template get<msgpack::array_desc>()
->count);
break;
case msgpack::format::type::nil: fmt::print("(nil)"); break;
case msgpack::format::type::invalid:
fmt::print("(invalid)");
break;
}
fmt::print("\n");
}
fmt::print("\n");
}
}).map_error([&](auto err){
}).map_error([&](auto err) {
fmt::print("Failed to read tokens: {}\n", (int)err);
});
fmt::print("Took {} us\n", (after - before) / 1us);

View File

@ -1,40 +1,37 @@
#include "parselink/msgpack/token.h"
#include <boost/ut.hpp>
using namespace boost::ut;
namespace {
template <typename... Bytes>
constexpr std::array<std::byte, sizeof...(Bytes)> make_bytes(Bytes &&...bytes) {
return {std::byte(std::forward<Bytes>(bytes))...};
}
template <typename... Bytes>
constexpr std::array<std::byte, sizeof...(Bytes)> make_bytes(Bytes&&... bytes) {
return {std::byte(std::forward<Bytes>(bytes))...};
}
template <typename First, typename... Others>
constexpr bool wrong_types(auto const& obj) {
auto err = tl::make_unexpected(msgpack::error::wrong_type);
if (obj.template get<First>() != err) return false;
if constexpr (sizeof...(Others)) {
return wrong_types<Others...>(obj);
} else {
return true;
}
}
template <typename T, typename E>
std::ostream &operator<<(std::ostream &os, tl::expected<T, E> const &exp) {
if (exp.has_value()) {
os << "Value: '" << *exp << "'";
} else {
os << "Error";
}
return os;
template <typename First, typename... Others>
constexpr bool wrong_types(auto const& obj) {
auto err = tl::make_unexpected(msgpack::error::wrong_type);
if (obj.template get<First>() != err) return false;
if constexpr (sizeof...(Others)) {
return wrong_types<Others...>(obj);
} else {
return true;
}
}
enum class foo : std::uint8_t {
a, b, c, d, e
};
template <typename T, typename E>
std::ostream& operator<<(std::ostream& os, tl::expected<T, E> const& exp) {
if (exp.has_value()) {
os << "Value: '" << *exp << "'";
} else {
os << "Error";
}
return os;
}
} // namespace
enum class foo : std::uint8_t { a, b, c, d, e };
suite size_and_enum = [] {
"expected use case"_test = [] {
@ -118,18 +115,19 @@ suite assignment_and_access = [] {
std::vector<std::byte> extracted_val;
{
auto val = make_bytes(0x32, 0xff, 0xaa, 0xce);
msgpack::token obj(val); expect(obj.type() == msgpack::format::type::binary);
msgpack::token obj(val);
expect(obj.type() == msgpack::format::type::binary);
auto retrieved = obj.get<std::span<std::byte const>>();
expect(bool(retrieved));
expect(std::equal(retrieved->begin(), retrieved->end(),
val.begin(), val.end()));
expect(std::equal(retrieved->begin(), retrieved->end(), val.begin(),
val.end()));
expect(wrong_types<bool, int, unsigned, std::string_view>(obj));
auto bytes_retrieved = obj.get<std::vector<std::byte>>();
expect(bool(bytes_retrieved));
extracted_val = std::move(*bytes_retrieved);
}
expect(std::equal(expected_val.begin(), expected_val.end(),
extracted_val.begin(), extracted_val.end()));
extracted_val.begin(), extracted_val.end()));
};
};

View File

@ -1,8 +1,8 @@
#include <boost/ut.hpp>
#include <magic_enum.hpp>
#include <msgpack/token.h>
#include <boost/ut.hpp>
#include <fmt/format.h>
#include <magic_enum.hpp>
using namespace boost::ut;
@ -18,17 +18,17 @@ constexpr std::array<std::byte, sizeof(T)> as_bytes(T&& t) {
}
template <typename... Bytes>
constexpr std::array<std::byte, sizeof...(Bytes)> make_bytes(Bytes &&...bytes) {
constexpr std::array<std::byte, sizeof...(Bytes)> make_bytes(Bytes&&... bytes) {
return {std::byte(std::forward<Bytes>(bytes))...};
}
template <typename T, std::size_t C = 1024 * 1024> struct oversized_array {
template <typename T, std::size_t C = 1024 * 1024>
struct oversized_array {
std::array<T, C> data;
std::size_t size;
};
constexpr auto to_bytes_array_oversized(auto const &container) {
constexpr auto to_bytes_array_oversized(auto const& container) {
using value_type = std::decay_t<decltype(container[0])>;
oversized_array<value_type> arr;
std::copy(std::begin(container), std::end(container), std::begin(arr.data));
@ -51,8 +51,8 @@ consteval auto build_string(auto callable) {
return string_array;
}
template <std::size_t ... Sizes>
constexpr auto cat(std::array<std::byte, Sizes>const&... a) noexcept {
template <std::size_t... Sizes>
constexpr auto cat(std::array<std::byte, Sizes> const&... a) noexcept {
std::array<std::byte, (Sizes + ...)> out;
std::size_t index{};
((std::copy_n(a.begin(), Sizes, out.begin() + index), index += Sizes), ...);
@ -89,14 +89,14 @@ constexpr auto from_string_view(std::string_view sv) {
}
template <typename T, typename E>
std::ostream &operator<<(std::ostream &os, tl::expected<T, E> const &exp) {
if (exp.has_value()) {
os << "Value: '" << *exp << "'";
} else {
os << "Error";
}
return os;
std::ostream& operator<<(std::ostream& os, tl::expected<T, E> const& exp) {
if (exp.has_value()) {
os << "Value: '" << *exp << "'";
} else {
os << "Error";
}
return os;
}
bool test_incomplete_message(auto const& payload) {
// Test incomplete message.
@ -105,9 +105,9 @@ bool test_incomplete_message(auto const& payload) {
msgpack::token_reader reader(std::span(payload.data(), i));
auto token = reader.read_one();
if (token != tl::make_unexpected(msgpack::error::incomplete_message)) {
fmt::print("Got the wrong response reading subview[0,{}] of payload: {}\n",
i,
token->get<std::string_view>().value());
fmt::print("Got the wrong response reading subview[0,{}] of "
"payload: {}\n",
i, token->get<std::string_view>().value());
return false;
}
}
@ -115,11 +115,11 @@ bool test_incomplete_message(auto const& payload) {
}
bool test_end_of_message(auto& reader) {
return reader.read_one() ==
tl::make_unexpected(msgpack::error::end_of_message);
return reader.read_one()
== tl::make_unexpected(msgpack::error::end_of_message);
}
}
} // namespace
suite reader_tests = [] {
//--------------------------------------------------------------------------
@ -173,8 +173,8 @@ suite reader_tests = [] {
msgpack::token_reader reader(payload);
auto token = reader.read_one();
expect(token && token->type() == msgpack::format::type::unsigned_int);
expect(token->get<std::uint8_t>() ==
tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::uint8_t>()
== tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::uint16_t>() == 0x0102);
expect(token->get<std::uint32_t>() == 0x0102);
expect(token->get<std::uint64_t>() == 0x0102);
@ -193,10 +193,10 @@ suite reader_tests = [] {
msgpack::token_reader reader(payload);
auto token = reader.read_one();
expect(token && token->type() == msgpack::format::type::unsigned_int);
expect(token->get<std::uint8_t>() ==
tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::uint16_t>() ==
tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::uint8_t>()
== tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::uint16_t>()
== tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::uint32_t>() == 0x01020304);
expect(token->get<std::uint64_t>() == 0x01020304);
expect(token == 0x01020304u);
@ -204,8 +204,8 @@ suite reader_tests = [] {
expect(test_end_of_message(reader));
};
"read format::uint64"_test = [] {
constexpr auto payload = make_bytes(0xcf,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08);
constexpr auto payload = make_bytes(
0xcf, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08);
// Test incomplete message.
expect(test_incomplete_message(payload));
@ -214,12 +214,12 @@ suite reader_tests = [] {
msgpack::token_reader reader(payload);
auto token = reader.read_one();
expect(token && token->type() == msgpack::format::type::unsigned_int);
expect(token->get<std::uint8_t>() ==
tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::uint16_t>() ==
tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::uint32_t>() ==
tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::uint8_t>()
== tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::uint16_t>()
== tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::uint32_t>()
== tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::uint64_t>() == 0x0102030405060708);
expect(token == 0x0102030405060708u);
// EOM
@ -277,8 +277,8 @@ suite reader_tests = [] {
msgpack::token_reader reader(payload);
auto token = reader.read_one();
expect(token && token->type() == msgpack::format::type::signed_int);
expect(token->get<std::int8_t>() ==
tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::int8_t>()
== tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::int16_t>() == -256);
expect(token->get<std::int32_t>() == -256);
expect(token->get<std::int64_t>() == -256);
@ -297,10 +297,10 @@ suite reader_tests = [] {
msgpack::token_reader reader(payload);
auto token = reader.read_one();
expect(token && token->type() == msgpack::format::type::signed_int);
expect(token->get<std::int8_t>() ==
tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::int16_t>() ==
tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::int8_t>()
== tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::int16_t>()
== tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::int32_t>() == -65536);
expect(token->get<std::int64_t>() == -65536);
expect(token == -65536);
@ -308,8 +308,8 @@ suite reader_tests = [] {
expect(test_end_of_message(reader));
};
"read format::int64"_test = [] {
constexpr auto payload = make_bytes(0xd3,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00);
constexpr auto payload = make_bytes(
0xd3, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00);
// Test incomplete message.
expect(test_incomplete_message(payload));
@ -318,12 +318,12 @@ suite reader_tests = [] {
msgpack::token_reader reader(payload);
auto token = reader.read_one();
expect(token && token->type() == msgpack::format::type::signed_int);
expect(token->get<std::int8_t>() ==
tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::int16_t>() ==
tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::int32_t>() ==
tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::int8_t>()
== tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::int16_t>()
== tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::int32_t>()
== tl::make_unexpected(msgpack::error::will_truncate));
expect(token->get<std::int64_t>() == -4294967296);
expect(token == -4294967296);
// EOM
@ -335,8 +335,7 @@ suite reader_tests = [] {
//--------------------------------------------------------------------------
"read format::fixstr"_test = [] {
constexpr std::string_view sv = "hello";
constexpr auto payload =
cat(make_bytes(0xa5),
constexpr auto payload = cat(make_bytes(0xa5),
generate_bytes([sv] { return from_string_view(sv); }));
// Test incomplete message.
@ -355,8 +354,7 @@ suite reader_tests = [] {
};
"read format::str8"_test = [] {
constexpr std::string_view sv = "hello world";
constexpr auto payload =
cat(make_bytes(0xd9, sv.size()),
constexpr auto payload = cat(make_bytes(0xd9, sv.size()),
generate_bytes([sv] { return from_string_view(sv); }));
// Test incomplete message.
@ -374,14 +372,12 @@ suite reader_tests = [] {
expect(test_end_of_message(reader));
};
"read format::str16"_test = [] {
static constexpr auto sa = build_string([]{
return repeat("0123456789abcdef", 17);
});
static constexpr auto sa =
build_string([] { return repeat("0123456789abcdef", 17); });
constexpr auto sv = std::string_view(sa.data(), sa.size());
constexpr auto sv_size = host_to_be(std::uint16_t(sv.size()));
auto payload =
cat(make_bytes(0xda), as_bytes(sv_size),
auto payload = cat(make_bytes(0xda), as_bytes(sv_size),
generate_bytes([] { return from_string_view(sv); }));
// Test incomplete message.
@ -399,14 +395,12 @@ suite reader_tests = [] {
expect(test_end_of_message(reader));
};
"read format::str32"_test = [] {
static constexpr auto sa = build_string([]{
return repeat("0123456789abcdef", 4097);
});
static constexpr auto sa =
build_string([] { return repeat("0123456789abcdef", 4097); });
constexpr auto sv = std::string_view(sa.data(), sa.size());
constexpr auto sv_size = host_to_be(std::uint32_t(sv.size()));
constexpr auto payload =
cat(make_bytes(0xdb), as_bytes(sv_size),
constexpr auto payload = cat(make_bytes(0xdb), as_bytes(sv_size),
generate_bytes([sv] { return from_string_view(sv); }));
// Test incomplete message.
@ -428,13 +422,11 @@ suite reader_tests = [] {
// Binary format tests
//--------------------------------------------------------------------------
"read format::bin8"_test = [] {
constexpr auto bytes = make_bytes(
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10
);
constexpr auto bytes = make_bytes(0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10);
constexpr auto bytes_size = host_to_be(std::uint8_t(bytes.size()));
constexpr auto payload =
cat(make_bytes(0xc4), as_bytes(bytes_size), bytes);
cat(make_bytes(0xc4), as_bytes(bytes_size), bytes);
// Test incomplete message.
expect(test_incomplete_message(payload));
@ -452,7 +444,7 @@ suite reader_tests = [] {
expect(test_end_of_message(reader));
};
"read format::bin16"_test = [] {
static constexpr auto bytes = generate_bytes([]{
static constexpr auto bytes = generate_bytes([] {
return repeat(
make_bytes(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08),
16);
@ -477,7 +469,7 @@ suite reader_tests = [] {
expect(test_end_of_message(reader));
};
"read format::bin32"_test = [] {
static constexpr auto bytes = generate_bytes([]{
static constexpr auto bytes = generate_bytes([] {
return repeat(
make_bytes(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08),
8193);
@ -485,7 +477,7 @@ suite reader_tests = [] {
constexpr auto bytes_size = host_to_be(std::uint32_t(bytes.size()));
constexpr auto payload =
cat(make_bytes(0xc6), as_bytes(bytes_size), bytes);
cat(make_bytes(0xc6), as_bytes(bytes_size), bytes);
// Test incomplete message.
expect(test_incomplete_message(payload));
@ -508,7 +500,7 @@ suite reader_tests = [] {
"read format::fixarray"_test = [] {
// A MessagePack array of 5 8-bit unsigned integers.
constexpr auto payload = make_bytes(0x95, 0xcc, 0x85, 0xcc, 0x84, 0xcc,
0x83, 0xcc, 0x82, 0xcc, 0x81);
0x83, 0xcc, 0x82, 0xcc, 0x81);
msgpack::token_reader reader(payload);
auto token = reader.read_one();
@ -517,16 +509,16 @@ suite reader_tests = [] {
expect(array && array->count == 5);
for (std::size_t i = 0; i < array->count; ++i) {
auto v = reader.read_one();
expect(v && v->get<std::uint8_t>() == 0x85 - i);
auto v = reader.read_one();
expect(v && v->get<std::uint8_t>() == 0x85 - i);
}
expect(test_end_of_message(reader));
};
"read format::array16"_test = [] {
// A MessagePack array of 5 8-bit unsigned integers.
constexpr auto payload = make_bytes(0xdc, 0x00, 0x05,
0xcc, 0x85, 0xcc, 0x84, 0xcc, 0x83, 0xcc, 0x82, 0xcc, 0x81);
constexpr auto payload = make_bytes(0xdc, 0x00, 0x05, 0xcc, 0x85, 0xcc,
0x84, 0xcc, 0x83, 0xcc, 0x82, 0xcc, 0x81);
msgpack::token_reader reader(payload);
auto token = reader.read_one();
@ -535,16 +527,16 @@ suite reader_tests = [] {
expect(array && array->count == 5);
for (std::size_t i = 0; i < array->count; ++i) {
auto v = reader.read_one();
expect(v && v == 0x85u - i);
auto v = reader.read_one();
expect(v && v == 0x85u - i);
}
expect(test_end_of_message(reader));
};
"read format::array32"_test = [] {
// A MessagePack array of 5 8-bit unsigned integers.
constexpr auto payload = make_bytes(0xdd, 0x00, 0x00, 0x00, 0x05,
0xcc, 0x85, 0xcc, 0x84, 0xcc, 0x83, 0xcc, 0x82, 0xcc, 0x81);
constexpr auto payload = make_bytes(0xdd, 0x00, 0x00, 0x00, 0x05, 0xcc,
0x85, 0xcc, 0x84, 0xcc, 0x83, 0xcc, 0x82, 0xcc, 0x81);
msgpack::token_reader reader(payload);
auto token = reader.read_one();
@ -553,8 +545,8 @@ suite reader_tests = [] {
expect(array && array->count == 5);
for (std::size_t i = 0; i < array->count; ++i) {
auto v = reader.read_one();
expect(v && v->get<std::uint8_t>() == 0x85 - i);
auto v = reader.read_one();
expect(v && v->get<std::uint8_t>() == 0x85 - i);
}
expect(test_end_of_message(reader));
@ -562,18 +554,15 @@ suite reader_tests = [] {
"read format::fixmap"_test = [] {
// A MessagePack map of 3 strings to 8-bit unsigned integers.
static constexpr std::array<std::string_view, 3> strings = {
"one", "two", "three"
};
"one", "two", "three"};
constexpr auto payload = cat(
make_bytes(0x83, 0xa3),
constexpr auto payload = cat(make_bytes(0x83, 0xa3),
generate_bytes([] { return from_string_view(strings[0]); }),
make_bytes(0x01, 0xa3),
generate_bytes([] { return from_string_view(strings[1]); }),
make_bytes(0x02, 0xa5),
generate_bytes([] { return from_string_view(strings[2]); }),
make_bytes(0x03)
);
make_bytes(0x03));
msgpack::token_reader reader(payload);
auto token = reader.read_one();
@ -593,18 +582,15 @@ suite reader_tests = [] {
"read format::map16"_test = [] {
// A MessagePack map of 3 strings to 8-bit unsigned integers.
static constexpr std::array<std::string_view, 3> strings = {
"one", "two", "three"
};
"one", "two", "three"};
constexpr auto payload = cat(
make_bytes(0xde, 0x00, 0x03, 0xa3),
constexpr auto payload = cat(make_bytes(0xde, 0x00, 0x03, 0xa3),
generate_bytes([] { return from_string_view(strings[0]); }),
make_bytes(0x01, 0xa3),
generate_bytes([] { return from_string_view(strings[1]); }),
make_bytes(0x02, 0xa5),
generate_bytes([] { return from_string_view(strings[2]); }),
make_bytes(0x03)
);
make_bytes(0x03));
msgpack::token_reader reader(payload);
auto token = reader.read_one();
@ -626,8 +612,7 @@ suite reader_tests = [] {
"read format::map32"_test = [] {
// A MessagePack map of 3 strings to 8-bit unsigned integers.
static constexpr std::array<std::string_view, 3> strings = {
"one", "two", "three"
};
"one", "two", "three"};
constexpr auto payload = cat(
make_bytes(0xdf, 0x00, 0x00, 0x00, 0x03, 0xa3),
@ -636,8 +621,7 @@ suite reader_tests = [] {
generate_bytes([] { return from_string_view(strings[1]); }),
make_bytes(0x02, 0xa5),
generate_bytes([] { return from_string_view(strings[2]); }),
make_bytes(0x03)
);
make_bytes(0x03));
msgpack::token_reader reader(payload);
auto token = reader.read_one();

View File

@ -1,11 +1,12 @@
#include <boost/ut.hpp>
#include <magic_enum.hpp>
#include <msgpack/token.h>
#include <boost/ut.hpp>
#include <fmt/format.h>
#include <fmt/ranges.h>
#include <magic_enum.hpp>
using namespace boost::ut;
namespace {
template <typename T>
constexpr bool operator==(std::span<T const> a, std::span<T const> b) noexcept {
@ -18,17 +19,17 @@ constexpr std::array<std::byte, sizeof(T)> as_bytes(T&& t) {
}
template <typename... Bytes>
constexpr std::array<std::byte, sizeof...(Bytes)> make_bytes(Bytes &&...bytes) {
constexpr std::array<std::byte, sizeof...(Bytes)> make_bytes(Bytes&&... bytes) {
return {std::byte(std::forward<Bytes>(bytes))...};
}
template <typename T, std::size_t C = 1024 * 1024> struct oversized_array {
template <typename T, std::size_t C = 1024 * 1024>
struct oversized_array {
std::array<T, C> data;
std::size_t size;
};
constexpr auto to_bytes_array_oversized(auto const &container) {
constexpr auto to_bytes_array_oversized(auto const& container) {
using value_type = std::decay_t<decltype(container[0])>;
oversized_array<value_type> arr;
std::copy(std::begin(container), std::end(container), std::begin(arr.data));
@ -51,8 +52,8 @@ consteval auto build_string(auto callable) {
return string_array;
}
template <std::size_t ... Sizes>
constexpr auto cat(std::array<std::byte, Sizes>const&... a) noexcept {
template <std::size_t... Sizes>
constexpr auto cat(std::array<std::byte, Sizes> const&... a) noexcept {
std::array<std::byte, (Sizes + ...)> out;
std::size_t index{};
((std::copy_n(a.begin(), Sizes, out.begin() + index), index += Sizes), ...);
@ -89,14 +90,14 @@ constexpr auto from_string_view(std::string_view sv) {
}
template <typename T, typename E>
std::ostream &operator<<(std::ostream &os, tl::expected<T, E> const &exp) {
if (exp.has_value()) {
os << "Value: '" << *exp << "'";
} else {
os << "Error";
}
return os;
std::ostream& operator<<(std::ostream& os, tl::expected<T, E> const& exp) {
if (exp.has_value()) {
os << "Value: '" << *exp << "'";
} else {
os << "Error";
}
return os;
}
bool test_incomplete_message(auto const& payload) {
// Test incomplete message.
@ -105,9 +106,9 @@ bool test_incomplete_message(auto const& payload) {
msgpack::token_reader reader(std::span(payload.data(), i));
auto token = reader.read_one();
if (token != tl::make_unexpected(msgpack::error::incomplete_message)) {
fmt::print("Got the wrong response reading subview[0,{}] of payload: {}\n",
i,
token->get<std::string_view>().value());
fmt::print("Got the wrong response reading subview[0,{}] of "
"payload: {}\n",
i, token->get<std::string_view>().value());
return false;
}
}
@ -115,10 +116,10 @@ bool test_incomplete_message(auto const& payload) {
}
bool test_end_of_message(auto& reader) {
return reader.read_one() ==
tl::make_unexpected(msgpack::error::end_of_message);
}
return reader.read_one()
== tl::make_unexpected(msgpack::error::end_of_message);
}
} // namespace
template <>
struct fmt::formatter<msgpack::token> {
@ -126,9 +127,11 @@ struct fmt::formatter<msgpack::token> {
constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template<typename FormatContext>
template <typename FormatContext>
auto format(msgpack::token const& v, FormatContext& ctx) const {
auto out = fmt::format_to(ctx.out(), "<msgpack {} = ", magic_enum::enum_name(v.type()));
auto out = fmt::format_to(
ctx.out(), "<msgpack {} = ", magic_enum::enum_name(v.type()));
switch (v.type()) {
case msgpack::format::type::unsigned_int:
fmt::format_to(out, "{}", (*(v.get<std::uint64_t>())));
@ -143,8 +146,8 @@ struct fmt::formatter<msgpack::token> {
out = fmt::format_to(out, "{}", (*(v.get<std::string_view>())));
break;
case msgpack::format::type::binary:
out = fmt::format_to(out, "{}",
(*(v.get<std::span<std::byte const>>())));
out = fmt::format_to(
out, "{}", (*(v.get<std::span<std::byte const>>())));
break;
case msgpack::format::type::map:
out = fmt::format_to(out, "(arity: {})",
@ -160,8 +163,7 @@ struct fmt::formatter<msgpack::token> {
case msgpack::format::type::invalid:
out = fmt::format_to(out, "(invalid)");
break;
default:
break;
default: break;
}
return fmt::format_to(out, ">");
}
@ -171,11 +173,16 @@ suite views_tests = [] {
"read format::fixmap"_test = [] {
// A MessagePack map of 3 strings to 8-bit unsigned integers.
static constexpr auto strings = std::to_array<std::string_view>({
"one", "two", "three", "array", "four", "map", "five",
"one",
"two",
"three",
"array",
"four",
"map",
"five",
});
constexpr auto payload = cat(
make_bytes(0x87, 0xa3),
constexpr auto payload = cat(make_bytes(0x87, 0xa3),
generate_bytes([] { return from_string_view(strings[0]); }),
make_bytes(0x01, 0xa3),
@ -201,7 +208,7 @@ suite views_tests = [] {
make_bytes(0xa4),
generate_bytes([] { return from_string_view(strings[6]); })
//make_bytes(0x05)
// make_bytes(0x05)
);
std::array<msgpack::token, 32> tokens;

View File

@ -2,9 +2,10 @@
#define msgpack_test_utils_4573e6627d8efe78
#include <boost/ut.hpp>
#include <fmt/format.h>
#include <magic_enum.hpp>
#include <fmt/format.h>
template <typename T>
constexpr bool operator==(std::span<T const> a, std::span<T const> b) noexcept {
return std::equal(a.begin(), a.end(), b.begin(), b.end());
@ -16,16 +17,17 @@ constexpr std::array<std::byte, sizeof(T)> as_bytes(T&& t) {
}
template <typename... Bytes>
constexpr std::array<std::byte, sizeof...(Bytes)> make_bytes(Bytes &&...bytes) {
constexpr std::array<std::byte, sizeof...(Bytes)> make_bytes(Bytes&&... bytes) {
return {std::byte(std::forward<Bytes>(bytes))...};
}
template <typename T, std::size_t C = 1024 * 1024> struct oversized_array {
template <typename T, std::size_t C = 1024 * 1024>
struct oversized_array {
std::array<T, C> data;
std::size_t size;
};
constexpr auto to_bytes_array_oversized(auto const &container) {
constexpr auto to_bytes_array_oversized(auto const& container) {
using value_type = std::decay_t<decltype(container[0])>;
oversized_array<value_type> arr;
std::copy(std::begin(container), std::end(container), std::begin(arr.data));
@ -48,8 +50,8 @@ consteval auto build_string(auto callable) {
return string_array;
}
template <std::size_t ... Sizes>
constexpr auto cat(std::array<std::byte, Sizes>const&... a) noexcept {
template <std::size_t... Sizes>
constexpr auto cat(std::array<std::byte, Sizes> const&... a) noexcept {
std::array<std::byte, (Sizes + ...)> out;
std::size_t index{};
((std::copy_n(a.begin(), Sizes, out.begin() + index), index += Sizes), ...);
@ -86,7 +88,7 @@ constexpr auto from_string_view(std::string_view sv) {
}
template <typename T, typename E>
std::ostream &operator<<(std::ostream &os, tl::expected<T, E> const &exp) {
std::ostream& operator<<(std::ostream& os, tl::expected<T, E> const& exp) {
if (exp.has_value()) {
os << "Value: '" << *exp << "'";
} else {

View File

@ -1,7 +1,5 @@
#include "parselink/msgpack/core/writer.h"
#include <boost/ut.hpp>
#include <string>
using namespace boost::ut;
@ -10,76 +8,77 @@ namespace format = msgpack::format;
namespace {
template <typename... Bytes>
constexpr std::array<std::byte, sizeof...(Bytes)> make_bytes(Bytes &&...bytes) {
return {std::byte(std::forward<Bytes>(bytes))...};
constexpr std::array<std::byte, sizeof...(Bytes)> make_bytes(Bytes&&... bytes) {
return {std::byte(std::forward<Bytes>(bytes))...};
}
template <typename T, std::size_t C = 1024> struct oversized_array {
std::array<T, C> data;
std::size_t size;
template <typename T, std::size_t C = 1024>
struct oversized_array {
std::array<T, C> data;
std::size_t size;
};
constexpr auto to_bytes_array_oversized(auto const &container) {
oversized_array<std::byte> arr;
std::copy(std::begin(container), std::end(container), std::begin(arr.data));
arr.size = std::distance(std::begin(container), std::end(container));
return arr;
constexpr auto to_bytes_array_oversized(auto const& container) {
oversized_array<std::byte> arr;
std::copy(std::begin(container), std::end(container), std::begin(arr.data));
arr.size = std::distance(std::begin(container), std::end(container));
return arr;
}
consteval auto generate_bytes(auto callable) {
constexpr auto oversized = to_bytes_array_oversized(callable());
std::array<std::byte, oversized.size> out;
std::copy(std::begin(oversized.data),
constexpr auto oversized = to_bytes_array_oversized(callable());
std::array<std::byte, oversized.size> out;
std::copy(std::begin(oversized.data),
std::next(std::begin(oversized.data), oversized.size),
std::begin(out));
return out;
return out;
}
template <std::size_t A, std::size_t B>
consteval auto cat(std::array<std::byte, A> const &a,
std::array<std::byte, B> const &b) {
std::array<std::byte, A + B> out;
std::copy(std::begin(a), std::next(std::begin(a), std::size(a)),
consteval auto cat(
std::array<std::byte, A> const& a, std::array<std::byte, B> const& b) {
std::array<std::byte, A + B> out;
std::copy(std::begin(a), std::next(std::begin(a), std::size(a)),
std::begin(out));
std::copy(std::begin(b), std::next(std::begin(b), std::size(b)),
std::copy(std::begin(b), std::next(std::begin(b), std::size(b)),
std::next(std::begin(out), std::size(a)));
return out;
return out;
}
template <typename T, typename Itr>
constexpr auto zip(T val, Itr begin, Itr end) {
std::vector<T> output;
for (auto itr = begin; itr != end; ++itr) {
output.emplace_back(val);
output.emplace_back(*itr);
}
return output;
std::vector<T> output;
for (auto itr = begin; itr != end; ++itr) {
output.emplace_back(val);
output.emplace_back(*itr);
}
return output;
}
template <typename T, typename Iterable>
constexpr auto zip(T val, Iterable const &itr) {
return zip(val, std::begin(itr), std::end(itr));
constexpr auto zip(T val, Iterable const& itr) {
return zip(val, std::begin(itr), std::end(itr));
}
constexpr auto from_string_view(std::string_view sv) {
std::vector<std::byte> range;
range.resize(sv.size());
auto itr = range.begin();
for (auto c : sv) {
*itr = std::byte(c);
++itr;
}
return range;
std::vector<std::byte> range;
range.resize(sv.size());
auto itr = range.begin();
for (auto c : sv) {
*itr = std::byte(c);
++itr;
}
return range;
}
constexpr auto make_contiguous_range(std::uint8_t start, std::uint8_t end) {
auto count = std::size_t(end) - std::size_t(start) + 1;
std::vector<std::byte> range;
range.resize(count);
for (auto i = std::size_t(start); i <= std::size_t(end); ++i) {
range[i - std::size_t(start)] = std::byte(i);
}
return range;
auto count = std::size_t(end) - std::size_t(start) + 1;
std::vector<std::byte> range;
range.resize(count);
for (auto i = std::size_t(start); i <= std::size_t(end); ++i) {
range[i - std::size_t(start)] = std::byte(i);
}
return range;
}
constexpr auto equal(auto a, auto b) {
@ -89,162 +88,175 @@ constexpr auto equal(auto a, auto b) {
} // namespace
suite writer = [] {
"writer empty span"_test = [] {
std::array<std::byte, 16> payload;
"writer empty span"_test = [] {
std::array<std::byte, 16> payload;
msgpack::writer writer(payload);
expect(writer.tell() == 0);
};
msgpack::writer writer(payload);
expect(writer.tell() == 0);
};
"writer::write<format::positive_fixint>"_test = [] {
using fmt = format::positive_fixint;
using error = msgpack::error;
"writer::write<format::positive_fixint>"_test = [] {
using fmt = format::positive_fixint;
using error = msgpack::error;
std::array<std::byte, 2> payload;
auto constexpr expected = make_bytes(0x32, 0x55);
msgpack::writer writer(payload);
auto result = writer.write<fmt>(std::uint8_t{0x32});
expect(!!result);
expect(writer.tell() == 1);
expect(*writer.subspan().begin() == std::byte{0x32});
expect(writer.write<fmt>(std::uint8_t{0x82}) == tl::make_unexpected(error::bad_value));
std::array<std::byte, 2> payload;
auto constexpr expected = make_bytes(0x32, 0x55);
msgpack::writer writer(payload);
auto result = writer.write<fmt>(std::uint8_t{0x32});
expect(!!result);
expect(writer.tell() == 1);
expect(*writer.subspan().begin() == std::byte{0x32});
expect(writer.write<fmt>(std::uint8_t{0x82})
== tl::make_unexpected(error::bad_value));
writer.write(std::uint8_t{0x55});
expect(writer.tell() == 2);
expect(equal(writer.subspan(), expected));
expect(writer.write(std::uint8_t{0x01}) == tl::make_unexpected(error::out_of_space));
};
writer.write(std::uint8_t{0x55});
expect(writer.tell() == 2);
expect(equal(writer.subspan(), expected));
expect(writer.write(std::uint8_t{0x01})
== tl::make_unexpected(error::out_of_space));
};
"writer::write<format::uint8>"_test = [] {
using fmt = format::uint8;
using error = msgpack::error;
"writer::write<format::uint8>"_test = [] {
using fmt = format::uint8;
using error = msgpack::error;
std::array<std::byte, 4> payload;
auto constexpr expected = make_bytes(0xcc, 0x32, 0xcc, 0x82);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(std::uint8_t{0x32}));
expect(writer.tell() == 2);
expect(equal(writer.subspan(), std::span{expected.begin(), 2}));
expect(!!writer.write<fmt>(std::uint8_t{0x82}));
expect(equal(writer.subspan(), expected));
expect(writer.write(std::uint8_t{0x01}) == tl::make_unexpected(error::out_of_space));
};
std::array<std::byte, 4> payload;
auto constexpr expected = make_bytes(0xcc, 0x32, 0xcc, 0x82);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(std::uint8_t{0x32}));
expect(writer.tell() == 2);
expect(equal(writer.subspan(), std::span{expected.begin(), 2}));
expect(!!writer.write<fmt>(std::uint8_t{0x82}));
expect(equal(writer.subspan(), expected));
expect(writer.write(std::uint8_t{0x01})
== tl::make_unexpected(error::out_of_space));
};
"writer::write<format::uint16>"_test = [] {
using fmt = format::uint16;
using error = msgpack::error;
"writer::write<format::uint16>"_test = [] {
using fmt = format::uint16;
using error = msgpack::error;
std::array<std::byte, 6> payload;
auto constexpr expected = make_bytes(0xcd, 0x32, 0xcc, 0xcd, 0xaa, 0xff);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(std::uint16_t{0x32cc}));
expect(writer.tell() == 3);
expect(equal(writer.subspan(), std::span{expected.begin(), 3}));
expect(!!writer.write<fmt>(0xaaff));
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(0x01) == tl::make_unexpected(error::out_of_space));
};
std::array<std::byte, 6> payload;
auto constexpr expected =
make_bytes(0xcd, 0x32, 0xcc, 0xcd, 0xaa, 0xff);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(std::uint16_t{0x32cc}));
expect(writer.tell() == 3);
expect(equal(writer.subspan(), std::span{expected.begin(), 3}));
expect(!!writer.write<fmt>(0xaaff));
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(0x01)
== tl::make_unexpected(error::out_of_space));
};
"writer::write<format::uint32>"_test = [] {
using fmt = format::uint32;
using error = msgpack::error;
"writer::write<format::uint32>"_test = [] {
using fmt = format::uint32;
using error = msgpack::error;
std::array<std::byte, 5> payload;
auto constexpr expected = make_bytes(0xce, 0x01, 0x02, 0x03, 0x04);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(0x01020304));
expect(writer.tell() == 5);
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(0x01) == tl::make_unexpected(error::out_of_space));
};
std::array<std::byte, 5> payload;
auto constexpr expected = make_bytes(0xce, 0x01, 0x02, 0x03, 0x04);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(0x01020304));
expect(writer.tell() == 5);
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(0x01)
== tl::make_unexpected(error::out_of_space));
};
"writer::write<format::uint64>"_test = [] {
using fmt = format::uint64;
using error = msgpack::error;
"writer::write<format::uint64>"_test = [] {
using fmt = format::uint64;
using error = msgpack::error;
std::array<std::byte, 9> payload;
auto constexpr expected = make_bytes(
0xcf, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(0x0102030405060708));
expect(writer.tell() == 9);
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(0x01) == tl::make_unexpected(error::out_of_space));
};
std::array<std::byte, 9> payload;
auto constexpr expected = make_bytes(
0xcf, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(0x0102030405060708));
expect(writer.tell() == 9);
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(0x01)
== tl::make_unexpected(error::out_of_space));
};
"writer::write<format::negative_fixint>"_test = [] {
using fmt = format::negative_fixint;
using error = msgpack::error;
"writer::write<format::negative_fixint>"_test = [] {
using fmt = format::negative_fixint;
using error = msgpack::error;
std::array<std::byte, 2> payload;
auto constexpr expected = make_bytes(0xff, 0xe0);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(-1));
expect(writer.tell() == 1);
expect(*writer.subspan().begin() == std::byte{0xff});
expect(writer.write<fmt>(-33) == tl::make_unexpected(error::bad_value));
expect(!!writer.write<fmt>(-32));
expect(writer.tell() == 2);
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(-5) == tl::make_unexpected(error::out_of_space));
};
std::array<std::byte, 2> payload;
auto constexpr expected = make_bytes(0xff, 0xe0);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(-1));
expect(writer.tell() == 1);
expect(*writer.subspan().begin() == std::byte{0xff});
expect(writer.write<fmt>(-33) == tl::make_unexpected(error::bad_value));
expect(!!writer.write<fmt>(-32));
expect(writer.tell() == 2);
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(-5)
== tl::make_unexpected(error::out_of_space));
};
"writer::write<format::int8>"_test = [] {
using fmt = format::int8;
using error = msgpack::error;
"writer::write<format::int8>"_test = [] {
using fmt = format::int8;
using error = msgpack::error;
std::array<std::byte, 4> payload;
auto constexpr expected = make_bytes(0xd0, 0x32, 0xd0, 0xfb);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(std::int8_t{0x32}));
expect(writer.tell() == 2);
expect(equal(writer.subspan(), std::span{expected.begin(), 2}));
expect(!!writer.write<fmt>(std::int8_t{-5}));
expect(equal(writer.subspan(), expected));
expect(writer.write(std::uint8_t{0x01}) == tl::make_unexpected(error::out_of_space));
};
std::array<std::byte, 4> payload;
auto constexpr expected = make_bytes(0xd0, 0x32, 0xd0, 0xfb);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(std::int8_t{0x32}));
expect(writer.tell() == 2);
expect(equal(writer.subspan(), std::span{expected.begin(), 2}));
expect(!!writer.write<fmt>(std::int8_t{-5}));
expect(equal(writer.subspan(), expected));
expect(writer.write(std::uint8_t{0x01})
== tl::make_unexpected(error::out_of_space));
};
"writer::write<format::int16>"_test = [] {
using fmt = format::int16;
using error = msgpack::error;
"writer::write<format::int16>"_test = [] {
using fmt = format::int16;
using error = msgpack::error;
std::array<std::byte, 6> payload;
auto constexpr expected = make_bytes(0xd1, 0x32, 0xcc, 0xd1, 0xff, 0xfa);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(std::int16_t{0x32cc}));
expect(writer.tell() == 3);
expect(equal(writer.subspan(), std::span{expected.begin(), 3}));
expect(!!writer.write<fmt>(-6));
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(0x01) == tl::make_unexpected(error::out_of_space));
};
std::array<std::byte, 6> payload;
auto constexpr expected =
make_bytes(0xd1, 0x32, 0xcc, 0xd1, 0xff, 0xfa);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(std::int16_t{0x32cc}));
expect(writer.tell() == 3);
expect(equal(writer.subspan(), std::span{expected.begin(), 3}));
expect(!!writer.write<fmt>(-6));
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(0x01)
== tl::make_unexpected(error::out_of_space));
};
"writer::write<format::int32>"_test = [] {
using fmt = format::int32;
using error = msgpack::error;
"writer::write<format::int32>"_test = [] {
using fmt = format::int32;
using error = msgpack::error;
std::array<std::byte, 5> payload;
auto constexpr expected = make_bytes(0xd2, 0x01, 0x02, 0x03, 0x04);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(0x01020304));
expect(writer.tell() == 5);
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(0x01) == tl::make_unexpected(error::out_of_space));
};
std::array<std::byte, 5> payload;
auto constexpr expected = make_bytes(0xd2, 0x01, 0x02, 0x03, 0x04);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(0x01020304));
expect(writer.tell() == 5);
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(0x01)
== tl::make_unexpected(error::out_of_space));
};
"writer::write<format::int64>"_test = [] {
using fmt = format::int64;
using error = msgpack::error;
"writer::write<format::int64>"_test = [] {
using fmt = format::int64;
using error = msgpack::error;
std::array<std::byte, 9> payload;
auto constexpr expected = make_bytes(
0xd3, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(std::int64_t{0x0102030405060708}));
expect(writer.tell() == 9);
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(0x01) == tl::make_unexpected(error::out_of_space));
};
std::array<std::byte, 9> payload;
auto constexpr expected = make_bytes(
0xd3, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(std::int64_t{0x0102030405060708}));
expect(writer.tell() == 9);
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(0x01)
== tl::make_unexpected(error::out_of_space));
};
"writer::write<format::fixstr>"_test = [] {
using fmt = format::fixstr;
@ -259,15 +271,14 @@ suite writer = [] {
"writer::write<format::str8>"_test = [] {
using fmt = format::str8;
std::array<std::byte, 46> payload;
auto constexpr expected = make_bytes(0xd9, 44,
't', 'h', 'e', ' ', 'q', 'u', 'i', 'c', 'k', ' ',
'b', 'r', 'o', 'w', 'n', ' ', 'f', 'o', 'x', ' ',
'j', 'u', 'm', 'p', 'e', 'd', ' ', 'o', 'v', 'e', 'r', ' ',
't', 'h', 'e', ' ', 'l', 'a', 'z', 'y', ' ', 'd', 'o', 'g');
auto constexpr expected = make_bytes(0xd9, 44, 't', 'h', 'e', ' ', 'q',
'u', 'i', 'c', 'k', ' ', 'b', 'r', 'o', 'w', 'n', ' ', 'f', 'o',
'x', ' ', 'j', 'u', 'm', 'p', 'e', 'd', ' ', 'o', 'v', 'e', 'r',
' ', 't', 'h', 'e', ' ', 'l', 'a', 'z', 'y', ' ', 'd', 'o',
'g');
msgpack::writer writer(payload);
std::string_view txt =
"the quick brown fox jumped over the lazy dog";
std::string_view txt = "the quick brown fox jumped over the lazy dog";
expect(!!writer.write<fmt>(std::move(txt)));
expect(equal(writer.subspan(), expected));
};
@ -275,15 +286,14 @@ suite writer = [] {
"writer::write<format::str16>"_test = [] {
using fmt = format::str16;
std::array<std::byte, 47> payload;
auto constexpr expected = make_bytes(0xda, 0, 44,
't', 'h', 'e', ' ', 'q', 'u', 'i', 'c', 'k', ' ',
'b', 'r', 'o', 'w', 'n', ' ', 'f', 'o', 'x', ' ',
'j', 'u', 'm', 'p', 'e', 'd', ' ', 'o', 'v', 'e', 'r', ' ',
't', 'h', 'e', ' ', 'l', 'a', 'z', 'y', ' ', 'd', 'o', 'g');
auto constexpr expected = make_bytes(0xda, 0, 44, 't', 'h', 'e', ' ',
'q', 'u', 'i', 'c', 'k', ' ', 'b', 'r', 'o', 'w', 'n', ' ', 'f',
'o', 'x', ' ', 'j', 'u', 'm', 'p', 'e', 'd', ' ', 'o', 'v', 'e',
'r', ' ', 't', 'h', 'e', ' ', 'l', 'a', 'z', 'y', ' ', 'd', 'o',
'g');
msgpack::writer writer(payload);
std::string_view txt =
"the quick brown fox jumped over the lazy dog";
std::string_view txt = "the quick brown fox jumped over the lazy dog";
expect(!!writer.write<fmt>(std::move(txt)));
expect(equal(writer.subspan(), expected));
};
@ -291,15 +301,14 @@ suite writer = [] {
"writer::write<format::str32>"_test = [] {
using fmt = format::str32;
std::array<std::byte, 49> payload;
auto constexpr expected = make_bytes(0xdb, 0, 0, 0, 44,
't', 'h', 'e', ' ', 'q', 'u', 'i', 'c', 'k', ' ',
'b', 'r', 'o', 'w', 'n', ' ', 'f', 'o', 'x', ' ',
'j', 'u', 'm', 'p', 'e', 'd', ' ', 'o', 'v', 'e', 'r', ' ',
't', 'h', 'e', ' ', 'l', 'a', 'z', 'y', ' ', 'd', 'o', 'g');
auto constexpr expected = make_bytes(0xdb, 0, 0, 0, 44, 't', 'h', 'e',
' ', 'q', 'u', 'i', 'c', 'k', ' ', 'b', 'r', 'o', 'w', 'n', ' ',
'f', 'o', 'x', ' ', 'j', 'u', 'm', 'p', 'e', 'd', ' ', 'o', 'v',
'e', 'r', ' ', 't', 'h', 'e', ' ', 'l', 'a', 'z', 'y', ' ', 'd',
'o', 'g');
msgpack::writer writer(payload);
std::string_view txt =
"the quick brown fox jumped over the lazy dog";
std::string_view txt = "the quick brown fox jumped over the lazy dog";
expect(!!writer.write<fmt>(std::move(txt)));
expect(equal(writer.subspan(), expected));
};
@ -307,8 +316,8 @@ suite writer = [] {
"writer::write<format::bin8>"_test = [] {
using fmt = format::bin8;
std::array<std::byte, 12> payload;
auto constexpr expected = make_bytes(0xc4, 0x07, 0x01, 0x02, 0x03, 0x04,
0xf8, 0xf9, 0xfa);
auto constexpr expected = make_bytes(
0xc4, 0x07, 0x01, 0x02, 0x03, 0x04, 0xf8, 0xf9, 0xfa);
msgpack::writer writer(payload);
std::span<std::byte const> bv(expected.begin() + 2, expected.end());
@ -319,8 +328,8 @@ suite writer = [] {
"writer::write<format::bin16>"_test = [] {
using fmt = format::bin16;
std::array<std::byte, 12> payload;
auto constexpr expected = make_bytes(0xc5, 0x0, 0x07, 0x01, 0x02, 0x03,
0x04, 0xf8, 0xf9, 0xfa);
auto constexpr expected = make_bytes(
0xc5, 0x0, 0x07, 0x01, 0x02, 0x03, 0x04, 0xf8, 0xf9, 0xfa);
msgpack::writer writer(payload);
std::span<std::byte const> bv(expected.begin() + 3, expected.end());