Coverage Report

Created: 2025-11-24 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libtorrent/src/entry.cpp
Line
Count
Source
1
/*
2
3
Copyright (c) 2003-2008, 2010, 2014-2020, Arvid Norberg
4
Copyright (c) 2016-2017, Alden Torres
5
Copyright (c) 2016, Steven Siloti
6
Copyright (c) 2017, Andrei Kurushin
7
Copyright (c) 2019, Amir Abrams
8
All rights reserved.
9
10
Redistribution and use in source and binary forms, with or without
11
modification, are permitted provided that the following conditions
12
are met:
13
14
    * Redistributions of source code must retain the above copyright
15
      notice, this list of conditions and the following disclaimer.
16
    * Redistributions in binary form must reproduce the above copyright
17
      notice, this list of conditions and the following disclaimer in
18
      the documentation and/or other materials provided with the distribution.
19
    * Neither the name of the author nor the names of its
20
      contributors may be used to endorse or promote products derived
21
      from this software without specific prior written permission.
22
23
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33
POSSIBILITY OF SUCH DAMAGE.
34
35
*/
36
37
#include "libtorrent/config.hpp"
38
#include "libtorrent/bdecode.hpp"
39
#include "libtorrent/bencode.hpp"
40
#include "libtorrent/entry.hpp"
41
#include "libtorrent/hex.hpp"
42
#include "libtorrent/string_util.hpp"
43
#include "libtorrent/aux_/throw.hpp"
44
45
namespace libtorrent {
46
47
namespace aux {
48
49
  string_view integer_to_str(std::array<char, 21>& buf, entry::integer_type val)
50
0
  {
51
0
    if (val >= 0)
52
0
    {
53
0
      if (val < 10)
54
0
      {
55
0
        buf[0] = '0' + static_cast<char>(val);
56
0
        return {buf.data(), std::size_t(1)};
57
0
      }
58
0
      if (val < 100)
59
0
      {
60
0
        buf[0] = '0' + (val / 10) % 10;
61
0
        buf[1] = '0' + val % 10;
62
0
        return {buf.data(), std::size_t(2)};
63
0
      }
64
0
      if (val < 1000)
65
0
      {
66
0
        buf[0] = '0' + (val / 100) % 10;
67
0
        buf[1] = '0' + (val / 10) % 10;
68
0
        buf[2] = '0' + val % 10;
69
0
        return {buf.data(), std::size_t(3)};
70
0
      }
71
0
      if (val < 10000)
72
0
      {
73
0
        buf[0] = '0' + (val / 1000) % 10;
74
0
        buf[1] = '0' + (val / 100) % 10;
75
0
        buf[2] = '0' + (val / 10) % 10;
76
0
        buf[3] = '0' + val % 10;
77
0
        return {buf.data(), std::size_t(4)};
78
0
      }
79
0
      if (val < 100000)
80
0
      {
81
0
        buf[0] = '0' + (val / 10000) % 10;
82
0
        buf[1] = '0' + (val / 1000) % 10;
83
0
        buf[2] = '0' + (val / 100) % 10;
84
0
        buf[3] = '0' + (val / 10) % 10;
85
0
        buf[4] = '0' + val % 10;
86
0
        return {buf.data(), std::size_t(5)};
87
0
      }
88
0
    }
89
    // val == 0 is handled in the fast-path above
90
0
    TORRENT_ASSERT(val != 0);
91
    // slow path
92
    // convert positive values to negative, since the negative space is
93
    // larger, so we can fit INT64_MIN
94
0
    int sign = 1;
95
0
    if (val > 0)
96
0
    {
97
0
      sign = 0;
98
0
      val = -val;
99
0
    }
100
0
    char* ptr = &buf.back();
101
0
    do
102
0
    {
103
0
      *ptr-- = '0' - char(val % 10);
104
0
      val /= 10;
105
0
    } while (val != 0);
106
0
    if (sign) *ptr-- = '-';
107
0
    ++ptr;
108
0
    return {ptr, static_cast<std::size_t>(&buf.back() - ptr + 1)};
109
0
  }
110
} // aux
111
112
namespace {
113
114
  [[noreturn]] inline void throw_error()
115
0
  { aux::throw_ex<system_error>(errors::invalid_entry_type); }
116
117
  template <class T>
118
  void call_destructor(T* o)
119
0
  {
120
0
    TORRENT_ASSERT(o);
121
0
    o->~T();
122
0
  }
Unexecuted instantiation: entry.cpp:void libtorrent::(anonymous namespace)::call_destructor<long>(long*)
Unexecuted instantiation: entry.cpp:void libtorrent::(anonymous namespace)::call_destructor<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)
Unexecuted instantiation: entry.cpp:void libtorrent::(anonymous namespace)::call_destructor<std::__1::vector<libtorrent::entry, std::__1::allocator<libtorrent::entry> > >(std::__1::vector<libtorrent::entry, std::__1::allocator<libtorrent::entry> >*)
Unexecuted instantiation: entry.cpp:void libtorrent::(anonymous namespace)::call_destructor<std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, libtorrent::entry, libtorrent::aux::strview_less, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, libtorrent::entry> > > >(std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, libtorrent::entry, libtorrent::aux::strview_less, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, libtorrent::entry> > >*)
Unexecuted instantiation: entry.cpp:void libtorrent::(anonymous namespace)::call_destructor<std::__1::vector<char, std::__1::allocator<char> > >(std::__1::vector<char, std::__1::allocator<char> >*)
123
} // anonymous
124
125
  entry& entry::operator[](string_view key)
