/// @file dpf/net/buffer_pool.hpp /// @brief Recycled byte buffers for framed writes. /// @details `acquire_buffer` hands out a `shared_ptr` whose deleter returns the /// vector to a process-wide free list (capped). Callers that already /// own a buffer pass it to `async_write_owned` so the socket path can /// scatter-gather without a second payload copy. #ifndef LIBDPF_INCLUDE_DPF_NET_BUFFER_POOL_HPP__ #define LIBDPF_INCLUDE_DPF_NET_BUFFER_POOL_HPP__ #include #include #include #include #include #include #include #include "dpf/net/policy.hpp" namespace dpf { namespace net { /// @brief Default outstanding-byte window (see `wire_policy::window_bytes`). inline std::size_t default_wire_window() noexcept { return wire_policy{}.window_bytes; } /// @brief Default receive cap (see `wire_policy::max_frame`). inline constexpr std::size_t k_max_frame = std::size_t{16} << 20; namespace detail { struct pool_state { std::mutex mu; std::vector>> free; }; inline pool_state & buffers() { static pool_state s; return s; } inline void recycle(std::vector * p) { std::unique_ptr> owned(p); owned->clear(); if (owned->capacity() > (std::size_t{1} << 20)) return; auto & st = buffers(); std::lock_guard lock(st.mu); if (st.free.size() < 128) st.free.push_back(std::move(owned)); } } // namespace detail /// @brief Buffer of at least `n` bytes, returned to the pool on last release. inline std::shared_ptr> acquire_buffer(std::size_t n) { std::unique_ptr> raw; { auto & st = detail::buffers(); std::lock_guard lock(st.mu); if (!st.free.empty()) { raw = std::move(st.free.back()); st.free.pop_back(); } } if (!raw) raw.reset(new std::vector()); if (raw->capacity() < n) raw->reserve(n); raw->resize(n); std::vector * p = raw.release(); return std::shared_ptr>(p, &detail::recycle); } /// @brief Copy `n` bytes into a pooled buffer. inline std::shared_ptr> copy_buffer(const void * src, std::size_t n) { auto buf = acquire_buffer(n); if (n != 0 && src != nullptr) std::memcpy(buf->data(), src, n); return buf; } } // namespace net } // namespace dpf #endif