mirror of
https://github.com/transmission/transmission.git
synced 2025-12-20 02:18:42 +00:00
refactor: add BufferReader, BufferWriter::reserve_space() (#5513)
This commit is contained in:
@@ -227,8 +227,7 @@ struct tau_announce_request
|
|||||||
response.leechers = buf.to_uint32();
|
response.leechers = buf.to_uint32();
|
||||||
response.seeders = buf.to_uint32();
|
response.seeders = buf.to_uint32();
|
||||||
|
|
||||||
auto const [bytes, n_bytes] = buf.pullup();
|
response.pex = tr_pex::from_compact_ipv4(std::data(buf), std::size(buf), nullptr, 0);
|
||||||
response.pex = tr_pex::from_compact_ipv4(bytes, n_bytes, nullptr, 0);
|
|
||||||
requestFinished();
|
requestFinished();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -378,8 +377,7 @@ struct tau_tracker
|
|||||||
buf.add_uint32(TAU_ACTION_CONNECT);
|
buf.add_uint32(TAU_ACTION_CONNECT);
|
||||||
buf.add_uint32(this->connection_transaction_id);
|
buf.add_uint32(this->connection_transaction_id);
|
||||||
|
|
||||||
auto const [bytes, n_bytes] = buf.pullup();
|
this->sendto(std::data(buf), std::size(buf));
|
||||||
this->sendto(bytes, n_bytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timeout_reqs)
|
if (timeout_reqs)
|
||||||
@@ -539,8 +537,7 @@ private:
|
|||||||
buf.add_uint64(this->connection_id);
|
buf.add_uint64(this->connection_id);
|
||||||
buf.add(payload, payload_len);
|
buf.add(payload, payload_len);
|
||||||
|
|
||||||
auto const [bytes, n_bytes] = buf.pullup();
|
this->sendto(std::data(buf), std::size(buf));
|
||||||
this->sendto(bytes, n_bytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -575,30 +575,6 @@ size_t tr_peerIo::get_write_buffer_space(uint64_t now) const noexcept
|
|||||||
return desired_len > current_len ? desired_len - current_len : 0U;
|
return desired_len > current_len ? desired_len - current_len : 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tr_peerIo::write(libtransmission::Buffer& buf, bool is_piece_data)
|
|
||||||
{
|
|
||||||
auto [bytes, len] = buf.pullup();
|
|
||||||
encrypt(len, bytes);
|
|
||||||
outbuf_info_.emplace_back(std::size(buf), is_piece_data);
|
|
||||||
outbuf_.add(buf);
|
|
||||||
buf.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void tr_peerIo::write_bytes(void const* bytes, size_t n_bytes, bool is_piece_data)
|
|
||||||
{
|
|
||||||
auto const old_size = std::size(outbuf_);
|
|
||||||
|
|
||||||
outbuf_.reserve(old_size + n_bytes);
|
|
||||||
outbuf_.add(bytes, n_bytes);
|
|
||||||
|
|
||||||
for (auto iter = std::begin(outbuf_) + old_size, end = std::end(outbuf_); iter != end; ++iter)
|
|
||||||
{
|
|
||||||
encrypt(1, &*iter);
|
|
||||||
}
|
|
||||||
|
|
||||||
outbuf_info_.emplace_back(n_bytes, is_piece_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
void tr_peerIo::read_bytes(void* bytes, size_t byte_count)
|
void tr_peerIo::read_bytes(void* bytes, size_t byte_count)
|
||||||
|
|||||||
@@ -123,11 +123,24 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] size_t get_write_buffer_space(uint64_t now) const noexcept;
|
[[nodiscard]] size_t get_write_buffer_space(uint64_t now) const noexcept;
|
||||||
|
|
||||||
void write_bytes(void const* bytes, size_t n_bytes, bool is_piece_data);
|
template<typename T>
|
||||||
|
void write_bytes(T const* buf, size_t n_bytes, bool is_piece_data)
|
||||||
|
{
|
||||||
|
outbuf_info_.emplace_back(n_bytes, is_piece_data);
|
||||||
|
|
||||||
|
auto [resbuf, reslen] = outbuf_.reserve_space(n_bytes);
|
||||||
|
filter_.encrypt(reinterpret_cast<std::byte const*>(buf), resbuf, n_bytes);
|
||||||
|
outbuf_.commit_space(n_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
// Write all the data from `buf`.
|
// Write all the data from `buf`.
|
||||||
// This is a destructive add: `buf` is empty after this call.
|
// This is a destructive add: `buf` is empty after this call.
|
||||||
void write(libtransmission::Buffer& buf, bool is_piece_data);
|
void write(libtransmission::BufferReader<std::byte>& buf, bool is_piece_data)
|
||||||
|
{
|
||||||
|
auto const n_bytes = std::size(buf);
|
||||||
|
write_bytes(std::data(buf), n_bytes, is_piece_data);
|
||||||
|
buf.drain(n_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
size_t flush_outgoing_protocol_msgs();
|
size_t flush_outgoing_protocol_msgs();
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#error only libtransmission should #include this header.
|
#error only libtransmission should #include this header.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <algorithm> // for std::copy_n
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef> // size_t, std::byte
|
#include <cstddef> // size_t, std::byte
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -99,6 +100,19 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr void encrypt(T const* src_data, T* dst_data, size_t n_bytes)
|
||||||
|
{
|
||||||
|
if (enc_active_)
|
||||||
|
{
|
||||||
|
enc_key_.process(src_data, dst_data, n_bytes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::copy_n(src_data, n_bytes, dst_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto is_active() const noexcept
|
[[nodiscard]] constexpr auto is_active() const noexcept
|
||||||
{
|
{
|
||||||
return dec_active_ || enc_active_;
|
return dec_active_ || enc_active_;
|
||||||
|
|||||||
@@ -791,15 +791,12 @@ void build_peer_message(tr_peerMsgsImpl const* const msgs, Buffer& out, uint8_t
|
|||||||
{
|
{
|
||||||
logtrace(msgs, build_log_message(type, args...));
|
logtrace(msgs, build_log_message(type, args...));
|
||||||
|
|
||||||
auto const old_len = std::size(out);
|
|
||||||
auto msg_len = sizeof(type);
|
auto msg_len = sizeof(type);
|
||||||
((msg_len += get_param_length(args)), ...);
|
((msg_len += get_param_length(args)), ...);
|
||||||
out.reserve(old_len + msg_len);
|
|
||||||
out.add_uint32(msg_len);
|
out.add_uint32(msg_len);
|
||||||
out.add_uint8(type);
|
out.add_uint8(type);
|
||||||
(add_param(out, args), ...);
|
(add_param(out, args), ...);
|
||||||
|
|
||||||
TR_ASSERT(old_len + sizeof(uint32_t) + msg_len);
|
|
||||||
TR_ASSERT(messageLengthIsCorrect(msgs->torrent, type, msg_len));
|
TR_ASSERT(messageLengthIsCorrect(msgs->torrent, type, msg_len));
|
||||||
}
|
}
|
||||||
} // namespace protocol_send_message_helpers
|
} // namespace protocol_send_message_helpers
|
||||||
@@ -1015,7 +1012,7 @@ void parseLtepHandshake(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload)
|
|||||||
{
|
{
|
||||||
msgs->peerSentLtepHandshake = true;
|
msgs->peerSentLtepHandshake = true;
|
||||||
|
|
||||||
auto const handshake_sv = payload.pullup_sv();
|
auto const handshake_sv = payload.to_string_view();
|
||||||
|
|
||||||
auto val = tr_variant{};
|
auto val = tr_variant{};
|
||||||
if (!tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, handshake_sv) || !tr_variantIsDict(&val))
|
if (!tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, handshake_sv) || !tr_variantIsDict(&val))
|
||||||
@@ -1127,7 +1124,7 @@ void parseUtMetadata(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload_in)
|
|||||||
int64_t piece = -1;
|
int64_t piece = -1;
|
||||||
int64_t total_size = 0;
|
int64_t total_size = 0;
|
||||||
|
|
||||||
auto const tmp = payload_in.pullup_sv();
|
auto const tmp = payload_in.to_string_view();
|
||||||
auto const* const msg_end = std::data(tmp) + std::size(tmp);
|
auto const* const msg_end = std::data(tmp) + std::size(tmp);
|
||||||
|
|
||||||
auto dict = tr_variant{};
|
auto dict = tr_variant{};
|
||||||
@@ -1184,7 +1181,7 @@ void parseUtPex(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const tmp = payload.pullup_sv();
|
auto const tmp = payload.to_string_view();
|
||||||
|
|
||||||
if (tr_variant val; tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, tmp))
|
if (tr_variant val; tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, tmp))
|
||||||
{
|
{
|
||||||
@@ -1448,15 +1445,12 @@ ReadResult process_peer_message(tr_peerMsgsImpl* msgs, uint8_t id, libtransmissi
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case BtPeerMsgs::Bitfield:
|
case BtPeerMsgs::Bitfield:
|
||||||
{
|
logtrace(msgs, "got a bitfield");
|
||||||
logtrace(msgs, "got a bitfield");
|
msgs->have_ = tr_bitfield{ msgs->torrent->has_metainfo() ? msgs->torrent->piece_count() : std::size(payload) * 8 };
|
||||||
auto const [buf, buflen] = payload.pullup();
|
msgs->have_.set_raw(reinterpret_cast<uint8_t const*>(std::data(payload)), std::size(payload));
|
||||||
msgs->have_ = tr_bitfield{ msgs->torrent->has_metainfo() ? msgs->torrent->piece_count() : buflen * 8 };
|
msgs->publish(tr_peer_event::GotBitfield(&msgs->have_));
|
||||||
msgs->have_.set_raw(reinterpret_cast<uint8_t const*>(buf), buflen);
|
msgs->invalidatePercentDone();
|
||||||
msgs->publish(tr_peer_event::GotBitfield(&msgs->have_));
|
break;
|
||||||
msgs->invalidatePercentDone();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case BtPeerMsgs::Request:
|
case BtPeerMsgs::Request:
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -85,7 +85,9 @@ size_t tr_peer_socket::try_write(Buffer& buf, size_t max, tr_error** error) cons
|
|||||||
#ifdef WITH_UTP
|
#ifdef WITH_UTP
|
||||||
if (is_utp())
|
if (is_utp())
|
||||||
{
|
{
|
||||||
auto const [data, datalen] = buf.pullup();
|
// NB: libutp doesn't change `data` but requires the arg to be non-const anyway
|
||||||
|
auto* const data = const_cast<std::byte*>(std::data(buf));
|
||||||
|
auto const datalen = std::size(buf);
|
||||||
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
auto const n_written = utp_write(handle.utp, data, std::min(datalen, max));
|
auto const n_written = utp_write(handle.utp, data, std::min(datalen, max));
|
||||||
|
|||||||
@@ -5,15 +5,18 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef> // for std::byte
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include <event2/buffer.h>
|
#include <event2/buffer.h>
|
||||||
|
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "net.h" // tr_socket_t
|
#include "net.h" // tr_socket_t
|
||||||
#include "tr-assert.h"
|
#include "tr-assert.h"
|
||||||
@@ -23,21 +26,117 @@
|
|||||||
namespace libtransmission
|
namespace libtransmission
|
||||||
{
|
{
|
||||||
|
|
||||||
template<typename T, typename ValueType>
|
template<typename value_type>
|
||||||
|
class BufferReader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~BufferReader() = default;
|
||||||
|
virtual void drain(size_t n_bytes) = 0;
|
||||||
|
[[nodiscard]] virtual size_t size() const noexcept = 0;
|
||||||
|
[[nodiscard]] virtual value_type const* data() const = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] auto empty() const noexcept
|
||||||
|
{
|
||||||
|
return size() == 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto* begin() noexcept
|
||||||
|
{
|
||||||
|
return data();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto const* begin() const
|
||||||
|
{
|
||||||
|
return data();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto const* end() const
|
||||||
|
{
|
||||||
|
return begin() + size();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto to_string() const
|
||||||
|
{
|
||||||
|
return std::string{ reinterpret_cast<char const*>(data()), size() };
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto to_string_view() const
|
||||||
|
{
|
||||||
|
return std::string_view{ reinterpret_cast<char const*>(data()), size() };
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]] bool starts_with(T const& needle) const
|
||||||
|
{
|
||||||
|
auto const n_bytes = std::size(needle);
|
||||||
|
auto const needle_begin = reinterpret_cast<value_type const*>(std::data(needle));
|
||||||
|
auto const needle_end = needle_begin + n_bytes;
|
||||||
|
return n_bytes <= size() && std::equal(needle_begin, needle_end, data());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto to_buf(void* tgt, size_t n_bytes)
|
||||||
|
{
|
||||||
|
n_bytes = std::min(n_bytes, size());
|
||||||
|
std::copy_n(data(), n_bytes, reinterpret_cast<value_type*>(tgt));
|
||||||
|
drain(n_bytes);
|
||||||
|
return n_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto to_uint8()
|
||||||
|
{
|
||||||
|
auto tmp = uint8_t{};
|
||||||
|
to_buf(&tmp, sizeof(tmp));
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] uint16_t to_uint16()
|
||||||
|
{
|
||||||
|
auto tmp = uint16_t{};
|
||||||
|
to_buf(&tmp, sizeof(tmp));
|
||||||
|
return ntohs(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] uint32_t to_uint32()
|
||||||
|
{
|
||||||
|
auto tmp = uint32_t{};
|
||||||
|
to_buf(&tmp, sizeof(tmp));
|
||||||
|
return ntohl(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] uint64_t to_uint64()
|
||||||
|
{
|
||||||
|
auto tmp = uint64_t{};
|
||||||
|
to_buf(&tmp, sizeof(tmp));
|
||||||
|
return tr_ntohll(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t to_socket(tr_socket_t sockfd, size_t n_bytes, tr_error** error = nullptr)
|
||||||
|
{
|
||||||
|
if (auto const n_sent = send(sockfd, reinterpret_cast<char const*>(data()), std::min(n_bytes, size()), 0); n_sent >= 0)
|
||||||
|
{
|
||||||
|
drain(n_sent);
|
||||||
|
return n_sent;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const err = sockerrno;
|
||||||
|
tr_error_set(error, err, tr_net_strerror(err));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename value_type>
|
||||||
class BufferWriter
|
class BufferWriter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BufferWriter(T* out)
|
virtual ~BufferWriter() = default;
|
||||||
: out_{ out }
|
virtual std::pair<value_type*, size_t> reserve_space(size_t n_bytes) = 0;
|
||||||
{
|
virtual void commit_space(size_t n_bytes) = 0;
|
||||||
static_assert(sizeof(ValueType) == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void add(void const* span_begin, size_t span_len)
|
void add(void const* span_begin, size_t span_len)
|
||||||
{
|
{
|
||||||
auto const* const begin = reinterpret_cast<ValueType const*>(span_begin);
|
auto [buf, buflen] = reserve_space(span_len);
|
||||||
auto const* const end = begin + span_len;
|
std::copy_n(reinterpret_cast<value_type const*>(span_begin), span_len, buf);
|
||||||
out_->insert(std::end(*out_), begin, end);
|
commit_space(span_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ContiguousContainer>
|
template<typename ContiguousContainer>
|
||||||
@@ -96,344 +195,78 @@ public:
|
|||||||
add(&nport, sizeof(nport));
|
add(&nport, sizeof(nport));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
size_t add_socket(tr_socket_t sockfd, size_t n_bytes, tr_error** error = nullptr)
|
||||||
T* out_;
|
{
|
||||||
|
auto const [buf, buflen] = reserve_space(n_bytes);
|
||||||
|
if (auto const n_read = recv(sockfd, reinterpret_cast<char*>(buf), n_bytes, 0); n_read >= 0)
|
||||||
|
{
|
||||||
|
commit_space(n_read);
|
||||||
|
fmt::print("{:p} read {:d} bytes (of {:d}) from socket {:d}\n", fmt::ptr(this), n_read, n_bytes, sockfd);
|
||||||
|
return n_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const err = sockerrno;
|
||||||
|
tr_error_set(error, err, tr_net_strerror(err));
|
||||||
|
fmt::print("{:p} error {:s}\n", fmt::ptr(this), tr_net_strerror(err));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Buffer : public BufferWriter<Buffer, std::byte>
|
class Buffer final
|
||||||
|
: public BufferReader<std::byte>
|
||||||
|
, public BufferWriter<std::byte>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
class Iterator
|
using value_type = std::byte;
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
constexpr Iterator(evbuffer* const buf, size_t offset)
|
|
||||||
: buf_{ buf }
|
|
||||||
, buf_offset_{ offset }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] value_type& operator*() noexcept
|
|
||||||
{
|
|
||||||
auto& info = iov();
|
|
||||||
return static_cast<value_type*>(info.iov.iov_base)[info.offset];
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] value_type operator*() const noexcept
|
|
||||||
{
|
|
||||||
auto const& info = iov();
|
|
||||||
return static_cast<value_type*>(info.iov.iov_base)[info.offset];
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr Iterator operator+(size_t n_bytes)
|
|
||||||
{
|
|
||||||
return Iterator{ buf_, offset() + n_bytes };
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr Iterator operator-(size_t n_bytes)
|
|
||||||
{
|
|
||||||
return Iterator{ buf_, offset() - n_bytes };
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto operator-(Iterator const& that) const noexcept
|
|
||||||
{
|
|
||||||
return offset() - that.offset();
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr Iterator& operator++() noexcept
|
|
||||||
{
|
|
||||||
inc_offset(1U);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr Iterator& operator+=(size_t n_bytes)
|
|
||||||
{
|
|
||||||
inc_offset(n_bytes);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr Iterator& operator--() noexcept
|
|
||||||
{
|
|
||||||
dec_offset(1);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr bool operator==(Iterator const& that) const noexcept
|
|
||||||
{
|
|
||||||
return this->buf_ == that.buf_ && this->offset() == that.offset();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr bool operator!=(Iterator const& that) const noexcept
|
|
||||||
{
|
|
||||||
return !(*this == that);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct IovInfo
|
|
||||||
{
|
|
||||||
evbuffer_iovec iov = {};
|
|
||||||
size_t offset = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr size_t offset() const noexcept
|
|
||||||
{
|
|
||||||
return buf_offset_;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr void dec_offset(size_t increment)
|
|
||||||
{
|
|
||||||
buf_offset_ -= increment;
|
|
||||||
|
|
||||||
if (iov_)
|
|
||||||
{
|
|
||||||
if (iov_->offset >= increment)
|
|
||||||
{
|
|
||||||
iov_->offset -= increment;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
iov_.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr void inc_offset(size_t increment)
|
|
||||||
{
|
|
||||||
buf_offset_ += increment;
|
|
||||||
|
|
||||||
if (iov_)
|
|
||||||
{
|
|
||||||
if (iov_->offset + increment < iov_->iov.iov_len)
|
|
||||||
{
|
|
||||||
iov_->offset += increment;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
iov_.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] IovInfo& iov() const noexcept
|
|
||||||
{
|
|
||||||
if (!iov_)
|
|
||||||
{
|
|
||||||
auto ptr = evbuffer_ptr{};
|
|
||||||
auto iov = IovInfo{};
|
|
||||||
evbuffer_ptr_set(buf_, &ptr, buf_offset_, EVBUFFER_PTR_SET);
|
|
||||||
evbuffer_peek(buf_, std::numeric_limits<ev_ssize_t>::max(), &ptr, &iov.iov, 1);
|
|
||||||
iov.offset = 0;
|
|
||||||
iov_ = iov;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *iov_;
|
|
||||||
}
|
|
||||||
|
|
||||||
mutable std::optional<IovInfo> iov_;
|
|
||||||
|
|
||||||
evbuffer* buf_;
|
|
||||||
size_t buf_offset_ = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
Buffer()
|
|
||||||
: BufferWriter<Buffer, std::byte>{ this }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Buffer(Buffer&& that)
|
|
||||||
: BufferWriter<Buffer, std::byte>(this)
|
|
||||||
, buf_{ std::move(that.buf_) }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Buffer& operator=(Buffer&& that)
|
|
||||||
{
|
|
||||||
buf_ = std::move(that.buf_);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Buffer() = default;
|
||||||
|
Buffer(Buffer&&) = default;
|
||||||
Buffer(Buffer const&) = delete;
|
Buffer(Buffer const&) = delete;
|
||||||
|
Buffer& operator=(Buffer&&) = default;
|
||||||
Buffer& operator=(Buffer const&) = delete;
|
Buffer& operator=(Buffer const&) = delete;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
explicit Buffer(T const& data)
|
explicit Buffer(T const& data)
|
||||||
: BufferWriter<Buffer, std::byte>{ this }
|
|
||||||
{
|
{
|
||||||
add(data);
|
add(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] auto size() const noexcept
|
[[nodiscard]] size_t size() const noexcept override
|
||||||
{
|
{
|
||||||
return evbuffer_get_length(buf_.get());
|
return evbuffer_get_length(buf_.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] auto empty() const noexcept
|
void drain(size_t n_bytes) override
|
||||||
{
|
|
||||||
return evbuffer_get_length(buf_.get()) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[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() };
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
[[nodiscard]] TR_CONSTEXPR20 bool starts_with(T const& needle) const
|
|
||||||
{
|
|
||||||
auto const n_bytes = std::size(needle);
|
|
||||||
auto const needle_begin = reinterpret_cast<std::byte const*>(std::data(needle));
|
|
||||||
auto const needle_end = needle_begin + n_bytes;
|
|
||||||
return n_bytes <= size() && std::equal(needle_begin, needle_end, cbegin());
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] std::string to_string() const
|
|
||||||
{
|
|
||||||
auto str = std::string{};
|
|
||||||
str.resize(size());
|
|
||||||
evbuffer_copyout(buf_.get(), std::data(str), std::size(str));
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto to_buf(void* tgt, size_t n_bytes)
|
|
||||||
{
|
|
||||||
return evbuffer_remove(buf_.get(), tgt, n_bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] auto to_uint8()
|
|
||||||
{
|
|
||||||
auto tmp = uint8_t{};
|
|
||||||
to_buf(&tmp, sizeof(tmp));
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] uint16_t to_uint16()
|
|
||||||
{
|
|
||||||
auto tmp = uint16_t{};
|
|
||||||
to_buf(&tmp, sizeof(tmp));
|
|
||||||
return ntohs(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] uint32_t to_uint32()
|
|
||||||
{
|
|
||||||
auto tmp = uint32_t{};
|
|
||||||
to_buf(&tmp, sizeof(tmp));
|
|
||||||
return ntohl(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] uint64_t to_uint64()
|
|
||||||
{
|
|
||||||
auto tmp = uint64_t{};
|
|
||||||
to_buf(&tmp, sizeof(tmp));
|
|
||||||
return tr_ntohll(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void drain(size_t n_bytes)
|
|
||||||
{
|
{
|
||||||
evbuffer_drain(buf_.get(), n_bytes);
|
evbuffer_drain(buf_.get(), n_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear()
|
[[nodiscard]] value_type const* data() const override
|
||||||
{
|
{
|
||||||
drain(size());
|
return reinterpret_cast<value_type*>(evbuffer_pullup(buf_.get(), -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the number of bytes written. Check `error` for error.
|
virtual std::pair<value_type*, size_t> reserve_space(size_t n_bytes) override
|
||||||
size_t to_socket(tr_socket_t sockfd, size_t n_bytes, tr_error** error = nullptr)
|
|
||||||
{
|
{
|
||||||
EVUTIL_SET_SOCKET_ERROR(0);
|
auto iov = evbuffer_iovec{};
|
||||||
auto const res = evbuffer_write_atmost(buf_.get(), sockfd, n_bytes);
|
evbuffer_reserve_space(buf_.get(), n_bytes, &iov, 1);
|
||||||
auto const err = EVUTIL_SOCKET_ERROR();
|
TR_ASSERT(iov.iov_len >= n_bytes);
|
||||||
if (res >= 0)
|
reserved_space_ = iov;
|
||||||
{
|
return { static_cast<value_type*>(iov.iov_base), static_cast<size_t>(iov.iov_len) };
|
||||||
return static_cast<size_t>(res);
|
|
||||||
}
|
|
||||||
tr_error_set(error, err, tr_net_strerror(err));
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::pair<std::byte*, size_t> pullup()
|
virtual void commit_space(size_t n_bytes) override
|
||||||
{
|
{
|
||||||
return { reinterpret_cast<std::byte*>(evbuffer_pullup(buf_.get(), -1)), size() };
|
TR_ASSERT(reserved_space_);
|
||||||
}
|
TR_ASSERT(reserved_space_->iov_len >= n_bytes);
|
||||||
|
reserved_space_->iov_len = n_bytes;
|
||||||
[[nodiscard]] std::byte const* data() const
|
evbuffer_commit_space(buf_.get(), &*reserved_space_, 1);
|
||||||
{
|
reserved_space_.reset();
|
||||||
return reinterpret_cast<std::byte*>(evbuffer_pullup(buf_.get(), -1));
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] auto pullup_sv()
|
|
||||||
{
|
|
||||||
auto const [buf, buflen] = pullup();
|
|
||||||
return std::string_view{ reinterpret_cast<char const*>(buf), buflen };
|
|
||||||
}
|
|
||||||
|
|
||||||
void reserve(size_t n_bytes)
|
|
||||||
{
|
|
||||||
evbuffer_expand(buf_.get(), n_bytes - size());
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t add_socket(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<int>(n_bytes));
|
|
||||||
auto const err = EVUTIL_SOCKET_ERROR();
|
|
||||||
|
|
||||||
if (res > 0)
|
|
||||||
{
|
|
||||||
return static_cast<size_t>(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res == 0)
|
|
||||||
{
|
|
||||||
tr_error_set_from_errno(error, ENOTCONN);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tr_error_set(error, err, tr_net_strerror(err));
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void insert([[maybe_unused]] Iterator iter, T const* const begin, T const* const end)
|
|
||||||
{
|
|
||||||
TR_ASSERT(iter == this->end()); // tr_buffer only supports appending
|
|
||||||
evbuffer_add(buf_.get(), begin, end - begin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
evhelpers::evbuffer_unique_ptr buf_{ evbuffer_new() };
|
evhelpers::evbuffer_unique_ptr buf_{ evbuffer_new() };
|
||||||
|
std::optional<evbuffer_iovec> reserved_space_;
|
||||||
[[nodiscard]] Iterator cbegin() const noexcept
|
|
||||||
{
|
|
||||||
return Iterator{ buf_.get(), 0U };
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] Iterator cend() const noexcept
|
|
||||||
{
|
|
||||||
return Iterator{ buf_.get(), size() };
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace libtransmission
|
} // namespace libtransmission
|
||||||
|
|||||||
@@ -277,34 +277,40 @@ namespace
|
|||||||
{
|
{
|
||||||
namespace to_string_helpers
|
namespace to_string_helpers
|
||||||
{
|
{
|
||||||
using Buffer = libtransmission::Buffer;
|
using OutBuf = libtransmission::Buffer;
|
||||||
|
|
||||||
void saveIntFunc(tr_variant const* val, void* vout)
|
void saveIntFunc(tr_variant const* val, void* vout)
|
||||||
{
|
{
|
||||||
auto buf = std::array<char, 64>{};
|
auto out = static_cast<OutBuf*>(vout);
|
||||||
auto const* const out = fmt::format_to(std::data(buf), FMT_COMPILE("i{:d}e"), val->val.i);
|
|
||||||
static_cast<Buffer*>(vout)->add(std::data(buf), static_cast<size_t>(out - std::data(buf)));
|
auto const [buf, buflen] = out->reserve_space(64U);
|
||||||
|
auto* walk = reinterpret_cast<char*>(buf);
|
||||||
|
auto const* const begin = walk;
|
||||||
|
walk = fmt::format_to(walk, FMT_COMPILE("i{:d}e"), val->val.i);
|
||||||
|
out->commit_space(walk - begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveBoolFunc(tr_variant const* val, void* vout)
|
void saveBoolFunc(tr_variant const* val, void* vout)
|
||||||
{
|
{
|
||||||
static_cast<Buffer*>(vout)->add(val->val.b ? "i1e"sv : "i0e"sv);
|
static_cast<OutBuf*>(vout)->add(val->val.b ? "i1e"sv : "i0e"sv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveStringImpl(Buffer* tgt, std::string_view sv)
|
void saveStringImpl(OutBuf* out, std::string_view sv)
|
||||||
{
|
{
|
||||||
// `${sv.size()}:${sv}`
|
// `${sv.size()}:${sv}`
|
||||||
auto prefix = std::array<char, 32>{};
|
auto const [buf, buflen] = out->reserve_space(std::size(sv) + 32U);
|
||||||
auto const* const out = fmt::format_to(std::data(prefix), FMT_COMPILE("{:d}:"), std::size(sv));
|
auto* walk = reinterpret_cast<char*>(buf);
|
||||||
tgt->add(std::data(prefix), out - std::data(prefix));
|
auto const* const begin = walk;
|
||||||
tgt->add(sv);
|
walk = fmt::format_to(walk, FMT_COMPILE("{:d}:"), std::size(sv));
|
||||||
|
walk = std::copy_n(std::data(sv), std::size(sv), walk);
|
||||||
|
out->commit_space(walk - begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveStringFunc(tr_variant const* v, void* vout)
|
void saveStringFunc(tr_variant const* v, void* vout)
|
||||||
{
|
{
|
||||||
auto sv = std::string_view{};
|
auto sv = std::string_view{};
|
||||||
(void)!tr_variantGetStrView(v, &sv);
|
(void)!tr_variantGetStrView(v, &sv);
|
||||||
saveStringImpl(static_cast<Buffer*>(vout), sv);
|
saveStringImpl(static_cast<OutBuf*>(vout), sv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveRealFunc(tr_variant const* val, void* vout)
|
void saveRealFunc(tr_variant const* val, void* vout)
|
||||||
@@ -313,22 +319,22 @@ void saveRealFunc(tr_variant const* val, void* vout)
|
|||||||
|
|
||||||
auto buf = std::array<char, 64>{};
|
auto buf = std::array<char, 64>{};
|
||||||
auto const* const out = fmt::format_to(std::data(buf), FMT_COMPILE("{:f}"), val->val.d);
|
auto const* const out = fmt::format_to(std::data(buf), FMT_COMPILE("{:f}"), val->val.d);
|
||||||
saveStringImpl(static_cast<Buffer*>(vout), { std::data(buf), static_cast<size_t>(out - std::data(buf)) });
|
saveStringImpl(static_cast<OutBuf*>(vout), { std::data(buf), static_cast<size_t>(out - std::data(buf)) });
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveDictBeginFunc(tr_variant const* /*val*/, void* vbuf)
|
void saveDictBeginFunc(tr_variant const* /*val*/, void* vbuf)
|
||||||
{
|
{
|
||||||
static_cast<Buffer*>(vbuf)->push_back('d');
|
static_cast<OutBuf*>(vbuf)->push_back('d');
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveListBeginFunc(tr_variant const* /*val*/, void* vbuf)
|
void saveListBeginFunc(tr_variant const* /*val*/, void* vbuf)
|
||||||
{
|
{
|
||||||
static_cast<Buffer*>(vbuf)->push_back('l');
|
static_cast<OutBuf*>(vbuf)->push_back('l');
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveContainerEndFunc(tr_variant const* /*val*/, void* vbuf)
|
void saveContainerEndFunc(tr_variant const* /*val*/, void* vbuf)
|
||||||
{
|
{
|
||||||
static_cast<Buffer*>(vbuf)->push_back('e');
|
static_cast<OutBuf*>(vbuf)->push_back('e');
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VariantWalkFuncs const walk_funcs = {
|
struct VariantWalkFuncs const walk_funcs = {
|
||||||
@@ -348,7 +354,7 @@ std::string tr_variantToStrBenc(tr_variant const* top)
|
|||||||
{
|
{
|
||||||
using namespace to_string_helpers;
|
using namespace to_string_helpers;
|
||||||
|
|
||||||
auto buf = libtransmission::Buffer{};
|
auto buf = OutBuf{};
|
||||||
tr_variantWalk(top, &walk_funcs, &buf, true);
|
tr_variantWalk(top, &walk_funcs, &buf, true);
|
||||||
return buf.to_string();
|
return buf.to_string();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -547,25 +547,27 @@ void jsonBoolFunc(tr_variant const* val, void* vdata)
|
|||||||
|
|
||||||
void jsonRealFunc(tr_variant const* val, void* vdata)
|
void jsonRealFunc(tr_variant const* val, void* vdata)
|
||||||
{
|
{
|
||||||
auto* data = static_cast<struct JsonWalk*>(vdata);
|
auto* const data = static_cast<struct JsonWalk*>(vdata);
|
||||||
|
|
||||||
|
auto const [buf, buflen] = data->out.reserve_space(64);
|
||||||
|
auto* walk = reinterpret_cast<char*>(buf);
|
||||||
|
auto const* const begin = walk;
|
||||||
|
|
||||||
if (fabs(val->val.d - (int)val->val.d) < 0.00001)
|
if (fabs(val->val.d - (int)val->val.d) < 0.00001)
|
||||||
{
|
{
|
||||||
auto buf = std::array<char, 64>{};
|
walk = fmt::format_to(walk, FMT_COMPILE("{:.0f}"), val->val.d);
|
||||||
auto const* const out = fmt::format_to(std::data(buf), FMT_COMPILE("{:.0f}"), val->val.d);
|
|
||||||
data->out.add(std::data(buf), static_cast<size_t>(out - std::data(buf)));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto buf = std::array<char, 64>{};
|
walk = fmt::format_to(walk, FMT_COMPILE("{:.4f}"), val->val.d);
|
||||||
auto const* const out = fmt::format_to(std::data(buf), FMT_COMPILE("{:.4f}"), val->val.d);
|
|
||||||
data->out.add(std::data(buf), static_cast<size_t>(out - std::data(buf)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data->out.commit_space(walk - begin);
|
||||||
|
|
||||||
jsonChildFunc(data);
|
jsonChildFunc(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_escaped_char(Buffer& out, std::string_view& sv)
|
[[nodiscard]] char* write_escaped_char(char* buf, char const* const end, std::string_view& sv)
|
||||||
{
|
{
|
||||||
auto u16buf = std::array<std::uint16_t, 2>{};
|
auto u16buf = std::array<std::uint16_t, 2>{};
|
||||||
|
|
||||||
@@ -577,85 +579,97 @@ void write_escaped_char(Buffer& out, std::string_view& sv)
|
|||||||
|
|
||||||
for (auto it = std::cbegin(u16buf); it != end16; ++it)
|
for (auto it = std::cbegin(u16buf); it != end16; ++it)
|
||||||
{
|
{
|
||||||
auto arr = std::array<char, 16>{};
|
buf = fmt::format_to_n(buf, end - buf - 1, FMT_COMPILE("\\u{:04x}"), *it).out;
|
||||||
auto const result = fmt::format_to_n(std::data(arr), std::size(arr), FMT_COMPILE("\\u{:04x}"), *it);
|
|
||||||
out.add(std::data(arr), result.size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sv.remove_prefix(walk8 - begin8 - 1);
|
sv.remove_prefix(walk8 - begin8 - 1);
|
||||||
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void jsonStringFunc(tr_variant const* val, void* vdata)
|
void jsonStringFunc(tr_variant const* val, void* vdata)
|
||||||
{
|
{
|
||||||
auto* data = static_cast<struct JsonWalk*>(vdata);
|
auto* const data = static_cast<struct JsonWalk*>(vdata);
|
||||||
|
|
||||||
auto sv = std::string_view{};
|
auto sv = std::string_view{};
|
||||||
(void)!tr_variantGetStrView(val, &sv);
|
(void)!tr_variantGetStrView(val, &sv);
|
||||||
|
|
||||||
auto& out = data->out;
|
auto& out = data->out;
|
||||||
out.reserve(std::size(data->out) + std::size(sv) * 6 + 2);
|
auto const [buf, buflen] = out.reserve_space(std::size(sv) * 6 + 2);
|
||||||
out.push_back('"');
|
auto* walk = reinterpret_cast<char*>(buf);
|
||||||
|
auto const* const begin = walk;
|
||||||
|
auto const* const end = begin + buflen;
|
||||||
|
|
||||||
|
*walk++ = '"';
|
||||||
|
|
||||||
for (; !std::empty(sv); sv.remove_prefix(1))
|
for (; !std::empty(sv); sv.remove_prefix(1))
|
||||||
{
|
{
|
||||||
switch (sv.front())
|
switch (sv.front())
|
||||||
{
|
{
|
||||||
case '\b':
|
case '\b':
|
||||||
out.add(R"(\b)"sv);
|
*walk++ = '\\';
|
||||||
|
*walk++ = 'b';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '\f':
|
case '\f':
|
||||||
out.add(R"(\f)"sv);
|
*walk++ = '\\';
|
||||||
|
*walk++ = 'f';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '\n':
|
case '\n':
|
||||||
out.add(R"(\n)"sv);
|
*walk++ = '\\';
|
||||||
|
*walk++ = 'n';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '\r':
|
case '\r':
|
||||||
out.add(R"(\r)"sv);
|
*walk++ = '\\';
|
||||||
|
*walk++ = 'r';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '\t':
|
case '\t':
|
||||||
out.add(R"(\t)"sv);
|
*walk++ = '\\';
|
||||||
|
*walk++ = 't';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '"':
|
case '"':
|
||||||
out.add(R"(\")"sv);
|
*walk++ = '\\';
|
||||||
|
*walk++ = '"';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '\\':
|
case '\\':
|
||||||
out.add(R"(\\)"sv);
|
*walk++ = '\\';
|
||||||
|
*walk++ = '\\';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (isprint((unsigned char)sv.front()) != 0)
|
if (isprint((unsigned char)sv.front()) != 0)
|
||||||
{
|
{
|
||||||
out.push_back(sv.front());
|
*walk++ = sv.front();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
write_escaped_char(out, sv);
|
walk = write_escaped_char(walk, end, sv);
|
||||||
}
|
}
|
||||||
catch (utf8::exception const&)
|
catch (utf8::exception const&)
|
||||||
{
|
{
|
||||||
out.push_back('?');
|
*walk++ = '?';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out.push_back('"');
|
*walk++ = '"';
|
||||||
|
TR_ASSERT(walk <= end);
|
||||||
|
out.commit_space(walk - begin);
|
||||||
|
|
||||||
jsonChildFunc(data);
|
jsonChildFunc(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void jsonDictBeginFunc(tr_variant const* val, void* vdata)
|
void jsonDictBeginFunc(tr_variant const* val, void* vdata)
|
||||||
{
|
{
|
||||||
auto* data = static_cast<struct JsonWalk*>(vdata);
|
auto* const data = static_cast<struct JsonWalk*>(vdata);
|
||||||
|
|
||||||
jsonPushParent(data, val);
|
jsonPushParent(data, val);
|
||||||
data->out.push_back('{');
|
data->out.push_back('{');
|
||||||
@@ -669,7 +683,7 @@ void jsonDictBeginFunc(tr_variant const* val, void* vdata)
|
|||||||
void jsonListBeginFunc(tr_variant const* val, void* vdata)
|
void jsonListBeginFunc(tr_variant const* val, void* vdata)
|
||||||
{
|
{
|
||||||
size_t const n_children = tr_variantListSize(val);
|
size_t const n_children = tr_variantListSize(val);
|
||||||
auto* data = static_cast<struct JsonWalk*>(vdata);
|
auto* const data = static_cast<struct JsonWalk*>(vdata);
|
||||||
|
|
||||||
jsonPushParent(data, val);
|
jsonPushParent(data, val);
|
||||||
data->out.push_back('[');
|
data->out.push_back('[');
|
||||||
@@ -682,7 +696,7 @@ void jsonListBeginFunc(tr_variant const* val, void* vdata)
|
|||||||
|
|
||||||
void jsonContainerEndFunc(tr_variant const* val, void* vdata)
|
void jsonContainerEndFunc(tr_variant const* val, void* vdata)
|
||||||
{
|
{
|
||||||
auto* data = static_cast<struct JsonWalk*>(vdata);
|
auto* const data = static_cast<struct JsonWalk*>(vdata);
|
||||||
|
|
||||||
jsonPopParent(data);
|
jsonPopParent(data);
|
||||||
|
|
||||||
|
|||||||
@@ -551,7 +551,7 @@ TEST_F(AnnouncerUdpTest, handleMessageReturnsFalseOnInvalidMessage)
|
|||||||
EXPECT_FALSE(announcer->handle_message(std::data(arr), response_size));
|
EXPECT_FALSE(announcer->handle_message(std::data(arr), response_size));
|
||||||
|
|
||||||
// send a connection response but with an *invalid* action
|
// send a connection response but with an *invalid* action
|
||||||
buf.clear();
|
buf = {};
|
||||||
buf.add_uint32(ScrapeAction);
|
buf.add_uint32(ScrapeAction);
|
||||||
buf.add_uint32(transaction_id);
|
buf.add_uint32(transaction_id);
|
||||||
buf.add_uint64(tr_rand_obj<uint64_t>());
|
buf.add_uint64(tr_rand_obj<uint64_t>());
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ TEST_F(BufferTest, Move)
|
|||||||
EXPECT_EQ(3U, std::size(a));
|
EXPECT_EQ(3U, std::size(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
TEST_F(BufferTest, NonBufferWriter)
|
TEST_F(BufferTest, NonBufferWriter)
|
||||||
{
|
{
|
||||||
auto constexpr Hello = "Hello, "sv;
|
auto constexpr Hello = "Hello, "sv;
|
||||||
@@ -141,3 +142,4 @@ TEST_F(BufferTest, NonBufferWriter)
|
|||||||
auto const result2 = std::string_view{ reinterpret_cast<char const*>(std::data(out2_vec)), std::size(out2_vec) };
|
auto const result2 = std::string_view{ reinterpret_cast<char const*>(std::data(out2_vec)), std::size(out2_vec) };
|
||||||
EXPECT_EQ(result1, result2);
|
EXPECT_EQ(result1, result2);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user