126
0
  {
127
    // at least GCC-5.4 for ARM (on travis) has a libstdc++ whose debug map$
128
    // doesn't seem to support transparent comparators$
129
#if ! defined _GLIBCXX_DEBUG
130
    auto const i = dict().find(key);
131
#else
132
0
    auto const i = dict().find(std::string(key));
133
0
#endif
134
0
    if (i != dict().end()) return i->second;
135
0
    auto const ret = dict().emplace(
136
0
      std::piecewise_construct,
137
0
      std::forward_as_tuple(key),
138
0
      std::forward_as_tuple()).first;
139
0
    return ret->second;
140
0
  }
141
142
  const entry& entry::operator[](string_view key) const
143
0
  {
144
    // at least GCC-5.4 for ARM (on travis) has a libstdc++ whose debug map$
145
    // doesn't seem to support transparent comparators$
146
#if ! defined _GLIBCXX_DEBUG
147
    auto const i = dict().find(key);
148
#else
149
0
    auto const i = dict().find(std::string(key));
150
0
#endif
151
0
    if (i == dict().end()) throw_error();
152
0
    return i->second;
153
0
  }
154
155
  entry* entry::find_key(string_view key)
156
0
  {
157
#if ! defined _GLIBCXX_DEBUG
158
    auto const i = dict().find(key);
159
#else
160
0
    auto const i = dict().find(std::string(key));
161
0
#endif
162
0
    if (i == dict().end()) return nullptr;
163
0
    return &i->second;
164
0
  }
165
166
  entry const* entry::find_key(string_view key) const
167
0
  {
168
#if ! defined _GLIBCXX_DEBUG
169
    auto const i = dict().find(key);
170
#else
171
0
    auto const i = dict().find(std::string(key));
172
0
#endif
173
0
    if (i == dict().end()) return nullptr;
174
0
    return &i->second;
175
0
  }
176
177
  entry::data_type entry::type() const
178
0
  {
179
0
#if TORRENT_USE_ASSERTS
180
0
    m_type_queried = true;
181
0
#endif
182
0
    return entry::data_type(m_type);
183
0
  }
184
185
0
  entry::~entry() { destruct(); }
186
187
  entry& entry::operator=(const entry& e) &
188
0
  {
189
0
    if (&e == this) return *this;
190
0
    destruct();
191
0
    copy(e);
192
0
    return *this;
193
0
  }
194
195
  entry& entry::operator=(entry&& e) & noexcept
