/* * Copyright (c) 2013-2025, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * * See full license text in LICENSE file at top of project tree */ #ifndef UTIL_H #define UTIL_H #include #include #include #include #include #include #include #include #include #include #ifdef ANDROID #ifndef __clang__ #include namespace std { template std::string to_string(T value) { return boost::lexical_cast(value); } inline int stoi(const std::string& str) { return boost::lexical_cast(str); } } #endif #endif namespace i2p { namespace util { template class MemoryPool { //BOOST_STATIC_ASSERT_MSG(sizeof(T) >= sizeof(void*), "size cannot be less that general pointer size"); public: MemoryPool (): m_Head (nullptr) {} ~MemoryPool () { CleanUp (); } void CleanUp () { CleanUp (m_Head); m_Head = nullptr; } template T * Acquire (TArgs&&... args) { if (!m_Head) return new T(std::forward(args)...); else { auto tmp = m_Head; m_Head = static_cast(*(void * *)m_Head); // next return new (tmp)T(std::forward(args)...); } } void Release (T * t) { if (!t) return; t->~T (); *(void * *)t = m_Head; // next m_Head = t; } template std::unique_ptr > AcquireUnique (TArgs&&... args) { return std::unique_ptr >(Acquire (std::forward(args)...), std::bind (&MemoryPool::Release, this, std::placeholders::_1)); } template std::shared_ptr AcquireShared (TArgs&&... args) { return std::shared_ptr(Acquire (std::forward(args)...), std::bind (&MemoryPool::Release, this, std::placeholders::_1)); } protected: void CleanUp (T * head) { while (head) { auto tmp = head; head = static_cast(*(void * *)head); // next ::operator delete ((void *)tmp); } } protected: T * m_Head; }; template class MemoryPoolMt: private MemoryPool { public: MemoryPoolMt () {} template T * AcquireMt (TArgs&&... args) { if (!this->m_Head) return new T(std::forward(args)...); std::lock_guard l(m_Mutex); return this->Acquire (std::forward(args)...); } void ReleaseMt (T * t) { std::lock_guard l(m_Mutex); this->Release (t); } void ReleaseMt (T * * arr, size_t num) { if (!arr || !num) return; std::lock_guard l(m_Mutex); for (size_t i = 0; i < num; i++) this->Release (arr[i]); } templateclass C, typename... R> void ReleaseMt(const C& c) { std::lock_guard l(m_Mutex); for (auto& it: c) this->Release (it); } template std::shared_ptr AcquireSharedMt (TArgs&&... args) { return std::shared_ptr(AcquireMt (std::forward(args)...), std::bind::*)(T *)> (&MemoryPoolMt::ReleaseMt, this, std::placeholders::_1)); } void CleanUpMt () { T * head; { std::lock_guard l(m_Mutex); head = this->m_Head; this->m_Head = nullptr; } if (head) this->CleanUp (head); } private: std::mutex m_Mutex; }; class RunnableService { protected: RunnableService (const std::string& name): m_Name (name), m_IsRunning (false) {} virtual ~RunnableService () {} auto& GetIOService () { return m_Service; } bool IsRunning () const { return m_IsRunning; }; void StartIOService (); void StopIOService (); void SetName (std::string_view name); private: void Run (); private: std::string m_Name; volatile bool m_IsRunning; std::unique_ptr m_Thread; boost::asio::io_context m_Service; }; class RunnableServiceWithWork: public RunnableService { protected: RunnableServiceWithWork (const std::string& name): RunnableService (name), m_Work (GetIOService ().get_executor ()) {} private: boost::asio::executor_work_guard m_Work; }; void SetThreadName (const char *name); template class SaveStateHelper { public: SaveStateHelper (T& orig): m_Original (orig), m_Copy (orig) {}; ~SaveStateHelper () { m_Original = m_Copy; }; private: T& m_Original; T m_Copy; }; class Mapping { public: Mapping () = default; Mapping (const Mapping& ) = default; Mapping (Mapping&& ) = default; Mapping (std::initializer_list > options): m_Options (options) {} size_t FromBuffer (const uint8_t * buf, size_t len); size_t FromBuffer (size_t size, const uint8_t * buf, size_t len); //without 2 bytes size size_t ToBuffer (uint8_t * buf, size_t len) const; std::string_view operator[](std::string_view param) const; bool Insert (std::string_view param, std::string_view value); bool Contains (std::string_view param) const; void CleanUp (); bool IsEmpty () const { return m_Options.empty (); } static std::string_view ExtractString (const uint8_t * buf, size_t len); static size_t WriteString (std::string_view str, uint8_t * buf, size_t len); static size_t WriteOption (std::string_view param, std::string_view value, uint8_t * buf, size_t len); template bool Get(std::string_view param, T& value) const { auto s = (*this)[param]; if (s.empty ()) return false; auto res = std::from_chars(s.data(), s.data() + s.size(), value); return res.ec == std::errc(); } bool Get(std::string_view param, bool& value) const { auto s = (*this)[param]; if (s.empty ()) return false; return GetBoolParamValue (s, value); } template bool Put (std::string_view param, T value) { return Insert (param, std::to_string (value)); } private: static bool GetBoolParamValue (std::string_view s, bool& value); private: std::map > m_Options; public: const decltype(m_Options)& GetOptions () const { return m_Options; } }; namespace net { int GetMTU (const boost::asio::ip::address& localAddress); int GetMaxMTU (const boost::asio::ip::address_v6& localAddress); // check tunnel broker for ipv6 address const boost::asio::ip::address GetInterfaceAddress (const std::string & ifname, bool ipv6=false); boost::asio::ip::address_v6 GetYggdrasilAddress (); boost::asio::ip::address_v6 GetClearnetIPV6Address (); bool IsLocalAddress (const boost::asio::ip::address& addr); bool IsInReservedRange (const boost::asio::ip::address& host); bool IsYggdrasilAddress (const boost::asio::ip::address& addr); bool IsPortInReservedRange (const uint16_t port) noexcept; } } } #endif