init repo.
This commit is contained in:
336
include/sled/any.h
Normal file
336
include/sled/any.h
Normal file
@@ -0,0 +1,336 @@
|
||||
/**
|
||||
* Port of boost::any for C++11 compilers.
|
||||
* See http://www.boost.org/libs/any for Documentation.
|
||||
*
|
||||
* See also:
|
||||
* + http://en.cppreference.com/w/cpp/any
|
||||
* + http://en.cppreference.com/w/cpp/experimental/any
|
||||
* + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4562.html#any
|
||||
* + https://cplusplus.github.io/LWG/lwg-active.html#2509
|
||||
*
|
||||
* Copyright Kevlin Henney, 2000, 2001, 2002. All rights reserved.
|
||||
* Copyright Claudio Fantacci, 2018. All rights reserved.
|
||||
*
|
||||
* what: variant type boost::any
|
||||
* who: contributed by Kevlin Henney,
|
||||
* with features contributed and bugs found by Antony Polukhin, Ed Brey, Mark Rodgers, Peter Dimov and James Curran,
|
||||
* with C++11 compiler port by Claudio Fantacci
|
||||
* when: July 2001, April 2013 - May 2013, September 2018
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE.md or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef ANY_H
|
||||
#define ANY_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
|
||||
namespace sled {
|
||||
|
||||
/**
|
||||
* The class any describes a type-safe container for single values of any type.
|
||||
* An object of class any stores an instance of any type that satisfies the
|
||||
* constructor requirements or is empty, and this is referred to as the state
|
||||
* of the class any object. The stored instance is called the contained object.
|
||||
* Two states are equivalent if they are either both empty or if both are not
|
||||
* empty and if the contained objects are equivalent.
|
||||
* The non-member any_cast functions provide type-safe access to the contained object.
|
||||
*/
|
||||
class any final {
|
||||
public:
|
||||
/**
|
||||
* Constructs an empty object.
|
||||
*/
|
||||
any() noexcept : content(0) {}
|
||||
|
||||
/**
|
||||
* Copies content of other into a new instance, so that any content is equivalent
|
||||
* in both type and value to those of other prior to the constructor call,
|
||||
* or empty if other is empty.
|
||||
*/
|
||||
any(const any &other) : content(other.content ? other.content->clone() : 0)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Moves content of other into a new instance, so that any content is equivalent
|
||||
* in both type and value to those of other prior to the constructor call,
|
||||
* or empty if other is empty.
|
||||
*/
|
||||
any(any &&other) noexcept : content(other.content) { other.content = 0; }
|
||||
|
||||
/**
|
||||
* Constructs an object with initial content an object of type std::decay_t<ValueType>,
|
||||
* direct-initialized from std::forward<ValueType>(value). If
|
||||
* std::is_copy_constructible<std::decay_t<ValueType>>::value is false, the program is ill-formed.
|
||||
*/
|
||||
template<typename ValueType>
|
||||
any(const ValueType &value)
|
||||
: content(new holder<typename std::remove_cv<
|
||||
typename std::decay<const ValueType>::type>::type>(value))
|
||||
{}
|
||||
|
||||
/**
|
||||
* Constructs an object with initial content an object of type std::decay_t<ValueType>,
|
||||
* direct-initialized from std::forward<ValueType>(value). If
|
||||
* std::is_copy_constructible<std::decay_t<ValueType>>::value is false, the program is ill-formed.
|
||||
*/
|
||||
template<typename ValueType>
|
||||
any(ValueType &&value,
|
||||
typename std::enable_if<!std::is_same<any &, ValueType>::value>::type
|
||||
* = 0,
|
||||
typename std::enable_if<!std::is_const<ValueType>::value>::type * = 0)
|
||||
: content(new holder<typename std::decay<ValueType>::type>(
|
||||
static_cast<ValueType &&>(value)))
|
||||
{}
|
||||
|
||||
/**
|
||||
* Destruct the object.
|
||||
*/
|
||||
~any() noexcept { delete content; }
|
||||
|
||||
/**
|
||||
* Assigns contents to the contained value.
|
||||
* Assigns by copying the state of rhs, as if by any(rhs).swap(*this).
|
||||
*
|
||||
* @param rhs object whose contained value to assign
|
||||
*/
|
||||
any &operator=(const any &rhs)
|
||||
{
|
||||
any(rhs).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns contents to the contained value.
|
||||
* Assigns by moving the state of rhs, as if by any(std::move(rhs)).swap(*this).
|
||||
* rhs is left in a valid but unspecified state after the assignment.
|
||||
*
|
||||
* @param rhs object whose contained value to assign
|
||||
*/
|
||||
any &operator=(any &&rhs) noexcept
|
||||
{
|
||||
rhs.swap(*this);
|
||||
any().swap(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns contents to the contained value.
|
||||
* Assigns the type and value of rhs, as if by any(std::forward<ValueType>(rhs)).swap(*this).
|
||||
* This overload only participates in overload resolution if std::decay_t<ValueType> is not
|
||||
* the same type as any and std::is_copy_constructible_v<std::decay_t<ValueType>> is true.
|
||||
*
|
||||
* @param rhs object whose contained value to assign
|
||||
*/
|
||||
template<class ValueType>
|
||||
any &operator=(ValueType &&rhs)
|
||||
{
|
||||
any(static_cast<ValueType &&>(rhs)).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If not empty, destroys the contained object.
|
||||
*/
|
||||
void reset() noexcept { any().swap(*this); }
|
||||
|
||||
/**
|
||||
* Swaps the content of two any objects.
|
||||
*
|
||||
* @param other object to swap with
|
||||
*/
|
||||
any &swap(any &rhs) noexcept
|
||||
{
|
||||
std::swap(content, rhs.content);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the object contains a value.
|
||||
*
|
||||
* @return true if instance contains a value, otherwise false.
|
||||
*/
|
||||
bool has_value() const noexcept { return content; }
|
||||
|
||||
/**
|
||||
* Queries the contained type.
|
||||
*
|
||||
* The typeid of the contained value if instance is non-empty, otherwise typeid(void).
|
||||
*/
|
||||
const std::type_info &type() const noexcept
|
||||
{
|
||||
return content ? content->type() : typeid(void);
|
||||
}
|
||||
|
||||
private:
|
||||
class placeholder {
|
||||
public:
|
||||
virtual ~placeholder() {}
|
||||
|
||||
public:
|
||||
virtual const std::type_info &type() const noexcept = 0;
|
||||
|
||||
virtual placeholder *clone() const = 0;
|
||||
};
|
||||
|
||||
template<typename ValueType>
|
||||
class holder : public placeholder {
|
||||
public:
|
||||
holder(const ValueType &value) : held(value) {}
|
||||
|
||||
holder(ValueType &&value) : held(static_cast<ValueType &&>(value)) {}
|
||||
|
||||
virtual const std::type_info &type() const noexcept
|
||||
{
|
||||
return typeid(ValueType);
|
||||
}
|
||||
|
||||
virtual placeholder *clone() const { return new holder(held); }
|
||||
|
||||
ValueType held;
|
||||
|
||||
private:
|
||||
holder &operator=(const holder &);
|
||||
};
|
||||
|
||||
private:
|
||||
template<typename ValueType>
|
||||
friend ValueType *any_cast(any *) noexcept;
|
||||
|
||||
placeholder *content;
|
||||
};
|
||||
|
||||
/**
|
||||
* Overloads the std::swap algorithm for std::any. Swaps the content of two any objects by calling lhs.swap(rhs).
|
||||
*
|
||||
* @param lhs objects to swap
|
||||
* @param rhs objects to swap
|
||||
*/
|
||||
inline void
|
||||
swap(any &lhs, any &rhs) noexcept
|
||||
{
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a type of object to be thrown by the value-returning forms of libanyboost::any_cast on failure.
|
||||
*/
|
||||
class bad_any_cast : public std::bad_cast {
|
||||
public:
|
||||
/**
|
||||
* Returns the explanatory string.
|
||||
*
|
||||
* Pointer to a null-terminated string with explanatory information. The pointer is guaranteed to be
|
||||
* valid at least until the exception object from which it is obtained is destroyed, or until a
|
||||
* non-const member function on the exception object is called.
|
||||
*/
|
||||
virtual const char *what() const noexcept override
|
||||
{
|
||||
return "bad any_cast";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Performs type-safe access to the contained object.
|
||||
*
|
||||
* Throws libanyboost::bad_any_cast if the typeid of the requested
|
||||
* ValueType does not match that of the contents of operand.
|
||||
*
|
||||
* @param operand target any object
|
||||
*/
|
||||
template<typename ValueType>
|
||||
ValueType *
|
||||
any_cast(any *operand) noexcept
|
||||
{
|
||||
return operand && operand->type() == typeid(ValueType) ? std::addressof(
|
||||
static_cast<any::holder<typename std::remove_cv<ValueType>::type>
|
||||
*>(operand->content)
|
||||
->held)
|
||||
: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs type-safe access to the contained object.
|
||||
*
|
||||
* Throws libanyboost::bad_any_cast if the typeid of the requested
|
||||
* ValueType does not match that of the contents of operand.
|
||||
*
|
||||
* @param operand target any object
|
||||
*/
|
||||
template<typename ValueType>
|
||||
inline const ValueType *
|
||||
any_cast(const any *operand) noexcept
|
||||
{
|
||||
return any_cast<ValueType>(const_cast<any *>(operand));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs type-safe access to the contained object.
|
||||
*
|
||||
* Throws libanyboost::bad_any_cast if the typeid of the requested
|
||||
* ValueType does not match that of the contents of operand.
|
||||
*
|
||||
* @param operand target any object
|
||||
*/
|
||||
template<typename ValueType>
|
||||
ValueType
|
||||
any_cast(any &operand)
|
||||
{
|
||||
typedef typename std::remove_reference<ValueType>::type nonref;
|
||||
|
||||
nonref *result = any_cast<nonref>(std::addressof(operand));
|
||||
if (!result) throw bad_any_cast();
|
||||
|
||||
typedef typename std::conditional<
|
||||
std::is_reference<ValueType>::value, ValueType,
|
||||
typename std::add_lvalue_reference<ValueType>::type>::type ref_type;
|
||||
|
||||
return static_cast<ref_type>(*result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs type-safe access to the contained object.
|
||||
*
|
||||
* Throws libanyboost::bad_any_cast if the typeid of the requested
|
||||
* ValueType does not match that of the contents of operand.
|
||||
*
|
||||
* @param operand target any object
|
||||
*/
|
||||
template<typename ValueType>
|
||||
inline ValueType
|
||||
any_cast(const any &operand)
|
||||
{
|
||||
typedef typename std::remove_reference<ValueType>::type nonref;
|
||||
return any_cast<const nonref &>(const_cast<any &>(operand));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs type-safe access to the contained object.
|
||||
*
|
||||
* Throws libanyboost::bad_any_cast if the typeid of the requested
|
||||
* ValueType does not match that of the contents of operand.
|
||||
*
|
||||
* @param operand target any object
|
||||
*/
|
||||
template<typename ValueType>
|
||||
inline ValueType
|
||||
any_cast(any &&operand)
|
||||
{
|
||||
static_assert(
|
||||
std::is_rvalue_reference<ValueType &&>::value
|
||||
|| std::is_const<
|
||||
typename std::remove_reference<ValueType>::type>::value,
|
||||
"any_cast shall not be used for getting nonconst references to "
|
||||
"temporary objects");
|
||||
|
||||
return any_cast<ValueType>(operand);
|
||||
}
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif /* ANY_H */
|
||||
278
include/sled/buffer.h
Normal file
278
include/sled/buffer.h
Normal file
@@ -0,0 +1,278 @@
|
||||
/**
|
||||
* @file : buffer
|
||||
* @created : Wednesday Feb 21, 2024 10:07:12 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef BUFFER_H
|
||||
#define BUFFER_H
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
#include <type_traits>
|
||||
|
||||
namespace sled {
|
||||
|
||||
namespace internal {
|
||||
template<typename T, typename U>
|
||||
struct BufferCompat {
|
||||
using RawU = typename std::remove_const<U>::type;
|
||||
|
||||
static constexpr bool value = !std::is_volatile<U>::value
|
||||
&& ((std::is_integral<T>::value && sizeof(T) == 1)
|
||||
? (std::is_integral<U>::value && sizeof(U) == 1)
|
||||
: (std::is_same<T, RawU>::value)
|
||||
|
||||
);
|
||||
};
|
||||
}// namespace internal
|
||||
|
||||
template<typename T, bool ZeroOnFree = false>
|
||||
class BufferT {
|
||||
static_assert(std::is_trivial<T>::value, "T must be a trivial type.");
|
||||
static_assert(!std::is_const<T>::value, "T may not be const");
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
using const_iterator = const T *;
|
||||
|
||||
BufferT() : size_(0), capacity_(0), data_(nullptr) { IsConsistent(); }
|
||||
|
||||
BufferT(const BufferT &) = delete;
|
||||
BufferT &operator=(const BufferT &) = delete;
|
||||
|
||||
BufferT(BufferT &&buf)
|
||||
: size_(buf.size()),
|
||||
capacity_(buf.capacity()),
|
||||
data_(std::move(buf.data_))
|
||||
{
|
||||
IsConsistent();
|
||||
}
|
||||
|
||||
explicit BufferT(size_t size) : BufferT(size, size) {}
|
||||
|
||||
BufferT(size_t size, size_t capacity)
|
||||
: size_(size),
|
||||
capacity_(std::max(size, capacity)),
|
||||
data_(capacity_ > 0 ? new T[capacity_] : nullptr)
|
||||
{
|
||||
IsConsistent();
|
||||
}
|
||||
|
||||
template<typename U,
|
||||
typename std::enable_if<internal::BufferCompat<T, U>::value>::type
|
||||
* = nullptr>
|
||||
BufferT(const U *data, size_t size) : BufferT(data, size, size)
|
||||
{}
|
||||
|
||||
template<typename U,
|
||||
typename std::enable_if<internal::BufferCompat<T, U>::value>::type
|
||||
* = nullptr>
|
||||
BufferT(U *data, size_t size, size_t capacity) : BufferT(size, capacity)
|
||||
{
|
||||
static_assert(sizeof(T) == sizeof(U), "");
|
||||
if (size > 0) { std::memcpy(data_.get(), data, size * sizeof(U)); }
|
||||
}
|
||||
|
||||
template<typename U,
|
||||
size_t N,
|
||||
typename std::enable_if<internal::BufferCompat<T, U>::value>::type
|
||||
* = nullptr>
|
||||
BufferT(U (&array)[N]) : BufferT(array, N)
|
||||
{}
|
||||
|
||||
~BufferT() {}
|
||||
|
||||
template<typename U,
|
||||
typename std::enable_if<internal::BufferCompat<T, U>::value>::type
|
||||
* = nullptr>
|
||||
const U *data() const
|
||||
{
|
||||
return reinterpret_cast<const U *>(data_.get());
|
||||
}
|
||||
|
||||
template<typename U,
|
||||
typename std::enable_if<internal::BufferCompat<T, U>::value>::type
|
||||
* = nullptr>
|
||||
U *data()
|
||||
{
|
||||
return reinterpret_cast<U *>(data_.get());
|
||||
}
|
||||
|
||||
bool empty() const { return size_ == 0; }
|
||||
|
||||
size_t size() const { return size_; }
|
||||
|
||||
size_t capacity() const { return capacity_; }
|
||||
|
||||
BufferT &operator=(BufferT &&buf)
|
||||
{
|
||||
size_ = buf.size_;
|
||||
capacity_ = buf.capacity_;
|
||||
using std::swap;
|
||||
swap(data_, buf.data_);
|
||||
buf.data_.reset();
|
||||
buf.OnMovedFrom();
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const BufferT &buf) const
|
||||
{
|
||||
if (size_ != buf.size_) { return false; }
|
||||
if (std::is_integral<T>::value) {
|
||||
return std::memcmp(data_.get(), buf.data_.get(), size_ * sizeof(T))
|
||||
== 0;
|
||||
}
|
||||
for (size_t i = 0; i < size_; i++) {
|
||||
if (data_[i] != buf.data_[i]) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const BufferT &buf) const { return !(*this == buf); }
|
||||
|
||||
T &operator[](size_t index) { return data()[index]; }
|
||||
|
||||
T operator[](size_t index) const { return data()[index]; }
|
||||
|
||||
T *begin() { return data(); }
|
||||
|
||||
T *end() { return data() + size(); }
|
||||
|
||||
const T *begin() const { return data(); }
|
||||
|
||||
const T *end() const { return data() + size(); }
|
||||
|
||||
const T *cbegin() const { return data(); }
|
||||
|
||||
const T *cend() const { return data() + size(); }
|
||||
|
||||
template<typename U,
|
||||
typename std::enable_if<internal::BufferCompat<T, U>::value>::type
|
||||
* = nullptr>
|
||||
void SetData(const U *data, size_t size)
|
||||
{
|
||||
const size_t old_size = size_;
|
||||
size_ = 0;
|
||||
AppentData(data, size);
|
||||
if (ZeroOnFree && size_ < old_size) {
|
||||
ZeroTrailingData(old_size - size_);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename U,
|
||||
size_t N,
|
||||
typename std::enable_if<internal::BufferCompat<T, U>::value>::type
|
||||
* = nullptr>
|
||||
void SetData(const U (&array)[N])
|
||||
{
|
||||
SetData(array, N);
|
||||
}
|
||||
|
||||
template<typename U,
|
||||
typename std::enable_if<internal::BufferCompat<T, U>::value>::type
|
||||
* = nullptr>
|
||||
void AppentData(const U *data, size_t size)
|
||||
{
|
||||
if (size == 0) { return; }
|
||||
|
||||
const size_t new_size = size_ + size;
|
||||
static_assert(sizeof(T) == sizeof(U), "");
|
||||
EnsureCapacityWithHeadroom(new_size, true);
|
||||
std::memcpy(data_.get() + size_, data, size * sizeof(U));
|
||||
size_ = new_size;
|
||||
}
|
||||
|
||||
template<typename U,
|
||||
size_t N,
|
||||
typename std::enable_if<internal::BufferCompat<T, U>::value>::type
|
||||
* = nullptr>
|
||||
void AppendData(const U (&array)[N])
|
||||
{
|
||||
AppendData(array, N);
|
||||
}
|
||||
|
||||
// template<typename W,
|
||||
// typename std::enable_if<
|
||||
// HasDataAndSize<const W, const T>::value>::type * = nullptr>
|
||||
// void AppendData(const W &w)
|
||||
// {
|
||||
// AppendData(w.data(), w.size());
|
||||
// }
|
||||
|
||||
template<typename U,
|
||||
typename std::enable_if<internal::BufferCompat<T, U>::value>::type
|
||||
* = nullptr>
|
||||
void AppendData(const U &item)
|
||||
{
|
||||
AppendData(&item, 1);
|
||||
}
|
||||
|
||||
void SetSize(size_t size)
|
||||
{
|
||||
const size_t old_size = size_;
|
||||
EnsureCapacityWithHeadroom(size, true);
|
||||
size_ = size;
|
||||
if (ZeroOnFree && size_ < old_size) {
|
||||
ZeroTrailingData(old_size - size_);
|
||||
}
|
||||
}
|
||||
|
||||
void EnsureCapacity(size_t capacity)
|
||||
{
|
||||
EnsureCapacityWithHeadroom(capacity, false);
|
||||
}
|
||||
|
||||
void Clear() { size_ = 0; }
|
||||
|
||||
friend void swap(BufferT &a, BufferT &b)
|
||||
{
|
||||
using std::swap;
|
||||
swap(a.size_, b.size_);
|
||||
swap(a.capacity_, b.capacity_);
|
||||
swap(a.data_, b.data_);
|
||||
}
|
||||
|
||||
private:
|
||||
void EnsureCapacityWithHeadroom(size_t capacity, bool extra_headroom)
|
||||
{
|
||||
if (capacity <= capacity_) { return; }
|
||||
|
||||
const size_t new_capacity = extra_headroom
|
||||
? std::max(capacity, capacity_ + capacity_ / 2)
|
||||
: capacity;
|
||||
std::unique_ptr<T[]> new_data(new T[new_capacity]);
|
||||
if (data_ != nullptr) {
|
||||
std::memcpy(new_data.get(), data_.get(), size_ * sizeof(T));
|
||||
}
|
||||
|
||||
data_ = std::move(new_data);
|
||||
capacity_ = new_capacity;
|
||||
}
|
||||
|
||||
void ZeroTrailingData(size_t count) {}
|
||||
|
||||
bool IsConsistent() const
|
||||
{
|
||||
return (data_ || capacity_ == 0) && capacity_ >= size_;
|
||||
}
|
||||
|
||||
void OnMovedFrom()
|
||||
{
|
||||
size_ = 0;
|
||||
capacity_ = 0;
|
||||
}
|
||||
|
||||
size_t size_;
|
||||
|
||||
size_t capacity_;
|
||||
std::unique_ptr<T[]> data_;
|
||||
};
|
||||
|
||||
using Buffer = BufferT<uint8_t>;
|
||||
template<typename T>
|
||||
using ZeroOnFreeBuffer = BufferT<T, true>;
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// BUFFER_H
|
||||
25
include/sled/byte_order.h
Normal file
25
include/sled/byte_order.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <arpa/inet.h>
|
||||
|
||||
inline uint16_t
|
||||
HostToNetwork16(uint16_t n)
|
||||
{
|
||||
return htobe16(n);
|
||||
}
|
||||
|
||||
inline uint32_t
|
||||
HostToNetwork32(uint32_t n)
|
||||
{
|
||||
return htobe32(n);
|
||||
}
|
||||
|
||||
inline uint16_t
|
||||
NetworkToHost16(uint16_t n)
|
||||
{
|
||||
return be16toh(n);
|
||||
}
|
||||
|
||||
inline uint32_t
|
||||
NetworkToHost32(uint32_t n)
|
||||
{
|
||||
return be32toh(n);
|
||||
}
|
||||
67
include/sled/cleanup.h
Normal file
67
include/sled/cleanup.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @file : cleanup
|
||||
* @created : Wednesday Feb 14, 2024 12:25:40 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef CLEANUP_H
|
||||
#define CLEANUP_H
|
||||
|
||||
#include "sled/optional.h"
|
||||
|
||||
namespace sled {
|
||||
|
||||
namespace internal {
|
||||
struct Tag {};
|
||||
|
||||
template<typename Arg, typename... Args>
|
||||
constexpr bool
|
||||
WasDeduced()
|
||||
{
|
||||
return (std::is_same<Tag, Tag>::value && (sizeof...(Args)) == 0);
|
||||
}
|
||||
}// namespace internal
|
||||
|
||||
template<typename Arg = internal::Tag,
|
||||
typename Callback = std::function<void()>>
|
||||
class Cleanup final {
|
||||
public:
|
||||
static_assert(internal::WasDeduced<Arg>(),
|
||||
"Do not specify the first template argument");
|
||||
|
||||
Cleanup(Callback callback) : callback_(std::move(callback)) {}
|
||||
|
||||
Cleanup(Cleanup &&other) = default;
|
||||
|
||||
void Cancel() && { callback_.reset(); }
|
||||
|
||||
void Invoke() &&
|
||||
{
|
||||
assert(callback_);
|
||||
(*callback_)();
|
||||
callback_.reset();
|
||||
}
|
||||
|
||||
~Cleanup()
|
||||
{
|
||||
if (callback_) {
|
||||
(*callback_)();
|
||||
callback_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
sled::optional<Callback> callback_;
|
||||
};
|
||||
|
||||
template<typename... Args, typename Callback>
|
||||
sled::Cleanup<internal::Tag, Callback>
|
||||
MakeCleanup(Callback callback)
|
||||
|
||||
{
|
||||
return {std::move(callback)};
|
||||
}
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// CLEANUP_H
|
||||
86
include/sled/log/log.h
Normal file
86
include/sled/log/log.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* @file : log
|
||||
* @created : Thursday Feb 22, 2024 14:32:32 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef LOG_H
|
||||
#define LOG_H
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace sled {
|
||||
enum class LogLevel {
|
||||
kTrace,
|
||||
kDebug,
|
||||
kInfo,
|
||||
kWarning,
|
||||
kError,
|
||||
kFatal,
|
||||
};
|
||||
|
||||
void Log(LogLevel level,
|
||||
const char *tag,
|
||||
const char *fmt,
|
||||
const char *file_name,
|
||||
int line,
|
||||
const char *func_name,
|
||||
...);
|
||||
|
||||
}// namespace sled
|
||||
|
||||
// #define _SLOG(level, tag, ...) \
|
||||
// sled::Log(level, tag, fmt, __FILE__, __FUNCTION__, __VA_ARGS__)
|
||||
|
||||
#define _SLOG(level, tag, fmt_str, ...) \
|
||||
sled::Log(level, tag, fmt::format(fmt_str, #__VA_ARGS__).c_str(), \
|
||||
__FILE__, __LINE__, __FUNCTION__)
|
||||
|
||||
#define SLOG(level, tag, fmt, ...) _SLOG(level, tag, fmt, #__VA_ARGS__)
|
||||
#define SLOG_TRACE(tag, fmt, ...) \
|
||||
SLOG(sled::LogLevel::kTrace, tag, fmt, __VA_ARGS__)
|
||||
#define SLOG_INFO(tag, fmt, ...) \
|
||||
SLOG(sled::LogLevel::kInfo, tag, fmt, __VA_ARGS__)
|
||||
#define SLOG_DEBUG(tag, fmt, ...) \
|
||||
SLOG(sled::LogLevel::kDebug, tag, fmt, __VA_ARGS__)
|
||||
#define SLOG_WARNING(tag, fmt, ...) \
|
||||
SLOG(sled::LogLevel::kWarning, tag, fmt, __VA_ARGS__)
|
||||
#define SLOG_ERROR(tag, fmt, ...) \
|
||||
SLOG(sled::LogLevel::kError, tag, fmt, __VA_ARGS__)
|
||||
#define SLOG_FATAL(tag, fmt, ...) \
|
||||
SLOG(sled::LogLevel::kFatal, tag, fmt, __VA_ARGS__)
|
||||
|
||||
#define SLOG_IF(cond, level, tag, fmt, ...) \
|
||||
do { \
|
||||
if (cond) { SLOG(level, tag, fmt, __VA_ARGS__); } \
|
||||
} while (0)
|
||||
|
||||
#define SLOG_ASSERT(cond, tag, fmt, ...) \
|
||||
do { \
|
||||
if (!(cond)) { \
|
||||
SLOG(sled::LogLevel::kFatal, __VA_ARGS__); \
|
||||
assert(cond); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define LOGV_IF(cond, tag, fmt, ...) \
|
||||
SLOG_IF(cond, sled::LogLevel::kTrace, tag, fmt, __VA_ARGS__)
|
||||
#define LOGD_IF(cond, tag, fmt, ...) \
|
||||
SLOG_IF(cond, sled::LogLevel::kDebug, tag, fmt, __VA_ARGS__)
|
||||
#define LOGI_IF(cond, tag, fmt, ...) \
|
||||
SLOG_IF(cond, sled::LogLevel::kInfo, tag, fmt, __VA_ARGS__)
|
||||
#define LOGW_IF(cond, tag, fmt, ...) \
|
||||
SLOG_IF(cond, sled::LogLevel::kWarning, tag, fmt, __VA_ARGS__)
|
||||
#define LOGE_IF(cond, tag, fmt, ...) \
|
||||
SLOG_IF(cond, sled::LogLevel::kError, tag, fmt, __VA_ARGS__)
|
||||
#define LOGF_IF(cond, tag, fmt, ...) \
|
||||
SLOG_IF(cond, sled::LogLevel::kFatal, tag, fmt, __VA_ARGS__)
|
||||
|
||||
#define LOGV(tag, fmt, ...) SLOG(sled::LogLevel::kTrace, tag, fmt, #__VA_ARGS__)
|
||||
#define LOGD(tag, fmt, ...) SLOG(sled::LogLevel::kDebug, tag, fmt, #__VA_ARGS__)
|
||||
#define LOGI(tag, fmt, ...) SLOG(sled::LogLevel::kInfo, tag, fmt, #__VA_ARGS__)
|
||||
#define LOGW(tag, fmt, ...) \
|
||||
SLOG(sled::LogLevel::kWarning, tag, fmt, #__VA_ARGS__)
|
||||
#define LOGE(tag, fmt, ...) SLOG(sled::LogLevel::kError, tag, fmt, #__VA_ARGS__)
|
||||
#define LOGF(tag, fmt, ...) SLOG(sled::LogLevel::kFatal, tag, fmt, #__VA_ARGS__)
|
||||
|
||||
#endif// LOG_H
|
||||
76
include/sled/make_ref_counted.h
Normal file
76
include/sled/make_ref_counted.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* @file : make_ref_counted
|
||||
* @created : Thursday Feb 01, 2024 16:18:30 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef MAKE_REF_COUNTED_H
|
||||
#define MAKE_REF_COUNTED_H
|
||||
|
||||
#include "sled/ref_count.h"
|
||||
#include "sled/ref_counted_object.h"
|
||||
#include "sled/scoped_refptr.h"
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace sled {
|
||||
|
||||
namespace internal {
|
||||
template<typename T>
|
||||
class HasAddRefAndRelease {
|
||||
private:
|
||||
template<typename TClass,
|
||||
decltype(std::declval<TClass>().AddRef()) * = nullptr,
|
||||
decltype(std::declval<TClass>().Release()) * = nullptr>
|
||||
static int Test(int);
|
||||
|
||||
template<typename>
|
||||
static char Test(...);
|
||||
|
||||
public:
|
||||
static constexpr bool value =
|
||||
std::is_same<decltype(Test<T>(0)), int>::value;
|
||||
};
|
||||
}// namespace internal
|
||||
|
||||
template<
|
||||
typename T,
|
||||
typename... Args,
|
||||
typename std::enable_if<std::is_convertible<T *, RefCountInterface *>::value
|
||||
&& std::is_abstract<T>::value,
|
||||
T>::type * = nullptr>
|
||||
scoped_refptr<T>
|
||||
make_ref_counted(Args &&...args)
|
||||
{
|
||||
|
||||
return scoped_refptr<T>(
|
||||
new RefCountedObject<T>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
typename... Args,
|
||||
typename std::enable_if<
|
||||
!std::is_convertible<T *, RefCountInterface *>::value
|
||||
&& internal::HasAddRefAndRelease<T>::value,
|
||||
T>::type * = nullptr>
|
||||
scoped_refptr<T>
|
||||
make_ref_counted(Args &&...args)
|
||||
{
|
||||
return scoped_refptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
typename... Args,
|
||||
typename std::enable_if<
|
||||
!std::is_convertible<T *, RefCountInterface *>::value
|
||||
&& !internal::HasAddRefAndRelease<T>::value>::type * = nullptr>
|
||||
scoped_refptr<FinalRefCountedObject<T>>
|
||||
make_ref_counted(Args &&...args)
|
||||
{
|
||||
return scoped_refptr<FinalRefCountedObject<T>>(
|
||||
new FinalRefCountedObject<T>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// MAKE_REF_COUNTED_H
|
||||
44
include/sled/network/async_resolver.h
Normal file
44
include/sled/network/async_resolver.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* @file : async_resolver
|
||||
* @created : Monday Feb 19, 2024 18:53:03 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef ASYNC_RESOLVER_H
|
||||
#define ASYNC_RESOLVER_H
|
||||
|
||||
#include "sled/network/async_resolver_interface.h"
|
||||
#include "sled/scoped_refptr.h"
|
||||
#include <vector>
|
||||
|
||||
namespace sled {
|
||||
|
||||
class AsyncResolver : public AsyncResolverInterface {
|
||||
public:
|
||||
AsyncResolver();
|
||||
~AsyncResolver() override;
|
||||
|
||||
void Start(const SocketAddress &addr) override;
|
||||
void Start(const SocketAddress &addr, int family) override;
|
||||
bool GetResolvedAddress(int family, SocketAddress *addr) const override;
|
||||
int GetError() const override;
|
||||
void Destroy(bool wait) override;
|
||||
|
||||
const std::vector<IPAddress> &addresses() const;
|
||||
|
||||
private:
|
||||
struct State;
|
||||
void ResolveDone(std::vector<IPAddress> addresses, int error);
|
||||
void MaybeSelfDestruct();
|
||||
SocketAddress addr_;
|
||||
std::vector<IPAddress> addresses_;
|
||||
int error_;
|
||||
bool recursion_check_ =
|
||||
false;// Protects against SignalDone calling into Destroy.
|
||||
bool destroy_called_ = false;
|
||||
scoped_refptr<State> state_;
|
||||
};
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// ASYNC_RESOLVER_H
|
||||
38
include/sled/network/async_resolver_interface.h
Normal file
38
include/sled/network/async_resolver_interface.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @file : async_resolver_interface
|
||||
* @created : Monday Feb 19, 2024 18:50:17 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef ASYNC_RESOLVER_INTERFACE_H
|
||||
#define ASYNC_RESOLVER_INTERFACE_H
|
||||
|
||||
#include "sled/network/socket_address.h"
|
||||
#include "sled/sigslot.h"
|
||||
|
||||
namespace sled {
|
||||
|
||||
class AsyncResolverInterface {
|
||||
public:
|
||||
AsyncResolverInterface() = default;
|
||||
virtual ~AsyncResolverInterface() = default;
|
||||
|
||||
virtual void Start(const SocketAddress &addr) = 0;
|
||||
virtual void Start(const SocketAddress &addr, int family) = 0;
|
||||
virtual bool GetResolvedAddress(int family, SocketAddress *addr) const = 0;
|
||||
virtual int GetError() const = 0;
|
||||
virtual void Destroy(bool wait) = 0;
|
||||
|
||||
inline SocketAddress address() const
|
||||
{
|
||||
SocketAddress addr;
|
||||
GetResolvedAddress(AF_INET, &addr);
|
||||
return addr;
|
||||
}
|
||||
|
||||
sigslot::signal1<AsyncResolverInterface *> SignalDone;
|
||||
};
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// ASYNC_RESOLVER_INTERFACE_H
|
||||
98
include/sled/network/ip_address.h
Normal file
98
include/sled/network/ip_address.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* @file : ip_address
|
||||
* @created : Monday Feb 19, 2024 14:38:40 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef IP_ADDRESS_H
|
||||
#define IP_ADDRESS_H
|
||||
|
||||
#include "sled/byte_order.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <sys/socket.h>
|
||||
|
||||
namespace sled {
|
||||
|
||||
class IPAddress {
|
||||
public:
|
||||
IPAddress() : family_(AF_UNSPEC) { ::memset(&u_, 0, sizeof(u_)); }
|
||||
|
||||
explicit IPAddress(const in_addr &ip4) : family_(AF_INET)
|
||||
{
|
||||
::memset(&u_, 0, sizeof(u_));
|
||||
u_.ip4 = ip4;
|
||||
}
|
||||
|
||||
explicit IPAddress(const in6_addr &ip6) : family_(AF_INET6)
|
||||
{
|
||||
u_.ip6 = ip6;
|
||||
}
|
||||
|
||||
explicit IPAddress(uint32_t ip_in_host_byte_order) : family_(AF_INET)
|
||||
{
|
||||
::memset(&u_, 0, sizeof(u_));
|
||||
u_.ip4.s_addr = HostToNetwork32(ip_in_host_byte_order);
|
||||
}
|
||||
|
||||
IPAddress(const IPAddress &other) : family_(other.family_)
|
||||
{
|
||||
::memcpy(&u_, &other.u_, sizeof(u_));
|
||||
}
|
||||
|
||||
virtual ~IPAddress() = default;
|
||||
|
||||
const IPAddress &operator=(const IPAddress &other)
|
||||
{
|
||||
family_ = other.family_;
|
||||
::memcpy(&u_, &other.u_, sizeof(u_));
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const IPAddress &other) const;
|
||||
bool operator!=(const IPAddress &other) const;
|
||||
bool operator<(const IPAddress &other) const;
|
||||
bool operator>(const IPAddress &other) const;
|
||||
|
||||
int family() const { return family_; };
|
||||
|
||||
in_addr ipv4_address() const;
|
||||
in6_addr ipv6_address() const;
|
||||
size_t Size() const;
|
||||
std::string ToString() const;
|
||||
IPAddress Normailzed() const;
|
||||
IPAddress AsIPv6Address() const;
|
||||
uint32_t v4AddressAsHostOrderInteger() const;
|
||||
int overhead() const;
|
||||
bool IsNil() const;
|
||||
|
||||
private:
|
||||
int family_;
|
||||
|
||||
union {
|
||||
in_addr ip4;
|
||||
in6_addr ip6;
|
||||
} u_;
|
||||
};
|
||||
|
||||
bool IPFromAddrInfo(struct addrinfo *info, IPAddress *out);
|
||||
|
||||
bool IPFromString(const std::string &str, IPAddress *out);
|
||||
|
||||
bool IPIsAny(const IPAddress &ip);
|
||||
bool IPIsLoopback(const IPAddress &ip);
|
||||
bool IPIsLinkLocal(const IPAddress &ip);
|
||||
// like "192.168.111.222"
|
||||
bool IPIsPrivateNetwork(const IPAddress &ip);
|
||||
// like "100.72.16.122"
|
||||
bool IPIsSharedNetwork(const IPAddress &ip);
|
||||
|
||||
bool IPIsPrivate(const IPAddress &ip);
|
||||
bool IPIsUnspec(const IPAddress &ip);
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// IP_ADDRESS_H
|
||||
29
include/sled/network/null_socket_server.h
Normal file
29
include/sled/network/null_socket_server.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* @file : null_socket_server
|
||||
* @created : Wednesday Feb 14, 2024 13:36:48 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef NULL_SOCKET_SERVER_H
|
||||
#define NULL_SOCKET_SERVER_H
|
||||
|
||||
#include "sled/network/socket_server.h"
|
||||
#include "sled/synchronization/event.h"
|
||||
|
||||
namespace sled {
|
||||
|
||||
class NullSocketServer : public SocketServer {
|
||||
public:
|
||||
NullSocketServer();
|
||||
~NullSocketServer() override;
|
||||
bool Wait(TimeDelta max_wait_duration, bool process_io) override;
|
||||
void WakeUp() override;
|
||||
|
||||
Socket *CreateSocket(int family, int type) override;
|
||||
private:
|
||||
Event event_;
|
||||
};
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// NULL_SOCKET_SERVER_H
|
||||
166
include/sled/network/physical_socket_server.h
Normal file
166
include/sled/network/physical_socket_server.h
Normal file
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* @file : physical_socket_server
|
||||
* @created : Monday Feb 19, 2024 11:35:13 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef PHYSICAL_SOCKET_SERVER_H
|
||||
#define PHYSICAL_SOCKET_SERVER_H
|
||||
|
||||
#include "sled/network/async_resolver.h"
|
||||
#include "sled/sigslot.h"
|
||||
#include "sled/synchronization/mutex.h"
|
||||
#include "socket.h"
|
||||
#include "socket_server.h"
|
||||
#include <unordered_map>
|
||||
|
||||
typedef int SOCKET;
|
||||
|
||||
namespace sled {
|
||||
|
||||
enum DispatcherEvent {
|
||||
DE_READ = 0x0001,
|
||||
DE_WRITE = 0x0002,
|
||||
DE_CONNECT = 0x0004,
|
||||
DE_CLOSE = 0x0008,
|
||||
DE_ACCEPT = 0x0010,
|
||||
};
|
||||
|
||||
class Signaler;
|
||||
|
||||
class Dispatcher {
|
||||
public:
|
||||
virtual ~Dispatcher() = default;
|
||||
virtual uint32_t GetRequestedEvents() = 0;
|
||||
virtual void OnEvent(uint32_t ff, int err) = 0;
|
||||
|
||||
virtual int GetDescriptor() = 0;
|
||||
virtual bool IsDescriptorClosed() = 0;
|
||||
};
|
||||
|
||||
class PhysicalSocketServer : public SocketServer {
|
||||
public:
|
||||
PhysicalSocketServer();
|
||||
~PhysicalSocketServer() override;
|
||||
Socket *CreateSocket(int family, int type) override;
|
||||
virtual Socket *WrapSocket(SOCKET s);
|
||||
bool Wait(TimeDelta max_wait_duration, bool process_io) override;
|
||||
void WakeUp() override;
|
||||
|
||||
void Add(Dispatcher *dispatcher);
|
||||
void Remove(Dispatcher *dispatcher);
|
||||
void Update(Dispatcher *dispatcher);
|
||||
|
||||
private:
|
||||
static const int kForeverMs = -1;
|
||||
static int ToCmsWait(TimeDelta max_wait_duration);
|
||||
|
||||
bool WaitSelect(int cmsWait, bool process_io);
|
||||
|
||||
uint64_t next_dispatcher_key_ = 0;
|
||||
std::unordered_map<uint64_t, Dispatcher *> dispatcher_by_key_;
|
||||
std::unordered_map<Dispatcher *, uint64_t> key_by_dispatcher_;
|
||||
std::vector<uint64_t> current_dispatcher_keys_;
|
||||
Signaler *signal_wakeup_;
|
||||
// Mutex lock_;
|
||||
RecursiveMutex lock_;
|
||||
|
||||
bool fWait_;
|
||||
bool waiting_ = false;
|
||||
};
|
||||
|
||||
class PhysicalSocket : public Socket, public sigslot::has_slots<> {
|
||||
public:
|
||||
PhysicalSocket(PhysicalSocketServer *ss, SOCKET s = INVALID_SOCKET);
|
||||
~PhysicalSocket() override;
|
||||
virtual bool Create(int family, int type);
|
||||
SocketAddress GetLocalAddress() const override;
|
||||
SocketAddress GetRemoteAddress() const override;
|
||||
int Bind(const SocketAddress &bind_addr) override;
|
||||
int Connect(const SocketAddress &addr) override;
|
||||
|
||||
int GetError() const override;
|
||||
void SetError(int error) override;
|
||||
|
||||
ConnState GetState() const override { return state_; };
|
||||
|
||||
int GetOption(Option opt, int *value) override;
|
||||
int SetOption(Option opt, int value) override;
|
||||
|
||||
int Send(const void *pv, size_t cb) override;
|
||||
int SendTo(const void *pv, size_t cb, const SocketAddress &addr) override;
|
||||
int Recv(void *pv, size_t cb, int64_t *timestamp) override;
|
||||
int RecvFrom(void *pv,
|
||||
size_t cb,
|
||||
SocketAddress *paddr,
|
||||
int64_t *timestamp) override;
|
||||
int Listen(int backlog) override;
|
||||
Socket *Accept(SocketAddress *paddr) override;
|
||||
|
||||
int Close() override;
|
||||
|
||||
SocketServer *socketserver() { return ss_; }
|
||||
|
||||
SOCKET GetSocketFD() const { return s_; }
|
||||
|
||||
protected:
|
||||
int DoConnect(const SocketAddress &addr);
|
||||
virtual SOCKET DoAccept(SOCKET socket, sockaddr *addr, socklen_t *addrlen);
|
||||
virtual int DoSend(SOCKET socket, const char *buf, int len, int flags);
|
||||
virtual int DoSendTo(SOCKET socket,
|
||||
const char *buf,
|
||||
int len,
|
||||
int flags,
|
||||
const struct sockaddr *dest_addr,
|
||||
socklen_t addrlen);
|
||||
int DoReadFromSocket(void *buffer,
|
||||
size_t length,
|
||||
SocketAddress *out_addr,
|
||||
int64_t *timestamp);
|
||||
|
||||
void OnResolveResult(AsyncResolverInterface *resolver);
|
||||
void UpdateLastError();
|
||||
|
||||
uint8_t enabled_events() const { return enabled_events_; }
|
||||
|
||||
virtual void SetEnabledEvents(uint8_t events);
|
||||
virtual void EnableEvents(uint8_t events);
|
||||
virtual void DisableEvents(uint8_t events);
|
||||
int TranslateOption(Option opt, int *slevel, int *sopt);
|
||||
|
||||
PhysicalSocketServer *ss_;
|
||||
SOCKET s_;
|
||||
bool udp_;
|
||||
int family_ = 0;
|
||||
mutable Mutex mutex_;
|
||||
int error_;
|
||||
ConnState state_;
|
||||
AsyncResolver *resolver_;
|
||||
|
||||
private:
|
||||
uint8_t enabled_events_ = 0;
|
||||
};
|
||||
|
||||
class SocketDispatcher : public Dispatcher, public PhysicalSocket {
|
||||
public:
|
||||
explicit SocketDispatcher(PhysicalSocketServer *ss);
|
||||
SocketDispatcher(SOCKET s, PhysicalSocketServer *ss);
|
||||
~SocketDispatcher() override;
|
||||
|
||||
bool Initialize();
|
||||
|
||||
virtual bool Create(int type);
|
||||
bool Create(int family, int type) override;
|
||||
|
||||
int GetDescriptor() override;
|
||||
bool IsDescriptorClosed() override;
|
||||
|
||||
uint32_t GetRequestedEvents() override;
|
||||
void OnEvent(uint32_t ff, int err) override;
|
||||
|
||||
int Close() override;
|
||||
};
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// PHYSICAL_SOCKET_SERVER_H
|
||||
78
include/sled/network/socket.h
Normal file
78
include/sled/network/socket.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* @file : socket
|
||||
* @created : Monday Feb 12, 2024 11:37:32 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef SOCKET_H
|
||||
#define SOCKET_H
|
||||
|
||||
#include "sled/network/socket_address.h"
|
||||
#include "sled/sigslot.h"
|
||||
#include <cerrno>
|
||||
|
||||
#define INVALID_SOCKET (-1)
|
||||
#define SOCKET_ERROR (-1)
|
||||
|
||||
namespace sled {
|
||||
|
||||
inline bool
|
||||
IsBlockingError(int e)
|
||||
{
|
||||
return (e == EWOULDBLOCK || e == EAGAIN || e == EINPROGRESS);
|
||||
}
|
||||
|
||||
class Socket {
|
||||
public:
|
||||
virtual ~Socket() = default;
|
||||
|
||||
Socket(const Socket &) = delete;
|
||||
Socket &operator=(const Socket &) = delete;
|
||||
|
||||
virtual SocketAddress GetLocalAddress() const = 0;
|
||||
virtual SocketAddress GetRemoteAddress() const = 0;
|
||||
|
||||
virtual int Bind(const SocketAddress &addr) = 0;
|
||||
virtual int Connect(const SocketAddress &addr) = 0;
|
||||
virtual int Send(const void *pv, size_t cb) = 0;
|
||||
virtual int
|
||||
SendTo(const void *pv, size_t cb, const SocketAddress &addr) = 0;
|
||||
virtual int Recv(void *pv, size_t cb, int64_t *timestamp) = 0;
|
||||
virtual int
|
||||
RecvFrom(void *pv, size_t cb, SocketAddress *paddr, int64_t *timestamp) = 0;
|
||||
virtual int Listen(int backlog) = 0;
|
||||
virtual Socket *Accept(SocketAddress *paddr) = 0;
|
||||
virtual int Close() = 0;
|
||||
virtual int GetError() const = 0;
|
||||
virtual void SetError(int error) = 0;
|
||||
|
||||
inline bool IsBlocking() const { return IsBlockingError(GetError()); }
|
||||
|
||||
enum ConnState { CS_CLOSED, CS_CONNECTING, CS_CONNECTED };
|
||||
|
||||
virtual ConnState GetState() const = 0;
|
||||
|
||||
enum Option {
|
||||
OPT_DONTFRAGMENT,
|
||||
OPT_RCVBUF, // receive buffer size
|
||||
OPT_SNDBUF, // send buffer size
|
||||
OPT_NODELAY, // whether Nagle algorithm is enabled
|
||||
OPT_IPV6_V6ONLY,// Whether the socket is IPv6 only
|
||||
OPT_DSCP, // DSCP code
|
||||
OPT_RTP_SENDTIME_EXTN_ID,
|
||||
};
|
||||
|
||||
virtual int GetOption(Option opt, int *value) = 0;
|
||||
virtual int SetOption(Option opt, int value) = 0;
|
||||
sigslot::signal1<Socket *, sigslot::multi_threaded_local> SignalReadEvent;
|
||||
sigslot::signal1<Socket *, sigslot::multi_threaded_local> SignalWriteEvent;
|
||||
sigslot::signal1<Socket *> SignalConnectEvent;
|
||||
sigslot::signal2<Socket *, int> SignalCloseEvent;
|
||||
|
||||
protected:
|
||||
Socket() = default;
|
||||
};
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// SOCKET_H
|
||||
71
include/sled/network/socket_address.h
Normal file
71
include/sled/network/socket_address.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* @file : socket_address
|
||||
* @created : Monday Feb 19, 2024 14:36:57 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef SOCKET_ADDRESS_H
|
||||
#define SOCKET_ADDRESS_H
|
||||
|
||||
#include "sled/network/ip_address.h"
|
||||
|
||||
namespace sled {
|
||||
|
||||
class SocketAddress {
|
||||
public:
|
||||
SocketAddress();
|
||||
SocketAddress(const std::string &hostname, int port);
|
||||
SocketAddress(uint32_t ip_as_host_order_integer, int port);
|
||||
SocketAddress(const IPAddress &ip, int port);
|
||||
SocketAddress(const SocketAddress &addr);
|
||||
|
||||
// Resets to the nil address
|
||||
void Clear();
|
||||
// empty hostname, any IP, null port
|
||||
bool IsNil() const;
|
||||
// Returns true if ip and port are set
|
||||
bool IsComplete() const;
|
||||
SocketAddress &operator=(const SocketAddress &addr);
|
||||
|
||||
// set ip
|
||||
void SetIP(uint32_t ip_as_host_order_integer);
|
||||
void SetIP(const IPAddress &ip);
|
||||
void SetIP(const std::string &hostname);
|
||||
void SetResolvedIP(uint32_t ip_as_host_order_integer);
|
||||
void SetResolvedIP(const IPAddress &ip);
|
||||
void SetPort(int port);
|
||||
|
||||
const std::string &hostname() const { return hostname_; }
|
||||
|
||||
uint32_t ip() const;
|
||||
|
||||
int family() const { return ip_.family(); }
|
||||
|
||||
const IPAddress &ipaddr() const;
|
||||
uint16_t port() const;
|
||||
|
||||
int scope_id() const { return scope_id_; }
|
||||
|
||||
void SetScopeID(int id) { scope_id_ = id; }
|
||||
|
||||
bool IsAnyIP() const;
|
||||
bool IsLoopbackIP() const;
|
||||
bool IsPrivateIP() const;
|
||||
bool IsUnresolvedIP() const;
|
||||
size_t ToSockAddrStorage(sockaddr_storage *saddr) const;
|
||||
void ToSockAddr(sockaddr_in *saddr) const;
|
||||
bool FromSockAddr(const sockaddr_in &saddr);
|
||||
|
||||
private:
|
||||
std::string hostname_;
|
||||
IPAddress ip_;
|
||||
uint16_t port_;
|
||||
int scope_id_;
|
||||
bool literal_;
|
||||
};
|
||||
|
||||
bool SocketAddressFromSockAddrStorage(const sockaddr_storage &saddr,
|
||||
SocketAddress *out);
|
||||
}// namespace sled
|
||||
|
||||
#endif// SOCKET_ADDRESS_H
|
||||
22
include/sled/network/socket_factory.h
Normal file
22
include/sled/network/socket_factory.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @file : socket_factory
|
||||
* @created : Monday Feb 12, 2024 11:36:52 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef SOCKET_FACTORY_H
|
||||
#define SOCKET_FACTORY_H
|
||||
|
||||
#include "sled/network/socket.h"
|
||||
|
||||
namespace sled {
|
||||
|
||||
class SocketFactory {
|
||||
public:
|
||||
virtual ~SocketFactory() = default;
|
||||
virtual Socket *CreateSocket(int family, int type) = 0;
|
||||
};
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// SOCKET_FACTORY_H
|
||||
33
include/sled/network/socket_server.h
Normal file
33
include/sled/network/socket_server.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @file : socket_server
|
||||
* @created : Monday Feb 12, 2024 11:36:32 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef SOCKET_SERVER_H
|
||||
#define SOCKET_SERVER_H
|
||||
|
||||
#include "sled/network/socket_factory.h"
|
||||
#include "sled/units/time_delta.h"
|
||||
#include <memory>
|
||||
|
||||
namespace sled {
|
||||
|
||||
class Thread;
|
||||
|
||||
class SocketServer : public SocketFactory {
|
||||
public:
|
||||
static constexpr TimeDelta kForever = TimeDelta::PlusInfinity();
|
||||
|
||||
virtual void SetMessageQueue(Thread *queue) {}
|
||||
|
||||
virtual bool Wait(TimeDelta max_wait_duration, bool process_io) = 0;
|
||||
|
||||
virtual void WakeUp() = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<sled::SocketServer> CreateDefaultSocketServer();
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// SOCKET_SERVER_H
|
||||
46
include/sled/numerics/divide_round.h
Normal file
46
include/sled/numerics/divide_round.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* @file : divide_round
|
||||
* @created : Saturday Feb 03, 2024 09:53:08 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef DIVIDE_ROUND_H
|
||||
#define DIVIDE_ROUND_H
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace sled {
|
||||
template<typename Dividend, typename Divisor>
|
||||
inline auto
|
||||
DivideRoundUp(Dividend dividend, Divisor divisor) -> decltype(Dividend() / Divisor())
|
||||
{
|
||||
static_assert(std::is_integral<Dividend>(), "");
|
||||
static_assert(std::is_integral<Divisor>(), "");
|
||||
auto quotient = dividend / divisor;
|
||||
auto remainder = dividend % divisor;
|
||||
return quotient + (remainder > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
template<typename Dividend, typename Divisor>
|
||||
inline auto
|
||||
DivideRoundToNearst(Dividend dividend, Divisor divisor) -> decltype(Dividend() / Divisor())
|
||||
{
|
||||
static_assert(std::is_integral<Dividend>(), "");
|
||||
static_assert(std::is_integral<Divisor>(), "");
|
||||
if (dividend < Dividend(0)) {
|
||||
auto half_of_divisor = divisor / 2;
|
||||
auto quotient = dividend / divisor;
|
||||
auto remainder = dividend % divisor;
|
||||
if (half_of_divisor < -remainder) { --quotient; }
|
||||
return quotient;
|
||||
}
|
||||
auto half_of_divisor = (divisor - 1) / 2;
|
||||
auto quotient = dividend / divisor;
|
||||
auto remainder = dividend % divisor;
|
||||
if (half_of_divisor < remainder) { ++quotient; }
|
||||
return quotient;
|
||||
}
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// DIVIDE_ROUND_H
|
||||
97
include/sled/operations_chain.h
Normal file
97
include/sled/operations_chain.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* @file : operations_chain
|
||||
* @created : Saturday Feb 03, 2024 12:40:36 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef OPERATIONS_CHAIN_H
|
||||
#define OPERATIONS_CHAIN_H
|
||||
|
||||
#include "sled/optional.h"
|
||||
#include "sled/ref_counted_base.h"
|
||||
#include "sled/scoped_refptr.h"
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
namespace sled {
|
||||
|
||||
namespace internal {
|
||||
class Operation {
|
||||
public:
|
||||
virtual ~Operation() {}
|
||||
|
||||
virtual void Run() = 0;
|
||||
};
|
||||
|
||||
template<typename FunctorT>
|
||||
class OperationWithFunctor final : public Operation {
|
||||
public:
|
||||
OperationWithFunctor(FunctorT &&functor, std::function<void()> callback)
|
||||
: functor_(std::forward<FunctorT>(functor)),
|
||||
callback_(std::move(callback))
|
||||
{}
|
||||
|
||||
~OperationWithFunctor() override {}
|
||||
|
||||
void Run() override
|
||||
{
|
||||
has_run_ = true;
|
||||
auto functor = std::move(functor_);
|
||||
functor(std::move(callback_));
|
||||
}
|
||||
|
||||
private:
|
||||
typename std::remove_reference<FunctorT>::type functor_;
|
||||
std::function<void()> callback_;
|
||||
bool has_run_ = false;
|
||||
};
|
||||
}// namespace internal
|
||||
|
||||
class OperationsChain final : public RefCountedNonVirtual<OperationsChain> {
|
||||
public:
|
||||
static scoped_refptr<OperationsChain> Create();
|
||||
~OperationsChain();
|
||||
OperationsChain(const OperationsChain &) = delete;
|
||||
OperationsChain operator=(const OperationsChain &) = delete;
|
||||
|
||||
void SetOnChainEmptyCallback(std::function<void()> on_chain_empty_callback);
|
||||
bool IsEmpty() const;
|
||||
|
||||
template<typename FunctorT>
|
||||
void ChainOperation(FunctorT &&functor)
|
||||
{
|
||||
auto wrapper = new internal::OperationWithFunctor<FunctorT>(std::forward<FunctorT>(functor), CreateOpeartionsChainCallback());
|
||||
chained_operations_.push(std::unique_ptr<internal::OperationWithFunctor<FunctorT>>(wrapper));
|
||||
|
||||
if (chained_operations_.size() == 1) { chained_operations_.front()->Run(); }
|
||||
}
|
||||
|
||||
private:
|
||||
friend class CallbackHandle;
|
||||
|
||||
class CallbackHandle final : public RefCountedNonVirtual<CallbackHandle> {
|
||||
public:
|
||||
explicit CallbackHandle(scoped_refptr<OperationsChain> operations_chain);
|
||||
~CallbackHandle();
|
||||
|
||||
CallbackHandle(const CallbackHandle &) = delete;
|
||||
CallbackHandle &operator=(const CallbackHandle &) = delete;
|
||||
void OnOperationComplete();
|
||||
|
||||
private:
|
||||
scoped_refptr<OperationsChain> operations_chain_;
|
||||
bool has_run_;
|
||||
};
|
||||
|
||||
OperationsChain();
|
||||
std::function<void()> CreateOpeartionsChainCallback();
|
||||
void OnOperationComplete();
|
||||
|
||||
std::queue<std::unique_ptr<internal::Operation>> chained_operations_;
|
||||
sled::optional<std::function<void()>> on_chain_empty_callback_;
|
||||
};
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// OPERATIONS_CHAIN_H
|
||||
2499
include/sled/optional.h
Normal file
2499
include/sled/optional.h
Normal file
File diff suppressed because it is too large
Load Diff
59
include/sled/random.h
Normal file
59
include/sled/random.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* @file : random
|
||||
* @created : Saturday Feb 03, 2024 11:47:02 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef RANDOM_H
|
||||
#define RANDOM_H
|
||||
|
||||
#include <limits>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace sled {
|
||||
|
||||
class Random {
|
||||
public:
|
||||
explicit Random(uint64_t seed);
|
||||
Random() = delete;
|
||||
Random(const Random &) = delete;
|
||||
Random &operator=(const Random &) = delete;
|
||||
|
||||
template<typename T>
|
||||
T Rand() const
|
||||
{
|
||||
static_assert(
|
||||
std::numeric_limits<T>::is_integer && std::numeric_limits<T>::radix == 2
|
||||
&& std::numeric_limits<T>::digits <= 32,
|
||||
"Rand is only supported for built-in integer types that are 32 bits or smaller");
|
||||
return static_cast<T>(NextOutput());
|
||||
}
|
||||
|
||||
uint32_t Rand(uint32_t t) const;
|
||||
uint32_t Rand(uint32_t low, uint32_t high) const;
|
||||
int32_t Rand(int32_t low, int32_t high) const;
|
||||
double Gaussian(double mean, double standard_deviation) const;
|
||||
double Exponential(double lambda) const;
|
||||
|
||||
private:
|
||||
inline uint64_t NextOutput() const
|
||||
{
|
||||
state_ ^= state_ >> 12;
|
||||
state_ ^= state_ << 25;
|
||||
state_ ^= state_ >> 27;
|
||||
return state_ * 2685821657736338717ull;
|
||||
}
|
||||
|
||||
mutable uint64_t state_;
|
||||
};
|
||||
|
||||
template<>
|
||||
float Random::Rand<float>() const;
|
||||
template<>
|
||||
double Random::Rand<double>() const;
|
||||
template<>
|
||||
bool Random::Rand<bool>() const;
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// RANDOM_H
|
||||
25
include/sled/ref_count.h
Normal file
25
include/sled/ref_count.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @file : ref_count
|
||||
* @created : Thursday Feb 01, 2024 15:59:19 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef REF_COUNT_H
|
||||
#define REF_COUNT_H
|
||||
|
||||
namespace sled {
|
||||
|
||||
enum class RefCountReleaseStatus { kDroppedLastRef, kOtherRefsRemained };
|
||||
|
||||
class RefCountInterface {
|
||||
public:
|
||||
virtual void AddRef() const = 0;
|
||||
virtual RefCountReleaseStatus Release() const = 0;
|
||||
|
||||
protected:
|
||||
virtual ~RefCountInterface() = default;
|
||||
};
|
||||
|
||||
} // namespace sled
|
||||
|
||||
#endif // REF_COUNT_H
|
||||
71
include/sled/ref_counted_base.h
Normal file
71
include/sled/ref_counted_base.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* @file : ref_counted_base
|
||||
* @created : Saturday Feb 03, 2024 12:46:20 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef REF_COUNTED_BASE_H
|
||||
#define REF_COUNTED_BASE_H
|
||||
|
||||
#include "sled/ref_count.h"
|
||||
#include "sled/ref_counter.h"
|
||||
#include <type_traits>
|
||||
|
||||
namespace sled {
|
||||
|
||||
class RefCountedBase {
|
||||
public:
|
||||
RefCountedBase() = default;
|
||||
RefCountedBase(const RefCountedBase &) = delete;
|
||||
RefCountedBase &operator=(const RefCountedBase &) = delete;
|
||||
|
||||
void AddRef() const { ref_count_.IncRef(); }
|
||||
|
||||
RefCountReleaseStatus Release() const
|
||||
{
|
||||
const auto status = ref_count_.DecRef();
|
||||
if (status == RefCountReleaseStatus::kDroppedLastRef) { delete this; }
|
||||
return status;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool HasOneRef() const { return ref_count_.HasOneRef(); }
|
||||
|
||||
virtual ~RefCountedBase() = default;
|
||||
|
||||
private:
|
||||
mutable sled_impl::RefCounter ref_count_{0};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class RefCountedNonVirtual {
|
||||
public:
|
||||
RefCountedNonVirtual() = default;
|
||||
RefCountedNonVirtual(const RefCountedNonVirtual &) = delete;
|
||||
RefCountedNonVirtual &operator=(const RefCountedNonVirtual &) = delete;
|
||||
|
||||
void AddRef() const { ref_count_.IncRef(); }
|
||||
|
||||
RefCountReleaseStatus Release() const
|
||||
{
|
||||
static_assert(!std::is_polymorphic<T>::value,
|
||||
"T has virtual methods. RefCountedBase is a better fit.");
|
||||
const auto status = ref_count_.DecRef();
|
||||
if (status == RefCountReleaseStatus::kDroppedLastRef) {
|
||||
delete static_cast<const T *>(this);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool HasOneRef() const { return ref_count_.HasOneRef(); }
|
||||
|
||||
~RefCountedNonVirtual() = default;
|
||||
|
||||
private:
|
||||
mutable sled_impl::RefCounter ref_count_{0};
|
||||
};
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// REF_COUNTED_BASE_H
|
||||
72
include/sled/ref_counted_object.h
Normal file
72
include/sled/ref_counted_object.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* @file : ref_counted_object
|
||||
* @created : Thursday Feb 01, 2024 16:09:01 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef REF_COUNTED_OBJECT_H
|
||||
#define REF_COUNTED_OBJECT_H
|
||||
|
||||
#include "sled/ref_count.h"
|
||||
#include "sled/ref_counter.h"
|
||||
|
||||
namespace sled {
|
||||
|
||||
template<typename T>
|
||||
class RefCountedObject : public T {
|
||||
public:
|
||||
RefCountedObject() = default;
|
||||
|
||||
RefCountedObject(const RefCountedObject &) = delete;
|
||||
RefCountedObject operator=(const RefCountedObject &) = delete;
|
||||
|
||||
template<typename TObject>
|
||||
explicit RefCountedObject(TObject &&obj) : T(std::forward<TObject>(obj))
|
||||
{}
|
||||
|
||||
void AddRef() const override { ref_count_.IncRef(); }
|
||||
|
||||
RefCountReleaseStatus Release() const override
|
||||
{
|
||||
const auto status = ref_count_.DecRef();
|
||||
if (status == RefCountReleaseStatus::kDroppedLastRef) { delete this; }
|
||||
return status;
|
||||
}
|
||||
|
||||
virtual bool HasOneRef() const { return ref_count_.HasOneRef(); }
|
||||
|
||||
protected:
|
||||
~RefCountedObject() override {}
|
||||
|
||||
mutable sled_impl::RefCounter ref_count_{0};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class FinalRefCountedObject final : public T {
|
||||
public:
|
||||
using T::T;
|
||||
|
||||
explicit FinalRefCountedObject(T &&other) : T(std::move(other)) {}
|
||||
|
||||
FinalRefCountedObject(const FinalRefCountedObject &) = delete;
|
||||
FinalRefCountedObject operator=(const FinalRefCountedObject &) = delete;
|
||||
|
||||
void AddRef() const { ref_count_.IncRef(); }
|
||||
|
||||
RefCountReleaseStatus Release() const
|
||||
{
|
||||
const auto status = ref_count_.DecRef();
|
||||
if (status == RefCountReleaseStatus::kDroppedLastRef) { delete this; }
|
||||
return status;
|
||||
}
|
||||
|
||||
bool HasOneRef() const { return ref_count_.HasOneRef(); }
|
||||
|
||||
private:
|
||||
~FinalRefCountedObject() = default;
|
||||
mutable sled_impl::RefCounter ref_count_{0};
|
||||
};
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// REF_COUNTED_OBJECT_H
|
||||
45
include/sled/ref_counter.h
Normal file
45
include/sled/ref_counter.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* @file : ref_counter
|
||||
* @created : Thursday Feb 01, 2024 16:00:51 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef REF_COUNTER_H
|
||||
#define REF_COUNTER_H
|
||||
|
||||
#include "sled/ref_count.h"
|
||||
#include <atomic>
|
||||
|
||||
namespace sled {
|
||||
namespace sled_impl {
|
||||
|
||||
class RefCounter {
|
||||
public:
|
||||
explicit RefCounter(int ref_count) : ref_count_(ref_count) {}
|
||||
|
||||
void IncRef() { ref_count_.fetch_add(1, std::memory_order_relaxed); }
|
||||
|
||||
sled::RefCountReleaseStatus DecRef()
|
||||
{
|
||||
int ref_count_after_subtract =
|
||||
ref_count_.fetch_sub(1, std::memory_order_acq_rel) - 1;
|
||||
|
||||
if (ref_count_after_subtract == 0) {
|
||||
return sled::RefCountReleaseStatus::kDroppedLastRef;
|
||||
}
|
||||
return sled::RefCountReleaseStatus::kOtherRefsRemained;
|
||||
}
|
||||
|
||||
bool HasOneRef() const
|
||||
{
|
||||
return ref_count_.load(std::memory_order_acquire) == 1;
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<int> ref_count_;
|
||||
};
|
||||
|
||||
}// namespace sled_impl
|
||||
}// namespace sled
|
||||
|
||||
#endif// REF_COUNTER_H
|
||||
189
include/sled/scoped_refptr.h
Normal file
189
include/sled/scoped_refptr.h
Normal file
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* @file : scoped_refptr
|
||||
* @created : Thursday Feb 01, 2024 16:24:37 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef SCOPED_REFPTR_H
|
||||
#define SCOPED_REFPTR_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace sled {
|
||||
|
||||
template<typename T>
|
||||
class scoped_refptr {
|
||||
public:
|
||||
using element_type = T;
|
||||
|
||||
scoped_refptr() : ptr_(nullptr) {}
|
||||
|
||||
scoped_refptr(std::nullptr_t) : ptr_(nullptr) {}
|
||||
|
||||
explicit scoped_refptr(T *p) : ptr_(p)
|
||||
{
|
||||
if (ptr_) ptr_->AddRef();
|
||||
}
|
||||
|
||||
scoped_refptr(const scoped_refptr &r) : ptr_(r.ptr_)
|
||||
{
|
||||
if (ptr_) ptr_->AddRef();
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
scoped_refptr(const scoped_refptr<U> &r) : ptr_(r.get())
|
||||
{
|
||||
if (ptr_) ptr_->AddRef();
|
||||
}
|
||||
|
||||
scoped_refptr(scoped_refptr<T> &&r) noexcept : ptr_(r.release()) {}
|
||||
|
||||
template<typename U>
|
||||
scoped_refptr(scoped_refptr<U> &&r) noexcept : ptr_(r.release())
|
||||
{}
|
||||
|
||||
~scoped_refptr()
|
||||
{
|
||||
if (ptr_) ptr_->Release();
|
||||
}
|
||||
|
||||
T *get() const { return ptr_; }
|
||||
|
||||
explicit operator bool() { return ptr_ != nullptr; }
|
||||
|
||||
T &operator*() const { return *ptr_; }
|
||||
|
||||
T *operator->() const { return ptr_; }
|
||||
|
||||
T *release()
|
||||
{
|
||||
T *ret_val = ptr_;
|
||||
ptr_ = nullptr;
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
scoped_refptr<T> &operator=(T *p)
|
||||
{
|
||||
if (p) p->AddRef();
|
||||
if (ptr_) ptr_->Release();
|
||||
ptr_ = p;
|
||||
return *this;
|
||||
}
|
||||
|
||||
scoped_refptr<T> &operator=(const scoped_refptr<T> &r)
|
||||
{
|
||||
return *this = r.get();
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
scoped_refptr<T> &operator=(const scoped_refptr<U> &r)
|
||||
{
|
||||
return *this = r.get();
|
||||
}
|
||||
|
||||
scoped_refptr<T> &operator=(const scoped_refptr<T> &&r) noexcept
|
||||
{
|
||||
scoped_refptr<T>(std::move(r)).swap(*this);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
scoped_refptr<T> &operator=(const scoped_refptr<U> &&r)
|
||||
{
|
||||
scoped_refptr<T>(std::move(r)).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(T **pp) noexcept
|
||||
{
|
||||
T *tmp = ptr_;
|
||||
ptr_ = *pp;
|
||||
*pp = tmp;
|
||||
}
|
||||
|
||||
void swap(scoped_refptr<T> &r) noexcept { swap(&r.ptr_); }
|
||||
|
||||
protected:
|
||||
T *ptr_;
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
bool
|
||||
operator==(const scoped_refptr<T> &lhs, const scoped_refptr<U> &rhs)
|
||||
{
|
||||
return lhs.get() == rhs.get();
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
bool
|
||||
operator!=(const scoped_refptr<T> &lhs, const scoped_refptr<U> &rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool
|
||||
operator==(const scoped_refptr<T> &lhs, std::nullptr_t)
|
||||
{
|
||||
return lhs.get() == nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool
|
||||
operator!=(const scoped_refptr<T> &lhs, std::nullptr_t)
|
||||
{
|
||||
return !(lhs == nullptr);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool
|
||||
operator==(std::nullptr_t, const scoped_refptr<T> &rhs)
|
||||
{
|
||||
return rhs.get() == nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool
|
||||
operator!=(std::nullptr_t, const scoped_refptr<T> &rhs)
|
||||
{
|
||||
return !(nullptr == rhs);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
bool
|
||||
operator==(const scoped_refptr<T> &lhs, const U *rhs)
|
||||
{
|
||||
return lhs.get() == rhs;
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
bool
|
||||
operator!=(const scoped_refptr<T> &lhs, const U *rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
bool
|
||||
operator==(const T *lhs, const scoped_refptr<U> &rhs)
|
||||
{
|
||||
return lhs == rhs.get();
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
bool
|
||||
operator!=(const T *lhs, const scoped_refptr<U> &rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
// for std::map
|
||||
template<typename T, typename U>
|
||||
bool
|
||||
operator<(const scoped_refptr<T> &lhs, const scoped_refptr<U> &rhs)
|
||||
{
|
||||
return lhs.get() < rhs.get();
|
||||
}
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// SCOPED_REFPTR_H
|
||||
24
include/sled/sequence_checker.h
Normal file
24
include/sled/sequence_checker.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @file : sequence_checker
|
||||
* @created : Saturday Feb 03, 2024 13:32:22 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef SEQUENCE_CHECKER_H
|
||||
#define SEQUENCE_CHECKER_H
|
||||
|
||||
namespace sled {
|
||||
|
||||
class SequenceChecker : public internal::SequenceCheckerImpl {
|
||||
public:
|
||||
enum InitialState : bool {
|
||||
kDetached = false,
|
||||
kAttached = true,
|
||||
};
|
||||
|
||||
explicit SequenceChecker(InitialState initial_state = kAttached) : Impl(initial_state) {}
|
||||
};
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// SEQUENCE_CHECKER_H
|
||||
689
include/sled/sigslot.h
Normal file
689
include/sled/sigslot.h
Normal file
@@ -0,0 +1,689 @@
|
||||
|
||||
// sigslot.h: Signal/Slot classes
|
||||
//
|
||||
// Written by Sarah Thompson (sarah@telergy.com) 2002.
|
||||
//
|
||||
// License: Public domain. You are free to use this code however you like, with
|
||||
// the proviso that the author takes on no responsibility or liability for any
|
||||
// use.
|
||||
//
|
||||
// QUICK DOCUMENTATION
|
||||
//
|
||||
// (see also the full documentation at http://sigslot.sourceforge.net/)
|
||||
//
|
||||
// #define switches
|
||||
// SIGSLOT_PURE_ISO:
|
||||
// Define this to force ISO C++ compliance. This also disables all of
|
||||
// the thread safety support on platforms where it is available.
|
||||
//
|
||||
// SIGSLOT_USE_POSIX_THREADS:
|
||||
// Force use of Posix threads when using a C++ compiler other than gcc
|
||||
// on a platform that supports Posix threads. (When using gcc, this is
|
||||
// the default - use SIGSLOT_PURE_ISO to disable this if necessary)
|
||||
//
|
||||
// SIGSLOT_DEFAULT_MT_POLICY:
|
||||
// Where thread support is enabled, this defaults to
|
||||
// multi_threaded_global. Otherwise, the default is single_threaded.
|
||||
// #define this yourself to override the default. In pure ISO mode,
|
||||
// anything other than single_threaded will cause a compiler error.
|
||||
//
|
||||
// PLATFORM NOTES
|
||||
//
|
||||
// Win32:
|
||||
// On Win32, the WEBRTC_WIN symbol must be #defined. Most mainstream
|
||||
// compilers do this by default, but you may need to define it yourself
|
||||
// if your build environment is less standard. This causes the Win32
|
||||
// thread support to be compiled in and used automatically.
|
||||
//
|
||||
// Unix/Linux/BSD, etc.:
|
||||
// If you're using gcc, it is assumed that you have Posix threads
|
||||
// available, so they are used automatically. You can override this (as
|
||||
// under Windows) with the SIGSLOT_PURE_ISO switch. If you're using
|
||||
// something other than gcc but still want to use Posix threads, you
|
||||
// need to #define SIGSLOT_USE_POSIX_THREADS.
|
||||
//
|
||||
// ISO C++:
|
||||
// If none of the supported platforms are detected, or if
|
||||
// SIGSLOT_PURE_ISO is defined, all multithreading support is turned
|
||||
// off, along with any code that might cause a pure ISO C++ environment
|
||||
// to complain. Before you ask, gcc -ansi -pedantic won't compile this
|
||||
// library, but gcc -ansi is fine. Pedantic mode seems to throw a lot of
|
||||
// errors that aren't really there. If you feel like investigating this,
|
||||
// please contact the author.
|
||||
//
|
||||
//
|
||||
// THREADING MODES
|
||||
//
|
||||
// single_threaded:
|
||||
// Your program is assumed to be single threaded from the point of view
|
||||
// of signal/slot usage (i.e. all objects using signals and slots are
|
||||
// created and destroyed from a single thread). Behaviour if objects are
|
||||
// destroyed concurrently is undefined (i.e. you'll get the occasional
|
||||
// segmentation fault/memory exception).
|
||||
//
|
||||
// multi_threaded_global:
|
||||
// Your program is assumed to be multi threaded. Objects using signals
|
||||
// and slots can be safely created and destroyed from any thread, even
|
||||
// when connections exist. In multi_threaded_global mode, this is
|
||||
// achieved by a single global mutex (actually a critical section on
|
||||
// Windows because they are faster). This option uses less OS resources,
|
||||
// but results in more opportunities for contention, possibly resulting
|
||||
// in more context switches than are strictly necessary.
|
||||
//
|
||||
// multi_threaded_local:
|
||||
// Behaviour in this mode is essentially the same as
|
||||
// multi_threaded_global, except that each signal, and each object that
|
||||
// inherits has_slots, all have their own mutex/critical section. In
|
||||
// practice, this means that mutex collisions (and hence context
|
||||
// switches) only happen if they are absolutely essential. However, on
|
||||
// some platforms, creating a lot of mutexes can slow down the whole OS,
|
||||
// so use this option with care.
|
||||
//
|
||||
// USING THE LIBRARY
|
||||
//
|
||||
// See the full documentation at http://sigslot.sourceforge.net/
|
||||
//
|
||||
// Libjingle specific:
|
||||
//
|
||||
// This file has been modified such that has_slots and signalx do not have to be
|
||||
// using the same threading requirements. E.g. it is possible to connect a
|
||||
// has_slots<single_threaded> and signal0<multi_threaded_local> or
|
||||
// has_slots<multi_threaded_local> and signal0<single_threaded>.
|
||||
// If has_slots is single threaded the user must ensure that it is not trying
|
||||
// to connect or disconnect to signalx concurrently or data race may occur.
|
||||
// If signalx is single threaded the user must ensure that disconnect, connect
|
||||
// or signal is not happening concurrently or data race may occur.
|
||||
|
||||
#ifndef RTC_BASE_THIRD_PARTY_SIGSLOT_SIGSLOT_H_
|
||||
#define RTC_BASE_THIRD_PARTY_SIGSLOT_SIGSLOT_H_
|
||||
|
||||
#include <cstring>
|
||||
#include <list>
|
||||
#include <set>
|
||||
|
||||
// On our copy of sigslot.h, we set single threading as default.
|
||||
#define SIGSLOT_DEFAULT_MT_POLICY single_threaded
|
||||
|
||||
#if defined(SIGSLOT_PURE_ISO) \
|
||||
|| (!defined(WEBRTC_WIN) && !defined(__GNUG__) \
|
||||
&& !defined(SIGSLOT_USE_POSIX_THREADS))
|
||||
#define _SIGSLOT_SINGLE_THREADED
|
||||
#elif defined(WEBRTC_WIN)
|
||||
#define _SIGSLOT_HAS_WIN32_THREADS
|
||||
#include "windows.h"
|
||||
#elif defined(__GNUG__) || defined(SIGSLOT_USE_POSIX_THREADS)
|
||||
#define _SIGSLOT_HAS_POSIX_THREADS
|
||||
#include <pthread.h>
|
||||
#else
|
||||
#define _SIGSLOT_SINGLE_THREADED
|
||||
#endif
|
||||
|
||||
#ifndef SIGSLOT_DEFAULT_MT_POLICY
|
||||
#ifdef _SIGSLOT_SINGLE_THREADED
|
||||
#define SIGSLOT_DEFAULT_MT_POLICY single_threaded
|
||||
#else
|
||||
#define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// TODO: change this namespace to rtc?
|
||||
namespace sigslot {
|
||||
|
||||
class single_threaded {
|
||||
public:
|
||||
void lock() {}
|
||||
|
||||
void unlock() {}
|
||||
};
|
||||
|
||||
#ifdef _SIGSLOT_HAS_WIN32_THREADS
|
||||
// The multi threading policies only get compiled in if they are enabled.
|
||||
class multi_threaded_global {
|
||||
public:
|
||||
multi_threaded_global()
|
||||
{
|
||||
static bool isinitialised = false;
|
||||
|
||||
if (!isinitialised) {
|
||||
InitializeCriticalSection(get_critsec());
|
||||
isinitialised = true;
|
||||
}
|
||||
}
|
||||
|
||||
void lock() { EnterCriticalSection(get_critsec()); }
|
||||
|
||||
void unlock() { LeaveCriticalSection(get_critsec()); }
|
||||
|
||||
private:
|
||||
CRITICAL_SECTION *get_critsec()
|
||||
{
|
||||
static CRITICAL_SECTION g_critsec;
|
||||
return &g_critsec;
|
||||
}
|
||||
};
|
||||
|
||||
class multi_threaded_local {
|
||||
public:
|
||||
multi_threaded_local() { InitializeCriticalSection(&m_critsec); }
|
||||
|
||||
multi_threaded_local(const multi_threaded_local &)
|
||||
{
|
||||
InitializeCriticalSection(&m_critsec);
|
||||
}
|
||||
|
||||
~multi_threaded_local() { DeleteCriticalSection(&m_critsec); }
|
||||
|
||||
void lock() { EnterCriticalSection(&m_critsec); }
|
||||
|
||||
void unlock() { LeaveCriticalSection(&m_critsec); }
|
||||
|
||||
private:
|
||||
CRITICAL_SECTION m_critsec;
|
||||
};
|
||||
#endif// _SIGSLOT_HAS_WIN32_THREADS
|
||||
|
||||
#ifdef _SIGSLOT_HAS_POSIX_THREADS
|
||||
// The multi threading policies only get compiled in if they are enabled.
|
||||
class multi_threaded_global {
|
||||
public:
|
||||
void lock() { pthread_mutex_lock(get_mutex()); }
|
||||
|
||||
void unlock() { pthread_mutex_unlock(get_mutex()); }
|
||||
|
||||
private:
|
||||
static pthread_mutex_t *get_mutex();
|
||||
};
|
||||
|
||||
class multi_threaded_local {
|
||||
public:
|
||||
multi_threaded_local() { pthread_mutex_init(&m_mutex, nullptr); }
|
||||
|
||||
multi_threaded_local(const multi_threaded_local &)
|
||||
{
|
||||
pthread_mutex_init(&m_mutex, nullptr);
|
||||
}
|
||||
|
||||
~multi_threaded_local() { pthread_mutex_destroy(&m_mutex); }
|
||||
|
||||
void lock() { pthread_mutex_lock(&m_mutex); }
|
||||
|
||||
void unlock() { pthread_mutex_unlock(&m_mutex); }
|
||||
|
||||
private:
|
||||
pthread_mutex_t m_mutex;
|
||||
};
|
||||
#endif// _SIGSLOT_HAS_POSIX_THREADS
|
||||
|
||||
template<class mt_policy>
|
||||
class lock_block {
|
||||
public:
|
||||
mt_policy *m_mutex;
|
||||
|
||||
lock_block(mt_policy *mtx) : m_mutex(mtx) { m_mutex->lock(); }
|
||||
|
||||
~lock_block() { m_mutex->unlock(); }
|
||||
};
|
||||
|
||||
class _signal_base_interface;
|
||||
|
||||
class has_slots_interface {
|
||||
private:
|
||||
typedef void (*signal_connect_t)(has_slots_interface *self,
|
||||
_signal_base_interface *sender);
|
||||
typedef void (*signal_disconnect_t)(has_slots_interface *self,
|
||||
_signal_base_interface *sender);
|
||||
typedef void (*disconnect_all_t)(has_slots_interface *self);
|
||||
|
||||
const signal_connect_t m_signal_connect;
|
||||
const signal_disconnect_t m_signal_disconnect;
|
||||
const disconnect_all_t m_disconnect_all;
|
||||
|
||||
protected:
|
||||
has_slots_interface(signal_connect_t conn,
|
||||
signal_disconnect_t disc,
|
||||
disconnect_all_t disc_all)
|
||||
: m_signal_connect(conn),
|
||||
m_signal_disconnect(disc),
|
||||
m_disconnect_all(disc_all)
|
||||
{}
|
||||
|
||||
// Doesn't really need to be virtual, but is for backwards compatibility
|
||||
// (it was virtual in a previous version of sigslot).
|
||||
virtual ~has_slots_interface() {}
|
||||
|
||||
public:
|
||||
void signal_connect(_signal_base_interface *sender)
|
||||
{
|
||||
m_signal_connect(this, sender);
|
||||
}
|
||||
|
||||
void signal_disconnect(_signal_base_interface *sender)
|
||||
{
|
||||
m_signal_disconnect(this, sender);
|
||||
}
|
||||
|
||||
void disconnect_all() { m_disconnect_all(this); }
|
||||
};
|
||||
|
||||
class _signal_base_interface {
|
||||
private:
|
||||
typedef void (*slot_disconnect_t)(_signal_base_interface *self,
|
||||
has_slots_interface *pslot);
|
||||
typedef void (*slot_duplicate_t)(_signal_base_interface *self,
|
||||
const has_slots_interface *poldslot,
|
||||
has_slots_interface *pnewslot);
|
||||
|
||||
const slot_disconnect_t m_slot_disconnect;
|
||||
const slot_duplicate_t m_slot_duplicate;
|
||||
|
||||
protected:
|
||||
_signal_base_interface(slot_disconnect_t disc, slot_duplicate_t dupl)
|
||||
: m_slot_disconnect(disc),
|
||||
m_slot_duplicate(dupl)
|
||||
{}
|
||||
|
||||
~_signal_base_interface() {}
|
||||
|
||||
public:
|
||||
void slot_disconnect(has_slots_interface *pslot)
|
||||
{
|
||||
m_slot_disconnect(this, pslot);
|
||||
}
|
||||
|
||||
void slot_duplicate(const has_slots_interface *poldslot,
|
||||
has_slots_interface *pnewslot)
|
||||
{
|
||||
m_slot_duplicate(this, poldslot, pnewslot);
|
||||
}
|
||||
};
|
||||
|
||||
class _opaque_connection {
|
||||
private:
|
||||
typedef void (*emit_t)(const _opaque_connection *);
|
||||
|
||||
template<typename FromT, typename ToT>
|
||||
union union_caster {
|
||||
FromT from;
|
||||
ToT to;
|
||||
};
|
||||
|
||||
emit_t pemit;
|
||||
has_slots_interface *pdest;
|
||||
// Pointers to member functions may be up to 16 bytes (24 bytes for MSVC)
|
||||
// for virtual classes, so make sure we have enough space to store it.
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
unsigned char pmethod[24];
|
||||
#else
|
||||
unsigned char pmethod[16];
|
||||
#endif
|
||||
|
||||
public:
|
||||
template<typename DestT, typename... Args>
|
||||
_opaque_connection(DestT *pd, void (DestT::*pm)(Args...)) : pdest(pd)
|
||||
{
|
||||
typedef void (DestT::*pm_t)(Args...);
|
||||
static_assert(sizeof(pm_t) <= sizeof(pmethod),
|
||||
"Size of slot function pointer too large.");
|
||||
|
||||
std::memcpy(pmethod, &pm, sizeof(pm_t));
|
||||
|
||||
typedef void (*em_t)(const _opaque_connection *self, Args...);
|
||||
union_caster<em_t, emit_t> caster2;
|
||||
caster2.from = &_opaque_connection::emitter<DestT, Args...>;
|
||||
pemit = caster2.to;
|
||||
}
|
||||
|
||||
has_slots_interface *getdest() const { return pdest; }
|
||||
|
||||
_opaque_connection duplicate(has_slots_interface *newtarget) const
|
||||
{
|
||||
_opaque_connection res = *this;
|
||||
res.pdest = newtarget;
|
||||
return res;
|
||||
}
|
||||
|
||||
// Just calls the stored "emitter" function pointer stored at construction
|
||||
// time.
|
||||
template<typename... Args>
|
||||
void emit(Args... args) const
|
||||
{
|
||||
typedef void (*em_t)(const _opaque_connection *, Args...);
|
||||
union_caster<emit_t, em_t> caster;
|
||||
caster.from = pemit;
|
||||
(caster.to)(this, args...);
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename DestT, typename... Args>
|
||||
static void emitter(const _opaque_connection *self, Args... args)
|
||||
{
|
||||
typedef void (DestT::*pm_t)(Args...);
|
||||
pm_t pm;
|
||||
static_assert(sizeof(pm_t) <= sizeof(pmethod),
|
||||
"Size of slot function pointer too large.");
|
||||
std::memcpy(&pm, self->pmethod, sizeof(pm_t));
|
||||
(static_cast<DestT *>(self->pdest)->*(pm))(args...);
|
||||
}
|
||||
};
|
||||
|
||||
template<class mt_policy>
|
||||
class _signal_base : public _signal_base_interface, public mt_policy {
|
||||
protected:
|
||||
typedef std::list<_opaque_connection> connections_list;
|
||||
|
||||
_signal_base()
|
||||
: _signal_base_interface(&_signal_base::do_slot_disconnect,
|
||||
&_signal_base::do_slot_duplicate),
|
||||
m_current_iterator(m_connected_slots.end())
|
||||
{}
|
||||
|
||||
~_signal_base() { disconnect_all(); }
|
||||
|
||||
private:
|
||||
_signal_base &operator=(_signal_base const &that);
|
||||
|
||||
public:
|
||||
_signal_base(const _signal_base &o)
|
||||
: _signal_base_interface(&_signal_base::do_slot_disconnect,
|
||||
&_signal_base::do_slot_duplicate),
|
||||
m_current_iterator(m_connected_slots.end())
|
||||
{
|
||||
lock_block<mt_policy> lock(this);
|
||||
for (const auto &connection : o.m_connected_slots) {
|
||||
connection.getdest()->signal_connect(this);
|
||||
m_connected_slots.push_back(connection);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_empty()
|
||||
{
|
||||
lock_block<mt_policy> lock(this);
|
||||
return m_connected_slots.empty();
|
||||
}
|
||||
|
||||
void disconnect_all()
|
||||
{
|
||||
lock_block<mt_policy> lock(this);
|
||||
|
||||
while (!m_connected_slots.empty()) {
|
||||
has_slots_interface *pdest = m_connected_slots.front().getdest();
|
||||
m_connected_slots.pop_front();
|
||||
pdest->signal_disconnect(
|
||||
static_cast<_signal_base_interface *>(this));
|
||||
}
|
||||
// If disconnect_all is called while the signal is firing, advance the
|
||||
// current slot iterator to the end to avoid an invalidated iterator from
|
||||
// being dereferenced.
|
||||
m_current_iterator = m_connected_slots.end();
|
||||
}
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
bool connected(has_slots_interface *pclass)
|
||||
{
|
||||
lock_block<mt_policy> lock(this);
|
||||
connections_list::const_iterator it = m_connected_slots.begin();
|
||||
connections_list::const_iterator itEnd = m_connected_slots.end();
|
||||
while (it != itEnd) {
|
||||
if (it->getdest() == pclass) return true;
|
||||
++it;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
void disconnect(has_slots_interface *pclass)
|
||||
{
|
||||
lock_block<mt_policy> lock(this);
|
||||
connections_list::iterator it = m_connected_slots.begin();
|
||||
connections_list::iterator itEnd = m_connected_slots.end();
|
||||
|
||||
while (it != itEnd) {
|
||||
if (it->getdest() == pclass) {
|
||||
// If we're currently using this iterator because the signal is firing,
|
||||
// advance it to avoid it being invalidated.
|
||||
if (m_current_iterator == it) {
|
||||
m_current_iterator = m_connected_slots.erase(it);
|
||||
} else {
|
||||
m_connected_slots.erase(it);
|
||||
}
|
||||
pclass->signal_disconnect(
|
||||
static_cast<_signal_base_interface *>(this));
|
||||
return;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static void do_slot_disconnect(_signal_base_interface *p,
|
||||
has_slots_interface *pslot)
|
||||
{
|
||||
_signal_base *const self = static_cast<_signal_base *>(p);
|
||||
lock_block<mt_policy> lock(self);
|
||||
connections_list::iterator it = self->m_connected_slots.begin();
|
||||
connections_list::iterator itEnd = self->m_connected_slots.end();
|
||||
|
||||
while (it != itEnd) {
|
||||
connections_list::iterator itNext = it;
|
||||
++itNext;
|
||||
|
||||
if (it->getdest() == pslot) {
|
||||
// If we're currently using this iterator because the signal is firing,
|
||||
// advance it to avoid it being invalidated.
|
||||
if (self->m_current_iterator == it) {
|
||||
self->m_current_iterator =
|
||||
self->m_connected_slots.erase(it);
|
||||
} else {
|
||||
self->m_connected_slots.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
it = itNext;
|
||||
}
|
||||
}
|
||||
|
||||
static void do_slot_duplicate(_signal_base_interface *p,
|
||||
const has_slots_interface *oldtarget,
|
||||
has_slots_interface *newtarget)
|
||||
{
|
||||
_signal_base *const self = static_cast<_signal_base *>(p);
|
||||
lock_block<mt_policy> lock(self);
|
||||
connections_list::iterator it = self->m_connected_slots.begin();
|
||||
connections_list::iterator itEnd = self->m_connected_slots.end();
|
||||
|
||||
while (it != itEnd) {
|
||||
if (it->getdest() == oldtarget) {
|
||||
self->m_connected_slots.push_back(it->duplicate(newtarget));
|
||||
}
|
||||
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
connections_list m_connected_slots;
|
||||
|
||||
// Used to handle a slot being disconnected while a signal is
|
||||
// firing (iterating m_connected_slots).
|
||||
connections_list::iterator m_current_iterator;
|
||||
bool m_erase_current_iterator = false;
|
||||
};
|
||||
|
||||
template<class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
|
||||
class has_slots : public has_slots_interface, public mt_policy {
|
||||
private:
|
||||
typedef std::set<_signal_base_interface *> sender_set;
|
||||
typedef sender_set::const_iterator const_iterator;
|
||||
|
||||
public:
|
||||
has_slots()
|
||||
: has_slots_interface(&has_slots::do_signal_connect,
|
||||
&has_slots::do_signal_disconnect,
|
||||
&has_slots::do_disconnect_all)
|
||||
{}
|
||||
|
||||
has_slots(has_slots const &o)
|
||||
: has_slots_interface(&has_slots::do_signal_connect,
|
||||
&has_slots::do_signal_disconnect,
|
||||
&has_slots::do_disconnect_all)
|
||||
{
|
||||
lock_block<mt_policy> lock(this);
|
||||
for (auto *sender : o.m_senders) {
|
||||
sender->slot_duplicate(&o, this);
|
||||
m_senders.insert(sender);
|
||||
}
|
||||
}
|
||||
|
||||
~has_slots() { this->disconnect_all(); }
|
||||
|
||||
private:
|
||||
has_slots &operator=(has_slots const &);
|
||||
|
||||
static void do_signal_connect(has_slots_interface *p,
|
||||
_signal_base_interface *sender)
|
||||
{
|
||||
has_slots *const self = static_cast<has_slots *>(p);
|
||||
lock_block<mt_policy> lock(self);
|
||||
self->m_senders.insert(sender);
|
||||
}
|
||||
|
||||
static void do_signal_disconnect(has_slots_interface *p,
|
||||
_signal_base_interface *sender)
|
||||
{
|
||||
has_slots *const self = static_cast<has_slots *>(p);
|
||||
lock_block<mt_policy> lock(self);
|
||||
self->m_senders.erase(sender);
|
||||
}
|
||||
|
||||
static void do_disconnect_all(has_slots_interface *p)
|
||||
{
|
||||
has_slots *const self = static_cast<has_slots *>(p);
|
||||
lock_block<mt_policy> lock(self);
|
||||
while (!self->m_senders.empty()) {
|
||||
std::set<_signal_base_interface *> senders;
|
||||
senders.swap(self->m_senders);
|
||||
const_iterator it = senders.begin();
|
||||
const_iterator itEnd = senders.end();
|
||||
|
||||
while (it != itEnd) {
|
||||
_signal_base_interface *s = *it;
|
||||
++it;
|
||||
s->slot_disconnect(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
sender_set m_senders;
|
||||
};
|
||||
|
||||
template<class mt_policy, typename... Args>
|
||||
class signal_with_thread_policy : public _signal_base<mt_policy> {
|
||||
private:
|
||||
typedef _signal_base<mt_policy> base;
|
||||
|
||||
protected:
|
||||
typedef typename base::connections_list connections_list;
|
||||
|
||||
public:
|
||||
signal_with_thread_policy() {}
|
||||
|
||||
template<class desttype>
|
||||
void connect(desttype *pclass, void (desttype::*pmemfun)(Args...))
|
||||
{
|
||||
lock_block<mt_policy> lock(this);
|
||||
this->m_connected_slots.push_back(_opaque_connection(pclass, pmemfun));
|
||||
pclass->signal_connect(static_cast<_signal_base_interface *>(this));
|
||||
}
|
||||
|
||||
void emit(Args... args)
|
||||
{
|
||||
lock_block<mt_policy> lock(this);
|
||||
this->m_current_iterator = this->m_connected_slots.begin();
|
||||
while (this->m_current_iterator != this->m_connected_slots.end()) {
|
||||
_opaque_connection const &conn = *this->m_current_iterator;
|
||||
++(this->m_current_iterator);
|
||||
conn.emit<Args...>(args...);
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(Args... args) { emit(args...); }
|
||||
};
|
||||
|
||||
// Alias with default thread policy. Needed because both default arguments
|
||||
// and variadic template arguments must go at the end of the list, so we
|
||||
// can't have both at once.
|
||||
template<typename... Args>
|
||||
using signal = signal_with_thread_policy<SIGSLOT_DEFAULT_MT_POLICY, Args...>;
|
||||
|
||||
// The previous verion of sigslot didn't use variadic templates, so you would
|
||||
// need to write "sigslot::signal2<Arg1, Arg2>", for example.
|
||||
// Now you can just write "sigslot::signal<Arg1, Arg2>", but these aliases
|
||||
// exist for backwards compatibility.
|
||||
template<typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
|
||||
using signal0 = signal_with_thread_policy<mt_policy>;
|
||||
|
||||
template<typename A1, typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
|
||||
using signal1 = signal_with_thread_policy<mt_policy, A1>;
|
||||
|
||||
template<typename A1,
|
||||
typename A2,
|
||||
typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
|
||||
using signal2 = signal_with_thread_policy<mt_policy, A1, A2>;
|
||||
|
||||
template<typename A1,
|
||||
typename A2,
|
||||
typename A3,
|
||||
typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
|
||||
using signal3 = signal_with_thread_policy<mt_policy, A1, A2, A3>;
|
||||
|
||||
template<typename A1,
|
||||
typename A2,
|
||||
typename A3,
|
||||
typename A4,
|
||||
typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
|
||||
using signal4 = signal_with_thread_policy<mt_policy, A1, A2, A3, A4>;
|
||||
|
||||
template<typename A1,
|
||||
typename A2,
|
||||
typename A3,
|
||||
typename A4,
|
||||
typename A5,
|
||||
typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
|
||||
using signal5 = signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5>;
|
||||
|
||||
template<typename A1,
|
||||
typename A2,
|
||||
typename A3,
|
||||
typename A4,
|
||||
typename A5,
|
||||
typename A6,
|
||||
typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
|
||||
using signal6 = signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5, A6>;
|
||||
|
||||
template<typename A1,
|
||||
typename A2,
|
||||
typename A3,
|
||||
typename A4,
|
||||
typename A5,
|
||||
typename A6,
|
||||
typename A7,
|
||||
typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
|
||||
using signal7 =
|
||||
signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5, A6, A7>;
|
||||
|
||||
template<typename A1,
|
||||
typename A2,
|
||||
typename A3,
|
||||
typename A4,
|
||||
typename A5,
|
||||
typename A6,
|
||||
typename A7,
|
||||
typename A8,
|
||||
typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
|
||||
using signal8 =
|
||||
signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5, A6, A7, A8>;
|
||||
|
||||
}// namespace sigslot
|
||||
|
||||
#endif /* RTC_BASE_THIRD_PARTY_SIGSLOT_SIGSLOT_H_ */
|
||||
111
include/sled/status.h
Normal file
111
include/sled/status.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* @file : status
|
||||
* @created : Thursday Feb 01, 2024 17:25:33 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef STATUS_H
|
||||
#define STATUS_H
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace sled {
|
||||
|
||||
enum class StatusCode {
|
||||
kOk = 0,
|
||||
kCancelled = 1,
|
||||
kUnknown = 2,
|
||||
kInvalidArgument = 3,
|
||||
kDeadlineExceeded = 4,
|
||||
kNotFound = 5,
|
||||
kAlreadyExists = 6,
|
||||
kPermissionDenied = 7,
|
||||
kResourceExhausted = 8,
|
||||
kFailedPrecondition = 9,
|
||||
kAborted = 10,
|
||||
kOutOfRange = 11,
|
||||
kUnimplemented = 12,
|
||||
kInternal = 13,
|
||||
kUnavailable = 14,
|
||||
kDataLoss = 15,
|
||||
kUnauthenticated = 16,
|
||||
};
|
||||
|
||||
std::string StatusCodeToString(StatusCode code);
|
||||
std::ostream &operator<<(std::ostream &os, StatusCode code);
|
||||
|
||||
class ErrorInfo;
|
||||
class Status;
|
||||
|
||||
namespace internal {
|
||||
void AddMetadata(ErrorInfo &, std::string const &key, std::string value);
|
||||
void SetPayload(Status &s, std::string key, std::string payload);
|
||||
}// namespace internal
|
||||
|
||||
class ErrorInfo {
|
||||
public:
|
||||
ErrorInfo() = default;
|
||||
|
||||
explicit ErrorInfo(std::string reason,
|
||||
std::string domain,
|
||||
std::unordered_map<std::string, std::string> metadata)
|
||||
: reason_(std::move(reason)),
|
||||
domain_(std::move(domain)),
|
||||
metadata_(std::move(metadata))
|
||||
{}
|
||||
|
||||
std::string const &reason() const { return reason_; }
|
||||
|
||||
std::string const &domain() const { return domain_; }
|
||||
|
||||
std::unordered_map<std::string, std::string> const &metadata() const
|
||||
{
|
||||
return metadata_;
|
||||
}
|
||||
|
||||
friend bool operator==(ErrorInfo const &, ErrorInfo const &);
|
||||
friend bool operator!=(ErrorInfo const &, ErrorInfo const &);
|
||||
|
||||
private:
|
||||
friend void internal::AddMetadata(ErrorInfo &,
|
||||
std::string const &key,
|
||||
std::string value);
|
||||
std::string reason_;
|
||||
std::string domain_;
|
||||
std::unordered_map<std::string, std::string> metadata_;
|
||||
};
|
||||
|
||||
class Status {
|
||||
public:
|
||||
Status();
|
||||
~Status();
|
||||
Status(Status const &);
|
||||
Status &operator=(Status const &);
|
||||
Status(Status &&) noexcept;
|
||||
Status &operator=(Status &&) noexcept;
|
||||
|
||||
explicit Status(StatusCode code, std::string message, ErrorInfo info = {});
|
||||
|
||||
bool ok() const { return !impl_; }
|
||||
|
||||
StatusCode code() const;
|
||||
std::string const &message() const;
|
||||
ErrorInfo const &error_info() const;
|
||||
|
||||
friend bool operator==(Status const &, Status const &);
|
||||
friend bool operator!=(Status const &, Status const &);
|
||||
|
||||
private:
|
||||
friend void internal::SetPayload(Status &, std::string, std::string);
|
||||
static bool Equals(Status const &, Status const &);
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, Status const &status);
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// STATUS_H
|
||||
164
include/sled/status_or.h
Normal file
164
include/sled/status_or.h
Normal file
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* @file : status_or
|
||||
* @created : Thursday Feb 01, 2024 18:04:44 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef STATUS_OR_H
|
||||
#define STATUS_OR_H
|
||||
#include "sled/optional.h"
|
||||
#include "sled/status.h"
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace sled {
|
||||
|
||||
template<typename T>
|
||||
class StatusOr final {
|
||||
public:
|
||||
static_assert(!std::is_reference<T>::value,
|
||||
"StatusOr<T> requires T to **not** be a reference type");
|
||||
using value_type = T;
|
||||
|
||||
StatusOr() : StatusOr(MakeDefaultStatus()) {}
|
||||
|
||||
StatusOr(StatusOr const &) = default;
|
||||
StatusOr &operator=(StatusOr const &) = default;
|
||||
|
||||
StatusOr(StatusOr &&other)
|
||||
: status_(std::move(other.status_)),
|
||||
value_(std::move(value_))
|
||||
{
|
||||
other.status_ = MakeDefaultStatus();
|
||||
}
|
||||
|
||||
StatusOr &operator=(StatusOr &&other)
|
||||
{
|
||||
status_ = std::move(other.status_);
|
||||
value_ = std::move(other.value_);
|
||||
other.status_ = MakeDefaultStatus();
|
||||
return *this;
|
||||
}
|
||||
|
||||
StatusOr(Status rhs) : status_(std::move(rhs))
|
||||
{
|
||||
if (status_.ok()) {
|
||||
throw std::invalid_argument(
|
||||
"Status::OK is not a valid argument to StatusOr<T>");
|
||||
}
|
||||
}
|
||||
|
||||
StatusOr &operator=(Status status)
|
||||
{
|
||||
*this = StatusOr(std::move(status));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U = T,
|
||||
/// @code implementation detail
|
||||
typename std::enable_if<
|
||||
!std::is_same<StatusOr, typename std::decay<U>::type>::value,
|
||||
int>::type = 0
|
||||
/// @code end
|
||||
>
|
||||
StatusOr &operator=(U &&rhs)
|
||||
{
|
||||
status_ = Status();
|
||||
value_ = std::forward<U>(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StatusOr(T &&rhs) : value_(std::move(rhs)) {}
|
||||
|
||||
StatusOr(T const &rhs) : value_(rhs) {}
|
||||
|
||||
bool ok() const { return status_.ok(); }
|
||||
|
||||
explicit operator bool() const { return status_.ok(); }
|
||||
|
||||
T &operator*() & { return *value_; }
|
||||
|
||||
T const &operator*() const & { return *value_; }
|
||||
|
||||
T &&operator*() && { return *std::move(value_); }
|
||||
|
||||
T const &&operator*() const && { return *std::move(value_); }
|
||||
|
||||
T *operator->() & { return &*value_; }
|
||||
|
||||
T const *operator->() const & { return &*value_; }
|
||||
|
||||
T &value() &
|
||||
{
|
||||
CheckHasValue();
|
||||
return **this;
|
||||
}
|
||||
|
||||
T const &value() const &
|
||||
{
|
||||
CheckHasValue();
|
||||
return **this;
|
||||
}
|
||||
|
||||
T &&value() &&
|
||||
{
|
||||
CheckHasValue();
|
||||
return **this;
|
||||
}
|
||||
|
||||
T const &&value() const &&
|
||||
{
|
||||
CheckHasValue();
|
||||
return **this;
|
||||
}
|
||||
|
||||
Status const &status() const & { return status_; }
|
||||
|
||||
Status &&status() && { return std::move(status_); }
|
||||
|
||||
private:
|
||||
static Status MakeDefaultStatus()
|
||||
{
|
||||
return Status{StatusCode::kUnknown, "default"};
|
||||
}
|
||||
|
||||
void CheckHasValue() const &
|
||||
{
|
||||
if (!ok()) { throw std::invalid_argument("no value"); }
|
||||
}
|
||||
|
||||
Status status_;
|
||||
sled::optional<T> value_;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
bool
|
||||
operator==(StatusOr<T> const &a, StatusOr<T> const &b)
|
||||
{
|
||||
if (!a || !b) return a.status() == b.status();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool
|
||||
operator!=(StatusOr<T> const &a, StatusOr<T> const &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
StatusOr<T>
|
||||
make_status_or(T rhs)
|
||||
{
|
||||
return StatusOr<T>(std::move(rhs));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
StatusOr<T>
|
||||
make_status_or(StatusCode code, std::string message = "", ErrorInfo info = {})
|
||||
{
|
||||
return StatusOr<T>(Status(code, std::move(message)));
|
||||
}
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// STATUS_OR_H
|
||||
23
include/sled/strings/base64.h
Normal file
23
include/sled/strings/base64.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @file : base64
|
||||
* @created : Monday Feb 19, 2024 15:32:59 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef BASE64_H
|
||||
#define BASE64_H
|
||||
|
||||
#include "sled/status_or.h"
|
||||
#include <string>
|
||||
|
||||
namespace sled {
|
||||
|
||||
class Base64 {
|
||||
public:
|
||||
static std::string Encode(const std::string &input);
|
||||
static StatusOr<std::string> Decode(const std::string &input);
|
||||
};
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// BASE64_H
|
||||
43
include/sled/synchronization/event.h
Normal file
43
include/sled/synchronization/event.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @file : event
|
||||
* @created : Wednesday Feb 14, 2024 13:38:08 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef EVENT_H
|
||||
#define EVENT_H
|
||||
|
||||
#include "sled/synchronization/mutex.h"
|
||||
#include "sled/units/time_delta.h"
|
||||
|
||||
namespace sled {
|
||||
|
||||
class Event {
|
||||
public:
|
||||
static constexpr TimeDelta kForever = ConditionVariable::kForever;
|
||||
Event();
|
||||
Event(bool manual_reset, bool initially_signaled);
|
||||
Event(const Event &) = delete;
|
||||
Event &operator=(const Event &) = delete;
|
||||
~Event();
|
||||
|
||||
void Set();
|
||||
void Reset();
|
||||
bool Wait(TimeDelta give_up_after, TimeDelta warn_after);
|
||||
|
||||
bool Wait(TimeDelta give_up_after)
|
||||
{
|
||||
return Wait(give_up_after,
|
||||
give_up_after.IsPlusInfinity() ? TimeDelta::Seconds(3)
|
||||
: kForever);
|
||||
}
|
||||
|
||||
Mutex mutex_;
|
||||
ConditionVariable cv_;
|
||||
const bool is_manual_reset_;
|
||||
bool event_status_;
|
||||
};
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// EVENT_H
|
||||
154
include/sled/synchronization/mutex.h
Normal file
154
include/sled/synchronization/mutex.h
Normal file
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* @file : mutex
|
||||
* @created : Saturday Feb 03, 2024 13:36:10 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef MUTEX_H
|
||||
#define MUTEX_H
|
||||
|
||||
#include "sled/units/time_delta.h"
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <type_traits>
|
||||
|
||||
namespace sled {
|
||||
namespace internal {
|
||||
template<typename T>
|
||||
struct HasLockAndUnlock {
|
||||
template<typename TClass,
|
||||
decltype(std::declval<TClass>().Lock()) * = nullptr,
|
||||
decltype(std::declval<TClass>().Unlock()) * = nullptr>
|
||||
static int Test(T);
|
||||
static char Test(...);
|
||||
|
||||
static constexpr bool value =
|
||||
std::is_same<decltype(Test<T>(0)), int>::value;
|
||||
};
|
||||
}// namespace internal
|
||||
|
||||
class Mutex final {
|
||||
public:
|
||||
Mutex() = default;
|
||||
Mutex(const Mutex &) = delete;
|
||||
Mutex &operator=(const Mutex &) = delete;
|
||||
|
||||
inline void Lock() { impl_.lock(); };
|
||||
|
||||
inline bool TryLock() { return impl_.try_lock(); }
|
||||
|
||||
inline void AssertHeld() {}
|
||||
|
||||
inline void Unlock() { impl_.unlock(); }
|
||||
|
||||
private:
|
||||
std::mutex impl_;
|
||||
friend class ConditionVariable;
|
||||
};
|
||||
|
||||
class RecursiveMutex final {
|
||||
public:
|
||||
RecursiveMutex() = default;
|
||||
RecursiveMutex(const RecursiveMutex &) = delete;
|
||||
RecursiveMutex &operator=(const RecursiveMutex &) = delete;
|
||||
|
||||
inline void Lock() { impl_.lock(); }
|
||||
|
||||
inline bool TryLock() { return impl_.try_lock(); }
|
||||
|
||||
inline void AssertHeld() {}
|
||||
|
||||
inline void Unlock() { impl_.unlock(); }
|
||||
|
||||
private:
|
||||
std::recursive_mutex impl_;
|
||||
};
|
||||
|
||||
template<typename TLock,
|
||||
typename std::enable_if<internal::HasLockAndUnlock<TLock>::value,
|
||||
TLock>::type * = nullptr>
|
||||
class LockGuard final {
|
||||
LockGuard(const LockGuard &) = delete;
|
||||
LockGuard &operator=(const LockGuard &) = delete;
|
||||
|
||||
explicit LockGuard(TLock *lock) : lock_(lock) { lock_->Lock(); };
|
||||
|
||||
~LockGuard() { lock_->Unlock(); };
|
||||
|
||||
private:
|
||||
TLock *lock_;
|
||||
};
|
||||
|
||||
class MutexLock final {
|
||||
public:
|
||||
MutexLock(const MutexLock &) = delete;
|
||||
MutexLock &operator=(const MutexLock &) = delete;
|
||||
|
||||
explicit MutexLock(Mutex *mutex) : mutex_(mutex) { mutex->Lock(); }
|
||||
|
||||
~MutexLock() { mutex_->Unlock(); }
|
||||
|
||||
private:
|
||||
Mutex *mutex_;
|
||||
};
|
||||
|
||||
class RecursiveMutexLock final {
|
||||
public:
|
||||
RecursiveMutexLock(const RecursiveMutexLock &) = delete;
|
||||
RecursiveMutexLock &operator=(const RecursiveMutexLock &) = delete;
|
||||
|
||||
explicit RecursiveMutexLock(RecursiveMutex *mutex) : mutex_(mutex)
|
||||
{
|
||||
mutex->Lock();
|
||||
}
|
||||
|
||||
~RecursiveMutexLock() { mutex_->Unlock(); }
|
||||
|
||||
private:
|
||||
RecursiveMutex *mutex_;
|
||||
};
|
||||
|
||||
class ConditionVariable final {
|
||||
public:
|
||||
static constexpr TimeDelta kForever = TimeDelta::PlusInfinity();
|
||||
ConditionVariable() = default;
|
||||
ConditionVariable(const ConditionVariable &) = delete;
|
||||
ConditionVariable &operator=(const ConditionVariable &) = delete;
|
||||
|
||||
template<typename Predicate>
|
||||
inline bool Wait(Mutex *mutex, Predicate pred)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex->impl_, std::adopt_lock);
|
||||
cv_.wait(lock, pred);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Predicate>
|
||||
inline bool WaitFor(Mutex *mutex, TimeDelta timeout, Predicate pred)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex->impl_, std::adopt_lock);
|
||||
if (timeout == kForever) {
|
||||
cv_.wait(lock, pred);
|
||||
return true;
|
||||
} else {
|
||||
return cv_.wait_for(lock, std::chrono::milliseconds(timeout.ms()),
|
||||
pred);
|
||||
}
|
||||
}
|
||||
|
||||
// template<typename Predicate>
|
||||
// bool WaitUntil(Mutex *mutex, TimeDelta timeout, Predicate pred)
|
||||
// {}
|
||||
|
||||
inline void NotifyOne() { cv_.notify_one(); }
|
||||
|
||||
inline void NotifyAll() { cv_.notify_all(); }
|
||||
|
||||
private:
|
||||
std::condition_variable cv_;
|
||||
};
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// MUTEX_H
|
||||
47
include/sled/synchronization/one_time_event.h
Normal file
47
include/sled/synchronization/one_time_event.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file : one_time_event
|
||||
* @created : Saturday Feb 03, 2024 16:51:40 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef ONE_TIME_EVENT_H
|
||||
#define ONE_TIME_EVENT_H
|
||||
|
||||
#include "sled/synchronization/mutex.h"
|
||||
|
||||
namespace sled {
|
||||
|
||||
class OneTimeEvent {
|
||||
public:
|
||||
OneTimeEvent() = default;
|
||||
|
||||
bool operator()()
|
||||
{
|
||||
MutexLock lock(&mutex_);
|
||||
if (happended_) { return false; }
|
||||
happended_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool happended_ = false;
|
||||
Mutex mutex_;
|
||||
};
|
||||
|
||||
class ThreadUnsafeOneTimeEvent {
|
||||
public:
|
||||
ThreadUnsafeOneTimeEvent() = default;
|
||||
|
||||
bool operator()()
|
||||
{
|
||||
if (happended_) { return false; }
|
||||
happended_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool happended_ = false;
|
||||
};
|
||||
}// namespace sled
|
||||
|
||||
#endif// ONE_TIME_EVENT_H
|
||||
56
include/sled/synchronization/sequence_checker_internal.h
Normal file
56
include/sled/synchronization/sequence_checker_internal.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* @file : sequence_checker_internal
|
||||
* @created : Saturday Feb 03, 2024 13:34:40 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef SEQUENCE_CHECKER_INTERNAL_H
|
||||
#define SEQUENCE_CHECKER_INTERNAL_H
|
||||
|
||||
#include "sled/synchronization/mutex.h"
|
||||
#include <string>
|
||||
|
||||
namespace sled {
|
||||
|
||||
class SequenceCheckerImpl {
|
||||
public:
|
||||
explicit SequenceCheckerImpl(bool attach_to_current_thread);
|
||||
~SequenceCheckerImpl() = default;
|
||||
|
||||
bool IsCurrent() const;
|
||||
void Detach();
|
||||
std::string ExpectationToString() const;
|
||||
|
||||
private:
|
||||
mutable Mutex lock_;
|
||||
mutable bool attached_;
|
||||
};
|
||||
|
||||
class SequenceCheckerDoNothing {
|
||||
public:
|
||||
explicit SequenceCheckerDoNothing(bool attach_to_current_thread) {}
|
||||
|
||||
bool IsCurrent() const { return true; }
|
||||
|
||||
void Detach() {}
|
||||
};
|
||||
|
||||
template<typename ThreadLikeObject>
|
||||
typename std::enable_if<std::is_base_of<SequenceCheckerImpl, ThreadLikeObject>::value,
|
||||
std::string>::type
|
||||
ExpectationToString(const ThreadLikeObject *checker)
|
||||
{
|
||||
return checker->ExpectationToString();
|
||||
}
|
||||
|
||||
template<typename ThreadLikeObject>
|
||||
typename std::enable_if<!std::is_base_of<SequenceCheckerImpl, ThreadLikeObject>::value,
|
||||
std::string>::type
|
||||
ExpectationToString(const ThreadLikeObject *checker)
|
||||
{
|
||||
return std::string();
|
||||
}
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// SEQUENCE_CHECKER_INTERNAL_H
|
||||
95
include/sled/synchronization/thread_local.h
Normal file
95
include/sled/synchronization/thread_local.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* @file : thread_local
|
||||
* @created : Sunday Feb 04, 2024 18:33:04 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef THREAD_LOCAL_H
|
||||
#define THREAD_LOCAL_H
|
||||
#include <cstddef>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
|
||||
namespace sled {
|
||||
|
||||
using ThreadId = std::thread::id;
|
||||
using ThreadLocalKey = uint64_t;
|
||||
|
||||
namespace detail {
|
||||
class ThreadLocalManager final {
|
||||
public:
|
||||
static ThreadLocalManager &Instance();
|
||||
~ThreadLocalManager();
|
||||
static ThreadId CurrentThreadId();
|
||||
static ThreadLocalKey NextKey();
|
||||
void *Get(const ThreadId &thread_id, const ThreadLocalKey &key) const;
|
||||
void Delete(const ThreadId &thread_id, const ThreadLocalKey &key);
|
||||
void Delete(const ThreadLocalKey &key);
|
||||
void Set(const ThreadId &thread_id, const ThreadLocalKey &key, void *value);
|
||||
|
||||
private:
|
||||
ThreadLocalManager();
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
};
|
||||
}// namespace detail
|
||||
|
||||
template<typename T>
|
||||
class ThreadLocal final {
|
||||
public:
|
||||
ThreadLocal() : key_(detail::ThreadLocalManager::NextKey()) {}
|
||||
|
||||
~ThreadLocal()
|
||||
{ /*detail::ThreadLocalManager::Instance().Delete(key_); */
|
||||
}
|
||||
|
||||
template<typename PointerT = T>
|
||||
typename std::enable_if<std::is_pointer<PointerT>::value, PointerT>::type
|
||||
Get() const
|
||||
{
|
||||
auto ptr = detail::ThreadLocalManager::Instance().Get(
|
||||
detail::ThreadLocalManager::CurrentThreadId(), key_);
|
||||
return static_cast<PointerT>(ptr);
|
||||
}
|
||||
|
||||
template<typename IntT = T>
|
||||
typename std::enable_if<std::is_integral<IntT>::value, IntT>::type
|
||||
Get() const
|
||||
{
|
||||
static_assert(sizeof(IntT) <= sizeof(void *), "");
|
||||
auto ptr = detail::ThreadLocalManager::Instance().Get(
|
||||
detail::ThreadLocalManager::CurrentThreadId(), key_);
|
||||
return static_cast<IntT>(static_cast<char *>(ptr)
|
||||
- static_cast<char *>(0));
|
||||
}
|
||||
|
||||
template<typename PointerT = T,
|
||||
typename std::enable_if<
|
||||
std::is_pointer<PointerT>::value
|
||||
|| std::is_same<std::nullptr_t, PointerT>::value,
|
||||
PointerT>::type = nullptr>
|
||||
void Set(PointerT value)
|
||||
{
|
||||
detail::ThreadLocalManager::Instance().Set(
|
||||
detail::ThreadLocalManager::CurrentThreadId(), key_,
|
||||
static_cast<void *>(value));
|
||||
}
|
||||
|
||||
template<typename IntT = T,
|
||||
typename std::enable_if<std::is_integral<IntT>::value, IntT>::type
|
||||
* = nullptr>
|
||||
void Set(IntT value)
|
||||
{
|
||||
static_assert(sizeof(IntT) <= sizeof(void *), "");
|
||||
detail::ThreadLocalManager::Instance().Set(
|
||||
detail::ThreadLocalManager::CurrentThreadId(), key_,
|
||||
(char *) 0 + value);
|
||||
}
|
||||
|
||||
private:
|
||||
const ThreadLocalKey key_;
|
||||
};
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// THREAD_LOCAL_H
|
||||
19
include/sled/system/location.h
Normal file
19
include/sled/system/location.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @file : location
|
||||
* @created : Sunday Feb 04, 2024 20:58:26 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef LOCATION_H
|
||||
#define LOCATION_H
|
||||
|
||||
namespace sled {
|
||||
|
||||
class Location {
|
||||
public:
|
||||
static Location Current() { return Location(); }
|
||||
};
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// LOCATION_H
|
||||
191
include/sled/system/thread.h
Normal file
191
include/sled/system/thread.h
Normal file
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* @file : thread
|
||||
* @created : Sunday Feb 04, 2024 20:08:56 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef THREAD_H
|
||||
#define THREAD_H
|
||||
#include "sled/synchronization/mutex.h"
|
||||
#include "sled/synchronization/thread_local.h"
|
||||
#include "sled/task_queue/task_queue_base.h"
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
|
||||
namespace sled {
|
||||
|
||||
class SocketServer;
|
||||
class Thread;
|
||||
|
||||
class ThreadManager {
|
||||
public:
|
||||
static const int kForever = -1;
|
||||
static ThreadManager *Instance();
|
||||
static void Add(Thread *message_queue);
|
||||
static void Remove(Thread *message_queue);
|
||||
Thread *CurrentThread();
|
||||
void SetCurrentThread(Thread *thread);
|
||||
Thread *WrapCurrentThread();
|
||||
void UnwrapCurrentThread();
|
||||
|
||||
private:
|
||||
void SetCurrentThreadInternal(Thread *message_queue);
|
||||
void AddInternal(Thread *message_queue);
|
||||
void RemoveInternal(Thread *message_queue);
|
||||
void ProcessAllMessageQueueInternal();
|
||||
void ProcessAllMessageQueuesForTesting();
|
||||
ThreadManager();
|
||||
~ThreadManager();
|
||||
ThreadManager(const ThreadManager &) = delete;
|
||||
ThreadManager &operator=(const ThreadManager &) = delete;
|
||||
|
||||
std::vector<Thread *> message_queues_;
|
||||
ThreadLocal<Thread *> current_thread_;
|
||||
Mutex cirt_;
|
||||
};
|
||||
|
||||
class Thread : public TaskQueueBase {
|
||||
public:
|
||||
static const int kForever = -1;
|
||||
explicit Thread(SocketServer *);
|
||||
explicit Thread(std::unique_ptr<SocketServer>);
|
||||
Thread(SocketServer *ss, bool do_init);
|
||||
Thread(std::unique_ptr<SocketServer> ss, bool do_init);
|
||||
~Thread() override;
|
||||
Thread(const Thread &) = delete;
|
||||
Thread &operator=(const Thread &) = delete;
|
||||
|
||||
void BlockingCall(std::function<void()> functor,
|
||||
const Location &location = Location::Current())
|
||||
{
|
||||
BlockingCallImpl(functor, location);
|
||||
}
|
||||
|
||||
template<typename Functor,
|
||||
typename ReturnT = typename std::result_of<Functor()>::type,
|
||||
typename = typename std::enable_if<!std::is_void<ReturnT>::value,
|
||||
ReturnT>::type>
|
||||
ReturnT BlockingCall(Functor &&functor,
|
||||
const Location &location = Location::Current())
|
||||
{
|
||||
ReturnT result;
|
||||
BlockingCall([&] { result = std::forward<Functor>(functor)(); },
|
||||
location);
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::unique_ptr<Thread> CreateWithSocketServer();
|
||||
static std::unique_ptr<Thread> Create();
|
||||
static Thread *Current();
|
||||
|
||||
SocketServer *socketserver();
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
MutexLock lock(&mutex_);
|
||||
return messages_.empty() && delayed_messages_.empty();
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
MutexLock lock(&mutex_);
|
||||
return messages_.size() + delayed_messages_.size();
|
||||
}
|
||||
|
||||
virtual void Quit();
|
||||
virtual bool IsQuitting();
|
||||
virtual void Restart();
|
||||
virtual void Stop();
|
||||
virtual void Run();
|
||||
void Delete() override;
|
||||
bool ProcessMessages(int cmsLoop);
|
||||
bool IsOwned() const;
|
||||
bool WrapCurrent();
|
||||
void UnwrapCurrent();
|
||||
// Amount of time until the next message can be retrieved.
|
||||
virtual int GetDelay();
|
||||
bool Start();
|
||||
void Join();
|
||||
bool IsCurrent() const;
|
||||
static bool SleepMs(int millis);
|
||||
|
||||
const std::string &name() const { return name_; }
|
||||
|
||||
bool SetName(const std::string &name, const void *obj);
|
||||
|
||||
protected:
|
||||
struct DelayedMessage {
|
||||
bool operator<(const DelayedMessage &dmsg) const
|
||||
{
|
||||
return (dmsg.run_time_ms < run_time_ms)
|
||||
|| ((dmsg.run_time_ms == run_time_ms)
|
||||
&& (dmsg.message_number < message_number));
|
||||
}
|
||||
|
||||
int64_t delay_ms;
|
||||
int64_t run_time_ms;
|
||||
uint32_t message_number;
|
||||
mutable std::function<void()> functor;
|
||||
};
|
||||
|
||||
void PostTaskImpl(std::function<void()> &&task,
|
||||
const PostTaskTraits &traits,
|
||||
const Location &location) override;
|
||||
void PostDelayedTaskImpl(std::function<void()> &&task,
|
||||
TimeDelta delay,
|
||||
const PostDelayedTaskTraits &traits,
|
||||
const Location &location) override;
|
||||
virtual void BlockingCallImpl(std::function<void()> functor,
|
||||
const Location &location);
|
||||
|
||||
void DoInit();
|
||||
void DoDestroy();
|
||||
void WakeUpSocketServer();
|
||||
|
||||
private:
|
||||
std::function<void()> Get(int cmsWait);
|
||||
void Dispatch(std::function<void()> &&task);
|
||||
static void *PreRun(void *pv);
|
||||
bool WrapCurrentWithThreadManager(ThreadManager *thread_manager,
|
||||
bool need_synchronize_access);
|
||||
bool IsRunning();
|
||||
|
||||
// for ThreadManager
|
||||
void EnsureIsCurrentTaskQueue();
|
||||
void ClearCurrentTaskQueue();
|
||||
|
||||
mutable Mutex mutex_;
|
||||
std::queue<std::function<void()>> messages_;
|
||||
std::priority_queue<DelayedMessage> delayed_messages_;
|
||||
uint32_t delayed_next_num_;
|
||||
bool fInitialized_;
|
||||
bool fDestroyed_;
|
||||
std::atomic<int> stop_;
|
||||
SocketServer *const ss_;
|
||||
std::unique_ptr<SocketServer> own_ss_;
|
||||
std::string name_;
|
||||
std::unique_ptr<std::thread> thread_;
|
||||
bool owned_;
|
||||
|
||||
std::unique_ptr<TaskQueueBase::CurrentTaskQueueSetter>
|
||||
task_queue_registration_;
|
||||
friend class ThreadManager;
|
||||
};
|
||||
|
||||
class AutoSocketServerThread : public Thread {
|
||||
public:
|
||||
explicit AutoSocketServerThread(SocketServer *ss);
|
||||
~AutoSocketServerThread() override;
|
||||
|
||||
AutoSocketServerThread(const AutoSocketServerThread &) = delete;
|
||||
AutoSocketServerThread &operator=(const AutoSocketServerThread &) = delete;
|
||||
private:
|
||||
Thread* old_thread_;
|
||||
};
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// THREAD_H
|
||||
17
include/sled/system_time.h
Normal file
17
include/sled/system_time.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @file : system_time
|
||||
* @created : Saturday Feb 03, 2024 15:17:03 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef SYSTEM_TIME_H
|
||||
#define SYSTEM_TIME_H
|
||||
#include <cstdint>
|
||||
|
||||
namespace sled {
|
||||
|
||||
int64_t SystemTimeNanos();
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// SYSTEM_TIME_H
|
||||
38
include/sled/task_queue/pending_task_safety_flag.h
Normal file
38
include/sled/task_queue/pending_task_safety_flag.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @file : pending_task_safety_flag
|
||||
* @created : Wednesday Feb 14, 2024 17:55:39 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef PENDING_TASK_SAFETY_FLAG_H
|
||||
#define PENDING_TASK_SAFETY_FLAG_H
|
||||
|
||||
#include "sled/ref_counted_base.h"
|
||||
#include "sled/scoped_refptr.h"
|
||||
|
||||
namespace sled {
|
||||
|
||||
class PendingTaskSafetyFlag final
|
||||
: public sled::RefCountedNonVirtual<PendingTaskSafetyFlag> {
|
||||
public:
|
||||
static sled::scoped_refptr<PendingTaskSafetyFlag> Create();
|
||||
static sled::scoped_refptr<PendingTaskSafetyFlag> CreateDetached();
|
||||
static sled::scoped_refptr<PendingTaskSafetyFlag> CreateDetachedInactive();
|
||||
|
||||
~PendingTaskSafetyFlag() = default;
|
||||
void SetNotAlive();
|
||||
void SetAlive();
|
||||
bool alive() const;
|
||||
|
||||
protected:
|
||||
explicit PendingTaskSafetyFlag(bool alive) : alive_(alive) {}
|
||||
|
||||
private:
|
||||
static sled::scoped_refptr<PendingTaskSafetyFlag>
|
||||
CreateInternal(bool alive);
|
||||
bool alive_ = true;
|
||||
};
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// PENDING_TASK_SAFETY_FLAG_H
|
||||
94
include/sled/task_queue/task_queue_base.h
Normal file
94
include/sled/task_queue/task_queue_base.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* @file : task_queue_base
|
||||
* @created : Sunday Feb 04, 2024 20:55:06 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef TASK_QUEUE_BASE_H
|
||||
#define TASK_QUEUE_BASE_H
|
||||
|
||||
#include "sled/system/location.h"
|
||||
#include "sled/units/time_delta.h"
|
||||
#include <functional>
|
||||
|
||||
namespace sled {
|
||||
|
||||
class TaskQueueBase {
|
||||
public:
|
||||
enum class DelayPrecision {
|
||||
kLow,
|
||||
kHigh,
|
||||
};
|
||||
|
||||
struct Deleter {
|
||||
void operator()(TaskQueueBase *task_queue) const
|
||||
{
|
||||
task_queue->Delete();
|
||||
}
|
||||
};
|
||||
|
||||
virtual void Delete() = 0;
|
||||
|
||||
void PostTask(std::function<void()> &&task,
|
||||
const Location &location = Location::Current())
|
||||
{
|
||||
PostTaskImpl(std::move(task), PostTaskTraits{}, location);
|
||||
}
|
||||
|
||||
void PostDelayedTask(std::function<void()> &&task,
|
||||
TimeDelta delay,
|
||||
const Location &location = Location::Current())
|
||||
{
|
||||
PostDelayedTaskImpl(std::move(task), delay, PostDelayedTaskTraits{},
|
||||
location);
|
||||
}
|
||||
|
||||
void
|
||||
PostDelayedHighPrecisionTask(std::function<void()> &&task,
|
||||
TimeDelta delay,
|
||||
const Location &location = Location::Current())
|
||||
{
|
||||
static PostDelayedTaskTraits traits(true);
|
||||
PostDelayedTaskImpl(std::move(task), delay, traits, location);
|
||||
}
|
||||
|
||||
static TaskQueueBase *Current();
|
||||
|
||||
bool IsCurrent() const { return Current() == this; };
|
||||
|
||||
protected:
|
||||
struct PostTaskTraits {};
|
||||
|
||||
struct PostDelayedTaskTraits {
|
||||
PostDelayedTaskTraits(bool high_precision = false)
|
||||
: high_precision(high_precision)
|
||||
{}
|
||||
|
||||
bool high_precision = false;
|
||||
};
|
||||
|
||||
virtual void PostTaskImpl(std::function<void()> &&task,
|
||||
const PostTaskTraits &traits,
|
||||
const Location &location) = 0;
|
||||
virtual void PostDelayedTaskImpl(std::function<void()> &&task,
|
||||
TimeDelta delay,
|
||||
const PostDelayedTaskTraits &traits,
|
||||
const Location &location) = 0;
|
||||
virtual ~TaskQueueBase() = default;
|
||||
|
||||
class CurrentTaskQueueSetter {
|
||||
public:
|
||||
explicit CurrentTaskQueueSetter(TaskQueueBase *task_queue);
|
||||
~CurrentTaskQueueSetter();
|
||||
CurrentTaskQueueSetter(const CurrentTaskQueueSetter &) = delete;
|
||||
CurrentTaskQueueSetter &
|
||||
operator=(const CurrentTaskQueueSetter &) = delete;
|
||||
|
||||
private:
|
||||
TaskQueueBase *const previous_;
|
||||
};
|
||||
};
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// TASK_QUEUE_BASE_H
|
||||
80
include/sled/time_utils.h
Normal file
80
include/sled/time_utils.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* @file : time_units
|
||||
* @created : Saturday Feb 03, 2024 15:20:05 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef TIME_UNITS_H
|
||||
#define TIME_UNITS_H
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
namespace sled {
|
||||
static const int64_t kNumMillisecsPerSec = 1000;
|
||||
static const int64_t kNumMicrosecsPerSec = 1000000;
|
||||
static const int64_t kNumNanosecsPerSec = 1000000000;
|
||||
static const int64_t kNumMicrosecsPerMillisec = kNumMicrosecsPerSec / kNumMillisecsPerSec;
|
||||
static const int64_t kNumNanosecsPerMillisec = kNumNanosecsPerSec / kNumMillisecsPerSec;
|
||||
static const int64_t kNumNanosecsPerMicrosec = kNumNanosecsPerSec / kNumMicrosecsPerSec;
|
||||
constexpr int64_t kNtpJan1970Millisecs = 2208988800 * kNumMillisecsPerSec;
|
||||
|
||||
class ClockInterface {
|
||||
public:
|
||||
virtual ~ClockInterface() = default;
|
||||
virtual int64_t TimeNanos() const = 0;
|
||||
};
|
||||
|
||||
int64_t SystemTimeMillis();
|
||||
int64_t TimeSecs();
|
||||
int64_t TimeMillis();
|
||||
int64_t TimeMicros();
|
||||
int64_t TimeNanos();
|
||||
int64_t TimeAfterMillis(int64_t elapsed);
|
||||
int64_t TimeDiff(int64_t later, int64_t earlier);
|
||||
int32_t TimeDiff(int32_t later, int32_t earlier);
|
||||
|
||||
inline int64_t
|
||||
TimeSinceMillis(int64_t earlier)
|
||||
{
|
||||
return TimeMillis() - earlier;
|
||||
}
|
||||
|
||||
inline int64_t
|
||||
TimeSinceMicros(int64_t earlier)
|
||||
{
|
||||
return TimeMicros() - earlier;
|
||||
}
|
||||
|
||||
inline int64_t
|
||||
TimeSinceNanos(int64_t earlier)
|
||||
{
|
||||
return TimeNanos() - earlier;
|
||||
}
|
||||
|
||||
inline int64_t
|
||||
TimeUntilMillis(int64_t later)
|
||||
{
|
||||
return later - TimeMillis();
|
||||
}
|
||||
|
||||
inline int64_t
|
||||
TimeUntilMicros(int64_t later)
|
||||
{
|
||||
return later - TimeMicros();
|
||||
}
|
||||
|
||||
inline int64_t
|
||||
TimeUntilNanos(int64_t later)
|
||||
{
|
||||
return later - TimeNanos();
|
||||
}
|
||||
|
||||
int64_t TmToSeconds(const tm &tm);
|
||||
int64_t TimeUTCSeconds();
|
||||
int64_t TimeUTCMicros();
|
||||
int64_t TimeUTCMillis();
|
||||
int64_t TimeUTCNanos();
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// TIME_UNITS_H
|
||||
107
include/sled/units/time_delta.h
Normal file
107
include/sled/units/time_delta.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* @file : time_delta
|
||||
* @created : Saturday Feb 03, 2024 10:31:29 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef TIME_DELTA_H
|
||||
#define TIME_DELTA_H
|
||||
|
||||
#include "sled/units/unit_base.h"
|
||||
#include <string>
|
||||
|
||||
namespace sled {
|
||||
|
||||
class TimeDelta : public detail::RelativeUnit<TimeDelta> {
|
||||
public:
|
||||
template<typename T>
|
||||
static constexpr TimeDelta Minutes(T value)
|
||||
{
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return Seconds(value * 60);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static constexpr TimeDelta Seconds(T value)
|
||||
{
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromFraction(1000000, value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static constexpr TimeDelta Millis(T value)
|
||||
{
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromFraction(1000, value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static constexpr TimeDelta Micros(T value)
|
||||
{
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromValue(value);
|
||||
}
|
||||
|
||||
TimeDelta() = delete;
|
||||
|
||||
template<typename T = int64_t>
|
||||
constexpr T seconds() const
|
||||
{
|
||||
return ToFraction<1000000, T>();
|
||||
}
|
||||
|
||||
template<typename T = int64_t>
|
||||
constexpr T ms() const
|
||||
{
|
||||
return ToFraction<1000, T>();
|
||||
}
|
||||
|
||||
template<typename T = int64_t>
|
||||
constexpr T us() const
|
||||
{
|
||||
return ToValue<T>();
|
||||
}
|
||||
|
||||
template<typename T = int64_t>
|
||||
constexpr T ns() const
|
||||
{
|
||||
return ToMultiple<1000, T>();
|
||||
}
|
||||
|
||||
constexpr int64_t seconds_or(int64_t fallback_value) const
|
||||
{
|
||||
return ToFractionOr<1000000>(fallback_value);
|
||||
}
|
||||
|
||||
constexpr int64_t ms_or(int64_t fallback_value) const
|
||||
{
|
||||
return ToFractionOr<1000>(fallback_value);
|
||||
}
|
||||
|
||||
constexpr int64_t us_or(int64_t fallback_value) const { return ToValueOr(fallback_value); }
|
||||
|
||||
constexpr TimeDelta Abs() const { return us() < 0 ? TimeDelta::Micros(-us()) : *this; }
|
||||
|
||||
private:
|
||||
friend class detail::UnitBase<TimeDelta>;
|
||||
using RelativeUnit::RelativeUnit;
|
||||
static constexpr bool one_sided = false;
|
||||
};
|
||||
|
||||
std::string ToString(TimeDelta value);
|
||||
|
||||
inline std::string
|
||||
ToLogString(TimeDelta value)
|
||||
{
|
||||
return ToString(value);
|
||||
}
|
||||
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream &stream, TimeDelta value)
|
||||
{
|
||||
return stream << ToString(value);
|
||||
}
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// TIME_DELTA_H
|
||||
147
include/sled/units/timestamp.h
Normal file
147
include/sled/units/timestamp.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* @file : timestamp
|
||||
* @created : Saturday Feb 03, 2024 10:52:15 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef TIMESTAMP_H
|
||||
#define TIMESTAMP_H
|
||||
#include "sled/units/time_delta.h"
|
||||
#include "sled/units/unit_base.h"
|
||||
|
||||
namespace sled {
|
||||
|
||||
class Timestamp final : public detail::UnitBase<Timestamp> {
|
||||
public:
|
||||
template<typename T>
|
||||
static constexpr Timestamp Seconds(T value)
|
||||
{
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromFraction(1000000, value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static constexpr Timestamp Millis(T value)
|
||||
{
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromFraction(1000, value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static constexpr Timestamp Micros(T value)
|
||||
{
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromValue(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static constexpr Timestamp Nanos(T value)
|
||||
{
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromValue(value) * 1000;
|
||||
}
|
||||
|
||||
Timestamp() = delete;
|
||||
|
||||
template<typename T = int64_t>
|
||||
constexpr T seconds() const
|
||||
{
|
||||
return ToFraction<1000000, T>();
|
||||
}
|
||||
|
||||
template<typename T = int64_t>
|
||||
constexpr T ms() const
|
||||
{
|
||||
return ToFraction<1000, T>();
|
||||
}
|
||||
|
||||
template<typename T = int64_t>
|
||||
constexpr T us() const
|
||||
{
|
||||
return ToValue<T>();
|
||||
}
|
||||
|
||||
template<typename T = int64_t>
|
||||
constexpr T ns()
|
||||
{
|
||||
return ToMultiple<1000, T>();
|
||||
}
|
||||
|
||||
constexpr int64_t seconds_or(int64_t fallback_value) const
|
||||
{
|
||||
return ToFractionOr<1000000>(fallback_value);
|
||||
}
|
||||
|
||||
constexpr int64_t ms_or(int64_t fallback_value) const
|
||||
{
|
||||
return ToFractionOr<1000>(fallback_value);
|
||||
}
|
||||
|
||||
constexpr int64_t us_or(int64_t fallback_value) const { return ToValueOr(fallback_value); }
|
||||
|
||||
Timestamp operator+(const TimeDelta delta) const
|
||||
{
|
||||
if (IsPlusInfinity() || delta.IsPlusInfinity()) {
|
||||
return PlusInfinity();
|
||||
} else if (IsMinusInfinity() || delta.IsMinusInfinity()) {
|
||||
return MinusInfinity();
|
||||
}
|
||||
return Timestamp::Micros(us() - delta.us());
|
||||
}
|
||||
|
||||
Timestamp operator-(const TimeDelta delta) const
|
||||
{
|
||||
if (IsPlusInfinity() || delta.IsMinusInfinity()) {
|
||||
return PlusInfinity();
|
||||
} else if (IsMinusInfinity() || delta.IsPlusInfinity()) {
|
||||
return MinusInfinity();
|
||||
}
|
||||
return Timestamp::Micros(us() - delta.us());
|
||||
}
|
||||
|
||||
TimeDelta operator-(const Timestamp other) const
|
||||
{
|
||||
if (IsPlusInfinity() || other.IsMinusInfinity()) {
|
||||
return TimeDelta::PlusInfinity();
|
||||
} else if (IsMinusInfinity() || other.IsPlusInfinity()) {
|
||||
return TimeDelta::MinusInfinity();
|
||||
}
|
||||
return TimeDelta::Micros(us() - other.us());
|
||||
}
|
||||
|
||||
Timestamp &operator-=(const TimeDelta delta)
|
||||
{
|
||||
*this = *this - delta;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Timestamp &operator+=(const TimeDelta delta)
|
||||
{
|
||||
*this = *this + delta;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class detail::UnitBase<Timestamp>;
|
||||
using UnitBase::UnitBase;
|
||||
static constexpr bool one_sided = true;
|
||||
};
|
||||
|
||||
std::string ToString(Timestamp value);
|
||||
|
||||
inline std::string
|
||||
ToLogString(Timestamp value)
|
||||
{
|
||||
return ToString(value);
|
||||
}
|
||||
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream &stream, Timestamp value)
|
||||
{
|
||||
stream << ToString(value);
|
||||
return stream;
|
||||
}
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// TIMESTAMP_H
|
||||
314
include/sled/units/unit_base.h
Normal file
314
include/sled/units/unit_base.h
Normal file
@@ -0,0 +1,314 @@
|
||||
/**
|
||||
* @file : unit_base
|
||||
* @created : Friday Feb 02, 2024 17:46:50 CST
|
||||
* @license : MIT
|
||||
**/
|
||||
|
||||
#ifndef UNIT_BASE_H
|
||||
#define UNIT_BASE_H
|
||||
|
||||
#include "sled/numerics/divide_round.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <stdint.h>
|
||||
#include <type_traits>
|
||||
|
||||
namespace sled {
|
||||
namespace detail {
|
||||
|
||||
template<typename Unit_T>
|
||||
class UnitBase {
|
||||
public:
|
||||
UnitBase() = delete;
|
||||
|
||||
static constexpr Unit_T Zero() { return Unit_T(0); }
|
||||
|
||||
static constexpr Unit_T PlusInfinity() { return Unit_T(PlusInfinityVal()); }
|
||||
|
||||
static constexpr Unit_T MinusInfinity() { return Unit_T(MinusInfinityVal()); }
|
||||
|
||||
constexpr bool IsZero() const { return value_ == 0; }
|
||||
|
||||
constexpr bool IsFinite() const { return !IsInfinite(); }
|
||||
|
||||
constexpr bool IsInfinite() const
|
||||
{
|
||||
return value_ == PlusInfinityVal() || value_ == MinusInfinityVal();
|
||||
}
|
||||
|
||||
constexpr bool IsPlusInfinity() const { return value_ == PlusInfinityVal(); }
|
||||
|
||||
constexpr bool IsMinusInfinity() const { return value_ == MinusInfinityVal(); }
|
||||
|
||||
constexpr bool operator==(const Unit_T &other) const { return value_ == other.value_; }
|
||||
|
||||
constexpr bool operator!=(const Unit_T &other) const { return value_ != other.value_; }
|
||||
|
||||
constexpr bool operator<(const Unit_T &other) const { return value_ < other.value_; }
|
||||
|
||||
constexpr bool operator<=(const Unit_T &other) const { return value_ <= other.value_; }
|
||||
|
||||
constexpr bool operator>(const Unit_T &other) const { return value_ > other.value_; }
|
||||
|
||||
constexpr bool operator>=(const Unit_T &other) const { return value_ >= other.value_; }
|
||||
|
||||
constexpr Unit_T RoundTo(const Unit_T &resolution) const
|
||||
{
|
||||
// ASSERT IsFinite();
|
||||
// ASSERT resolution.IsFinite();
|
||||
// ASSERT resolution.value_ > 0;
|
||||
return Unit_T((value_ + resolution.value_ / 2) / resolution.value_) * resolution.value_;
|
||||
}
|
||||
|
||||
constexpr Unit_T RoundUpTo(const Unit_T &resolution) const
|
||||
{
|
||||
// ASSERT IsFinite();
|
||||
// ASSERT resolution.IsFinite();
|
||||
// ASSERT resolution.value_ > 0;
|
||||
return Unit_T((value_ + resolution.value_ - 1) / resolution.value_) * resolution.value_;
|
||||
}
|
||||
|
||||
constexpr Unit_T RoundDownTo(const Unit_T &resolution) const
|
||||
{
|
||||
// ASSERT IsFinite();
|
||||
// ASSERT resolution.IsFinite();
|
||||
// ASSERT resolution.value_ > 0;
|
||||
return Unit_T(value_ / resolution.value_) * resolution.value_;
|
||||
}
|
||||
|
||||
protected:
|
||||
template<typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
|
||||
static constexpr Unit_T FromValue(T value)
|
||||
{
|
||||
return Unit_T(static_cast<int64_t>(value));
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
|
||||
static /*constexpr*/ Unit_T FromValue(T value)
|
||||
{
|
||||
if (value == std::numeric_limits<T>::infinity()) {
|
||||
return PlusInfinity();
|
||||
} else if (value == -std::numeric_limits<T>::infinity()) {
|
||||
return MinusInfinity();
|
||||
} else {
|
||||
return FromValue(static_cast<int64_t>(value));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
|
||||
static constexpr Unit_T FromFraction(int64_t denominator, T value)
|
||||
{
|
||||
return Unit_T(static_cast<int64_t>(value * denominator));
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
|
||||
static constexpr Unit_T FromFraction(int64_t denominator, T value)
|
||||
{
|
||||
return FromValue(value * denominator);
|
||||
}
|
||||
|
||||
template<typename T = int64_t>
|
||||
constexpr typename std::enable_if<std::is_integral<T>::value, T>::type ToValue() const
|
||||
{
|
||||
return IsPlusInfinity() ? std::numeric_limits<T>::infinity()
|
||||
: IsMinusInfinity() ? -std::numeric_limits<T>::infinity()
|
||||
: value_;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type ToValue() const
|
||||
{
|
||||
return IsPlusInfinity() ? std::numeric_limits<T>::infinity()
|
||||
: IsMinusInfinity() ? -std::numeric_limits<T>::infinity()
|
||||
: value_;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T ToValueOr(T fallback_value) const
|
||||
{
|
||||
return IsFinite() ? value_ : fallback_value;
|
||||
}
|
||||
|
||||
template<int64_t Denominator, typename T = int64_t>
|
||||
constexpr typename std::enable_if<std::is_integral<T>::value, T>::type ToFraction() const
|
||||
{
|
||||
return static_cast<T>(DivideRoundToNearst(value_, Denominator));
|
||||
}
|
||||
|
||||
template<int64_t Denominator, typename T = int64_t>
|
||||
constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type ToFraction() const
|
||||
{
|
||||
return ToValue<T>() * (1 / static_cast<T>(Denominator));
|
||||
}
|
||||
|
||||
template<int64_t Denominator>
|
||||
constexpr int64_t ToFractionOr(int64_t fallback_value) const
|
||||
{
|
||||
return IsFinite() ? DivideRoundToNearst(value_, Denominator) : fallback_value;
|
||||
}
|
||||
|
||||
template<int64_t Factor, typename T = int64_t>
|
||||
constexpr typename std::enable_if<std::is_integral<T>::value, T>::type ToMultiple() const
|
||||
{
|
||||
return static_cast<T>(ToValue() * Factor);
|
||||
}
|
||||
|
||||
template<int64_t Factor, typename T>
|
||||
constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type ToMultiple() const
|
||||
{
|
||||
return ToValue() * Factor;
|
||||
}
|
||||
|
||||
explicit constexpr UnitBase(int64_t value) : value_(value) {}
|
||||
|
||||
private:
|
||||
template<typename RelativeUnit_T>
|
||||
friend class RelativeUnit;
|
||||
|
||||
static inline constexpr int64_t PlusInfinityVal()
|
||||
{
|
||||
return std::numeric_limits<int64_t>::max();
|
||||
}
|
||||
|
||||
static inline constexpr int64_t MinusInfinityVal()
|
||||
{
|
||||
return std::numeric_limits<int64_t>::min();
|
||||
}
|
||||
|
||||
Unit_T &AsSubClassRef() { return static_cast<Unit_T &>(*this); }
|
||||
|
||||
constexpr const Unit_T &AsSubClassRef() const { return static_cast<const Unit_T &>(*this); }
|
||||
|
||||
int64_t value_;
|
||||
};
|
||||
|
||||
template<typename Unit_T>
|
||||
class RelativeUnit : public UnitBase<Unit_T> {
|
||||
public:
|
||||
constexpr Unit_T Clamped(Unit_T min_value, Unit_T max_value) const
|
||||
{
|
||||
return std::max(min_value, std::min(max_value, Unit_T()));
|
||||
}
|
||||
|
||||
void Clamp(Unit_T min_value, Unit_T max_value) { *this = Clamped(min_value, max_value); }
|
||||
|
||||
Unit_T operator+(const Unit_T other) const
|
||||
{
|
||||
if (this->IsPlusInfinity() || other.IsPlusInfinity()) {
|
||||
return this->PlusInfinity();
|
||||
} else if (this->IsMinusInfinity() || other->IsMinusInfinity()) {
|
||||
return this->MinusInfinity();
|
||||
}
|
||||
return UnitBase<Unit_T>::FromValue(this->ToValue() + other.ToValue());
|
||||
}
|
||||
|
||||
Unit_T operator-(const Unit_T other) const
|
||||
{
|
||||
if (this->IsPlusInfinity() || other.IsMinusInfinity()) {
|
||||
return this->PlusInfinity();
|
||||
} else if (this->IsMinusInfinity() || other.IsPlusInfinity()) {
|
||||
return this->MinusInfinity();
|
||||
}
|
||||
return UnitBase<Unit_T>::FromValue(this->ToValue() - other.ToValue());
|
||||
}
|
||||
|
||||
Unit_T &operator+=(const Unit_T other)
|
||||
{
|
||||
*this = *this + other;
|
||||
return this->AsSubClassRef();
|
||||
}
|
||||
|
||||
Unit_T &operator-=(const Unit_T other)
|
||||
{
|
||||
*this = *this - other;
|
||||
return this->AsSubClassRef();
|
||||
}
|
||||
|
||||
constexpr double operator/(const Unit_T other) const
|
||||
{
|
||||
return UnitBase<Unit_T>::template ToValue<double>() / other.template ToValue<double>();
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
typename std::enable_if<std::is_floating_point<T>::value, T>::type * = nullptr>
|
||||
constexpr Unit_T operator/(T scalar) const
|
||||
{
|
||||
return UnitBase<Unit_T>::FromValue(std::llround(this->ToValue() / scalar));
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if<std::is_integral<T>::value, T>::type * = nullptr>
|
||||
constexpr Unit_T operator/(T scalar) const
|
||||
{
|
||||
return UnitBase<Unit_T>::FromValue(this->ToValue() / scalar);
|
||||
}
|
||||
|
||||
constexpr Unit_T operator*(double scalar) const
|
||||
{
|
||||
return UnitBase<Unit_T>::FromValue(std::llround(this->ToValue() * scalar));
|
||||
}
|
||||
|
||||
constexpr Unit_T operator*(int64_t scalar) const
|
||||
{
|
||||
return UnitBase<Unit_T>::FromValue(this->ToValue() * scalar);
|
||||
}
|
||||
|
||||
constexpr Unit_T operator*(int32_t scalar) const
|
||||
{
|
||||
return UnitBase<Unit_T>::FromValue(this->ToValue() * scalar);
|
||||
}
|
||||
|
||||
constexpr Unit_T operator*(size_t scalar) const
|
||||
{
|
||||
return UnitBase<Unit_T>::FromValue(this->ToValue() * scalar);
|
||||
}
|
||||
|
||||
protected:
|
||||
// use UnitBase(value) as constructor
|
||||
using UnitBase<Unit_T>::UnitBase;
|
||||
};
|
||||
|
||||
template<typename Unit_T>
|
||||
inline constexpr Unit_T
|
||||
operator*(double scalar, RelativeUnit<Unit_T> other)
|
||||
{
|
||||
return other * scalar;
|
||||
}
|
||||
|
||||
template<typename Unit_T>
|
||||
inline constexpr Unit_T
|
||||
operator*(int64_t scalar, RelativeUnit<Unit_T> other)
|
||||
{
|
||||
return other * scalar;
|
||||
}
|
||||
|
||||
template<typename Unit_T>
|
||||
inline constexpr Unit_T
|
||||
operator*(int32_t scalar, RelativeUnit<Unit_T> other)
|
||||
{
|
||||
return other * scalar;
|
||||
}
|
||||
|
||||
template<typename Unit_T>
|
||||
inline constexpr Unit_T
|
||||
operator*(size_t scalar, RelativeUnit<Unit_T> other)
|
||||
{
|
||||
return other * scalar;
|
||||
}
|
||||
|
||||
template<typename Unit_T>
|
||||
inline Unit_T
|
||||
operator-(RelativeUnit<Unit_T> other)
|
||||
{
|
||||
if (other.IsPlusInfinity()) { return UnitBase<Unit_T>::MinusInfinity(); }
|
||||
if (other.IsMinusInfinity()) { return UnitBase<Unit_T>::PlusInfinity(); }
|
||||
return -1 * other;
|
||||
}
|
||||
}// namespace detail
|
||||
|
||||
}// namespace sled
|
||||
|
||||
#endif// UNIT_BASE_H
|
||||
3041
include/sled/variant.h
Normal file
3041
include/sled/variant.h
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user