@peerbit/native-backbone 0.1.1 → 0.1.3
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/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +18 -14
- package/dist/src/index.js.map +1 -1
- package/dist/wasm/native_backbone.d.ts +64 -64
- package/dist/wasm/native_backbone.js +3 -0
- package/dist/wasm/native_backbone_bg.wasm +0 -0
- package/dist/wasm/native_backbone_bg.wasm.d.ts +64 -64
- package/package.json +1 -1
- package/src/append_tx/committed_latest.rs +390 -317
- package/src/append_tx/committed_no_next.rs +66 -64
- package/src/append_tx/facts.rs +213 -147
- package/src/append_tx/mod.rs +160 -58
- package/src/append_tx/storage.rs +52 -28
- package/src/coordinates.rs +135 -44
- package/src/documents.rs +340 -100
- package/src/error.rs +244 -0
- package/src/index.ts +25 -21
- package/src/js_interop.rs +230 -90
- package/src/lib.rs +4 -0
- package/src/raw_receive.rs +182 -107
- package/src/shared_log_plan.rs +65 -23
- package/src/sync_send.rs +19 -3
- package/src/time.rs +14 -0
- package/src/wire_sync.rs +147 -36
package/src/js_interop.rs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
use crate::error::BackboneError;
|
|
1
2
|
use js_sys::{Array, Reflect, Uint8Array};
|
|
2
|
-
use peerbit_indexer_core::wire
|
|
3
|
+
use peerbit_indexer_core::wire;
|
|
3
4
|
use std::collections::HashSet;
|
|
4
5
|
use wasm_bindgen::prelude::*;
|
|
5
6
|
use wasm_bindgen::JsCast;
|
|
@@ -22,51 +23,97 @@ pub(crate) fn clear_journal_prefix(
|
|
|
22
23
|
*journal_record_count = journal_record_count.saturating_sub(record_count);
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
/// Exclusive upper bound for f64 → u64 conversion. `u64::MAX as f64` rounds
|
|
27
|
+
/// UP to 2^64 (exactly representable), so a `> u64::MAX as f64` check admits
|
|
28
|
+
/// 2^64 itself, which an `as` cast then saturates to `u64::MAX`. Valid u64
|
|
29
|
+
/// values in f64 form are exactly the integers in [0, 2^64).
|
|
30
|
+
const F64_U64_EXCLUSIVE_BOUND: f64 = 18_446_744_073_709_551_616.0; // 2^64
|
|
31
|
+
|
|
32
|
+
/// Rejects non-integral, negative, non-finite and out-of-range values instead
|
|
33
|
+
/// of silently truncating them with an `as` cast.
|
|
34
|
+
pub(crate) fn checked_usize_from_f64(value: f64) -> Option<usize> {
|
|
35
|
+
checked_u64_from_f64(value).and_then(|v| usize::try_from(v).ok())
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/// Rejects non-integral, negative, non-finite and out-of-range values instead
|
|
39
|
+
/// of silently truncating them with an `as` cast.
|
|
40
|
+
pub(crate) fn checked_u64_from_f64(value: f64) -> Option<u64> {
|
|
41
|
+
if !value.is_finite() || value < 0.0 || value.fract() != 0.0 || value >= F64_U64_EXCLUSIVE_BOUND
|
|
42
|
+
{
|
|
43
|
+
return None;
|
|
44
|
+
}
|
|
45
|
+
Some(value as u64)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
pub(crate) fn array_from_value(
|
|
49
|
+
value: JsValue,
|
|
50
|
+
label: &'static str,
|
|
51
|
+
) -> Result<Array, BackboneError> {
|
|
26
52
|
value
|
|
27
53
|
.dyn_into::<Array>()
|
|
28
|
-
.map_err(|_|
|
|
54
|
+
.map_err(|_| BackboneError::ExpectedArray(label))
|
|
29
55
|
}
|
|
30
56
|
|
|
31
|
-
pub(crate) fn string_field(
|
|
57
|
+
pub(crate) fn string_field(
|
|
58
|
+
row: &Array,
|
|
59
|
+
index: u32,
|
|
60
|
+
label: &'static str,
|
|
61
|
+
) -> Result<String, BackboneError> {
|
|
32
62
|
row.get(index)
|
|
33
63
|
.as_string()
|
|
34
|
-
.
|
|
64
|
+
.ok_or(BackboneError::ExpectedString(label))
|
|
35
65
|
}
|
|
36
66
|
|
|
37
|
-
pub(crate) fn stringish_field(
|
|
67
|
+
pub(crate) fn stringish_field(
|
|
68
|
+
row: &Array,
|
|
69
|
+
index: u32,
|
|
70
|
+
label: &'static str,
|
|
71
|
+
) -> Result<String, BackboneError> {
|
|
38
72
|
let value = row.get(index);
|
|
39
73
|
if let Some(value) = value.as_string() {
|
|
40
74
|
return Ok(value);
|
|
41
75
|
}
|
|
42
76
|
if let Some(value) = value.as_f64() {
|
|
43
|
-
|
|
77
|
+
if let Some(value) = checked_u64_from_f64(value) {
|
|
78
|
+
return Ok(value.to_string());
|
|
79
|
+
}
|
|
44
80
|
}
|
|
45
|
-
Err(
|
|
81
|
+
Err(BackboneError::ExpectedString(label))
|
|
46
82
|
}
|
|
47
83
|
|
|
48
|
-
pub(crate) fn bool_field(
|
|
84
|
+
pub(crate) fn bool_field(
|
|
85
|
+
row: &Array,
|
|
86
|
+
index: u32,
|
|
87
|
+
label: &'static str,
|
|
88
|
+
) -> Result<bool, BackboneError> {
|
|
49
89
|
row.get(index)
|
|
50
90
|
.as_bool()
|
|
51
|
-
.
|
|
91
|
+
.ok_or(BackboneError::ExpectedBoolean(label))
|
|
52
92
|
}
|
|
53
93
|
|
|
54
|
-
pub(crate) fn usize_field(
|
|
94
|
+
pub(crate) fn usize_field(
|
|
95
|
+
row: &Array,
|
|
96
|
+
index: u32,
|
|
97
|
+
label: &'static str,
|
|
98
|
+
) -> Result<usize, BackboneError> {
|
|
55
99
|
row.get(index)
|
|
56
100
|
.as_f64()
|
|
57
|
-
.
|
|
58
|
-
.
|
|
101
|
+
.and_then(checked_usize_from_f64)
|
|
102
|
+
.ok_or(BackboneError::ExpectedNumber(label))
|
|
59
103
|
}
|
|
60
104
|
|
|
61
|
-
pub(crate) fn bytes_field(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
105
|
+
pub(crate) fn bytes_field(
|
|
106
|
+
row: &Array,
|
|
107
|
+
index: u32,
|
|
108
|
+
label: &'static str,
|
|
109
|
+
) -> Result<Vec<u8>, BackboneError> {
|
|
110
|
+
row.get(index)
|
|
111
|
+
.dyn_ref::<Uint8Array>()
|
|
112
|
+
.map(Uint8Array::to_vec)
|
|
113
|
+
.ok_or(BackboneError::ExpectedBytes(label))
|
|
67
114
|
}
|
|
68
115
|
|
|
69
|
-
pub(crate) fn trim_hashes_vec(trim_rows: &Array) -> Result<Vec<String>,
|
|
116
|
+
pub(crate) fn trim_hashes_vec(trim_rows: &Array) -> Result<Vec<String>, BackboneError> {
|
|
70
117
|
let mut hashes = Vec::with_capacity(trim_rows.length() as usize);
|
|
71
118
|
for index in 0..trim_rows.length() {
|
|
72
119
|
let row = array_from_value(trim_rows.get(index), "trim row")?;
|
|
@@ -111,27 +158,29 @@ pub(crate) fn strings_slice_to_array(values: &[String]) -> Array {
|
|
|
111
158
|
out
|
|
112
159
|
}
|
|
113
160
|
|
|
114
|
-
pub(crate) fn strings_from_array(values: Array) -> Result<Vec<String>,
|
|
161
|
+
pub(crate) fn strings_from_array(values: Array) -> Result<Vec<String>, BackboneError> {
|
|
115
162
|
let mut out = Vec::with_capacity(values.length() as usize);
|
|
116
163
|
for index in 0..values.length() {
|
|
117
164
|
out.push(
|
|
118
165
|
values
|
|
119
166
|
.get(index)
|
|
120
167
|
.as_string()
|
|
121
|
-
.
|
|
168
|
+
.ok_or(BackboneError::ExpectedStringArray)?,
|
|
122
169
|
);
|
|
123
170
|
}
|
|
124
171
|
Ok(out)
|
|
125
172
|
}
|
|
126
173
|
|
|
127
|
-
pub(crate) fn bytes_vec_from_array(values: Array) -> Result<Vec<Vec<u8>>,
|
|
174
|
+
pub(crate) fn bytes_vec_from_array(values: Array) -> Result<Vec<Vec<u8>>, BackboneError> {
|
|
128
175
|
let mut out = Vec::with_capacity(values.length() as usize);
|
|
129
176
|
for index in 0..values.length() {
|
|
130
177
|
let value = values.get(index);
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
178
|
+
out.push(
|
|
179
|
+
value
|
|
180
|
+
.dyn_ref::<Uint8Array>()
|
|
181
|
+
.map(Uint8Array::to_vec)
|
|
182
|
+
.ok_or(BackboneError::ExpectedBytesArray)?,
|
|
183
|
+
);
|
|
135
184
|
}
|
|
136
185
|
Ok(out)
|
|
137
186
|
}
|
|
@@ -139,73 +188,79 @@ pub(crate) fn bytes_vec_from_array(values: Array) -> Result<Vec<Vec<u8>>, JsValu
|
|
|
139
188
|
pub(crate) fn required_bytes_from_array(
|
|
140
189
|
values: &Array,
|
|
141
190
|
index: u32,
|
|
142
|
-
field: &str,
|
|
143
|
-
) -> Result<Uint8Array,
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
Ok(Uint8Array::new(&value))
|
|
191
|
+
field: &'static str,
|
|
192
|
+
) -> Result<Uint8Array, BackboneError> {
|
|
193
|
+
values
|
|
194
|
+
.get(index)
|
|
195
|
+
.dyn_into::<Uint8Array>()
|
|
196
|
+
.map_err(|_| BackboneError::ExpectedBytes(field))
|
|
149
197
|
}
|
|
150
198
|
|
|
151
199
|
pub(crate) fn string_batches_from_array(
|
|
152
200
|
values: Array,
|
|
153
|
-
label: &str,
|
|
154
|
-
) -> Result<Vec<Vec<String>>,
|
|
201
|
+
label: &'static str,
|
|
202
|
+
) -> Result<Vec<Vec<String>>, BackboneError> {
|
|
155
203
|
let mut out = Vec::with_capacity(values.length() as usize);
|
|
156
204
|
for index in 0..values.length() {
|
|
157
205
|
let value = values.get(index);
|
|
158
206
|
if !Array::is_array(&value) {
|
|
159
|
-
return Err(
|
|
207
|
+
return Err(BackboneError::Expected(label));
|
|
160
208
|
}
|
|
161
209
|
out.push(strings_from_array(Array::from(&value))?);
|
|
162
210
|
}
|
|
163
211
|
Ok(out)
|
|
164
212
|
}
|
|
165
213
|
|
|
166
|
-
pub(crate) fn usize_values_from_array(values: Array) -> Result<Vec<usize>,
|
|
214
|
+
pub(crate) fn usize_values_from_array(values: Array) -> Result<Vec<usize>, BackboneError> {
|
|
167
215
|
let mut out = Vec::with_capacity(values.length() as usize);
|
|
168
216
|
for index in 0..values.length() {
|
|
169
217
|
let value = values
|
|
170
218
|
.get(index)
|
|
171
219
|
.as_f64()
|
|
172
|
-
.
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
out.push(value as usize);
|
|
220
|
+
.and_then(checked_usize_from_f64)
|
|
221
|
+
.ok_or(BackboneError::ExpectedUnsignedIntegerArray)?;
|
|
222
|
+
out.push(value);
|
|
177
223
|
}
|
|
178
224
|
Ok(out)
|
|
179
225
|
}
|
|
180
226
|
|
|
181
|
-
pub(crate) fn ensure_same_len(
|
|
227
|
+
pub(crate) fn ensure_same_len(
|
|
228
|
+
left: usize,
|
|
229
|
+
right: usize,
|
|
230
|
+
label: &'static str,
|
|
231
|
+
) -> Result<(), BackboneError> {
|
|
182
232
|
if left == right {
|
|
183
233
|
Ok(())
|
|
184
234
|
} else {
|
|
185
|
-
Err(
|
|
186
|
-
"Mismatched {label} input lengths"
|
|
187
|
-
)))
|
|
235
|
+
Err(BackboneError::MismatchedInputLengths(label))
|
|
188
236
|
}
|
|
189
237
|
}
|
|
190
238
|
|
|
191
|
-
pub(crate) fn optional_bytes_from_js(
|
|
239
|
+
pub(crate) fn optional_bytes_from_js(
|
|
240
|
+
value: JsValue,
|
|
241
|
+
label: &'static str,
|
|
242
|
+
) -> Result<Option<Vec<u8>>, BackboneError> {
|
|
192
243
|
if value.is_undefined() || value.is_null() {
|
|
193
|
-
return None;
|
|
244
|
+
return Ok(None);
|
|
194
245
|
}
|
|
195
|
-
|
|
246
|
+
value
|
|
247
|
+
.dyn_ref::<Uint8Array>()
|
|
248
|
+
.map(|value| Some(value.to_vec()))
|
|
249
|
+
.ok_or(BackboneError::ExpectedBytes(label))
|
|
196
250
|
}
|
|
197
251
|
|
|
198
252
|
pub(crate) fn optional_usize_from_js(
|
|
199
253
|
value: JsValue,
|
|
200
|
-
label: &str,
|
|
201
|
-
) -> Result<Option<usize>,
|
|
254
|
+
label: &'static str,
|
|
255
|
+
) -> Result<Option<usize>, BackboneError> {
|
|
202
256
|
if value.is_undefined() || value.is_null() {
|
|
203
257
|
return Ok(None);
|
|
204
258
|
}
|
|
205
259
|
value
|
|
206
260
|
.as_f64()
|
|
207
|
-
.
|
|
208
|
-
.
|
|
261
|
+
.and_then(checked_usize_from_f64)
|
|
262
|
+
.map(Some)
|
|
263
|
+
.ok_or(BackboneError::MustBeNumber(label))
|
|
209
264
|
}
|
|
210
265
|
|
|
211
266
|
pub(crate) fn number_strings_to_array(values: &[u64]) -> Array {
|
|
@@ -216,13 +271,16 @@ pub(crate) fn number_strings_to_array(values: &[u64]) -> Array {
|
|
|
216
271
|
out
|
|
217
272
|
}
|
|
218
273
|
|
|
219
|
-
pub(crate) fn parse_u64_string(value: &str, label: &str) -> Result<u64,
|
|
274
|
+
pub(crate) fn parse_u64_string(value: &str, label: &'static str) -> Result<u64, BackboneError> {
|
|
220
275
|
value
|
|
221
276
|
.parse::<u64>()
|
|
222
|
-
.map_err(|_|
|
|
277
|
+
.map_err(|_| BackboneError::ExpectedU64String(label))
|
|
223
278
|
}
|
|
224
279
|
|
|
225
|
-
pub(crate) fn parse_optional_u64_string(
|
|
280
|
+
pub(crate) fn parse_optional_u64_string(
|
|
281
|
+
value: &str,
|
|
282
|
+
label: &'static str,
|
|
283
|
+
) -> Result<Option<u64>, BackboneError> {
|
|
226
284
|
if value.is_empty() {
|
|
227
285
|
Ok(None)
|
|
228
286
|
} else {
|
|
@@ -308,55 +366,54 @@ pub(crate) fn write_bytes(out: &mut Vec<u8>, value: &[u8]) {
|
|
|
308
366
|
out.extend_from_slice(value);
|
|
309
367
|
}
|
|
310
368
|
|
|
311
|
-
pub(crate) fn wire_error_to_js(error: WireError) -> JsValue {
|
|
312
|
-
JsValue::from_str(&error.to_string())
|
|
313
|
-
}
|
|
314
|
-
|
|
315
369
|
pub(crate) fn read_u32(
|
|
316
370
|
bytes: &[u8],
|
|
317
371
|
offset: &mut usize,
|
|
318
372
|
label: &'static str,
|
|
319
|
-
) -> Result<u32,
|
|
320
|
-
wire::read_u32(bytes, offset, label)
|
|
373
|
+
) -> Result<u32, BackboneError> {
|
|
374
|
+
Ok(wire::read_u32(bytes, offset, label)?)
|
|
321
375
|
}
|
|
322
376
|
|
|
323
377
|
pub(crate) fn read_u64(
|
|
324
378
|
bytes: &[u8],
|
|
325
379
|
offset: &mut usize,
|
|
326
380
|
label: &'static str,
|
|
327
|
-
) -> Result<u64,
|
|
328
|
-
wire::read_u64(bytes, offset, label)
|
|
381
|
+
) -> Result<u64, BackboneError> {
|
|
382
|
+
Ok(wire::read_u64(bytes, offset, label)?)
|
|
329
383
|
}
|
|
330
384
|
|
|
331
385
|
pub(crate) fn read_encoded_string(
|
|
332
386
|
bytes: &[u8],
|
|
333
387
|
offset: &mut usize,
|
|
334
388
|
label: &'static str,
|
|
335
|
-
) -> Result<String,
|
|
336
|
-
wire::read_encoded_string(bytes, offset, label)
|
|
389
|
+
) -> Result<String, BackboneError> {
|
|
390
|
+
Ok(wire::read_encoded_string(bytes, offset, label)?)
|
|
337
391
|
}
|
|
338
392
|
|
|
339
393
|
pub(crate) fn read_bytes(
|
|
340
394
|
bytes: &[u8],
|
|
341
395
|
offset: &mut usize,
|
|
342
396
|
label: &'static str,
|
|
343
|
-
) -> Result<Vec<u8>,
|
|
344
|
-
wire::read_bytes(bytes, offset, label)
|
|
397
|
+
) -> Result<Vec<u8>, BackboneError> {
|
|
398
|
+
Ok(wire::read_bytes(bytes, offset, label)?)
|
|
345
399
|
}
|
|
346
400
|
|
|
347
401
|
pub(crate) fn js_get(value: &JsValue, key: &str) -> JsValue {
|
|
348
402
|
Reflect::get(value, &JsValue::from_str(key)).unwrap_or(JsValue::UNDEFINED)
|
|
349
403
|
}
|
|
350
404
|
|
|
351
|
-
fn js_string(value: JsValue, field: &str) -> Result<String,
|
|
405
|
+
fn js_string(value: JsValue, field: &'static str) -> Result<String, BackboneError> {
|
|
352
406
|
value
|
|
353
407
|
.as_string()
|
|
354
|
-
.
|
|
408
|
+
.ok_or(BackboneError::MissingOrInvalid(field))
|
|
355
409
|
}
|
|
356
410
|
|
|
357
|
-
pub(crate) fn array_strings(
|
|
411
|
+
pub(crate) fn array_strings(
|
|
412
|
+
value: JsValue,
|
|
413
|
+
field: &'static str,
|
|
414
|
+
) -> Result<Vec<String>, BackboneError> {
|
|
358
415
|
if !Array::is_array(&value) {
|
|
359
|
-
return Err(
|
|
416
|
+
return Err(BackboneError::MustBeArray(field));
|
|
360
417
|
}
|
|
361
418
|
let array = Array::from(&value);
|
|
362
419
|
let mut out = Vec::with_capacity(array.length() as usize);
|
|
@@ -366,12 +423,17 @@ pub(crate) fn array_strings(value: JsValue, field: &str) -> Result<Vec<String>,
|
|
|
366
423
|
Ok(out)
|
|
367
424
|
}
|
|
368
425
|
|
|
369
|
-
pub(crate) fn optional_string(
|
|
426
|
+
pub(crate) fn optional_string(
|
|
427
|
+
value: JsValue,
|
|
428
|
+
field: &'static str,
|
|
429
|
+
) -> Result<Option<String>, BackboneError> {
|
|
370
430
|
if value.is_null() || value.is_undefined() {
|
|
371
|
-
None
|
|
372
|
-
} else {
|
|
373
|
-
value.as_string()
|
|
431
|
+
return Ok(None);
|
|
374
432
|
}
|
|
433
|
+
value
|
|
434
|
+
.as_string()
|
|
435
|
+
.map(Some)
|
|
436
|
+
.ok_or(BackboneError::MissingOrInvalid(field))
|
|
375
437
|
}
|
|
376
438
|
|
|
377
439
|
pub(crate) fn write_u8(out: &mut Vec<u8>, value: u8) {
|
|
@@ -382,38 +444,36 @@ pub(crate) fn write_bool(out: &mut Vec<u8>, value: bool) {
|
|
|
382
444
|
out.push(if value { 1 } else { 0 });
|
|
383
445
|
}
|
|
384
446
|
|
|
385
|
-
pub(crate) fn
|
|
386
|
-
JsValue::from_str(&error.to_string())
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
pub(crate) fn decode_error(error: impl std::fmt::Display) -> JsValue {
|
|
390
|
-
JsValue::from_str(&error.to_string())
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
pub(crate) fn hash_number_u64(resolution: &str, digest: &[u8]) -> Result<u64, JsValue> {
|
|
447
|
+
pub(crate) fn hash_number_u64(resolution: &str, digest: &[u8]) -> Result<u64, BackboneError> {
|
|
394
448
|
match resolution {
|
|
395
449
|
"u32" => {
|
|
396
450
|
if digest.len() < 4 {
|
|
397
|
-
return Err(
|
|
451
|
+
return Err(BackboneError::HashDigestTooShortU32);
|
|
398
452
|
}
|
|
399
453
|
Ok(u32::from_le_bytes(digest[0..4].try_into().unwrap()) as u64)
|
|
400
454
|
}
|
|
401
455
|
"u64" => {
|
|
402
456
|
if digest.len() < 8 {
|
|
403
|
-
return Err(
|
|
457
|
+
return Err(BackboneError::HashDigestTooShortU64);
|
|
404
458
|
}
|
|
405
459
|
Ok(u64::from_le_bytes(digest[0..8].try_into().unwrap()))
|
|
406
460
|
}
|
|
407
|
-
_ => Err(
|
|
461
|
+
_ => Err(BackboneError::ResolutionMustBeU32OrU64),
|
|
408
462
|
}
|
|
409
463
|
}
|
|
410
464
|
|
|
411
465
|
#[cfg(test)]
|
|
412
466
|
mod tests {
|
|
413
|
-
use super::{
|
|
467
|
+
use super::{
|
|
468
|
+
append_journal_delete_record, append_journal_put_record, checked_u64_from_f64,
|
|
469
|
+
checked_usize_from_f64, ensure_same_len, hash_number_u64, parse_optional_u64_string,
|
|
470
|
+
parse_u64_string, read_u32,
|
|
471
|
+
};
|
|
472
|
+
use crate::error::BackboneError;
|
|
414
473
|
use peerbit_indexer_core::persistence::{
|
|
415
474
|
encode_journal_delete_record, encode_journal_put_record,
|
|
416
475
|
};
|
|
476
|
+
use peerbit_indexer_core::wire::WireError;
|
|
417
477
|
|
|
418
478
|
#[test]
|
|
419
479
|
fn decodes_hash_numbers_like_shared_log_integer_helpers() {
|
|
@@ -422,6 +482,86 @@ mod tests {
|
|
|
422
482
|
assert_eq!(hash_number_u64("u64", &bytes).unwrap(), 8_589_934_593);
|
|
423
483
|
}
|
|
424
484
|
|
|
485
|
+
#[test]
|
|
486
|
+
fn hash_number_u64_reports_typed_errors() {
|
|
487
|
+
let error = hash_number_u64("u32", &[1, 2, 3]).unwrap_err();
|
|
488
|
+
assert_eq!(error, BackboneError::HashDigestTooShortU32);
|
|
489
|
+
assert_eq!(error.to_string(), "hash digest must have at least 4 bytes");
|
|
490
|
+
|
|
491
|
+
let error = hash_number_u64("u64", &[1, 2, 3, 4]).unwrap_err();
|
|
492
|
+
assert_eq!(error, BackboneError::HashDigestTooShortU64);
|
|
493
|
+
assert_eq!(error.to_string(), "hash digest must have at least 8 bytes");
|
|
494
|
+
|
|
495
|
+
let error = hash_number_u64("u128", &[0; 16]).unwrap_err();
|
|
496
|
+
assert_eq!(error, BackboneError::ResolutionMustBeU32OrU64);
|
|
497
|
+
assert_eq!(error.to_string(), "resolution must be u32 or u64");
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
#[test]
|
|
501
|
+
fn wire_reads_report_typed_errors() {
|
|
502
|
+
let mut offset = 0usize;
|
|
503
|
+
let error = read_u32(&[1, 2], &mut offset, "coordinate count").unwrap_err();
|
|
504
|
+
assert_eq!(
|
|
505
|
+
error,
|
|
506
|
+
BackboneError::Wire(WireError::Truncated("coordinate count"))
|
|
507
|
+
);
|
|
508
|
+
assert_eq!(error.to_string(), "Truncated coordinate count");
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
#[test]
|
|
512
|
+
fn parse_u64_string_reports_typed_errors() {
|
|
513
|
+
assert_eq!(parse_u64_string("42", "coordinate").unwrap(), 42);
|
|
514
|
+
assert_eq!(parse_optional_u64_string("", "coordinate").unwrap(), None);
|
|
515
|
+
|
|
516
|
+
let error = parse_u64_string("not-a-number", "coordinate").unwrap_err();
|
|
517
|
+
assert_eq!(error, BackboneError::ExpectedU64String("coordinate"));
|
|
518
|
+
assert_eq!(error.to_string(), "Expected coordinate u64 string");
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
#[test]
|
|
522
|
+
fn ensure_same_len_reports_typed_errors() {
|
|
523
|
+
assert_eq!(ensure_same_len(2, 2, "batch gids"), Ok(()));
|
|
524
|
+
|
|
525
|
+
let error = ensure_same_len(1, 2, "batch gids").unwrap_err();
|
|
526
|
+
assert_eq!(error, BackboneError::MismatchedInputLengths("batch gids"));
|
|
527
|
+
assert_eq!(error.to_string(), "Mismatched batch gids input lengths");
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
#[test]
|
|
531
|
+
fn checked_integer_conversions_reject_invalid_numbers() {
|
|
532
|
+
assert_eq!(checked_usize_from_f64(0.0), Some(0));
|
|
533
|
+
assert_eq!(checked_usize_from_f64(3.0), Some(3));
|
|
534
|
+
assert_eq!(checked_usize_from_f64(-1.0), None);
|
|
535
|
+
assert_eq!(checked_usize_from_f64(1.5), None);
|
|
536
|
+
assert_eq!(checked_usize_from_f64(f64::NAN), None);
|
|
537
|
+
assert_eq!(checked_usize_from_f64(f64::INFINITY), None);
|
|
538
|
+
assert_eq!(checked_usize_from_f64(1e20), None);
|
|
539
|
+
|
|
540
|
+
assert_eq!(checked_u64_from_f64(42.0), Some(42));
|
|
541
|
+
assert_eq!(checked_u64_from_f64(-0.5), None);
|
|
542
|
+
assert_eq!(checked_u64_from_f64(f64::NEG_INFINITY), None);
|
|
543
|
+
assert_eq!(checked_u64_from_f64(1e20), None);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
#[test]
|
|
547
|
+
fn checked_integer_conversions_handle_the_two_pow_64_boundary() {
|
|
548
|
+
// 2^64 is exactly representable and equals `u64::MAX as f64` after
|
|
549
|
+
// rounding; it must be rejected, not saturated to u64::MAX.
|
|
550
|
+
let two_pow_64 = 18_446_744_073_709_551_616.0_f64;
|
|
551
|
+
assert_eq!(checked_u64_from_f64(two_pow_64), None);
|
|
552
|
+
assert_eq!(checked_u64_from_f64(u64::MAX as f64), None);
|
|
553
|
+
assert_eq!(checked_usize_from_f64(two_pow_64), None);
|
|
554
|
+
// The largest f64 strictly below 2^64 is a valid u64.
|
|
555
|
+
let below = 18_446_744_073_709_549_568.0_f64; // 2^64 - 2048
|
|
556
|
+
assert_eq!(
|
|
557
|
+
checked_u64_from_f64(below),
|
|
558
|
+
Some(18_446_744_073_709_549_568)
|
|
559
|
+
);
|
|
560
|
+
// 2^53 region is unaffected.
|
|
561
|
+
let two_pow_53 = 9_007_199_254_740_992.0_f64;
|
|
562
|
+
assert_eq!(checked_u64_from_f64(two_pow_53), Some(1 << 53));
|
|
563
|
+
}
|
|
564
|
+
|
|
425
565
|
#[test]
|
|
426
566
|
fn journal_record_encoding_matches_indexer_core() {
|
|
427
567
|
for (key, value) in [
|
package/src/lib.rs
CHANGED
|
@@ -10,14 +10,18 @@ use wasm_bindgen::prelude::*;
|
|
|
10
10
|
mod append_tx;
|
|
11
11
|
mod coordinates;
|
|
12
12
|
mod documents;
|
|
13
|
+
mod error;
|
|
13
14
|
mod graph_blocks;
|
|
14
15
|
mod js_interop;
|
|
15
16
|
mod profile;
|
|
16
17
|
mod raw_receive;
|
|
17
18
|
mod shared_log_plan;
|
|
18
19
|
mod sync_send;
|
|
20
|
+
mod time;
|
|
19
21
|
mod wire_sync;
|
|
20
22
|
|
|
23
|
+
pub use crate::error::BackboneError;
|
|
24
|
+
|
|
21
25
|
use crate::documents::{DocumentContextFields, DocumentPreviousSignerFact, ParsedProjectionPlan};
|
|
22
26
|
use crate::profile::NativeBackboneAppendProfile;
|
|
23
27
|
use crate::raw_receive::PendingRawReceiveEntry;
|