// This file Copyright 2022 Mnemosyne LLC. // It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only), // or any future license endorsed by Mnemosyne LLC. // License text can be found in the licenses/ folder. #pragma once #include #include #include #include #include #include #include #include "error.h" #include "net.h" // tr_socket_t #include "utils-ev.h" #include "utils.h" namespace libtransmission { class Buffer { public: using Iovec = evbuffer_iovec; class Iterator { public: using difference_type = long; using value_type = std::byte; using pointer = value_type*; using reference = value_type&; using iterator_category = std::random_access_iterator_tag; Iterator(evbuffer* buf, size_t offset) : buf_{ buf } { setOffset(offset); } [[nodiscard]] value_type& operator*() noexcept { return *reinterpret_cast(iov_.iov_base); } [[nodiscard]] value_type operator*() const noexcept { return *reinterpret_cast(iov_.iov_base); } [[nodiscard]] Iterator operator+(int n_bytes) { return Iterator(buf_, offset_ + n_bytes); } [[nodiscard]] Iterator operator-(int n_bytes) { return Iterator(buf_, offset_ - n_bytes); } [[nodiscard]] constexpr auto operator-(Iterator const& that) const noexcept { return offset_ - that.offset_; } Iterator& operator++() noexcept { if (iov_.iov_len > 1) { iov_.iov_base = reinterpret_cast(iov_.iov_base) + 1; --iov_.iov_len; ++offset_; } else { setOffset(offset_ + 1); } return *this; } Iterator& operator+=(int n_bytes) { setOffset(offset_ + n_bytes); return *this; } Iterator& operator--() noexcept { // TODO(ckerr) inefficient; calls evbuffer_ptr_peek() every time setOffset(offset_ - 1); return *this; } [[nodiscard]] constexpr bool operator==(Iterator const& that) const noexcept { return offset_ == that.offset_; } [[nodiscard]] constexpr bool operator!=(Iterator const& that) const noexcept { return !(*this == that); } private: void setOffset(size_t offset) { offset_ = offset; auto ptr = evbuffer_ptr{}; evbuffer_ptr_set(buf_, &ptr, offset, EVBUFFER_PTR_SET); evbuffer_peek(buf_, std::numeric_limits::max(), &ptr, &iov_, 1); } evbuffer* buf_; Iovec iov_ = {}; size_t offset_ = 0; }; Buffer() = default; Buffer(Buffer&&) = default; Buffer(Buffer const&) = delete; Buffer& operator=(Buffer const&) = delete; Buffer& operator=(Buffer&&) = default; template explicit Buffer(T const& data) { add(std::data(data), std::size(data)); } [[nodiscard]] auto size() const noexcept { return evbuffer_get_length(buf_.get()); } [[nodiscard]] auto empty() const noexcept { return evbuffer_get_length(buf_.get()) == 0; } [[nodiscard]] auto vecs(size_t n_bytes) const { auto chains = std::vector(evbuffer_peek(buf_.get(), n_bytes, nullptr, nullptr, 0)); evbuffer_peek(buf_.get(), n_bytes, nullptr, std::data(chains), std::size(chains)); return chains; } [[nodiscard]] auto vecs() const { return vecs(size()); } [[nodiscard]] auto begin() noexcept { return Iterator(buf_.get(), 0U); } [[nodiscard]] auto end() noexcept { return Iterator(buf_.get(), size()); } [[nodiscard]] auto begin() const noexcept { return Iterator(buf_.get(), 0U); } [[nodiscard]] auto end() const noexcept { return Iterator(buf_.get(), size()); } [[nodiscard]] auto cbegin() const noexcept { return Iterator(buf_.get(), 0U); } [[nodiscard]] auto cend() const noexcept { return Iterator(buf_.get(), size()); } template [[nodiscard]] bool startsWith(T const& needle) const { auto const n_bytes = std::size(needle); auto const needle_begin = reinterpret_cast(std::data(needle)); auto const needle_end = needle_begin + n_bytes; return n_bytes <= size() && std::equal(needle_begin, needle_end, cbegin()); } auto toBuf(void* tgt, size_t n_bytes) { return evbuffer_remove(buf_.get(), tgt, n_bytes); } [[nodiscard]] uint16_t toUint16() { auto tmp = uint16_t{}; toBuf(&tmp, sizeof(tmp)); return ntohs(tmp); } [[nodiscard]] uint32_t toUint32() { auto tmp = uint32_t{}; toBuf(&tmp, sizeof(tmp)); return ntohl(tmp); } [[nodiscard]] uint64_t toUint64() { auto tmp = uint64_t{}; toBuf(&tmp, sizeof(tmp)); return tr_ntohll(tmp); } void drain(size_t n_bytes) { evbuffer_drain(buf_.get(), n_bytes); } void clear() { drain(size()); } // Returns the number of bytes written. Check `error` for error. size_t toSocket(tr_socket_t sockfd, size_t n_bytes, tr_error** error = nullptr) { EVUTIL_SET_SOCKET_ERROR(0); auto const res = evbuffer_write_atmost(buf_.get(), sockfd, n_bytes); auto const err = EVUTIL_SOCKET_ERROR(); if (res >= 0) { return static_cast(res); } tr_error_set(error, err, tr_net_strerror(err)); return 0; } [[nodiscard]] Iovec alloc(size_t n_bytes) { auto iov = Iovec{}; evbuffer_reserve_space(buf_.get(), static_cast(n_bytes), &iov, 1); return iov; } void commit(Iovec iov) { evbuffer_commit_space(buf_.get(), &iov, 1); } void reserve(size_t n_bytes) { evbuffer_expand(buf_.get(), n_bytes - size()); } // -1 on error, 0 on eof, >0 for num bytes read auto addSocket(tr_socket_t sockfd, size_t n_bytes, tr_error** error = nullptr) { EVUTIL_SET_SOCKET_ERROR(0); auto const res = evbuffer_read(buf_.get(), sockfd, static_cast(n_bytes)); auto const err = EVUTIL_SOCKET_ERROR(); if (res == -1) { tr_error_set(error, err, tr_net_strerror(err)); } return res; } // Move all data from one buffer into another. // This is a destructive add: the source buffer is empty after this call. void add(Buffer& that) { evbuffer_add_buffer(buf_.get(), that.buf_.get()); } void add(Buffer&& that) { evbuffer_add_buffer(buf_.get(), that.buf_.get()); } void add(void const* bytes, size_t n_bytes) { evbuffer_add(buf_.get(), bytes, n_bytes); } template void add(T const& data) { add(std::data(data), std::size(data)); } template< typename T, typename std::enable_if_t< std::is_same_v || std::is_same_v || std::is_same_v>* = nullptr> void push_back(T ch) { add(&ch, 1); } void addPort(tr_port const& port) { auto nport = port.network(); add(&nport, sizeof(nport)); } void addUint8(uint8_t uch) { add(&uch, 1); } void addUint16(uint16_t hs) { uint16_t const ns = htons(hs); add(&ns, sizeof(ns)); } void addHton16(uint16_t hs) { addUint16(hs); } void addUint32(uint32_t hl) { uint32_t const nl = htonl(hl); add(&nl, sizeof(nl)); } void addHton32(uint32_t hl) { addUint32(hl); } void addUint64(uint64_t hll) { uint64_t const nll = tr_htonll(hll); add(&nll, sizeof(nll)); } void addHton64(uint64_t hll) { addUint64(hll); } [[nodiscard]] std::string toString() const { auto str = std::string{}; str.reserve(size()); for (auto const& by : *this) { str.push_back(*reinterpret_cast(&by)); } return str; } private: evhelpers::evbuffer_unique_ptr buf_{ evbuffer_new() }; }; } // namespace libtransmission