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
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
#include "storage.h"
|
|
2
|
-
#include "
|
|
3
|
-
#include "librats.h"
|
|
4
|
-
#include "fs.h"
|
|
5
|
-
#include "logger.h"
|
|
1
|
+
#include "librats/storage/storage.h"
|
|
2
|
+
#include "librats/node/node_context.h"
|
|
3
|
+
#include "librats/crypto/crc32.h"
|
|
4
|
+
#include "librats/util/fs.h"
|
|
5
|
+
#include "librats/util/logger.h"
|
|
6
6
|
#include <algorithm>
|
|
7
7
|
#include <cstring>
|
|
8
8
|
#include <cstdlib>
|
|
9
|
-
#include <
|
|
10
|
-
#include <sstream>
|
|
11
|
-
#include <iomanip>
|
|
9
|
+
#include <cstdio>
|
|
12
10
|
|
|
13
11
|
// Define logging module for this file
|
|
14
12
|
#define LOG_STORAGE_INFO(message) LOG_INFO("storage", message)
|
|
@@ -18,6 +16,14 @@
|
|
|
18
16
|
|
|
19
17
|
namespace librats {
|
|
20
18
|
|
|
19
|
+
namespace {
|
|
20
|
+
|
|
21
|
+
void put_u32(std::vector<uint8_t>& b, uint32_t v) {
|
|
22
|
+
for (int i = 3; i >= 0; --i) b.push_back(static_cast<uint8_t>((v >> (i * 8)) & 0xFF));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
} // namespace
|
|
26
|
+
|
|
21
27
|
//=============================================================================
|
|
22
28
|
// StorageEntry Implementation
|
|
23
29
|
//=============================================================================
|
|
@@ -25,27 +31,27 @@ namespace librats {
|
|
|
25
31
|
void StorageEntry::calculate_checksum() {
|
|
26
32
|
// Calculate CRC32 over key + type + data + timestamp + peer_id
|
|
27
33
|
std::vector<uint8_t> buffer;
|
|
28
|
-
|
|
34
|
+
|
|
29
35
|
// Add key
|
|
30
36
|
buffer.insert(buffer.end(), key.begin(), key.end());
|
|
31
|
-
|
|
37
|
+
|
|
32
38
|
// Add type
|
|
33
39
|
buffer.push_back(static_cast<uint8_t>(type));
|
|
34
|
-
|
|
40
|
+
|
|
35
41
|
// Add data
|
|
36
42
|
buffer.insert(buffer.end(), data.begin(), data.end());
|
|
37
|
-
|
|
43
|
+
|
|
38
44
|
// Add timestamp (8 bytes, big endian)
|
|
39
45
|
for (int i = 7; i >= 0; i--) {
|
|
40
46
|
buffer.push_back(static_cast<uint8_t>((timestamp_ms >> (i * 8)) & 0xFF));
|
|
41
47
|
}
|
|
42
|
-
|
|
48
|
+
|
|
43
49
|
// Add peer_id
|
|
44
50
|
buffer.insert(buffer.end(), origin_peer_id.begin(), origin_peer_id.end());
|
|
45
|
-
|
|
51
|
+
|
|
46
52
|
// Add deleted flag
|
|
47
53
|
buffer.push_back(deleted ? 1 : 0);
|
|
48
|
-
|
|
54
|
+
|
|
49
55
|
checksum = storage_calculate_crc32(buffer.data(), buffer.size());
|
|
50
56
|
}
|
|
51
57
|
|
|
@@ -57,7 +63,7 @@ bool StorageEntry::verify_checksum() const {
|
|
|
57
63
|
|
|
58
64
|
std::vector<uint8_t> StorageEntry::serialize() const {
|
|
59
65
|
std::vector<uint8_t> buffer;
|
|
60
|
-
|
|
66
|
+
|
|
61
67
|
// Format:
|
|
62
68
|
// [4 bytes] total_length (excluding this field)
|
|
63
69
|
// [4 bytes] key_length
|
|
@@ -70,90 +76,90 @@ std::vector<uint8_t> StorageEntry::serialize() const {
|
|
|
70
76
|
// [4 bytes] data_length
|
|
71
77
|
// [data_length bytes] data
|
|
72
78
|
// [4 bytes] checksum
|
|
73
|
-
|
|
79
|
+
|
|
74
80
|
// Calculate total size first
|
|
75
81
|
uint32_t key_len = static_cast<uint32_t>(key.size());
|
|
76
82
|
uint32_t peer_id_len = static_cast<uint32_t>(origin_peer_id.size());
|
|
77
83
|
uint32_t data_len = static_cast<uint32_t>(data.size());
|
|
78
84
|
uint32_t total_len = 4 + key_len + 1 + 1 + 8 + 4 + peer_id_len + 4 + data_len + 4;
|
|
79
|
-
|
|
85
|
+
|
|
80
86
|
buffer.reserve(4 + total_len);
|
|
81
|
-
|
|
87
|
+
|
|
82
88
|
// Total length (big endian)
|
|
83
89
|
buffer.push_back((total_len >> 24) & 0xFF);
|
|
84
90
|
buffer.push_back((total_len >> 16) & 0xFF);
|
|
85
91
|
buffer.push_back((total_len >> 8) & 0xFF);
|
|
86
92
|
buffer.push_back(total_len & 0xFF);
|
|
87
|
-
|
|
93
|
+
|
|
88
94
|
// Key length (big endian)
|
|
89
95
|
buffer.push_back((key_len >> 24) & 0xFF);
|
|
90
96
|
buffer.push_back((key_len >> 16) & 0xFF);
|
|
91
97
|
buffer.push_back((key_len >> 8) & 0xFF);
|
|
92
98
|
buffer.push_back(key_len & 0xFF);
|
|
93
|
-
|
|
99
|
+
|
|
94
100
|
// Key
|
|
95
101
|
buffer.insert(buffer.end(), key.begin(), key.end());
|
|
96
|
-
|
|
102
|
+
|
|
97
103
|
// Type
|
|
98
104
|
buffer.push_back(static_cast<uint8_t>(type));
|
|
99
|
-
|
|
105
|
+
|
|
100
106
|
// Deleted flag
|
|
101
107
|
buffer.push_back(deleted ? 1 : 0);
|
|
102
|
-
|
|
108
|
+
|
|
103
109
|
// Timestamp (big endian)
|
|
104
110
|
for (int i = 7; i >= 0; i--) {
|
|
105
111
|
buffer.push_back(static_cast<uint8_t>((timestamp_ms >> (i * 8)) & 0xFF));
|
|
106
112
|
}
|
|
107
|
-
|
|
113
|
+
|
|
108
114
|
// Peer ID length (big endian)
|
|
109
115
|
buffer.push_back((peer_id_len >> 24) & 0xFF);
|
|
110
116
|
buffer.push_back((peer_id_len >> 16) & 0xFF);
|
|
111
117
|
buffer.push_back((peer_id_len >> 8) & 0xFF);
|
|
112
118
|
buffer.push_back(peer_id_len & 0xFF);
|
|
113
|
-
|
|
119
|
+
|
|
114
120
|
// Peer ID
|
|
115
121
|
buffer.insert(buffer.end(), origin_peer_id.begin(), origin_peer_id.end());
|
|
116
|
-
|
|
122
|
+
|
|
117
123
|
// Data length (big endian)
|
|
118
124
|
buffer.push_back((data_len >> 24) & 0xFF);
|
|
119
125
|
buffer.push_back((data_len >> 16) & 0xFF);
|
|
120
126
|
buffer.push_back((data_len >> 8) & 0xFF);
|
|
121
127
|
buffer.push_back(data_len & 0xFF);
|
|
122
|
-
|
|
128
|
+
|
|
123
129
|
// Data
|
|
124
130
|
buffer.insert(buffer.end(), data.begin(), data.end());
|
|
125
|
-
|
|
131
|
+
|
|
126
132
|
// Checksum (big endian)
|
|
127
133
|
buffer.push_back((checksum >> 24) & 0xFF);
|
|
128
134
|
buffer.push_back((checksum >> 16) & 0xFF);
|
|
129
135
|
buffer.push_back((checksum >> 8) & 0xFF);
|
|
130
136
|
buffer.push_back(checksum & 0xFF);
|
|
131
|
-
|
|
137
|
+
|
|
132
138
|
return buffer;
|
|
133
139
|
}
|
|
134
140
|
|
|
135
|
-
bool StorageEntry::deserialize(const std::vector<uint8_t>& buffer, size_t offset,
|
|
141
|
+
bool StorageEntry::deserialize(const std::vector<uint8_t>& buffer, size_t offset,
|
|
136
142
|
StorageEntry& entry, size_t& bytes_read) {
|
|
137
143
|
bytes_read = 0;
|
|
138
|
-
|
|
144
|
+
|
|
139
145
|
// Minimum size check (4 bytes for total_length)
|
|
140
146
|
if (offset + 4 > buffer.size()) {
|
|
141
147
|
return false;
|
|
142
148
|
}
|
|
143
|
-
|
|
149
|
+
|
|
144
150
|
// Read total length
|
|
145
151
|
uint32_t total_len = (static_cast<uint32_t>(buffer[offset]) << 24) |
|
|
146
152
|
(static_cast<uint32_t>(buffer[offset + 1]) << 16) |
|
|
147
153
|
(static_cast<uint32_t>(buffer[offset + 2]) << 8) |
|
|
148
154
|
static_cast<uint32_t>(buffer[offset + 3]);
|
|
149
|
-
|
|
155
|
+
|
|
150
156
|
// Check if we have enough data
|
|
151
157
|
if (offset + 4 + total_len > buffer.size()) {
|
|
152
158
|
return false;
|
|
153
159
|
}
|
|
154
|
-
|
|
160
|
+
|
|
155
161
|
size_t pos = offset + 4;
|
|
156
|
-
|
|
162
|
+
|
|
157
163
|
// Read key length
|
|
158
164
|
if (pos + 4 > buffer.size()) return false;
|
|
159
165
|
uint32_t key_len = (static_cast<uint32_t>(buffer[pos]) << 24) |
|
|
@@ -161,22 +167,22 @@ bool StorageEntry::deserialize(const std::vector<uint8_t>& buffer, size_t offset
|
|
|
161
167
|
(static_cast<uint32_t>(buffer[pos + 2]) << 8) |
|
|
162
168
|
static_cast<uint32_t>(buffer[pos + 3]);
|
|
163
169
|
pos += 4;
|
|
164
|
-
|
|
170
|
+
|
|
165
171
|
// Read key
|
|
166
172
|
if (pos + key_len > buffer.size()) return false;
|
|
167
173
|
entry.key = std::string(buffer.begin() + pos, buffer.begin() + pos + key_len);
|
|
168
174
|
pos += key_len;
|
|
169
|
-
|
|
175
|
+
|
|
170
176
|
// Read type
|
|
171
177
|
if (pos + 1 > buffer.size()) return false;
|
|
172
178
|
entry.type = static_cast<StorageValueType>(buffer[pos]);
|
|
173
179
|
pos += 1;
|
|
174
|
-
|
|
180
|
+
|
|
175
181
|
// Read deleted flag
|
|
176
182
|
if (pos + 1 > buffer.size()) return false;
|
|
177
183
|
entry.deleted = buffer[pos] != 0;
|
|
178
184
|
pos += 1;
|
|
179
|
-
|
|
185
|
+
|
|
180
186
|
// Read timestamp
|
|
181
187
|
if (pos + 8 > buffer.size()) return false;
|
|
182
188
|
entry.timestamp_ms = 0;
|
|
@@ -184,7 +190,7 @@ bool StorageEntry::deserialize(const std::vector<uint8_t>& buffer, size_t offset
|
|
|
184
190
|
entry.timestamp_ms = (entry.timestamp_ms << 8) | buffer[pos + i];
|
|
185
191
|
}
|
|
186
192
|
pos += 8;
|
|
187
|
-
|
|
193
|
+
|
|
188
194
|
// Read peer ID length
|
|
189
195
|
if (pos + 4 > buffer.size()) return false;
|
|
190
196
|
uint32_t peer_id_len = (static_cast<uint32_t>(buffer[pos]) << 24) |
|
|
@@ -192,12 +198,12 @@ bool StorageEntry::deserialize(const std::vector<uint8_t>& buffer, size_t offset
|
|
|
192
198
|
(static_cast<uint32_t>(buffer[pos + 2]) << 8) |
|
|
193
199
|
static_cast<uint32_t>(buffer[pos + 3]);
|
|
194
200
|
pos += 4;
|
|
195
|
-
|
|
201
|
+
|
|
196
202
|
// Read peer ID
|
|
197
203
|
if (pos + peer_id_len > buffer.size()) return false;
|
|
198
204
|
entry.origin_peer_id = std::string(buffer.begin() + pos, buffer.begin() + pos + peer_id_len);
|
|
199
205
|
pos += peer_id_len;
|
|
200
|
-
|
|
206
|
+
|
|
201
207
|
// Read data length
|
|
202
208
|
if (pos + 4 > buffer.size()) return false;
|
|
203
209
|
uint32_t data_len = (static_cast<uint32_t>(buffer[pos]) << 24) |
|
|
@@ -205,12 +211,12 @@ bool StorageEntry::deserialize(const std::vector<uint8_t>& buffer, size_t offset
|
|
|
205
211
|
(static_cast<uint32_t>(buffer[pos + 2]) << 8) |
|
|
206
212
|
static_cast<uint32_t>(buffer[pos + 3]);
|
|
207
213
|
pos += 4;
|
|
208
|
-
|
|
214
|
+
|
|
209
215
|
// Read data
|
|
210
216
|
if (pos + data_len > buffer.size()) return false;
|
|
211
217
|
entry.data = std::vector<uint8_t>(buffer.begin() + pos, buffer.begin() + pos + data_len);
|
|
212
218
|
pos += data_len;
|
|
213
|
-
|
|
219
|
+
|
|
214
220
|
// Read checksum
|
|
215
221
|
if (pos + 4 > buffer.size()) return false;
|
|
216
222
|
entry.checksum = (static_cast<uint32_t>(buffer[pos]) << 24) |
|
|
@@ -218,7 +224,7 @@ bool StorageEntry::deserialize(const std::vector<uint8_t>& buffer, size_t offset
|
|
|
218
224
|
(static_cast<uint32_t>(buffer[pos + 2]) << 8) |
|
|
219
225
|
static_cast<uint32_t>(buffer[pos + 3]);
|
|
220
226
|
pos += 4;
|
|
221
|
-
|
|
227
|
+
|
|
222
228
|
bytes_read = pos - offset;
|
|
223
229
|
return true;
|
|
224
230
|
}
|
|
@@ -228,7 +234,7 @@ bool StorageEntry::wins_over(const StorageEntry& other) const {
|
|
|
228
234
|
if (timestamp_ms != other.timestamp_ms) {
|
|
229
235
|
return timestamp_ms > other.timestamp_ms;
|
|
230
236
|
}
|
|
231
|
-
|
|
237
|
+
|
|
232
238
|
// Tie-breaker: lexicographic comparison of peer IDs
|
|
233
239
|
return origin_peer_id > other.origin_peer_id;
|
|
234
240
|
}
|
|
@@ -261,18 +267,17 @@ StorageValueType string_to_storage_value_type(const std::string& str) {
|
|
|
261
267
|
// StorageManager Implementation
|
|
262
268
|
//=============================================================================
|
|
263
269
|
|
|
264
|
-
StorageManager::StorageManager(
|
|
265
|
-
:
|
|
266
|
-
config_(config),
|
|
270
|
+
StorageManager::StorageManager(const StorageConfig& config)
|
|
271
|
+
: config_(config),
|
|
267
272
|
sync_status_(StorageSyncStatus::NOT_STARTED),
|
|
268
273
|
initial_sync_complete_(false),
|
|
269
274
|
running_(true),
|
|
270
275
|
dirty_(false) {
|
|
271
|
-
|
|
276
|
+
|
|
272
277
|
// Initialize statistics
|
|
273
278
|
stats_ = StorageStatistics();
|
|
274
279
|
stats_.sync_status = StorageSyncStatus::NOT_STARTED;
|
|
275
|
-
|
|
280
|
+
|
|
276
281
|
initialize();
|
|
277
282
|
}
|
|
278
283
|
|
|
@@ -281,81 +286,57 @@ StorageManager::~StorageManager() {
|
|
|
281
286
|
}
|
|
282
287
|
|
|
283
288
|
void StorageManager::initialize() {
|
|
284
|
-
// Ensure data directory exists
|
|
289
|
+
// Ensure data directory exists and load any existing data from disk.
|
|
285
290
|
if (config_.persist_to_disk) {
|
|
286
291
|
create_directories(config_.data_directory.c_str());
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// Load existing data from disk
|
|
290
|
-
if (config_.persist_to_disk) {
|
|
291
292
|
load();
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// Subscribe to GossipSub topic for real-time updates
|
|
295
|
-
if (config_.enable_sync && client_.is_gossipsub_available()) {
|
|
296
|
-
client_.subscribe_to_topic(STORAGE_GOSSIP_TOPIC);
|
|
297
|
-
|
|
298
|
-
// Set up message handler for storage updates
|
|
299
|
-
client_.on_topic_message(STORAGE_GOSSIP_TOPIC,
|
|
300
|
-
[this](const std::string& peer_id, const std::string& topic, const std::string& message) {
|
|
301
|
-
handle_gossip_message(peer_id, topic, message);
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// Register message handlers for sync protocol
|
|
306
|
-
if (config_.enable_sync) {
|
|
307
|
-
client_.on(MSG_TYPE_SYNC_REQUEST, [this](const std::string& peer_id, const nlohmann::json& data) {
|
|
308
|
-
handle_sync_request(peer_id, data);
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
client_.on(MSG_TYPE_SYNC_RESPONSE, [this](const std::string& peer_id, const nlohmann::json& data) {
|
|
312
|
-
handle_sync_response(peer_id, data);
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// Start persistence thread
|
|
317
|
-
if (config_.persist_to_disk) {
|
|
318
293
|
persistence_thread_ = std::thread(&StorageManager::persistence_thread_loop, this);
|
|
319
294
|
}
|
|
320
|
-
|
|
295
|
+
|
|
321
296
|
LOG_STORAGE_INFO("StorageManager initialized with data directory: " << config_.data_directory);
|
|
322
297
|
}
|
|
323
298
|
|
|
324
299
|
void StorageManager::shutdown() {
|
|
300
|
+
// Idempotent: safe to call from both stop() and the destructor.
|
|
301
|
+
if (!running_.exchange(false)) return;
|
|
302
|
+
|
|
325
303
|
LOG_STORAGE_INFO("StorageManager shutting down...");
|
|
326
|
-
|
|
327
|
-
running_.store(false);
|
|
328
|
-
|
|
304
|
+
|
|
329
305
|
// Wake up persistence thread
|
|
330
306
|
{
|
|
331
307
|
std::lock_guard<std::mutex> lock(persistence_mutex_);
|
|
332
308
|
persistence_cv_.notify_all();
|
|
333
309
|
}
|
|
334
|
-
|
|
310
|
+
|
|
335
311
|
// Join persistence thread
|
|
336
312
|
if (persistence_thread_.joinable()) {
|
|
337
313
|
persistence_thread_.join();
|
|
338
314
|
}
|
|
339
|
-
|
|
315
|
+
|
|
340
316
|
// Final save
|
|
341
|
-
|
|
317
|
+
bool needs_save;
|
|
318
|
+
{
|
|
319
|
+
std::lock_guard<std::mutex> lock(persistence_mutex_);
|
|
320
|
+
needs_save = dirty_;
|
|
321
|
+
}
|
|
322
|
+
if (config_.persist_to_disk && needs_save) {
|
|
342
323
|
save();
|
|
343
324
|
}
|
|
344
|
-
|
|
325
|
+
|
|
345
326
|
LOG_STORAGE_INFO("StorageManager shut down");
|
|
346
327
|
}
|
|
347
328
|
|
|
348
329
|
void StorageManager::persistence_thread_loop() {
|
|
349
330
|
const auto save_interval = std::chrono::seconds(5);
|
|
350
|
-
|
|
331
|
+
|
|
351
332
|
while (running_.load()) {
|
|
352
333
|
std::unique_lock<std::mutex> lock(persistence_mutex_);
|
|
353
|
-
persistence_cv_.wait_for(lock, save_interval, [this] {
|
|
354
|
-
return !running_.load() || dirty_;
|
|
334
|
+
persistence_cv_.wait_for(lock, save_interval, [this] {
|
|
335
|
+
return !running_.load() || dirty_;
|
|
355
336
|
});
|
|
356
|
-
|
|
337
|
+
|
|
357
338
|
if (!running_.load()) break;
|
|
358
|
-
|
|
339
|
+
|
|
359
340
|
if (dirty_) {
|
|
360
341
|
lock.unlock();
|
|
361
342
|
save();
|
|
@@ -363,10 +344,34 @@ void StorageManager::persistence_thread_loop() {
|
|
|
363
344
|
}
|
|
364
345
|
}
|
|
365
346
|
|
|
347
|
+
//=============================================================================
|
|
348
|
+
// Subsystem
|
|
349
|
+
//=============================================================================
|
|
350
|
+
|
|
351
|
+
void StorageManager::attach(NodeContext& ctx) {
|
|
352
|
+
network_ = &ctx.network;
|
|
353
|
+
|
|
354
|
+
if (!config_.enable_sync) return;
|
|
355
|
+
|
|
356
|
+
network_->on(MessageType::Storage,
|
|
357
|
+
[this](const Peer& peer, ByteView payload) { on_storage_message(peer.id(), payload); });
|
|
358
|
+
network_->on_peer_connected(
|
|
359
|
+
[this](const Peer& peer) { on_peer_connected(peer.id()); });
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
void StorageManager::start() {
|
|
363
|
+
// The persistence thread is already running (started in the constructor) and
|
|
364
|
+
// sync is driven by peer events registered in attach(); nothing to spin up here.
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
void StorageManager::stop() {
|
|
368
|
+
shutdown();
|
|
369
|
+
}
|
|
370
|
+
|
|
366
371
|
void StorageManager::set_config(const StorageConfig& config) {
|
|
367
372
|
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
368
373
|
config_ = config;
|
|
369
|
-
|
|
374
|
+
|
|
370
375
|
if (config_.persist_to_disk) {
|
|
371
376
|
create_directories(config_.data_directory.c_str());
|
|
372
377
|
}
|
|
@@ -396,12 +401,12 @@ bool StorageManager::put(const std::string& key, const std::vector<uint8_t>& val
|
|
|
396
401
|
return put_internal(key, StorageValueType::BINARY, value);
|
|
397
402
|
}
|
|
398
403
|
|
|
399
|
-
bool StorageManager::put_json(const std::string& key, const
|
|
404
|
+
bool StorageManager::put_json(const std::string& key, const librats::Json& value) {
|
|
400
405
|
std::string json_str = value.dump();
|
|
401
406
|
return put_internal(key, StorageValueType::JSON, serialize_value(json_str));
|
|
402
407
|
}
|
|
403
408
|
|
|
404
|
-
bool StorageManager::put_internal(const std::string& key, StorageValueType type,
|
|
409
|
+
bool StorageManager::put_internal(const std::string& key, StorageValueType type,
|
|
405
410
|
const std::vector<uint8_t>& data,
|
|
406
411
|
uint64_t timestamp_ms,
|
|
407
412
|
const std::string& origin_peer_id,
|
|
@@ -410,22 +415,22 @@ bool StorageManager::put_internal(const std::string& key, StorageValueType type,
|
|
|
410
415
|
LOG_STORAGE_ERROR("Cannot put with empty key");
|
|
411
416
|
return false;
|
|
412
417
|
}
|
|
413
|
-
|
|
418
|
+
|
|
414
419
|
if (data.size() > config_.max_value_size) {
|
|
415
420
|
LOG_STORAGE_ERROR("Value size " << data.size() << " exceeds maximum " << config_.max_value_size);
|
|
416
421
|
return false;
|
|
417
422
|
}
|
|
418
|
-
|
|
423
|
+
|
|
419
424
|
// Use current time if not provided
|
|
420
425
|
if (timestamp_ms == 0) {
|
|
421
426
|
timestamp_ms = get_current_timestamp_ms();
|
|
422
427
|
}
|
|
423
|
-
|
|
428
|
+
|
|
424
429
|
// Use our peer ID if not provided
|
|
425
430
|
std::string peer_id = origin_peer_id.empty() ? get_our_peer_id() : origin_peer_id;
|
|
426
|
-
|
|
431
|
+
|
|
427
432
|
StorageEntry new_entry(key, type, data, timestamp_ms, peer_id);
|
|
428
|
-
|
|
433
|
+
|
|
429
434
|
StorageChangeEvent event;
|
|
430
435
|
event.operation = StorageOperation::OP_PUT;
|
|
431
436
|
event.key = key;
|
|
@@ -434,10 +439,10 @@ bool StorageManager::put_internal(const std::string& key, StorageValueType type,
|
|
|
434
439
|
event.timestamp_ms = timestamp_ms;
|
|
435
440
|
event.origin_peer_id = peer_id;
|
|
436
441
|
event.is_remote = !origin_peer_id.empty() && origin_peer_id != get_our_peer_id();
|
|
437
|
-
|
|
442
|
+
|
|
438
443
|
{
|
|
439
444
|
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
440
|
-
|
|
445
|
+
|
|
441
446
|
auto it = entries_.find(key);
|
|
442
447
|
if (it != entries_.end()) {
|
|
443
448
|
// Check LWW - only update if new entry wins
|
|
@@ -445,24 +450,24 @@ bool StorageManager::put_internal(const std::string& key, StorageValueType type,
|
|
|
445
450
|
LOG_STORAGE_DEBUG("Rejected put for key '" << key << "' - existing entry is newer");
|
|
446
451
|
return false;
|
|
447
452
|
}
|
|
448
|
-
|
|
453
|
+
|
|
449
454
|
event.old_data = it->second.data;
|
|
450
455
|
it->second = new_entry;
|
|
451
456
|
} else {
|
|
452
457
|
entries_[key] = new_entry;
|
|
453
458
|
}
|
|
454
459
|
}
|
|
455
|
-
|
|
460
|
+
|
|
456
461
|
mark_dirty();
|
|
457
|
-
|
|
462
|
+
|
|
458
463
|
// Broadcast to peers if this is a local change
|
|
459
464
|
if (broadcast && config_.enable_sync) {
|
|
460
|
-
|
|
465
|
+
broadcast_entry(new_entry);
|
|
461
466
|
}
|
|
462
|
-
|
|
467
|
+
|
|
463
468
|
// Notify change callback
|
|
464
469
|
notify_change(event);
|
|
465
|
-
|
|
470
|
+
|
|
466
471
|
LOG_STORAGE_DEBUG("Put key '" << key << "' with type " << storage_value_type_to_string(type));
|
|
467
472
|
return true;
|
|
468
473
|
}
|
|
@@ -473,104 +478,93 @@ bool StorageManager::put_internal(const std::string& key, StorageValueType type,
|
|
|
473
478
|
|
|
474
479
|
std::optional<std::string> StorageManager::get_string(const std::string& key) const {
|
|
475
480
|
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
476
|
-
|
|
481
|
+
|
|
477
482
|
auto it = entries_.find(key);
|
|
478
483
|
if (it == entries_.end() || it->second.deleted) {
|
|
479
484
|
return std::nullopt;
|
|
480
485
|
}
|
|
481
|
-
|
|
486
|
+
|
|
482
487
|
if (it->second.type != StorageValueType::STRING) {
|
|
483
488
|
return std::nullopt;
|
|
484
489
|
}
|
|
485
|
-
|
|
490
|
+
|
|
486
491
|
return deserialize_string(it->second.data);
|
|
487
492
|
}
|
|
488
493
|
|
|
489
494
|
std::optional<int64_t> StorageManager::get_int(const std::string& key) const {
|
|
490
495
|
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
491
|
-
|
|
496
|
+
|
|
492
497
|
auto it = entries_.find(key);
|
|
493
498
|
if (it == entries_.end() || it->second.deleted) {
|
|
494
499
|
return std::nullopt;
|
|
495
500
|
}
|
|
496
|
-
|
|
501
|
+
|
|
497
502
|
if (it->second.type != StorageValueType::INT64) {
|
|
498
503
|
return std::nullopt;
|
|
499
504
|
}
|
|
500
|
-
|
|
505
|
+
|
|
501
506
|
return deserialize_int64(it->second.data);
|
|
502
507
|
}
|
|
503
508
|
|
|
504
509
|
std::optional<double> StorageManager::get_double(const std::string& key) const {
|
|
505
510
|
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
506
|
-
|
|
511
|
+
|
|
507
512
|
auto it = entries_.find(key);
|
|
508
513
|
if (it == entries_.end() || it->second.deleted) {
|
|
509
514
|
return std::nullopt;
|
|
510
515
|
}
|
|
511
|
-
|
|
516
|
+
|
|
512
517
|
if (it->second.type != StorageValueType::DOUBLE) {
|
|
513
518
|
return std::nullopt;
|
|
514
519
|
}
|
|
515
|
-
|
|
520
|
+
|
|
516
521
|
return deserialize_double(it->second.data);
|
|
517
522
|
}
|
|
518
523
|
|
|
519
524
|
std::optional<std::vector<uint8_t>> StorageManager::get_binary(const std::string& key) const {
|
|
520
525
|
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
521
|
-
|
|
526
|
+
|
|
522
527
|
auto it = entries_.find(key);
|
|
523
528
|
if (it == entries_.end() || it->second.deleted) {
|
|
524
529
|
return std::nullopt;
|
|
525
530
|
}
|
|
526
|
-
|
|
531
|
+
|
|
527
532
|
if (it->second.type != StorageValueType::BINARY) {
|
|
528
533
|
return std::nullopt;
|
|
529
534
|
}
|
|
530
|
-
|
|
535
|
+
|
|
531
536
|
return it->second.data;
|
|
532
537
|
}
|
|
533
538
|
|
|
534
|
-
std::optional<
|
|
539
|
+
std::optional<librats::Json> StorageManager::get_json(const std::string& key) const {
|
|
535
540
|
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
536
|
-
|
|
541
|
+
|
|
537
542
|
auto it = entries_.find(key);
|
|
538
543
|
if (it == entries_.end() || it->second.deleted) {
|
|
539
544
|
return std::nullopt;
|
|
540
545
|
}
|
|
541
|
-
|
|
546
|
+
|
|
542
547
|
if (it->second.type != StorageValueType::JSON) {
|
|
543
548
|
return std::nullopt;
|
|
544
549
|
}
|
|
545
|
-
|
|
550
|
+
|
|
546
551
|
try {
|
|
547
552
|
std::string json_str = deserialize_string(it->second.data);
|
|
548
|
-
return
|
|
553
|
+
return librats::Json::parse(json_str);
|
|
549
554
|
} catch (const std::exception& e) {
|
|
550
555
|
LOG_STORAGE_ERROR("Failed to parse JSON for key '" << key << "': " << e.what());
|
|
551
556
|
return std::nullopt;
|
|
552
557
|
}
|
|
553
558
|
}
|
|
554
559
|
|
|
555
|
-
const StorageEntry* StorageManager::get_entry(const std::string& key) const {
|
|
556
|
-
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
557
|
-
|
|
558
|
-
auto it = entries_.find(key);
|
|
559
|
-
if (it == entries_.end() || it->second.deleted) {
|
|
560
|
-
return nullptr;
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
return &it->second;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
560
|
std::optional<StorageValueType> StorageManager::get_type(const std::string& key) const {
|
|
567
561
|
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
568
|
-
|
|
562
|
+
|
|
569
563
|
auto it = entries_.find(key);
|
|
570
564
|
if (it == entries_.end() || it->second.deleted) {
|
|
571
565
|
return std::nullopt;
|
|
572
566
|
}
|
|
573
|
-
|
|
567
|
+
|
|
574
568
|
return it->second.type;
|
|
575
569
|
}
|
|
576
570
|
|
|
@@ -581,98 +575,100 @@ std::optional<StorageValueType> StorageManager::get_type(const std::string& key)
|
|
|
581
575
|
bool StorageManager::remove(const std::string& key) {
|
|
582
576
|
uint64_t timestamp_ms = get_current_timestamp_ms();
|
|
583
577
|
std::string our_peer_id = get_our_peer_id();
|
|
584
|
-
|
|
578
|
+
|
|
585
579
|
StorageChangeEvent event;
|
|
586
580
|
event.operation = StorageOperation::OP_DELETE;
|
|
587
581
|
event.key = key;
|
|
588
582
|
event.timestamp_ms = timestamp_ms;
|
|
589
583
|
event.origin_peer_id = our_peer_id;
|
|
590
584
|
event.is_remote = false;
|
|
591
|
-
|
|
585
|
+
|
|
586
|
+
StorageEntry tombstone;
|
|
592
587
|
{
|
|
593
588
|
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
594
|
-
|
|
589
|
+
|
|
595
590
|
auto it = entries_.find(key);
|
|
596
591
|
if (it == entries_.end()) {
|
|
597
592
|
return false;
|
|
598
593
|
}
|
|
599
|
-
|
|
594
|
+
|
|
600
595
|
if (it->second.deleted) {
|
|
601
596
|
return false; // Already deleted
|
|
602
597
|
}
|
|
603
|
-
|
|
598
|
+
|
|
604
599
|
event.old_data = it->second.data;
|
|
605
|
-
|
|
600
|
+
|
|
606
601
|
// Mark as deleted (tombstone)
|
|
607
602
|
it->second.deleted = true;
|
|
608
603
|
it->second.timestamp_ms = timestamp_ms;
|
|
609
604
|
it->second.origin_peer_id = our_peer_id;
|
|
610
605
|
it->second.data.clear();
|
|
611
606
|
it->second.calculate_checksum();
|
|
607
|
+
tombstone = it->second;
|
|
612
608
|
}
|
|
613
|
-
|
|
609
|
+
|
|
614
610
|
mark_dirty();
|
|
615
|
-
|
|
616
|
-
// Broadcast
|
|
611
|
+
|
|
612
|
+
// Broadcast the tombstone to peers
|
|
617
613
|
if (config_.enable_sync) {
|
|
618
|
-
|
|
614
|
+
broadcast_entry(tombstone);
|
|
619
615
|
}
|
|
620
|
-
|
|
616
|
+
|
|
621
617
|
// Notify change callback
|
|
622
618
|
notify_change(event);
|
|
623
|
-
|
|
619
|
+
|
|
624
620
|
LOG_STORAGE_DEBUG("Deleted key '" << key << "'");
|
|
625
621
|
return true;
|
|
626
622
|
}
|
|
627
623
|
|
|
628
624
|
bool StorageManager::has(const std::string& key) const {
|
|
629
625
|
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
630
|
-
|
|
626
|
+
|
|
631
627
|
auto it = entries_.find(key);
|
|
632
628
|
return it != entries_.end() && !it->second.deleted;
|
|
633
629
|
}
|
|
634
630
|
|
|
635
631
|
std::vector<std::string> StorageManager::keys() const {
|
|
636
632
|
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
637
|
-
|
|
633
|
+
|
|
638
634
|
std::vector<std::string> result;
|
|
639
635
|
result.reserve(entries_.size());
|
|
640
|
-
|
|
636
|
+
|
|
641
637
|
for (const auto& pair : entries_) {
|
|
642
638
|
if (!pair.second.deleted) {
|
|
643
639
|
result.push_back(pair.first);
|
|
644
640
|
}
|
|
645
641
|
}
|
|
646
|
-
|
|
642
|
+
|
|
647
643
|
return result;
|
|
648
644
|
}
|
|
649
645
|
|
|
650
646
|
std::vector<std::string> StorageManager::keys_with_prefix(const std::string& prefix) const {
|
|
651
647
|
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
652
|
-
|
|
648
|
+
|
|
653
649
|
std::vector<std::string> result;
|
|
654
|
-
|
|
650
|
+
|
|
655
651
|
for (const auto& pair : entries_) {
|
|
656
|
-
if (!pair.second.deleted &&
|
|
652
|
+
if (!pair.second.deleted &&
|
|
657
653
|
pair.first.size() >= prefix.size() &&
|
|
658
654
|
pair.first.compare(0, prefix.size(), prefix) == 0) {
|
|
659
655
|
result.push_back(pair.first);
|
|
660
656
|
}
|
|
661
657
|
}
|
|
662
|
-
|
|
658
|
+
|
|
663
659
|
return result;
|
|
664
660
|
}
|
|
665
661
|
|
|
666
662
|
size_t StorageManager::size() const {
|
|
667
663
|
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
668
|
-
|
|
664
|
+
|
|
669
665
|
size_t count = 0;
|
|
670
666
|
for (const auto& pair : entries_) {
|
|
671
667
|
if (!pair.second.deleted) {
|
|
672
668
|
count++;
|
|
673
669
|
}
|
|
674
670
|
}
|
|
675
|
-
|
|
671
|
+
|
|
676
672
|
return count;
|
|
677
673
|
}
|
|
678
674
|
|
|
@@ -681,24 +677,22 @@ bool StorageManager::empty() const {
|
|
|
681
677
|
}
|
|
682
678
|
|
|
683
679
|
void StorageManager::clear() {
|
|
684
|
-
uint64_t timestamp_ms = get_current_timestamp_ms();
|
|
685
|
-
|
|
686
680
|
std::vector<std::string> keys_to_delete;
|
|
687
|
-
|
|
681
|
+
|
|
688
682
|
{
|
|
689
683
|
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
690
|
-
|
|
684
|
+
|
|
691
685
|
for (auto& pair : entries_) {
|
|
692
686
|
if (!pair.second.deleted) {
|
|
693
687
|
keys_to_delete.push_back(pair.first);
|
|
694
688
|
}
|
|
695
689
|
}
|
|
696
690
|
}
|
|
697
|
-
|
|
691
|
+
|
|
698
692
|
for (const auto& key : keys_to_delete) {
|
|
699
693
|
remove(key);
|
|
700
694
|
}
|
|
701
|
-
|
|
695
|
+
|
|
702
696
|
LOG_STORAGE_INFO("Cleared all entries");
|
|
703
697
|
}
|
|
704
698
|
|
|
@@ -710,16 +704,21 @@ bool StorageManager::save() {
|
|
|
710
704
|
if (!config_.persist_to_disk) {
|
|
711
705
|
return true;
|
|
712
706
|
}
|
|
713
|
-
|
|
707
|
+
|
|
714
708
|
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
715
|
-
|
|
709
|
+
|
|
716
710
|
bool result = write_data_file();
|
|
717
|
-
|
|
711
|
+
|
|
718
712
|
if (result) {
|
|
719
|
-
dirty_
|
|
713
|
+
// dirty_ belongs to persistence_mutex_, not storage_mutex_ — the persistence
|
|
714
|
+
// thread reads it under that lock. Same order as load() (storage → persistence).
|
|
715
|
+
{
|
|
716
|
+
std::lock_guard<std::mutex> dirty_lock(persistence_mutex_);
|
|
717
|
+
dirty_ = false;
|
|
718
|
+
}
|
|
720
719
|
LOG_STORAGE_DEBUG("Saved " << entries_.size() << " entries to disk");
|
|
721
720
|
}
|
|
722
|
-
|
|
721
|
+
|
|
723
722
|
return result;
|
|
724
723
|
}
|
|
725
724
|
|
|
@@ -727,29 +726,29 @@ bool StorageManager::load() {
|
|
|
727
726
|
if (!config_.persist_to_disk) {
|
|
728
727
|
return true;
|
|
729
728
|
}
|
|
730
|
-
|
|
729
|
+
|
|
731
730
|
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
732
|
-
|
|
731
|
+
|
|
733
732
|
std::string data_path = get_data_file_path();
|
|
734
733
|
if (!file_exists(data_path.c_str())) {
|
|
735
734
|
LOG_STORAGE_DEBUG("No existing data file found at " << data_path);
|
|
736
735
|
return true; // Not an error, just no data yet
|
|
737
736
|
}
|
|
738
|
-
|
|
737
|
+
|
|
739
738
|
bool result = read_data_file();
|
|
740
|
-
|
|
739
|
+
|
|
741
740
|
if (result) {
|
|
742
741
|
LOG_STORAGE_INFO("Loaded " << entries_.size() << " entries from disk");
|
|
743
742
|
}
|
|
744
|
-
|
|
743
|
+
|
|
745
744
|
return result;
|
|
746
745
|
}
|
|
747
746
|
|
|
748
747
|
size_t StorageManager::compact() {
|
|
749
748
|
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
750
|
-
|
|
749
|
+
|
|
751
750
|
size_t removed = 0;
|
|
752
|
-
|
|
751
|
+
|
|
753
752
|
for (auto it = entries_.begin(); it != entries_.end();) {
|
|
754
753
|
if (it->second.deleted) {
|
|
755
754
|
it = entries_.erase(it);
|
|
@@ -758,12 +757,12 @@ size_t StorageManager::compact() {
|
|
|
758
757
|
++it;
|
|
759
758
|
}
|
|
760
759
|
}
|
|
761
|
-
|
|
760
|
+
|
|
762
761
|
if (removed > 0) {
|
|
763
762
|
mark_dirty();
|
|
764
763
|
LOG_STORAGE_INFO("Compacted storage, removed " << removed << " tombstones");
|
|
765
764
|
}
|
|
766
|
-
|
|
765
|
+
|
|
767
766
|
return removed;
|
|
768
767
|
}
|
|
769
768
|
|
|
@@ -772,29 +771,27 @@ size_t StorageManager::compact() {
|
|
|
772
771
|
//=============================================================================
|
|
773
772
|
|
|
774
773
|
bool StorageManager::request_sync() {
|
|
775
|
-
if (!config_.enable_sync) {
|
|
774
|
+
if (!config_.enable_sync || !network_) {
|
|
776
775
|
return false;
|
|
777
776
|
}
|
|
778
|
-
|
|
777
|
+
|
|
779
778
|
// Get a connected peer to sync from
|
|
780
|
-
auto peers =
|
|
779
|
+
auto peers = network_->connected_peers();
|
|
781
780
|
if (peers.empty()) {
|
|
782
781
|
LOG_STORAGE_WARN("No peers available for sync");
|
|
783
782
|
return false;
|
|
784
783
|
}
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
784
|
+
|
|
785
|
+
const PeerId& peer_id = peers[0];
|
|
786
|
+
|
|
789
787
|
{
|
|
790
788
|
std::lock_guard<std::mutex> lock(sync_mutex_);
|
|
791
789
|
sync_status_ = StorageSyncStatus::IN_PROGRESS;
|
|
792
|
-
sync_peer_id_ = peer_id;
|
|
793
790
|
}
|
|
794
|
-
|
|
791
|
+
|
|
795
792
|
send_sync_request(peer_id);
|
|
796
|
-
|
|
797
|
-
LOG_STORAGE_INFO("Requested sync from peer " << peer_id);
|
|
793
|
+
|
|
794
|
+
LOG_STORAGE_INFO("Requested sync from peer " << peer_id.short_hex());
|
|
798
795
|
return true;
|
|
799
796
|
}
|
|
800
797
|
|
|
@@ -826,17 +823,17 @@ void StorageManager::set_sync_complete_callback(StorageSyncCompleteCallback call
|
|
|
826
823
|
|
|
827
824
|
StorageStatistics StorageManager::get_statistics() const {
|
|
828
825
|
std::lock_guard<std::mutex> lock(stats_mutex_);
|
|
829
|
-
|
|
826
|
+
|
|
830
827
|
StorageStatistics result = stats_;
|
|
831
|
-
|
|
828
|
+
|
|
832
829
|
// Calculate current counts
|
|
833
830
|
{
|
|
834
831
|
std::lock_guard<std::mutex> storage_lock(storage_mutex_);
|
|
835
|
-
|
|
832
|
+
|
|
836
833
|
result.total_entries = 0;
|
|
837
834
|
result.deleted_entries = 0;
|
|
838
835
|
result.total_data_bytes = 0;
|
|
839
|
-
|
|
836
|
+
|
|
840
837
|
for (const auto& pair : entries_) {
|
|
841
838
|
if (pair.second.deleted) {
|
|
842
839
|
result.deleted_entries++;
|
|
@@ -846,20 +843,20 @@ StorageStatistics StorageManager::get_statistics() const {
|
|
|
846
843
|
}
|
|
847
844
|
}
|
|
848
845
|
}
|
|
849
|
-
|
|
846
|
+
|
|
850
847
|
{
|
|
851
848
|
std::lock_guard<std::mutex> sync_lock(sync_mutex_);
|
|
852
849
|
result.sync_status = sync_status_;
|
|
853
850
|
result.last_sync_time = last_sync_time_;
|
|
854
851
|
}
|
|
855
|
-
|
|
852
|
+
|
|
856
853
|
return result;
|
|
857
854
|
}
|
|
858
855
|
|
|
859
|
-
|
|
856
|
+
librats::Json StorageManager::get_statistics_json() const {
|
|
860
857
|
StorageStatistics stats = get_statistics();
|
|
861
|
-
|
|
862
|
-
|
|
858
|
+
|
|
859
|
+
librats::Json result;
|
|
863
860
|
result["total_entries"] = stats.total_entries;
|
|
864
861
|
result["deleted_entries"] = stats.deleted_entries;
|
|
865
862
|
result["total_data_bytes"] = stats.total_data_bytes;
|
|
@@ -868,231 +865,110 @@ nlohmann::json StorageManager::get_statistics_json() const {
|
|
|
868
865
|
result["entries_sent"] = stats.entries_sent;
|
|
869
866
|
result["sync_requests_received"] = stats.sync_requests_received;
|
|
870
867
|
result["sync_requests_sent"] = stats.sync_requests_sent;
|
|
871
|
-
|
|
868
|
+
|
|
872
869
|
switch (stats.sync_status) {
|
|
873
870
|
case StorageSyncStatus::NOT_STARTED: result["sync_status"] = "not_started"; break;
|
|
874
871
|
case StorageSyncStatus::IN_PROGRESS: result["sync_status"] = "in_progress"; break;
|
|
875
872
|
case StorageSyncStatus::COMPLETED: result["sync_status"] = "completed"; break;
|
|
876
873
|
case StorageSyncStatus::FAILED: result["sync_status"] = "failed"; break;
|
|
877
874
|
}
|
|
878
|
-
|
|
875
|
+
|
|
879
876
|
return result;
|
|
880
877
|
}
|
|
881
878
|
|
|
882
879
|
//=============================================================================
|
|
883
|
-
// Network Message Handlers
|
|
880
|
+
// Network Message Handlers (run on a reactor thread)
|
|
884
881
|
//=============================================================================
|
|
885
882
|
|
|
886
|
-
void StorageManager::
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
std::
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
910
|
-
|
|
911
|
-
std::vector<int> T(256, -1);
|
|
912
|
-
for (int i = 0; i < 64; i++) T[static_cast<unsigned char>(base64_chars[i])] = i;
|
|
913
|
-
|
|
914
|
-
int val = 0, valb = -8;
|
|
915
|
-
for (unsigned char c : data_base64) {
|
|
916
|
-
if (T[c] == -1) break;
|
|
917
|
-
val = (val << 6) + T[c];
|
|
918
|
-
valb += 6;
|
|
919
|
-
if (valb >= 0) {
|
|
920
|
-
data.push_back((val >> valb) & 0xFF);
|
|
921
|
-
valb -= 8;
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
StorageValueType value_type = string_to_storage_value_type(type_str);
|
|
926
|
-
|
|
927
|
-
// Apply with LWW resolution
|
|
928
|
-
put_internal(key, value_type, data, timestamp_ms, origin_peer, false);
|
|
929
|
-
|
|
930
|
-
{
|
|
931
|
-
std::lock_guard<std::mutex> lock(stats_mutex_);
|
|
932
|
-
stats_.entries_synced++;
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
} else if (msg_type == "delete") {
|
|
936
|
-
std::string key = msg["key"];
|
|
937
|
-
uint64_t timestamp_ms = msg["timestamp"];
|
|
938
|
-
std::string origin_peer = msg["origin_peer"];
|
|
939
|
-
|
|
940
|
-
// Apply delete with LWW
|
|
941
|
-
{
|
|
942
|
-
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
943
|
-
|
|
944
|
-
auto it = entries_.find(key);
|
|
945
|
-
if (it != entries_.end()) {
|
|
946
|
-
// Check if this delete is newer
|
|
947
|
-
if (timestamp_ms > it->second.timestamp_ms ||
|
|
948
|
-
(timestamp_ms == it->second.timestamp_ms && origin_peer > it->second.origin_peer_id)) {
|
|
949
|
-
it->second.deleted = true;
|
|
950
|
-
it->second.timestamp_ms = timestamp_ms;
|
|
951
|
-
it->second.origin_peer_id = origin_peer;
|
|
952
|
-
it->second.data.clear();
|
|
953
|
-
it->second.calculate_checksum();
|
|
954
|
-
mark_dirty();
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
|
|
883
|
+
void StorageManager::on_storage_message(const PeerId& from, ByteView payload) {
|
|
884
|
+
if (payload.empty()) return;
|
|
885
|
+
|
|
886
|
+
const uint8_t* p = payload.data();
|
|
887
|
+
const size_t n = payload.size();
|
|
888
|
+
const uint8_t op = p[0];
|
|
889
|
+
|
|
890
|
+
if (op == OP_ENTRY) {
|
|
891
|
+
// Deserialize a single entry from the payload (after the opcode byte).
|
|
892
|
+
std::vector<uint8_t> buf(p + 1, p + n);
|
|
893
|
+
StorageEntry entry;
|
|
894
|
+
size_t bytes_read = 0;
|
|
895
|
+
if (!StorageEntry::deserialize(buf, 0, entry, bytes_read)) {
|
|
896
|
+
LOG_STORAGE_WARN("Malformed storage entry from " << from.short_hex());
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
if (!entry.verify_checksum()) {
|
|
900
|
+
LOG_STORAGE_WARN("Checksum mismatch on entry '" << entry.key << "' from " << from.short_hex());
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
StorageChangeEvent event;
|
|
905
|
+
if (apply_remote_entry(entry, &event)) {
|
|
959
906
|
{
|
|
960
907
|
std::lock_guard<std::mutex> lock(stats_mutex_);
|
|
961
908
|
stats_.entries_synced++;
|
|
962
909
|
}
|
|
910
|
+
mark_dirty();
|
|
911
|
+
notify_change(event);
|
|
912
|
+
// Re-flood to other peers; LWW makes a duplicate lose, so this stops.
|
|
913
|
+
forward_entry(entry, from);
|
|
963
914
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
void StorageManager::handle_sync_request(const std::string& peer_id, const nlohmann::json& data) {
|
|
971
|
-
LOG_STORAGE_INFO("Received sync request from peer " << peer_id);
|
|
972
|
-
|
|
973
|
-
{
|
|
974
|
-
std::lock_guard<std::mutex> lock(stats_mutex_);
|
|
975
|
-
stats_.sync_requests_received++;
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
// Send full database snapshot
|
|
979
|
-
send_sync_response(peer_id);
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
void StorageManager::handle_sync_response(const std::string& peer_id, const nlohmann::json& data) {
|
|
983
|
-
LOG_STORAGE_INFO("Received sync response from peer " << peer_id);
|
|
984
|
-
|
|
985
|
-
try {
|
|
986
|
-
if (!data.contains("entries") || !data["entries"].is_array()) {
|
|
987
|
-
LOG_STORAGE_ERROR("Invalid sync response format");
|
|
988
|
-
|
|
989
|
-
{
|
|
990
|
-
std::lock_guard<std::mutex> lock(sync_mutex_);
|
|
991
|
-
sync_status_ = StorageSyncStatus::FAILED;
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
if (sync_complete_callback_) {
|
|
995
|
-
sync_complete_callback_(false, "Invalid sync response format");
|
|
996
|
-
}
|
|
997
|
-
return;
|
|
915
|
+
} else if (op == OP_SYNC_REQUEST) {
|
|
916
|
+
{
|
|
917
|
+
std::lock_guard<std::mutex> lock(stats_mutex_);
|
|
918
|
+
stats_.sync_requests_received++;
|
|
998
919
|
}
|
|
999
|
-
|
|
920
|
+
send_sync_response(from);
|
|
921
|
+
} else if (op == OP_SYNC_RESPONSE) {
|
|
922
|
+
// [3][count:u32][entry]*
|
|
923
|
+
if (n < 5) return;
|
|
924
|
+
uint32_t count = (static_cast<uint32_t>(p[1]) << 24) |
|
|
925
|
+
(static_cast<uint32_t>(p[2]) << 16) |
|
|
926
|
+
(static_cast<uint32_t>(p[3]) << 8) |
|
|
927
|
+
static_cast<uint32_t>(p[4]);
|
|
928
|
+
std::vector<uint8_t> buf(p + 5, p + n);
|
|
929
|
+
size_t offset = 0;
|
|
1000
930
|
int applied = 0;
|
|
1001
|
-
|
|
1002
|
-
for (const auto& entry_json : data["entries"]) {
|
|
1003
|
-
std::string key = entry_json["key"];
|
|
1004
|
-
std::string type_str = entry_json["value_type"];
|
|
1005
|
-
std::string data_base64 = entry_json["data"];
|
|
1006
|
-
uint64_t timestamp_ms = entry_json["timestamp"];
|
|
1007
|
-
std::string origin_peer = entry_json["origin_peer"];
|
|
1008
|
-
bool deleted = entry_json.value("deleted", false);
|
|
1009
|
-
|
|
1010
|
-
// Decode base64 data
|
|
1011
|
-
std::vector<uint8_t> decoded_data;
|
|
1012
|
-
static const std::string base64_chars =
|
|
1013
|
-
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
1014
|
-
|
|
1015
|
-
std::vector<int> T(256, -1);
|
|
1016
|
-
for (int i = 0; i < 64; i++) T[static_cast<unsigned char>(base64_chars[i])] = i;
|
|
1017
|
-
|
|
1018
|
-
int val = 0, valb = -8;
|
|
1019
|
-
for (unsigned char c : data_base64) {
|
|
1020
|
-
if (T[c] == -1) break;
|
|
1021
|
-
val = (val << 6) + T[c];
|
|
1022
|
-
valb += 6;
|
|
1023
|
-
if (valb >= 0) {
|
|
1024
|
-
decoded_data.push_back((val >> valb) & 0xFF);
|
|
1025
|
-
valb -= 8;
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
StorageValueType value_type = string_to_storage_value_type(type_str);
|
|
1030
|
-
|
|
1031
|
-
// Create entry
|
|
931
|
+
for (uint32_t i = 0; i < count && offset < buf.size(); i++) {
|
|
1032
932
|
StorageEntry entry;
|
|
1033
|
-
|
|
1034
|
-
entry
|
|
1035
|
-
|
|
1036
|
-
entry.
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
entry
|
|
1040
|
-
|
|
1041
|
-
if (apply_remote_entry(entry)) {
|
|
933
|
+
size_t bytes_read = 0;
|
|
934
|
+
if (!StorageEntry::deserialize(buf, offset, entry, bytes_read)) break;
|
|
935
|
+
offset += bytes_read;
|
|
936
|
+
if (!entry.verify_checksum()) continue;
|
|
937
|
+
|
|
938
|
+
StorageChangeEvent event;
|
|
939
|
+
if (apply_remote_entry(entry, &event)) {
|
|
1042
940
|
applied++;
|
|
941
|
+
notify_change(event);
|
|
1043
942
|
}
|
|
1044
943
|
}
|
|
1045
|
-
|
|
944
|
+
|
|
1046
945
|
{
|
|
1047
946
|
std::lock_guard<std::mutex> lock(sync_mutex_);
|
|
1048
947
|
sync_status_ = StorageSyncStatus::COMPLETED;
|
|
1049
948
|
initial_sync_complete_ = true;
|
|
1050
949
|
last_sync_time_ = std::chrono::steady_clock::now();
|
|
1051
950
|
}
|
|
1052
|
-
|
|
1053
951
|
{
|
|
1054
952
|
std::lock_guard<std::mutex> lock(stats_mutex_);
|
|
1055
953
|
stats_.entries_synced += applied;
|
|
1056
954
|
}
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
if (sync_complete_callback_) {
|
|
1063
|
-
sync_complete_callback_(true, "");
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
} catch (const std::exception& e) {
|
|
1067
|
-
LOG_STORAGE_ERROR("Failed to handle sync response: " << e.what());
|
|
1068
|
-
|
|
1069
|
-
{
|
|
1070
|
-
std::lock_guard<std::mutex> lock(sync_mutex_);
|
|
1071
|
-
sync_status_ = StorageSyncStatus::FAILED;
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
if (sync_complete_callback_) {
|
|
1075
|
-
sync_complete_callback_(false, e.what());
|
|
1076
|
-
}
|
|
955
|
+
if (applied > 0) mark_dirty();
|
|
956
|
+
|
|
957
|
+
LOG_STORAGE_INFO("Sync from " << from.short_hex() << " applied " << applied << " entries");
|
|
958
|
+
if (sync_complete_callback_) sync_complete_callback_(true, "");
|
|
1077
959
|
}
|
|
1078
960
|
}
|
|
1079
961
|
|
|
1080
|
-
void StorageManager::on_peer_connected(const
|
|
1081
|
-
if (!config_.enable_sync)
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
// If we haven't synced yet, request sync from this peer
|
|
962
|
+
void StorageManager::on_peer_connected(const PeerId& peer_id) {
|
|
963
|
+
if (!config_.enable_sync) return;
|
|
964
|
+
|
|
965
|
+
// Anti-entropy: ask the new peer for a full snapshot. Both ends do this on
|
|
966
|
+
// connect, so the two databases converge via LWW.
|
|
1086
967
|
{
|
|
1087
968
|
std::lock_guard<std::mutex> lock(sync_mutex_);
|
|
1088
|
-
if (
|
|
969
|
+
if (sync_status_ == StorageSyncStatus::NOT_STARTED)
|
|
1089
970
|
sync_status_ = StorageSyncStatus::IN_PROGRESS;
|
|
1090
|
-
sync_peer_id_ = peer_id;
|
|
1091
|
-
} else {
|
|
1092
|
-
return; // Already synced or in progress
|
|
1093
|
-
}
|
|
1094
971
|
}
|
|
1095
|
-
|
|
1096
972
|
send_sync_request(peer_id);
|
|
1097
973
|
}
|
|
1098
974
|
|
|
@@ -1124,7 +1000,7 @@ std::vector<uint8_t> StorageManager::serialize_value(const std::string& value) c
|
|
|
1124
1000
|
|
|
1125
1001
|
int64_t StorageManager::deserialize_int64(const std::vector<uint8_t>& data) const {
|
|
1126
1002
|
if (data.size() < 8) return 0;
|
|
1127
|
-
|
|
1003
|
+
|
|
1128
1004
|
int64_t value = 0;
|
|
1129
1005
|
for (int i = 0; i < 8; i++) {
|
|
1130
1006
|
value = (value << 8) | data[i];
|
|
@@ -1134,12 +1010,12 @@ int64_t StorageManager::deserialize_int64(const std::vector<uint8_t>& data) cons
|
|
|
1134
1010
|
|
|
1135
1011
|
double StorageManager::deserialize_double(const std::vector<uint8_t>& data) const {
|
|
1136
1012
|
if (data.size() < 8) return 0.0;
|
|
1137
|
-
|
|
1013
|
+
|
|
1138
1014
|
uint64_t bits = 0;
|
|
1139
1015
|
for (int i = 0; i < 8; i++) {
|
|
1140
1016
|
bits = (bits << 8) | data[i];
|
|
1141
1017
|
}
|
|
1142
|
-
|
|
1018
|
+
|
|
1143
1019
|
double value;
|
|
1144
1020
|
std::memcpy(&value, &bits, sizeof(double));
|
|
1145
1021
|
return value;
|
|
@@ -1153,133 +1029,103 @@ std::string StorageManager::deserialize_string(const std::vector<uint8_t>& data)
|
|
|
1153
1029
|
// Private Methods - Network Operations
|
|
1154
1030
|
//=============================================================================
|
|
1155
1031
|
|
|
1156
|
-
void StorageManager::
|
|
1157
|
-
if (!
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
for (uint8_t c : entry.data) {
|
|
1167
|
-
val = (val << 8) + c;
|
|
1168
|
-
valb += 8;
|
|
1169
|
-
while (valb >= 0) {
|
|
1170
|
-
data_base64.push_back(base64_chars[(val >> valb) & 0x3F]);
|
|
1171
|
-
valb -= 6;
|
|
1172
|
-
}
|
|
1173
|
-
}
|
|
1174
|
-
if (valb > -6) data_base64.push_back(base64_chars[((val << 8) >> (valb + 8)) & 0x3F]);
|
|
1175
|
-
while (data_base64.size() % 4) data_base64.push_back('=');
|
|
1176
|
-
|
|
1177
|
-
nlohmann::json msg;
|
|
1178
|
-
msg["type"] = "put";
|
|
1179
|
-
msg["key"] = entry.key;
|
|
1180
|
-
msg["value_type"] = storage_value_type_to_string(entry.type);
|
|
1181
|
-
msg["data"] = data_base64;
|
|
1182
|
-
msg["timestamp"] = entry.timestamp_ms;
|
|
1183
|
-
msg["origin_peer"] = entry.origin_peer_id;
|
|
1184
|
-
|
|
1185
|
-
client_.publish_to_topic(STORAGE_GOSSIP_TOPIC, msg.dump());
|
|
1186
|
-
|
|
1032
|
+
void StorageManager::broadcast_entry(const StorageEntry& entry) {
|
|
1033
|
+
if (!network_) return;
|
|
1034
|
+
|
|
1035
|
+
std::vector<uint8_t> msg;
|
|
1036
|
+
msg.push_back(OP_ENTRY);
|
|
1037
|
+
std::vector<uint8_t> serialized = entry.serialize();
|
|
1038
|
+
msg.insert(msg.end(), serialized.begin(), serialized.end());
|
|
1039
|
+
|
|
1040
|
+
network_->broadcast(MessageType::Storage, ByteView(msg));
|
|
1041
|
+
|
|
1187
1042
|
{
|
|
1188
1043
|
std::lock_guard<std::mutex> lock(stats_mutex_);
|
|
1189
1044
|
stats_.entries_sent++;
|
|
1190
1045
|
}
|
|
1191
1046
|
}
|
|
1192
1047
|
|
|
1193
|
-
void StorageManager::
|
|
1194
|
-
if (!
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
msg
|
|
1200
|
-
msg
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
{
|
|
1207
|
-
std::lock_guard<std::mutex> lock(stats_mutex_);
|
|
1208
|
-
stats_.entries_sent++;
|
|
1048
|
+
void StorageManager::forward_entry(const StorageEntry& entry, const PeerId& except) {
|
|
1049
|
+
if (!network_) return;
|
|
1050
|
+
|
|
1051
|
+
std::vector<uint8_t> msg;
|
|
1052
|
+
msg.push_back(OP_ENTRY);
|
|
1053
|
+
std::vector<uint8_t> serialized = entry.serialize();
|
|
1054
|
+
msg.insert(msg.end(), serialized.begin(), serialized.end());
|
|
1055
|
+
ByteView view(msg);
|
|
1056
|
+
|
|
1057
|
+
for (const PeerId& peer : network_->connected_peers()) {
|
|
1058
|
+
if (peer == except) continue;
|
|
1059
|
+
network_->send(peer, MessageType::Storage, view);
|
|
1209
1060
|
}
|
|
1210
1061
|
}
|
|
1211
1062
|
|
|
1212
|
-
void StorageManager::send_sync_request(const
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1063
|
+
void StorageManager::send_sync_request(const PeerId& peer_id) {
|
|
1064
|
+
if (!network_) return;
|
|
1065
|
+
|
|
1066
|
+
std::vector<uint8_t> msg{OP_SYNC_REQUEST};
|
|
1067
|
+
network_->send(peer_id, MessageType::Storage, ByteView(msg));
|
|
1068
|
+
|
|
1218
1069
|
{
|
|
1219
1070
|
std::lock_guard<std::mutex> lock(stats_mutex_);
|
|
1220
1071
|
stats_.sync_requests_sent++;
|
|
1221
1072
|
}
|
|
1222
|
-
|
|
1223
|
-
LOG_STORAGE_DEBUG("Sent sync request to peer " << peer_id);
|
|
1073
|
+
|
|
1074
|
+
LOG_STORAGE_DEBUG("Sent sync request to peer " << peer_id.short_hex());
|
|
1224
1075
|
}
|
|
1225
1076
|
|
|
1226
|
-
void StorageManager::send_sync_response(const
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1077
|
+
void StorageManager::send_sync_response(const PeerId& peer_id) {
|
|
1078
|
+
if (!network_) return;
|
|
1079
|
+
|
|
1080
|
+
std::vector<uint8_t> msg;
|
|
1081
|
+
msg.push_back(OP_SYNC_RESPONSE);
|
|
1082
|
+
|
|
1083
|
+
uint32_t count = 0;
|
|
1084
|
+
std::vector<uint8_t> entries_blob;
|
|
1230
1085
|
{
|
|
1231
1086
|
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
1232
|
-
|
|
1233
1087
|
for (const auto& pair : entries_) {
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
static const char* base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
1238
|
-
std::string data_base64;
|
|
1239
|
-
|
|
1240
|
-
int val = 0, valb = -6;
|
|
1241
|
-
for (uint8_t c : entry.data) {
|
|
1242
|
-
val = (val << 8) + c;
|
|
1243
|
-
valb += 8;
|
|
1244
|
-
while (valb >= 0) {
|
|
1245
|
-
data_base64.push_back(base64_chars[(val >> valb) & 0x3F]);
|
|
1246
|
-
valb -= 6;
|
|
1247
|
-
}
|
|
1248
|
-
}
|
|
1249
|
-
if (valb > -6) data_base64.push_back(base64_chars[((val << 8) >> (valb + 8)) & 0x3F]);
|
|
1250
|
-
while (data_base64.size() % 4) data_base64.push_back('=');
|
|
1251
|
-
|
|
1252
|
-
nlohmann::json entry_json;
|
|
1253
|
-
entry_json["key"] = entry.key;
|
|
1254
|
-
entry_json["value_type"] = storage_value_type_to_string(entry.type);
|
|
1255
|
-
entry_json["data"] = data_base64;
|
|
1256
|
-
entry_json["timestamp"] = entry.timestamp_ms;
|
|
1257
|
-
entry_json["origin_peer"] = entry.origin_peer_id;
|
|
1258
|
-
entry_json["deleted"] = entry.deleted;
|
|
1259
|
-
|
|
1260
|
-
entries_json.push_back(entry_json);
|
|
1088
|
+
std::vector<uint8_t> serialized = pair.second.serialize();
|
|
1089
|
+
entries_blob.insert(entries_blob.end(), serialized.begin(), serialized.end());
|
|
1090
|
+
count++;
|
|
1261
1091
|
}
|
|
1262
1092
|
}
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1093
|
+
|
|
1094
|
+
put_u32(msg, count);
|
|
1095
|
+
msg.insert(msg.end(), entries_blob.begin(), entries_blob.end());
|
|
1096
|
+
|
|
1097
|
+
network_->send(peer_id, MessageType::Storage, ByteView(msg));
|
|
1098
|
+
|
|
1099
|
+
LOG_STORAGE_DEBUG("Sent sync response to peer " << peer_id.short_hex() << " with " << count << " entries");
|
|
1269
1100
|
}
|
|
1270
1101
|
|
|
1271
|
-
bool StorageManager::apply_remote_entry(const StorageEntry& entry) {
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1102
|
+
bool StorageManager::apply_remote_entry(const StorageEntry& entry, StorageChangeEvent* out_event) {
|
|
1103
|
+
StorageChangeEvent event;
|
|
1104
|
+
event.operation = entry.deleted ? StorageOperation::OP_DELETE : StorageOperation::OP_PUT;
|
|
1105
|
+
event.key = entry.key;
|
|
1106
|
+
event.type = entry.type;
|
|
1107
|
+
event.new_data = entry.data;
|
|
1108
|
+
event.timestamp_ms = entry.timestamp_ms;
|
|
1109
|
+
event.origin_peer_id = entry.origin_peer_id;
|
|
1110
|
+
event.is_remote = true;
|
|
1111
|
+
|
|
1112
|
+
{
|
|
1113
|
+
std::lock_guard<std::mutex> lock(storage_mutex_);
|
|
1114
|
+
|
|
1115
|
+
auto it = entries_.find(entry.key);
|
|
1116
|
+
if (it != entries_.end()) {
|
|
1117
|
+
// Check LWW
|
|
1118
|
+
if (!entry.wins_over(it->second)) {
|
|
1119
|
+
return false; // Our entry is newer or identical, don't apply
|
|
1120
|
+
}
|
|
1121
|
+
event.old_data = it->second.data;
|
|
1122
|
+
it->second = entry;
|
|
1123
|
+
} else {
|
|
1124
|
+
entries_[entry.key] = entry;
|
|
1279
1125
|
}
|
|
1280
1126
|
}
|
|
1281
|
-
|
|
1282
|
-
|
|
1127
|
+
|
|
1128
|
+
if (out_event) *out_event = event;
|
|
1283
1129
|
return true;
|
|
1284
1130
|
}
|
|
1285
1131
|
|
|
@@ -1298,14 +1144,14 @@ std::string StorageManager::get_index_file_path() const {
|
|
|
1298
1144
|
bool StorageManager::write_data_file() {
|
|
1299
1145
|
std::string data_path = get_data_file_path();
|
|
1300
1146
|
std::string temp_path = data_path + ".tmp";
|
|
1301
|
-
|
|
1147
|
+
|
|
1302
1148
|
try {
|
|
1303
1149
|
FILE* file = fopen(temp_path.c_str(), "wb");
|
|
1304
1150
|
if (!file) {
|
|
1305
1151
|
LOG_STORAGE_ERROR("Failed to open temp file for writing: " << temp_path);
|
|
1306
1152
|
return false;
|
|
1307
1153
|
}
|
|
1308
|
-
|
|
1154
|
+
|
|
1309
1155
|
// Write file header
|
|
1310
1156
|
// Magic: "RATS" (4 bytes)
|
|
1311
1157
|
// Version: 1 (4 bytes)
|
|
@@ -1313,9 +1159,9 @@ bool StorageManager::write_data_file() {
|
|
|
1313
1159
|
const char* magic = "RATS";
|
|
1314
1160
|
uint32_t version = 1;
|
|
1315
1161
|
uint32_t entry_count = static_cast<uint32_t>(entries_.size());
|
|
1316
|
-
|
|
1162
|
+
|
|
1317
1163
|
fwrite(magic, 1, 4, file);
|
|
1318
|
-
|
|
1164
|
+
|
|
1319
1165
|
uint8_t version_bytes[4] = {
|
|
1320
1166
|
static_cast<uint8_t>((version >> 24) & 0xFF),
|
|
1321
1167
|
static_cast<uint8_t>((version >> 16) & 0xFF),
|
|
@@ -1323,7 +1169,7 @@ bool StorageManager::write_data_file() {
|
|
|
1323
1169
|
static_cast<uint8_t>(version & 0xFF)
|
|
1324
1170
|
};
|
|
1325
1171
|
fwrite(version_bytes, 1, 4, file);
|
|
1326
|
-
|
|
1172
|
+
|
|
1327
1173
|
uint8_t count_bytes[4] = {
|
|
1328
1174
|
static_cast<uint8_t>((entry_count >> 24) & 0xFF),
|
|
1329
1175
|
static_cast<uint8_t>((entry_count >> 16) & 0xFF),
|
|
@@ -1331,29 +1177,29 @@ bool StorageManager::write_data_file() {
|
|
|
1331
1177
|
static_cast<uint8_t>(entry_count & 0xFF)
|
|
1332
1178
|
};
|
|
1333
1179
|
fwrite(count_bytes, 1, 4, file);
|
|
1334
|
-
|
|
1180
|
+
|
|
1335
1181
|
// Write each entry
|
|
1336
1182
|
for (const auto& pair : entries_) {
|
|
1337
1183
|
std::vector<uint8_t> serialized = pair.second.serialize();
|
|
1338
1184
|
fwrite(serialized.data(), 1, serialized.size(), file);
|
|
1339
1185
|
}
|
|
1340
|
-
|
|
1186
|
+
|
|
1341
1187
|
fclose(file);
|
|
1342
|
-
|
|
1188
|
+
|
|
1343
1189
|
// On Windows, rename fails if destination exists, so delete it first
|
|
1344
1190
|
if (file_exists(data_path.c_str())) {
|
|
1345
1191
|
delete_file(data_path.c_str());
|
|
1346
1192
|
}
|
|
1347
|
-
|
|
1193
|
+
|
|
1348
1194
|
// Atomically rename temp file to final
|
|
1349
1195
|
if (!rename_file(temp_path.c_str(), data_path.c_str())) {
|
|
1350
1196
|
LOG_STORAGE_ERROR("Failed to rename temp file to final: " << temp_path << " -> " << data_path);
|
|
1351
1197
|
delete_file(temp_path.c_str());
|
|
1352
1198
|
return false;
|
|
1353
1199
|
}
|
|
1354
|
-
|
|
1200
|
+
|
|
1355
1201
|
return true;
|
|
1356
|
-
|
|
1202
|
+
|
|
1357
1203
|
} catch (const std::exception& e) {
|
|
1358
1204
|
LOG_STORAGE_ERROR("Exception while writing data file: " << e.what());
|
|
1359
1205
|
delete_file(temp_path.c_str());
|
|
@@ -1363,7 +1209,7 @@ bool StorageManager::write_data_file() {
|
|
|
1363
1209
|
|
|
1364
1210
|
bool StorageManager::read_data_file() {
|
|
1365
1211
|
std::string data_path = get_data_file_path();
|
|
1366
|
-
|
|
1212
|
+
|
|
1367
1213
|
try {
|
|
1368
1214
|
size_t file_size;
|
|
1369
1215
|
void* file_data = read_file_binary(data_path.c_str(), &file_size);
|
|
@@ -1371,66 +1217,66 @@ bool StorageManager::read_data_file() {
|
|
|
1371
1217
|
LOG_STORAGE_ERROR("Failed to read data file: " << data_path);
|
|
1372
1218
|
return false;
|
|
1373
1219
|
}
|
|
1374
|
-
|
|
1375
|
-
std::vector<uint8_t> buffer(static_cast<uint8_t*>(file_data),
|
|
1220
|
+
|
|
1221
|
+
std::vector<uint8_t> buffer(static_cast<uint8_t*>(file_data),
|
|
1376
1222
|
static_cast<uint8_t*>(file_data) + file_size);
|
|
1377
1223
|
free_file_buffer(file_data);
|
|
1378
|
-
|
|
1224
|
+
|
|
1379
1225
|
// Read header
|
|
1380
1226
|
if (buffer.size() < 12) {
|
|
1381
1227
|
LOG_STORAGE_ERROR("Data file too small: " << buffer.size());
|
|
1382
1228
|
return false;
|
|
1383
1229
|
}
|
|
1384
|
-
|
|
1230
|
+
|
|
1385
1231
|
// Check magic
|
|
1386
1232
|
if (buffer[0] != 'R' || buffer[1] != 'A' || buffer[2] != 'T' || buffer[3] != 'S') {
|
|
1387
1233
|
LOG_STORAGE_ERROR("Invalid magic in data file");
|
|
1388
1234
|
return false;
|
|
1389
1235
|
}
|
|
1390
|
-
|
|
1236
|
+
|
|
1391
1237
|
// Read version
|
|
1392
1238
|
uint32_t version = (static_cast<uint32_t>(buffer[4]) << 24) |
|
|
1393
1239
|
(static_cast<uint32_t>(buffer[5]) << 16) |
|
|
1394
1240
|
(static_cast<uint32_t>(buffer[6]) << 8) |
|
|
1395
1241
|
static_cast<uint32_t>(buffer[7]);
|
|
1396
|
-
|
|
1242
|
+
|
|
1397
1243
|
if (version != 1) {
|
|
1398
1244
|
LOG_STORAGE_ERROR("Unsupported data file version: " << version);
|
|
1399
1245
|
return false;
|
|
1400
1246
|
}
|
|
1401
|
-
|
|
1247
|
+
|
|
1402
1248
|
// Read entry count
|
|
1403
1249
|
uint32_t entry_count = (static_cast<uint32_t>(buffer[8]) << 24) |
|
|
1404
1250
|
(static_cast<uint32_t>(buffer[9]) << 16) |
|
|
1405
1251
|
(static_cast<uint32_t>(buffer[10]) << 8) |
|
|
1406
1252
|
static_cast<uint32_t>(buffer[11]);
|
|
1407
|
-
|
|
1253
|
+
|
|
1408
1254
|
// Read entries
|
|
1409
1255
|
entries_.clear();
|
|
1410
1256
|
size_t offset = 12;
|
|
1411
|
-
|
|
1257
|
+
|
|
1412
1258
|
for (uint32_t i = 0; i < entry_count && offset < buffer.size(); i++) {
|
|
1413
1259
|
StorageEntry entry;
|
|
1414
1260
|
size_t bytes_read = 0;
|
|
1415
|
-
|
|
1261
|
+
|
|
1416
1262
|
if (!StorageEntry::deserialize(buffer, offset, entry, bytes_read)) {
|
|
1417
1263
|
LOG_STORAGE_ERROR("Failed to deserialize entry " << i << " at offset " << offset);
|
|
1418
1264
|
return false;
|
|
1419
1265
|
}
|
|
1420
|
-
|
|
1266
|
+
|
|
1421
1267
|
// Verify checksum
|
|
1422
1268
|
if (!entry.verify_checksum()) {
|
|
1423
1269
|
LOG_STORAGE_WARN("Checksum mismatch for entry '" << entry.key << "', skipping");
|
|
1424
1270
|
offset += bytes_read;
|
|
1425
1271
|
continue;
|
|
1426
1272
|
}
|
|
1427
|
-
|
|
1273
|
+
|
|
1428
1274
|
entries_[entry.key] = entry;
|
|
1429
1275
|
offset += bytes_read;
|
|
1430
1276
|
}
|
|
1431
|
-
|
|
1277
|
+
|
|
1432
1278
|
return true;
|
|
1433
|
-
|
|
1279
|
+
|
|
1434
1280
|
} catch (const std::exception& e) {
|
|
1435
1281
|
LOG_STORAGE_ERROR("Exception while reading data file: " << e.what());
|
|
1436
1282
|
return false;
|
|
@@ -1448,7 +1294,8 @@ uint64_t StorageManager::get_current_timestamp_ms() const {
|
|
|
1448
1294
|
}
|
|
1449
1295
|
|
|
1450
1296
|
std::string StorageManager::get_our_peer_id() const {
|
|
1451
|
-
|
|
1297
|
+
if (!network_) return "";
|
|
1298
|
+
return network_->local_id().to_hex();
|
|
1452
1299
|
}
|
|
1453
1300
|
|
|
1454
1301
|
void StorageManager::notify_change(const StorageChangeEvent& event) {
|