Hello libcode-seafire-common
This commit is contained in:
commit
674b3f7cd8
17
.editorconfig
Normal file
17
.editorconfig
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
indent_size = 4
|
||||||
|
max_line_length = off
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.yaml]
|
||||||
|
indent_size = 2
|
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
* text=auto
|
24
.gitea/workflows/on-push.yaml
Normal file
24
.gitea/workflows/on-push.yaml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
name: on-push
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-test:
|
||||||
|
runs-on: linux
|
||||||
|
container: code.helloryan.se/infra/buildenv/cxx-amd64-fedora-40:latest
|
||||||
|
volumes:
|
||||||
|
- /build
|
||||||
|
steps:
|
||||||
|
- name: Clone repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Authenticate
|
||||||
|
run: |
|
||||||
|
git config unset http.https://code.helloryan.se/.extraheader
|
||||||
|
echo "${{ secrets.NETRC }}" >> ~/.netrc
|
||||||
|
- name: Initialize
|
||||||
|
run: |
|
||||||
|
bpkg create -d /build cc config.cc.coptions="-Wall -Werror"
|
||||||
|
bdep init -A /build
|
||||||
|
- name: Build
|
||||||
|
run: b
|
||||||
|
- name: Test
|
||||||
|
run: b test
|
32
.gitignore
vendored
Normal file
32
.gitignore
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
.bdep/
|
||||||
|
Doxyfile
|
||||||
|
|
||||||
|
# Local default options files.
|
||||||
|
#
|
||||||
|
.build2/local/
|
||||||
|
|
||||||
|
# Compiler/linker output.
|
||||||
|
#
|
||||||
|
*.d
|
||||||
|
*.t
|
||||||
|
*.i
|
||||||
|
*.i.*
|
||||||
|
*.ii
|
||||||
|
*.ii.*
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.gcm
|
||||||
|
*.pcm
|
||||||
|
*.ifc
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
*.a
|
||||||
|
*.lib
|
||||||
|
*.exp
|
||||||
|
*.pdb
|
||||||
|
*.ilk
|
||||||
|
*.exe
|
||||||
|
*.exe.dlls/
|
||||||
|
*.exe.manifest
|
||||||
|
*.pc
|
31
LICENSE
Normal file
31
LICENSE
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
Copyright © 2024 Ryan. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. All advertising materials mentioning features or use of this software must
|
||||||
|
display the following acknowledgement:
|
||||||
|
|
||||||
|
This product includes software developed by Ryan, http://helloryan.se/.
|
||||||
|
|
||||||
|
4. Neither the name(s) of the copyright holder(s) nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from this
|
||||||
|
software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR
|
||||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||||
|
NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
21
README.md
Normal file
21
README.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# libcode-seafire-common
|
||||||
|
|
||||||
|
![Build status](https://code.helloryan.se/code/libcode-seafire-common/actions/workflows/on-push.yaml/badge.svg)
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
None, other than a modern C++-compiler.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
See the wiki, https://code.helloryan.se/code/wiki/wiki/Build-Instructions, for
|
||||||
|
build instructions.
|
||||||
|
|
||||||
|
## Contact
|
||||||
|
|
||||||
|
Please report bugs and issues by sending an e-mail to: ryan@helloryan.se.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Please send an e-mail to ryan@helloryan.se to request an account and
|
||||||
|
write-access to the libcode-seafire-common repository.
|
4
build/.gitignore
vendored
Normal file
4
build/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/config.build
|
||||||
|
/root/
|
||||||
|
/bootstrap/
|
||||||
|
build/
|
7
build/bootstrap.build
Normal file
7
build/bootstrap.build
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
project = libcode-seafire-common
|
||||||
|
|
||||||
|
using version
|
||||||
|
using config
|
||||||
|
using test
|
||||||
|
using install
|
||||||
|
using dist
|
6
build/export.build
Normal file
6
build/export.build
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
$out_root/
|
||||||
|
{
|
||||||
|
include code/seafire/common/
|
||||||
|
}
|
||||||
|
|
||||||
|
export $out_root/code/seafire/common/$import.target
|
16
build/root.build
Normal file
16
build/root.build
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Uncomment to suppress warnings coming from external libraries.
|
||||||
|
#
|
||||||
|
#cxx.internal.scope = current
|
||||||
|
|
||||||
|
cxx.std = latest
|
||||||
|
|
||||||
|
using cxx
|
||||||
|
|
||||||
|
hxx{*}: extension = hxx
|
||||||
|
ixx{*}: extension = ixx
|
||||||
|
txx{*}: extension = txx
|
||||||
|
cxx{*}: extension = cxx
|
||||||
|
|
||||||
|
# The test target for cross-testing (running tests under Wine, etc).
|
||||||
|
#
|
||||||
|
test.target = $cxx.target
|
5
buildfile
Normal file
5
buildfile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
./: {code/ tests/} doc{README.md} legal{LICENSE} manifest
|
||||||
|
|
||||||
|
# Don't install tests.
|
||||||
|
#
|
||||||
|
tests/: install = false
|
9
code/seafire/common/.gitignore
vendored
Normal file
9
code/seafire/common/.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Generated version header.
|
||||||
|
#
|
||||||
|
version.hxx
|
||||||
|
|
||||||
|
# Unit test executables and Testscript output directories
|
||||||
|
# (can be symlinks).
|
||||||
|
#
|
||||||
|
*.test
|
||||||
|
test-*.test
|
15
code/seafire/common/allocator.cxx
Normal file
15
code/seafire/common/allocator.cxx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include <code/seafire/common/allocator.hxx>
|
||||||
|
|
||||||
|
namespace code::seafire::common
|
||||||
|
{
|
||||||
|
|
||||||
|
allocator_t::
|
||||||
|
allocator_t() = default;
|
||||||
|
|
||||||
|
allocator_t::allocation_t::
|
||||||
|
allocation_t() = default;
|
||||||
|
|
||||||
|
allocator_t::allocation_t::
|
||||||
|
~allocation_t() noexcept = default;
|
||||||
|
|
||||||
|
} // namespace code::seafire::common
|
66
code/seafire/common/allocator.hxx
Normal file
66
code/seafire/common/allocator.hxx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#ifndef code__seafire__common__allocator_hxx_
|
||||||
|
#define code__seafire__common__allocator_hxx_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
namespace code::seafire::common
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Implements a memory allocator keeping track of allocations.
|
||||||
|
///
|
||||||
|
/// All allocations made through an allocator_t are valid for the
|
||||||
|
/// lifetime of the allocator_t object.
|
||||||
|
///
|
||||||
|
class allocator_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
allocator_t();
|
||||||
|
|
||||||
|
allocator_t(allocator_t const&) = delete;
|
||||||
|
allocator_t(allocator_t&&) = delete;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T&
|
||||||
|
alloc();
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T&
|
||||||
|
alloc(T const&);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T&
|
||||||
|
alloc(T&&);
|
||||||
|
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
T&
|
||||||
|
alloc_emplace(Args&&...);
|
||||||
|
|
||||||
|
allocator_t& operator=(allocator_t const&) = delete;
|
||||||
|
allocator_t& operator=(allocator_t&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct allocation_t
|
||||||
|
{
|
||||||
|
allocation_t();
|
||||||
|
|
||||||
|
allocation_t(allocation_t const&) = delete;
|
||||||
|
allocation_t(allocation_t&&) = delete;
|
||||||
|
|
||||||
|
virtual
|
||||||
|
~allocation_t() noexcept;
|
||||||
|
|
||||||
|
allocation_t& operator=(allocation_t const&) = delete;
|
||||||
|
allocation_t& operator=(allocation_t&&) = delete;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
std::set<std::unique_ptr<allocation_t>> allocations_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace code::seafire::common
|
||||||
|
|
||||||
|
#include <code/seafire/common/allocator.txx>
|
||||||
|
|
||||||
|
#endif
|
55
code/seafire/common/allocator.txx
Normal file
55
code/seafire/common/allocator.txx
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
namespace code::seafire::common
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T&
|
||||||
|
allocator_t::
|
||||||
|
alloc()
|
||||||
|
{
|
||||||
|
return alloc_emplace<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T&
|
||||||
|
allocator_t::
|
||||||
|
alloc(T const& object)
|
||||||
|
{
|
||||||
|
return alloc_emplace<T>(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T&
|
||||||
|
allocator_t::
|
||||||
|
alloc(T&& object)
|
||||||
|
{
|
||||||
|
return alloc_emplace<T>(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
T&
|
||||||
|
allocator_t::
|
||||||
|
alloc_emplace(Args&&... args)
|
||||||
|
{
|
||||||
|
struct object_t
|
||||||
|
: allocation_t
|
||||||
|
{
|
||||||
|
object_t(Args&&... args)
|
||||||
|
: object{std::forward<Args>(args)...}
|
||||||
|
{}
|
||||||
|
|
||||||
|
T object;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<object_t> a{
|
||||||
|
new object_t{std::forward<Args>(args)...}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto& ref = a->object;
|
||||||
|
|
||||||
|
allocations_.emplace(std::move(a));
|
||||||
|
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace code::seafire::common
|
65
code/seafire/common/buildfile
Normal file
65
code/seafire/common/buildfile
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
intf_libs = # Interface dependencies.
|
||||||
|
impl_libs = # Implementation dependencies.
|
||||||
|
|
||||||
|
import intf_libs =+ libasio%lib{asio}
|
||||||
|
|
||||||
|
./: lib{code-seafire-common}: libul{code-seafire-common}
|
||||||
|
|
||||||
|
libul{code-seafire-common}: {hxx ixx txx cxx}{** -**.test... -version} \
|
||||||
|
{hxx }{ version}
|
||||||
|
|
||||||
|
libul{code-seafire-common}: $impl_libs $intf_libs
|
||||||
|
|
||||||
|
# Unit tests.
|
||||||
|
#
|
||||||
|
exe{*.test}:
|
||||||
|
{
|
||||||
|
test = true
|
||||||
|
install = false
|
||||||
|
}
|
||||||
|
|
||||||
|
for t: cxx{**.test...}
|
||||||
|
{
|
||||||
|
d = $directory($t)
|
||||||
|
n = $name($t)...
|
||||||
|
|
||||||
|
./: $d/exe{$n}: $t $d/{hxx ixx txx}{+$n} $d/testscript{+$n}
|
||||||
|
$d/exe{$n}: libul{code-seafire-common}: bin.whole = false
|
||||||
|
}
|
||||||
|
|
||||||
|
hxx{version}: in{version} $src_root/manifest
|
||||||
|
{
|
||||||
|
dist = true
|
||||||
|
clean = ($src_root != $out_root)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build options.
|
||||||
|
#
|
||||||
|
cxx.poptions =+ "-I$out_root" "-I$src_root"
|
||||||
|
cxx.coptions =+ -fvisibility=hidden
|
||||||
|
|
||||||
|
# Export options.
|
||||||
|
#
|
||||||
|
lib{code-seafire-common}:
|
||||||
|
{
|
||||||
|
cxx.export.poptions = "-I$out_root" "-I$src_root"
|
||||||
|
cxx.export.libs = $intf_libs
|
||||||
|
}
|
||||||
|
|
||||||
|
# For pre-releases use the complete version to make sure they cannot
|
||||||
|
# be used in place of another pre-release or the final version. See
|
||||||
|
# the version module for details on the version.* variable values.
|
||||||
|
#
|
||||||
|
if $version.pre_release
|
||||||
|
lib{code-seafire-common}: bin.lib.version = "-$version.project_id"
|
||||||
|
else
|
||||||
|
lib{code-seafire-common}: bin.lib.version = "-$version.major.$version.minor"
|
||||||
|
|
||||||
|
# Install into the code/seafire/common/ subdirectory of, say, /usr/include/
|
||||||
|
# recreating subdirectories.
|
||||||
|
#
|
||||||
|
{hxx ixx txx}{*}:
|
||||||
|
{
|
||||||
|
install = include/code/seafire/common/
|
||||||
|
install.subdirs = true
|
||||||
|
}
|
132
code/seafire/common/diagnostics.cxx
Normal file
132
code/seafire/common/diagnostics.cxx
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
#include <code/seafire/common/diagnostics.hxx>
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
namespace code::seafire::common
|
||||||
|
{
|
||||||
|
|
||||||
|
void
|
||||||
|
diagnostics_t::
|
||||||
|
enable(category_t const& category)
|
||||||
|
{
|
||||||
|
categories_.emplace(&category);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
diagnostics_t::
|
||||||
|
disable(category_t const& category)
|
||||||
|
{
|
||||||
|
categories_.erase(&category);
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnostics_t::proxy_t
|
||||||
|
diagnostics_t::
|
||||||
|
operator<<(category_t const& category)
|
||||||
|
{
|
||||||
|
return proxy_t{*this, category};
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnostics_t::
|
||||||
|
diagnostics_t() = default;
|
||||||
|
|
||||||
|
diagnostics_t::
|
||||||
|
~diagnostics_t() noexcept = default;
|
||||||
|
|
||||||
|
bool
|
||||||
|
diagnostics_t::
|
||||||
|
is_enabled(category_t const& category)
|
||||||
|
{
|
||||||
|
return categories_.count(&category) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
diagnostics_t::
|
||||||
|
log(record_t record)
|
||||||
|
{
|
||||||
|
if (!is_enabled(record.category)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t max{0};
|
||||||
|
|
||||||
|
for (auto const& j : categories_) {
|
||||||
|
if (auto size = j->prefix().size(); size > max) {
|
||||||
|
max = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream message;
|
||||||
|
|
||||||
|
for (std::string line; std::getline(record.message, line);) {
|
||||||
|
message << " -> "
|
||||||
|
<< std::left
|
||||||
|
<< std::setw(max)
|
||||||
|
<< record.category.prefix()
|
||||||
|
<< ": "
|
||||||
|
<< line
|
||||||
|
<< '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
do_log(message.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnostics_t::category_t::
|
||||||
|
category_t(std::string prefix)
|
||||||
|
: prefix_{std::move(prefix)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
diagnostics_t::category_t::
|
||||||
|
~category_t() noexcept = default;
|
||||||
|
|
||||||
|
std::string const&
|
||||||
|
diagnostics_t::category_t::
|
||||||
|
prefix() const
|
||||||
|
{
|
||||||
|
return prefix_;
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnostics_t::proxy_t::
|
||||||
|
~proxy_t() noexcept
|
||||||
|
{
|
||||||
|
if (diagnostics_) {
|
||||||
|
diagnostics_->log({category_, std::move(message_)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnostics_t::proxy_t::
|
||||||
|
proxy_t(diagnostics_t& diagnostics, category_t const& category)
|
||||||
|
: diagnostics_{&diagnostics},
|
||||||
|
category_{category}
|
||||||
|
{}
|
||||||
|
|
||||||
|
diagnostics_t::proxy_t::
|
||||||
|
proxy_t(proxy_t&& other) noexcept
|
||||||
|
: category_{other.category_}
|
||||||
|
{
|
||||||
|
std::swap(diagnostics_, other.diagnostics_);
|
||||||
|
}
|
||||||
|
|
||||||
|
ostream_diagnostics_t::
|
||||||
|
ostream_diagnostics_t(std::ostream& output)
|
||||||
|
: output_{output}
|
||||||
|
{}
|
||||||
|
|
||||||
|
ostream_diagnostics_t::
|
||||||
|
~ostream_diagnostics_t() noexcept = default;
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
ostream_diagnostics_t::
|
||||||
|
output()
|
||||||
|
{
|
||||||
|
return output_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ostream_diagnostics_t::
|
||||||
|
do_log(std::string const& message)
|
||||||
|
{
|
||||||
|
output() << message;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace code::seafire::common
|
160
code/seafire/common/diagnostics.hxx
Normal file
160
code/seafire/common/diagnostics.hxx
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
#ifndef code__seafire__common__diagnostics_hxx_
|
||||||
|
#define code__seafire__common__diagnostics_hxx_
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
#include <set>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace code::seafire::common
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Provides a base for implementing diagnostics.
|
||||||
|
///
|
||||||
|
class diagnostics_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class category_t;
|
||||||
|
class proxy_t;
|
||||||
|
|
||||||
|
friend proxy_t;
|
||||||
|
|
||||||
|
void
|
||||||
|
enable(category_t const&);
|
||||||
|
|
||||||
|
void
|
||||||
|
disable(category_t const&);
|
||||||
|
|
||||||
|
proxy_t
|
||||||
|
operator<<(category_t const&);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
struct record_t;
|
||||||
|
|
||||||
|
diagnostics_t();
|
||||||
|
|
||||||
|
diagnostics_t(diagnostics_t const&) = delete;
|
||||||
|
diagnostics_t(diagnostics_t&&) = delete;
|
||||||
|
|
||||||
|
~diagnostics_t() noexcept;
|
||||||
|
|
||||||
|
bool
|
||||||
|
is_enabled(category_t const&);
|
||||||
|
|
||||||
|
void
|
||||||
|
log(record_t);
|
||||||
|
|
||||||
|
virtual
|
||||||
|
void
|
||||||
|
do_log(std::string const&) = 0;
|
||||||
|
|
||||||
|
diagnostics_t& operator=(diagnostics_t const&) = delete;
|
||||||
|
diagnostics_t& operator=(diagnostics_t&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::set<category_t const*> categories_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Represents a diagnostic category.
|
||||||
|
///
|
||||||
|
class diagnostics_t::category_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit
|
||||||
|
category_t(std::string);
|
||||||
|
|
||||||
|
category_t(category_t const&) = delete;
|
||||||
|
category_t(category_t&&) = delete;
|
||||||
|
|
||||||
|
~category_t() noexcept;
|
||||||
|
|
||||||
|
std::string const&
|
||||||
|
prefix() const;
|
||||||
|
|
||||||
|
category_t& operator=(category_t const&) = delete;
|
||||||
|
category_t& operator=(category_t&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string prefix_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Proxy for writing diagnostics.
|
||||||
|
///
|
||||||
|
class diagnostics_t::proxy_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~proxy_t() noexcept;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
proxy_t&
|
||||||
|
operator<<(T const& other)
|
||||||
|
{
|
||||||
|
message_ << other;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend diagnostics_t;
|
||||||
|
|
||||||
|
proxy_t(diagnostics_t&, category_t const&);
|
||||||
|
|
||||||
|
proxy_t(proxy_t const&) = delete;
|
||||||
|
|
||||||
|
proxy_t(proxy_t&&) noexcept;
|
||||||
|
|
||||||
|
proxy_t& operator=(proxy_t const&) = delete;
|
||||||
|
proxy_t& operator=(proxy_t&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
diagnostics_t* diagnostics_{};
|
||||||
|
category_t const& category_;
|
||||||
|
std::stringstream message_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Represents a diagnostic record.
|
||||||
|
///
|
||||||
|
struct diagnostics_t::record_t
|
||||||
|
{
|
||||||
|
/// The diagnostic category.
|
||||||
|
///
|
||||||
|
category_t const& category;
|
||||||
|
|
||||||
|
/// The diagnostic message.
|
||||||
|
///
|
||||||
|
std::stringstream message;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class ostream_diagnostics_t
|
||||||
|
: public diagnostics_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit
|
||||||
|
ostream_diagnostics_t(std::ostream&);
|
||||||
|
|
||||||
|
ostream_diagnostics_t(ostream_diagnostics_t const&) = delete;
|
||||||
|
ostream_diagnostics_t(ostream_diagnostics_t&&) = delete;
|
||||||
|
|
||||||
|
~ostream_diagnostics_t() noexcept;
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
output();
|
||||||
|
|
||||||
|
ostream_diagnostics_t& operator=(ostream_diagnostics_t const&) = delete;
|
||||||
|
ostream_diagnostics_t& operator=(ostream_diagnostics_t&&) = delete;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void
|
||||||
|
do_log(std::string const&) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::ostream& output_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace code::seafire::common
|
||||||
|
|
||||||
|
#endif
|
48
code/seafire/common/extension-context.cxx
Normal file
48
code/seafire/common/extension-context.cxx
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#include <code/seafire/common/extension-context.hxx>
|
||||||
|
|
||||||
|
namespace code::seafire::common
|
||||||
|
{
|
||||||
|
|
||||||
|
void*
|
||||||
|
extension_context_t::
|
||||||
|
use(std::type_index const& key) const
|
||||||
|
{
|
||||||
|
auto it = extensions_.find(key);
|
||||||
|
|
||||||
|
if (it == extensions_.end())
|
||||||
|
throw extension_not_found_t{};
|
||||||
|
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
extension_context_t::
|
||||||
|
extend(std::type_index const& key, void* ptr)
|
||||||
|
{
|
||||||
|
if (0 != extensions_.count(key))
|
||||||
|
throw duplicate_extension_t{};
|
||||||
|
|
||||||
|
if (!ptr)
|
||||||
|
throw std::invalid_argument{"invalid pointer"};
|
||||||
|
|
||||||
|
extensions_.emplace(key, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
extension_context_t::
|
||||||
|
erase_extension(std::type_index const& key)
|
||||||
|
{
|
||||||
|
extensions_.erase(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
extension_not_found_t::
|
||||||
|
extension_not_found_t()
|
||||||
|
: std::runtime_error{"extension not found"}
|
||||||
|
{}
|
||||||
|
|
||||||
|
duplicate_extension_t::
|
||||||
|
duplicate_extension_t()
|
||||||
|
: std::runtime_error{"extension already registered"}
|
||||||
|
{}
|
||||||
|
|
||||||
|
} // namespace code::seafire::common
|
91
code/seafire/common/extension-context.hxx
Normal file
91
code/seafire/common/extension-context.hxx
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#ifndef code__seafire__common__extension_context_hxx_
|
||||||
|
#define code__seafire__common__extension_context_hxx_
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <typeindex>
|
||||||
|
|
||||||
|
namespace code::seafire::common
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Implements a context tracking extensions.
|
||||||
|
///
|
||||||
|
/// Ownership of registered extensions is not assumed.
|
||||||
|
///
|
||||||
|
class extension_context_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template<typename E>
|
||||||
|
E&
|
||||||
|
use();
|
||||||
|
|
||||||
|
template<typename E>
|
||||||
|
E const&
|
||||||
|
use() const;
|
||||||
|
|
||||||
|
template<typename E>
|
||||||
|
void
|
||||||
|
extend(E* ptr);
|
||||||
|
|
||||||
|
template<typename E>
|
||||||
|
void
|
||||||
|
erase();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void*
|
||||||
|
use(std::type_index const& key) const;
|
||||||
|
|
||||||
|
void
|
||||||
|
extend(std::type_index const& key, void* ptr);
|
||||||
|
|
||||||
|
void
|
||||||
|
erase_extension(std::type_index const& key);
|
||||||
|
|
||||||
|
std::map<std::type_index, void*> extensions_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class extension_not_found_t
|
||||||
|
: public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
extension_not_found_t();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class duplicate_extension_t
|
||||||
|
: public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
duplicate_extension_t();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Provides extending an extension context using RAII.
|
||||||
|
///
|
||||||
|
template<typename E>
|
||||||
|
class extend_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using extension_type = typename std::decay_t<E>;
|
||||||
|
|
||||||
|
extend_t(extension_context_t&, extension_type&);
|
||||||
|
|
||||||
|
extend_t(extend_t const&) = delete;
|
||||||
|
extend_t(extend_t&&) = delete;
|
||||||
|
|
||||||
|
~extend_t() noexcept;
|
||||||
|
|
||||||
|
extend_t& operator=(extend_t const&) = delete;
|
||||||
|
extend_t& operator=(extend_t&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
extension_context_t& target_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace code::seafire::common
|
||||||
|
|
||||||
|
#include <code/seafire/common/extension-context.txx>
|
||||||
|
|
||||||
|
#endif
|
63
code/seafire/common/extension-context.txx
Normal file
63
code/seafire/common/extension-context.txx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
namespace code::seafire::common
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Use an extension of type E.
|
||||||
|
///
|
||||||
|
template<typename E>
|
||||||
|
E&
|
||||||
|
extension_context_t::
|
||||||
|
use()
|
||||||
|
{
|
||||||
|
static std::type_index const key{typeid(E)};
|
||||||
|
return *(static_cast<E*>(use(key)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Use an extension of type E.
|
||||||
|
///
|
||||||
|
template<typename E>
|
||||||
|
E const&
|
||||||
|
extension_context_t::
|
||||||
|
use() const
|
||||||
|
{
|
||||||
|
static std::type_index const key{typeid(E)};
|
||||||
|
return *(static_cast<E*>(use(key)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an extension to this context.
|
||||||
|
///
|
||||||
|
template<typename E>
|
||||||
|
void
|
||||||
|
extension_context_t::
|
||||||
|
extend(E* ptr)
|
||||||
|
{
|
||||||
|
static std::type_index const key{typeid(E)};
|
||||||
|
extend(key, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Erase an extension from this context.
|
||||||
|
///
|
||||||
|
template<typename E>
|
||||||
|
void
|
||||||
|
extension_context_t::
|
||||||
|
erase()
|
||||||
|
{
|
||||||
|
static std::type_index const key{typeid(E)};
|
||||||
|
erase_extension(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename E>
|
||||||
|
extend_t<E>::
|
||||||
|
extend_t(extension_context_t& target, extension_type& ext)
|
||||||
|
: target_{target}
|
||||||
|
{
|
||||||
|
target.extend(&ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename E>
|
||||||
|
extend_t<E>::
|
||||||
|
~extend_t() noexcept
|
||||||
|
{
|
||||||
|
target_.erase<extension_type>();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace code::seafire::common
|
54
code/seafire/common/invoke.hxx
Normal file
54
code/seafire/common/invoke.hxx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#ifndef code__seafire__common__invoke_hxx_
|
||||||
|
#define code__seafire__common__invoke_hxx_
|
||||||
|
|
||||||
|
#include <code/seafire/server/request.hxx>
|
||||||
|
#include <code/seafire/server/response.hxx>
|
||||||
|
|
||||||
|
namespace code::seafire::common
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename Ret, typename T, typename... Direct, typename... Params>
|
||||||
|
Ret
|
||||||
|
do_invoke(T& target,
|
||||||
|
server::request_t& req,
|
||||||
|
Direct const&... direct,
|
||||||
|
Ret (T::*func)(Direct const&..., Params const&...));
|
||||||
|
|
||||||
|
template<typename Ret, typename T, typename... Params, typename... Direct>
|
||||||
|
Ret
|
||||||
|
invoke(T& target,
|
||||||
|
server::request_t& req,
|
||||||
|
Ret (T::*func)(Params const&...),
|
||||||
|
Direct const&... direct);
|
||||||
|
|
||||||
|
template<typename Ret, typename T, typename... Direct, typename... Params>
|
||||||
|
Ret
|
||||||
|
do_invoke(T const& target,
|
||||||
|
server::request_t& req,
|
||||||
|
Direct const&... direct,
|
||||||
|
Ret (T::*func)(Direct const&..., Params const&...) const);
|
||||||
|
|
||||||
|
template<typename Ret, typename T, typename... Params, typename... Direct>
|
||||||
|
Ret
|
||||||
|
invoke(T const& target,
|
||||||
|
server::request_t& req,
|
||||||
|
Ret (T::*func)(Params const&...) const,
|
||||||
|
Direct const&... direct);
|
||||||
|
|
||||||
|
template<typename Ret, typename... Direct, typename... Params>
|
||||||
|
Ret
|
||||||
|
do_invoke(server::request_t& req,
|
||||||
|
Direct&&... direct,
|
||||||
|
Ret (*func)(Direct..., Params...));
|
||||||
|
|
||||||
|
template<typename Ret, typename... Params, typename... Direct>
|
||||||
|
Ret
|
||||||
|
invoke(server::request_t& req,
|
||||||
|
Ret (*func)(Params...),
|
||||||
|
Direct&&... direct);
|
||||||
|
|
||||||
|
} // namespace code::seafire::common
|
||||||
|
|
||||||
|
#include <code/seafire/common/invoke.txx>
|
||||||
|
|
||||||
|
#endif
|
62
code/seafire/common/invoke.txx
Normal file
62
code/seafire/common/invoke.txx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
namespace code::seafire::common
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename Ret, typename T, typename... Direct, typename... Params>
|
||||||
|
Ret
|
||||||
|
do_invoke(T& target,
|
||||||
|
server::request_t& req,
|
||||||
|
Direct const&... direct,
|
||||||
|
Ret (T::*func)(Direct const&..., Params const&...))
|
||||||
|
{
|
||||||
|
return (target.*func)(direct..., std::decay_t<Params>::fetch(req)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Ret, typename T, typename... Params, typename... Direct>
|
||||||
|
Ret
|
||||||
|
invoke(T& target,
|
||||||
|
server::request_t& req,
|
||||||
|
Ret (T::*func)(Params const&...),
|
||||||
|
Direct const&... direct)
|
||||||
|
{
|
||||||
|
return do_invoke<Ret, T, Direct...>(target, req, direct..., func);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Ret, typename T, typename... Direct, typename... Params>
|
||||||
|
Ret
|
||||||
|
do_invoke(T const& target,
|
||||||
|
server::request_t& req,
|
||||||
|
Direct const&... direct,
|
||||||
|
Ret (T::*func)(Direct const&..., Params const&...) const)
|
||||||
|
{
|
||||||
|
return (target.*func)(direct..., std::decay_t<Params>::fetch(req)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Ret, typename T, typename... Params, typename... Direct>
|
||||||
|
Ret
|
||||||
|
invoke(T const& target,
|
||||||
|
server::request_t& req,
|
||||||
|
Ret (T::*func)(Params const&...) const,
|
||||||
|
Direct const&... direct)
|
||||||
|
{
|
||||||
|
return do_invoke<Ret, T, Direct...>(target, req, direct..., func);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Ret, typename... Direct, typename... Params>
|
||||||
|
Ret
|
||||||
|
do_invoke(server::request_t& req,
|
||||||
|
Direct&&... direct,
|
||||||
|
Ret (*func)(Direct..., Params...))
|
||||||
|
{
|
||||||
|
return (*func)(direct..., std::decay_t<Params>::fetch(req)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Ret, typename... Params, typename... Direct>
|
||||||
|
Ret
|
||||||
|
invoke(server::request_t& req,
|
||||||
|
Ret (*func)(Params...),
|
||||||
|
Direct&&... direct)
|
||||||
|
{
|
||||||
|
return do_invoke<Ret, Direct...>(req, direct..., func);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace code::seafire::common
|
12
code/seafire/common/io/acceptor.cxx
Normal file
12
code/seafire/common/io/acceptor.cxx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include <code/seafire/common/io/acceptor.hxx>
|
||||||
|
|
||||||
|
namespace code::seafire::common::io
|
||||||
|
{
|
||||||
|
|
||||||
|
acceptor_t::
|
||||||
|
~acceptor_t() noexcept = default;
|
||||||
|
|
||||||
|
acceptor_t::
|
||||||
|
acceptor_t() = default;
|
||||||
|
|
||||||
|
} // namespace code::seafire::common::io
|
51
code/seafire/common/io/acceptor.hxx
Normal file
51
code/seafire/common/io/acceptor.hxx
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#ifndef code__seafire__common__io__acceptor_hxx_
|
||||||
|
#define code__seafire__common__io__acceptor_hxx_
|
||||||
|
|
||||||
|
#include <code/seafire/common/io/stream.hxx>
|
||||||
|
|
||||||
|
#include <asio.hpp>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace code::seafire::common::io
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Abstract base class for acceptors.
|
||||||
|
///
|
||||||
|
class acceptor_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Non-blocking accept handler type.
|
||||||
|
///
|
||||||
|
using accept_handler_t = std::function<
|
||||||
|
void(std::error_code, std::unique_ptr<stream_t>)
|
||||||
|
>;
|
||||||
|
|
||||||
|
virtual
|
||||||
|
~acceptor_t() noexcept;
|
||||||
|
|
||||||
|
virtual
|
||||||
|
asio::any_io_executor const&
|
||||||
|
get_executor() = 0;
|
||||||
|
|
||||||
|
/// Initiate a non-blocking accept of a new stream.
|
||||||
|
///
|
||||||
|
virtual
|
||||||
|
void
|
||||||
|
async_accept(accept_handler_t) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
acceptor_t();
|
||||||
|
|
||||||
|
acceptor_t(acceptor_t const&) = delete;
|
||||||
|
acceptor_t(acceptor_t&&) = delete;
|
||||||
|
|
||||||
|
acceptor_t& operator=(acceptor_t const&) = delete;
|
||||||
|
acceptor_t& operator=(acceptor_t&&) = delete;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace code::seafire::common::io
|
||||||
|
|
||||||
|
#endif
|
23
code/seafire/common/io/buffer.hxx
Normal file
23
code/seafire/common/io/buffer.hxx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#ifndef code__seafire__common__io__buffer_hxx_
|
||||||
|
#define code__seafire__common__io__buffer_hxx_
|
||||||
|
|
||||||
|
#include <asio.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace code::seafire::common::io
|
||||||
|
{
|
||||||
|
|
||||||
|
using const_buffer_t = asio::const_buffer;
|
||||||
|
using const_buffers_t = std::vector<const_buffer_t>;
|
||||||
|
|
||||||
|
using mutable_buffer_t = asio::mutable_buffer;
|
||||||
|
using mutable_buffers_t = std::vector<mutable_buffer_t>;
|
||||||
|
|
||||||
|
using asio::buffer;
|
||||||
|
using asio::buffer_size;
|
||||||
|
using asio::buffer_copy;
|
||||||
|
|
||||||
|
} // namespace code::seafire::common::io
|
||||||
|
|
||||||
|
#endif
|
13
code/seafire/common/io/diagnostics.cxx
Normal file
13
code/seafire/common/io/diagnostics.cxx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#include <code/seafire/common/io/diagnostics.hxx>
|
||||||
|
|
||||||
|
namespace code::seafire::common::io
|
||||||
|
{
|
||||||
|
|
||||||
|
common::diagnostics_t::category_t const&
|
||||||
|
io_category()
|
||||||
|
{
|
||||||
|
static common::diagnostics_t::category_t category{"io"};
|
||||||
|
return category;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace code::seafire::common::io
|
14
code/seafire/common/io/diagnostics.hxx
Normal file
14
code/seafire/common/io/diagnostics.hxx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#ifndef code__seafire__common__io_diagnostics_hxx_
|
||||||
|
#define code__seafire__common__io_diagnostics_hxx_
|
||||||
|
|
||||||
|
#include <code/seafire/common/diagnostics.hxx>
|
||||||
|
|
||||||
|
namespace code::seafire::common::io
|
||||||
|
{
|
||||||
|
|
||||||
|
common::diagnostics_t::category_t const&
|
||||||
|
io_category();
|
||||||
|
|
||||||
|
} // namespace code::seafire::common::io
|
||||||
|
|
||||||
|
#endif
|
31
code/seafire/common/io/error.cxx
Normal file
31
code/seafire/common/io/error.cxx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#include <code/seafire/common/io/error.hxx>
|
||||||
|
|
||||||
|
namespace code::seafire::common::io
|
||||||
|
{
|
||||||
|
|
||||||
|
std::error_code
|
||||||
|
make_error_code(error_t e)
|
||||||
|
{
|
||||||
|
static
|
||||||
|
struct : std::error_category
|
||||||
|
{
|
||||||
|
char const* name() const noexcept override
|
||||||
|
{
|
||||||
|
return "seafire.common.io";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string message(int e) const override
|
||||||
|
{
|
||||||
|
switch (static_cast<error_t>(e)) {
|
||||||
|
case error_t::unknown: return "unknown error";
|
||||||
|
case error_t::read_until_buffer_overflow: return "buffer overflow";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "(unrecognized error)";
|
||||||
|
}
|
||||||
|
} category;
|
||||||
|
|
||||||
|
return {static_cast<int>(e), category};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace code::seafire::common::io
|
37
code/seafire/common/io/error.hxx
Normal file
37
code/seafire/common/io/error.hxx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef code__seafire__common__io__error_hxx_
|
||||||
|
#define code__seafire__common__io__error_hxx_
|
||||||
|
|
||||||
|
#include <system_error>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace code::seafire::common::io
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Common error codes.
|
||||||
|
///
|
||||||
|
enum class error_t
|
||||||
|
{
|
||||||
|
/// Represents an unknown error.
|
||||||
|
///
|
||||||
|
unknown = 1,
|
||||||
|
|
||||||
|
read_until_buffer_overflow
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
std::error_code
|
||||||
|
make_error_code(error_t);
|
||||||
|
|
||||||
|
} // namespace code::seafire::common::io
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct is_error_code_enum<::code::seafire::common::io::error_t>
|
||||||
|
: true_type
|
||||||
|
{};
|
||||||
|
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
#endif
|
131
code/seafire/common/io/read-until.cxx
Normal file
131
code/seafire/common/io/read-until.cxx
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
#include <code/seafire/common/io/read-until.hxx>
|
||||||
|
|
||||||
|
#include <code/seafire/common/io/error.hxx>
|
||||||
|
|
||||||
|
namespace code::seafire::common::io
|
||||||
|
{
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
read_until(stream_t& s, asio::streambuf& b, match_condition_t m)
|
||||||
|
{
|
||||||
|
std::error_code ec;
|
||||||
|
auto n = read_until(s, b, m, ec);
|
||||||
|
|
||||||
|
if (ec) {
|
||||||
|
throw std::system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
read_until(stream_t& s,
|
||||||
|
asio::streambuf& b,
|
||||||
|
match_condition_t m,
|
||||||
|
std::error_code& ec)
|
||||||
|
{
|
||||||
|
for (;;) {
|
||||||
|
// search for a match.
|
||||||
|
//
|
||||||
|
auto begin = static_cast<char const*>(b.data().data());
|
||||||
|
auto result = m(begin, begin + b.data().size(), ec);
|
||||||
|
|
||||||
|
if (ec) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.second) {
|
||||||
|
return result.first - begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bail out if buffer is already full.
|
||||||
|
//
|
||||||
|
if (b.size() == b.max_size()) {
|
||||||
|
ec = make_error_code(error_t::read_until_buffer_overflow);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read more data.
|
||||||
|
//
|
||||||
|
auto bytes_to_read = std::min<std::size_t>(
|
||||||
|
std::max<std::size_t>( 512, b.capacity() - b.size()),
|
||||||
|
std::min<std::size_t>(65536, b.max_size() - b.size())
|
||||||
|
);
|
||||||
|
|
||||||
|
auto n = s.read(b.prepare(bytes_to_read), ec);
|
||||||
|
b.commit(n);
|
||||||
|
|
||||||
|
if (ec) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
async_read_until(stream_t& s,
|
||||||
|
asio::streambuf& b,
|
||||||
|
match_condition_t m,
|
||||||
|
read_until_handler_t h)
|
||||||
|
{
|
||||||
|
struct operation_t
|
||||||
|
{
|
||||||
|
stream_t& s;
|
||||||
|
asio::streambuf& b;
|
||||||
|
match_condition_t match;
|
||||||
|
read_until_handler_t handler;
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(std::error_code const& ec,
|
||||||
|
std::size_t n,
|
||||||
|
bool start = false)
|
||||||
|
{
|
||||||
|
if (ec || (!start && n == 0)) {
|
||||||
|
handler(ec, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
b.commit(n);
|
||||||
|
|
||||||
|
// search for a match.
|
||||||
|
//
|
||||||
|
auto begin = static_cast<char const*>(b.data().data());
|
||||||
|
|
||||||
|
std::error_code match_ec;
|
||||||
|
auto result = match(begin, begin + b.data().size(), match_ec);
|
||||||
|
|
||||||
|
if (match_ec) {
|
||||||
|
handler(match_ec, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.second) {
|
||||||
|
handler({}, result.first - begin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b.size() == b.max_size()) {
|
||||||
|
handler(make_error_code(error_t::read_until_buffer_overflow), 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read more data.
|
||||||
|
//
|
||||||
|
auto bytes_to_read = std::min<std::size_t>(
|
||||||
|
std::max<std::size_t>( 512, b.capacity() - b.size()),
|
||||||
|
std::min<std::size_t>(65536, b.max_size() - b.size())
|
||||||
|
);
|
||||||
|
|
||||||
|
s.async_read(b.prepare(bytes_to_read), std::move(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
auto init = [&s, &b, m, h]
|
||||||
|
{
|
||||||
|
operation_t{s, b, m, h}({}, 0, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
asio::post(s.get_executor(), init);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace code::seafire::common::io
|
39
code/seafire/common/io/read-until.hxx
Normal file
39
code/seafire/common/io/read-until.hxx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#ifndef code__seafire__common__io__read_until_hxx_
|
||||||
|
#define code__seafire__common__io__read_until_hxx_
|
||||||
|
|
||||||
|
#include <code/seafire/common/io/stream.hxx>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
#include <asio.hpp>
|
||||||
|
|
||||||
|
namespace code::seafire::common::io
|
||||||
|
{
|
||||||
|
|
||||||
|
using match_condition_t = std::function<
|
||||||
|
std::pair<char const*, bool>(char const*, char const*, std::error_code&)
|
||||||
|
>;
|
||||||
|
|
||||||
|
using read_until_handler_t = std::function<
|
||||||
|
void(std::error_code, std::size_t)
|
||||||
|
>;
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
read_until(stream_t&, asio::streambuf&, match_condition_t);
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
read_until(stream_t&,
|
||||||
|
asio::streambuf&,
|
||||||
|
match_condition_t,
|
||||||
|
std::error_code&);
|
||||||
|
|
||||||
|
void
|
||||||
|
async_read_until(stream_t&,
|
||||||
|
asio::streambuf&,
|
||||||
|
match_condition_t,
|
||||||
|
read_until_handler_t);
|
||||||
|
|
||||||
|
} // namespace code::seafire::common::io
|
||||||
|
|
||||||
|
#endif
|
12
code/seafire/common/io/stream.cxx
Normal file
12
code/seafire/common/io/stream.cxx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include <code/seafire/common/io/stream.hxx>
|
||||||
|
|
||||||
|
namespace code::seafire::common::io
|
||||||
|
{
|
||||||
|
|
||||||
|
stream_t::
|
||||||
|
~stream_t() noexcept = default;
|
||||||
|
|
||||||
|
stream_t::
|
||||||
|
stream_t() = default;
|
||||||
|
|
||||||
|
} // namespace code::seafire::common::io
|
137
code/seafire/common/io/stream.hxx
Normal file
137
code/seafire/common/io/stream.hxx
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
#ifndef code__seafire__common__io__stream_hxx_
|
||||||
|
#define code__seafire__common__io__stream_hxx_
|
||||||
|
|
||||||
|
#include <code/seafire/common/io/buffer.hxx>
|
||||||
|
|
||||||
|
#include <asio.hpp>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
namespace code::seafire::common::io
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Abstract base class for stream.
|
||||||
|
///
|
||||||
|
class stream_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Non-blocking read handler type.
|
||||||
|
///
|
||||||
|
using read_handler_t = std::function<void(std::error_code, std::size_t)>;
|
||||||
|
|
||||||
|
/// Non-blocking write handler type.
|
||||||
|
///
|
||||||
|
using write_handler_t = std::function<void(std::error_code, std::size_t)>;
|
||||||
|
|
||||||
|
/// Non-blocking graceful close handler type.
|
||||||
|
///
|
||||||
|
using graceful_close_handler_t = std::function<void()>;
|
||||||
|
|
||||||
|
virtual
|
||||||
|
~stream_t() noexcept;
|
||||||
|
|
||||||
|
/// Get the asio executor associated with this stream.
|
||||||
|
///
|
||||||
|
virtual
|
||||||
|
asio::any_io_executor const&
|
||||||
|
get_executor() = 0;
|
||||||
|
|
||||||
|
/// Cancel any pending non-blocking operations.
|
||||||
|
///
|
||||||
|
virtual
|
||||||
|
void
|
||||||
|
cancel() = 0;
|
||||||
|
|
||||||
|
/// Close this stream.
|
||||||
|
///
|
||||||
|
virtual
|
||||||
|
void
|
||||||
|
close() = 0;
|
||||||
|
|
||||||
|
/// Close this stream.
|
||||||
|
///
|
||||||
|
virtual
|
||||||
|
void
|
||||||
|
close(std::error_code&) = 0;
|
||||||
|
|
||||||
|
/// Perform a blocking graceful close of this stream.
|
||||||
|
///
|
||||||
|
virtual
|
||||||
|
void
|
||||||
|
graceful_close() = 0;
|
||||||
|
|
||||||
|
/// Initiate a non-blocking graceful close of this stream.
|
||||||
|
///
|
||||||
|
virtual
|
||||||
|
void
|
||||||
|
async_graceful_close(graceful_close_handler_t) = 0;
|
||||||
|
|
||||||
|
/// Perform a blocking read on this stream.
|
||||||
|
///
|
||||||
|
virtual
|
||||||
|
std::size_t
|
||||||
|
read(mutable_buffer_t const&) = 0;
|
||||||
|
|
||||||
|
/// Perform a blocking read on this stream.
|
||||||
|
///
|
||||||
|
virtual
|
||||||
|
std::size_t
|
||||||
|
read(mutable_buffer_t const&, std::error_code&) = 0;
|
||||||
|
|
||||||
|
/// Initiate a non-blocking read on this stream.
|
||||||
|
///
|
||||||
|
virtual
|
||||||
|
void
|
||||||
|
async_read(mutable_buffer_t const&, read_handler_t) = 0;
|
||||||
|
|
||||||
|
/// Perform a blocking write on this stream.
|
||||||
|
///
|
||||||
|
virtual
|
||||||
|
std::size_t
|
||||||
|
write(const_buffer_t const&) = 0;
|
||||||
|
|
||||||
|
/// Perform a blocking write on this stream.
|
||||||
|
///
|
||||||
|
virtual
|
||||||
|
std::size_t
|
||||||
|
write(const_buffers_t const&) = 0;
|
||||||
|
|
||||||
|
/// Initiate a non-block write on this stream.
|
||||||
|
///
|
||||||
|
virtual
|
||||||
|
std::size_t
|
||||||
|
write(const_buffer_t const&, std::error_code&) = 0;
|
||||||
|
|
||||||
|
/// Initiate a non-block write on this stream.
|
||||||
|
///
|
||||||
|
virtual
|
||||||
|
std::size_t
|
||||||
|
write(const_buffers_t const&, std::error_code&) = 0;
|
||||||
|
|
||||||
|
/// Initiate a non-blocking write on this stream.
|
||||||
|
///
|
||||||
|
virtual
|
||||||
|
void
|
||||||
|
async_write(const_buffer_t const&, write_handler_t) = 0;
|
||||||
|
|
||||||
|
/// Initiate a non-blocking write on this stream.
|
||||||
|
///
|
||||||
|
virtual
|
||||||
|
void
|
||||||
|
async_write(const_buffers_t const&, write_handler_t) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
stream_t();
|
||||||
|
|
||||||
|
stream_t(stream_t const&) = delete;
|
||||||
|
stream_t(stream_t&&) = delete;
|
||||||
|
|
||||||
|
stream_t& operator=(stream_t const&) = delete;
|
||||||
|
stream_t& operator=(stream_t&&) = delete;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace code::seafire::common::io
|
||||||
|
|
||||||
|
#endif
|
36
code/seafire/common/io/tcp-acceptor.cxx
Normal file
36
code/seafire/common/io/tcp-acceptor.cxx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#include <code/seafire/common/io/tcp-acceptor.hxx>
|
||||||
|
#include <code/seafire/common/io/tcp-socket.hxx>
|
||||||
|
|
||||||
|
|
||||||
|
namespace code::seafire::common::io
|
||||||
|
{
|
||||||
|
|
||||||
|
tcp_acceptor_t::
|
||||||
|
tcp_acceptor_t(asio::io_context& io_context,
|
||||||
|
asio::ip::tcp::endpoint const& endpoint)
|
||||||
|
: acceptor_{io_context, endpoint}
|
||||||
|
{}
|
||||||
|
|
||||||
|
tcp_acceptor_t::
|
||||||
|
~tcp_acceptor_t() noexcept = default;
|
||||||
|
|
||||||
|
asio::any_io_executor const&
|
||||||
|
tcp_acceptor_t::
|
||||||
|
get_executor()
|
||||||
|
{
|
||||||
|
return acceptor_.get_executor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tcp_acceptor_t::
|
||||||
|
async_accept(accept_handler_t handler)
|
||||||
|
{
|
||||||
|
auto bound = [handler](std::error_code const& ec, asio::ip::tcp::socket socket)
|
||||||
|
{
|
||||||
|
handler(ec, std::make_unique<tcp_socket_t>(std::move(socket)));
|
||||||
|
};
|
||||||
|
|
||||||
|
acceptor_.async_accept(bound);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace code::seafire::common::io
|
43
code/seafire/common/io/tcp-acceptor.hxx
Normal file
43
code/seafire/common/io/tcp-acceptor.hxx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#ifndef code__seafire__common__io__tcp_acceptor_hxx_
|
||||||
|
#define code__seafire__common__io__tcp_acceptor_hxx_
|
||||||
|
|
||||||
|
#include <code/seafire/common/diagnostics.hxx>
|
||||||
|
|
||||||
|
#include <code/seafire/common/io/acceptor.hxx>
|
||||||
|
|
||||||
|
#include <asio.hpp>
|
||||||
|
|
||||||
|
namespace code::seafire::common::io
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Implements an tcp acceptor.
|
||||||
|
///
|
||||||
|
class tcp_acceptor_t
|
||||||
|
: public acceptor_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
tcp_acceptor_t(asio::io_context&,
|
||||||
|
asio::ip::tcp::endpoint const&);
|
||||||
|
|
||||||
|
tcp_acceptor_t(tcp_acceptor_t const&) = delete;
|
||||||
|
tcp_acceptor_t(tcp_acceptor_t&&) = delete;
|
||||||
|
|
||||||
|
~tcp_acceptor_t() noexcept override;
|
||||||
|
|
||||||
|
asio::any_io_executor const&
|
||||||
|
get_executor() override;
|
||||||
|
|
||||||
|
void
|
||||||
|
async_accept(accept_handler_t) override;
|
||||||
|
|
||||||
|
tcp_acceptor_t& operator=(tcp_acceptor_t const&) = delete;
|
||||||
|
tcp_acceptor_t& operator=(tcp_acceptor_t&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
asio::ip::tcp::acceptor acceptor_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace code::seafire::common::io
|
||||||
|
|
||||||
|
#endif
|
149
code/seafire/common/io/tcp-socket.cxx
Normal file
149
code/seafire/common/io/tcp-socket.cxx
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
#include <code/seafire/common/io/tcp-socket.hxx>
|
||||||
|
|
||||||
|
namespace code::seafire::common::io
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Construct a new TCP/IP socket from an asio socket.
|
||||||
|
///
|
||||||
|
tcp_socket_t::
|
||||||
|
tcp_socket_t(asio::ip::tcp::socket socket)
|
||||||
|
: socket_{std::move(socket)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
tcp_socket_t::
|
||||||
|
~tcp_socket_t() noexcept = default;
|
||||||
|
|
||||||
|
asio::any_io_executor const&
|
||||||
|
tcp_socket_t::
|
||||||
|
get_executor()
|
||||||
|
{
|
||||||
|
return socket_.get_executor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tcp_socket_t::
|
||||||
|
cancel()
|
||||||
|
{
|
||||||
|
socket_.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tcp_socket_t::
|
||||||
|
close()
|
||||||
|
{
|
||||||
|
socket_.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tcp_socket_t::
|
||||||
|
close(std::error_code& ec)
|
||||||
|
{
|
||||||
|
socket_.close(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tcp_socket_t::
|
||||||
|
graceful_close()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
socket_.shutdown(asio::ip::tcp::socket::shutdown_send);
|
||||||
|
socket_.read_some(buffer(throwaway_));
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
// Ignore errors during shutdown/close.
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tcp_socket_t::
|
||||||
|
async_graceful_close(graceful_close_handler_t handler)
|
||||||
|
{
|
||||||
|
std::error_code ec;
|
||||||
|
socket_.shutdown(asio::ip::tcp::socket::shutdown_send, ec);
|
||||||
|
|
||||||
|
if (ec) {
|
||||||
|
auto bound = [handler]()
|
||||||
|
{
|
||||||
|
handler();
|
||||||
|
};
|
||||||
|
|
||||||
|
asio::post(get_executor(), bound);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto bound = [handler](std::error_code const&, std::size_t)
|
||||||
|
{
|
||||||
|
// Errors are ignored during graceful shutdown/close, so we
|
||||||
|
// don't pass anything to the handler.
|
||||||
|
//
|
||||||
|
handler();
|
||||||
|
};
|
||||||
|
|
||||||
|
socket_.async_read_some(buffer(throwaway_), bound);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
tcp_socket_t::
|
||||||
|
read(mutable_buffer_t const& buffer)
|
||||||
|
{
|
||||||
|
return socket_.read_some(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
tcp_socket_t::
|
||||||
|
read(mutable_buffer_t const& buffer, std::error_code& ec)
|
||||||
|
{
|
||||||
|
return socket_.read_some(buffer, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tcp_socket_t::
|
||||||
|
async_read(mutable_buffer_t const& buffer, read_handler_t handler)
|
||||||
|
{
|
||||||
|
return socket_.async_read_some(buffer, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
tcp_socket_t::
|
||||||
|
write(const_buffer_t const& buffer)
|
||||||
|
{
|
||||||
|
return asio::write(socket_, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
tcp_socket_t::
|
||||||
|
write(const_buffers_t const& buffers)
|
||||||
|
{
|
||||||
|
return asio::write(socket_, buffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
tcp_socket_t::
|
||||||
|
write(const_buffer_t const& buffer, std::error_code& ec)
|
||||||
|
{
|
||||||
|
return asio::write(socket_, buffer, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
tcp_socket_t::
|
||||||
|
write(const_buffers_t const& buffers, std::error_code& ec)
|
||||||
|
{
|
||||||
|
return asio::write(socket_, buffers, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tcp_socket_t::
|
||||||
|
async_write(const_buffer_t const& buffer, write_handler_t handler)
|
||||||
|
{
|
||||||
|
asio::async_write(socket_, buffer, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tcp_socket_t::
|
||||||
|
async_write(const_buffers_t const& buffers, write_handler_t handler)
|
||||||
|
{
|
||||||
|
asio::async_write(socket_, buffers, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace code::seafire::common::io
|
82
code/seafire/common/io/tcp-socket.hxx
Normal file
82
code/seafire/common/io/tcp-socket.hxx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#ifndef code__seafire__common__io__tcp_socket_hxx_
|
||||||
|
#define code__seafire__common__io__tcp_socket_hxx_
|
||||||
|
|
||||||
|
#include <code/seafire/common/io/buffer.hxx>
|
||||||
|
#include <code/seafire/common/io/stream.hxx>
|
||||||
|
|
||||||
|
#include <asio.hpp>
|
||||||
|
|
||||||
|
namespace code::seafire::common::io
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Implements a TCP/IP socket.
|
||||||
|
///
|
||||||
|
class tcp_socket_t
|
||||||
|
: public stream_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit
|
||||||
|
tcp_socket_t(asio::ip::tcp::socket);
|
||||||
|
|
||||||
|
tcp_socket_t(tcp_socket_t const&) = delete;
|
||||||
|
tcp_socket_t(tcp_socket_t&&) = delete;
|
||||||
|
|
||||||
|
~tcp_socket_t() noexcept override;
|
||||||
|
|
||||||
|
asio::any_io_executor const&
|
||||||
|
get_executor() override;
|
||||||
|
|
||||||
|
void
|
||||||
|
cancel() override;
|
||||||
|
|
||||||
|
void
|
||||||
|
close() override;
|
||||||
|
|
||||||
|
void
|
||||||
|
close(std::error_code&) override;
|
||||||
|
|
||||||
|
void
|
||||||
|
graceful_close() override;
|
||||||
|
|
||||||
|
void
|
||||||
|
async_graceful_close(graceful_close_handler_t) override;
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
read(mutable_buffer_t const&) override;
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
read(mutable_buffer_t const&, std::error_code&) override;
|
||||||
|
|
||||||
|
void
|
||||||
|
async_read(mutable_buffer_t const&, read_handler_t) override;
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
write(const_buffer_t const&) override;
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
write(const_buffers_t const&) override;
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
write(const_buffer_t const&, std::error_code&) override;
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
write(const_buffers_t const&, std::error_code&) override;
|
||||||
|
|
||||||
|
void
|
||||||
|
async_write(const_buffer_t const&, write_handler_t) override;
|
||||||
|
|
||||||
|
void
|
||||||
|
async_write(const_buffers_t const&, write_handler_t) override;
|
||||||
|
|
||||||
|
tcp_socket_t& operator=(tcp_socket_t const&) = delete;
|
||||||
|
tcp_socket_t& operator=(tcp_socket_t&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
asio::ip::tcp::socket socket_;
|
||||||
|
char throwaway_[1];
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace code::seafire::common::io
|
||||||
|
|
||||||
|
#endif
|
162
code/seafire/common/traits.hxx
Normal file
162
code/seafire/common/traits.hxx
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
#ifndef code__seafire__common__traits_hxx_
|
||||||
|
#define code__seafire__common__traits_hxx_
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <tuple>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace code::seafire::common::traits
|
||||||
|
{
|
||||||
|
|
||||||
|
// is_optional
|
||||||
|
//
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct is_optional
|
||||||
|
: std::false_type
|
||||||
|
{};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct is_optional<std::optional<T>>
|
||||||
|
: std::true_type
|
||||||
|
{};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr bool is_optional_v{is_optional<T>::value};
|
||||||
|
|
||||||
|
// remove_optional
|
||||||
|
//
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct remove_optional
|
||||||
|
{
|
||||||
|
using type = T;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct remove_optional<std::optional<T>>
|
||||||
|
{
|
||||||
|
using type = T;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using remove_optional_t = remove_optional<T>::type;
|
||||||
|
|
||||||
|
// add_optional
|
||||||
|
//
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::optional<remove_optional_t<T>>
|
||||||
|
add_optional(remove_optional<T>&& non_optional)
|
||||||
|
{
|
||||||
|
return std::optional<remove_optional<T>>{non_optional};
|
||||||
|
}
|
||||||
|
|
||||||
|
// function_traits
|
||||||
|
//
|
||||||
|
|
||||||
|
template<typename>
|
||||||
|
struct function_traits;
|
||||||
|
|
||||||
|
template<typename Ret, typename... Args>
|
||||||
|
struct function_traits<Ret(Args...)>
|
||||||
|
{
|
||||||
|
static constexpr std::size_t arity = sizeof...(Args);
|
||||||
|
using return_type = std::decay_t<Ret>;
|
||||||
|
using argument_tuple = std::tuple<std::decay_t<Args>...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Ret, typename Class, typename... Args>
|
||||||
|
struct function_traits<Ret (Class::*)(Args...)>
|
||||||
|
{
|
||||||
|
static constexpr std::size_t arity = sizeof...(Args);
|
||||||
|
using return_type = std::decay_t<Ret>;
|
||||||
|
using class_type = Class;
|
||||||
|
using argument_tuple = std::tuple<std::decay_t<Args>...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Ret, typename Class, typename... Args>
|
||||||
|
struct function_traits<Ret (Class::*)(Args...) const>
|
||||||
|
{
|
||||||
|
static constexpr std::size_t arity = sizeof...(Args);
|
||||||
|
using return_type = std::decay_t<Ret>;
|
||||||
|
using class_type = Class;
|
||||||
|
using argument_tuple = std::tuple<std::decay_t<Args>...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Ret, typename... Args>
|
||||||
|
struct function_traits<Ret(*)(Args...)>
|
||||||
|
{
|
||||||
|
static constexpr std::size_t arity = sizeof...(Args);
|
||||||
|
using return_type = std::decay_t<Ret>;
|
||||||
|
using argument_tuple = std::tuple<std::decay_t<Args>...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// return_type_t
|
||||||
|
//
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
using return_type_t = typename function_traits<F>::return_type;
|
||||||
|
|
||||||
|
// first_arg
|
||||||
|
//
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
struct first_arg
|
||||||
|
{
|
||||||
|
using function_traits = traits::function_traits<F>;
|
||||||
|
using type = std::tuple_element_t<0, typename function_traits::argument_tuple>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
using first_arg_t = first_arg<F>::type;
|
||||||
|
|
||||||
|
// function_arg_n
|
||||||
|
//
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename F,
|
||||||
|
std::size_t arg,
|
||||||
|
std::size_t arity = function_traits<F>::arity
|
||||||
|
>
|
||||||
|
struct function_arg_n;
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename F,
|
||||||
|
std::size_t arg
|
||||||
|
>
|
||||||
|
struct function_arg_n<F, arg, 0>
|
||||||
|
{
|
||||||
|
using function_traits = traits::function_traits<F>;
|
||||||
|
using type = void;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename F,
|
||||||
|
std::size_t arg
|
||||||
|
>
|
||||||
|
struct function_arg_n<F, arg, 1>
|
||||||
|
{
|
||||||
|
using function_traits = traits::function_traits<F>;
|
||||||
|
using type = std::tuple_element_t<arg, typename function_traits::argument_tuple>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename F,
|
||||||
|
std::size_t arg
|
||||||
|
>
|
||||||
|
struct function_arg_n<F, arg, 2>
|
||||||
|
{
|
||||||
|
using function_traits = traits::function_traits<F>;
|
||||||
|
using type = std::tuple_element_t<arg, typename function_traits::argument_tuple>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename F,
|
||||||
|
std::size_t arg
|
||||||
|
>
|
||||||
|
using function_arg_n_t = function_arg_n<F, arg>::type;
|
||||||
|
|
||||||
|
} // namespace code::seafire::common::traits
|
||||||
|
|
||||||
|
#endif
|
37
code/seafire/common/version.hxx.in
Normal file
37
code/seafire/common/version.hxx.in
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef code__seafire__common__version_hxx_
|
||||||
|
#define code__seafire__common__version_hxx_
|
||||||
|
|
||||||
|
// The numeric version format is AAAAABBBBBCCCCCDDDE where:
|
||||||
|
//
|
||||||
|
// AAAAA - major version number
|
||||||
|
// BBBBB - minor version number
|
||||||
|
// CCCCC - bugfix version number
|
||||||
|
// DDD - alpha / beta (DDD + 500) version number
|
||||||
|
// E - final (0) / snapshot (1)
|
||||||
|
//
|
||||||
|
// When DDDE is not 0, 1 is subtracted from AAAAABBBBBCCCCC. For example:
|
||||||
|
//
|
||||||
|
// Version AAAAABBBBBCCCCCDDDE
|
||||||
|
//
|
||||||
|
// 0.1.0 0000000001000000000
|
||||||
|
// 0.1.2 0000000001000020000
|
||||||
|
// 1.2.3 0000100002000030000
|
||||||
|
// 2.2.0-a.1 0000200001999990010
|
||||||
|
// 3.0.0-b.2 0000299999999995020
|
||||||
|
// 2.2.0-a.1.z 0000200001999990011
|
||||||
|
//
|
||||||
|
#define LIBCODE_SEAFIRE_COMMON_VERSION $libcode_seafire_common.version.project_number$ULL
|
||||||
|
#define LIBCODE_SEAFIRE_COMMON_VERSION_STR "$libcode_seafire_common.version.project$"
|
||||||
|
#define LIBCODE_SEAFIRE_COMMON_VERSION_ID "$libcode_seafire_common.version.project_id$"
|
||||||
|
#define LIBCODE_SEAFIRE_COMMON_VERSION_FULL "$libcode_seafire_common.version$"
|
||||||
|
|
||||||
|
#define LIBCODE_SEAFIRE_COMMON_VERSION_MAJOR $libcode_seafire_common.version.major$
|
||||||
|
#define LIBCODE_SEAFIRE_COMMON_VERSION_MINOR $libcode_seafire_common.version.minor$
|
||||||
|
#define LIBCODE_SEAFIRE_COMMON_VERSION_PATCH $libcode_seafire_common.version.patch$
|
||||||
|
|
||||||
|
#define LIBCODE_SEAFIRE_COMMON_PRE_RELEASE $libcode_seafire_common.version.pre_release$
|
||||||
|
|
||||||
|
#define LIBCODE_SEAFIRE_COMMON_SNAPSHOT_SN $libcode_seafire_common.version.snapshot_sn$ULL
|
||||||
|
#define LIBCODE_SEAFIRE_COMMON_SNAPSHOT_ID "$libcode_seafire_common.version.snapshot_id$"
|
||||||
|
|
||||||
|
#endif
|
12
manifest
Normal file
12
manifest
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
: 1
|
||||||
|
name: libcode-seafire-common
|
||||||
|
version: 0.1.0-a.0.z
|
||||||
|
language: c++
|
||||||
|
summary: libcode-seafire-common C++ library
|
||||||
|
license: BSD-4-Clause
|
||||||
|
description-file: README.md
|
||||||
|
url: https://helloryan.se/code/
|
||||||
|
email: ryan@helloryan.se
|
||||||
|
depends: * build2 >= 0.17.0
|
||||||
|
depends: * bpkg >= 0.17.0
|
||||||
|
depends: libasio ^1.29.0
|
7
repositories.manifest
Normal file
7
repositories.manifest
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
: 1
|
||||||
|
summary: libcode-seafire-common project repository
|
||||||
|
|
||||||
|
:
|
||||||
|
role: prerequisite
|
||||||
|
location: https://pkg.cppget.org/1/beta
|
||||||
|
trust: 70:64:FE:E4:E0:F3:60:F1:B4:51:E1:FA:12:5C:E0:B3:DB:DF:96:33:39:B9:2E:E5:C2:68:63:4C:A6:47:39:43
|
8
tests/.gitignore
vendored
Normal file
8
tests/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Test executables.
|
||||||
|
#
|
||||||
|
driver
|
||||||
|
|
||||||
|
# Testscript output directories (can be symlinks).
|
||||||
|
#
|
||||||
|
test
|
||||||
|
test-*
|
4
tests/build/.gitignore
vendored
Normal file
4
tests/build/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/config.build
|
||||||
|
/root/
|
||||||
|
/bootstrap/
|
||||||
|
build/
|
5
tests/build/bootstrap.build
Normal file
5
tests/build/bootstrap.build
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
project = # Unnamed tests subproject.
|
||||||
|
|
||||||
|
using config
|
||||||
|
using test
|
||||||
|
using dist
|
16
tests/build/root.build
Normal file
16
tests/build/root.build
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
cxx.std = latest
|
||||||
|
|
||||||
|
using cxx
|
||||||
|
|
||||||
|
hxx{*}: extension = hxx
|
||||||
|
ixx{*}: extension = ixx
|
||||||
|
txx{*}: extension = txx
|
||||||
|
cxx{*}: extension = cxx
|
||||||
|
|
||||||
|
# Every exe{} in this subproject is by default a test.
|
||||||
|
#
|
||||||
|
exe{*}: test = true
|
||||||
|
|
||||||
|
# The test target for cross-testing (running tests under Wine, etc).
|
||||||
|
#
|
||||||
|
test.target = $cxx.target
|
1
tests/buildfile
Normal file
1
tests/buildfile
Normal file
@ -0,0 +1 @@
|
|||||||
|
./: {*/ -build/}
|
Loading…
x
Reference in New Issue
Block a user