mirror of
https://github.com/transmission/transmission.git
synced 2026-02-15 07:26:49 +00:00
334 lines
9.1 KiB
C++
334 lines
9.1 KiB
C++
// This file Copyright © 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.
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <iterator>
|
|
#include <mutex>
|
|
#include <string_view>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#include <fmt/format.h>
|
|
|
|
#include <small/set.hpp>
|
|
#include <small/vector.hpp>
|
|
|
|
#include "lib/base/serializer.h"
|
|
#include "lib/base/string-utils.h"
|
|
#include "lib/base/variant.h"
|
|
|
|
#include "libtransmission/net.h" // tr_diffserv_t, tr_port
|
|
#include "libtransmission/open-files.h" // tr_open_files::Preallocation
|
|
#include "libtransmission/peer-io.h" // tr_preferred_transport
|
|
#include "libtransmission/types.h" // tr_encryption_mode, tr_verify_added_mode, tr_mode_t
|
|
|
|
using namespace std::literals;
|
|
|
|
namespace tr::serializer
|
|
{
|
|
namespace
|
|
{
|
|
template<typename T, size_t N>
|
|
using LookupTable = std::array<std::pair<std::string_view, T>, N>;
|
|
|
|
template<typename T, size_t N>
|
|
[[nodiscard]] tr_variant from_enum_or_integral_with_lookup(LookupTable<T, N> const& rows, T const src)
|
|
{
|
|
static_assert(std::is_enum_v<T> || std::is_integral_v<T>);
|
|
|
|
for (auto const& [key, value] : rows)
|
|
{
|
|
if (value == src)
|
|
{
|
|
return tr_variant::unmanaged_string(key);
|
|
}
|
|
}
|
|
|
|
return static_cast<int64_t>(src);
|
|
}
|
|
|
|
template<typename T, size_t N>
|
|
[[nodiscard]] bool to_enum_or_integral_with_lookup(LookupTable<T, N> const& rows, tr_variant const& src, T* tgt)
|
|
{
|
|
static_assert(std::is_enum_v<T> || std::is_integral_v<T>);
|
|
|
|
if (tgt == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (auto const val = src.value_if<std::string_view>())
|
|
{
|
|
auto const needle = tr_strlower(tr_strv_strip(*val));
|
|
|
|
for (auto const& [key, value] : rows)
|
|
{
|
|
if (key == needle)
|
|
{
|
|
*tgt = value;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto const val = src.value_if<int64_t>())
|
|
{
|
|
for (auto const& [key, value] : rows)
|
|
{
|
|
if (static_cast<int64_t>(value) == *val)
|
|
{
|
|
*tgt = value;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// ---
|
|
|
|
// RFCs 2474, 3246, 4594 & 8622
|
|
// Service class names are defined in RFC 4594, RFC 5865, and RFC 8622.
|
|
// Not all platforms have these IPTOS_ definitions, so hardcode them here
|
|
auto constexpr DiffServKeys = LookupTable<int, 28U>{ {
|
|
{ "cs0", 0x00 }, // IPTOS_CLASS_CS0
|
|
{ "le", 0x04 },
|
|
{ "cs1", 0x20 }, // IPTOS_CLASS_CS1
|
|
{ "af11", 0x28 }, // IPTOS_DSCP_AF11
|
|
{ "af12", 0x30 }, // IPTOS_DSCP_AF12
|
|
{ "af13", 0x38 }, // IPTOS_DSCP_AF13
|
|
{ "cs2", 0x40 }, // IPTOS_CLASS_CS2
|
|
{ "af21", 0x48 }, // IPTOS_DSCP_AF21
|
|
{ "af22", 0x50 }, // IPTOS_DSCP_AF22
|
|
{ "af23", 0x58 }, // IPTOS_DSCP_AF23
|
|
{ "cs3", 0x60 }, // IPTOS_CLASS_CS3
|
|
{ "af31", 0x68 }, // IPTOS_DSCP_AF31
|
|
{ "af32", 0x70 }, // IPTOS_DSCP_AF32
|
|
{ "af33", 0x78 }, // IPTOS_DSCP_AF33
|
|
{ "cs4", 0x80 }, // IPTOS_CLASS_CS4
|
|
{ "af41", 0x88 }, // IPTOS_DSCP_AF41
|
|
{ "af42", 0x90 }, // IPTOS_DSCP_AF42
|
|
{ "af43", 0x98 }, // IPTOS_DSCP_AF43
|
|
{ "cs5", 0xa0 }, // IPTOS_CLASS_CS5
|
|
{ "ef", 0xb8 }, // IPTOS_DSCP_EF
|
|
{ "cs6", 0xc0 }, // IPTOS_CLASS_CS6
|
|
{ "cs7", 0xe0 }, // IPTOS_CLASS_CS7
|
|
|
|
// <netinet/ip.h> lists these TOS names as deprecated,
|
|
// but keep them defined here for backward compatibility
|
|
{ "routine", 0x00 }, // IPTOS_PREC_ROUTINE
|
|
{ "lowcost", 0x02 }, // IPTOS_LOWCOST
|
|
{ "mincost", 0x02 }, // IPTOS_MINCOST
|
|
{ "reliable", 0x04 }, // IPTOS_RELIABILITY
|
|
{ "throughput", 0x08 }, // IPTOS_THROUGHPUT
|
|
{ "lowdelay", 0x10 }, // IPTOS_LOWDELAY
|
|
} };
|
|
|
|
bool to_diffserv_t(tr_variant const& src, tr_diffserv_t* tgt)
|
|
{
|
|
auto tmp = int{};
|
|
if (!to_enum_or_integral_with_lookup(DiffServKeys, src, &tmp))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
*tgt = tr_diffserv_t{ tmp };
|
|
return true;
|
|
}
|
|
|
|
tr_variant from_diffserv_t(tr_diffserv_t const& val)
|
|
{
|
|
return from_enum_or_integral_with_lookup(DiffServKeys, static_cast<int>(val));
|
|
}
|
|
|
|
// ---
|
|
|
|
auto constexpr EncryptionKeys = LookupTable<tr_encryption_mode, 3U>{ {
|
|
{ "required", TR_ENCRYPTION_REQUIRED },
|
|
{ "preferred", TR_ENCRYPTION_PREFERRED },
|
|
{ "allowed", TR_CLEAR_PREFERRED },
|
|
} };
|
|
|
|
bool to_encryption_mode(tr_variant const& src, tr_encryption_mode* tgt)
|
|
{
|
|
return to_enum_or_integral_with_lookup(EncryptionKeys, src, tgt);
|
|
}
|
|
|
|
tr_variant from_encryption_mode(tr_encryption_mode const& val)
|
|
{
|
|
return from_enum_or_integral_with_lookup(EncryptionKeys, val);
|
|
}
|
|
|
|
// ---
|
|
|
|
bool to_mode_t(tr_variant const& src, tr_mode_t* tgt)
|
|
{
|
|
if (auto const val = src.value_if<std::string_view>())
|
|
{
|
|
if (auto const mode = tr_num_parse<uint32_t>(*val, nullptr, 8); mode)
|
|
{
|
|
*tgt = static_cast<tr_mode_t>(*mode);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (auto const val = src.value_if<int64_t>())
|
|
{
|
|
*tgt = static_cast<tr_mode_t>(*val);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
tr_variant from_mode_t(tr_mode_t const& val)
|
|
{
|
|
return fmt::format("{:#03o}", val);
|
|
}
|
|
|
|
// ---
|
|
|
|
bool to_port(tr_variant const& src, tr_port* tgt)
|
|
{
|
|
if (auto const val = src.value_if<int64_t>())
|
|
{
|
|
*tgt = tr_port::from_host(*val);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
tr_variant from_port(tr_port const& val)
|
|
{
|
|
return int64_t{ val.host() };
|
|
}
|
|
|
|
// ---
|
|
|
|
auto constexpr PreallocationKeys = LookupTable<tr_open_files::Preallocation, 5U>{ {
|
|
{ "off", tr_open_files::Preallocation::None },
|
|
{ "none", tr_open_files::Preallocation::None },
|
|
{ "fast", tr_open_files::Preallocation::Sparse },
|
|
{ "sparse", tr_open_files::Preallocation::Sparse },
|
|
{ "full", tr_open_files::Preallocation::Full },
|
|
} };
|
|
|
|
bool to_preallocation_mode(tr_variant const& src, tr_open_files::Preallocation* tgt)
|
|
{
|
|
return to_enum_or_integral_with_lookup(PreallocationKeys, src, tgt);
|
|
}
|
|
|
|
tr_variant from_preallocation_mode(tr_open_files::Preallocation const& val)
|
|
{
|
|
return static_cast<int64_t>(val);
|
|
}
|
|
|
|
// ---
|
|
|
|
auto constexpr PreferredTransportKeys = LookupTable<tr_preferred_transport, TR_NUM_PREFERRED_TRANSPORT>{ {
|
|
{ "utp", TR_PREFER_UTP },
|
|
{ "tcp", TR_PREFER_TCP },
|
|
} };
|
|
|
|
bool to_preferred_transport(
|
|
tr_variant const& src,
|
|
small::max_size_vector<tr_preferred_transport, TR_NUM_PREFERRED_TRANSPORT>* tgt)
|
|
{
|
|
static auto constexpr LoadSingle = [](tr_variant const& var)
|
|
{
|
|
auto tmp = tr_preferred_transport{};
|
|
return to_enum_or_integral_with_lookup(PreferredTransportKeys, var, &tmp) ? tmp : TR_NUM_PREFERRED_TRANSPORT;
|
|
};
|
|
|
|
if (auto* const l = src.get_if<tr_variant::Vector>(); l != nullptr)
|
|
{
|
|
auto tmp = small::max_size_unordered_set<tr_preferred_transport, TR_NUM_PREFERRED_TRANSPORT>{};
|
|
tmp.reserve(tmp.max_size());
|
|
|
|
for (size_t i = 0, n = std::min(std::size(*l), tmp.max_size()); i < n; ++i)
|
|
{
|
|
auto const value = LoadSingle((*l)[i]);
|
|
if (value >= TR_NUM_PREFERRED_TRANSPORT || !tmp.insert(value).second)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// N.B. As of small 0.2.2, small::max_size_unordered_set preserves insertion order,
|
|
// so we can directly copy the elements
|
|
tgt->assign(std::begin(tmp), std::end(tmp));
|
|
return true;
|
|
}
|
|
|
|
auto const preferred = LoadSingle(src);
|
|
if (preferred >= TR_NUM_PREFERRED_TRANSPORT)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
tgt->assign(1U, preferred);
|
|
return true;
|
|
}
|
|
|
|
tr_variant from_preferred_transport(small::max_size_vector<tr_preferred_transport, TR_NUM_PREFERRED_TRANSPORT> const& val)
|
|
{
|
|
static auto constexpr SaveSingle = [](tr_preferred_transport const ele) -> tr_variant
|
|
{
|
|
return from_enum_or_integral_with_lookup(PreferredTransportKeys, ele);
|
|
};
|
|
|
|
auto ret = tr_variant::Vector{};
|
|
ret.reserve(std::size(val));
|
|
for (auto const ele : val)
|
|
{
|
|
ret.emplace_back(SaveSingle(ele));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// ---
|
|
|
|
auto constexpr VerifyModeKeys = LookupTable<tr_verify_added_mode, 2U>{ {
|
|
{ "fast", TR_VERIFY_ADDED_FAST },
|
|
{ "full", TR_VERIFY_ADDED_FULL },
|
|
} };
|
|
|
|
bool to_verify_added_mode(tr_variant const& src, tr_verify_added_mode* tgt)
|
|
{
|
|
return to_enum_or_integral_with_lookup(VerifyModeKeys, src, tgt);
|
|
}
|
|
|
|
tr_variant from_verify_added_mode(tr_verify_added_mode const& val)
|
|
{
|
|
return from_enum_or_integral_with_lookup(VerifyModeKeys, val);
|
|
}
|
|
|
|
} // unnamed namespace
|
|
|
|
void install_libtransmission_converters()
|
|
{
|
|
static auto once = std::once_flag{};
|
|
std::call_once(
|
|
once,
|
|
[]
|
|
{
|
|
Converters::add(to_diffserv_t, from_diffserv_t);
|
|
Converters::add(to_encryption_mode, from_encryption_mode);
|
|
Converters::add(to_mode_t, from_mode_t);
|
|
Converters::add(to_port, from_port);
|
|
Converters::add(to_preallocation_mode, from_preallocation_mode);
|
|
Converters::add(to_preferred_transport, from_preferred_transport);
|
|
Converters::add(to_verify_added_mode, from_verify_added_mode);
|
|
});
|
|
}
|
|
|
|
} // namespace tr::serializer
|