196
0
  {
197
0
    if (&e == this) return *this;
198
0
    destruct();
199
0
    const auto t = e.type();
200
0
    switch (t)
201
0
    {
202
0
    case int_t:
203
0
      new (&data) integer_type(std::move(e.integer()));
204
0
      break;
205
0
    case string_t:
206
0
      new (&data) string_type(std::move(e.string()));
207
0
      break;
208
0
    case list_t:
209
0
      new (&data) list_type(std::move(e.list()));
210
0
      break;
211
0
    case dictionary_t:
212
0
      new (&data) dictionary_type(std::move(e.dict()));
213
0
      break;
214
0
    case undefined_t:
215
0
      break;
216
0
    case preformatted_t:
217
0
      new (&data) preformatted_type(std::move(e.preformatted()));
218
0
      break;
219
0
    }
220
0
    m_type = t;
221
0
#if TORRENT_USE_ASSERTS
222
0
    m_type_queried = true;
223
0
#endif
224
0
    return *this;
225
0
  }
226
227
  entry::integer_type& entry::integer()
228
0
  {
229
0
    if (m_type == undefined_t) construct(int_t);
230
#ifdef BOOST_NO_EXCEPTIONS
231
    TORRENT_ASSERT(m_type_queried);
232
#endif
233
0
    if (m_type != int_t) throw_error();
234
0
    TORRENT_ASSERT(m_type == int_t);
235
0
    return *reinterpret_cast<integer_type*>(&data);
236
0
  }
237
238
  entry::integer_type const& entry::integer() const
239
0
  {
240
0
    if (m_type != int_t) throw_error();
241
#ifdef BOOST_NO_EXCEPTIONS
242
    TORRENT_ASSERT(m_type_queried);
243
#endif
244
0
    TORRENT_ASSERT(m_type == int_t);
245
0
    return *reinterpret_cast<const integer_type*>(&data);
246
0
  }
247
248
  entry::string_type& entry::string()
249
0
  {
250
0
    if (m_type == undefined_t) construct(string_t);
251
#ifdef BOOST_NO_EXCEPTIONS
252
    TORRENT_ASSERT(m_type_queried);
253
#endif
254
0
    if (m_type != string_t) throw_error();
255
0
    TORRENT_ASSERT(m_type == string_t);
256
0
    return *reinterpret_cast<string_type*>(&data);
257
0
  }
258
259
  entry::string_type const& entry::string() const
260
0
  {
261
0
    if (m_type != string_t) throw_error();
262
#ifdef BOOST_NO_EXCEPTIONS
263
    TORRENT_ASSERT(m_type_queried);
264
#endif
265
0
    TORRENT_ASSERT(m_type == string_t);
266
0
    return *reinterpret_cast<const string_type*>(&data);
267
0
  }
268
269
  entry::list_type& entry::list()
270
0
  {
271
0
    if (m_type == undefined_t) construct(list_t);
272
#ifdef BOOST_NO_EXCEPTIONS
273
    TORRENT_ASSERT(m_type_queried);
274
#endif
275
0
    if (m_type != list_t) throw_error();
276
0
    TORRENT_ASSERT(m_type == list_t);
277
0
    return *reinterpret_cast<list_type*>(&data);
278
0
  }
279
280
  entry::list_type const& entry::list() const
281
0
  {
282
0
    if (m_type != list_t) throw_error();
283
#ifdef BOOST_NO_EXCEPTIONS
284
    TORRENT_ASSERT(m_type_queried);
285
#endif
286
0
    TORRENT_ASSERT(m_type == list_t);
287
0
    return *reinterpret_cast<const list_type*>(&data);
288
0
  }
289
290
  entry::dictionary_type& entry::dict()
291
0
  {
292
0
    if (m_type == undefined_t) construct(dictionary_t);
293
#ifdef BOOST_NO_EXCEPTIONS
294
    TORRENT_ASSERT(m_type_queried);
295
#endif
296
0
    if (m_type != dictionary_t) throw_error();
297
0
    TORRENT_ASSERT(m_type == dictionary_t);
298
0
    return *reinterpret_cast<dictionary_type*>(&data);
299
0
  }
