init repo.

This commit is contained in:
tqcq
2024-02-23 18:07:37 +08:00
commit 1a9e41d167
512 changed files with 191774 additions and 0 deletions

336
include/sled/any.h Normal file
View 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
View 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
View 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
View 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
View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

59
include/sled/random.h Normal file
View 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
View 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

View 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

View 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

View 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

View 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

View 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
View 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
View 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
View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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
View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff