librats 1.0.2 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -331
- package/binding.gyp +16 -3
- package/lib/index.d.ts +288 -696
- package/lib/index.js +407 -44
- package/native-src/3rdparty/android/ifaddrs-android.c +1 -0
- package/native-src/3rdparty/android/ifaddrs-android.h +1 -0
- package/native-src/CMakeLists.txt +404 -179
- package/native-src/LICENSE +1 -1
- package/native-src/src/librats/bindings/rats.cpp +762 -0
- package/native-src/src/librats/bindings/rats.h +380 -0
- package/native-src/src/librats/bittorrent/bencode.cpp +437 -0
- package/native-src/src/librats/bittorrent/bencode.h +176 -0
- package/native-src/src/librats/bittorrent/bitfield.cpp +97 -0
- package/native-src/src/librats/bittorrent/bitfield.h +76 -0
- package/native-src/src/librats/bittorrent/byte_io.h +58 -0
- package/native-src/src/librats/bittorrent/choker.cpp +25 -0
- package/native-src/src/librats/bittorrent/choker.h +46 -0
- package/native-src/src/librats/bittorrent/client.cpp +413 -0
- package/native-src/src/librats/bittorrent/client.h +227 -0
- package/native-src/src/librats/bittorrent/disk_io.cpp +209 -0
- package/native-src/src/librats/bittorrent/disk_io.h +150 -0
- package/native-src/src/librats/bittorrent/extensions.cpp +191 -0
- package/native-src/src/librats/bittorrent/extensions.h +94 -0
- package/native-src/src/librats/bittorrent/file_storage.cpp +77 -0
- package/native-src/src/librats/bittorrent/file_storage.h +79 -0
- package/native-src/src/librats/bittorrent/log.h +42 -0
- package/native-src/src/librats/bittorrent/magnet_uri.cpp +98 -0
- package/native-src/src/librats/bittorrent/magnet_uri.h +35 -0
- package/native-src/src/librats/bittorrent/peer_connection.cpp +502 -0
- package/native-src/src/librats/bittorrent/peer_connection.h +194 -0
- package/native-src/src/librats/bittorrent/peer_list.cpp +68 -0
- package/native-src/src/librats/bittorrent/peer_list.h +75 -0
- package/native-src/src/librats/bittorrent/piece_picker.cpp +352 -0
- package/native-src/src/librats/bittorrent/piece_picker.h +201 -0
- package/native-src/src/librats/bittorrent/reactor.cpp +97 -0
- package/native-src/src/librats/bittorrent/reactor.h +89 -0
- package/native-src/src/librats/bittorrent/resume_data.cpp +72 -0
- package/native-src/src/librats/bittorrent/resume_data.h +41 -0
- package/native-src/src/librats/bittorrent/store_buffer.cpp +48 -0
- package/native-src/src/librats/bittorrent/store_buffer.h +47 -0
- package/native-src/src/librats/bittorrent/torrent.cpp +870 -0
- package/native-src/src/librats/bittorrent/torrent.h +260 -0
- package/native-src/src/librats/bittorrent/torrent_creator.cpp +129 -0
- package/native-src/src/librats/bittorrent/torrent_creator.h +58 -0
- package/native-src/src/librats/bittorrent/torrent_info.cpp +314 -0
- package/native-src/src/librats/bittorrent/torrent_info.h +118 -0
- package/native-src/src/librats/bittorrent/tracker.cpp +374 -0
- package/native-src/src/librats/bittorrent/tracker.h +108 -0
- package/native-src/src/librats/bittorrent/types.cpp +206 -0
- package/native-src/src/librats/bittorrent/types.h +86 -0
- package/native-src/src/librats/core/address.cpp +35 -0
- package/native-src/src/librats/core/address.h +78 -0
- package/native-src/src/librats/core/bytes.h +69 -0
- package/native-src/src/librats/core/chained_send_buffer.cpp +172 -0
- package/native-src/src/librats/core/chained_send_buffer.h +183 -0
- package/native-src/src/librats/core/endpoint_parse.cpp +41 -0
- package/native-src/src/librats/core/endpoint_parse.h +31 -0
- package/native-src/src/librats/core/event_bus.h +70 -0
- package/native-src/src/librats/core/host_endpoint.h +56 -0
- package/native-src/src/{io_poller.cpp → librats/core/io_poller.cpp} +520 -65
- package/native-src/src/{io_poller.h → librats/core/io_poller.h} +12 -6
- package/native-src/src/librats/core/ip_address.cpp +120 -0
- package/native-src/src/librats/core/ip_address.h +109 -0
- package/native-src/src/librats/core/mpsc_queue.h +47 -0
- package/native-src/src/librats/core/notifier.h +74 -0
- package/native-src/src/librats/core/receive_buffer.cpp +219 -0
- package/native-src/src/librats/core/receive_buffer.h +171 -0
- package/native-src/src/librats/core/service_registry.h +58 -0
- package/native-src/src/{socket.cpp → librats/core/socket.cpp} +625 -118
- package/native-src/src/librats/core/socket.h +496 -0
- package/native-src/src/librats/core/timer_queue.h +105 -0
- package/native-src/src/librats/core/types.cpp +43 -0
- package/native-src/src/librats/core/types.h +103 -0
- package/native-src/src/librats/core/wakeup_pipe.h +83 -0
- package/native-src/src/{crypto → librats/crypto}/blake2_endian.h +21 -23
- package/native-src/src/{crypto → librats/crypto}/blake2b.c +34 -33
- package/native-src/src/{crypto → librats/crypto}/blake2b.h +7 -6
- package/native-src/src/{crypto → librats/crypto}/blake2s.c +55 -54
- package/native-src/src/{crypto → librats/crypto}/blake2s.h +13 -12
- package/native-src/src/{crypto → librats/crypto}/chacha.c +22 -21
- package/native-src/src/{crypto → librats/crypto}/chacha.h +14 -13
- package/native-src/src/{crypto → librats/crypto}/chachapoly.c +56 -56
- package/native-src/src/{crypto → librats/crypto}/chachapoly.h +24 -17
- package/native-src/src/{crc32.cpp → librats/crypto/crc32.cpp} +1 -1
- package/native-src/src/{crc32.h → librats/crypto/crc32.h} +3 -1
- package/native-src/src/{crypto → librats/crypto}/curve25519.c +6 -4
- package/native-src/src/{crypto → librats/crypto}/curve25519.h +6 -3
- package/native-src/src/librats/crypto/hkdf.c +266 -0
- package/native-src/src/{crypto → librats/crypto}/hkdf.h +19 -19
- package/native-src/src/{noise.cpp → librats/crypto/noise.cpp} +84 -73
- package/native-src/src/{noise.h → librats/crypto/noise.h} +18 -8
- package/native-src/src/{crypto → librats/crypto}/poly1305.c +47 -46
- package/native-src/src/librats/crypto/poly1305.h +37 -0
- package/native-src/src/{sha1.cpp → librats/crypto/sha1.cpp} +33 -1
- package/native-src/src/{sha1.h → librats/crypto/sha1.h} +14 -6
- package/native-src/src/{crypto → librats/crypto}/sha256.c +15 -14
- package/native-src/src/{crypto → librats/crypto}/sha256.h +8 -7
- package/native-src/src/{crypto → librats/crypto}/sha512.c +15 -14
- package/native-src/src/{crypto → librats/crypto}/sha512.h +8 -7
- package/native-src/src/librats/dht/announce.cpp +37 -0
- package/native-src/src/librats/dht/announce.h +41 -0
- package/native-src/src/librats/dht/bep42.cpp +109 -0
- package/native-src/src/librats/dht/bep42.h +48 -0
- package/native-src/src/librats/dht/dht.cpp +501 -0
- package/native-src/src/librats/dht/dht.h +119 -0
- package/native-src/src/librats/dht/dht_runner.cpp +103 -0
- package/native-src/src/librats/dht/dht_runner.h +71 -0
- package/native-src/src/librats/dht/dos_blocker.cpp +42 -0
- package/native-src/src/librats/dht/dos_blocker.h +47 -0
- package/native-src/src/librats/dht/find_peers.cpp +52 -0
- package/native-src/src/librats/dht/find_peers.h +73 -0
- package/native-src/src/librats/dht/id.h +167 -0
- package/native-src/src/{krpc.cpp → librats/dht/krpc.cpp} +32 -81
- package/native-src/src/{krpc.h → librats/dht/krpc.h} +19 -23
- package/native-src/src/librats/dht/log.h +38 -0
- package/native-src/src/librats/dht/node.cpp +473 -0
- package/native-src/src/librats/dht/node.h +164 -0
- package/native-src/src/librats/dht/node_entry.h +81 -0
- package/native-src/src/librats/dht/observer.h +72 -0
- package/native-src/src/librats/dht/persistence.cpp +90 -0
- package/native-src/src/librats/dht/persistence.h +32 -0
- package/native-src/src/librats/dht/routing_table.cpp +559 -0
- package/native-src/src/librats/dht/routing_table.h +185 -0
- package/native-src/src/librats/dht/rpc_manager.cpp +127 -0
- package/native-src/src/librats/dht/rpc_manager.h +77 -0
- package/native-src/src/librats/dht/storage.cpp +92 -0
- package/native-src/src/librats/dht/storage.h +74 -0
- package/native-src/src/librats/dht/transport.h +27 -0
- package/native-src/src/librats/dht/traversal.cpp +326 -0
- package/native-src/src/librats/dht/traversal.h +120 -0
- package/native-src/src/librats/dht/udp_transport.cpp +49 -0
- package/native-src/src/librats/dht/udp_transport.h +51 -0
- package/native-src/src/librats/mdns/log.h +22 -0
- package/native-src/src/{mdns.cpp → librats/mdns/mdns.cpp} +75 -40
- package/native-src/src/{mdns.h → librats/mdns/mdns.h} +9 -8
- package/native-src/src/{natpmp.cpp → librats/nat/natpmp.cpp} +12 -9
- package/native-src/src/{natpmp.h → librats/nat/natpmp.h} +3 -3
- package/native-src/src/{port_mapping.h → librats/nat/port_mapping.h} +3 -2
- package/native-src/src/{stun.cpp → librats/nat/stun.cpp} +4 -4
- package/native-src/src/{stun.h → librats/nat/stun.h} +1 -1
- package/native-src/src/{upnp.cpp → librats/nat/upnp.cpp} +6 -6
- package/native-src/src/{upnp.h → librats/nat/upnp.h} +2 -2
- package/native-src/src/librats/node/circuit_service.h +84 -0
- package/native-src/src/librats/node/config.h +110 -0
- package/native-src/src/librats/node/dial_service.h +54 -0
- package/native-src/src/librats/node/dialer.cpp +264 -0
- package/native-src/src/librats/node/dialer.h +188 -0
- package/native-src/src/librats/node/host_events.h +26 -0
- package/native-src/src/librats/node/identify.cpp +130 -0
- package/native-src/src/librats/node/identify.h +71 -0
- package/native-src/src/librats/node/nat_status.cpp +103 -0
- package/native-src/src/librats/node/nat_status.h +118 -0
- package/native-src/src/librats/node/node.cpp +865 -0
- package/native-src/src/librats/node/node.h +344 -0
- package/native-src/src/librats/node/node_context.h +33 -0
- package/native-src/src/librats/node/peer_network.h +91 -0
- package/native-src/src/librats/peer/peer.h +49 -0
- package/native-src/src/librats/peer/peer_book.cpp +181 -0
- package/native-src/src/librats/peer/peer_book.h +88 -0
- package/native-src/src/librats/peer/peer_id.cpp +72 -0
- package/native-src/src/librats/peer/peer_id.h +62 -0
- package/native-src/src/librats/peer/peer_info.h +37 -0
- package/native-src/src/librats/peer/peer_table.cpp +170 -0
- package/native-src/src/librats/peer/peer_table.h +148 -0
- package/native-src/src/librats/security/handshaker.h +66 -0
- package/native-src/src/librats/security/identity.h +43 -0
- package/native-src/src/librats/security/noise_security.cpp +122 -0
- package/native-src/src/librats/security/noise_security.h +37 -0
- package/native-src/src/librats/security/plaintext_security.h +106 -0
- package/native-src/src/librats/security/session.h +37 -0
- package/native-src/src/{storage.cpp → librats/storage/storage.cpp} +369 -522
- package/native-src/src/{storage.h → librats/storage/storage.h} +135 -299
- package/native-src/src/librats/subsystems/bittorrent.cpp +211 -0
- package/native-src/src/librats/subsystems/bittorrent.h +136 -0
- package/native-src/src/librats/subsystems/dht_discovery.cpp +202 -0
- package/native-src/src/librats/subsystems/dht_discovery.h +123 -0
- package/native-src/src/librats/subsystems/dht_service.h +36 -0
- package/native-src/src/librats/subsystems/file_transfer.cpp +972 -0
- package/native-src/src/librats/subsystems/file_transfer.h +367 -0
- package/native-src/src/librats/subsystems/hole_punch.cpp +605 -0
- package/native-src/src/librats/subsystems/hole_punch.h +290 -0
- package/native-src/src/librats/subsystems/hole_punch_service.h +38 -0
- package/native-src/src/librats/subsystems/mdns_discovery.cpp +66 -0
- package/native-src/src/librats/subsystems/mdns_discovery.h +55 -0
- package/native-src/src/librats/subsystems/message_json.cpp +112 -0
- package/native-src/src/librats/subsystems/message_json.h +88 -0
- package/native-src/src/librats/subsystems/peer_exchange.cpp +241 -0
- package/native-src/src/librats/subsystems/peer_exchange.h +136 -0
- package/native-src/src/librats/subsystems/ping_service.cpp +98 -0
- package/native-src/src/librats/subsystems/ping_service.h +66 -0
- package/native-src/src/librats/subsystems/port_mapping_service.cpp +192 -0
- package/native-src/src/librats/subsystems/port_mapping_service.h +84 -0
- package/native-src/src/librats/subsystems/pubsub.cpp +567 -0
- package/native-src/src/librats/subsystems/pubsub.h +175 -0
- package/native-src/src/librats/subsystems/reconnection.cpp +239 -0
- package/native-src/src/librats/subsystems/reconnection.h +126 -0
- package/native-src/src/librats/subsystems/relay.cpp +1142 -0
- package/native-src/src/librats/subsystems/relay.h +211 -0
- package/native-src/src/librats/subsystems/relay_service.h +46 -0
- package/native-src/src/librats/transport/connection.cpp +343 -0
- package/native-src/src/librats/transport/connection.h +283 -0
- package/native-src/src/librats/transport/link.h +96 -0
- package/native-src/src/librats/transport/reactor.cpp +588 -0
- package/native-src/src/librats/transport/reactor.h +262 -0
- package/native-src/src/librats/transport/reactor_pool.h +81 -0
- package/native-src/src/librats/transport/relay_link.cpp +208 -0
- package/native-src/src/librats/transport/relay_link.h +303 -0
- package/native-src/src/librats/transport/tcp_link.cpp +49 -0
- package/native-src/src/librats/transport/tcp_link.h +43 -0
- package/native-src/src/librats/transport/udp_mux.cpp +617 -0
- package/native-src/src/librats/transport/udp_mux.h +363 -0
- package/native-src/src/librats/transport/udp_packet.cpp +121 -0
- package/native-src/src/librats/transport/udp_packet.h +190 -0
- package/native-src/src/librats/transport/udp_stream.cpp +1194 -0
- package/native-src/src/librats/transport/udp_stream.h +614 -0
- package/native-src/src/librats/util/features.h.in +51 -0
- package/native-src/src/{fs.cpp → librats/util/fs.cpp} +51 -3
- package/native-src/src/librats/util/fs.h +136 -0
- package/native-src/src/librats/util/json.cpp +1002 -0
- package/native-src/src/librats/util/json.h +444 -0
- package/native-src/src/{logger.cpp → librats/util/logger.cpp} +1 -1
- package/native-src/src/{logger.h → librats/util/logger.h} +43 -31
- package/native-src/src/{network_monitor.cpp → librats/util/network_monitor.cpp} +12 -4
- package/native-src/src/{network_monitor.h → librats/util/network_monitor.h} +2 -1
- package/native-src/src/{network_utils.cpp → librats/util/network_utils.cpp} +38 -23
- package/native-src/src/{network_utils.h → librats/util/network_utils.h} +15 -8
- package/native-src/src/{os.cpp → librats/util/os.cpp} +48 -18
- package/native-src/src/librats/util/rats_export.h +69 -0
- package/native-src/src/{version.cpp → librats/util/version.cpp} +2 -2
- package/native-src/src/{version.h.in → librats/util/version.h.in} +1 -1
- package/native-src/src/librats/wire/frame.cpp +76 -0
- package/native-src/src/librats/wire/frame.h +111 -0
- package/native-src/src/librats/wire/message_router.cpp +45 -0
- package/native-src/src/librats/wire/message_router.h +47 -0
- package/package.json +5 -4
- package/scripts/build-librats.js +1 -0
- package/scripts/postinstall.js +3 -3
- package/scripts/prepare-package.js +4 -4
- package/scripts/verify-installation.js +63 -105
- package/src/librats_node.cpp +1067 -1323
- package/native-src/src/bencode.cpp +0 -485
- package/native-src/src/bencode.h +0 -145
- package/native-src/src/bittorrent.cpp +0 -14
- package/native-src/src/bittorrent.h +0 -74
- package/native-src/src/bt_bitfield.cpp +0 -372
- package/native-src/src/bt_bitfield.h +0 -316
- package/native-src/src/bt_choker.cpp +0 -228
- package/native-src/src/bt_choker.h +0 -147
- package/native-src/src/bt_client.cpp +0 -1047
- package/native-src/src/bt_client.h +0 -445
- package/native-src/src/bt_create_torrent.cpp +0 -677
- package/native-src/src/bt_create_torrent.h +0 -473
- package/native-src/src/bt_extension.cpp +0 -469
- package/native-src/src/bt_extension.h +0 -309
- package/native-src/src/bt_file_storage.cpp +0 -261
- package/native-src/src/bt_file_storage.h +0 -298
- package/native-src/src/bt_handshake.cpp +0 -134
- package/native-src/src/bt_handshake.h +0 -157
- package/native-src/src/bt_messages.cpp +0 -364
- package/native-src/src/bt_messages.h +0 -324
- package/native-src/src/bt_network.cpp +0 -1007
- package/native-src/src/bt_network.h +0 -417
- package/native-src/src/bt_peer_connection.cpp +0 -742
- package/native-src/src/bt_peer_connection.h +0 -592
- package/native-src/src/bt_piece_picker.cpp +0 -786
- package/native-src/src/bt_piece_picker.h +0 -473
- package/native-src/src/bt_resume_data.cpp +0 -410
- package/native-src/src/bt_resume_data.h +0 -249
- package/native-src/src/bt_torrent.cpp +0 -2120
- package/native-src/src/bt_torrent.h +0 -641
- package/native-src/src/bt_torrent_info.cpp +0 -659
- package/native-src/src/bt_torrent_info.h +0 -418
- package/native-src/src/bt_types.h +0 -621
- package/native-src/src/chained_send_buffer.cpp +0 -75
- package/native-src/src/chained_send_buffer.h +0 -137
- package/native-src/src/crypto/hkdf.c +0 -266
- package/native-src/src/crypto/poly1305.h +0 -36
- package/native-src/src/dht.cpp +0 -3311
- package/native-src/src/dht.h +0 -717
- package/native-src/src/disk_io.cpp +0 -632
- package/native-src/src/disk_io.h +0 -315
- package/native-src/src/file_transfer.cpp +0 -1415
- package/native-src/src/file_transfer.h +0 -286
- package/native-src/src/fs.h +0 -108
- package/native-src/src/gossipsub.cpp +0 -1139
- package/native-src/src/gossipsub.h +0 -403
- package/native-src/src/ice.cpp +0 -893
- package/native-src/src/ice.h +0 -559
- package/native-src/src/json.hpp +0 -25526
- package/native-src/src/librats.cpp +0 -2378
- package/native-src/src/librats.h +0 -2324
- package/native-src/src/librats_bittorrent.cpp +0 -601
- package/native-src/src/librats_c.cpp +0 -1557
- package/native-src/src/librats_c.h +0 -323
- package/native-src/src/librats_discovery.cpp +0 -402
- package/native-src/src/librats_encryption.cpp +0 -275
- package/native-src/src/librats_file_transfer.cpp +0 -144
- package/native-src/src/librats_gossipsub.cpp +0 -289
- package/native-src/src/librats_ice.cpp +0 -213
- package/native-src/src/librats_log_macros.h +0 -36
- package/native-src/src/librats_logging.cpp +0 -173
- package/native-src/src/librats_mdns.cpp +0 -166
- package/native-src/src/librats_persistence.cpp +0 -796
- package/native-src/src/librats_portmap.cpp +0 -419
- package/native-src/src/librats_reconnection.cpp +0 -218
- package/native-src/src/librats_statistic.cpp +0 -105
- package/native-src/src/librats_storage.cpp +0 -189
- package/native-src/src/rats_export.h +0 -17
- package/native-src/src/receive_buffer.cpp +0 -82
- package/native-src/src/receive_buffer.h +0 -127
- package/native-src/src/socket.h +0 -228
- package/native-src/src/threadmanager.cpp +0 -105
- package/native-src/src/threadmanager.h +0 -53
- package/native-src/src/tracker.cpp +0 -1264
- package/native-src/src/tracker.h +0 -319
- package/native-src/src/turn.cpp +0 -762
- package/native-src/src/turn.h +0 -460
- package/native-src/src/wakeup_pipe.h +0 -60
- /package/native-src/src/{os.h → librats/util/os.h} +0 -0
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
#include "librats/bittorrent/bencode.h"
|
|
2
|
+
|
|
3
|
+
#include <algorithm>
|
|
4
|
+
#include <limits>
|
|
5
|
+
#include <stdexcept>
|
|
6
|
+
|
|
7
|
+
namespace librats {
|
|
8
|
+
|
|
9
|
+
// =============================================================================
|
|
10
|
+
// Container boxing helpers — the only code aware of LIBRATS_BENCODE_BOXED_CONTAINERS
|
|
11
|
+
// =============================================================================
|
|
12
|
+
|
|
13
|
+
void BencodeValue::set_list(BencodeList l) {
|
|
14
|
+
#if LIBRATS_BENCODE_BOXED_CONTAINERS
|
|
15
|
+
value_ = std::make_shared<BencodeList>(std::move(l));
|
|
16
|
+
#else
|
|
17
|
+
value_ = std::move(l);
|
|
18
|
+
#endif
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
void BencodeValue::set_dict(BencodeDict d) {
|
|
22
|
+
#if LIBRATS_BENCODE_BOXED_CONTAINERS
|
|
23
|
+
value_ = std::make_shared<BencodeDict>(std::move(d));
|
|
24
|
+
#else
|
|
25
|
+
value_ = std::move(d);
|
|
26
|
+
#endif
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
BencodeList& BencodeValue::list_ref() {
|
|
30
|
+
#if LIBRATS_BENCODE_BOXED_CONTAINERS
|
|
31
|
+
return *std::get<std::shared_ptr<BencodeList>>(value_);
|
|
32
|
+
#else
|
|
33
|
+
return std::get<BencodeList>(value_);
|
|
34
|
+
#endif
|
|
35
|
+
}
|
|
36
|
+
const BencodeList& BencodeValue::list_ref() const {
|
|
37
|
+
#if LIBRATS_BENCODE_BOXED_CONTAINERS
|
|
38
|
+
return *std::get<std::shared_ptr<BencodeList>>(value_);
|
|
39
|
+
#else
|
|
40
|
+
return std::get<BencodeList>(value_);
|
|
41
|
+
#endif
|
|
42
|
+
}
|
|
43
|
+
BencodeDict& BencodeValue::dict_ref() {
|
|
44
|
+
#if LIBRATS_BENCODE_BOXED_CONTAINERS
|
|
45
|
+
return *std::get<std::shared_ptr<BencodeDict>>(value_);
|
|
46
|
+
#else
|
|
47
|
+
return std::get<BencodeDict>(value_);
|
|
48
|
+
#endif
|
|
49
|
+
}
|
|
50
|
+
const BencodeDict& BencodeValue::dict_ref() const {
|
|
51
|
+
#if LIBRATS_BENCODE_BOXED_CONTAINERS
|
|
52
|
+
return *std::get<std::shared_ptr<BencodeDict>>(value_);
|
|
53
|
+
#else
|
|
54
|
+
return std::get<BencodeDict>(value_);
|
|
55
|
+
#endif
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// =============================================================================
|
|
59
|
+
// Construction / value semantics
|
|
60
|
+
// =============================================================================
|
|
61
|
+
|
|
62
|
+
BencodeValue::BencodeValue() : type_(Type::String), value_(std::string()) {}
|
|
63
|
+
BencodeValue::BencodeValue(std::int64_t v) : type_(Type::Integer), value_(v) {}
|
|
64
|
+
BencodeValue::BencodeValue(std::string v) : type_(Type::String), value_(std::move(v)) {}
|
|
65
|
+
BencodeValue::BencodeValue(const char* v) : type_(Type::String), value_(std::string(v)) {}
|
|
66
|
+
BencodeValue::BencodeValue(BencodeList v) : type_(Type::List) { set_list(std::move(v)); }
|
|
67
|
+
BencodeValue::BencodeValue(BencodeDict v) : type_(Type::Dictionary) { set_dict(std::move(v)); }
|
|
68
|
+
|
|
69
|
+
BencodeValue::BencodeValue(const BencodeValue& other) : type_(other.type_) {
|
|
70
|
+
// Always a deep copy, regardless of whether containers are boxed.
|
|
71
|
+
switch (type_) {
|
|
72
|
+
case Type::Integer: value_ = std::get<std::int64_t>(other.value_); break;
|
|
73
|
+
case Type::String: value_ = std::get<std::string>(other.value_); break;
|
|
74
|
+
case Type::List: set_list(other.list_ref()); break;
|
|
75
|
+
case Type::Dictionary: set_dict(other.dict_ref()); break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
BencodeValue::BencodeValue(BencodeValue&& other) noexcept
|
|
80
|
+
: type_(other.type_), value_(std::move(other.value_)) {}
|
|
81
|
+
|
|
82
|
+
BencodeValue& BencodeValue::operator=(const BencodeValue& other) {
|
|
83
|
+
if (this != &other) {
|
|
84
|
+
BencodeValue tmp(other); // copy-and-swap keeps it exception-safe and simple
|
|
85
|
+
*this = std::move(tmp);
|
|
86
|
+
}
|
|
87
|
+
return *this;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
BencodeValue& BencodeValue::operator=(BencodeValue&& other) noexcept {
|
|
91
|
+
if (this != &other) {
|
|
92
|
+
type_ = other.type_;
|
|
93
|
+
value_ = std::move(other.value_);
|
|
94
|
+
}
|
|
95
|
+
return *this;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
BencodeValue::~BencodeValue() = default;
|
|
99
|
+
|
|
100
|
+
// =============================================================================
|
|
101
|
+
// Typed access
|
|
102
|
+
// =============================================================================
|
|
103
|
+
|
|
104
|
+
std::int64_t BencodeValue::as_integer() const {
|
|
105
|
+
if (type_ != Type::Integer) throw std::runtime_error("BencodeValue is not an integer");
|
|
106
|
+
return std::get<std::int64_t>(value_);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const std::string& BencodeValue::as_string() const {
|
|
110
|
+
if (type_ != Type::String) throw std::runtime_error("BencodeValue is not a string");
|
|
111
|
+
return std::get<std::string>(value_);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const BencodeList& BencodeValue::as_list() const {
|
|
115
|
+
if (type_ != Type::List) throw std::runtime_error("BencodeValue is not a list");
|
|
116
|
+
return list_ref();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
BencodeList& BencodeValue::as_list() {
|
|
120
|
+
if (type_ != Type::List) throw std::runtime_error("BencodeValue is not a list");
|
|
121
|
+
return list_ref();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const BencodeDict& BencodeValue::as_dict() const {
|
|
125
|
+
if (type_ != Type::Dictionary) throw std::runtime_error("BencodeValue is not a dictionary");
|
|
126
|
+
return dict_ref();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// =============================================================================
|
|
130
|
+
// Dictionary lookup / building (sorted-vector invariant)
|
|
131
|
+
// =============================================================================
|
|
132
|
+
|
|
133
|
+
namespace {
|
|
134
|
+
// Compare a (key, value) entry against a bare key — used for binary search.
|
|
135
|
+
struct KeyLess {
|
|
136
|
+
bool operator()(const std::pair<std::string, BencodeValue>& e, std::string_view k) const {
|
|
137
|
+
return e.first < k;
|
|
138
|
+
}
|
|
139
|
+
bool operator()(std::string_view k, const std::pair<std::string, BencodeValue>& e) const {
|
|
140
|
+
return k < e.first;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
} // namespace
|
|
144
|
+
|
|
145
|
+
const BencodeValue* BencodeValue::find(std::string_view key) const noexcept {
|
|
146
|
+
if (type_ != Type::Dictionary) return nullptr;
|
|
147
|
+
const BencodeDict& d = dict_ref();
|
|
148
|
+
auto it = std::lower_bound(d.begin(), d.end(), key, KeyLess{});
|
|
149
|
+
if (it != d.end() && it->first == key) return &it->second;
|
|
150
|
+
return nullptr;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const BencodeValue& BencodeValue::operator[](std::string_view key) const {
|
|
154
|
+
const BencodeValue* v = find(key);
|
|
155
|
+
if (!v) throw std::runtime_error("Key not found in dictionary: " + std::string(key));
|
|
156
|
+
return *v;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
BencodeValue& BencodeValue::operator[](std::string_view key) {
|
|
160
|
+
if (type_ != Type::Dictionary) throw std::runtime_error("BencodeValue is not a dictionary");
|
|
161
|
+
BencodeDict& d = dict_ref();
|
|
162
|
+
auto it = std::lower_bound(d.begin(), d.end(), key, KeyLess{});
|
|
163
|
+
if (it != d.end() && it->first == key) return it->second;
|
|
164
|
+
it = d.emplace(it, std::string(key), BencodeValue()); // insert keeps the vector sorted
|
|
165
|
+
return it->second;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// =============================================================================
|
|
169
|
+
// List access
|
|
170
|
+
// =============================================================================
|
|
171
|
+
|
|
172
|
+
const BencodeValue& BencodeValue::operator[](std::size_t index) const {
|
|
173
|
+
if (type_ != Type::List) throw std::runtime_error("BencodeValue is not a list");
|
|
174
|
+
const BencodeList& l = list_ref();
|
|
175
|
+
if (index >= l.size()) throw std::runtime_error("Index out of bounds");
|
|
176
|
+
return l[index];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
BencodeValue& BencodeValue::operator[](std::size_t index) {
|
|
180
|
+
if (type_ != Type::List) throw std::runtime_error("BencodeValue is not a list");
|
|
181
|
+
BencodeList& l = list_ref();
|
|
182
|
+
if (index >= l.size()) throw std::runtime_error("Index out of bounds");
|
|
183
|
+
return l[index];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
void BencodeValue::push_back(BencodeValue value) {
|
|
187
|
+
if (type_ != Type::List) throw std::runtime_error("BencodeValue is not a list");
|
|
188
|
+
list_ref().push_back(std::move(value));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
std::size_t BencodeValue::size() const {
|
|
192
|
+
switch (type_) {
|
|
193
|
+
case Type::String: return std::get<std::string>(value_).size();
|
|
194
|
+
case Type::List: return list_ref().size();
|
|
195
|
+
case Type::Dictionary: return dict_ref().size();
|
|
196
|
+
default: throw std::runtime_error("Size not applicable to this type");
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// =============================================================================
|
|
201
|
+
// Encoding (dictionaries are already sorted → output is canonical)
|
|
202
|
+
// =============================================================================
|
|
203
|
+
|
|
204
|
+
void BencodeValue::encode_to(std::vector<std::uint8_t>& out) const {
|
|
205
|
+
auto append = [&out](const char* s, std::size_t n) {
|
|
206
|
+
out.insert(out.end(), s, s + n);
|
|
207
|
+
};
|
|
208
|
+
auto append_str = [&](const std::string& s) {
|
|
209
|
+
std::string len = std::to_string(s.size());
|
|
210
|
+
append(len.data(), len.size());
|
|
211
|
+
out.push_back(':');
|
|
212
|
+
out.insert(out.end(), s.begin(), s.end());
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
switch (type_) {
|
|
216
|
+
case Type::Integer: {
|
|
217
|
+
std::string s = "i" + std::to_string(std::get<std::int64_t>(value_)) + "e";
|
|
218
|
+
append(s.data(), s.size());
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
case Type::String:
|
|
222
|
+
append_str(std::get<std::string>(value_));
|
|
223
|
+
break;
|
|
224
|
+
case Type::List:
|
|
225
|
+
out.push_back('l');
|
|
226
|
+
for (const BencodeValue& item : list_ref()) item.encode_to(out);
|
|
227
|
+
out.push_back('e');
|
|
228
|
+
break;
|
|
229
|
+
case Type::Dictionary:
|
|
230
|
+
out.push_back('d');
|
|
231
|
+
for (const auto& [key, val] : dict_ref()) { // already sorted, no duplicates
|
|
232
|
+
append_str(key);
|
|
233
|
+
val.encode_to(out);
|
|
234
|
+
}
|
|
235
|
+
out.push_back('e');
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
std::vector<std::uint8_t> BencodeValue::encode() const {
|
|
241
|
+
std::vector<std::uint8_t> out;
|
|
242
|
+
encode_to(out);
|
|
243
|
+
return out;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
std::string BencodeValue::encode_string() const {
|
|
247
|
+
std::vector<std::uint8_t> out = encode();
|
|
248
|
+
return std::string(out.begin(), out.end());
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// =============================================================================
|
|
252
|
+
// Decoding — hardened recursive-descent parser
|
|
253
|
+
// =============================================================================
|
|
254
|
+
|
|
255
|
+
namespace {
|
|
256
|
+
|
|
257
|
+
constexpr int kMaxDepth = 100; // libtorrent's default nesting limit; stops stack-overflow bombs
|
|
258
|
+
|
|
259
|
+
/// Parses one bencode value out of [p, end). All methods advance `p` past what
|
|
260
|
+
/// they consume and return std::nullopt on any malformed input — never throw,
|
|
261
|
+
/// never read out of bounds, never overflow.
|
|
262
|
+
class Parser {
|
|
263
|
+
public:
|
|
264
|
+
Parser(const std::uint8_t* data, const std::uint8_t* end) : p_(data), end_(end) {}
|
|
265
|
+
|
|
266
|
+
std::optional<BencodeValue> parse(int depth) {
|
|
267
|
+
if (depth > kMaxDepth || p_ >= end_) return std::nullopt;
|
|
268
|
+
const std::uint8_t c = *p_;
|
|
269
|
+
if (c == 'i') return parse_integer();
|
|
270
|
+
if (c == 'l') return parse_list(depth);
|
|
271
|
+
if (c == 'd') return parse_dict(depth);
|
|
272
|
+
if (c >= '0' && c <= '9') return parse_string();
|
|
273
|
+
return std::nullopt;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const std::uint8_t* cursor() const { return p_; }
|
|
277
|
+
|
|
278
|
+
private:
|
|
279
|
+
bool eof() const { return p_ >= end_; }
|
|
280
|
+
|
|
281
|
+
// Strict canonical integer: optional '-', no leading zeros, no "-0", in
|
|
282
|
+
// int64 range. `term` is the terminating byte ('e' for ints, ':' for strings).
|
|
283
|
+
std::optional<std::int64_t> parse_raw_int(std::uint8_t term) {
|
|
284
|
+
bool negative = false;
|
|
285
|
+
if (!eof() && *p_ == '-') { negative = true; ++p_; }
|
|
286
|
+
if (eof() || *p_ < '0' || *p_ > '9') return std::nullopt; // need at least one digit
|
|
287
|
+
|
|
288
|
+
if (*p_ == '0') { // canonical: a lone zero, never "-0" or "0..."
|
|
289
|
+
++p_;
|
|
290
|
+
if (negative) return std::nullopt;
|
|
291
|
+
if (eof() || *p_ != term) return std::nullopt;
|
|
292
|
+
++p_;
|
|
293
|
+
return 0;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
constexpr std::uint64_t kMaxMag = 9223372036854775808ull; // |INT64_MIN|
|
|
297
|
+
std::uint64_t mag = 0;
|
|
298
|
+
while (!eof() && *p_ >= '0' && *p_ <= '9') {
|
|
299
|
+
const std::uint64_t d = std::uint64_t(*p_ - '0');
|
|
300
|
+
if (mag > (kMaxMag - d) / 10) return std::nullopt; // would exceed int64 range
|
|
301
|
+
mag = mag * 10 + d;
|
|
302
|
+
++p_;
|
|
303
|
+
}
|
|
304
|
+
if (eof() || *p_ != term) return std::nullopt;
|
|
305
|
+
++p_;
|
|
306
|
+
|
|
307
|
+
if (negative) {
|
|
308
|
+
if (mag == kMaxMag) return std::numeric_limits<std::int64_t>::min();
|
|
309
|
+
return -std::int64_t(mag);
|
|
310
|
+
}
|
|
311
|
+
if (mag >= kMaxMag) return std::nullopt; // > INT64_MAX
|
|
312
|
+
return std::int64_t(mag);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// String length is bounded by the remaining buffer, which both rejects
|
|
316
|
+
// impossible lengths early and keeps the accumulation far from overflow.
|
|
317
|
+
std::optional<std::string> parse_raw_string() {
|
|
318
|
+
if (eof() || *p_ < '0' || *p_ > '9') return std::nullopt;
|
|
319
|
+
const std::uint64_t cap = std::uint64_t(end_ - p_);
|
|
320
|
+
std::uint64_t len = 0;
|
|
321
|
+
while (!eof() && *p_ >= '0' && *p_ <= '9') {
|
|
322
|
+
len = len * 10 + std::uint64_t(*p_ - '0');
|
|
323
|
+
if (len > cap) return std::nullopt; // cannot possibly fit in the buffer
|
|
324
|
+
++p_;
|
|
325
|
+
}
|
|
326
|
+
if (eof() || *p_ != ':') return std::nullopt;
|
|
327
|
+
++p_;
|
|
328
|
+
if (std::uint64_t(end_ - p_) < len) return std::nullopt;
|
|
329
|
+
std::string s(reinterpret_cast<const char*>(p_), std::size_t(len));
|
|
330
|
+
p_ += len;
|
|
331
|
+
return s;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
std::optional<BencodeValue> parse_integer() {
|
|
335
|
+
++p_; // 'i'
|
|
336
|
+
auto v = parse_raw_int('e');
|
|
337
|
+
if (!v) return std::nullopt;
|
|
338
|
+
return BencodeValue(*v);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
std::optional<BencodeValue> parse_string() {
|
|
342
|
+
auto s = parse_raw_string();
|
|
343
|
+
if (!s) return std::nullopt;
|
|
344
|
+
return BencodeValue(std::move(*s));
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
std::optional<BencodeValue> parse_list(int depth) {
|
|
348
|
+
++p_; // 'l'
|
|
349
|
+
BencodeList items;
|
|
350
|
+
while (!eof() && *p_ != 'e') {
|
|
351
|
+
auto v = parse(depth + 1);
|
|
352
|
+
if (!v) return std::nullopt;
|
|
353
|
+
items.push_back(std::move(*v));
|
|
354
|
+
}
|
|
355
|
+
if (eof()) return std::nullopt; // unterminated
|
|
356
|
+
++p_; // 'e'
|
|
357
|
+
return BencodeValue(std::move(items));
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
std::optional<BencodeValue> parse_dict(int depth) {
|
|
361
|
+
++p_; // 'd'
|
|
362
|
+
BencodeDict entries;
|
|
363
|
+
while (!eof() && *p_ != 'e') {
|
|
364
|
+
auto key = parse_raw_string();
|
|
365
|
+
if (!key) return std::nullopt;
|
|
366
|
+
auto val = parse(depth + 1);
|
|
367
|
+
if (!val) return std::nullopt;
|
|
368
|
+
entries.emplace_back(std::move(*key), std::move(*val));
|
|
369
|
+
}
|
|
370
|
+
if (eof()) return std::nullopt; // unterminated
|
|
371
|
+
++p_; // 'e'
|
|
372
|
+
|
|
373
|
+
// Establish the sorted, duplicate-free invariant. We accept any input
|
|
374
|
+
// order (lenient, for interop) but reject genuinely ambiguous duplicate
|
|
375
|
+
// keys. Stable sort preserves first-seen order among would-be dups so the
|
|
376
|
+
// duplicate check below is deterministic.
|
|
377
|
+
std::stable_sort(entries.begin(), entries.end(),
|
|
378
|
+
[](const auto& a, const auto& b) { return a.first < b.first; });
|
|
379
|
+
for (std::size_t i = 1; i < entries.size(); ++i)
|
|
380
|
+
if (entries[i].first == entries[i - 1].first) return std::nullopt;
|
|
381
|
+
return BencodeValue(std::move(entries));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const std::uint8_t* p_;
|
|
385
|
+
const std::uint8_t* end_;
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
// Decode exactly one value that must span the whole buffer.
|
|
389
|
+
std::optional<BencodeValue> decode_full(const std::uint8_t* data, std::size_t size) noexcept {
|
|
390
|
+
if (data == nullptr || size == 0) return std::nullopt;
|
|
391
|
+
Parser parser(data, data + size);
|
|
392
|
+
std::optional<BencodeValue> value = parser.parse(0);
|
|
393
|
+
if (!value) return std::nullopt;
|
|
394
|
+
if (parser.cursor() != data + size) return std::nullopt; // trailing garbage
|
|
395
|
+
return value;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
BencodeValue decode_or_throw(const std::uint8_t* data, std::size_t size) {
|
|
399
|
+
std::optional<BencodeValue> value = decode_full(data, size);
|
|
400
|
+
if (!value) throw std::runtime_error("Invalid bencode data");
|
|
401
|
+
return std::move(*value);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
} // namespace
|
|
405
|
+
|
|
406
|
+
BencodeValue BencodeDecoder::decode(const std::vector<std::uint8_t>& data) {
|
|
407
|
+
return decode_or_throw(data.data(), data.size());
|
|
408
|
+
}
|
|
409
|
+
BencodeValue BencodeDecoder::decode(const std::string& data) {
|
|
410
|
+
return decode_or_throw(reinterpret_cast<const std::uint8_t*>(data.data()), data.size());
|
|
411
|
+
}
|
|
412
|
+
BencodeValue BencodeDecoder::decode(const std::uint8_t* data, std::size_t size) {
|
|
413
|
+
return decode_or_throw(data, size);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
namespace bencode {
|
|
417
|
+
|
|
418
|
+
BencodeValue decode(const std::vector<std::uint8_t>& data) {
|
|
419
|
+
return decode_or_throw(data.data(), data.size());
|
|
420
|
+
}
|
|
421
|
+
BencodeValue decode(const std::string& data) {
|
|
422
|
+
return decode_or_throw(reinterpret_cast<const std::uint8_t*>(data.data()), data.size());
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
std::optional<BencodeValue> try_decode(const std::uint8_t* data, std::size_t size) noexcept {
|
|
426
|
+
return decode_full(data, size);
|
|
427
|
+
}
|
|
428
|
+
std::optional<BencodeValue> try_decode(std::string_view data) noexcept {
|
|
429
|
+
return decode_full(reinterpret_cast<const std::uint8_t*>(data.data()), data.size());
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
std::vector<std::uint8_t> encode(const BencodeValue& value) { return value.encode(); }
|
|
433
|
+
std::string encode_string(const BencodeValue& value) { return value.encode_string(); }
|
|
434
|
+
|
|
435
|
+
} // namespace bencode
|
|
436
|
+
|
|
437
|
+
} // namespace librats
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file bencode.h
|
|
5
|
+
* @brief Bencode value tree, decoder and encoder (BEP 3).
|
|
6
|
+
*
|
|
7
|
+
* Bencode is the serialization format used by every part of BitTorrent and by
|
|
8
|
+
* the DHT's KRPC protocol. This module is shared between both, so it is built
|
|
9
|
+
* unconditionally (not behind RATS_SEARCH_FEATURES).
|
|
10
|
+
*
|
|
11
|
+
* Design:
|
|
12
|
+
* - `BencodeValue` is an owning value tree: an integer, a (binary-safe) string,
|
|
13
|
+
* a list, or a dictionary. The ergonomic accessors make reading parsed data
|
|
14
|
+
* read like the structure itself: `root["info"]["piece length"].as_integer()`.
|
|
15
|
+
* - A dictionary is stored as a *sorted, duplicate-free* vector of key/value
|
|
16
|
+
* pairs. That keeps lookups O(log n) without a per-dict hash allocation, and
|
|
17
|
+
* makes encoding canonical by construction (bencode requires sorted keys).
|
|
18
|
+
* - Decoding is hardened against hostile input: bounded recursion depth,
|
|
19
|
+
* overflow-safe length/integer parsing, strict canonical integers, and a
|
|
20
|
+
* full-buffer-consumption check. Malformed input never crashes — the
|
|
21
|
+
* non-throwing `try_decode` returns `std::nullopt`, the throwing `decode`
|
|
22
|
+
* throws `std::runtime_error`.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
#include <cstdint>
|
|
26
|
+
#include <optional>
|
|
27
|
+
#include <string>
|
|
28
|
+
#include <string_view>
|
|
29
|
+
#include <utility>
|
|
30
|
+
#include <variant>
|
|
31
|
+
#include <vector>
|
|
32
|
+
|
|
33
|
+
// Some older standard libraries can't place a container of an incomplete type
|
|
34
|
+
// directly inside std::variant (the recursion `BencodeValue` -> container of
|
|
35
|
+
// `BencodeValue`). On those toolchains we box the containers in a shared_ptr.
|
|
36
|
+
// This is fully isolated behind the list_ref()/dict_ref() helpers below, so the
|
|
37
|
+
// rest of the code never sees it. (Deep-copy is implemented explicitly either
|
|
38
|
+
// way, so value semantics are identical on both paths.)
|
|
39
|
+
#ifndef LIBRATS_BENCODE_BOXED_CONTAINERS
|
|
40
|
+
# if defined(__GNUC__) && (__GNUC__ <= 11)
|
|
41
|
+
# define LIBRATS_BENCODE_BOXED_CONTAINERS 1
|
|
42
|
+
# else
|
|
43
|
+
# define LIBRATS_BENCODE_BOXED_CONTAINERS 0
|
|
44
|
+
# endif
|
|
45
|
+
#endif
|
|
46
|
+
|
|
47
|
+
#if LIBRATS_BENCODE_BOXED_CONTAINERS
|
|
48
|
+
#include <memory>
|
|
49
|
+
#endif
|
|
50
|
+
|
|
51
|
+
namespace librats {
|
|
52
|
+
|
|
53
|
+
class BencodeValue;
|
|
54
|
+
|
|
55
|
+
/// A bencode list is a plain vector of values.
|
|
56
|
+
using BencodeList = std::vector<BencodeValue>;
|
|
57
|
+
|
|
58
|
+
/// A bencode dictionary, stored sorted by key with no duplicates. Keys are
|
|
59
|
+
/// byte strings (binary-safe). The sorted invariant is maintained by both the
|
|
60
|
+
/// decoder and the mutable `operator[]`, so iteration and encoding are canonical.
|
|
61
|
+
using BencodeDict = std::vector<std::pair<std::string, BencodeValue>>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @brief One bencode value: integer, string, list or dictionary.
|
|
65
|
+
*
|
|
66
|
+
* Copying is a deep copy (true value semantics). Construct leaves directly
|
|
67
|
+
* (`BencodeValue(42)`, `BencodeValue("x")`) or containers via `create_list()` /
|
|
68
|
+
* `create_dict()` and the mutable accessors.
|
|
69
|
+
*/
|
|
70
|
+
class BencodeValue {
|
|
71
|
+
public:
|
|
72
|
+
enum class Type { Integer, String, List, Dictionary };
|
|
73
|
+
|
|
74
|
+
BencodeValue(); ///< empty string (binary-safe, zero length)
|
|
75
|
+
BencodeValue(std::int64_t value);
|
|
76
|
+
BencodeValue(std::string value);
|
|
77
|
+
BencodeValue(const char* value);
|
|
78
|
+
BencodeValue(BencodeList value);
|
|
79
|
+
BencodeValue(BencodeDict value);
|
|
80
|
+
|
|
81
|
+
BencodeValue(const BencodeValue& other);
|
|
82
|
+
BencodeValue(BencodeValue&& other) noexcept;
|
|
83
|
+
BencodeValue& operator=(const BencodeValue& other);
|
|
84
|
+
BencodeValue& operator=(BencodeValue&& other) noexcept;
|
|
85
|
+
~BencodeValue();
|
|
86
|
+
|
|
87
|
+
// ---- type ----
|
|
88
|
+
Type type() const noexcept { return type_; }
|
|
89
|
+
Type get_type() const noexcept { return type_; } ///< alias
|
|
90
|
+
bool is_integer() const noexcept { return type_ == Type::Integer; }
|
|
91
|
+
bool is_string() const noexcept { return type_ == Type::String; }
|
|
92
|
+
bool is_list() const noexcept { return type_ == Type::List; }
|
|
93
|
+
bool is_dict() const noexcept { return type_ == Type::Dictionary; }
|
|
94
|
+
|
|
95
|
+
// ---- typed access (throw std::runtime_error on a type mismatch) ----
|
|
96
|
+
std::int64_t as_integer() const;
|
|
97
|
+
const std::string& as_string() const;
|
|
98
|
+
const BencodeList& as_list() const;
|
|
99
|
+
const BencodeDict& as_dict() const;
|
|
100
|
+
BencodeList& as_list(); ///< mutable list (lists carry no invariant)
|
|
101
|
+
|
|
102
|
+
// ---- safe, non-throwing helpers ----
|
|
103
|
+
/// Dictionary lookup. Returns nullptr if this is not a dict or the key is
|
|
104
|
+
/// absent. Never throws — the preferred way to read untrusted input.
|
|
105
|
+
const BencodeValue* find(std::string_view key) const noexcept;
|
|
106
|
+
bool has_key(std::string_view key) const noexcept { return find(key) != nullptr; }
|
|
107
|
+
|
|
108
|
+
// ---- dictionary building / reading ----
|
|
109
|
+
/// Const dictionary read; throws std::runtime_error if the key is missing.
|
|
110
|
+
const BencodeValue& operator[](std::string_view key) const;
|
|
111
|
+
/// Mutable dictionary access; inserts a default-constructed value (keeping
|
|
112
|
+
/// the sorted invariant) if the key is absent. Throws if this is not a dict.
|
|
113
|
+
BencodeValue& operator[](std::string_view key);
|
|
114
|
+
|
|
115
|
+
// ---- list building / reading ----
|
|
116
|
+
const BencodeValue& operator[](std::size_t index) const; ///< throws if out of range
|
|
117
|
+
BencodeValue& operator[](std::size_t index); ///< throws if out of range
|
|
118
|
+
void push_back(BencodeValue value); ///< append to a list
|
|
119
|
+
std::size_t size() const; ///< element/byte count for string/list/dict; throws otherwise
|
|
120
|
+
|
|
121
|
+
// ---- encoding ----
|
|
122
|
+
std::vector<std::uint8_t> encode() const;
|
|
123
|
+
std::string encode_string() const;
|
|
124
|
+
void encode_to(std::vector<std::uint8_t>& out) const;
|
|
125
|
+
|
|
126
|
+
// ---- factories (kept for readability at call sites) ----
|
|
127
|
+
static BencodeValue create_integer(std::int64_t v) { return BencodeValue(v); }
|
|
128
|
+
static BencodeValue create_string(std::string v) { return BencodeValue(std::move(v)); }
|
|
129
|
+
static BencodeValue create_list() { return BencodeValue(BencodeList{}); }
|
|
130
|
+
static BencodeValue create_dict() { return BencodeValue(BencodeDict{}); }
|
|
131
|
+
|
|
132
|
+
private:
|
|
133
|
+
Type type_;
|
|
134
|
+
#if LIBRATS_BENCODE_BOXED_CONTAINERS
|
|
135
|
+
std::variant<std::int64_t, std::string,
|
|
136
|
+
std::shared_ptr<BencodeList>, std::shared_ptr<BencodeDict>> value_;
|
|
137
|
+
#else
|
|
138
|
+
std::variant<std::int64_t, std::string, BencodeList, BencodeDict> value_;
|
|
139
|
+
#endif
|
|
140
|
+
|
|
141
|
+
// The only place that knows about container boxing.
|
|
142
|
+
void set_list(BencodeList l);
|
|
143
|
+
void set_dict(BencodeDict d);
|
|
144
|
+
BencodeList& list_ref();
|
|
145
|
+
const BencodeList& list_ref() const;
|
|
146
|
+
BencodeDict& dict_ref();
|
|
147
|
+
const BencodeDict& dict_ref() const;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* @brief Bencode decoder entry points (throwing — kept for existing callers).
|
|
152
|
+
*
|
|
153
|
+
* Each decodes exactly one value and requires it to span the whole buffer.
|
|
154
|
+
* Throws std::runtime_error on any malformed input.
|
|
155
|
+
*/
|
|
156
|
+
class BencodeDecoder {
|
|
157
|
+
public:
|
|
158
|
+
static BencodeValue decode(const std::vector<std::uint8_t>& data);
|
|
159
|
+
static BencodeValue decode(const std::string& data);
|
|
160
|
+
static BencodeValue decode(const std::uint8_t* data, std::size_t size);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
namespace bencode {
|
|
164
|
+
/// Throwing decode (one value, whole buffer). Throws std::runtime_error.
|
|
165
|
+
BencodeValue decode(const std::vector<std::uint8_t>& data);
|
|
166
|
+
BencodeValue decode(const std::string& data);
|
|
167
|
+
|
|
168
|
+
/// Non-throwing decode (one value, whole buffer). nullopt on malformed input.
|
|
169
|
+
std::optional<BencodeValue> try_decode(const std::uint8_t* data, std::size_t size) noexcept;
|
|
170
|
+
std::optional<BencodeValue> try_decode(std::string_view data) noexcept;
|
|
171
|
+
|
|
172
|
+
std::vector<std::uint8_t> encode(const BencodeValue& value);
|
|
173
|
+
std::string encode_string(const BencodeValue& value);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
} // namespace librats
|