300
301
  entry::dictionary_type const& entry::dict() const
302
0
  {
303
0
    if (m_type != dictionary_t) throw_error();
304
#ifdef BOOST_NO_EXCEPTIONS
305
    TORRENT_ASSERT(m_type_queried);
306
#endif
307
0
    TORRENT_ASSERT(m_type == dictionary_t);
308
0
    return *reinterpret_cast<const dictionary_type*>(&data);
309
0
  }
310
311
  entry::preformatted_type& entry::preformatted()
312
0
  {
313
0
    if (m_type == undefined_t) construct(preformatted_t);
314
#ifdef BOOST_NO_EXCEPTIONS
315
    TORRENT_ASSERT(m_type_queried);
316
#endif
317
0
    if (m_type != preformatted_t) throw_error();
318
0
    TORRENT_ASSERT(m_type == preformatted_t);
319
0
    return *reinterpret_cast<preformatted_type*>(&data);
320
0
  }
321
322
  entry::preformatted_type const& entry::preformatted() const
323
0
  {
324
0
    if (m_type != preformatted_t) throw_error();
325
#ifdef BOOST_NO_EXCEPTIONS
326
    TORRENT_ASSERT(m_type_queried);
327
#endif
328
0
    TORRENT_ASSERT(m_type == preformatted_t);
329
0
    return *reinterpret_cast<const preformatted_type*>(&data);
330
0
  }
331
332
  entry::entry()
333
0
    : m_type(undefined_t)
334
0
  {
335
0
#if TORRENT_USE_ASSERTS
336
0
    m_type_queried = true;
337
0
#endif
338
0
  }
339
340
  entry::entry(data_type t)
341
0
    : m_type(undefined_t)
342
0
  {
343
0
    construct(t);
344
0
#if TORRENT_USE_ASSERTS
345
0
    m_type_queried = true;
346
0
#endif
347
0
  }
348
349
  entry::entry(const entry& e)
350
0
    : m_type(undefined_t)
351
0
  {
352
0
    copy(e);
353
0
#if TORRENT_USE_ASSERTS
354
0
    m_type_queried = e.m_type_queried;
355
0
#endif
356
0
  }
357
358
  entry::entry(entry&& e) noexcept
359
0
    : m_type(undefined_t)
360
0
  {
361
0
    this->operator=(std::move(e));
362
0
  }
363
364
  entry::entry(bdecode_node const& n)
365
0
    : m_type(undefined_t)
366
0
  {
367
0
    this->operator=(n);
368
0
  }
369
370
  entry::entry(dictionary_type v)
371
0
    : m_type(undefined_t)
372
0
  {
373
0
#if TORRENT_USE_ASSERTS
374
0
    m_type_queried = true;
375
0
#endif
376
0
    new(&data) dictionary_type(std::move(v));
377
0
    m_type = dictionary_t;
378
0
  }
379
380
  entry::entry(span<char const> v)
381
0
    : m_type(undefined_t)
382
0
  {
383
0
#if TORRENT_USE_ASSERTS
384
0
    m_type_queried = true;
385
0
#endif
386
0
    new(&data) string_type(v.data(), std::size_t(v.size()));
387
0
    m_type = string_t;
388
0
  }
389
390
  entry::entry(list_type v)
391
0
    : m_type(undefined_t)
392
0
  {
393
0
#if TORRENT_USE_ASSERTS
394
0
    m_type_queried = true;
395
0
#endif
396
0
    new(&data) list_type(std::move(v));
397
0
    m_type = list_t;
398
0
  }
399
400
  entry::entry(integer_type v)
401
0
    : m_type(undefined_t)
402
0
  {
403
0
#if TORRENT_USE_ASSERTS
404
0
    m_type_queried = true;
405
0
#endif
406
0
    new(&data) integer_type(std::move(v));
407
0
    m_type = int_t;
408
0
  }
409
410
  entry::entry(preformatted_type v)
411
0
    : m_type(undefined_t)
