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.
Files changed (319) hide show
  1. package/README.md +145 -331
  2. package/binding.gyp +16 -3
  3. package/lib/index.d.ts +288 -696
  4. package/lib/index.js +407 -44
  5. package/native-src/3rdparty/android/ifaddrs-android.c +1 -0
  6. package/native-src/3rdparty/android/ifaddrs-android.h +1 -0
  7. package/native-src/CMakeLists.txt +404 -179
  8. package/native-src/LICENSE +1 -1
  9. package/native-src/src/librats/bindings/rats.cpp +762 -0
  10. package/native-src/src/librats/bindings/rats.h +380 -0
  11. package/native-src/src/librats/bittorrent/bencode.cpp +437 -0
  12. package/native-src/src/librats/bittorrent/bencode.h +176 -0
  13. package/native-src/src/librats/bittorrent/bitfield.cpp +97 -0
  14. package/native-src/src/librats/bittorrent/bitfield.h +76 -0
  15. package/native-src/src/librats/bittorrent/byte_io.h +58 -0
  16. package/native-src/src/librats/bittorrent/choker.cpp +25 -0
  17. package/native-src/src/librats/bittorrent/choker.h +46 -0
  18. package/native-src/src/librats/bittorrent/client.cpp +413 -0
  19. package/native-src/src/librats/bittorrent/client.h +227 -0
  20. package/native-src/src/librats/bittorrent/disk_io.cpp +209 -0
  21. package/native-src/src/librats/bittorrent/disk_io.h +150 -0
  22. package/native-src/src/librats/bittorrent/extensions.cpp +191 -0
  23. package/native-src/src/librats/bittorrent/extensions.h +94 -0
  24. package/native-src/src/librats/bittorrent/file_storage.cpp +77 -0
  25. package/native-src/src/librats/bittorrent/file_storage.h +79 -0
  26. package/native-src/src/librats/bittorrent/log.h +42 -0
  27. package/native-src/src/librats/bittorrent/magnet_uri.cpp +98 -0
  28. package/native-src/src/librats/bittorrent/magnet_uri.h +35 -0
  29. package/native-src/src/librats/bittorrent/peer_connection.cpp +502 -0
  30. package/native-src/src/librats/bittorrent/peer_connection.h +194 -0
  31. package/native-src/src/librats/bittorrent/peer_list.cpp +68 -0
  32. package/native-src/src/librats/bittorrent/peer_list.h +75 -0
  33. package/native-src/src/librats/bittorrent/piece_picker.cpp +352 -0
  34. package/native-src/src/librats/bittorrent/piece_picker.h +201 -0
  35. package/native-src/src/librats/bittorrent/reactor.cpp +97 -0
  36. package/native-src/src/librats/bittorrent/reactor.h +89 -0
  37. package/native-src/src/librats/bittorrent/resume_data.cpp +72 -0
  38. package/native-src/src/librats/bittorrent/resume_data.h +41 -0
  39. package/native-src/src/librats/bittorrent/store_buffer.cpp +48 -0
  40. package/native-src/src/librats/bittorrent/store_buffer.h +47 -0
  41. package/native-src/src/librats/bittorrent/torrent.cpp +870 -0
  42. package/native-src/src/librats/bittorrent/torrent.h +260 -0
  43. package/native-src/src/librats/bittorrent/torrent_creator.cpp +129 -0
  44. package/native-src/src/librats/bittorrent/torrent_creator.h +58 -0
  45. package/native-src/src/librats/bittorrent/torrent_info.cpp +314 -0
  46. package/native-src/src/librats/bittorrent/torrent_info.h +118 -0
  47. package/native-src/src/librats/bittorrent/tracker.cpp +374 -0
  48. package/native-src/src/librats/bittorrent/tracker.h +108 -0
  49. package/native-src/src/librats/bittorrent/types.cpp +206 -0
  50. package/native-src/src/librats/bittorrent/types.h +86 -0
  51. package/native-src/src/librats/core/address.cpp +35 -0
  52. package/native-src/src/librats/core/address.h +78 -0
  53. package/native-src/src/librats/core/bytes.h +69 -0
  54. package/native-src/src/librats/core/chained_send_buffer.cpp +172 -0
  55. package/native-src/src/librats/core/chained_send_buffer.h +183 -0
  56. package/native-src/src/librats/core/endpoint_parse.cpp +41 -0
  57. package/native-src/src/librats/core/endpoint_parse.h +31 -0
  58. package/native-src/src/librats/core/event_bus.h +70 -0
  59. package/native-src/src/librats/core/host_endpoint.h +56 -0
  60. package/native-src/src/{io_poller.cpp → librats/core/io_poller.cpp} +520 -65
  61. package/native-src/src/{io_poller.h → librats/core/io_poller.h} +12 -6
  62. package/native-src/src/librats/core/ip_address.cpp +120 -0
  63. package/native-src/src/librats/core/ip_address.h +109 -0
  64. package/native-src/src/librats/core/mpsc_queue.h +47 -0
  65. package/native-src/src/librats/core/notifier.h +74 -0
  66. package/native-src/src/librats/core/receive_buffer.cpp +219 -0
  67. package/native-src/src/librats/core/receive_buffer.h +171 -0
  68. package/native-src/src/librats/core/service_registry.h +58 -0
  69. package/native-src/src/{socket.cpp → librats/core/socket.cpp} +625 -118
  70. package/native-src/src/librats/core/socket.h +496 -0
  71. package/native-src/src/librats/core/timer_queue.h +105 -0
  72. package/native-src/src/librats/core/types.cpp +43 -0
  73. package/native-src/src/librats/core/types.h +103 -0
  74. package/native-src/src/librats/core/wakeup_pipe.h +83 -0
  75. package/native-src/src/{crypto → librats/crypto}/blake2_endian.h +21 -23
  76. package/native-src/src/{crypto → librats/crypto}/blake2b.c +34 -33
  77. package/native-src/src/{crypto → librats/crypto}/blake2b.h +7 -6
  78. package/native-src/src/{crypto → librats/crypto}/blake2s.c +55 -54
  79. package/native-src/src/{crypto → librats/crypto}/blake2s.h +13 -12
  80. package/native-src/src/{crypto → librats/crypto}/chacha.c +22 -21
  81. package/native-src/src/{crypto → librats/crypto}/chacha.h +14 -13
  82. package/native-src/src/{crypto → librats/crypto}/chachapoly.c +56 -56
  83. package/native-src/src/{crypto → librats/crypto}/chachapoly.h +24 -17
  84. package/native-src/src/{crc32.cpp → librats/crypto/crc32.cpp} +1 -1
  85. package/native-src/src/{crc32.h → librats/crypto/crc32.h} +3 -1
  86. package/native-src/src/{crypto → librats/crypto}/curve25519.c +6 -4
  87. package/native-src/src/{crypto → librats/crypto}/curve25519.h +6 -3
  88. package/native-src/src/librats/crypto/hkdf.c +266 -0
  89. package/native-src/src/{crypto → librats/crypto}/hkdf.h +19 -19
  90. package/native-src/src/{noise.cpp → librats/crypto/noise.cpp} +84 -73
  91. package/native-src/src/{noise.h → librats/crypto/noise.h} +18 -8
  92. package/native-src/src/{crypto → librats/crypto}/poly1305.c +47 -46
  93. package/native-src/src/librats/crypto/poly1305.h +37 -0
  94. package/native-src/src/{sha1.cpp → librats/crypto/sha1.cpp} +33 -1
  95. package/native-src/src/{sha1.h → librats/crypto/sha1.h} +14 -6
  96. package/native-src/src/{crypto → librats/crypto}/sha256.c +15 -14
  97. package/native-src/src/{crypto → librats/crypto}/sha256.h +8 -7
  98. package/native-src/src/{crypto → librats/crypto}/sha512.c +15 -14
  99. package/native-src/src/{crypto → librats/crypto}/sha512.h +8 -7
  100. package/native-src/src/librats/dht/announce.cpp +37 -0
  101. package/native-src/src/librats/dht/announce.h +41 -0
  102. package/native-src/src/librats/dht/bep42.cpp +109 -0
  103. package/native-src/src/librats/dht/bep42.h +48 -0
  104. package/native-src/src/librats/dht/dht.cpp +501 -0
  105. package/native-src/src/librats/dht/dht.h +119 -0
  106. package/native-src/src/librats/dht/dht_runner.cpp +103 -0
  107. package/native-src/src/librats/dht/dht_runner.h +71 -0
  108. package/native-src/src/librats/dht/dos_blocker.cpp +42 -0
  109. package/native-src/src/librats/dht/dos_blocker.h +47 -0
  110. package/native-src/src/librats/dht/find_peers.cpp +52 -0
  111. package/native-src/src/librats/dht/find_peers.h +73 -0
  112. package/native-src/src/librats/dht/id.h +167 -0
  113. package/native-src/src/{krpc.cpp → librats/dht/krpc.cpp} +32 -81
  114. package/native-src/src/{krpc.h → librats/dht/krpc.h} +19 -23
  115. package/native-src/src/librats/dht/log.h +38 -0
  116. package/native-src/src/librats/dht/node.cpp +473 -0
  117. package/native-src/src/librats/dht/node.h +164 -0
  118. package/native-src/src/librats/dht/node_entry.h +81 -0
  119. package/native-src/src/librats/dht/observer.h +72 -0
  120. package/native-src/src/librats/dht/persistence.cpp +90 -0
  121. package/native-src/src/librats/dht/persistence.h +32 -0
  122. package/native-src/src/librats/dht/routing_table.cpp +559 -0
  123. package/native-src/src/librats/dht/routing_table.h +185 -0
  124. package/native-src/src/librats/dht/rpc_manager.cpp +127 -0
  125. package/native-src/src/librats/dht/rpc_manager.h +77 -0
  126. package/native-src/src/librats/dht/storage.cpp +92 -0
  127. package/native-src/src/librats/dht/storage.h +74 -0
  128. package/native-src/src/librats/dht/transport.h +27 -0
  129. package/native-src/src/librats/dht/traversal.cpp +326 -0
  130. package/native-src/src/librats/dht/traversal.h +120 -0
  131. package/native-src/src/librats/dht/udp_transport.cpp +49 -0
  132. package/native-src/src/librats/dht/udp_transport.h +51 -0
  133. package/native-src/src/librats/mdns/log.h +22 -0
  134. package/native-src/src/{mdns.cpp → librats/mdns/mdns.cpp} +75 -40
  135. package/native-src/src/{mdns.h → librats/mdns/mdns.h} +9 -8
  136. package/native-src/src/{natpmp.cpp → librats/nat/natpmp.cpp} +12 -9
  137. package/native-src/src/{natpmp.h → librats/nat/natpmp.h} +3 -3
  138. package/native-src/src/{port_mapping.h → librats/nat/port_mapping.h} +3 -2
  139. package/native-src/src/{stun.cpp → librats/nat/stun.cpp} +4 -4
  140. package/native-src/src/{stun.h → librats/nat/stun.h} +1 -1
  141. package/native-src/src/{upnp.cpp → librats/nat/upnp.cpp} +6 -6
  142. package/native-src/src/{upnp.h → librats/nat/upnp.h} +2 -2
  143. package/native-src/src/librats/node/circuit_service.h +84 -0
  144. package/native-src/src/librats/node/config.h +110 -0
  145. package/native-src/src/librats/node/dial_service.h +54 -0
  146. package/native-src/src/librats/node/dialer.cpp +264 -0
  147. package/native-src/src/librats/node/dialer.h +188 -0
  148. package/native-src/src/librats/node/host_events.h +26 -0
  149. package/native-src/src/librats/node/identify.cpp +130 -0
  150. package/native-src/src/librats/node/identify.h +71 -0
  151. package/native-src/src/librats/node/nat_status.cpp +103 -0
  152. package/native-src/src/librats/node/nat_status.h +118 -0
  153. package/native-src/src/librats/node/node.cpp +865 -0
  154. package/native-src/src/librats/node/node.h +344 -0
  155. package/native-src/src/librats/node/node_context.h +33 -0
  156. package/native-src/src/librats/node/peer_network.h +91 -0
  157. package/native-src/src/librats/peer/peer.h +49 -0
  158. package/native-src/src/librats/peer/peer_book.cpp +181 -0
  159. package/native-src/src/librats/peer/peer_book.h +88 -0
  160. package/native-src/src/librats/peer/peer_id.cpp +72 -0
  161. package/native-src/src/librats/peer/peer_id.h +62 -0
  162. package/native-src/src/librats/peer/peer_info.h +37 -0
  163. package/native-src/src/librats/peer/peer_table.cpp +170 -0
  164. package/native-src/src/librats/peer/peer_table.h +148 -0
  165. package/native-src/src/librats/security/handshaker.h +66 -0
  166. package/native-src/src/librats/security/identity.h +43 -0
  167. package/native-src/src/librats/security/noise_security.cpp +122 -0
  168. package/native-src/src/librats/security/noise_security.h +37 -0
  169. package/native-src/src/librats/security/plaintext_security.h +106 -0
  170. package/native-src/src/librats/security/session.h +37 -0
  171. package/native-src/src/{storage.cpp → librats/storage/storage.cpp} +369 -522
  172. package/native-src/src/{storage.h → librats/storage/storage.h} +135 -299
  173. package/native-src/src/librats/subsystems/bittorrent.cpp +211 -0
  174. package/native-src/src/librats/subsystems/bittorrent.h +136 -0
  175. package/native-src/src/librats/subsystems/dht_discovery.cpp +202 -0
  176. package/native-src/src/librats/subsystems/dht_discovery.h +123 -0
  177. package/native-src/src/librats/subsystems/dht_service.h +36 -0
  178. package/native-src/src/librats/subsystems/file_transfer.cpp +972 -0
  179. package/native-src/src/librats/subsystems/file_transfer.h +367 -0
  180. package/native-src/src/librats/subsystems/hole_punch.cpp +605 -0
  181. package/native-src/src/librats/subsystems/hole_punch.h +290 -0
  182. package/native-src/src/librats/subsystems/hole_punch_service.h +38 -0
  183. package/native-src/src/librats/subsystems/mdns_discovery.cpp +66 -0
  184. package/native-src/src/librats/subsystems/mdns_discovery.h +55 -0
  185. package/native-src/src/librats/subsystems/message_json.cpp +112 -0
  186. package/native-src/src/librats/subsystems/message_json.h +88 -0
  187. package/native-src/src/librats/subsystems/peer_exchange.cpp +241 -0
  188. package/native-src/src/librats/subsystems/peer_exchange.h +136 -0
  189. package/native-src/src/librats/subsystems/ping_service.cpp +98 -0
  190. package/native-src/src/librats/subsystems/ping_service.h +66 -0
  191. package/native-src/src/librats/subsystems/port_mapping_service.cpp +192 -0
  192. package/native-src/src/librats/subsystems/port_mapping_service.h +84 -0
  193. package/native-src/src/librats/subsystems/pubsub.cpp +567 -0
  194. package/native-src/src/librats/subsystems/pubsub.h +175 -0
  195. package/native-src/src/librats/subsystems/reconnection.cpp +239 -0
  196. package/native-src/src/librats/subsystems/reconnection.h +126 -0
  197. package/native-src/src/librats/subsystems/relay.cpp +1142 -0
  198. package/native-src/src/librats/subsystems/relay.h +211 -0
  199. package/native-src/src/librats/subsystems/relay_service.h +46 -0
  200. package/native-src/src/librats/transport/connection.cpp +343 -0
  201. package/native-src/src/librats/transport/connection.h +283 -0
  202. package/native-src/src/librats/transport/link.h +96 -0
  203. package/native-src/src/librats/transport/reactor.cpp +588 -0
  204. package/native-src/src/librats/transport/reactor.h +262 -0
  205. package/native-src/src/librats/transport/reactor_pool.h +81 -0
  206. package/native-src/src/librats/transport/relay_link.cpp +208 -0
  207. package/native-src/src/librats/transport/relay_link.h +303 -0
  208. package/native-src/src/librats/transport/tcp_link.cpp +49 -0
  209. package/native-src/src/librats/transport/tcp_link.h +43 -0
  210. package/native-src/src/librats/transport/udp_mux.cpp +617 -0
  211. package/native-src/src/librats/transport/udp_mux.h +363 -0
  212. package/native-src/src/librats/transport/udp_packet.cpp +121 -0
  213. package/native-src/src/librats/transport/udp_packet.h +190 -0
  214. package/native-src/src/librats/transport/udp_stream.cpp +1194 -0
  215. package/native-src/src/librats/transport/udp_stream.h +614 -0
  216. package/native-src/src/librats/util/features.h.in +51 -0
  217. package/native-src/src/{fs.cpp → librats/util/fs.cpp} +51 -3
  218. package/native-src/src/librats/util/fs.h +136 -0
  219. package/native-src/src/librats/util/json.cpp +1002 -0
  220. package/native-src/src/librats/util/json.h +444 -0
  221. package/native-src/src/{logger.cpp → librats/util/logger.cpp} +1 -1
  222. package/native-src/src/{logger.h → librats/util/logger.h} +43 -31
  223. package/native-src/src/{network_monitor.cpp → librats/util/network_monitor.cpp} +12 -4
  224. package/native-src/src/{network_monitor.h → librats/util/network_monitor.h} +2 -1
  225. package/native-src/src/{network_utils.cpp → librats/util/network_utils.cpp} +38 -23
  226. package/native-src/src/{network_utils.h → librats/util/network_utils.h} +15 -8
  227. package/native-src/src/{os.cpp → librats/util/os.cpp} +48 -18
  228. package/native-src/src/librats/util/rats_export.h +69 -0
  229. package/native-src/src/{version.cpp → librats/util/version.cpp} +2 -2
  230. package/native-src/src/{version.h.in → librats/util/version.h.in} +1 -1
  231. package/native-src/src/librats/wire/frame.cpp +76 -0
  232. package/native-src/src/librats/wire/frame.h +111 -0
  233. package/native-src/src/librats/wire/message_router.cpp +45 -0
  234. package/native-src/src/librats/wire/message_router.h +47 -0
  235. package/package.json +5 -4
  236. package/scripts/build-librats.js +1 -0
  237. package/scripts/postinstall.js +3 -3
  238. package/scripts/prepare-package.js +4 -4
  239. package/scripts/verify-installation.js +63 -105
  240. package/src/librats_node.cpp +1067 -1323
  241. package/native-src/src/bencode.cpp +0 -485
  242. package/native-src/src/bencode.h +0 -145
  243. package/native-src/src/bittorrent.cpp +0 -14
  244. package/native-src/src/bittorrent.h +0 -74
  245. package/native-src/src/bt_bitfield.cpp +0 -372
  246. package/native-src/src/bt_bitfield.h +0 -316
  247. package/native-src/src/bt_choker.cpp +0 -228
  248. package/native-src/src/bt_choker.h +0 -147
  249. package/native-src/src/bt_client.cpp +0 -1047
  250. package/native-src/src/bt_client.h +0 -445
  251. package/native-src/src/bt_create_torrent.cpp +0 -677
  252. package/native-src/src/bt_create_torrent.h +0 -473
  253. package/native-src/src/bt_extension.cpp +0 -469
  254. package/native-src/src/bt_extension.h +0 -309
  255. package/native-src/src/bt_file_storage.cpp +0 -261
  256. package/native-src/src/bt_file_storage.h +0 -298
  257. package/native-src/src/bt_handshake.cpp +0 -134
  258. package/native-src/src/bt_handshake.h +0 -157
  259. package/native-src/src/bt_messages.cpp +0 -364
  260. package/native-src/src/bt_messages.h +0 -324
  261. package/native-src/src/bt_network.cpp +0 -1007
  262. package/native-src/src/bt_network.h +0 -417
  263. package/native-src/src/bt_peer_connection.cpp +0 -742
  264. package/native-src/src/bt_peer_connection.h +0 -592
  265. package/native-src/src/bt_piece_picker.cpp +0 -786
  266. package/native-src/src/bt_piece_picker.h +0 -473
  267. package/native-src/src/bt_resume_data.cpp +0 -410
  268. package/native-src/src/bt_resume_data.h +0 -249
  269. package/native-src/src/bt_torrent.cpp +0 -2120
  270. package/native-src/src/bt_torrent.h +0 -641
  271. package/native-src/src/bt_torrent_info.cpp +0 -659
  272. package/native-src/src/bt_torrent_info.h +0 -418
  273. package/native-src/src/bt_types.h +0 -621
  274. package/native-src/src/chained_send_buffer.cpp +0 -75
  275. package/native-src/src/chained_send_buffer.h +0 -137
  276. package/native-src/src/crypto/hkdf.c +0 -266
  277. package/native-src/src/crypto/poly1305.h +0 -36
  278. package/native-src/src/dht.cpp +0 -3311
  279. package/native-src/src/dht.h +0 -717
  280. package/native-src/src/disk_io.cpp +0 -632
  281. package/native-src/src/disk_io.h +0 -315
  282. package/native-src/src/file_transfer.cpp +0 -1415
  283. package/native-src/src/file_transfer.h +0 -286
  284. package/native-src/src/fs.h +0 -108
  285. package/native-src/src/gossipsub.cpp +0 -1139
  286. package/native-src/src/gossipsub.h +0 -403
  287. package/native-src/src/ice.cpp +0 -893
  288. package/native-src/src/ice.h +0 -559
  289. package/native-src/src/json.hpp +0 -25526
  290. package/native-src/src/librats.cpp +0 -2378
  291. package/native-src/src/librats.h +0 -2324
  292. package/native-src/src/librats_bittorrent.cpp +0 -601
  293. package/native-src/src/librats_c.cpp +0 -1557
  294. package/native-src/src/librats_c.h +0 -323
  295. package/native-src/src/librats_discovery.cpp +0 -402
  296. package/native-src/src/librats_encryption.cpp +0 -275
  297. package/native-src/src/librats_file_transfer.cpp +0 -144
  298. package/native-src/src/librats_gossipsub.cpp +0 -289
  299. package/native-src/src/librats_ice.cpp +0 -213
  300. package/native-src/src/librats_log_macros.h +0 -36
  301. package/native-src/src/librats_logging.cpp +0 -173
  302. package/native-src/src/librats_mdns.cpp +0 -166
  303. package/native-src/src/librats_persistence.cpp +0 -796
  304. package/native-src/src/librats_portmap.cpp +0 -419
  305. package/native-src/src/librats_reconnection.cpp +0 -218
  306. package/native-src/src/librats_statistic.cpp +0 -105
  307. package/native-src/src/librats_storage.cpp +0 -189
  308. package/native-src/src/rats_export.h +0 -17
  309. package/native-src/src/receive_buffer.cpp +0 -82
  310. package/native-src/src/receive_buffer.h +0 -127
  311. package/native-src/src/socket.h +0 -228
  312. package/native-src/src/threadmanager.cpp +0 -105
  313. package/native-src/src/threadmanager.h +0 -53
  314. package/native-src/src/tracker.cpp +0 -1264
  315. package/native-src/src/tracker.h +0 -319
  316. package/native-src/src/turn.cpp +0 -762
  317. package/native-src/src/turn.h +0 -460
  318. package/native-src/src/wakeup_pipe.h +0 -60
  319. /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