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
@@ -21,7 +21,8 @@
21
21
  * }
22
22
  */
23
23
 
24
- #include "socket.h"
24
+ #include "librats/util/rats_export.h"
25
+ #include "librats/core/socket.h"
25
26
 
26
27
  #include <memory>
27
28
  #include <cstdint>
@@ -67,12 +68,14 @@ struct PollResult {
67
68
  * @brief Abstract I/O multiplexer
68
69
  *
69
70
  * Thread-safety:
70
- * - add/modify/remove: Safe to call from any thread (epoll_ctl is thread-safe,
71
- * kqueue changes are atomic, WSAPoll rebuilds on wait).
71
+ * - add/modify/remove: Safe to call from any thread. epoll_ctl is thread-safe on
72
+ * its own; the backends that keep a registration table of their own (kqueue,
73
+ * IOCP, poll) guard it with a mutex that wait() never takes, so a control call
74
+ * cannot stall the event loop.
72
75
  * - wait: Should be called from a single I/O thread.
73
76
  * - add/modify/remove can be called concurrently with wait().
74
77
  */
75
- class IOPoller {
78
+ class RATS_API IOPoller {
76
79
  public:
77
80
  virtual ~IOPoller() = default;
78
81
 
@@ -88,10 +91,13 @@ public:
88
91
 
89
92
  /**
90
93
  * @brief Add a socket to the poll set
91
- *
94
+ *
95
+ * A socket that is already in the set is rejected rather than re-registered:
96
+ * use modify() to change what an existing registration watches for.
97
+ *
92
98
  * @param fd Socket to monitor
93
99
  * @param events Bitmask of PollFlags to watch for
94
- * @return true on success
100
+ * @return true on success, false if fd is already registered or on error
95
101
  */
96
102
  virtual bool add(socket_t fd, uint32_t events) = 0;
97
103
 
@@ -0,0 +1,120 @@
1
+ #include "librats/core/ip_address.h"
2
+ #include "librats/core/socket.h" // brings in the platform sockaddr / inet_ntop / inet_pton
3
+
4
+ #include <cstring>
5
+
6
+ namespace librats {
7
+
8
+ IpAddress IpAddress::from_v4(const std::array<uint8_t, 4>& b) {
9
+ IpAddress a;
10
+ std::memcpy(a.bytes_.data(), b.data(), 4);
11
+ a.family_ = Family::V4;
12
+ return a;
13
+ }
14
+
15
+ IpAddress IpAddress::from_v6(const std::array<uint8_t, 16>& b) {
16
+ IpAddress a;
17
+ std::memcpy(a.bytes_.data(), b.data(), 16);
18
+ a.family_ = Family::V6;
19
+ return a;
20
+ }
21
+
22
+ std::optional<IpAddress> IpAddress::from_bytes(ByteView bytes) {
23
+ IpAddress a;
24
+ if (bytes.size() == 4) {
25
+ std::memcpy(a.bytes_.data(), bytes.data(), 4);
26
+ a.family_ = Family::V4;
27
+ } else if (bytes.size() == 16) {
28
+ std::memcpy(a.bytes_.data(), bytes.data(), 16);
29
+ a.family_ = Family::V6;
30
+ } else {
31
+ return std::nullopt;
32
+ }
33
+ return a;
34
+ }
35
+
36
+ std::optional<IpAddress> IpAddress::parse(std::string_view literal) {
37
+ // inet_pton needs a NUL-terminated string; string_view may not be terminated.
38
+ std::string s(literal);
39
+
40
+ std::array<uint8_t, 4> v4{};
41
+ if (inet_pton(AF_INET, s.c_str(), v4.data()) == 1)
42
+ return from_v4(v4);
43
+
44
+ std::array<uint8_t, 16> v6{};
45
+ if (inet_pton(AF_INET6, s.c_str(), v6.data()) == 1)
46
+ return from_v6(v6);
47
+
48
+ return std::nullopt;
49
+ }
50
+
51
+ std::optional<IpAddress> IpAddress::from_sockaddr(const sockaddr* sa) {
52
+ if (!sa) return std::nullopt;
53
+ if (sa->sa_family == AF_INET) {
54
+ const auto* in = reinterpret_cast<const sockaddr_in*>(sa);
55
+ std::array<uint8_t, 4> b{};
56
+ std::memcpy(b.data(), &in->sin_addr, 4);
57
+ return from_v4(b);
58
+ }
59
+ if (sa->sa_family == AF_INET6) {
60
+ const auto* in6 = reinterpret_cast<const sockaddr_in6*>(sa);
61
+ const auto* raw = reinterpret_cast<const uint8_t*>(&in6->sin6_addr);
62
+ // Unwrap IPv4-mapped IPv6 (::ffff:a.b.c.d) into a native IPv4 so a dual-stack
63
+ // socket's view of an IPv4 peer compares/hashes identically to a v4 one.
64
+ static const uint8_t v4_mapped_prefix[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff};
65
+ if (std::memcmp(raw, v4_mapped_prefix, 12) == 0) {
66
+ std::array<uint8_t, 4> b{};
67
+ std::memcpy(b.data(), raw + 12, 4);
68
+ return from_v4(b);
69
+ }
70
+ std::array<uint8_t, 16> b{};
71
+ std::memcpy(b.data(), raw, 16);
72
+ return from_v6(b);
73
+ }
74
+ return std::nullopt;
75
+ }
76
+
77
+ std::string IpAddress::to_string() const {
78
+ if (family_ == Family::V4) {
79
+ char buf[INET_ADDRSTRLEN] = {0};
80
+ in_addr a{};
81
+ std::memcpy(&a, bytes_.data(), 4);
82
+ if (inet_ntop(AF_INET, &a, buf, sizeof(buf))) return buf;
83
+ } else if (family_ == Family::V6) {
84
+ char buf[INET6_ADDRSTRLEN] = {0};
85
+ in6_addr a{};
86
+ std::memcpy(&a, bytes_.data(), 16);
87
+ if (inet_ntop(AF_INET6, &a, buf, sizeof(buf))) return buf;
88
+ }
89
+ return std::string();
90
+ }
91
+
92
+ size_t IpAddress::to_sockaddr(sockaddr* sa, uint16_t port) const {
93
+ if (family_ == Family::V4) {
94
+ auto* in = reinterpret_cast<sockaddr_in*>(sa);
95
+ std::memset(in, 0, sizeof(*in));
96
+ in->sin_family = AF_INET;
97
+ in->sin_port = htons(port);
98
+ std::memcpy(&in->sin_addr, bytes_.data(), 4);
99
+ return sizeof(sockaddr_in);
100
+ }
101
+ if (family_ == Family::V6) {
102
+ auto* in6 = reinterpret_cast<sockaddr_in6*>(sa);
103
+ std::memset(in6, 0, sizeof(*in6));
104
+ in6->sin6_family = AF_INET6;
105
+ in6->sin6_port = htons(port);
106
+ std::memcpy(&in6->sin6_addr, bytes_.data(), 16);
107
+ return sizeof(sockaddr_in6);
108
+ }
109
+ return 0;
110
+ }
111
+
112
+ size_t IpAddress::hash() const noexcept {
113
+ // FNV-1a over the family tag and all 16 bytes.
114
+ uint64_t h = 1469598103934665603ull;
115
+ h = (h ^ static_cast<uint8_t>(family_)) * 1099511628211ull;
116
+ for (uint8_t b : bytes_) h = (h ^ b) * 1099511628211ull;
117
+ return static_cast<size_t>(h);
118
+ }
119
+
120
+ } // namespace librats
@@ -0,0 +1,109 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file ip_address.h
5
+ * @brief A numeric IP address (IPv4 or IPv6) stored as raw bytes.
6
+ *
7
+ * The building block of a dialable endpoint. Unlike a textual "1.2.3.4" this is
8
+ * a fixed 16-byte value + a family tag — trivially copyable, allocation-free, and
9
+ * cheap to hash/compare (it is just its bytes). Text only appears at the edges:
10
+ * parse() on input, to_string() for logs/serialisation. Raw bytes flow straight
11
+ * through the hot paths (sockaddr conversion, BEP 42, the DHT compact codec).
12
+ *
13
+ * An IpAddress is strictly NUMERIC — it never holds a hostname. An unresolved
14
+ * "host:port" the user typed is a HostEndpoint (core/host_endpoint.h); it becomes
15
+ * one or more IpAddress only after the resolver runs at the network boundary.
16
+ */
17
+
18
+ #include "librats/util/rats_export.h"
19
+ #include "librats/core/bytes.h"
20
+
21
+ #include <array>
22
+ #include <cstddef>
23
+ #include <cstdint>
24
+ #include <functional>
25
+ #include <optional>
26
+ #include <string>
27
+ #include <string_view>
28
+
29
+ struct sockaddr;
30
+
31
+ namespace librats {
32
+
33
+ class RATS_API IpAddress {
34
+ public:
35
+ enum class Family : uint8_t { None, V4, V6 };
36
+
37
+ /// The unspecified address (is_unspecified() == true).
38
+ IpAddress() = default;
39
+
40
+ /// Parse a bare numeric literal — "1.2.3.4" or "2001:db8::1" (no brackets, no
41
+ /// port). nullopt for anything that is not a valid IPv4/IPv6 literal (including
42
+ /// hostnames — those are a HostEndpoint, not an IpAddress).
43
+ static std::optional<IpAddress> parse(std::string_view literal);
44
+
45
+ static IpAddress from_v4(const std::array<uint8_t, 4>& b);
46
+ static IpAddress from_v6(const std::array<uint8_t, 16>& b);
47
+
48
+ /// Build from raw address bytes: exactly 4 (IPv4) or 16 (IPv6). nullopt for any
49
+ /// other length. This is the compact/wire form used by the DHT and PEX.
50
+ static std::optional<IpAddress> from_bytes(ByteView bytes);
51
+
52
+ /// Build from a sockaddr (AF_INET / AF_INET6). nullopt for other families.
53
+ /// An IPv4-mapped IPv6 sockaddr (::ffff:a.b.c.d) is unwrapped to a plain IPv4.
54
+ static std::optional<IpAddress> from_sockaddr(const sockaddr* sa);
55
+
56
+ bool is_v4() const noexcept { return family_ == Family::V4; }
57
+ bool is_v6() const noexcept { return family_ == Family::V6; }
58
+ bool is_unspecified() const noexcept { return family_ == Family::None; }
59
+ Family family() const noexcept { return family_; }
60
+
61
+ /// Not a dialable endpoint: either unspecified, or the all-zero wildcard
62
+ /// (0.0.0.0 / ::) that INADDR_ANY-style "any interface" bindings carry.
63
+ bool is_any() const noexcept {
64
+ if (family_ == Family::None) return true;
65
+ for (size_t i = 0, n = size(); i < n; ++i)
66
+ if (bytes_[i] != 0) return false;
67
+ return true;
68
+ }
69
+
70
+ /// The significant address bytes: 4 for IPv4, 16 for IPv6, empty otherwise.
71
+ /// The view is valid for the lifetime of this IpAddress.
72
+ ByteView bytes() const noexcept { return ByteView(bytes_.data(), size()); }
73
+ size_t size() const noexcept {
74
+ return family_ == Family::V6 ? 16 : (family_ == Family::V4 ? 4 : 0);
75
+ }
76
+
77
+ /// Textual form via inet_ntop ("" for the unspecified address).
78
+ std::string to_string() const;
79
+
80
+ /// Fill *sa (must point to storage at least sizeof(sockaddr_in6)) with this
81
+ /// address and `port`. Returns the sockaddr length written, or 0 if unspecified.
82
+ size_t to_sockaddr(sockaddr* sa, uint16_t port) const;
83
+
84
+ bool operator==(const IpAddress& o) const noexcept {
85
+ return family_ == o.family_ && bytes_ == o.bytes_;
86
+ }
87
+ bool operator!=(const IpAddress& o) const noexcept { return !(*this == o); }
88
+ bool operator<(const IpAddress& o) const noexcept {
89
+ return family_ != o.family_ ? family_ < o.family_ : bytes_ < o.bytes_;
90
+ }
91
+
92
+ /// Hash over (family, all 16 bytes). Unused tail bytes are always zero, so this
93
+ /// is well-defined without masking.
94
+ size_t hash() const noexcept;
95
+
96
+ private:
97
+ // v4 occupies the first 4 bytes (tail kept zero); v6 uses all 16.
98
+ std::array<uint8_t, 16> bytes_{};
99
+ Family family_ = Family::None;
100
+ };
101
+
102
+ } // namespace librats
103
+
104
+ namespace std {
105
+ template <>
106
+ struct hash<librats::IpAddress> {
107
+ size_t operator()(const librats::IpAddress& a) const noexcept { return a.hash(); }
108
+ };
109
+ } // namespace std
@@ -0,0 +1,47 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file mpsc_queue.h
5
+ * @brief Multi-producer / single-consumer task queue used to hand work to a Reactor.
6
+ *
7
+ * Many threads call push(); exactly one (the reactor thread) calls drain().
8
+ * drain() swaps the entire backlog out under a single lock, so the consumer
9
+ * pays one lock per *batch*, not per item — the lock guards the handoff, never
10
+ * the work itself. This is deliberately simple and correct; it can be swapped
11
+ * for an intrusive lock-free MPSC later without touching callers.
12
+ */
13
+
14
+ #include <mutex>
15
+ #include <vector>
16
+
17
+ namespace librats {
18
+
19
+ template <typename T>
20
+ class MpscQueue {
21
+ public:
22
+ /// Enqueue an item. Safe to call from any thread.
23
+ void push(T item) {
24
+ std::lock_guard<std::mutex> lock(mutex_);
25
+ queue_.push_back(std::move(item));
26
+ }
27
+
28
+ /// Move the entire backlog into `out` (cleared first). Single consumer only.
29
+ /// Returns the number of items handed over.
30
+ size_t drain(std::vector<T>& out) {
31
+ out.clear();
32
+ std::lock_guard<std::mutex> lock(mutex_);
33
+ out.swap(queue_);
34
+ return out.size();
35
+ }
36
+
37
+ bool empty() const {
38
+ std::lock_guard<std::mutex> lock(mutex_);
39
+ return queue_.empty();
40
+ }
41
+
42
+ private:
43
+ mutable std::mutex mutex_;
44
+ std::vector<T> queue_;
45
+ };
46
+
47
+ } // namespace librats
@@ -0,0 +1,74 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file notifier.h
5
+ * @brief Reliable cross-platform wakeup for a poller-driven reactor.
6
+ *
7
+ * A reactor blocks in IOPoller::wait(); to make it run a freshly-posted task
8
+ * promptly we need to interrupt that wait from another thread. Notifier is a
9
+ * self-pipe built from a *connected TCP loopback pair*:
10
+ *
11
+ * reader_ ── registered in the poller for PollIn, drained on wake
12
+ * writer_ ── signal() writes one byte, making reader_ readable
13
+ *
14
+ * A connected TCP socket is the happy path for every backend — epoll, kqueue,
15
+ * and crucially IOCP (where a bare UDP socket does not give reliable readiness).
16
+ * If construction fails the notifier degrades to a no-op: fd() is invalid and
17
+ * signal() does nothing, so the reactor simply falls back to its poll timeout.
18
+ */
19
+
20
+ #include "librats/core/socket.h"
21
+
22
+ #include <cstdint>
23
+
24
+ namespace librats {
25
+
26
+ class Notifier {
27
+ public:
28
+ Notifier() {
29
+ socket_t listener = create_tcp_server(0, 1, "127.0.0.1", AddressFamily::IPv4);
30
+ if (!is_valid_socket(listener)) return;
31
+
32
+ const int port = get_bound_port(listener);
33
+ writer_ = create_tcp_client("127.0.0.1", port, /*timeout_ms=*/2000);
34
+ if (is_valid_socket(writer_)) {
35
+ reader_ = accept_client(listener); // accept our own loopback connect
36
+ }
37
+ close_socket(listener);
38
+
39
+ if (is_valid_socket(reader_)) set_socket_nonblocking(reader_);
40
+ if (is_valid_socket(writer_)) set_socket_nonblocking(writer_);
41
+ }
42
+
43
+ ~Notifier() {
44
+ if (is_valid_socket(reader_)) close_socket(reader_);
45
+ if (is_valid_socket(writer_)) close_socket(writer_);
46
+ }
47
+
48
+ Notifier(const Notifier&) = delete;
49
+ Notifier& operator=(const Notifier&) = delete;
50
+
51
+ /// The pollable end; register this for PollIn. INVALID if construction failed.
52
+ socket_t fd() const noexcept { return reader_; }
53
+
54
+ /// Wake the reactor. Coalescing-friendly: many signals drain as one. Any thread.
55
+ void signal() {
56
+ if (!is_valid_socket(writer_)) return;
57
+ const uint8_t byte = 1;
58
+ ::send(writer_, reinterpret_cast<const char*>(&byte), 1, 0);
59
+ }
60
+
61
+ /// Discard pending wake bytes. Called by the reactor when reader_ is readable.
62
+ void drain() {
63
+ uint8_t scratch[64];
64
+ while (::recv(reader_, reinterpret_cast<char*>(scratch), sizeof(scratch), 0) > 0) {
65
+ // discard
66
+ }
67
+ }
68
+
69
+ private:
70
+ socket_t reader_ = RATS_INVALID_SOCKET;
71
+ socket_t writer_ = RATS_INVALID_SOCKET;
72
+ };
73
+
74
+ } // namespace librats
@@ -0,0 +1,219 @@
1
+ #include "librats/core/receive_buffer.h"
2
+
3
+ #include <algorithm>
4
+ #include <cassert>
5
+ #include <cstring>
6
+
7
+ namespace librats {
8
+
9
+ namespace {
10
+
11
+ /// Weight of one sample in the usage average: watermark += (peak - watermark) / 16.
12
+ /// Slow enough that a single quiet moment can't shrink a busy buffer, fast enough
13
+ /// that a connection that saw one big message releases it within a few messages.
14
+ constexpr size_t kUsageAverageShift = 4; // 1/16
15
+
16
+ /// Allocations are rounded up to this, so a chain of 1.5x growth steps lands on
17
+ /// allocator-friendly sizes rather than 17496-byte oddities.
18
+ constexpr size_t kAllocGranularity = 256;
19
+
20
+ constexpr size_t round_up(size_t n, size_t granularity) noexcept {
21
+ return (n + granularity - 1) / granularity * granularity;
22
+ }
23
+
24
+ } // namespace
25
+
26
+ ReceiveBuffer::ReceiveBuffer(size_t initial_capacity) {
27
+ if (initial_capacity > 0) grow_to(round_up(initial_capacity, kAllocGranularity));
28
+ }
29
+
30
+ ReceiveBuffer& ReceiveBuffer::operator=(ReceiveBuffer&& other) noexcept {
31
+ if (this == &other) return *this;
32
+
33
+ // Every cursor moves with the allocation. Zeroing the source is what makes it a
34
+ // valid *empty* buffer rather than one that still claims cap_/end_ bytes it no
35
+ // longer owns — the state the compiler-generated move would have left behind.
36
+ buf_ = std::move(other.buf_);
37
+ cap_ = std::exchange(other.cap_, 0);
38
+ start_ = std::exchange(other.start_, 0);
39
+ end_ = std::exchange(other.end_, 0);
40
+ peak_ = std::exchange(other.peak_, 0);
41
+ watermark_ = std::exchange(other.watermark_, 0);
42
+ received_since_decay_ = std::exchange(other.received_since_decay_, false);
43
+ return *this;
44
+ }
45
+
46
+ // ── Write side ──────────────────────────────────────────────────────────────
47
+
48
+ ByteSpan ReceiveBuffer::prepare(size_t min_bytes) {
49
+ if (cap_ - end_ < min_bytes) make_room(min_bytes);
50
+
51
+ // Fold the *demand* into the peak, not just the bytes that end up arriving. A
52
+ // caller reads with a fixed chunk size (16 KiB here, 64 KiB in the BitTorrent
53
+ // peer), so an allocation too small to serve the next prepare() is not idle
54
+ // memory — no matter how little the last message actually used. Sampling only
55
+ // the live bytes would let the average sink below the read size, shrink the
56
+ // buffer, and have the very next prepare() grow it straight back.
57
+ // (libtorrent samples max(recv_end, packet_size) for the same reason.)
58
+ peak_ = (std::max)(peak_, size() + min_bytes);
59
+
60
+ return ByteSpan(buf_.get() + end_, cap_ - end_);
61
+ }
62
+
63
+ void ReceiveBuffer::commit(size_t bytes) {
64
+ assert(bytes <= cap_ - end_ && "commit() past the span handed out by prepare()");
65
+ end_ += bytes;
66
+ peak_ = (std::max)(peak_, size());
67
+ received_since_decay_ = true; // this tick saw traffic; decay() must leave us alone
68
+ }
69
+
70
+ void ReceiveBuffer::make_room(size_t bytes) {
71
+ const size_t live = size();
72
+
73
+ // Reclaiming the consumed prefix is enough: a memmove of the live bytes beats
74
+ // an allocation. Only reachable when something has been consumed, so it costs
75
+ // at most one memmove per bufferful of traffic.
76
+ if (cap_ - live >= bytes) {
77
+ compact();
78
+ return;
79
+ }
80
+ grow_to(grown_capacity(live + bytes));
81
+ }
82
+
83
+ void ReceiveBuffer::grow_to(size_t new_capacity) {
84
+ reallocate(new_capacity);
85
+ // Restart the usage average at the new size: the buffer just grew for a reason,
86
+ // and judging it oversized on the next drained message would only make it flap
87
+ // between two sizes. Demand has to fall for a while before it shrinks again.
88
+ watermark_ = cap_;
89
+ peak_ = size();
90
+ }
91
+
92
+ size_t ReceiveBuffer::grown_capacity(size_t needed) const noexcept {
93
+ size_t next = cap_ + cap_ / 2; // 1.5x, as libtorrent does
94
+ if (next < kMinCapacity) next = kMinCapacity;
95
+ if (next < needed) next = needed; // a single big message may outrun 1.5x
96
+ return round_up(next, kAllocGranularity);
97
+ }
98
+
99
+ void ReceiveBuffer::reallocate(size_t new_capacity) {
100
+ const size_t live = size();
101
+ assert(new_capacity >= live);
102
+
103
+ // Deliberately uninitialised (`new uint8_t[n]` default-initialises scalars,
104
+ // i.e. does nothing): every byte is about to be overwritten by recv().
105
+ std::unique_ptr<uint8_t[]> fresh(new uint8_t[new_capacity]);
106
+ if (live > 0) std::memcpy(fresh.get(), data(), live);
107
+
108
+ buf_ = std::move(fresh);
109
+ cap_ = new_capacity;
110
+ start_ = 0;
111
+ end_ = live;
112
+ }
113
+
114
+ // ── Read side ───────────────────────────────────────────────────────────────
115
+
116
+ void ReceiveBuffer::consume(size_t bytes) {
117
+ assert(bytes <= size() && "consume() past the live data");
118
+ start_ += bytes;
119
+ if (start_ != end_) return;
120
+
121
+ // Fully drained — the common case. Rewinding both cursors keeps the buffer
122
+ // normalised for free, so the steady state never memmoves.
123
+ start_ = end_ = 0;
124
+ sample_usage();
125
+ maybe_shrink();
126
+ }
127
+
128
+ // ── Maintenance ─────────────────────────────────────────────────────────────
129
+
130
+ void ReceiveBuffer::compact() {
131
+ if (start_ == 0) return;
132
+
133
+ const size_t live = size();
134
+ if (live > 0) std::memmove(buf_.get(), buf_.get() + start_, live);
135
+ start_ = 0;
136
+ end_ = live;
137
+ sample_usage();
138
+ }
139
+
140
+ void ReceiveBuffer::decay() {
141
+ // Nothing to reclaim below the floor.
142
+ if (cap_ <= kMinCapacity) return;
143
+
144
+ // Traffic since the last tick: consume() already sampled it (or a partial message
145
+ // is still streaming in), and this connection is sized for a reason. Aging it as
146
+ // well would shrink it below its read size only for the next prepare() to grow it
147
+ // straight back.
148
+ if (std::exchange(received_since_decay_, false)) return;
149
+
150
+ // Silent for a whole tick. Halving (rather than folding in one more zero sample)
151
+ // is what makes this converge in seconds rather than minutes: maybe_shrink() wants
152
+ // the allocation to be more than twice the watermark, so halving the watermark
153
+ // halves the allocation every couple of ticks until it is back at the floor.
154
+ //
155
+ // This runs even when a partial message is still live (empty() is false). A peer
156
+ // that sends a length prefix — or the first slice of a large frame — and then
157
+ // stalls would otherwise pin the whole eager allocation for the life of the
158
+ // connection, and decay() is the only thing that ever visits an idle one.
159
+ // maybe_shrink() never drops below the live bytes, so the stalled data is kept;
160
+ // only the unused tail is handed back.
161
+ //
162
+ // Unless the live bytes already fill more than half the allocation: the tail worth
163
+ // reclaiming is then smaller than the memcpy of the data we have to keep, and the
164
+ // next byte the peer sends grows us straight back — copying it a second time. A
165
+ // stalled peer could otherwise trickle one byte per few ticks and have us memcpy its
166
+ // half-arrived block up and down forever. Pinning only ever comes from the *eager*
167
+ // reserve (a declared length that never arrived), where the live bytes are a tiny
168
+ // fraction of cap_, so bailing here costs no reclaim that mattered — it merely caps
169
+ // the unreclaimed slack at the size of the data itself, the same 2x any geometric
170
+ // buffer accepts. (Drained buffers reach maybe_shrink() via consume(), where
171
+ // size() == 0, so this never holds them up.)
172
+ if (size() * 2 > cap_) return;
173
+
174
+ watermark_ /= 2;
175
+ maybe_shrink();
176
+ }
177
+
178
+ void ReceiveBuffer::clear() noexcept {
179
+ start_ = end_ = 0;
180
+ peak_ = 0;
181
+ }
182
+
183
+ void ReceiveBuffer::reset() noexcept {
184
+ buf_.reset();
185
+ cap_ = start_ = end_ = peak_ = watermark_ = 0;
186
+ }
187
+
188
+ void ReceiveBuffer::sample_usage() noexcept {
189
+ // Exponential average; the +(N-1) rounds up so the average still tracks a
190
+ // rising peak instead of stalling below it on integer division.
191
+ constexpr size_t n = size_t{1} << kUsageAverageShift;
192
+ watermark_ = (watermark_ * (n - 1) + peak_ + (n - 1)) / n;
193
+ peak_ = size();
194
+ }
195
+
196
+ void ReceiveBuffer::maybe_shrink() {
197
+ // Only when the allocation is worth reclaiming *and* recent demand has clearly
198
+ // moved on — otherwise a buffer merely sitting between two messages would free
199
+ // and re-allocate itself on every single message.
200
+ if (cap_ <= kMinCapacity || cap_ / 2 <= watermark_) return;
201
+
202
+ // Fall back to what recent demand actually says, not to a padded version of it:
203
+ // the trigger above guarantees that halves the allocation at least, so a buffer
204
+ // that ballooned for one huge message is back to normal in a handful of steps
205
+ // (at most log2(cap) reallocations), not by creeping down a few percent at a
206
+ // time. Growth is geometric and cheap if demand returns.
207
+ //
208
+ // Never below the bytes still live: a partial message caught mid-stream keeps its
209
+ // storage — reallocate() moves those bytes to the front — so a stalled peer's data
210
+ // is preserved while its unused tail is reclaimed. On a drained buffer size() is 0,
211
+ // so this is exactly the plain watermark target.
212
+ // The watermark is deliberately NOT reset here (unlike after a grow): it is the
213
+ // decaying memory of demand that has to keep falling to shrink us further.
214
+ const size_t floor = (std::max)(kMinCapacity, size());
215
+ const size_t target = round_up((std::max)(floor, watermark_), kAllocGranularity);
216
+ if (target < cap_) reallocate(target);
217
+ }
218
+
219
+ } // namespace librats