412
0
  {
413
0
#if TORRENT_USE_ASSERTS
414
0
    m_type_queried = true;
415
0
#endif
416
0
    new(&data) preformatted_type(std::move(v));
417
0
    m_type = preformatted_t;
418
0
  }
419
420
  // convert a bdecode_node into an old school entry
421
  entry& entry::operator=(bdecode_node const& e) &
422
0
  {
423
0
    destruct();
424
0
    switch (e.type())
425
0
    {
426
0
      case bdecode_node::string_t:
427
0
        this->string() = e.string_value().to_string();
428
0
        break;
429
0
      case bdecode_node::int_t:
430
0
        this->integer() = e.int_value();
431
0
        break;
432
0
      case bdecode_node::dict_t:
433
0
      {
434
0
        dictionary_type& d = this->dict();
435
0
        for (int i = 0; i < e.dict_size(); ++i)
436
0
        {
437
0
          std::pair<string_view, bdecode_node> elem = e.dict_at(i);
438
0
          d[elem.first.to_string()] = elem.second;
439
0
        }
440
0
        break;
441
0
      }
442
0
      case bdecode_node::list_t:
443
0
      {
444
0
        list_type& l = this->list();
445
0
        for (int i = 0; i < e.list_size(); ++i)
446
0
        {
447
0
          l.emplace_back();
448
0
          l.back() = e.list_at(i);
449
0
        }
450
0
        break;
451
0
      }
452
0
      case bdecode_node::none_t:
453
0
        break;
454
0
    }
455
0
    return *this;
456
0
  }
457
458
  entry& entry::operator=(preformatted_type v) &
459
0
  {
460
0
    destruct();
461
0
    new(&data) preformatted_type(std::move(v));
462
0
    m_type = preformatted_t;
463
0
#if TORRENT_USE_ASSERTS
464
0
    m_type_queried = true;
465
0
#endif
466
0
    return *this;
467
0
  }
468
469
  entry& entry::operator=(dictionary_type v) &
470
0
  {
471
0
    destruct();
472
0
    new(&data) dictionary_type(std::move(v));
473
0
    m_type = dictionary_t;
474
0
#if TORRENT_USE_ASSERTS
475
0
    m_type_queried = true;
476
0
#endif
477
0
    return *this;
478
0
  }
479
480
  entry& entry::operator=(span<char const> v) &
481
0
  {
482
0
    destruct();
483
0
    new(&data) string_type(v.data(), std::size_t(v.size()));
484
0
    m_type = string_t;
485
0
#if TORRENT_USE_ASSERTS
486
0
    m_type_queried = true;
487
0
#endif
488
0
    return *this;
489
0
  }
490
491
  entry& entry::operator=(list_type v) &
492
0
  {
493
0
    destruct();
494
0
    new(&data) list_type(std::move(v));
495
0
    m_type = list_t;
496
0
#if TORRENT_USE_ASSERTS
497
0
    m_type_queried = true;
498
0
#endif
499
0
    return *this;
500
0
  }
501
502
  entry& entry::operator=(integer_type v) &
503
0
  {
504
0
    destruct();
505
0
    new(&data) integer_type(std::move(v));
506
0
    m_type = int_t;
507
0
#if TORRENT_USE_ASSERTS
508
0
    m_type_queried = true;
509
0
#endif
510
0
    return *this;
511
0
  }
512
513
  bool operator==(entry const& lhs, entry const& rhs)
514
0
  {
515
0
    if (lhs.type() != rhs.type()) return false;
516
517
0
    switch (lhs.type())
518
0
    {
519
0
    case entry::int_t:
520
0
      return lhs.integer() == rhs.integer();
521
0
    case entry::string_t:
522
0
      return lhs.string() == rhs.string();
523
0
    case entry::list_t:
524
0
      return lhs.list() == rhs.list();
525
0
    case entry::dictionary_t:
526
0
      return lhs.dict() == rhs.dict();
527
0
    case entry::preformatted_t:
528
0
      return lhs.preformatted() == rhs.preformatted();
529
0
    case entry::undefined_t:
530
0
      return true;
531
0
    }
532
0
    return false;
533
0
  }
