@peerbit/native-backbone 0.1.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/LICENSE +202 -0
- package/README.md +27 -0
- package/dist/src/benchmark.d.ts +19 -0
- package/dist/src/benchmark.d.ts.map +1 -0
- package/dist/src/benchmark.js +12 -0
- package/dist/src/benchmark.js.map +1 -0
- package/dist/src/index.d.ts +1561 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +3406 -0
- package/dist/src/index.js.map +1 -0
- package/dist/wasm/README.md +27 -0
- package/dist/wasm/native_backbone.d.ts +1609 -0
- package/dist/wasm/native_backbone.js +10221 -0
- package/dist/wasm/native_backbone_bg.wasm +0 -0
- package/dist/wasm/native_backbone_bg.wasm.d.ts +625 -0
- package/package.json +64 -0
- package/src/append_tx/committed_latest.rs +2063 -0
- package/src/append_tx/committed_no_next.rs +1165 -0
- package/src/append_tx/facts.rs +791 -0
- package/src/append_tx/mod.rs +751 -0
- package/src/append_tx/storage.rs +599 -0
- package/src/benchmark.ts +58 -0
- package/src/coordinates.rs +422 -0
- package/src/documents.rs +1698 -0
- package/src/graph_blocks.rs +362 -0
- package/src/index.ts +9134 -0
- package/src/js_interop.rs +448 -0
- package/src/lib.rs +144 -0
- package/src/profile.rs +164 -0
- package/src/raw_receive.rs +1724 -0
- package/src/shared_log_plan.rs +1464 -0
- package/src/sync_send.rs +180 -0
- package/src/wire_sync.rs +727 -0
package/src/sync_send.rs
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
//! Send fusion for shared-log raw exchange-heads sync.
|
|
2
|
+
//!
|
|
3
|
+
//! The outbound counterpart of `wire_sync.rs`: the full
|
|
4
|
+
//! `PubSubData → RequestV0 → DecryptedThing → RawExchangeHeadsMessage` payload
|
|
5
|
+
//! is serialized inside this wasm module by `peerbit_wire::sync_payload`,
|
|
6
|
+
//! reading the entry block bytes straight from the native block store the
|
|
7
|
+
//! append/receive paths already committed them to. The only boundary crossing
|
|
8
|
+
//! is the single finished payload buffer handed to JS to become the
|
|
9
|
+
//! `DataMessage` payload — entry block bytes never materialize as JS values on
|
|
10
|
+
//! the send path.
|
|
11
|
+
//!
|
|
12
|
+
//! The store-reading core is `JsValue`-free so host `cargo test` covers it.
|
|
13
|
+
|
|
14
|
+
use js_sys::{Array, Uint32Array, Uint8Array};
|
|
15
|
+
use peerbit_log_rust::NativeLogBlockStore;
|
|
16
|
+
use peerbit_wire::sync_payload::{encode_raw_exchange_sync_payload_refs, SyncPayloadHeadRef};
|
|
17
|
+
use wasm_bindgen::prelude::*;
|
|
18
|
+
|
|
19
|
+
use crate::js_interop::{ensure_same_len, string_batches_from_array, strings_from_array};
|
|
20
|
+
use crate::NativePeerbitBackbone;
|
|
21
|
+
|
|
22
|
+
/// Marks a missing block in `block_byte_lengths` results (a real entry block
|
|
23
|
+
/// cannot reach this size: message payloads are bounded far below it).
|
|
24
|
+
pub(crate) const SYNC_SEND_MISSING_BLOCK: u32 = u32::MAX;
|
|
25
|
+
|
|
26
|
+
/// Encode the outbound raw exchange sync payload for `hashes`, resolving each
|
|
27
|
+
/// head's block bytes from `blocks`. Returns `None` when any block is missing
|
|
28
|
+
/// (callers fall back to the TS send path).
|
|
29
|
+
pub(crate) fn encode_sync_payload_from_store(
|
|
30
|
+
blocks: &NativeLogBlockStore,
|
|
31
|
+
topic: &str,
|
|
32
|
+
strict: bool,
|
|
33
|
+
hashes: &[String],
|
|
34
|
+
gid_refrences: &[Vec<String>],
|
|
35
|
+
reserved: [u8; 4],
|
|
36
|
+
) -> Option<Vec<u8>> {
|
|
37
|
+
const EMPTY_REFS: &[String] = &[];
|
|
38
|
+
let mut heads = Vec::with_capacity(hashes.len());
|
|
39
|
+
for (index, hash) in hashes.iter().enumerate() {
|
|
40
|
+
let bytes = blocks.get_ref(hash)?;
|
|
41
|
+
heads.push(SyncPayloadHeadRef {
|
|
42
|
+
hash,
|
|
43
|
+
bytes,
|
|
44
|
+
gid_refrences: gid_refrences
|
|
45
|
+
.get(index)
|
|
46
|
+
.map(Vec::as_slice)
|
|
47
|
+
.unwrap_or(EMPTY_REFS),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
Some(encode_raw_exchange_sync_payload_refs(
|
|
51
|
+
&[topic.to_string()],
|
|
52
|
+
strict,
|
|
53
|
+
&heads,
|
|
54
|
+
reserved,
|
|
55
|
+
))
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
pub(crate) fn block_byte_lengths_core(blocks: &NativeLogBlockStore, hashes: &[String]) -> Vec<u32> {
|
|
59
|
+
hashes
|
|
60
|
+
.iter()
|
|
61
|
+
.map(|hash| {
|
|
62
|
+
blocks
|
|
63
|
+
.get_ref(hash)
|
|
64
|
+
.map(|bytes| bytes.len() as u32)
|
|
65
|
+
.unwrap_or(SYNC_SEND_MISSING_BLOCK)
|
|
66
|
+
})
|
|
67
|
+
.collect()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#[wasm_bindgen]
|
|
71
|
+
impl NativePeerbitBackbone {
|
|
72
|
+
/// Byte lengths of natively stored entry blocks for `hashes`;
|
|
73
|
+
/// `u32::MAX` marks a missing block. Used by the fused send path to plan
|
|
74
|
+
/// message chunking without materializing block bytes in JS.
|
|
75
|
+
pub fn sync_send_block_byte_lengths(&self, hashes: Array) -> Result<Uint32Array, JsValue> {
|
|
76
|
+
let hashes = strings_from_array(hashes)?;
|
|
77
|
+
Ok(Uint32Array::from(
|
|
78
|
+
block_byte_lengths_core(&self.blocks, &hashes).as_slice(),
|
|
79
|
+
))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/// Serialize one outbound raw exchange sync payload (the full PubSubData
|
|
83
|
+
/// nesting) from the native block store. Returns `undefined` when any
|
|
84
|
+
/// head's block is not natively stored (the caller falls back to the TS
|
|
85
|
+
/// serialization path).
|
|
86
|
+
pub fn encode_raw_exchange_sync_payload(
|
|
87
|
+
&self,
|
|
88
|
+
topic: &str,
|
|
89
|
+
strict: bool,
|
|
90
|
+
hashes: Array,
|
|
91
|
+
gid_refrences: Array,
|
|
92
|
+
reserved: &[u8],
|
|
93
|
+
) -> Result<JsValue, JsValue> {
|
|
94
|
+
let hashes = strings_from_array(hashes)?;
|
|
95
|
+
let gid_refrences = string_batches_from_array(gid_refrences, "sync send gid references")?;
|
|
96
|
+
ensure_same_len(hashes.len(), gid_refrences.len(), "sync send heads")?;
|
|
97
|
+
let reserved: [u8; 4] = reserved
|
|
98
|
+
.try_into()
|
|
99
|
+
.map_err(|_| JsValue::from_str("expected 4 reserved bytes"))?;
|
|
100
|
+
match encode_sync_payload_from_store(
|
|
101
|
+
&self.blocks,
|
|
102
|
+
topic,
|
|
103
|
+
strict,
|
|
104
|
+
&hashes,
|
|
105
|
+
&gid_refrences,
|
|
106
|
+
reserved,
|
|
107
|
+
) {
|
|
108
|
+
Some(payload) => Ok(Uint8Array::from(payload.as_slice()).into()),
|
|
109
|
+
None => Ok(JsValue::UNDEFINED),
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
#[cfg(test)]
|
|
115
|
+
mod tests {
|
|
116
|
+
use super::*;
|
|
117
|
+
use peerbit_wire::sync_payload::{
|
|
118
|
+
encode_raw_exchange_sync_payload, parse_pubsub_data, parse_raw_exchange_rpc_request,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
fn store_with(entries: &[(&str, Vec<u8>)]) -> NativeLogBlockStore {
|
|
122
|
+
let mut store = NativeLogBlockStore::new();
|
|
123
|
+
store.put_entries_core(
|
|
124
|
+
entries
|
|
125
|
+
.iter()
|
|
126
|
+
.map(|(hash, bytes)| (hash.to_string(), bytes.clone()))
|
|
127
|
+
.collect(),
|
|
128
|
+
);
|
|
129
|
+
store
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
#[test]
|
|
133
|
+
fn encodes_store_blocks_byte_identical_to_owned_encoder() {
|
|
134
|
+
let store = store_with(&[("zb2AA", vec![0xde, 0xad]), ("zb2BB", vec![1, 2, 3])]);
|
|
135
|
+
let hashes = vec!["zb2AA".to_string(), "zb2BB".to_string()];
|
|
136
|
+
let gid_refrences = vec![vec!["g1".to_string()], Vec::new()];
|
|
137
|
+
let payload = encode_sync_payload_from_store(
|
|
138
|
+
&store,
|
|
139
|
+
"topic",
|
|
140
|
+
true,
|
|
141
|
+
&hashes,
|
|
142
|
+
&gid_refrences,
|
|
143
|
+
[1, 0, 0, 0],
|
|
144
|
+
)
|
|
145
|
+
.unwrap();
|
|
146
|
+
let expected = encode_raw_exchange_sync_payload(
|
|
147
|
+
&["topic".to_string()],
|
|
148
|
+
true,
|
|
149
|
+
&[
|
|
150
|
+
(
|
|
151
|
+
"zb2AA".to_string(),
|
|
152
|
+
vec![0xde, 0xad],
|
|
153
|
+
vec!["g1".to_string()],
|
|
154
|
+
),
|
|
155
|
+
("zb2BB".to_string(), vec![1, 2, 3], Vec::new()),
|
|
156
|
+
],
|
|
157
|
+
[1, 0, 0, 0],
|
|
158
|
+
);
|
|
159
|
+
assert_eq!(payload, expected);
|
|
160
|
+
|
|
161
|
+
let pubsub = parse_pubsub_data(&payload).unwrap();
|
|
162
|
+
let data = &payload[pubsub.data_offset..pubsub.data_offset + pubsub.data_length];
|
|
163
|
+
let parsed = parse_raw_exchange_rpc_request(data).unwrap();
|
|
164
|
+
assert_eq!(parsed.heads.len(), 2);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
#[test]
|
|
168
|
+
fn missing_blocks_fall_back() {
|
|
169
|
+
let store = store_with(&[("zb2AA", vec![0xde])]);
|
|
170
|
+
let hashes = vec!["zb2AA".to_string(), "zb2MISSING".to_string()];
|
|
171
|
+
let refs = vec![Vec::new(), Vec::new()];
|
|
172
|
+
assert!(
|
|
173
|
+
encode_sync_payload_from_store(&store, "t", true, &hashes, &refs, [0; 4]).is_none()
|
|
174
|
+
);
|
|
175
|
+
assert_eq!(
|
|
176
|
+
block_byte_lengths_core(&store, &hashes),
|
|
177
|
+
vec![1, SYNC_SEND_MISSING_BLOCK]
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
}
|