2012-12-14 04:34:42 +00:00
|
|
|
/*
|
2014-01-19 01:09:44 +00:00
|
|
|
* This file Copyright (C) 2008-2014 Mnemosyne LLC
|
2012-12-14 04:34:42 +00:00
|
|
|
*
|
2014-01-21 03:10:30 +00:00
|
|
|
* It may be used under the GNU GPL versions 2 or 3
|
2014-01-19 01:09:44 +00:00
|
|
|
* or any future license endorsed by Mnemosyne LLC.
|
2012-12-14 04:34:42 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2021-12-15 21:25:42 +00:00
|
|
|
#include <array>
|
2021-10-17 20:17:18 +00:00
|
|
|
#include <cctype> /* isdigit() */
|
2021-09-29 00:09:01 +00:00
|
|
|
#include <deque>
|
2021-10-17 20:17:18 +00:00
|
|
|
#include <cerrno>
|
2021-10-15 13:28:47 +00:00
|
|
|
#include <string_view>
|
2021-10-15 18:13:33 +00:00
|
|
|
#include <optional>
|
2012-12-14 04:34:42 +00:00
|
|
|
|
|
|
|
#include <event2/buffer.h>
|
|
|
|
|
2020-08-11 18:11:55 +00:00
|
|
|
#define LIBTRANSMISSION_VARIANT_MODULE
|
2017-11-14 20:21:28 +00:00
|
|
|
|
2012-12-14 04:34:42 +00:00
|
|
|
#include "transmission.h"
|
2021-11-18 05:37:35 +00:00
|
|
|
|
|
|
|
#include "tr-assert.h"
|
2022-01-13 02:13:58 +00:00
|
|
|
#include "quark.h"
|
2012-12-24 22:38:41 +00:00
|
|
|
#include "utils.h" /* tr_snprintf() */
|
2012-12-14 04:34:42 +00:00
|
|
|
#include "variant-common.h"
|
2021-11-18 05:37:35 +00:00
|
|
|
#include "variant.h"
|
2012-12-14 04:34:42 +00:00
|
|
|
|
2021-11-17 03:29:30 +00:00
|
|
|
using namespace std::literals;
|
|
|
|
|
2021-12-29 08:28:12 +00:00
|
|
|
auto constexpr MaxBencStrLength = size_t{ 128 * 1024 * 1024 }; // arbitrary
|
2017-05-16 18:37:00 +00:00
|
|
|
|
2012-12-14 04:34:42 +00:00
|
|
|
/***
|
2012-12-24 22:38:41 +00:00
|
|
|
**** tr_variantParse()
|
|
|
|
**** tr_variantLoad()
|
2012-12-14 04:34:42 +00:00
|
|
|
***/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The initial i and trailing e are beginning and ending delimiters.
|
|
|
|
* You can have negative numbers such as i-3e. You cannot prefix the
|
|
|
|
* number with a zero such as i04e. However, i0e is valid.
|
|
|
|
* Example: i3e represents the integer "3"
|
2021-11-17 03:29:30 +00:00
|
|
|
*
|
|
|
|
* The maximum number of bit of this integer is unspecified,
|
2012-12-14 04:34:42 +00:00
|
|
|
* but to handle it as a signed 64bit integer is mandatory to handle
|
|
|
|
* "large files" aka .torrent for more that 4Gbyte
|
|
|
|
*/
|
2021-11-17 03:29:30 +00:00
|
|
|
std::optional<int64_t> tr_bencParseInt(std::string_view* benc)
|
2012-12-14 04:34:42 +00:00
|
|
|
{
|
2021-12-29 08:28:12 +00:00
|
|
|
auto constexpr Prefix = "i"sv;
|
|
|
|
auto constexpr Suffix = "e"sv;
|
|
|
|
|
2021-11-17 03:29:30 +00:00
|
|
|
// find the beginning delimiter
|
|
|
|
auto walk = *benc;
|
2021-12-29 08:28:12 +00:00
|
|
|
if (std::size(walk) < 3 || !tr_strvStartsWith(walk, Prefix))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-11-17 03:29:30 +00:00
|
|
|
return {};
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2012-12-14 16:04:44 +00:00
|
|
|
|
2021-11-17 03:29:30 +00:00
|
|
|
// find the ending delimiter
|
2021-12-29 08:28:12 +00:00
|
|
|
walk.remove_prefix(std::size(Prefix));
|
|
|
|
auto const pos = walk.find(Suffix);
|
2021-12-17 05:47:51 +00:00
|
|
|
if (pos == std::string_view::npos)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-11-17 03:29:30 +00:00
|
|
|
return {};
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2012-12-14 16:04:44 +00:00
|
|
|
|
2021-11-17 03:29:30 +00:00
|
|
|
// leading zeroes are not allowed
|
|
|
|
if ((walk[0] == '0' && isdigit(walk[1])) || (walk[0] == '-' && walk[1] == '0' && isdigit(walk[2])))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-11-17 03:29:30 +00:00
|
|
|
return {};
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
2021-12-29 08:28:12 +00:00
|
|
|
// parse the string and make sure the next char is `Suffix`
|
|
|
|
auto const value = tr_parseNum<int64_t>(walk);
|
|
|
|
if (!value || !tr_strvStartsWith(walk, Suffix))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-11-17 03:29:30 +00:00
|
|
|
return {};
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
2021-12-29 08:28:12 +00:00
|
|
|
walk.remove_prefix(std::size(Suffix));
|
2021-11-17 03:29:30 +00:00
|
|
|
*benc = walk;
|
2021-12-29 08:28:12 +00:00
|
|
|
return *value;
|
2012-12-14 04:34:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Byte strings are encoded as follows:
|
|
|
|
* <string length encoded in base ten ASCII>:<string data>
|
|
|
|
* Note that there is no constant beginning delimiter, and no ending delimiter.
|
|
|
|
* Example: 4:spam represents the string "spam"
|
|
|
|
*/
|
2021-11-17 03:29:30 +00:00
|
|
|
std::optional<std::string_view> tr_bencParseStr(std::string_view* benc)
|
2012-12-14 04:34:42 +00:00
|
|
|
{
|
2021-11-17 03:29:30 +00:00
|
|
|
// find the ':' delimiter
|
|
|
|
auto const colon_pos = benc->find(':');
|
2021-12-17 05:47:51 +00:00
|
|
|
if (colon_pos == std::string_view::npos)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-11-17 03:29:30 +00:00
|
|
|
return {};
|
|
|
|
}
|
2012-12-14 04:34:42 +00:00
|
|
|
|
2021-11-17 03:29:30 +00:00
|
|
|
// get the string length
|
2021-12-29 08:28:12 +00:00
|
|
|
auto svtmp = benc->substr(0, colon_pos);
|
|
|
|
auto const len = tr_parseNum<size_t>(svtmp);
|
|
|
|
if (!len || *len >= MaxBencStrLength)
|
2021-11-17 03:29:30 +00:00
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
2012-12-14 04:34:42 +00:00
|
|
|
|
2021-11-17 03:29:30 +00:00
|
|
|
// do we have `len` bytes of string data?
|
2021-12-29 08:28:12 +00:00
|
|
|
svtmp = benc->substr(colon_pos + 1);
|
|
|
|
if (std::size(svtmp) < len)
|
2021-11-17 03:29:30 +00:00
|
|
|
{
|
|
|
|
return {};
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2012-12-14 04:34:42 +00:00
|
|
|
|
2021-12-29 08:28:12 +00:00
|
|
|
auto const string = svtmp.substr(0, *len);
|
|
|
|
*benc = svtmp.substr(*len);
|
2021-11-17 03:29:30 +00:00
|
|
|
return string;
|
2012-12-14 04:34:42 +00:00
|
|
|
}
|
|
|
|
|
2021-10-15 18:13:33 +00:00
|
|
|
static tr_variant* get_node(std::deque<tr_variant*>& stack, std::optional<tr_quark>& dict_key, tr_variant* top, int* err)
|
2012-12-14 04:34:42 +00:00
|
|
|
{
|
2021-09-15 00:18:09 +00:00
|
|
|
tr_variant* node = nullptr;
|
2012-12-14 04:34:42 +00:00
|
|
|
|
2021-09-29 00:09:01 +00:00
|
|
|
if (std::empty(stack))
|
2012-12-14 04:34:42 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
node = top;
|
2012-12-14 04:34:42 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
else
|
2012-12-24 22:38:41 +00:00
|
|
|
{
|
2021-09-29 00:09:01 +00:00
|
|
|
auto* parent = stack.back();
|
2012-12-14 04:34:42 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (tr_variantIsList(parent))
|
2012-12-24 22:38:41 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
node = tr_variantListAdd(parent);
|
2012-12-24 22:38:41 +00:00
|
|
|
}
|
2021-10-15 18:13:33 +00:00
|
|
|
else if (dict_key && tr_variantIsDict(parent))
|
2012-12-24 22:38:41 +00:00
|
|
|
{
|
2021-10-15 18:13:33 +00:00
|
|
|
node = tr_variantDictAdd(parent, *dict_key);
|
|
|
|
dict_key.reset();
|
2012-12-24 22:38:41 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
else
|
2012-12-24 22:38:41 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
*err = EILSEQ;
|
2012-12-24 22:38:41 +00:00
|
|
|
}
|
|
|
|
}
|
2012-12-14 04:34:42 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return node;
|
2012-12-14 04:34:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This function's previous recursive implementation was
|
|
|
|
* easier to read, but was vulnerable to a smash-stacking
|
|
|
|
* attack via maliciously-crafted bencoded data. (#667)
|
|
|
|
*/
|
2021-11-18 05:37:35 +00:00
|
|
|
int tr_variantParseBenc(tr_variant& top, int parse_opts, std::string_view benc, char const** setme_end)
|
2012-12-14 04:34:42 +00:00
|
|
|
{
|
2021-11-18 05:37:35 +00:00
|
|
|
TR_ASSERT((parse_opts & TR_VARIANT_PARSE_BENC) != 0);
|
|
|
|
|
2021-09-29 00:09:01 +00:00
|
|
|
auto stack = std::deque<tr_variant*>{};
|
2021-10-15 18:13:33 +00:00
|
|
|
auto key = std::optional<tr_quark>{};
|
2012-12-14 04:34:42 +00:00
|
|
|
|
2021-11-17 03:29:30 +00:00
|
|
|
tr_variantInit(&top, 0);
|
2012-12-14 04:34:42 +00:00
|
|
|
|
2021-11-17 03:29:30 +00:00
|
|
|
int err = 0;
|
|
|
|
for (;;)
|
2012-12-14 04:34:42 +00:00
|
|
|
{
|
2021-11-17 03:29:30 +00:00
|
|
|
if (std::empty(benc))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
err = EILSEQ;
|
|
|
|
}
|
2012-12-24 22:38:41 +00:00
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if (err != 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2012-12-14 04:34:42 +00:00
|
|
|
|
2021-11-17 03:29:30 +00:00
|
|
|
switch (benc.front())
|
2012-12-14 04:34:42 +00:00
|
|
|
{
|
2021-11-17 03:29:30 +00:00
|
|
|
case 'i': // int
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-11-17 03:29:30 +00:00
|
|
|
auto const value = tr_bencParseInt(&benc);
|
|
|
|
if (!value)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2012-12-24 22:38:41 +00:00
|
|
|
|
2021-11-28 01:58:35 +00:00
|
|
|
if (tr_variant* const v = get_node(stack, key, &top, &err); v != nullptr)
|
2021-11-17 03:29:30 +00:00
|
|
|
{
|
|
|
|
tr_variantInitInt(v, *value);
|
|
|
|
}
|
|
|
|
break;
|
2012-12-24 22:38:41 +00:00
|
|
|
}
|
2021-11-17 03:29:30 +00:00
|
|
|
case 'l': // list
|
2021-11-28 01:58:35 +00:00
|
|
|
benc.remove_prefix(1);
|
2012-12-14 04:34:42 +00:00
|
|
|
|
2021-11-28 01:58:35 +00:00
|
|
|
if (tr_variant* const v = get_node(stack, key, &top, &err); v != nullptr)
|
|
|
|
{
|
|
|
|
tr_variantInitList(v, 0);
|
|
|
|
stack.push_back(v);
|
2012-12-24 22:38:41 +00:00
|
|
|
}
|
2021-11-28 01:58:35 +00:00
|
|
|
break;
|
|
|
|
|
2021-11-17 03:29:30 +00:00
|
|
|
case 'd': // dict
|
2021-11-28 01:58:35 +00:00
|
|
|
benc.remove_prefix(1);
|
2021-11-17 03:29:30 +00:00
|
|
|
|
2021-11-28 01:58:35 +00:00
|
|
|
if (tr_variant* const v = get_node(stack, key, &top, &err); v != nullptr)
|
|
|
|
{
|
|
|
|
tr_variantInitDict(v, 0);
|
|
|
|
stack.push_back(v);
|
2012-12-14 04:34:42 +00:00
|
|
|
}
|
2021-11-28 01:58:35 +00:00
|
|
|
break;
|
2021-11-17 03:29:30 +00:00
|
|
|
case 'e': // end of list or dict
|
2021-11-28 01:58:35 +00:00
|
|
|
benc.remove_prefix(1);
|
2012-12-14 04:34:42 +00:00
|
|
|
|
2021-11-28 01:58:35 +00:00
|
|
|
if (std::empty(stack) || key)
|
|
|
|
{
|
|
|
|
err = EILSEQ;
|
2021-11-17 03:29:30 +00:00
|
|
|
break;
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2021-11-28 01:58:35 +00:00
|
|
|
|
|
|
|
stack.pop_back();
|
|
|
|
break;
|
|
|
|
|
2021-11-17 03:29:30 +00:00
|
|
|
case '0':
|
|
|
|
case '1':
|
|
|
|
case '2':
|
|
|
|
case '3':
|
|
|
|
case '4':
|
|
|
|
case '5':
|
|
|
|
case '6':
|
|
|
|
case '7':
|
|
|
|
case '8':
|
|
|
|
case '9': // string?
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-11-17 03:29:30 +00:00
|
|
|
auto const sv = tr_bencParseStr(&benc);
|
|
|
|
if (!sv)
|
|
|
|
{
|
2022-01-10 20:27:05 +00:00
|
|
|
benc.remove_prefix(1);
|
2021-11-17 03:29:30 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!key && !std::empty(stack) && tr_variantIsDict(stack.back()))
|
2021-10-15 17:27:12 +00:00
|
|
|
{
|
2021-11-17 03:29:30 +00:00
|
|
|
key = tr_quark_new(*sv);
|
2021-10-15 17:27:12 +00:00
|
|
|
}
|
2021-11-17 03:29:30 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
tr_variant* const v = get_node(stack, key, &top, &err);
|
|
|
|
if (v != nullptr)
|
|
|
|
{
|
2021-11-18 05:37:35 +00:00
|
|
|
if ((parse_opts & TR_VARIANT_PARSE_INPLACE) != 0)
|
|
|
|
{
|
|
|
|
tr_variantInitStrView(v, *sv);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tr_variantInitStr(v, *sv);
|
|
|
|
}
|
2021-11-17 03:29:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2021-11-17 03:29:30 +00:00
|
|
|
default: // invalid bencoded text... march past it
|
|
|
|
benc.remove_prefix(1);
|
|
|
|
break;
|
2012-12-14 04:34:42 +00:00
|
|
|
}
|
2012-12-24 22:38:41 +00:00
|
|
|
|
2021-09-29 00:09:01 +00:00
|
|
|
if (std::empty(stack))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2012-12-14 04:34:42 +00:00
|
|
|
}
|
|
|
|
|
2021-11-17 03:29:30 +00:00
|
|
|
if (err == 0 && (top.type == 0 || !std::empty(stack)))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
err = EILSEQ;
|
|
|
|
}
|
2012-12-14 04:34:42 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (err == 0)
|
2017-01-17 20:55:34 +00:00
|
|
|
{
|
2021-09-15 00:18:09 +00:00
|
|
|
if (setme_end != nullptr)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-11-17 03:29:30 +00:00
|
|
|
*setme_end = std::data(benc);
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2017-01-17 20:55:34 +00:00
|
|
|
}
|
2021-11-17 03:29:30 +00:00
|
|
|
else if (top.type != 0)
|
2017-01-17 20:55:34 +00:00
|
|
|
{
|
2021-11-17 03:29:30 +00:00
|
|
|
tr_variantFree(&top);
|
|
|
|
tr_variantInit(&top, 0);
|
2017-01-17 20:55:34 +00:00
|
|
|
}
|
2012-12-14 04:34:42 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return err;
|
2012-12-14 04:34:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/****
|
|
|
|
*****
|
|
|
|
****/
|
|
|
|
|
2021-09-12 17:41:49 +00:00
|
|
|
static void saveIntFunc(tr_variant const* val, void* vevbuf)
|
2012-12-14 04:34:42 +00:00
|
|
|
{
|
2021-09-12 17:41:49 +00:00
|
|
|
auto* evbuf = static_cast<struct evbuffer*>(vevbuf);
|
2017-04-19 12:04:45 +00:00
|
|
|
evbuffer_add_printf(evbuf, "i%" PRId64 "e", val->val.i);
|
2012-12-14 04:34:42 +00:00
|
|
|
}
|
|
|
|
|
2021-09-12 17:41:49 +00:00
|
|
|
static void saveBoolFunc(tr_variant const* val, void* vevbuf)
|
2012-12-14 04:34:42 +00:00
|
|
|
{
|
2021-09-12 17:41:49 +00:00
|
|
|
auto* evbuf = static_cast<struct evbuffer*>(vevbuf);
|
2017-04-19 12:04:45 +00:00
|
|
|
if (val->val.b)
|
|
|
|
{
|
|
|
|
evbuffer_add(evbuf, "i1e", 3);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
evbuffer_add(evbuf, "i0e", 3);
|
|
|
|
}
|
2012-12-14 04:34:42 +00:00
|
|
|
}
|
|
|
|
|
2021-09-12 17:41:49 +00:00
|
|
|
static void saveRealFunc(tr_variant const* val, void* vevbuf)
|
2012-12-14 04:34:42 +00:00
|
|
|
{
|
2021-11-17 03:29:30 +00:00
|
|
|
auto buf = std::array<char, 64>{};
|
|
|
|
int const len = tr_snprintf(std::data(buf), std::size(buf), "%f", val->val.d);
|
2012-12-14 04:34:42 +00:00
|
|
|
|
2021-11-17 03:29:30 +00:00
|
|
|
auto* evbuf = static_cast<evbuffer*>(vevbuf);
|
2017-04-19 12:04:45 +00:00
|
|
|
evbuffer_add_printf(evbuf, "%d:", len);
|
2021-11-17 03:29:30 +00:00
|
|
|
evbuffer_add(evbuf, std::data(buf), len);
|
2012-12-14 04:34:42 +00:00
|
|
|
}
|
|
|
|
|
2021-09-12 17:41:49 +00:00
|
|
|
static void saveStringFunc(tr_variant const* v, void* vevbuf)
|
2012-12-14 04:34:42 +00:00
|
|
|
{
|
2021-11-05 06:29:19 +00:00
|
|
|
auto sv = std::string_view{};
|
|
|
|
(void)!tr_variantGetStrView(v, &sv);
|
2019-02-18 22:38:24 +00:00
|
|
|
|
2021-09-12 17:41:49 +00:00
|
|
|
auto* evbuf = static_cast<struct evbuffer*>(vevbuf);
|
2021-11-05 06:29:19 +00:00
|
|
|
evbuffer_add_printf(evbuf, "%zu:", std::size(sv));
|
|
|
|
evbuffer_add(evbuf, std::data(sv), std::size(sv));
|
2012-12-14 04:34:42 +00:00
|
|
|
}
|
|
|
|
|
2021-10-24 16:41:54 +00:00
|
|
|
static void saveDictBeginFunc(tr_variant const* /*val*/, void* vevbuf)
|
2012-12-14 04:34:42 +00:00
|
|
|
{
|
2021-09-12 17:41:49 +00:00
|
|
|
auto* evbuf = static_cast<struct evbuffer*>(vevbuf);
|
2017-04-19 12:04:45 +00:00
|
|
|
evbuffer_add(evbuf, "d", 1);
|
2012-12-14 04:34:42 +00:00
|
|
|
}
|
|
|
|
|
2021-10-24 16:41:54 +00:00
|
|
|
static void saveListBeginFunc(tr_variant const* /*val*/, void* vevbuf)
|
2012-12-14 04:34:42 +00:00
|
|
|
{
|
2021-09-12 17:41:49 +00:00
|
|
|
auto* evbuf = static_cast<struct evbuffer*>(vevbuf);
|
2017-04-19 12:04:45 +00:00
|
|
|
evbuffer_add(evbuf, "l", 1);
|
2012-12-14 04:34:42 +00:00
|
|
|
}
|
|
|
|
|
2021-10-24 16:41:54 +00:00
|
|
|
static void saveContainerEndFunc(tr_variant const* /*val*/, void* vevbuf)
|
2012-12-14 04:34:42 +00:00
|
|
|
{
|
2021-09-12 17:41:49 +00:00
|
|
|
auto* evbuf = static_cast<struct evbuffer*>(vevbuf);
|
2017-04-19 12:04:45 +00:00
|
|
|
evbuffer_add(evbuf, "e", 1);
|
2012-12-14 04:34:42 +00:00
|
|
|
}
|
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
static struct VariantWalkFuncs const walk_funcs = {
|
|
|
|
saveIntFunc, //
|
|
|
|
saveBoolFunc, //
|
|
|
|
saveRealFunc, //
|
|
|
|
saveStringFunc, //
|
|
|
|
saveDictBeginFunc, //
|
|
|
|
saveListBeginFunc, //
|
|
|
|
saveContainerEndFunc, //
|
2017-04-19 12:04:45 +00:00
|
|
|
};
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
void tr_variantToBufBenc(tr_variant const* top, struct evbuffer* buf)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
tr_variantWalk(top, &walk_funcs, buf, true);
|
2012-12-14 04:34:42 +00:00
|
|
|
}
|