534
535
  void entry::construct(data_type t)
536
0
  {
537
0
    switch (t)
538
0
    {
539
0
    case int_t:
540
0
      new (&data) integer_type(0);
541
0
      break;
542
0
    case string_t:
543
0
      new (&data) string_type;
544
0
      break;
545
0
    case list_t:
546
0
      new (&data) list_type;
547
0
      break;
548
0
    case dictionary_t:
549
0
      new (&data) dictionary_type;
550
0
      break;
551
0
    case undefined_t:
552
0
      break;
553
0
    case preformatted_t:
554
0
      new (&data) preformatted_type;
555
0
      break;
556
0
    }
557
0
    m_type = t;
558
0
#if TORRENT_USE_ASSERTS
559
0
    m_type_queried = true;
560
0
#endif
561
0
  }
562
563
  void entry::copy(entry const& e)
564
0
  {
565
0
    switch (e.type())
566
0
    {
567
0
    case int_t:
568
0
      new (&data) integer_type(e.integer());
569
0
      break;
570
0
    case string_t:
571
0
      new (&data) string_type(e.string());
572
0
      break;
573
0
    case list_t:
574
0
      new (&data) list_type(e.list());
575
0
      break;
576
0
    case dictionary_t:
577
0
      new (&data) dictionary_type(e.dict());
578
0
      break;
579
0
    case undefined_t:
580
0
      TORRENT_ASSERT(e.type() == undefined_t);
581
0
      break;
582
0
    case preformatted_t:
583
0
      new (&data) preformatted_type(e.preformatted());
584
0
      break;
585
0
    }
586
0
    m_type = e.type();
587
0
#if TORRENT_USE_ASSERTS
588
0
    m_type_queried = true;
589
0
#endif
590
0
  }
591
592
  void entry::destruct()
593
0
  {
594
0
    switch(m_type)
595
0
    {
596
0
    case int_t:
597
0
      call_destructor(reinterpret_cast<integer_type*>(&data));
598
0
      break;
599
0
    case string_t:
600
0
      call_destructor(reinterpret_cast<string_type*>(&data));
601
0
      break;
602
0
    case list_t:
603
0
      call_destructor(reinterpret_cast<list_type*>(&data));
604
0
      break;
605
0
    case dictionary_t:
606
0
      call_destructor(reinterpret_cast<dictionary_type*>(&data));
607
0
      break;
608
0
    case preformatted_t:
609
0
      call_destructor(reinterpret_cast<preformatted_type*>(&data));
610
0
      break;
611
0
    default:
612
0
      TORRENT_ASSERT(m_type == undefined_t);
613
0
      break;
614
0
    }
615
0
    m_type = undefined_t;
616
0
#if TORRENT_USE_ASSERTS
617
0
    m_type_queried = false;
618
0
#endif
619
0
  }
620
621
  void entry::swap(entry& e)
622
0
  {
623
0
    bool clear_this = false;
624
0
    bool clear_that = false;
625
626
0
    if (m_type == undefined_t && e.m_type == undefined_t)
627
0
      return;
628
629
0
    if (m_type == undefined_t)
630
0
    {
631
0
      construct(data_type(e.m_type));
632
0
      clear_that = true;
633
0
    }
634
635
0
    if (e.m_type == undefined_t)
636
0
    {
637
0
      e.construct(data_type(m_type));
638
0
      clear_this = true;
639
0
    }
640
641
0
    if (m_type == e.m_type)
642
0
    {
643
0
      switch (m_type)
644
0
      {
645
0
      case int_t:
646
0
        std::swap(*reinterpret_cast<integer_type*>(&data)
647
0
          , *reinterpret_cast<integer_type*>(&e.data));
648
0
        break;
649
0
      case string_t:
650
0
        std::swap(*reinterpret_cast<string_type*>(&data)
651
0
          , *reinterpret_cast<string_type*>(&e.data));
652
0
        break;
653
0
      case list_t:
654
0
        std::swap(*reinterpret_cast<list_type*>(&data)
655
0
          , *reinterpret_cast<list_type*>(&e.data));
656
0
        break;
657
0
      case dictionary_t:
658
0
        std::swap(*reinterpret_cast<dictionary_type*>(&data)
659
0
          , *reinterpret_cast<dictionary_type*>(&e.data));
660
0
        break;
661
0
      case preformatted_t:
662
0
        std::swap(*reinterpret_cast<preformatted_type*>(&data)
663
0
          , *reinterpret_cast<preformatted_type*>(&e.data));
664
0
        break;
665
0
      default:
666
0
        break;
667
0
      }
668
669
0
      if (clear_this)
670
0
        destruct();
671
672
0
      if (clear_that)
673
0
        e.destruct();
674
0
    }
675
0
    else
676
0
    {
677
      // currently, only swapping entries of the same type or where one
678
      // of the entries is uninitialized is supported.
679
0
      TORRENT_ASSERT_FAIL();
680
0
    }
681
0
  }
