refactor: add tr_variant_serde (#5903)
This commit is contained in:
parent
9df4adf9b6
commit
a4d205612a
|
@ -168,7 +168,6 @@
|
|||
A25964A6106D73A800453B31 /* announcer.cc in Sources */ = {isa = PBXBuildFile; fileRef = A25964A4106D73A800453B31 /* announcer.cc */; };
|
||||
A25964A7106D73A800453B31 /* announcer.h in Headers */ = {isa = PBXBuildFile; fileRef = A25964A5106D73A800453B31 /* announcer.h */; };
|
||||
A25BFD69167BED3B0039D1AA /* variant-benc.cc in Sources */ = {isa = PBXBuildFile; fileRef = A25BFD63167BED3B0039D1AA /* variant-benc.cc */; };
|
||||
A25BFD6A167BED3B0039D1AA /* variant-common.h in Headers */ = {isa = PBXBuildFile; fileRef = A25BFD64167BED3B0039D1AA /* variant-common.h */; };
|
||||
A25BFD6B167BED3B0039D1AA /* variant-json.cc in Sources */ = {isa = PBXBuildFile; fileRef = A25BFD65167BED3B0039D1AA /* variant-json.cc */; };
|
||||
A25BFD6D167BED3B0039D1AA /* variant.cc in Sources */ = {isa = PBXBuildFile; fileRef = A25BFD67167BED3B0039D1AA /* variant.cc */; };
|
||||
A25BFD6E167BED3B0039D1AA /* variant.h in Headers */ = {isa = PBXBuildFile; fileRef = A25BFD68167BED3B0039D1AA /* variant.h */; };
|
||||
|
@ -927,7 +926,6 @@
|
|||
A25964A4106D73A800453B31 /* announcer.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = announcer.cc; sourceTree = "<group>"; };
|
||||
A25964A5106D73A800453B31 /* announcer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = announcer.h; sourceTree = "<group>"; };
|
||||
A25BFD63167BED3B0039D1AA /* variant-benc.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = "variant-benc.cc"; sourceTree = "<group>"; };
|
||||
A25BFD64167BED3B0039D1AA /* variant-common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "variant-common.h"; sourceTree = "<group>"; };
|
||||
A25BFD65167BED3B0039D1AA /* variant-json.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = "variant-json.cc"; sourceTree = "<group>"; };
|
||||
A25BFD67167BED3B0039D1AA /* variant.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = variant.cc; sourceTree = "<group>"; };
|
||||
A25BFD68167BED3B0039D1AA /* variant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = variant.h; sourceTree = "<group>"; };
|
||||
|
@ -1825,7 +1823,6 @@
|
|||
BEFC1DF10C07861A00B0BB3C /* utils.h */,
|
||||
C843FC8329C51B9400491854 /* utils.mm */,
|
||||
A25BFD63167BED3B0039D1AA /* variant-benc.cc */,
|
||||
A25BFD64167BED3B0039D1AA /* variant-common.h */,
|
||||
A25BFD65167BED3B0039D1AA /* variant-json.cc */,
|
||||
A25BFD67167BED3B0039D1AA /* variant.cc */,
|
||||
A25BFD68167BED3B0039D1AA /* variant.h */,
|
||||
|
@ -2334,7 +2331,6 @@
|
|||
A23F29A1132A447400E9A83B /* announcer-common.h in Headers */,
|
||||
A2EE726F14DCCC950093C99A /* port-forwarding-natpmp.h in Headers */,
|
||||
A2D77451154CC25700A62B93 /* WebSeedTableView.h in Headers */,
|
||||
A25BFD6A167BED3B0039D1AA /* variant-common.h in Headers */,
|
||||
A25BFD6E167BED3B0039D1AA /* variant.h in Headers */,
|
||||
A2EA52321686AC0D00180493 /* quark.h in Headers */,
|
||||
A2AF23C916B44FA0003BC59E /* log.h in Headers */,
|
||||
|
|
|
@ -935,8 +935,7 @@ bool tr_daemon::init(int argc, char const* const argv[], bool* foreground, int*
|
|||
|
||||
if (dumpSettings)
|
||||
{
|
||||
auto const str = tr_variantToStr(&settings_, TR_VARIANT_FMT_JSON);
|
||||
fprintf(stderr, "%s", str.c_str());
|
||||
fmt::print("{:s}\n", tr_variant_serde::json().to_string(settings_));
|
||||
goto EXIT_EARLY;
|
||||
}
|
||||
|
||||
|
|
|
@ -481,15 +481,14 @@ bool Application::Impl::on_rpc_changed_idle(tr_rpc_callback_type type, tr_torren
|
|||
tr_variantInitDict(&tmp, 100);
|
||||
tr_sessionGetSettings(session, &tmp);
|
||||
|
||||
auto const serde = tr_variant_serde::benc();
|
||||
for (int i = 0; tr_variantDictChild(&tmp, i, &key, &newval); ++i)
|
||||
{
|
||||
bool changed = true;
|
||||
|
||||
if (tr_variant const* oldval = tr_variantDictFind(oldvals, key); oldval != nullptr)
|
||||
{
|
||||
auto const a = tr_variantToStr(oldval, TR_VARIANT_FMT_BENC);
|
||||
auto const b = tr_variantToStr(newval, TR_VARIANT_FMT_BENC);
|
||||
changed = a != b;
|
||||
changed = serde.to_string(*oldval) != serde.to_string(*newval);
|
||||
}
|
||||
|
||||
if (changed)
|
||||
|
|
|
@ -159,7 +159,6 @@ target_sources(${TR_NAME}
|
|||
utils.h
|
||||
utils.mm
|
||||
variant-benc.cc
|
||||
variant-common.h
|
||||
variant-converters.cc
|
||||
variant-json.cc
|
||||
variant.cc
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "libtransmission/transmission.h"
|
||||
|
||||
#include "libtransmission/announce-list.h"
|
||||
#include "libtransmission/error.h"
|
||||
#include "libtransmission/quark.h"
|
||||
#include "libtransmission/torrent-metainfo.h"
|
||||
#include "libtransmission/utils.h"
|
||||
|
@ -236,11 +237,14 @@ bool tr_announce_list::can_add(tr_url_parsed_t const& announce) const noexcept
|
|||
bool tr_announce_list::save(std::string_view torrent_file, tr_error** error) const
|
||||
{
|
||||
// load the torrent file
|
||||
auto metainfo = tr_variant{};
|
||||
if (!tr_variantFromFile(&metainfo, TR_VARIANT_PARSE_BENC, torrent_file, error))
|
||||
auto serde = tr_variant_serde::benc();
|
||||
auto ometainfo = serde.parse_file(torrent_file);
|
||||
if (!ometainfo)
|
||||
{
|
||||
tr_error_propagate(error, &serde.error_);
|
||||
return false;
|
||||
}
|
||||
auto& metainfo = *ometainfo;
|
||||
|
||||
// remove the old fields
|
||||
tr_variantDictRemove(&metainfo, TR_KEY_announce);
|
||||
|
@ -271,9 +275,13 @@ bool tr_announce_list::save(std::string_view torrent_file, tr_error** error) con
|
|||
}
|
||||
|
||||
// confirm that it's good by parsing it back again
|
||||
auto const contents = tr_variantToStr(&metainfo, TR_VARIANT_FMT_BENC);
|
||||
auto const contents = serde.to_string(metainfo);
|
||||
tr_variantClear(&metainfo);
|
||||
if (auto tm = tr_torrent_metainfo{}; !tm.parse_benc(contents, error))
|
||||
if (auto tmp = serde.parse(contents); tmp)
|
||||
{
|
||||
tr_variantClear(&*tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -384,6 +384,11 @@ bool parse(
|
|||
}
|
||||
}
|
||||
|
||||
if (setme_end != nullptr)
|
||||
{
|
||||
*setme_end = std::data(benc);
|
||||
}
|
||||
|
||||
if (err != 0)
|
||||
{
|
||||
errno = err;
|
||||
|
@ -406,11 +411,6 @@ bool parse(
|
|||
return false;
|
||||
}
|
||||
|
||||
if (setme_end != nullptr)
|
||||
{
|
||||
*setme_end = std::data(benc);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -377,7 +377,7 @@ std::string tr_metainfo_builder::benc(tr_error** error) const
|
|||
tr_variantDictAddStr(info_dict, TR_KEY_source, source_);
|
||||
}
|
||||
|
||||
auto ret = tr_variantToStr(&top, TR_VARIANT_FMT_BENC);
|
||||
auto ret = tr_variant_serde::benc().to_string(top);
|
||||
tr_variantClear(&top);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -1023,7 +1023,7 @@ void sendLtepHandshake(tr_peerMsgsImpl* msgs)
|
|||
}
|
||||
}
|
||||
|
||||
protocol_send_message(msgs, BtPeerMsgs::Ltep, LtepMessages::Handshake, tr_variantToStr(&val, TR_VARIANT_FMT_BENC));
|
||||
protocol_send_message(msgs, BtPeerMsgs::Ltep, LtepMessages::Handshake, tr_variant_serde::benc().to_string(val));
|
||||
|
||||
/* cleanup */
|
||||
tr_variantClear(&val);
|
||||
|
@ -1033,8 +1033,8 @@ void parseLtepHandshake(tr_peerMsgsImpl* msgs, MessageReader& payload)
|
|||
{
|
||||
auto const handshake_sv = payload.to_string_view();
|
||||
|
||||
auto val = tr_variant{};
|
||||
if (!tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, handshake_sv) || !tr_variantIsDict(&val))
|
||||
auto var = tr_variant_serde::benc().inplace().parse(handshake_sv);
|
||||
if (!var || !tr_variantIsDict(&*var))
|
||||
{
|
||||
logtrace(msgs, "GET extended-handshake, couldn't get dictionary");
|
||||
return;
|
||||
|
@ -1045,7 +1045,7 @@ void parseLtepHandshake(tr_peerMsgsImpl* msgs, MessageReader& payload)
|
|||
/* does the peer prefer encrypted connections? */
|
||||
auto i = int64_t{};
|
||||
auto pex = tr_pex{};
|
||||
if (tr_variantDictFindInt(&val, TR_KEY_e, &i))
|
||||
if (tr_variantDictFindInt(&*var, TR_KEY_e, &i))
|
||||
{
|
||||
msgs->encryption_preference = i != 0 ? EncryptionPreference::Yes : EncryptionPreference::No;
|
||||
|
||||
|
@ -1059,7 +1059,7 @@ void parseLtepHandshake(tr_peerMsgsImpl* msgs, MessageReader& payload)
|
|||
msgs->peerSupportsPex = false;
|
||||
msgs->peerSupportsMetadataXfer = false;
|
||||
|
||||
if (tr_variant* sub = nullptr; tr_variantDictFindDict(&val, TR_KEY_m, &sub))
|
||||
if (tr_variant* sub = nullptr; tr_variantDictFindDict(&*var, TR_KEY_m, &sub))
|
||||
{
|
||||
if (tr_variantDictFindInt(sub, TR_KEY_ut_pex, &i))
|
||||
{
|
||||
|
@ -1085,13 +1085,13 @@ void parseLtepHandshake(tr_peerMsgsImpl* msgs, MessageReader& payload)
|
|||
}
|
||||
|
||||
/* look for metainfo size (BEP 9) */
|
||||
if (tr_variantDictFindInt(&val, TR_KEY_metadata_size, &i) && tr_torrentSetMetadataSizeHint(msgs->torrent, i))
|
||||
if (tr_variantDictFindInt(&*var, TR_KEY_metadata_size, &i) && tr_torrentSetMetadataSizeHint(msgs->torrent, i))
|
||||
{
|
||||
msgs->metadata_size_hint = i;
|
||||
}
|
||||
|
||||
/* look for upload_only (BEP 21) */
|
||||
if (tr_variantDictFindInt(&val, TR_KEY_upload_only, &i))
|
||||
if (tr_variantDictFindInt(&*var, TR_KEY_upload_only, &i))
|
||||
{
|
||||
pex.flags |= ADDED_F_SEED_FLAG;
|
||||
}
|
||||
|
@ -1100,13 +1100,13 @@ void parseLtepHandshake(tr_peerMsgsImpl* msgs, MessageReader& payload)
|
|||
// Client name and version (as a utf-8 string). This is a much more
|
||||
// reliable way of identifying the client than relying on the
|
||||
// peer id encoding.
|
||||
if (auto sv = std::string_view{}; tr_variantDictFindStrView(&val, TR_KEY_v, &sv))
|
||||
if (auto sv = std::string_view{}; tr_variantDictFindStrView(&*var, TR_KEY_v, &sv))
|
||||
{
|
||||
msgs->set_user_agent(tr_interned_string{ sv });
|
||||
}
|
||||
|
||||
/* get peer's listening port */
|
||||
if (tr_variantDictFindInt(&val, TR_KEY_p, &i) && i > 0)
|
||||
if (tr_variantDictFindInt(&*var, TR_KEY_p, &i) && i > 0)
|
||||
{
|
||||
pex.port.setHost(i);
|
||||
msgs->publish(tr_peer_event::GotPort(pex.port));
|
||||
|
@ -1115,14 +1115,14 @@ void parseLtepHandshake(tr_peerMsgsImpl* msgs, MessageReader& payload)
|
|||
|
||||
uint8_t const* addr = nullptr;
|
||||
auto addr_len = size_t{};
|
||||
if (msgs->io->is_incoming() && tr_variantDictFindRaw(&val, TR_KEY_ipv4, &addr, &addr_len) && addr_len == 4)
|
||||
if (msgs->io->is_incoming() && tr_variantDictFindRaw(&*var, TR_KEY_ipv4, &addr, &addr_len) && addr_len == 4)
|
||||
{
|
||||
pex.addr.type = TR_AF_INET;
|
||||
memcpy(&pex.addr.addr.addr4, addr, 4);
|
||||
tr_peerMgrAddPex(msgs->torrent, TR_PEER_FROM_LTEP, &pex, 1);
|
||||
}
|
||||
|
||||
if (msgs->io->is_incoming() && tr_variantDictFindRaw(&val, TR_KEY_ipv6, &addr, &addr_len) && addr_len == 16)
|
||||
if (msgs->io->is_incoming() && tr_variantDictFindRaw(&*var, TR_KEY_ipv6, &addr, &addr_len) && addr_len == 16)
|
||||
{
|
||||
pex.addr.type = TR_AF_INET6;
|
||||
memcpy(&pex.addr.addr.addr6, addr, 16);
|
||||
|
@ -1130,12 +1130,12 @@ void parseLtepHandshake(tr_peerMsgsImpl* msgs, MessageReader& payload)
|
|||
}
|
||||
|
||||
/* get peer's maximum request queue size */
|
||||
if (tr_variantDictFindInt(&val, TR_KEY_reqq, &i))
|
||||
if (tr_variantDictFindInt(&*var, TR_KEY_reqq, &i))
|
||||
{
|
||||
msgs->reqq = i;
|
||||
}
|
||||
|
||||
tr_variantClear(&val);
|
||||
tr_variantClear(&*var);
|
||||
}
|
||||
|
||||
void parseUtMetadata(tr_peerMsgsImpl* msgs, MessageReader& payload_in)
|
||||
|
@ -1147,14 +1147,13 @@ void parseUtMetadata(tr_peerMsgsImpl* msgs, MessageReader& payload_in)
|
|||
auto const tmp = payload_in.to_string_view();
|
||||
auto const* const msg_end = std::data(tmp) + std::size(tmp);
|
||||
|
||||
auto dict = tr_variant{};
|
||||
char const* benc_end = nullptr;
|
||||
if (tr_variantFromBuf(&dict, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, tmp, &benc_end))
|
||||
auto serde = tr_variant_serde::benc();
|
||||
if (auto var = serde.inplace().parse(tmp); var)
|
||||
{
|
||||
(void)tr_variantDictFindInt(&dict, TR_KEY_msg_type, &msg_type);
|
||||
(void)tr_variantDictFindInt(&dict, TR_KEY_piece, &piece);
|
||||
(void)tr_variantDictFindInt(&dict, TR_KEY_total_size, &total_size);
|
||||
tr_variantClear(&dict);
|
||||
(void)tr_variantDictFindInt(&*var, TR_KEY_msg_type, &msg_type);
|
||||
(void)tr_variantDictFindInt(&*var, TR_KEY_piece, &piece);
|
||||
(void)tr_variantDictFindInt(&*var, TR_KEY_total_size, &total_size);
|
||||
tr_variantClear(&*var);
|
||||
}
|
||||
|
||||
logtrace(
|
||||
|
@ -1166,6 +1165,8 @@ void parseUtMetadata(tr_peerMsgsImpl* msgs, MessageReader& payload_in)
|
|||
/* NOOP */
|
||||
}
|
||||
|
||||
auto const* const benc_end = serde.end();
|
||||
|
||||
if (msg_type == MetadataMsgType::Data && total_size == msgs->metadata_size_hint && !msgs->torrent->has_metainfo() &&
|
||||
msg_end - benc_end <= MetadataPieceSize && piece * MetadataPieceSize + (msg_end - benc_end) <= total_size)
|
||||
{
|
||||
|
@ -1187,7 +1188,7 @@ void parseUtMetadata(tr_peerMsgsImpl* msgs, MessageReader& payload_in)
|
|||
tr_variantInitDict(&v, 2);
|
||||
tr_variantDictAddInt(&v, TR_KEY_msg_type, MetadataMsgType::Reject);
|
||||
tr_variantDictAddInt(&v, TR_KEY_piece, piece);
|
||||
protocol_send_message(msgs, BtPeerMsgs::Ltep, msgs->ut_metadata_id, tr_variantToStr(&v, TR_VARIANT_FMT_BENC));
|
||||
protocol_send_message(msgs, BtPeerMsgs::Ltep, msgs->ut_metadata_id, serde.to_string(v));
|
||||
tr_variantClear(&v);
|
||||
}
|
||||
}
|
||||
|
@ -1201,17 +1202,15 @@ void parseUtPex(tr_peerMsgsImpl* msgs, MessageReader& payload)
|
|||
return;
|
||||
}
|
||||
|
||||
auto const tmp = payload.to_string_view();
|
||||
|
||||
if (tr_variant val; tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, tmp))
|
||||
if (auto var = tr_variant_serde::benc().inplace().parse(payload.to_string_view()); var)
|
||||
{
|
||||
uint8_t const* added = nullptr;
|
||||
auto added_len = size_t{};
|
||||
if (tr_variantDictFindRaw(&val, TR_KEY_added, &added, &added_len))
|
||||
if (tr_variantDictFindRaw(&*var, TR_KEY_added, &added, &added_len))
|
||||
{
|
||||
uint8_t const* added_f = nullptr;
|
||||
auto added_f_len = size_t{};
|
||||
if (!tr_variantDictFindRaw(&val, TR_KEY_added_f, &added_f, &added_f_len))
|
||||
if (!tr_variantDictFindRaw(&*var, TR_KEY_added_f, &added_f, &added_f_len))
|
||||
{
|
||||
added_f_len = 0;
|
||||
added_f = nullptr;
|
||||
|
@ -1222,11 +1221,11 @@ void parseUtPex(tr_peerMsgsImpl* msgs, MessageReader& payload)
|
|||
tr_peerMgrAddPex(tor, TR_PEER_FROM_PEX, std::data(pex), std::size(pex));
|
||||
}
|
||||
|
||||
if (tr_variantDictFindRaw(&val, TR_KEY_added6, &added, &added_len))
|
||||
if (tr_variantDictFindRaw(&*var, TR_KEY_added6, &added, &added_len))
|
||||
{
|
||||
uint8_t const* added_f = nullptr;
|
||||
auto added_f_len = size_t{};
|
||||
if (!tr_variantDictFindRaw(&val, TR_KEY_added6_f, &added_f, &added_f_len))
|
||||
if (!tr_variantDictFindRaw(&*var, TR_KEY_added6_f, &added_f, &added_f_len))
|
||||
{
|
||||
added_f_len = 0;
|
||||
added_f = nullptr;
|
||||
|
@ -1237,7 +1236,7 @@ void parseUtPex(tr_peerMsgsImpl* msgs, MessageReader& payload)
|
|||
tr_peerMgrAddPex(tor, TR_PEER_FROM_PEX, std::data(pex), std::size(pex));
|
||||
}
|
||||
|
||||
tr_variantClear(&val);
|
||||
tr_variantClear(&*var);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1812,7 +1811,7 @@ void updateMetadataRequests(tr_peerMsgsImpl* msgs, time_t now)
|
|||
tr_variantInitDict(&tmp, 3);
|
||||
tr_variantDictAddInt(&tmp, TR_KEY_msg_type, MetadataMsgType::Request);
|
||||
tr_variantDictAddInt(&tmp, TR_KEY_piece, *piece);
|
||||
protocol_send_message(msgs, BtPeerMsgs::Ltep, msgs->ut_metadata_id, tr_variantToStr(&tmp, TR_VARIANT_FMT_BENC));
|
||||
protocol_send_message(msgs, BtPeerMsgs::Ltep, msgs->ut_metadata_id, tr_variant_serde::benc().to_string(tmp));
|
||||
tr_variantClear(&tmp);
|
||||
}
|
||||
}
|
||||
|
@ -1870,7 +1869,7 @@ namespace peer_pulse_helpers
|
|||
msgs,
|
||||
BtPeerMsgs::Ltep,
|
||||
msgs->ut_metadata_id,
|
||||
tr_variantToStr(&tmp, TR_VARIANT_FMT_BENC));
|
||||
tr_variant_serde::benc().to_string(tmp));
|
||||
tr_variantClear(&tmp);
|
||||
return n_bytes_written;
|
||||
}
|
||||
|
@ -1885,7 +1884,7 @@ namespace peer_pulse_helpers
|
|||
msgs,
|
||||
BtPeerMsgs::Ltep,
|
||||
msgs->ut_metadata_id,
|
||||
tr_variantToStr(&tmp, TR_VARIANT_FMT_BENC),
|
||||
tr_variant_serde::benc().to_string(tmp),
|
||||
data);
|
||||
tr_variantClear(&tmp);
|
||||
return n_bytes_written;
|
||||
|
@ -2114,7 +2113,7 @@ void tr_peerMsgsImpl::sendPex()
|
|||
}
|
||||
}
|
||||
|
||||
protocol_send_message(this, BtPeerMsgs::Ltep, this->ut_pex_id, tr_variantToStr(&val, TR_VARIANT_FMT_BENC));
|
||||
protocol_send_message(this, BtPeerMsgs::Ltep, this->ut_pex_id, tr_variant_serde::benc().to_string(val));
|
||||
|
||||
tr_variantClear(&val);
|
||||
}
|
||||
|
|
|
@ -619,10 +619,8 @@ auto loadProgress(tr_variant* dict, tr_torrent* tor)
|
|||
|
||||
// ---
|
||||
|
||||
auto loadFromFile(tr_torrent* tor, tr_resume::fields_t fields_to_load)
|
||||
tr_resume::fields_t loadFromFile(tr_torrent* tor, tr_resume::fields_t fields_to_load)
|
||||
{
|
||||
auto fields_loaded = tr_resume::fields_t{};
|
||||
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
auto const was_dirty = tor->is_dirty();
|
||||
|
||||
|
@ -631,22 +629,20 @@ auto loadFromFile(tr_torrent* tor, tr_resume::fields_t fields_to_load)
|
|||
auto const filename = tor->resume_file();
|
||||
if (!tr_sys_path_exists(filename))
|
||||
{
|
||||
return fields_loaded;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto buf = std::vector<char>{};
|
||||
tr_error* error = nullptr;
|
||||
auto top = tr_variant{};
|
||||
if (!tr_file_read(filename, buf, &error) ||
|
||||
!tr_variantFromBuf(&top, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, buf, nullptr, &error))
|
||||
auto serde = tr_variant_serde::benc();
|
||||
auto otop = serde.parse_file(filename);
|
||||
if (!otop)
|
||||
{
|
||||
tr_logAddDebugTor(tor, fmt::format("Couldn't read '{}': {}", filename, error->message));
|
||||
tr_error_clear(&error);
|
||||
return fields_loaded;
|
||||
tr_logAddDebugTor(tor, fmt::format("Couldn't read '{}': {}", filename, serde.error_->message));
|
||||
return {};
|
||||
}
|
||||
auto& top = *otop;
|
||||
|
||||
tr_logAddDebugTor(tor, fmt::format("Read resume file '{}'", filename));
|
||||
|
||||
auto fields_loaded = tr_resume::fields_t{};
|
||||
auto i = int64_t{};
|
||||
auto sv = std::string_view{};
|
||||
|
||||
|
@ -916,10 +912,10 @@ void save(tr_torrent* tor)
|
|||
saveLabels(&top, tor);
|
||||
saveGroup(&top, tor);
|
||||
|
||||
auto const resume_file = tor->resume_file();
|
||||
if (auto const err = tr_variantToFile(&top, TR_VARIANT_FMT_BENC, resume_file); err != 0)
|
||||
auto serde = tr_variant_serde::benc();
|
||||
if (!serde.to_file(top, tor->resume_file()))
|
||||
{
|
||||
tor->set_local_error(fmt::format("Unable to save resume file: {:s}", tr_strerror(err)));
|
||||
tor->set_local_error(fmt::format("Unable to save resume file: {:s}", serde.error_->message));
|
||||
}
|
||||
|
||||
tr_variantClear(&top);
|
||||
|
|
|
@ -351,7 +351,7 @@ void rpc_response_func(tr_session* /*session*/, tr_variant* content, void* user_
|
|||
{
|
||||
auto* data = static_cast<struct rpc_response_data*>(user_data);
|
||||
|
||||
auto* const response = make_response(data->req, data->server, tr_variantToStr(content, TR_VARIANT_FMT_JSON_LEAN));
|
||||
auto* const response = make_response(data->req, data->server, tr_variant_serde::json().compact().to_string(*content));
|
||||
evhttp_add_header(data->req->output_headers, "Content-Type", "application/json; charset=UTF-8");
|
||||
evhttp_send_reply(data->req, HTTP_OK, "OK", response);
|
||||
evbuffer_free(response);
|
||||
|
@ -361,18 +361,13 @@ void rpc_response_func(tr_session* /*session*/, tr_variant* content, void* user_
|
|||
|
||||
void handle_rpc_from_json(struct evhttp_request* req, tr_rpc_server* server, std::string_view json)
|
||||
{
|
||||
auto top = tr_variant{};
|
||||
auto const have_content = tr_variantFromBuf(&top, TR_VARIANT_PARSE_JSON | TR_VARIANT_PARSE_INPLACE, json);
|
||||
auto otop = tr_variant_serde::json().inplace().parse(json);
|
||||
|
||||
tr_rpc_request_exec_json(
|
||||
server->session,
|
||||
have_content ? &top : nullptr,
|
||||
rpc_response_func,
|
||||
new rpc_response_data{ req, server });
|
||||
tr_rpc_request_exec_json(server->session, otop ? &*otop : nullptr, rpc_response_func, new rpc_response_data{ req, server });
|
||||
|
||||
if (have_content)
|
||||
if (otop)
|
||||
{
|
||||
tr_variantClear(&top);
|
||||
tr_variantClear(&*otop);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -71,9 +71,13 @@ auto constexpr BandwidthGroupsFilename = "bandwidth-groups.json"sv;
|
|||
void bandwidthGroupRead(tr_session* session, std::string_view config_dir)
|
||||
{
|
||||
auto const filename = tr_pathbuf{ config_dir, '/', BandwidthGroupsFilename };
|
||||
auto groups_dict = tr_variant{};
|
||||
if (!tr_sys_path_exists(filename) || !tr_variantFromFile(&groups_dict, TR_VARIANT_PARSE_JSON, filename, nullptr) ||
|
||||
!tr_variantIsDict(&groups_dict))
|
||||
if (!tr_sys_path_exists(filename))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto groups_var = tr_variant_serde::json().parse_file(filename);
|
||||
if (!groups_var)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -81,7 +85,7 @@ void bandwidthGroupRead(tr_session* session, std::string_view config_dir)
|
|||
auto idx = size_t{ 0 };
|
||||
auto key = tr_quark{};
|
||||
tr_variant* dict = nullptr;
|
||||
while (tr_variantDictChild(&groups_dict, idx, &key, &dict))
|
||||
while (tr_variantDictChild(&*groups_var, idx, &key, &dict))
|
||||
{
|
||||
++idx;
|
||||
|
||||
|
@ -110,10 +114,10 @@ void bandwidthGroupRead(tr_session* session, std::string_view config_dir)
|
|||
group.honor_parent_limits(TR_DOWN, honors);
|
||||
}
|
||||
}
|
||||
tr_variantClear(&groups_dict);
|
||||
tr_variantClear(&*groups_var);
|
||||
}
|
||||
|
||||
int bandwidthGroupWrite(tr_session const* session, std::string_view config_dir)
|
||||
void bandwidthGroupWrite(tr_session const* session, std::string_view config_dir)
|
||||
{
|
||||
auto const& groups = session->bandwidthGroups();
|
||||
|
||||
|
@ -134,9 +138,8 @@ int bandwidthGroupWrite(tr_session const* session, std::string_view config_dir)
|
|||
}
|
||||
|
||||
auto const filename = tr_pathbuf{ config_dir, '/', BandwidthGroupsFilename };
|
||||
auto const ret = tr_variantToFile(&groups_dict, TR_VARIANT_FMT_JSON, filename);
|
||||
tr_variant_serde::json().to_file(groups_dict, filename);
|
||||
tr_variantClear(&groups_dict);
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace bandwidth_group_helpers
|
||||
|
@ -501,10 +504,10 @@ bool tr_sessionLoadSettings(tr_variant* dict, char const* config_dir, char const
|
|||
{
|
||||
success = true;
|
||||
}
|
||||
else if (auto file_settings = tr_variant{}; tr_variantFromFile(&file_settings, TR_VARIANT_PARSE_JSON, filename))
|
||||
else if (auto file_settings = tr_variant_serde::json().parse_file(filename); file_settings)
|
||||
{
|
||||
tr_variantMergeDicts(dict, &file_settings);
|
||||
tr_variantClear(&file_settings);
|
||||
tr_variantMergeDicts(dict, &*file_settings);
|
||||
tr_variantClear(&*file_settings);
|
||||
success = true;
|
||||
}
|
||||
else
|
||||
|
@ -528,10 +531,10 @@ void tr_sessionSaveSettings(tr_session* session, char const* config_dir, tr_vari
|
|||
tr_variantInitDict(&settings, 0);
|
||||
|
||||
/* the existing file settings are the fallback values */
|
||||
if (auto file_settings = tr_variant{}; tr_variantFromFile(&file_settings, TR_VARIANT_PARSE_JSON, filename))
|
||||
if (auto file_settings = tr_variant_serde::json().parse_file(filename); file_settings)
|
||||
{
|
||||
tr_variantMergeDicts(&settings, &file_settings);
|
||||
tr_variantClear(&file_settings);
|
||||
tr_variantMergeDicts(&settings, &*file_settings);
|
||||
tr_variantClear(&*file_settings);
|
||||
}
|
||||
|
||||
/* the client's settings override the file settings */
|
||||
|
@ -547,7 +550,7 @@ void tr_sessionSaveSettings(tr_session* session, char const* config_dir, tr_vari
|
|||
}
|
||||
|
||||
/* save the result */
|
||||
tr_variantToFile(&settings, TR_VARIANT_FMT_JSON, filename);
|
||||
tr_variant_serde::json().to_file(settings, filename);
|
||||
|
||||
/* cleanup */
|
||||
tr_variantClear(&settings);
|
||||
|
|
|
@ -14,52 +14,49 @@
|
|||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
std::optional<tr_variant> load_stats(std::string_view config_dir)
|
||||
{
|
||||
if (auto filename = tr_pathbuf{ config_dir, "/stats.json"sv }; tr_sys_path_exists(filename))
|
||||
{
|
||||
return tr_variant_serde::json().parse_file(filename);
|
||||
}
|
||||
|
||||
// maybe the user just upgraded from an old version of Transmission
|
||||
// that was still using stats.benc
|
||||
if (auto filename = tr_pathbuf{ config_dir, "/stats.benc"sv }; tr_sys_path_exists(filename))
|
||||
{
|
||||
return tr_variant_serde::benc().parse_file(filename);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
tr_session_stats tr_stats::load_old_stats(std::string_view config_dir)
|
||||
{
|
||||
auto ret = tr_session_stats{};
|
||||
|
||||
auto top = tr_variant{};
|
||||
auto filename = tr_pathbuf{ config_dir, "/stats.json"sv };
|
||||
bool loaded = tr_sys_path_exists(filename) && tr_variantFromFile(&top, TR_VARIANT_PARSE_JSON, filename.sv(), nullptr);
|
||||
|
||||
if (!loaded)
|
||||
if (auto stats = load_stats(config_dir); stats)
|
||||
{
|
||||
// maybe the user just upgraded from an old version of Transmission
|
||||
// that was still using stats.benc
|
||||
filename.assign(config_dir, "/stats.benc");
|
||||
loaded = tr_sys_path_exists(filename) && tr_variantFromFile(&top, TR_VARIANT_PARSE_BENC, filename.sv(), nullptr);
|
||||
}
|
||||
auto const key_tgts = std::array<std::pair<tr_quark, uint64_t*>, 5>{
|
||||
{ { TR_KEY_downloaded_bytes, &ret.downloadedBytes },
|
||||
{ TR_KEY_files_added, &ret.filesAdded },
|
||||
{ TR_KEY_seconds_active, &ret.secondsActive },
|
||||
{ TR_KEY_session_count, &ret.sessionCount },
|
||||
{ TR_KEY_uploaded_bytes, &ret.uploadedBytes } }
|
||||
};
|
||||
|
||||
if (loaded)
|
||||
{
|
||||
auto i = int64_t{};
|
||||
|
||||
if (tr_variantDictFindInt(&top, TR_KEY_downloaded_bytes, &i))
|
||||
for (auto& [key, tgt] : key_tgts)
|
||||
{
|
||||
ret.downloadedBytes = (uint64_t)i;
|
||||
if (auto val = int64_t{}; tr_variantDictFindInt(&*stats, key, &val))
|
||||
{
|
||||
*tgt = val;
|
||||
}
|
||||
}
|
||||
|
||||
if (tr_variantDictFindInt(&top, TR_KEY_files_added, &i))
|
||||
{
|
||||
ret.filesAdded = (uint64_t)i;
|
||||
}
|
||||
|
||||
if (tr_variantDictFindInt(&top, TR_KEY_seconds_active, &i))
|
||||
{
|
||||
ret.secondsActive = (uint64_t)i;
|
||||
}
|
||||
|
||||
if (tr_variantDictFindInt(&top, TR_KEY_session_count, &i))
|
||||
{
|
||||
ret.sessionCount = (uint64_t)i;
|
||||
}
|
||||
|
||||
if (tr_variantDictFindInt(&top, TR_KEY_uploaded_bytes, &i))
|
||||
{
|
||||
ret.uploadedBytes = (uint64_t)i;
|
||||
}
|
||||
|
||||
tr_variantClear(&top);
|
||||
tr_variantClear(&*stats);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -68,7 +65,6 @@ tr_session_stats tr_stats::load_old_stats(std::string_view config_dir)
|
|||
void tr_stats::save() const
|
||||
{
|
||||
auto const saveme = cumulative();
|
||||
auto const filename = tr_pathbuf{ config_dir_, "/stats.json"sv };
|
||||
auto top = tr_variant{};
|
||||
tr_variantInitDict(&top, 5);
|
||||
tr_variantDictAddInt(&top, TR_KEY_downloaded_bytes, saveme.downloadedBytes);
|
||||
|
@ -76,7 +72,7 @@ void tr_stats::save() const
|
|||
tr_variantDictAddInt(&top, TR_KEY_seconds_active, saveme.secondsActive);
|
||||
tr_variantDictAddInt(&top, TR_KEY_session_count, saveme.sessionCount);
|
||||
tr_variantDictAddInt(&top, TR_KEY_uploaded_bytes, saveme.uploadedBytes);
|
||||
tr_variantToFile(&top, TR_VARIANT_FMT_JSON, filename);
|
||||
tr_variant_serde::json().to_file(top, tr_pathbuf{ config_dir_, "/stats.json"sv });
|
||||
tr_variantClear(&top);
|
||||
}
|
||||
|
||||
|
|
|
@ -229,19 +229,21 @@ bool use_new_metainfo(tr_torrent* tor, tr_error** error)
|
|||
}
|
||||
|
||||
// checksum passed; now try to parse it as benc
|
||||
auto info_dict_v = tr_variant{};
|
||||
if (!tr_variantFromBuf(&info_dict_v, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, m->metadata, nullptr, error))
|
||||
auto serde = tr_variant_serde::benc().inplace();
|
||||
auto info_dict_v = serde.parse(m->metadata);
|
||||
if (!info_dict_v)
|
||||
{
|
||||
tr_error_propagate(error, &serde.error_);
|
||||
return false;
|
||||
}
|
||||
|
||||
// yay we have an info dict. Let's make a torrent file
|
||||
auto top_v = tr_variant{};
|
||||
build_metainfo_except_info_dict(tor->metainfo_, &top_v);
|
||||
tr_variantMergeDicts(tr_variantDictAddDict(&top_v, TR_KEY_info, 0), &info_dict_v);
|
||||
auto const benc = tr_variantToStr(&top_v, TR_VARIANT_FMT_BENC);
|
||||
tr_variantMergeDicts(tr_variantDictAddDict(&top_v, TR_KEY_info, 0), &*info_dict_v);
|
||||
auto const benc = serde.to_string(top_v);
|
||||
tr_variantClear(&top_v);
|
||||
tr_variantClear(&info_dict_v);
|
||||
tr_variantClear(&*info_dict_v);
|
||||
|
||||
// does this synthetic torrent file parse?
|
||||
auto metainfo = tr_torrent_metainfo{};
|
||||
|
|
|
@ -459,7 +459,7 @@ private:
|
|||
tr_variantDictAddRaw(&benc, TR_KEY_nodes6, std::data(compact6), out6 - std::data(compact6));
|
||||
}
|
||||
|
||||
tr_variantToFile(&benc, TR_VARIANT_FMT_BENC, state_filename_);
|
||||
tr_variant_serde::benc().to_file(benc, state_filename_);
|
||||
tr_variantClear(&benc);
|
||||
}
|
||||
|
||||
|
@ -471,17 +471,18 @@ private:
|
|||
|
||||
auto nodes = Nodes{};
|
||||
|
||||
if (auto dict = tr_variant{}; tr_variantFromFile(&dict, TR_VARIANT_PARSE_BENC, filename))
|
||||
if (auto otop = tr_variant_serde::benc().parse_file(filename); otop)
|
||||
{
|
||||
if (auto sv = std::string_view{};
|
||||
tr_variantDictFindStrView(&dict, TR_KEY_id, &sv) && std::size(sv) == std::size(id))
|
||||
auto& top = *otop;
|
||||
|
||||
if (auto sv = std::string_view{}; tr_variantDictFindStrView(&top, TR_KEY_id, &sv) && std::size(sv) == std::size(id))
|
||||
{
|
||||
std::copy(std::begin(sv), std::end(sv), std::begin(id));
|
||||
}
|
||||
|
||||
size_t raw_len = 0U;
|
||||
std::byte const* raw = nullptr;
|
||||
if (tr_variantDictFindRaw(&dict, TR_KEY_nodes, &raw, &raw_len) && raw_len % 6 == 0)
|
||||
if (tr_variantDictFindRaw(&top, TR_KEY_nodes, &raw, &raw_len) && raw_len % 6 == 0)
|
||||
{
|
||||
auto* walk = raw;
|
||||
auto const* const end = raw + raw_len;
|
||||
|
@ -495,7 +496,7 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
if (tr_variantDictFindRaw(&dict, TR_KEY_nodes6, &raw, &raw_len) && raw_len % 18 == 0)
|
||||
if (tr_variantDictFindRaw(&top, TR_KEY_nodes6, &raw, &raw_len) && raw_len % 18 == 0)
|
||||
{
|
||||
auto* walk = raw;
|
||||
auto const* const end = raw + raw_len;
|
||||
|
@ -509,7 +510,7 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
tr_variantClear(&dict);
|
||||
tr_variantClear(&top);
|
||||
}
|
||||
|
||||
return std::make_pair(id, nodes);
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include "libtransmission/quark.h"
|
||||
#include "libtransmission/tr-buffer.h"
|
||||
#include "libtransmission/utils.h"
|
||||
#include "libtransmission/variant-common.h"
|
||||
#include "libtransmission/variant.h"
|
||||
|
||||
struct tr_error;
|
||||
|
@ -135,13 +134,13 @@ namespace parse_helpers
|
|||
struct MyHandler : public transmission::benc::Handler
|
||||
{
|
||||
tr_variant* const top_;
|
||||
int const parse_opts_;
|
||||
bool inplace_;
|
||||
std::deque<tr_variant*> stack_;
|
||||
std::optional<tr_quark> key_;
|
||||
|
||||
MyHandler(tr_variant* top, int parse_opts)
|
||||
MyHandler(tr_variant* top, bool inplace)
|
||||
: top_{ top }
|
||||
, parse_opts_{ parse_opts }
|
||||
, inplace_{ inplace }
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -172,7 +171,7 @@ struct MyHandler : public transmission::benc::Handler
|
|||
return false;
|
||||
}
|
||||
|
||||
if ((parse_opts_ & TR_VARIANT_PARSE_INPLACE) != 0)
|
||||
if (inplace_)
|
||||
{
|
||||
tr_variantInitStrView(variant, sv);
|
||||
}
|
||||
|
@ -264,14 +263,21 @@ private:
|
|||
} // namespace parse_helpers
|
||||
} // namespace
|
||||
|
||||
bool tr_variantParseBenc(tr_variant& top, int parse_opts, std::string_view benc, char const** setme_end, tr_error** error)
|
||||
std::optional<tr_variant> tr_variant_serde::parse_benc(std::string_view input)
|
||||
{
|
||||
using namespace parse_helpers;
|
||||
using Stack = transmission::benc::ParserStack<512>;
|
||||
|
||||
auto top = tr_variant{};
|
||||
auto stack = Stack{};
|
||||
auto handler = MyHandler{ &top, parse_opts };
|
||||
return transmission::benc::parse(benc, stack, handler, setme_end, error) && std::empty(stack);
|
||||
auto handler = MyHandler{ &top, parse_inplace_ };
|
||||
if (transmission::benc::parse(input, stack, handler, &end_, &error_) && std::empty(stack))
|
||||
{
|
||||
return top;
|
||||
}
|
||||
|
||||
tr_variantClear(&top);
|
||||
return {};
|
||||
}
|
||||
|
||||
// ---
|
||||
|
@ -282,20 +288,20 @@ namespace to_string_helpers
|
|||
{
|
||||
using OutBuf = libtransmission::StackBuffer<1024U * 8U, std::byte>;
|
||||
|
||||
void saveIntFunc(tr_variant const* val, void* vout)
|
||||
void saveIntFunc(tr_variant const& /*var*/, int64_t const val, void* vout)
|
||||
{
|
||||
auto out = static_cast<OutBuf*>(vout);
|
||||
|
||||
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);
|
||||
walk = fmt::format_to(walk, FMT_COMPILE("i{:d}e"), val);
|
||||
out->commit_space(walk - begin);
|
||||
}
|
||||
|
||||
void saveBoolFunc(tr_variant const* val, void* vout)
|
||||
void saveBoolFunc(tr_variant const& /*var*/, bool const val, void* vout)
|
||||
{
|
||||
static_cast<OutBuf*>(vout)->add(val->val.b ? "i1e"sv : "i0e"sv);
|
||||
static_cast<OutBuf*>(vout)->add(val ? "i1e"sv : "i0e"sv);
|
||||
}
|
||||
|
||||
void saveStringImpl(OutBuf* out, std::string_view sv)
|
||||
|
@ -309,55 +315,52 @@ void saveStringImpl(OutBuf* out, std::string_view sv)
|
|||
out->commit_space(walk - begin);
|
||||
}
|
||||
|
||||
void saveStringFunc(tr_variant const* v, void* vout)
|
||||
void saveStringFunc(tr_variant const& /*var*/, std::string_view const val, void* vout)
|
||||
{
|
||||
auto sv = std::string_view{};
|
||||
(void)!tr_variantGetStrView(v, &sv);
|
||||
saveStringImpl(static_cast<OutBuf*>(vout), sv);
|
||||
saveStringImpl(static_cast<OutBuf*>(vout), val);
|
||||
}
|
||||
|
||||
void saveRealFunc(tr_variant const* val, void* vout)
|
||||
void saveRealFunc(tr_variant const& /*val*/, double const val, void* vout)
|
||||
{
|
||||
// the benc spec doesn't handle floats; save it as a string.
|
||||
|
||||
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);
|
||||
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<OutBuf*>(vbuf)->push_back('d');
|
||||
}
|
||||
|
||||
void saveListBeginFunc(tr_variant const* /*val*/, void* vbuf)
|
||||
void saveListBeginFunc(tr_variant const& /*val*/, void* vbuf)
|
||||
{
|
||||
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<OutBuf*>(vbuf)->push_back('e');
|
||||
}
|
||||
|
||||
struct VariantWalkFuncs const walk_funcs = {
|
||||
saveIntFunc, //
|
||||
saveBoolFunc, //
|
||||
saveRealFunc, //
|
||||
saveStringFunc, //
|
||||
saveDictBeginFunc, //
|
||||
saveListBeginFunc, //
|
||||
saveContainerEndFunc, //
|
||||
};
|
||||
|
||||
} // namespace to_string_helpers
|
||||
} // namespace
|
||||
|
||||
std::string tr_variantToStrBenc(tr_variant const* top)
|
||||
std::string tr_variant_serde::to_benc_string(tr_variant const& var)
|
||||
{
|
||||
using namespace to_string_helpers;
|
||||
|
||||
static auto constexpr Funcs = WalkFuncs{
|
||||
saveIntFunc, //
|
||||
saveBoolFunc, //
|
||||
saveRealFunc, //
|
||||
saveStringFunc, //
|
||||
saveDictBeginFunc, //
|
||||
saveListBeginFunc, //
|
||||
saveContainerEndFunc, //
|
||||
};
|
||||
|
||||
auto buf = OutBuf{};
|
||||
tr_variantWalk(top, &walk_funcs, &buf, true);
|
||||
walk(var, Funcs, &buf, true);
|
||||
return buf.to_string();
|
||||
}
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
// This file Copyright © 2008-2023 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
|
||||
|
||||
#ifndef LIBTRANSMISSION_VARIANT_MODULE
|
||||
#error only libtransmission/variant-*.c should #include this header.
|
||||
#endif
|
||||
|
||||
#include <cstdint> // int64_t
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
#include "variant.h"
|
||||
|
||||
using VariantWalkFunc = void (*)(tr_variant const* val, void* user_data);
|
||||
|
||||
struct VariantWalkFuncs
|
||||
{
|
||||
VariantWalkFunc intFunc;
|
||||
VariantWalkFunc boolFunc;
|
||||
VariantWalkFunc realFunc;
|
||||
VariantWalkFunc stringFunc;
|
||||
VariantWalkFunc dictBeginFunc;
|
||||
VariantWalkFunc listBeginFunc;
|
||||
VariantWalkFunc containerEndFunc;
|
||||
};
|
||||
|
||||
void tr_variantWalk(tr_variant const* top, VariantWalkFuncs const* walk_funcs, void* user_data, bool sort_dicts);
|
||||
|
||||
[[nodiscard]] std::string tr_variantToStrJson(tr_variant const* top, bool lean);
|
||||
|
||||
[[nodiscard]] std::string tr_variantToStrBenc(tr_variant const* top);
|
||||
|
||||
/** @brief Private function that's exposed here only for unit tests */
|
||||
[[nodiscard]] std::optional<int64_t> tr_bencParseInt(std::string_view* benc_inout);
|
||||
|
||||
/** @brief Private function that's exposed here only for unit tests */
|
||||
[[nodiscard]] std::optional<std::string_view> tr_bencParseStr(std::string_view* benc_inout);
|
||||
|
||||
bool tr_variantParseBenc(tr_variant& top, int parse_opts, std::string_view benc, char const** setme_end, tr_error** error);
|
||||
|
||||
bool tr_variantParseJson(tr_variant& setme, int opts, std::string_view json, char const** setme_end, tr_error** error);
|
|
@ -33,7 +33,6 @@
|
|||
#include "libtransmission/tr-assert.h"
|
||||
#include "libtransmission/tr-buffer.h"
|
||||
#include "libtransmission/utils.h"
|
||||
#include "libtransmission/variant-common.h"
|
||||
#include "libtransmission/variant.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
@ -55,7 +54,7 @@ struct json_wrapper_data
|
|||
tr_error* error;
|
||||
std::deque<tr_variant*> stack;
|
||||
tr_variant* top;
|
||||
int parse_opts;
|
||||
bool inplace = false;
|
||||
|
||||
/* A very common pattern is for a container's children to be similar,
|
||||
* e.g. they may all be objects with the same set of keys. So when
|
||||
|
@ -317,7 +316,7 @@ void action_callback_POP(jsonsl_t jsn, jsonsl_action_t /*action*/, struct jsonsl
|
|||
if (state->type == JSONSL_T_STRING)
|
||||
{
|
||||
auto const [str, inplace] = extract_string(jsn, state, data->strbuf);
|
||||
if (inplace && ((data->parse_opts & TR_VARIANT_PARSE_INPLACE) != 0))
|
||||
if (inplace && data->inplace)
|
||||
{
|
||||
tr_variantInitStrView(get_node(jsn), str);
|
||||
}
|
||||
|
@ -373,53 +372,55 @@ void action_callback_POP(jsonsl_t jsn, jsonsl_action_t /*action*/, struct jsonsl
|
|||
} // namespace parse_helpers
|
||||
} // namespace
|
||||
|
||||
bool tr_variantParseJson(tr_variant& setme, int parse_opts, std::string_view json, char const** setme_end, tr_error** error)
|
||||
std::optional<tr_variant> tr_variant_serde::parse_json(std::string_view input)
|
||||
{
|
||||
using namespace parse_helpers;
|
||||
|
||||
TR_ASSERT((parse_opts & TR_VARIANT_PARSE_JSON) != 0);
|
||||
auto top = tr_variant{};
|
||||
|
||||
auto data = json_wrapper_data{};
|
||||
data.error = nullptr;
|
||||
data.size = std::size(input);
|
||||
data.has_content = false;
|
||||
data.key = ""sv;
|
||||
data.inplace = parse_inplace_;
|
||||
data.preallocGuess = {};
|
||||
data.stack = {};
|
||||
data.top = ⊤
|
||||
|
||||
jsonsl_t jsn = jsonsl_new(MaxDepth);
|
||||
auto jsn = jsonsl_new(MaxDepth);
|
||||
jsn->action_callback_PUSH = action_callback_PUSH;
|
||||
jsn->action_callback_POP = action_callback_POP;
|
||||
jsn->error_callback = error_callback;
|
||||
jsn->data = &data;
|
||||
jsonsl_enable_all_callbacks(jsn);
|
||||
|
||||
data.error = nullptr;
|
||||
data.size = std::size(json);
|
||||
data.has_content = false;
|
||||
data.key = ""sv;
|
||||
data.parse_opts = parse_opts;
|
||||
data.preallocGuess = {};
|
||||
data.stack = {};
|
||||
data.top = &setme;
|
||||
// parse it
|
||||
jsonsl_feed(jsn, static_cast<jsonsl_char_t const*>(std::data(input)), std::size(input));
|
||||
|
||||
/* parse it */
|
||||
jsonsl_feed(jsn, static_cast<jsonsl_char_t const*>(std::data(json)), std::size(json));
|
||||
|
||||
/* EINVAL if there was no content */
|
||||
// EINVAL if there was no content
|
||||
if (data.error == nullptr && !data.has_content)
|
||||
{
|
||||
tr_error_set(&data.error, EINVAL, "No content");
|
||||
}
|
||||
|
||||
/* maybe set the end ptr */
|
||||
if (setme_end != nullptr)
|
||||
{
|
||||
*setme_end = std::data(json) + jsn->pos;
|
||||
}
|
||||
end_ = std::data(input) + jsn->pos;
|
||||
|
||||
/* cleanup */
|
||||
auto const success = data.error == nullptr;
|
||||
if (data.error != nullptr)
|
||||
{
|
||||
tr_error_propagate(error, &data.error);
|
||||
tr_error_propagate(&error_, &data.error);
|
||||
}
|
||||
|
||||
// cleanup
|
||||
jsonsl_destroy(jsn);
|
||||
return success;
|
||||
|
||||
if (error_ == nullptr)
|
||||
{
|
||||
return top;
|
||||
}
|
||||
|
||||
tr_variantClear(&top);
|
||||
return {};
|
||||
}
|
||||
|
||||
// ---
|
||||
|
@ -430,9 +431,10 @@ namespace to_string_helpers
|
|||
{
|
||||
struct ParentState
|
||||
{
|
||||
int variantType;
|
||||
int childIndex;
|
||||
int childCount;
|
||||
bool is_map = false;
|
||||
bool is_list = false;
|
||||
size_t child_index;
|
||||
size_t child_count;
|
||||
};
|
||||
|
||||
struct JsonWalk
|
||||
|
@ -465,54 +467,49 @@ void jsonIndent(struct JsonWalk* data)
|
|||
|
||||
void jsonChildFunc(struct JsonWalk* data)
|
||||
{
|
||||
if (!std::empty(data->parents))
|
||||
if (std::empty(data->parents))
|
||||
{
|
||||
auto& pstate = data->parents.back();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (pstate.variantType)
|
||||
auto& parent_state = data->parents.back();
|
||||
|
||||
if (parent_state.is_map)
|
||||
{
|
||||
int const i = parent_state.child_index;
|
||||
++parent_state.child_index;
|
||||
|
||||
if (i % 2 == 0)
|
||||
{
|
||||
case TR_VARIANT_TYPE_DICT:
|
||||
{
|
||||
int const i = pstate.childIndex;
|
||||
++pstate.childIndex;
|
||||
|
||||
if (i % 2 == 0)
|
||||
{
|
||||
data->out.add(data->doIndent ? ": "sv : ":"sv);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool const is_last = pstate.childIndex == pstate.childCount;
|
||||
if (!is_last)
|
||||
{
|
||||
data->out.push_back(',');
|
||||
jsonIndent(data);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case TR_VARIANT_TYPE_LIST:
|
||||
++pstate.childIndex;
|
||||
if (bool const is_last = pstate.childIndex == pstate.childCount; !is_last)
|
||||
data->out.add(data->doIndent ? ": "sv : ":"sv);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool const is_last = parent_state.child_index == parent_state.child_count;
|
||||
if (!is_last)
|
||||
{
|
||||
data->out.push_back(',');
|
||||
jsonIndent(data);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (parent_state.is_list)
|
||||
{
|
||||
++parent_state.child_index;
|
||||
if (bool const is_last = parent_state.child_index == parent_state.child_count; !is_last)
|
||||
{
|
||||
data->out.push_back(',');
|
||||
jsonIndent(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void jsonPushParent(struct JsonWalk* data, tr_variant const* v)
|
||||
void jsonPushParent(struct JsonWalk* data, tr_variant const& v)
|
||||
{
|
||||
int const n_children = tr_variantIsDict(v) ? v->val.l.count * 2 : v->val.l.count;
|
||||
data->parents.push_back({ v->type, 0, n_children });
|
||||
auto const is_dict = tr_variantIsDict(&v);
|
||||
auto const is_list = tr_variantIsList(&v);
|
||||
auto const n_children = is_dict ? v.val.l.count * 2U : v.val.l.count;
|
||||
data->parents.push_back({ is_dict, is_list, 0, n_children });
|
||||
}
|
||||
|
||||
void jsonPopParent(struct JsonWalk* data)
|
||||
|
@ -520,32 +517,23 @@ void jsonPopParent(struct JsonWalk* data)
|
|||
data->parents.pop_back();
|
||||
}
|
||||
|
||||
void jsonIntFunc(tr_variant const* val, void* vdata)
|
||||
void jsonIntFunc(tr_variant const& /*var*/, int64_t const val, void* vdata)
|
||||
{
|
||||
auto buf = std::array<char, 64>{};
|
||||
auto const* const out = fmt::format_to(std::data(buf), FMT_COMPILE("{:d}"), val->val.i);
|
||||
auto const* const out = fmt::format_to(std::data(buf), FMT_COMPILE("{:d}"), val);
|
||||
auto* const data = static_cast<JsonWalk*>(vdata);
|
||||
data->out.add(std::data(buf), static_cast<size_t>(out - std::data(buf)));
|
||||
jsonChildFunc(data);
|
||||
}
|
||||
|
||||
void jsonBoolFunc(tr_variant const* val, void* vdata)
|
||||
void jsonBoolFunc(tr_variant const& /*var*/, bool const val, void* vdata)
|
||||
{
|
||||
auto* data = static_cast<struct JsonWalk*>(vdata);
|
||||
|
||||
if (val->val.b)
|
||||
{
|
||||
data->out.add("true"sv);
|
||||
}
|
||||
else
|
||||
{
|
||||
data->out.add("false"sv);
|
||||
}
|
||||
|
||||
data->out.add(val ? "true"sv : "false"sv);
|
||||
jsonChildFunc(data);
|
||||
}
|
||||
|
||||
void jsonRealFunc(tr_variant const* val, void* vdata)
|
||||
void jsonRealFunc(tr_variant const& /*var*/, double const val, void* vdata)
|
||||
{
|
||||
auto* const data = static_cast<struct JsonWalk*>(vdata);
|
||||
|
||||
|
@ -553,13 +541,13 @@ void jsonRealFunc(tr_variant const* val, void* vdata)
|
|||
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 - (int)val) < 0.00001)
|
||||
{
|
||||
walk = fmt::format_to(walk, FMT_COMPILE("{:.0f}"), val->val.d);
|
||||
walk = fmt::format_to(walk, FMT_COMPILE("{:.0f}"), val);
|
||||
}
|
||||
else
|
||||
{
|
||||
walk = fmt::format_to(walk, FMT_COMPILE("{:.4f}"), val->val.d);
|
||||
walk = fmt::format_to(walk, FMT_COMPILE("{:.4f}"), val);
|
||||
}
|
||||
|
||||
data->out.commit_space(walk - begin);
|
||||
|
@ -586,13 +574,10 @@ void jsonRealFunc(tr_variant const* val, void* vdata)
|
|||
return buf;
|
||||
}
|
||||
|
||||
void jsonStringFunc(tr_variant const* val, void* vdata)
|
||||
void jsonStringFunc(tr_variant const& /*var*/, std::string_view sv, void* vdata)
|
||||
{
|
||||
auto* const data = static_cast<struct JsonWalk*>(vdata);
|
||||
|
||||
auto sv = std::string_view{};
|
||||
(void)!tr_variantGetStrView(val, &sv);
|
||||
|
||||
auto& out = data->out;
|
||||
auto const [buf, buflen] = out.reserve_space(std::size(sv) * 6 + 2);
|
||||
auto* walk = reinterpret_cast<char*>(buf);
|
||||
|
@ -667,34 +652,33 @@ void jsonStringFunc(tr_variant const* val, void* vdata)
|
|||
jsonChildFunc(data);
|
||||
}
|
||||
|
||||
void jsonDictBeginFunc(tr_variant const* val, void* vdata)
|
||||
void jsonDictBeginFunc(tr_variant const& var, void* vdata)
|
||||
{
|
||||
auto* const data = static_cast<struct JsonWalk*>(vdata);
|
||||
|
||||
jsonPushParent(data, val);
|
||||
jsonPushParent(data, var);
|
||||
data->out.push_back('{');
|
||||
|
||||
if (val->val.l.count != 0)
|
||||
if (var.val.l.count != 0U)
|
||||
{
|
||||
jsonIndent(data);
|
||||
}
|
||||
}
|
||||
|
||||
void jsonListBeginFunc(tr_variant const* val, void* vdata)
|
||||
void jsonListBeginFunc(tr_variant const& var, void* vdata)
|
||||
{
|
||||
size_t const n_children = tr_variantListSize(val);
|
||||
auto* const data = static_cast<struct JsonWalk*>(vdata);
|
||||
|
||||
jsonPushParent(data, val);
|
||||
jsonPushParent(data, var);
|
||||
data->out.push_back('[');
|
||||
|
||||
if (n_children != 0)
|
||||
if (var.val.l.count != 0U)
|
||||
{
|
||||
jsonIndent(data);
|
||||
}
|
||||
}
|
||||
|
||||
void jsonContainerEndFunc(tr_variant const* val, void* vdata)
|
||||
void jsonContainerEndFunc(tr_variant const& var, void* vdata)
|
||||
{
|
||||
auto* const data = static_cast<struct JsonWalk*>(vdata);
|
||||
|
||||
|
@ -702,7 +686,7 @@ void jsonContainerEndFunc(tr_variant const* val, void* vdata)
|
|||
|
||||
jsonIndent(data);
|
||||
|
||||
if (tr_variantIsDict(val))
|
||||
if (tr_variantIsDict(&var))
|
||||
{
|
||||
data->out.push_back('}');
|
||||
}
|
||||
|
@ -714,26 +698,25 @@ void jsonContainerEndFunc(tr_variant const* val, void* vdata)
|
|||
jsonChildFunc(data);
|
||||
}
|
||||
|
||||
struct VariantWalkFuncs const walk_funcs = {
|
||||
jsonIntFunc, //
|
||||
jsonBoolFunc, //
|
||||
jsonRealFunc, //
|
||||
jsonStringFunc, //
|
||||
jsonDictBeginFunc, //
|
||||
jsonListBeginFunc, //
|
||||
jsonContainerEndFunc, //
|
||||
};
|
||||
|
||||
} // namespace to_string_helpers
|
||||
} // namespace
|
||||
|
||||
std::string tr_variantToStrJson(tr_variant const* top, bool lean)
|
||||
std::string tr_variant_serde::to_json_string(tr_variant const& var) const
|
||||
{
|
||||
using namespace to_string_helpers;
|
||||
|
||||
auto data = JsonWalk{ !lean };
|
||||
static auto constexpr Funcs = WalkFuncs{
|
||||
jsonIntFunc, //
|
||||
jsonBoolFunc, //
|
||||
jsonRealFunc, //
|
||||
jsonStringFunc, //
|
||||
jsonDictBeginFunc, //
|
||||
jsonListBeginFunc, //
|
||||
jsonContainerEndFunc, //
|
||||
};
|
||||
|
||||
tr_variantWalk(top, &walk_funcs, &data, true);
|
||||
auto data = JsonWalk{ !compact_ };
|
||||
walk(var, Funcs, &data, true);
|
||||
|
||||
auto& buf = data.out;
|
||||
if (!std::empty(buf))
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include "libtransmission/quark.h"
|
||||
#include "libtransmission/tr-assert.h"
|
||||
#include "libtransmission/utils.h"
|
||||
#include "libtransmission/variant-common.h"
|
||||
#include "libtransmission/variant.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
@ -693,10 +692,10 @@ private:
|
|||
* easier to read, but was vulnerable to a smash-stacking
|
||||
* attack via maliciously-crafted data. (#667)
|
||||
*/
|
||||
void tr_variantWalk(tr_variant const* top, struct VariantWalkFuncs const* walk_funcs, void* user_data, bool sort_dicts)
|
||||
void tr_variant_serde::walk(tr_variant const& top, WalkFuncs const& walk_funcs, void* user_data, bool sort_dicts)
|
||||
{
|
||||
auto stack = VariantWalker{};
|
||||
stack.emplace(top, sort_dicts);
|
||||
stack.emplace(&top, sort_dicts);
|
||||
|
||||
while (!stack.empty())
|
||||
{
|
||||
|
@ -717,16 +716,17 @@ void tr_variantWalk(tr_variant const* top, struct VariantWalkFuncs const* walk_f
|
|||
{
|
||||
if (tr_variantIsDict(&node.v))
|
||||
{
|
||||
auto const keystr = tr_quark_get_string_view(v->key);
|
||||
auto tmp = tr_variant{};
|
||||
tr_variantInitQuark(&tmp, v->key);
|
||||
walk_funcs->stringFunc(&tmp, user_data);
|
||||
walk_funcs.string_func(tmp, keystr, user_data);
|
||||
}
|
||||
}
|
||||
else // finished with this node
|
||||
{
|
||||
if (tr_variantIsContainer(&node.v))
|
||||
{
|
||||
walk_funcs->containerEndFunc(&node.v, user_data);
|
||||
walk_funcs.container_end_func(node.v, user_data);
|
||||
}
|
||||
|
||||
stack.pop();
|
||||
|
@ -739,25 +739,25 @@ void tr_variantWalk(tr_variant const* top, struct VariantWalkFuncs const* walk_f
|
|||
switch (v->type)
|
||||
{
|
||||
case TR_VARIANT_TYPE_INT:
|
||||
walk_funcs->intFunc(v, user_data);
|
||||
walk_funcs.int_func(*v, v->val.i, user_data);
|
||||
break;
|
||||
|
||||
case TR_VARIANT_TYPE_BOOL:
|
||||
walk_funcs->boolFunc(v, user_data);
|
||||
walk_funcs.bool_func(*v, v->val.b, user_data);
|
||||
break;
|
||||
|
||||
case TR_VARIANT_TYPE_REAL:
|
||||
walk_funcs->realFunc(v, user_data);
|
||||
walk_funcs.double_func(*v, v->val.d, user_data);
|
||||
break;
|
||||
|
||||
case TR_VARIANT_TYPE_STR:
|
||||
walk_funcs->stringFunc(v, user_data);
|
||||
walk_funcs.string_func(*v, v->val.s.get(), user_data);
|
||||
break;
|
||||
|
||||
case TR_VARIANT_TYPE_LIST:
|
||||
if (v == &node.v)
|
||||
{
|
||||
walk_funcs->listBeginFunc(v, user_data);
|
||||
walk_funcs.list_begin_func(*v, user_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -768,7 +768,7 @@ void tr_variantWalk(tr_variant const* top, struct VariantWalkFuncs const* walk_f
|
|||
case TR_VARIANT_TYPE_DICT:
|
||||
if (v == &node.v)
|
||||
{
|
||||
walk_funcs->dictBeginFunc(v, user_data);
|
||||
walk_funcs.dict_begin_func(*v, user_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -787,43 +787,23 @@ void tr_variantWalk(tr_variant const* top, struct VariantWalkFuncs const* walk_f
|
|||
|
||||
// ---
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace clear_helpers
|
||||
{
|
||||
void freeDummyFunc(tr_variant const* /*v*/, void* /*buf*/)
|
||||
{
|
||||
}
|
||||
|
||||
void freeStringFunc(tr_variant const* v, void* /*user_data*/)
|
||||
{
|
||||
const_cast<tr_variant*>(v)->val.s.clear();
|
||||
}
|
||||
|
||||
void freeContainerEndFunc(tr_variant const* v, void* /*user_data*/)
|
||||
{
|
||||
delete[] v->val.l.vals;
|
||||
}
|
||||
|
||||
VariantWalkFuncs constexpr FreeWalkFuncs = {
|
||||
freeDummyFunc, //
|
||||
freeDummyFunc, //
|
||||
freeDummyFunc, //
|
||||
freeStringFunc, //
|
||||
freeDummyFunc, //
|
||||
freeDummyFunc, //
|
||||
freeContainerEndFunc, //
|
||||
};
|
||||
} // namespace clear_helpers
|
||||
} // namespace
|
||||
|
||||
void tr_variantClear(tr_variant* clearme)
|
||||
{
|
||||
using namespace clear_helpers;
|
||||
// clang-format off
|
||||
tr_variant_serde::WalkFuncs cleanup_funcs = {
|
||||
[](tr_variant const&, int64_t, void*) {},
|
||||
[](tr_variant const&, bool, void*) {},
|
||||
[](tr_variant const&, double, void*) {},
|
||||
[](tr_variant const& var, std::string_view, void*){ const_cast<tr_variant&>(var).val.s.clear(); },
|
||||
[](tr_variant const&, void*) {},
|
||||
[](tr_variant const&, void*) {},
|
||||
[](tr_variant const& var, void*) { delete[] var.val.l.vals; }
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
if (!tr_variantIsEmpty(clearme))
|
||||
{
|
||||
tr_variantWalk(clearme, &FreeWalkFuncs, nullptr, false);
|
||||
tr_variant_serde::walk(*clearme, cleanup_funcs, nullptr, false);
|
||||
}
|
||||
|
||||
*clearme = {};
|
||||
|
@ -992,71 +972,48 @@ void tr_variantMergeDicts(tr_variant* target, tr_variant const* source)
|
|||
|
||||
// ---
|
||||
|
||||
std::string tr_variantToStr(tr_variant const* v, tr_variant_fmt fmt)
|
||||
tr_variant_serde::~tr_variant_serde()
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case TR_VARIANT_FMT_JSON:
|
||||
return tr_variantToStrJson(v, false);
|
||||
|
||||
case TR_VARIANT_FMT_JSON_LEAN:
|
||||
return tr_variantToStrJson(v, true);
|
||||
|
||||
default: // TR_VARIANT_FMT_BENC:
|
||||
return tr_variantToStrBenc(v);
|
||||
}
|
||||
tr_error_clear(&error_);
|
||||
}
|
||||
|
||||
int tr_variantToFile(tr_variant const* v, tr_variant_fmt fmt, std::string_view filename)
|
||||
std::optional<tr_variant> tr_variant_serde::parse(std::string_view input)
|
||||
{
|
||||
auto error_code = int{ 0 };
|
||||
auto const contents = tr_variantToStr(v, fmt);
|
||||
tr_error_clear(&error_);
|
||||
return type_ == Type::Json ? parse_json(input) : parse_benc(input);
|
||||
}
|
||||
|
||||
tr_error* error = nullptr;
|
||||
tr_file_save(filename, contents, &error);
|
||||
if (error != nullptr)
|
||||
[[nodiscard]] std::optional<tr_variant> tr_variant_serde::parse_file(std::string_view filename)
|
||||
{
|
||||
TR_ASSERT_MSG(!parse_inplace_, "not supported in from_file()");
|
||||
parse_inplace_ = false;
|
||||
|
||||
if (auto buf = std::vector<char>{}; tr_file_read(filename, buf, &error_))
|
||||
{
|
||||
return parse(buf);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string tr_variant_serde::to_string(tr_variant const& var) const
|
||||
{
|
||||
return type_ == Type::Json ? to_json_string(var) : to_benc_string(var);
|
||||
}
|
||||
|
||||
bool tr_variant_serde::to_file(tr_variant const& var, std::string_view filename)
|
||||
{
|
||||
tr_file_save(filename, to_string(var), &error_);
|
||||
|
||||
if (error_ != nullptr)
|
||||
{
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't save '{path}': {error} ({error_code})"),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("error", error->message),
|
||||
fmt::arg("error_code", error->code)));
|
||||
error_code = error->code;
|
||||
tr_error_clear(&error);
|
||||
fmt::arg("error", error_->message),
|
||||
fmt::arg("error_code", error_->code)));
|
||||
return false;
|
||||
}
|
||||
|
||||
return error_code;
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
bool tr_variantFromBuf(tr_variant* setme, int opts, std::string_view buf, char const** setme_end, tr_error** error)
|
||||
{
|
||||
// supported formats: benc, json
|
||||
TR_ASSERT((opts & (TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_JSON)) != 0);
|
||||
|
||||
*setme = {};
|
||||
|
||||
auto const success = ((opts & TR_VARIANT_PARSE_BENC) != 0) ? tr_variantParseBenc(*setme, opts, buf, setme_end, error) :
|
||||
tr_variantParseJson(*setme, opts, buf, setme_end, error);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
tr_variantClear(setme);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool tr_variantFromFile(tr_variant* setme, tr_variant_parse_opts opts, std::string_view filename, tr_error** error)
|
||||
{
|
||||
// can't do inplace when this function is allocating & freeing the memory...
|
||||
TR_ASSERT((opts & TR_VARIANT_PARSE_INPLACE) == 0);
|
||||
|
||||
if (auto buf = std::vector<char>{}; tr_file_read(filename, buf, error))
|
||||
{
|
||||
return tr_variantFromBuf(setme, opts, buf, nullptr, error);
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -147,55 +147,6 @@ public:
|
|||
*/
|
||||
void tr_variantClear(tr_variant* clearme);
|
||||
|
||||
// --- Serialization / Deserialization
|
||||
|
||||
enum tr_variant_fmt
|
||||
{
|
||||
TR_VARIANT_FMT_BENC,
|
||||
TR_VARIANT_FMT_JSON,
|
||||
TR_VARIANT_FMT_JSON_LEAN /* saves bandwidth by omitting all whitespace. */
|
||||
};
|
||||
|
||||
int tr_variantToFile(tr_variant const* variant, tr_variant_fmt fmt, std::string_view filename);
|
||||
|
||||
[[nodiscard]] std::string tr_variantToStr(tr_variant const* variant, tr_variant_fmt fmt);
|
||||
|
||||
enum tr_variant_parse_opts
|
||||
{
|
||||
TR_VARIANT_PARSE_BENC = (1 << 0),
|
||||
TR_VARIANT_PARSE_JSON = (1 << 1),
|
||||
TR_VARIANT_PARSE_INPLACE = (1 << 2)
|
||||
};
|
||||
|
||||
bool tr_variantFromFile(
|
||||
tr_variant* setme,
|
||||
tr_variant_parse_opts opts,
|
||||
std::string_view filename,
|
||||
struct tr_error** error = nullptr);
|
||||
|
||||
bool tr_variantFromBuf(
|
||||
tr_variant* setme,
|
||||
int variant_parse_opts,
|
||||
std::string_view buf,
|
||||
char const** setme_end = nullptr,
|
||||
tr_error** error = nullptr);
|
||||
|
||||
template<typename T>
|
||||
bool tr_variantFromBuf(
|
||||
tr_variant* setme,
|
||||
int variant_parse_opts,
|
||||
T const& buf,
|
||||
char const** setme_end = nullptr,
|
||||
tr_error** error = nullptr)
|
||||
{
|
||||
return tr_variantFromBuf(
|
||||
setme,
|
||||
variant_parse_opts,
|
||||
std::string_view{ std::data(buf), static_cast<size_t>(std::size(buf)) },
|
||||
setme_end,
|
||||
error);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool tr_variantIsType(tr_variant const* b, int type)
|
||||
{
|
||||
return b != nullptr && b->type == type;
|
||||
|
@ -340,6 +291,110 @@ bool tr_variantDictFindRaw(tr_variant* dict, tr_quark key, std::byte const** set
|
|||
/* this is only quasi-supported. don't rely on it too heavily outside of libT */
|
||||
void tr_variantMergeDicts(tr_variant* dict_target, tr_variant const* dict_source);
|
||||
|
||||
// tr_variant serializer / deserializer
|
||||
class tr_variant_serde
|
||||
{
|
||||
public:
|
||||
~tr_variant_serde();
|
||||
|
||||
static tr_variant_serde benc() noexcept
|
||||
{
|
||||
return tr_variant_serde{ Type::Benc };
|
||||
}
|
||||
|
||||
static tr_variant_serde json() noexcept
|
||||
{
|
||||
return tr_variant_serde{ Type::Json };
|
||||
}
|
||||
|
||||
// Serialize data as compactly as possible, e.g.
|
||||
// omit pretty-printing JSON whitespace
|
||||
constexpr tr_variant_serde& compact() noexcept
|
||||
{
|
||||
compact_ = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// When set, assumes that the `input` passed to parse() is valid
|
||||
// for the lifespan of the variant and we can use string_views of
|
||||
// `input` instead of cloning new strings.
|
||||
constexpr tr_variant_serde& inplace() noexcept
|
||||
{
|
||||
parse_inplace_ = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
[[nodiscard]] std::optional<tr_variant> parse(std::string_view input);
|
||||
|
||||
template<typename CharSpan>
|
||||
[[nodiscard]] std::optional<tr_variant> parse(CharSpan const& input)
|
||||
{
|
||||
return parse(std::string_view{ std::data(input), std::size(input) });
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<tr_variant> parse_file(std::string_view filename);
|
||||
|
||||
[[nodiscard]] constexpr char const* end() const noexcept
|
||||
{
|
||||
return end_;
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
[[nodiscard]] std::string to_string(tr_variant const& var) const;
|
||||
|
||||
bool to_file(tr_variant const& var, std::string_view filename);
|
||||
|
||||
// ---
|
||||
|
||||
// Tracks errors when parsing / saving
|
||||
tr_error* error_ = nullptr;
|
||||
|
||||
private:
|
||||
friend void tr_variantClear(tr_variant* clearme);
|
||||
|
||||
enum class Type
|
||||
{
|
||||
Benc,
|
||||
Json
|
||||
};
|
||||
|
||||
struct WalkFuncs
|
||||
{
|
||||
void (*int_func)(tr_variant const& var, int64_t val, void* user_data);
|
||||
void (*bool_func)(tr_variant const& var, bool val, void* user_data);
|
||||
void (*double_func)(tr_variant const& var, double val, void* user_data);
|
||||
void (*string_func)(tr_variant const& var, std::string_view val, void* user_data);
|
||||
void (*dict_begin_func)(tr_variant const& var, void* user_data);
|
||||
void (*list_begin_func)(tr_variant const& var, void* user_data);
|
||||
void (*container_end_func)(tr_variant const& var, void* user_data);
|
||||
};
|
||||
|
||||
tr_variant_serde(Type type)
|
||||
: type_{ type }
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<tr_variant> parse_json(std::string_view input);
|
||||
[[nodiscard]] std::optional<tr_variant> parse_benc(std::string_view input);
|
||||
|
||||
[[nodiscard]] std::string to_json_string(tr_variant const& var) const;
|
||||
[[nodiscard]] static std::string to_benc_string(tr_variant const& var);
|
||||
|
||||
static void walk(tr_variant const& top, WalkFuncs const& walk_funcs, void* user_data, bool sort_dicts);
|
||||
|
||||
Type type_;
|
||||
|
||||
bool compact_ = false;
|
||||
|
||||
bool parse_inplace_ = false;
|
||||
|
||||
// This is set to the first unparsed character after `parse()`.
|
||||
char const* end_ = nullptr;
|
||||
};
|
||||
|
||||
namespace libtransmission
|
||||
{
|
||||
|
||||
|
|
19
qt/Prefs.cc
19
qt/Prefs.cc
|
@ -403,17 +403,20 @@ Prefs::~Prefs()
|
|||
}
|
||||
|
||||
// update settings.json with our settings
|
||||
tr_variant file_settings;
|
||||
QFile const file(QDir(config_dir_).absoluteFilePath(QStringLiteral("settings.json")));
|
||||
|
||||
if (!tr_variantFromFile(&file_settings, TR_VARIANT_PARSE_JSON, file.fileName().toStdString(), nullptr))
|
||||
auto serde = tr_variant_serde::json();
|
||||
auto const file = QFile{ QDir{ config_dir_ }.absoluteFilePath(QStringLiteral("settings.json")) };
|
||||
auto const filename = file.fileName().toStdString();
|
||||
auto settings = serde.parse_file(filename);
|
||||
if (!settings)
|
||||
{
|
||||
tr_variantInitDict(&file_settings, PREFS_COUNT);
|
||||
auto empty_dict = tr_variant{};
|
||||
tr_variantInitDict(&empty_dict, PREFS_COUNT);
|
||||
settings = empty_dict;
|
||||
}
|
||||
|
||||
tr_variantMergeDicts(&file_settings, ¤t_settings);
|
||||
tr_variantToFile(&file_settings, TR_VARIANT_FMT_JSON, file.fileName().toStdString());
|
||||
tr_variantClear(&file_settings);
|
||||
tr_variantMergeDicts(&*settings, ¤t_settings);
|
||||
serde.to_file(*settings, filename);
|
||||
tr_variantClear(&*settings);
|
||||
|
||||
// cleanup
|
||||
tr_variantClear(¤t_settings);
|
||||
|
|
|
@ -137,7 +137,7 @@ void RpcClient::sendNetworkRequest(TrVariantPtr json, QFutureInterface<RpcRespon
|
|||
request_ = request;
|
||||
}
|
||||
|
||||
auto const json_data = QByteArray::fromStdString(tr_variantToStr(json.get(), TR_VARIANT_FMT_JSON_LEAN));
|
||||
auto const json_data = QByteArray::fromStdString(tr_variant_serde::json().compact().to_string(*json));
|
||||
QNetworkReply* reply = networkAccessManager()->post(*request_, json_data);
|
||||
reply->setProperty(RequestDataPropertyKey, QVariant::fromValue(json));
|
||||
reply->setProperty(RequestFutureinterfacePropertyKey, QVariant::fromValue(promise));
|
||||
|
@ -164,7 +164,7 @@ void RpcClient::sendLocalRequest(TrVariantPtr json, QFutureInterface<RpcResponse
|
|||
{
|
||||
if (verbose_)
|
||||
{
|
||||
fmt::print("{:s}:{:d} sending req:\n{:s}\n", __FILE__, __LINE__, tr_variantToStr(json.get(), TR_VARIANT_FMT_JSON));
|
||||
fmt::print("{:s}:{:d} sending req:\n{:s}\n", __FILE__, __LINE__, tr_variant_serde::json().to_string(*json));
|
||||
}
|
||||
|
||||
local_requests_.try_emplace(tag, promise);
|
||||
|
@ -216,7 +216,7 @@ void RpcClient::localSessionCallback(tr_session* s, tr_variant* response, void*
|
|||
|
||||
if (self->verbose_)
|
||||
{
|
||||
fmt::print("{:s}:{:d} got response:\n{:s}\n", __FILE__, __LINE__, tr_variantToStr(response, TR_VARIANT_FMT_JSON));
|
||||
fmt::print("{:s}:{:d} got response:\n{:s}\n", __FILE__, __LINE__, tr_variant_serde::json().to_string(*response));
|
||||
}
|
||||
|
||||
TrVariantPtr const json = createVariant();
|
||||
|
@ -272,12 +272,15 @@ void RpcClient::networkRequestFinished(QNetworkReply* reply)
|
|||
}
|
||||
else
|
||||
{
|
||||
auto const json_data = reply->readAll().trimmed();
|
||||
auto const json_data = reply->readAll().trimmed().toStdString();
|
||||
auto const json = createVariant();
|
||||
RpcResponse result;
|
||||
if (tr_variantFromBuf(json.get(), TR_VARIANT_PARSE_JSON, json_data))
|
||||
auto result = RpcResponse{};
|
||||
|
||||
if (auto top = tr_variant_serde::json().parse(json_data); top)
|
||||
{
|
||||
std::swap(*json, *top);
|
||||
result = parseResponseData(*json);
|
||||
tr_variantClear(&*top);
|
||||
}
|
||||
|
||||
promise.setProgressValue(1);
|
||||
|
|
|
@ -140,7 +140,7 @@ protected:
|
|||
addr.to_compact_ipv6(std::back_inserter(compact), port);
|
||||
}
|
||||
tr_variantDictAddRaw(&dict, TR_KEY_nodes6, std::data(compact), std::size(compact));
|
||||
tr_variantToFile(&dict, TR_VARIANT_FMT_BENC, dat_file);
|
||||
tr_variant_serde::benc().to_file(dict, dat_file);
|
||||
tr_variantClear(&dict);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -59,8 +59,7 @@ TEST_P(JSONTest, testElements)
|
|||
" \"null\": null }"
|
||||
};
|
||||
|
||||
tr_variant top;
|
||||
EXPECT_TRUE(tr_variantFromBuf(&top, TR_VARIANT_PARSE_JSON | TR_VARIANT_PARSE_INPLACE, in));
|
||||
auto top = tr_variant_serde::json().inplace().parse(in).value_or(tr_variant{});
|
||||
EXPECT_TRUE(tr_variantIsDict(&top));
|
||||
|
||||
auto sv = std::string_view{};
|
||||
|
@ -95,18 +94,19 @@ TEST_P(JSONTest, testElements)
|
|||
TEST_P(JSONTest, testUtf8)
|
||||
{
|
||||
auto in = "{ \"key\": \"Letöltések\" }"sv;
|
||||
tr_variant top;
|
||||
auto sv = std::string_view{};
|
||||
tr_quark const key = tr_quark_new("key"sv);
|
||||
|
||||
EXPECT_TRUE(tr_variantFromBuf(&top, TR_VARIANT_PARSE_JSON | TR_VARIANT_PARSE_INPLACE, in));
|
||||
auto serde = tr_variant_serde::json();
|
||||
serde.inplace();
|
||||
auto top = serde.parse(in).value_or(tr_variant{});
|
||||
EXPECT_TRUE(tr_variantIsDict(&top));
|
||||
EXPECT_TRUE(tr_variantDictFindStrView(&top, key, &sv));
|
||||
EXPECT_EQ("Letöltések"sv, sv);
|
||||
tr_variantClear(&top);
|
||||
|
||||
in = R"({ "key": "\u005C" })"sv;
|
||||
EXPECT_TRUE(tr_variantFromBuf(&top, TR_VARIANT_PARSE_JSON | TR_VARIANT_PARSE_INPLACE, in));
|
||||
top = serde.parse(in).value_or(tr_variant{});
|
||||
EXPECT_TRUE(tr_variantIsDict(&top));
|
||||
EXPECT_TRUE(tr_variantDictFindStrView(&top, key, &sv));
|
||||
EXPECT_EQ("\\"sv, sv);
|
||||
|
@ -121,17 +121,17 @@ TEST_P(JSONTest, testUtf8)
|
|||
* 6. Confirm that the result is UTF-8.
|
||||
*/
|
||||
in = R"({ "key": "Let\u00f6lt\u00e9sek" })"sv;
|
||||
EXPECT_TRUE(tr_variantFromBuf(&top, TR_VARIANT_PARSE_JSON | TR_VARIANT_PARSE_INPLACE, in));
|
||||
top = serde.parse(in).value_or(tr_variant{});
|
||||
EXPECT_TRUE(tr_variantIsDict(&top));
|
||||
EXPECT_TRUE(tr_variantDictFindStrView(&top, key, &sv));
|
||||
EXPECT_EQ("Letöltések"sv, sv);
|
||||
auto json = tr_variantToStr(&top, TR_VARIANT_FMT_JSON);
|
||||
auto json = serde.to_string(top);
|
||||
tr_variantClear(&top);
|
||||
|
||||
EXPECT_FALSE(std::empty(json));
|
||||
EXPECT_NE(std::string::npos, json.find("\\u00f6"));
|
||||
EXPECT_NE(std::string::npos, json.find("\\u00e9"));
|
||||
EXPECT_TRUE(tr_variantFromBuf(&top, TR_VARIANT_PARSE_JSON | TR_VARIANT_PARSE_INPLACE, json));
|
||||
top = serde.parse(json).value_or(tr_variant{});
|
||||
EXPECT_TRUE(tr_variantIsDict(&top));
|
||||
EXPECT_TRUE(tr_variantDictFindStrView(&top, key, &sv));
|
||||
EXPECT_EQ("Letöltések"sv, sv);
|
||||
|
@ -145,13 +145,14 @@ TEST_P(JSONTest, testUtf16Surrogates)
|
|||
tr_variantInitDict(&top, 1);
|
||||
auto const key = tr_quark_new("key"sv);
|
||||
tr_variantDictAddStr(&top, key, ThinkingFaceEmojiUtf8);
|
||||
auto const json = tr_variantToStr(&top, TR_VARIANT_FMT_JSON_LEAN);
|
||||
|
||||
auto serde = tr_variant_serde::json();
|
||||
auto const json = serde.compact().to_string(top);
|
||||
EXPECT_NE(std::string::npos, json.find("ud83e"));
|
||||
EXPECT_NE(std::string::npos, json.find("udd14"));
|
||||
tr_variantClear(&top);
|
||||
|
||||
auto parsed = tr_variant{};
|
||||
EXPECT_TRUE(tr_variantFromBuf(&parsed, TR_VARIANT_PARSE_JSON | TR_VARIANT_PARSE_INPLACE, json));
|
||||
auto parsed = serde.parse(json).value_or(tr_variant{});
|
||||
EXPECT_TRUE(tr_variantIsDict(&parsed));
|
||||
auto value = std::string_view{};
|
||||
EXPECT_TRUE(tr_variantDictFindStrView(&parsed, key, &value));
|
||||
|
@ -161,7 +162,7 @@ TEST_P(JSONTest, testUtf16Surrogates)
|
|||
|
||||
TEST_P(JSONTest, test1)
|
||||
{
|
||||
auto const in = std::string{
|
||||
static auto constexpr Input =
|
||||
"{\n"
|
||||
" \"headers\": {\n"
|
||||
" \"type\": \"request\",\n"
|
||||
|
@ -173,15 +174,14 @@ TEST_P(JSONTest, test1)
|
|||
" \"ids\": [ 7, 10 ]\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
};
|
||||
"}\n"sv;
|
||||
|
||||
tr_variant top;
|
||||
EXPECT_TRUE(tr_variantFromBuf(&top, TR_VARIANT_PARSE_JSON | TR_VARIANT_PARSE_INPLACE, in));
|
||||
auto serde = tr_variant_serde::json();
|
||||
auto top = serde.inplace().parse(Input).value_or(tr_variant{});
|
||||
EXPECT_TRUE(tr_variantIsDict(&top));
|
||||
|
||||
auto sv = std::string_view{};
|
||||
auto i = int64_t{};
|
||||
EXPECT_TRUE(tr_variantIsDict(&top));
|
||||
auto* headers = tr_variantDictFind(&top, tr_quark_new("headers"sv));
|
||||
EXPECT_NE(nullptr, headers);
|
||||
EXPECT_TRUE(tr_variantIsDict(headers));
|
||||
|
@ -210,25 +210,22 @@ TEST_P(JSONTest, test1)
|
|||
|
||||
TEST_P(JSONTest, test2)
|
||||
{
|
||||
tr_variant top;
|
||||
auto const in = std::string{ " " };
|
||||
|
||||
top.type = 0;
|
||||
EXPECT_FALSE(tr_variantFromBuf(&top, TR_VARIANT_PARSE_JSON | TR_VARIANT_PARSE_INPLACE, in));
|
||||
EXPECT_FALSE(tr_variantIsDict(&top));
|
||||
static auto constexpr Input = " "sv;
|
||||
auto top = tr_variant_serde::json().inplace().parse(Input);
|
||||
EXPECT_FALSE(top.has_value());
|
||||
}
|
||||
|
||||
TEST_P(JSONTest, test3)
|
||||
{
|
||||
auto const
|
||||
in = "{ \"error\": 2,"
|
||||
" \"errorString\": \"torrent not registered with this tracker 6UHsVW'*C\","
|
||||
" \"eta\": 262792,"
|
||||
" \"id\": 25,"
|
||||
" \"leftUntilDone\": 2275655680 }"sv;
|
||||
static auto constexpr Input =
|
||||
"{ \"error\": 2,"
|
||||
" \"errorString\": \"torrent not registered with this tracker 6UHsVW'*C\","
|
||||
" \"eta\": 262792,"
|
||||
" \"id\": 25,"
|
||||
" \"leftUntilDone\": 2275655680 }"sv;
|
||||
|
||||
tr_variant top;
|
||||
EXPECT_TRUE(tr_variantFromBuf(&top, TR_VARIANT_PARSE_JSON | TR_VARIANT_PARSE_INPLACE, in));
|
||||
auto top = tr_variant_serde::json().inplace().parse(Input).value_or(tr_variant{});
|
||||
EXPECT_TRUE(tr_variantIsDict(&top));
|
||||
|
||||
auto sv = std::string_view{};
|
||||
EXPECT_TRUE(tr_variantDictFindStrView(&top, TR_KEY_errorString, &sv));
|
||||
|
@ -239,14 +236,14 @@ TEST_P(JSONTest, test3)
|
|||
|
||||
TEST_P(JSONTest, unescape)
|
||||
{
|
||||
tr_variant top;
|
||||
auto const in = std::string{ R"({ "string-1": "\/usr\/lib" })" };
|
||||
EXPECT_TRUE(tr_variantFromBuf(&top, TR_VARIANT_PARSE_JSON | TR_VARIANT_PARSE_INPLACE, in));
|
||||
static auto constexpr Input = R"({ "string-1": "\/usr\/lib" })"sv;
|
||||
|
||||
auto top = tr_variant_serde::json().inplace().parse(Input).value_or(tr_variant{});
|
||||
EXPECT_TRUE(tr_variantIsDict(&top));
|
||||
|
||||
auto sv = std::string_view{};
|
||||
EXPECT_TRUE(tr_variantDictFindStrView(&top, tr_quark_new("string-1"sv), &sv));
|
||||
EXPECT_EQ("/usr/lib"sv, sv);
|
||||
|
||||
tr_variantClear(&top);
|
||||
}
|
||||
|
||||
|
|
|
@ -234,19 +234,18 @@ TEST_F(MakemetaTest, announceSingleTracker)
|
|||
|
||||
// generate the torrent and parse it as a variant
|
||||
EXPECT_EQ(nullptr, builder.make_checksums().get());
|
||||
auto top = tr_variant{};
|
||||
auto const benc = builder.benc();
|
||||
EXPECT_TRUE(tr_variantFromBuf(&top, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, benc));
|
||||
auto top = tr_variant_serde::benc().parse(builder.benc());
|
||||
EXPECT_TRUE(top.has_value());
|
||||
|
||||
// confirm there's an "announce" entry
|
||||
auto single_announce = std::string_view{};
|
||||
EXPECT_TRUE(tr_variantDictFindStrView(&top, TR_KEY_announce, &single_announce));
|
||||
EXPECT_TRUE(tr_variantDictFindStrView(&*top, TR_KEY_announce, &single_announce));
|
||||
EXPECT_EQ(SingleAnnounce, single_announce);
|
||||
|
||||
// confirm there's not an "announce-list" entry
|
||||
EXPECT_EQ(nullptr, tr_variantDictFind(&top, TR_KEY_announce_list));
|
||||
EXPECT_EQ(nullptr, tr_variantDictFind(&*top, TR_KEY_announce_list));
|
||||
|
||||
tr_variantClear(&top);
|
||||
tr_variantClear(&*top);
|
||||
}
|
||||
|
||||
TEST_F(MakemetaTest, announceMultiTracker)
|
||||
|
@ -265,22 +264,21 @@ TEST_F(MakemetaTest, announceMultiTracker)
|
|||
|
||||
// generate the torrent and parse it as a variant
|
||||
EXPECT_EQ(nullptr, builder.make_checksums().get());
|
||||
auto top = tr_variant{};
|
||||
auto const benc = builder.benc();
|
||||
EXPECT_TRUE(tr_variantFromBuf(&top, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, benc));
|
||||
auto top = tr_variant_serde::benc().parse(builder.benc());
|
||||
EXPECT_TRUE(top.has_value());
|
||||
|
||||
// confirm there's an "announce" entry
|
||||
auto single_announce = std::string_view{};
|
||||
EXPECT_TRUE(tr_variantDictFindStrView(&top, TR_KEY_announce, &single_announce));
|
||||
EXPECT_TRUE(tr_variantDictFindStrView(&*top, TR_KEY_announce, &single_announce));
|
||||
EXPECT_EQ(builder.announce_list().at(0).announce.sv(), single_announce);
|
||||
|
||||
// confirm there's an "announce-list" entry
|
||||
tr_variant* announce_list_variant = nullptr;
|
||||
EXPECT_TRUE(tr_variantDictFindList(&top, TR_KEY_announce_list, &announce_list_variant));
|
||||
EXPECT_TRUE(tr_variantDictFindList(&*top, TR_KEY_announce_list, &announce_list_variant));
|
||||
EXPECT_NE(nullptr, announce_list_variant);
|
||||
EXPECT_EQ(std::size(builder.announce_list()), tr_variantListSize(announce_list_variant));
|
||||
|
||||
tr_variantClear(&top);
|
||||
tr_variantClear(&*top);
|
||||
}
|
||||
|
||||
TEST_F(MakemetaTest, privateAndSourceHasDifferentInfoHash)
|
||||
|
|
|
@ -217,49 +217,48 @@ TEST_F(VariantTest, str)
|
|||
|
||||
TEST_F(VariantTest, parse)
|
||||
{
|
||||
auto serde = tr_variant_serde::benc();
|
||||
serde.inplace();
|
||||
|
||||
auto benc = "i64e"sv;
|
||||
auto var = serde.parse(benc).value_or(tr_variant{});
|
||||
auto i = int64_t{};
|
||||
auto val = tr_variant{};
|
||||
char const* end = nullptr;
|
||||
auto ok = tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, benc, &end);
|
||||
EXPECT_TRUE(ok);
|
||||
EXPECT_TRUE(tr_variantGetInt(&val, &i));
|
||||
EXPECT_EQ(int64_t(64), i);
|
||||
EXPECT_EQ(std::data(benc) + std::size(benc), end);
|
||||
tr_variantClear(&val);
|
||||
EXPECT_TRUE(tr_variantGetInt(&var, &i));
|
||||
EXPECT_EQ(64, i);
|
||||
EXPECT_EQ(std::data(benc) + std::size(benc), serde.end());
|
||||
tr_variantClear(&var);
|
||||
|
||||
benc = "li64ei32ei16ee"sv;
|
||||
ok = tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, benc, &end);
|
||||
EXPECT_TRUE(ok);
|
||||
EXPECT_EQ(std::data(benc) + std::size(benc), end);
|
||||
EXPECT_EQ(size_t{ 3 }, tr_variantListSize(&val));
|
||||
EXPECT_TRUE(tr_variantGetInt(tr_variantListChild(&val, 0), &i));
|
||||
var = serde.parse(benc).value_or(tr_variant{});
|
||||
EXPECT_TRUE(tr_variantIsList(&var));
|
||||
EXPECT_EQ(std::data(benc) + std::size(benc), serde.end());
|
||||
EXPECT_EQ(3, tr_variantListSize(&var));
|
||||
EXPECT_TRUE(tr_variantGetInt(tr_variantListChild(&var, 0), &i));
|
||||
EXPECT_EQ(64, i);
|
||||
EXPECT_TRUE(tr_variantGetInt(tr_variantListChild(&val, 1), &i));
|
||||
EXPECT_TRUE(tr_variantGetInt(tr_variantListChild(&var, 1), &i));
|
||||
EXPECT_EQ(32, i);
|
||||
EXPECT_TRUE(tr_variantGetInt(tr_variantListChild(&val, 2), &i));
|
||||
EXPECT_TRUE(tr_variantGetInt(tr_variantListChild(&var, 2), &i));
|
||||
EXPECT_EQ(16, i);
|
||||
EXPECT_EQ(benc, tr_variantToStr(&val, TR_VARIANT_FMT_BENC));
|
||||
|
||||
tr_variantClear(&val);
|
||||
end = nullptr;
|
||||
EXPECT_EQ(benc, serde.to_string(var));
|
||||
tr_variantClear(&var);
|
||||
|
||||
benc = "lllee"sv;
|
||||
ok = tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, benc, &end);
|
||||
EXPECT_FALSE(ok);
|
||||
EXPECT_EQ(nullptr, end);
|
||||
var = serde.parse(benc).value_or(tr_variant{});
|
||||
EXPECT_TRUE(tr_variantIsEmpty(&var));
|
||||
EXPECT_EQ(std::data(benc) + std::size(benc), serde.end());
|
||||
tr_variantClear(&var);
|
||||
|
||||
benc = "le"sv;
|
||||
EXPECT_TRUE(tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, benc, &end));
|
||||
EXPECT_EQ(std::data(benc) + std::size(benc), end);
|
||||
EXPECT_EQ(benc, tr_variantToStr(&val, TR_VARIANT_FMT_BENC));
|
||||
tr_variantClear(&val);
|
||||
var = serde.parse(benc).value_or(tr_variant{});
|
||||
EXPECT_TRUE(tr_variantIsList(&var));
|
||||
EXPECT_EQ(std::data(benc) + std::size(benc), serde.end());
|
||||
EXPECT_EQ(benc, serde.to_string(var));
|
||||
tr_variantClear(&var);
|
||||
|
||||
benc = "d20:"sv;
|
||||
end = nullptr;
|
||||
ok = tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, benc, &end);
|
||||
EXPECT_FALSE(ok);
|
||||
EXPECT_EQ(nullptr, end);
|
||||
var = serde.parse(benc).value_or(tr_variant{});
|
||||
EXPECT_TRUE(tr_variantIsEmpty(&var));
|
||||
EXPECT_EQ(std::data(benc) + 1U, serde.end());
|
||||
}
|
||||
|
||||
TEST_F(VariantTest, bencParseAndReencode)
|
||||
|
@ -282,64 +281,67 @@ TEST_F(VariantTest, bencParseAndReencode)
|
|||
{ " "sv, false },
|
||||
} };
|
||||
|
||||
auto serde = tr_variant_serde::benc();
|
||||
serde.inplace();
|
||||
|
||||
for (auto const& test : Tests)
|
||||
{
|
||||
tr_variant val;
|
||||
char const* end = nullptr;
|
||||
auto const is_good = tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, test.benc, &end);
|
||||
auto var = serde.parse(test.benc);
|
||||
|
||||
EXPECT_EQ(test.is_good, is_good);
|
||||
if (is_good)
|
||||
EXPECT_EQ(test.is_good, var.has_value());
|
||||
if (var)
|
||||
{
|
||||
EXPECT_EQ(test.benc.data() + test.benc.size(), end);
|
||||
EXPECT_EQ(test.benc, tr_variantToStr(&val, TR_VARIANT_FMT_BENC));
|
||||
tr_variantClear(&val);
|
||||
EXPECT_EQ(test.benc.data() + test.benc.size(), serde.end());
|
||||
EXPECT_EQ(test.benc, serde.to_string(*var));
|
||||
tr_variantClear(&*var);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(VariantTest, bencSortWhenSerializing)
|
||||
{
|
||||
auto constexpr In = "lld1:bi32e1:ai64eeee"sv;
|
||||
auto constexpr ExpectedOut = "lld1:ai64e1:bi32eeee"sv;
|
||||
static auto constexpr In = "lld1:bi32e1:ai64eeee"sv;
|
||||
static auto constexpr ExpectedOut = "lld1:ai64e1:bi32eeee"sv;
|
||||
|
||||
tr_variant val;
|
||||
char const* end = nullptr;
|
||||
auto const ok = tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, In, &end);
|
||||
EXPECT_TRUE(ok);
|
||||
EXPECT_EQ(std::data(In) + std::size(In), end);
|
||||
EXPECT_EQ(ExpectedOut, tr_variantToStr(&val, TR_VARIANT_FMT_BENC));
|
||||
auto serde = tr_variant_serde::benc();
|
||||
auto var = serde.inplace().parse(In);
|
||||
EXPECT_TRUE(var.has_value());
|
||||
EXPECT_EQ(std::data(In) + std::size(In), serde.end());
|
||||
EXPECT_EQ(ExpectedOut, serde.to_string(*var));
|
||||
|
||||
tr_variantClear(&val);
|
||||
tr_variantClear(&*var);
|
||||
}
|
||||
|
||||
TEST_F(VariantTest, bencMalformedTooManyEndings)
|
||||
{
|
||||
auto constexpr In = "leee"sv;
|
||||
auto constexpr ExpectedOut = "le"sv;
|
||||
static auto constexpr In = "leee"sv;
|
||||
static auto constexpr ExpectedOut = "le"sv;
|
||||
|
||||
tr_variant val;
|
||||
char const* end = nullptr;
|
||||
auto const ok = tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, In, &end);
|
||||
EXPECT_TRUE(ok);
|
||||
EXPECT_EQ(std::data(In) + std::size(ExpectedOut), end);
|
||||
EXPECT_EQ(ExpectedOut, tr_variantToStr(&val, TR_VARIANT_FMT_BENC));
|
||||
auto serde = tr_variant_serde::benc();
|
||||
auto var = serde.inplace().parse(In);
|
||||
EXPECT_TRUE(var.has_value());
|
||||
EXPECT_EQ(std::data(In) + std::size(ExpectedOut), serde.end());
|
||||
EXPECT_EQ(ExpectedOut, serde.to_string(*var));
|
||||
|
||||
tr_variantClear(&val);
|
||||
tr_variantClear(&*var);
|
||||
}
|
||||
|
||||
TEST_F(VariantTest, bencMalformedNoEnding)
|
||||
{
|
||||
auto constexpr In = "l1:a1:b1:c"sv;
|
||||
tr_variant val;
|
||||
EXPECT_FALSE(tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, In));
|
||||
static auto constexpr In = "l1:a1:b1:c"sv;
|
||||
|
||||
auto serde = tr_variant_serde::benc();
|
||||
auto const var = serde.inplace().parse(In);
|
||||
EXPECT_FALSE(var.has_value());
|
||||
}
|
||||
|
||||
TEST_F(VariantTest, bencMalformedIncompleteString)
|
||||
{
|
||||
auto constexpr In = "1:"sv;
|
||||
tr_variant val;
|
||||
EXPECT_FALSE(tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, In));
|
||||
static auto constexpr In = "1:"sv;
|
||||
|
||||
auto serde = tr_variant_serde::benc();
|
||||
auto const var = serde.inplace().parse(In);
|
||||
EXPECT_FALSE(var.has_value());
|
||||
}
|
||||
|
||||
TEST_F(VariantTest, bencToJson)
|
||||
|
@ -359,13 +361,15 @@ TEST_F(VariantTest, bencToJson)
|
|||
R"({"args":{"status":[],"status2":[]},"result":"success"})"sv } }
|
||||
};
|
||||
|
||||
auto benc_serde = tr_variant_serde::benc();
|
||||
auto json_serde = tr_variant_serde::json();
|
||||
benc_serde.inplace();
|
||||
json_serde.compact();
|
||||
|
||||
for (auto const& test : Tests)
|
||||
{
|
||||
tr_variant top;
|
||||
tr_variantFromBuf(&top, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, test.benc);
|
||||
|
||||
auto const str = tr_variantToStr(&top, TR_VARIANT_FMT_JSON_LEAN);
|
||||
EXPECT_EQ(test.expected, stripWhitespace(str));
|
||||
auto top = benc_serde.parse(test.benc).value_or(tr_variant{});
|
||||
EXPECT_EQ(test.expected, stripWhitespace(json_serde.to_string(top)));
|
||||
tr_variantClear(&top);
|
||||
}
|
||||
}
|
||||
|
@ -433,15 +437,11 @@ TEST_F(VariantTest, stackSmash)
|
|||
std::string const in = std::string(Depth, 'l') + std::string(Depth, 'e');
|
||||
|
||||
// confirm that it fails instead of crashing
|
||||
char const* end = nullptr;
|
||||
tr_variant val;
|
||||
tr_error* error = nullptr;
|
||||
auto ok = tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, in, &end, &error);
|
||||
EXPECT_NE(nullptr, error);
|
||||
EXPECT_EQ(E2BIG, error->code);
|
||||
EXPECT_FALSE(ok);
|
||||
|
||||
tr_error_clear(&error);
|
||||
auto serde = tr_variant_serde::benc();
|
||||
auto var = serde.inplace().parse(in);
|
||||
EXPECT_FALSE(var.has_value());
|
||||
EXPECT_NE(nullptr, serde.error_);
|
||||
EXPECT_EQ(E2BIG, serde.error_ != nullptr ? serde.error_->code : 0);
|
||||
}
|
||||
|
||||
TEST_F(VariantTest, boolAndIntRecast)
|
||||
|
@ -547,24 +547,23 @@ TEST_F(VariantTest, dictFindType)
|
|||
|
||||
TEST_F(VariantTest, variantFromBufFuzz)
|
||||
{
|
||||
auto benc_serde = tr_variant_serde::json();
|
||||
auto json_serde = tr_variant_serde::json();
|
||||
auto buf = std::vector<char>{};
|
||||
|
||||
for (size_t i = 0; i < 100000; ++i)
|
||||
{
|
||||
buf.resize(tr_rand_int(4096U));
|
||||
tr_rand_buffer(std::data(buf), std::size(buf));
|
||||
// std::cerr << '[' << tr_base64_encode({ std::data(buf), std::size(buf) }) << ']' << std::endl;
|
||||
|
||||
if (auto top = tr_variant{};
|
||||
tr_variantFromBuf(&top, TR_VARIANT_PARSE_JSON | TR_VARIANT_PARSE_INPLACE, buf, nullptr, nullptr))
|
||||
if (auto var = benc_serde.inplace().parse(buf); var)
|
||||
{
|
||||
tr_variantClear(&top);
|
||||
tr_variantClear(&*var);
|
||||
}
|
||||
|
||||
if (auto top = tr_variant{};
|
||||
tr_variantFromBuf(&top, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, buf, nullptr, nullptr))
|
||||
if (auto var = json_serde.inplace().parse(buf); var)
|
||||
{
|
||||
tr_variantClear(&top);
|
||||
tr_variantClear(&*var);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -356,20 +356,20 @@ int tr_main(int argc, char* argv[])
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
auto serde = tr_variant_serde::benc();
|
||||
for (auto const& filename : options.files)
|
||||
{
|
||||
tr_variant top;
|
||||
bool changed = false;
|
||||
tr_error* error = nullptr;
|
||||
|
||||
fmt::print("{:s}\n", filename);
|
||||
|
||||
if (!tr_variantFromFile(&top, TR_VARIANT_PARSE_BENC, filename, &error))
|
||||
auto otop = serde.parse_file(filename);
|
||||
if (!otop)
|
||||
{
|
||||
fmt::print("\tError reading file: {:s}\n", error->message);
|
||||
tr_error_free(error);
|
||||
fmt::print("\tError reading file: {:s}\n", serde.error_->message);
|
||||
continue;
|
||||
}
|
||||
auto& top = *otop;
|
||||
|
||||
if (options.deleteme != nullptr)
|
||||
{
|
||||
|
@ -394,7 +394,7 @@ int tr_main(int argc, char* argv[])
|
|||
if (changed)
|
||||
{
|
||||
++changedCount;
|
||||
tr_variantToFile(&top, TR_VARIANT_FMT_BENC, filename);
|
||||
serde.to_file(top, filename);
|
||||
}
|
||||
|
||||
tr_variantClear(&top);
|
||||
|
|
|
@ -2149,7 +2149,6 @@ static void filterIds(tr_variant* top, Config& config)
|
|||
}
|
||||
static int processResponse(char const* rpcurl, std::string_view response, Config& config)
|
||||
{
|
||||
auto top = tr_variant{};
|
||||
auto status = int{ EXIT_SUCCESS };
|
||||
|
||||
if (config.debug)
|
||||
|
@ -2163,13 +2162,14 @@ static int processResponse(char const* rpcurl, std::string_view response, Config
|
|||
return status;
|
||||
}
|
||||
|
||||
if (!tr_variantFromBuf(&top, TR_VARIANT_PARSE_JSON | TR_VARIANT_PARSE_INPLACE, response))
|
||||
if (auto otop = tr_variant_serde::json().inplace().parse(response); !otop)
|
||||
{
|
||||
tr_logAddWarn(fmt::format("Unable to parse response '{}'", response));
|
||||
status |= EXIT_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& top = *otop;
|
||||
int64_t tag = -1;
|
||||
auto sv = std::string_view{};
|
||||
|
||||
|
@ -2258,14 +2258,14 @@ static int processResponse(char const* rpcurl, std::string_view response, Config
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
tr_variantClear(&top);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
status |= EXIT_FAILURE;
|
||||
}
|
||||
|
||||
tr_variantClear(&top);
|
||||
}
|
||||
|
||||
return status;
|
||||
|
@ -2339,8 +2339,7 @@ static void tr_curl_easy_cleanup(CURL* curl)
|
|||
|
||||
static int flush(char const* rpcurl, tr_variant* benc, Config& config)
|
||||
{
|
||||
int status = EXIT_SUCCESS;
|
||||
auto const json = tr_variantToStr(benc, TR_VARIANT_FMT_JSON_LEAN);
|
||||
auto const json = tr_variant_serde::json().compact().to_string(*benc);
|
||||
auto const scheme = config.use_ssl ? "https"sv : "http"sv;
|
||||
auto const rpcurl_http = fmt::format(FMT_STRING("{:s}://{:s}"), scheme, rpcurl);
|
||||
|
||||
|
@ -2355,6 +2354,7 @@ static int flush(char const* rpcurl, tr_variant* benc, Config& config)
|
|||
fmt::print(stderr, "posting:\n--------\n{:s}\n--------\n", json);
|
||||
}
|
||||
|
||||
auto status = EXIT_SUCCESS;
|
||||
if (auto const res = curl_easy_perform(curl); res != CURLE_OK)
|
||||
{
|
||||
tr_logAddWarn(fmt::format(" ({}) {}", rpcurl_http, curl_easy_strerror(res)));
|
||||
|
|
|
@ -359,12 +359,13 @@ void doScrape(tr_torrent_metainfo const& metainfo)
|
|||
}
|
||||
|
||||
// print it out
|
||||
auto top = tr_variant{};
|
||||
if (!tr_variantFromBuf(&top, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, response.body))
|
||||
auto otop = tr_variant_serde::benc().inplace().parse(response.body);
|
||||
if (!!otop)
|
||||
{
|
||||
fmt::print("error parsing scrape response\n");
|
||||
continue;
|
||||
}
|
||||
auto& top = *otop;
|
||||
|
||||
bool matched = false;
|
||||
if (tr_variant* files = nullptr; tr_variantDictFindDict(&top, TR_KEY_files, &files))
|
||||
|
|
Loading…
Reference in New Issue