682
683
namespace {
684
  bool is_binary(std::string const& str)
685
0
  {
686
0
    return std::any_of(str.begin(), str.end()
687
0
      , [](char const c) { return !is_print(c); });
688
0
  }
689
690
  std::string print_string(std::string const& str)
691
0
  {
692
0
    if (is_binary(str)) return aux::to_hex(str);
693
0
    else return str;
694
0
  }
695
696
  void add_indent(std::string& out, int const indent)
697
0
  {
698
0
    out.resize(out.size() + size_t(indent), ' ');
699
0
  }
700
701
  void print_list(std::string&, entry const&, int, bool);
702
  void print_dict(std::string&, entry const&, int, bool);
703
704
  void to_string_impl(std::string& out, entry const& e, int const indent
705
    , bool const single_line)
706
0
  {
707
0
    TORRENT_ASSERT(indent >= 0);
708
0
    switch (e.type())
709
0
    {
710
0
    case entry::int_t:
711
0
      out += libtorrent::to_string(e.integer()).data();
712
0
      break;
713
0
    case entry::string_t:
714
0
      out += "'";
715
0
      out += print_string(e.string());
716
0
      out += "'";
717
0
      break;
718
0
    case entry::list_t:
719
0
      print_list(out, e, indent + 1, single_line);
720
0
      break;
721
0
    case entry::dictionary_t:
722
0
      print_dict(out, e, indent + 1, single_line);
723
0
      break;
724
0
    case entry::preformatted_t:
725
0
      out += "<preformatted>";
726
0
      break;
727
0
    case entry::undefined_t:
728
0
      out += "<uninitialized>";
729
0
      break;
730
0
    }
731
0
  }
732
733
  void print_list(std::string& out, entry const& e
734
    , int const indent, bool const single_line)
735
0
  {
736
0
    out += single_line ? "[ " : "[\n";
737
0
    bool first = true;
738
0
    for (auto const& item : e.list())
739
0
    {
740
0
      if (!first) out += single_line ? ", " : ",\n";
741
0
      first = false;
742
0
      if (!single_line) add_indent(out, indent);
743
0
      to_string_impl(out, item, indent, single_line);
744
0
    }
745
0
    out += " ]";
746
0
  }
747
748
  void print_dict(std::string& out, entry const& e
749
    , int const indent, bool const single_line)
750
0
  {
751
0
    out += single_line ? "{ " : "{\n";
752
0
    bool first = true;
753
0
    for (auto const& item : e.dict())
754
0
    {
755
0
      if (!first) out += single_line ? ", " : ",\n";
756
0
      first = false;
757
0
      if (!single_line) add_indent(out, indent);
758
0
      out += "'";
759
0
      out += print_string(item.first);
760
0
      out += "': ";
761
762
0
      to_string_impl(out, item.second, indent+1, single_line);
763
0
    }
764
0
    out += " }";
765
0
  }
766
}
767
768
  std::string entry::to_string(bool const single_line) const
769
0
  {
770
0
    std::string ret;
771
0
    to_string_impl(ret, *this, 0, single_line);
772
0
    return ret;
773
0
  }
774
775
}