1#[allow(clippy::inline_always)]
31#[inline(always)]
32pub const fn serial_type_len(serial_type: u64) -> Option<u64> {
33 match serial_type {
34 0 | 8 | 9 | 10 | 11 => Some(0),
35 1 => Some(1),
36 2 => Some(2),
37 3 => Some(3),
38 4 => Some(4),
39 5 => Some(6),
40 6 | 7 => Some(8),
41 n if n % 2 == 0 => Some((n - 12) / 2),
42 n => Some((n - 13) / 2),
43 }
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum SerialTypeClass {
49 Null,
51 Integer,
53 Float,
55 Zero,
57 One,
59 Reserved,
61 Blob,
63 Text,
65}
66
67#[allow(clippy::inline_always)]
69#[inline(always)]
70pub const fn classify_serial_type(serial_type: u64) -> SerialTypeClass {
71 match serial_type {
72 0 => SerialTypeClass::Null,
73 1..=6 => SerialTypeClass::Integer,
74 7 => SerialTypeClass::Float,
75 8 => SerialTypeClass::Zero,
76 9 => SerialTypeClass::One,
77 10 | 11 => SerialTypeClass::Reserved,
78 n if n % 2 == 0 => SerialTypeClass::Blob,
79 _ => SerialTypeClass::Text,
80 }
81}
82
83#[allow(clippy::cast_sign_loss)]
85pub const fn serial_type_for_integer(value: i64) -> u64 {
86 let u = if value < 0 {
87 !(value as u64)
88 } else {
89 value as u64
90 };
91
92 if u <= 127 {
93 if value == 0 {
94 return 8;
95 }
96 if value == 1 {
97 return 9;
98 }
99 1
100 } else if u <= 32767 {
101 2
102 } else if u <= 8_388_607 {
103 3
104 } else if u <= 2_147_483_647 {
105 4
106 } else if u <= 0x0000_7FFF_FFFF_FFFF {
107 5
108 } else {
109 6
110 }
111}
112
113#[allow(clippy::inline_always)]
115#[inline(always)]
116pub const fn integer_serial_type_and_len(value: i64) -> (u64, usize) {
117 let u = if value < 0 {
118 !(value as u64)
119 } else {
120 value as u64
121 };
122
123 if u <= 127 {
124 if value == 0 {
125 return (8, 0);
126 }
127 if value == 1 {
128 return (9, 0);
129 }
130 (1, 1)
131 } else if u <= 32767 {
132 (2, 2)
133 } else if u <= 8_388_607 {
134 (3, 3)
135 } else if u <= 2_147_483_647 {
136 (4, 4)
137 } else if u <= 0x0000_7FFF_FFFF_FFFF {
138 (5, 6)
139 } else {
140 (6, 8)
141 }
142}
143
144pub const fn serial_type_for_text(len: u64) -> u64 {
146 len.saturating_mul(2).saturating_add(13)
147}
148
149pub const fn serial_type_for_blob(len: u64) -> u64 {
151 len.saturating_mul(2).saturating_add(12)
152}
153
154pub const SMALL_TYPE_SIZES: [u8; 128] = {
157 let mut table = [0u8; 128];
158 let mut i: usize = 0;
159 loop {
160 if i >= 128 {
161 break;
162 }
163 #[allow(clippy::cast_possible_truncation)]
164 let size = match serial_type_len(i as u64) {
165 Some(n) if n <= 255 => n as u8,
166 _ => 0,
167 };
168 table[i] = size;
169 i += 1;
170 }
171 table
172};
173
174#[allow(clippy::inline_always)]
179#[inline(always)]
180pub fn read_varint(buf: &[u8]) -> Option<(u64, usize)> {
181 if buf.is_empty() {
182 return None;
183 }
184
185 let first = buf[0];
186 if first < 0x80 {
188 return Some((u64::from(first), 1));
189 }
190
191 if buf.len() >= 2 {
194 let second = buf[1];
195 if second & 0x80 == 0 {
196 return Some(((u64::from(first & 0x7F) << 7) | u64::from(second), 2));
197 }
198 }
199
200 let mut value = u64::from(first & 0x7F);
205
206 if buf.len() < 2 {
207 return None;
208 }
209 let byte = buf[1];
210 value = (value << 7) | u64::from(byte & 0x7F);
211
212 if buf.len() < 3 {
213 return None;
214 }
215 let byte = buf[2];
216 if byte & 0x80 == 0 {
217 return Some(((value << 7) | u64::from(byte), 3));
218 }
219 value = (value << 7) | u64::from(byte & 0x7F);
220
221 if buf.len() < 4 {
222 return None;
223 }
224 let byte = buf[3];
225 if byte & 0x80 == 0 {
226 return Some(((value << 7) | u64::from(byte), 4));
227 }
228 value = (value << 7) | u64::from(byte & 0x7F);
229
230 if buf.len() < 5 {
231 return None;
232 }
233 let byte = buf[4];
234 if byte & 0x80 == 0 {
235 return Some(((value << 7) | u64::from(byte), 5));
236 }
237 value = (value << 7) | u64::from(byte & 0x7F);
238
239 if buf.len() < 6 {
240 return None;
241 }
242 let byte = buf[5];
243 if byte & 0x80 == 0 {
244 return Some(((value << 7) | u64::from(byte), 6));
245 }
246 value = (value << 7) | u64::from(byte & 0x7F);
247
248 if buf.len() < 7 {
249 return None;
250 }
251 let byte = buf[6];
252 if byte & 0x80 == 0 {
253 return Some(((value << 7) | u64::from(byte), 7));
254 }
255 value = (value << 7) | u64::from(byte & 0x7F);
256
257 if buf.len() < 8 {
258 return None;
259 }
260 let byte = buf[7];
261 if byte & 0x80 == 0 {
262 return Some(((value << 7) | u64::from(byte), 8));
263 }
264 value = (value << 7) | u64::from(byte & 0x7F);
265
266 if buf.len() > 8 {
267 return Some(((value << 8) | u64::from(buf[8]), 9));
268 }
269
270 None
271}
272
273pub const fn varint_len(value: u64) -> usize {
275 if value <= 0x7F {
276 1
277 } else if value <= 0x3FFF {
278 2
279 } else if value <= 0x001F_FFFF {
280 3
281 } else if value <= 0x0FFF_FFFF {
282 4
283 } else if value <= 0x07_FFFF_FFFF {
284 5
285 } else if value <= 0x03FF_FFFF_FFFF {
286 6
287 } else if value <= 0x01_FFFF_FFFF_FFFF {
288 7
289 } else if value <= 0xFF_FFFF_FFFF_FFFF {
290 8
291 } else {
292 9
293 }
294}
295
296#[allow(clippy::cast_possible_truncation)]
300pub fn write_varint(buf: &mut [u8], value: u64) -> usize {
301 let len = varint_len(value);
302
303 if len == 1 {
304 buf[0] = value as u8;
305 } else if len == 9 {
306 let mut v = value >> 8;
308 for i in (0..8).rev() {
309 buf[i] = (v as u8 & 0x7F) | 0x80;
310 v >>= 7;
311 }
312 buf[8] = value as u8;
313 } else {
314 let mut v = value;
315 for i in (0..len).rev() {
316 if i == len - 1 {
317 buf[i] = v as u8 & 0x7F;
318 } else {
319 buf[i] = (v as u8 & 0x7F) | 0x80;
320 }
321 v >>= 7;
322 }
323 }
324
325 len
326}
327
328#[cfg(test)]
329mod tests {
330 use super::*;
331
332 #[test]
333 fn serial_type_sizes() {
334 assert_eq!(serial_type_len(0), Some(0)); assert_eq!(serial_type_len(1), Some(1)); assert_eq!(serial_type_len(2), Some(2)); assert_eq!(serial_type_len(3), Some(3)); assert_eq!(serial_type_len(4), Some(4)); assert_eq!(serial_type_len(5), Some(6)); assert_eq!(serial_type_len(6), Some(8)); assert_eq!(serial_type_len(7), Some(8)); assert_eq!(serial_type_len(8), Some(0)); assert_eq!(serial_type_len(9), Some(0)); assert_eq!(serial_type_len(10), Some(0)); assert_eq!(serial_type_len(11), Some(0)); }
350
351 #[test]
352 fn serial_type_blob_text() {
353 assert_eq!(serial_type_len(12), Some(0)); assert_eq!(serial_type_len(14), Some(1)); assert_eq!(serial_type_len(20), Some(4)); assert_eq!(serial_type_len(13), Some(0)); assert_eq!(serial_type_len(15), Some(1)); assert_eq!(serial_type_len(21), Some(4)); }
363
364 #[test]
365 fn classification() {
366 assert_eq!(classify_serial_type(0), SerialTypeClass::Null);
367 assert_eq!(classify_serial_type(1), SerialTypeClass::Integer);
368 assert_eq!(classify_serial_type(6), SerialTypeClass::Integer);
369 assert_eq!(classify_serial_type(7), SerialTypeClass::Float);
370 assert_eq!(classify_serial_type(8), SerialTypeClass::Zero);
371 assert_eq!(classify_serial_type(9), SerialTypeClass::One);
372 assert_eq!(classify_serial_type(10), SerialTypeClass::Reserved);
373 assert_eq!(classify_serial_type(11), SerialTypeClass::Reserved);
374 assert_eq!(classify_serial_type(12), SerialTypeClass::Blob);
375 assert_eq!(classify_serial_type(13), SerialTypeClass::Text);
376 assert_eq!(classify_serial_type(14), SerialTypeClass::Blob);
377 assert_eq!(classify_serial_type(15), SerialTypeClass::Text);
378 }
379
380 #[test]
381 fn serial_type_for_integers() {
382 assert_eq!(serial_type_for_integer(0), 8);
383 assert_eq!(serial_type_for_integer(1), 9);
384 assert_eq!(serial_type_for_integer(2), 1);
385 assert_eq!(serial_type_for_integer(127), 1);
386 assert_eq!(serial_type_for_integer(-1), 1);
387 assert_eq!(serial_type_for_integer(-128), 1);
388 assert_eq!(serial_type_for_integer(128), 2);
389 assert_eq!(serial_type_for_integer(32767), 2);
390 assert_eq!(serial_type_for_integer(32768), 3);
391 assert_eq!(serial_type_for_integer(8_388_607), 3);
392 assert_eq!(serial_type_for_integer(8_388_608), 4);
393 assert_eq!(serial_type_for_integer(2_147_483_647), 4);
394 assert_eq!(serial_type_for_integer(2_147_483_648), 5);
395 assert_eq!(serial_type_for_integer(i64::MAX), 6);
396 assert_eq!(serial_type_for_integer(i64::MIN), 6);
397 }
398
399 #[test]
400 fn serial_type_for_text_and_blob() {
401 assert_eq!(serial_type_for_text(0), 13);
402 assert_eq!(serial_type_for_text(1), 15);
403 assert_eq!(serial_type_for_text(5), 23);
404 assert_eq!(serial_type_for_blob(0), 12);
405 assert_eq!(serial_type_for_blob(1), 14);
406 assert_eq!(serial_type_for_blob(5), 22);
407 }
408
409 #[test]
410 fn small_type_sizes_table() {
411 assert_eq!(SMALL_TYPE_SIZES[0], 0);
412 assert_eq!(SMALL_TYPE_SIZES[1], 1);
413 assert_eq!(SMALL_TYPE_SIZES[2], 2);
414 assert_eq!(SMALL_TYPE_SIZES[3], 3);
415 assert_eq!(SMALL_TYPE_SIZES[4], 4);
416 assert_eq!(SMALL_TYPE_SIZES[5], 6);
417 assert_eq!(SMALL_TYPE_SIZES[6], 8);
418 assert_eq!(SMALL_TYPE_SIZES[7], 8);
419 assert_eq!(SMALL_TYPE_SIZES[8], 0);
420 assert_eq!(SMALL_TYPE_SIZES[9], 0);
421 }
422
423 #[test]
424 fn varint_roundtrip() {
425 let test_values: &[u64] = &[
426 0,
427 1,
428 127,
429 128,
430 0x3FFF,
431 0x4000,
432 0x001F_FFFF,
433 0x0020_0000,
434 0x0FFF_FFFF,
435 0x1000_0000,
436 u64::from(u32::MAX),
437 u64::MAX / 2,
438 u64::MAX,
439 ];
440
441 let mut buf = [0u8; 9];
442 for &value in test_values {
443 let written = write_varint(&mut buf, value);
444 let (decoded, consumed) = read_varint(&buf[..written]).unwrap();
445 assert_eq!(decoded, value, "roundtrip failed for {value}");
446 assert_eq!(written, consumed, "length mismatch for {value}");
447 assert_eq!(
448 written,
449 varint_len(value),
450 "varint_len mismatch for {value}"
451 );
452 }
453 }
454
455 #[test]
456 fn varint_single_byte() {
457 let mut buf = [0u8; 9];
458 assert_eq!(write_varint(&mut buf, 0), 1);
459 assert_eq!(buf[0], 0);
460
461 assert_eq!(write_varint(&mut buf, 127), 1);
462 assert_eq!(buf[0], 127);
463 }
464
465 #[test]
466 fn varint_two_bytes() {
467 let mut buf = [0u8; 9];
468 let written = write_varint(&mut buf, 128);
469 assert_eq!(written, 2);
470 let (value, consumed) = read_varint(&buf[..written]).unwrap();
471 assert_eq!(value, 128);
472 assert_eq!(consumed, 2);
473 }
474
475 #[test]
476 fn varint_nine_bytes_uses_full_8bit_last_byte() {
477 let value: u64 = (1u64 << 56) | 0xFF;
480
481 let mut buf = [0u8; 9];
482 let written = write_varint(&mut buf, value);
483 assert_eq!(written, 9);
484 assert_eq!(buf[8], 0xFF);
485
486 assert!(buf[..8].iter().all(|b| b & 0x80 != 0));
488
489 let (decoded, consumed) = read_varint(&buf).unwrap();
490 assert_eq!(decoded, value);
491 assert_eq!(consumed, 9);
492 }
493
494 #[test]
495 fn read_varint_empty() {
496 assert!(read_varint(&[]).is_none());
497 }
498
499 const BEAD_ID: &str = "bd-1y7b";
504
505 const BYTE_BOUNDARIES: [(u64, u64, usize); 9] = [
507 (0, 0x7F, 1), (0x80, 0x3FFF, 2), (0x4000, 0x001F_FFFF, 3), (0x0020_0000, 0x0FFF_FFFF, 4), (0x1000_0000, 0x07_FFFF_FFFF, 5), (0x08_0000_0000, 0x03FF_FFFF_FFFF, 6), (0x0400_0000_0000, 0x01_FFFF_FFFF_FFFF, 7), (0x02_0000_0000_0000, 0xFF_FFFF_FFFF_FFFF, 8), (0x0100_0000_0000_0000, u64::MAX, 9), ];
517
518 #[test]
519 fn test_varint_1byte_boundary() {
520 let mut buf = [0u8; 9];
521 for value in [0u64, 1, 42, 126, 127] {
522 let written = write_varint(&mut buf, value);
523 assert_eq!(
524 written, 1,
525 "bead_id={BEAD_ID} case=1byte_boundary value={value}"
526 );
527 let (decoded, consumed) = read_varint(&buf[..written]).unwrap();
528 assert_eq!(decoded, value);
529 assert_eq!(consumed, 1);
530 }
531 }
532
533 #[test]
534 fn test_varint_2byte_boundary() {
535 let mut buf = [0u8; 9];
536 let written = write_varint(&mut buf, 128);
538 assert_eq!(written, 2, "bead_id={BEAD_ID} case=2byte_min");
539 assert_eq!(
540 &buf[..2],
541 [0x81, 0x00],
542 "bead_id={BEAD_ID} case=2byte_min_bytes"
543 );
544 let (decoded, _) = read_varint(&buf[..2]).unwrap();
545 assert_eq!(decoded, 128);
546
547 let written = write_varint(&mut buf, 16383);
549 assert_eq!(written, 2, "bead_id={BEAD_ID} case=2byte_max");
550 assert_eq!(
551 &buf[..2],
552 [0xFF, 0x7F],
553 "bead_id={BEAD_ID} case=2byte_max_bytes"
554 );
555 let (decoded, _) = read_varint(&buf[..2]).unwrap();
556 assert_eq!(decoded, 16383);
557 }
558
559 #[test]
560 fn test_varint_3byte_boundary() {
561 let mut buf = [0u8; 9];
562 let written = write_varint(&mut buf, 16384);
563 assert_eq!(written, 3, "bead_id={BEAD_ID} case=3byte_min");
564 let (decoded, consumed) = read_varint(&buf[..written]).unwrap();
565 assert_eq!(decoded, 16384);
566 assert_eq!(consumed, 3);
567
568 let written = write_varint(&mut buf, 2_097_151);
569 assert_eq!(written, 3, "bead_id={BEAD_ID} case=3byte_max");
570 let (decoded, _) = read_varint(&buf[..written]).unwrap();
571 assert_eq!(decoded, 2_097_151);
572 }
573
574 #[test]
575 fn test_varint_4byte_boundary() {
576 let mut buf = [0u8; 9];
577 let written = write_varint(&mut buf, 2_097_152);
578 assert_eq!(written, 4, "bead_id={BEAD_ID} case=4byte_min");
579 let (decoded, _) = read_varint(&buf[..written]).unwrap();
580 assert_eq!(decoded, 2_097_152);
581
582 let written = write_varint(&mut buf, 268_435_455);
583 assert_eq!(written, 4, "bead_id={BEAD_ID} case=4byte_max");
584 let (decoded, _) = read_varint(&buf[..written]).unwrap();
585 assert_eq!(decoded, 268_435_455);
586 }
587
588 #[test]
589 fn test_varint_5byte_boundary() {
590 let mut buf = [0u8; 9];
591 let written = write_varint(&mut buf, 268_435_456);
592 assert_eq!(written, 5, "bead_id={BEAD_ID} case=5byte_min");
593 let (decoded, _) = read_varint(&buf[..written]).unwrap();
594 assert_eq!(decoded, 268_435_456);
595
596 let written = write_varint(&mut buf, 34_359_738_367);
597 assert_eq!(written, 5, "bead_id={BEAD_ID} case=5byte_max");
598 let (decoded, _) = read_varint(&buf[..written]).unwrap();
599 assert_eq!(decoded, 34_359_738_367);
600 }
601
602 #[test]
603 fn test_varint_6byte_boundary() {
604 let mut buf = [0u8; 9];
605 let written = write_varint(&mut buf, 34_359_738_368);
606 assert_eq!(written, 6, "bead_id={BEAD_ID} case=6byte_min");
607 let (decoded, _) = read_varint(&buf[..written]).unwrap();
608 assert_eq!(decoded, 34_359_738_368);
609
610 let written = write_varint(&mut buf, 4_398_046_511_103);
611 assert_eq!(written, 6, "bead_id={BEAD_ID} case=6byte_max");
612 let (decoded, _) = read_varint(&buf[..written]).unwrap();
613 assert_eq!(decoded, 4_398_046_511_103);
614 }
615
616 #[test]
617 fn test_varint_7byte_boundary() {
618 let mut buf = [0u8; 9];
619 let written = write_varint(&mut buf, 4_398_046_511_104);
620 assert_eq!(written, 7, "bead_id={BEAD_ID} case=7byte_min");
621 let (decoded, _) = read_varint(&buf[..written]).unwrap();
622 assert_eq!(decoded, 4_398_046_511_104);
623
624 let written = write_varint(&mut buf, 562_949_953_421_311);
625 assert_eq!(written, 7, "bead_id={BEAD_ID} case=7byte_max");
626 let (decoded, _) = read_varint(&buf[..written]).unwrap();
627 assert_eq!(decoded, 562_949_953_421_311);
628 }
629
630 #[test]
631 fn test_varint_8byte_boundary() {
632 let mut buf = [0u8; 9];
633 let written = write_varint(&mut buf, 562_949_953_421_312);
634 assert_eq!(written, 8, "bead_id={BEAD_ID} case=8byte_min");
635 let (decoded, _) = read_varint(&buf[..written]).unwrap();
636 assert_eq!(decoded, 562_949_953_421_312);
637
638 let written = write_varint(&mut buf, 72_057_594_037_927_935);
639 assert_eq!(written, 8, "bead_id={BEAD_ID} case=8byte_max");
640 let (decoded, _) = read_varint(&buf[..written]).unwrap();
641 assert_eq!(decoded, 72_057_594_037_927_935);
642 }
643
644 #[test]
645 fn test_varint_9byte_full_u64() {
646 let mut buf = [0u8; 9];
647
648 let min9 = 72_057_594_037_927_936u64; let written = write_varint(&mut buf, min9);
651 assert_eq!(written, 9, "bead_id={BEAD_ID} case=9byte_min");
652 let (decoded, consumed) = read_varint(&buf).unwrap();
653 assert_eq!(decoded, min9);
654 assert_eq!(consumed, 9);
655
656 let written = write_varint(&mut buf, u64::MAX);
658 assert_eq!(written, 9, "bead_id={BEAD_ID} case=9byte_max");
659 assert_eq!(
660 buf,
661 [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
662 "bead_id={BEAD_ID} case=9byte_max_bytes u64::MAX must be all-0xFF"
663 );
664 let (decoded, consumed) = read_varint(&buf).unwrap();
665 assert_eq!(decoded, u64::MAX);
666 assert_eq!(consumed, 9);
667 }
668
669 #[test]
670 fn test_varint_9th_byte_all_bits() {
671 let mut buf = [0u8; 9];
674
675 for low_byte in [0x80u8, 0xFF, 0xAB, 0xFE] {
676 let value = (1u64 << 56) | u64::from(low_byte);
677 let written = write_varint(&mut buf, value);
678 assert_eq!(written, 9);
679 assert_eq!(
680 buf[8], low_byte,
681 "bead_id={BEAD_ID} case=9th_byte_all_bits low={low_byte:#04x}"
682 );
683 for (i, &b) in buf[..8].iter().enumerate() {
685 assert_ne!(
686 b & 0x80,
687 0,
688 "bead_id={BEAD_ID} case=continuation_bit byte={i}"
689 );
690 }
691 let (decoded, consumed) = read_varint(&buf).unwrap();
692 assert_eq!(decoded, value);
693 assert_eq!(consumed, 9);
694 }
695 }
696
697 #[test]
698 fn test_varint_signed_negative_rowid() {
699 let mut buf = [0u8; 9];
700
701 #[allow(clippy::cast_sign_loss)]
703 let min_u64 = i64::MIN as u64;
704 assert_eq!(min_u64, 0x8000_0000_0000_0000);
705
706 let written = write_varint(&mut buf, min_u64);
707 assert_eq!(written, 9, "bead_id={BEAD_ID} case=i64_min_length");
708 let (decoded, _) = read_varint(&buf[..written]).unwrap();
709 assert_eq!(decoded, min_u64);
710
711 #[allow(clippy::cast_possible_wrap)]
713 let signed = decoded as i64;
714 assert_eq!(signed, i64::MIN, "bead_id={BEAD_ID} case=i64_min_roundtrip");
715 }
716
717 #[test]
718 fn test_varint_signed_minus_one() {
719 let mut buf = [0u8; 9];
720
721 #[allow(clippy::cast_sign_loss)]
723 let minus_one_u64 = (-1i64) as u64;
724 assert_eq!(minus_one_u64, u64::MAX);
725
726 let written = write_varint(&mut buf, minus_one_u64);
727 assert_eq!(written, 9, "bead_id={BEAD_ID} case=minus_one_length");
728 let (decoded, _) = read_varint(&buf[..written]).unwrap();
729
730 #[allow(clippy::cast_possible_wrap)]
731 let signed = decoded as i64;
732 assert_eq!(signed, -1, "bead_id={BEAD_ID} case=minus_one_roundtrip");
733 }
734
735 #[test]
736 fn test_varint_not_protobuf() {
737 let mut buf = [0u8; 9];
740 let sqlite_len = write_varint(&mut buf, u64::MAX);
741 assert_eq!(
742 sqlite_len, 9,
743 "bead_id={BEAD_ID} case=not_protobuf SQLite u64::MAX must be 9 bytes"
744 );
745
746 let protobuf_len = leb128_len(u64::MAX);
748 assert_eq!(
749 protobuf_len, 10,
750 "bead_id={BEAD_ID} case=not_protobuf protobuf u64::MAX must be 10 bytes"
751 );
752
753 let value = 1u64 << 56;
755 let sqlite_len = write_varint(&mut buf, value);
756 assert_eq!(sqlite_len, 9);
757 let protobuf_len = leb128_len(value);
758 assert_eq!(protobuf_len, 9); let mut leb_buf = [0u8; 10];
762 let leb_n = leb128_encode(&mut leb_buf, value);
763 assert_ne!(
764 &buf[..sqlite_len],
765 &leb_buf[..leb_n],
766 "bead_id={BEAD_ID} case=not_protobuf byte sequences must differ for 2^56"
767 );
768 }
769
770 fn leb128_len(mut v: u64) -> usize {
772 let mut len = 1;
773 while v >= 0x80 {
774 v >>= 7;
775 len += 1;
776 }
777 len
778 }
779
780 fn leb128_encode(buf: &mut [u8], mut v: u64) -> usize {
782 let mut i = 0;
783 while v >= 0x80 {
784 #[allow(clippy::cast_possible_truncation)]
785 {
786 buf[i] = (v as u8 & 0x7F) | 0x80;
787 }
788 v >>= 7;
789 i += 1;
790 }
791 #[allow(clippy::cast_possible_truncation)]
792 {
793 buf[i] = v as u8;
794 }
795 i + 1
796 }
797
798 #[test]
799 fn test_varint_all_boundaries_roundtrip() {
800 let mut buf = [0u8; 9];
801 for &(min_val, max_val, expected_len) in &BYTE_BOUNDARIES {
802 let written = write_varint(&mut buf, min_val);
804 assert_eq!(
805 written, expected_len,
806 "bead_id={BEAD_ID} case=boundary_min value={min_val} expected_len={expected_len}"
807 );
808 let (decoded, consumed) = read_varint(&buf[..written]).unwrap();
809 assert_eq!(decoded, min_val);
810 assert_eq!(consumed, expected_len);
811
812 let written = write_varint(&mut buf, max_val);
814 assert_eq!(
815 written, expected_len,
816 "bead_id={BEAD_ID} case=boundary_max value={max_val} expected_len={expected_len}"
817 );
818 let (decoded, consumed) = read_varint(&buf[..written]).unwrap();
819 assert_eq!(decoded, max_val);
820 assert_eq!(consumed, expected_len);
821
822 assert_eq!(varint_len(min_val), expected_len);
824 assert_eq!(varint_len(max_val), expected_len);
825 }
826 }
827
828 #[test]
829 fn test_varint_canonical_encoding() {
830 for &(min_val, _, expected_len) in &BYTE_BOUNDARIES {
833 if min_val == 0 {
834 continue;
835 }
836 let below = min_val - 1;
837 let mut buf = [0u8; 9];
838 let written = write_varint(&mut buf, below);
839 assert!(
840 written < expected_len,
841 "bead_id={BEAD_ID} case=canonical value={below} written={written} \
842 must be < {expected_len}"
843 );
844 }
845 }
846
847 #[test]
848 fn test_varint_decode_from_longer_buffer() {
849 let mut buf = [0xCC_u8; 16]; let written = write_varint(&mut buf, 128); assert_eq!(written, 2);
853
854 let (decoded, consumed) = read_varint(&buf).unwrap();
856 assert_eq!(decoded, 128);
857 assert_eq!(
858 consumed, 2,
859 "bead_id={BEAD_ID} case=longer_buffer decoder must stop at 2 bytes"
860 );
861 assert!(
863 buf[2..].iter().all(|&b| b == 0xCC),
864 "bead_id={BEAD_ID} case=longer_buffer trailing bytes must be untouched"
865 );
866 }
867
868 #[test]
869 fn test_varint_decode_truncated_returns_none() {
870 let mut buf = [0u8; 9];
872 let written = write_varint(&mut buf, 128); assert_eq!(written, 2);
874
875 assert!(
877 read_varint(&buf[..1]).is_none(),
878 "bead_id={BEAD_ID} case=truncated_2byte"
879 );
880
881 let written = write_varint(&mut buf, u64::MAX);
883 assert_eq!(written, 9);
884 assert!(
885 read_varint(&buf[..8]).is_none(),
886 "bead_id={BEAD_ID} case=truncated_9byte"
887 );
888 }
889
890 #[test]
891 fn test_varint_golden_vectors() {
892 let cases: &[(u64, &[u8])] = &[
894 (0, &[0x00]),
895 (1, &[0x01]),
896 (127, &[0x7F]),
897 (128, &[0x81, 0x00]),
898 (129, &[0x81, 0x01]),
899 (16383, &[0xFF, 0x7F]),
900 (16384, &[0x81, 0x80, 0x00]),
901 (
902 u64::MAX,
903 &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
904 ),
905 ];
906
907 let mut buf = [0u8; 9];
908 for &(value, expected_bytes) in cases {
909 let written = write_varint(&mut buf, value);
910 assert_eq!(
911 &buf[..written],
912 expected_bytes,
913 "bead_id={BEAD_ID} case=golden_vector value={value}"
914 );
915 let (decoded, consumed) = read_varint(expected_bytes).unwrap();
916 assert_eq!(decoded, value);
917 assert_eq!(consumed, expected_bytes.len());
918 }
919 }
920
921 #[test]
922 fn test_varint_i64_max_and_nearby() {
923 let mut buf = [0u8; 9];
924
925 #[allow(clippy::cast_sign_loss)]
927 let i64_max_u = i64::MAX as u64;
928 let written = write_varint(&mut buf, i64_max_u);
929 assert_eq!(written, 9, "bead_id={BEAD_ID} case=i64_max");
930 let (decoded, _) = read_varint(&buf[..written]).unwrap();
931 assert_eq!(decoded, i64_max_u);
932
933 let written = write_varint(&mut buf, i64_max_u + 1);
935 assert_eq!(written, 9);
936 let (decoded, _) = read_varint(&buf[..written]).unwrap();
937 assert_eq!(decoded, i64_max_u + 1);
938 }
939
940 use proptest::prelude::*;
944
945 proptest! {
946 #[test]
948 fn prop_varint_roundtrip(value: u64) {
949 let mut buf = [0u8; 9];
950 let written = write_varint(&mut buf, value);
951 let (decoded, consumed) = read_varint(&buf[..written]).unwrap();
952 prop_assert_eq!(decoded, value);
953 prop_assert_eq!(consumed, written);
954 }
955
956 #[test]
958 fn prop_varint_len_matches_write(value: u64) {
959 let mut buf = [0u8; 9];
960 let written = write_varint(&mut buf, value);
961 prop_assert_eq!(varint_len(value), written);
962 }
963
964 #[test]
967 fn prop_varint_canonical(value: u64) {
968 let mut buf = [0u8; 9];
969 let written = write_varint(&mut buf, value);
970 if written > 1 {
973 if let Some((alt, _)) = read_varint(&buf[1..written]) {
974 prop_assert_ne!(alt, value, "shorter encoding yields same value — not canonical");
975 }
976 }
977 }
978
979 #[test]
982 fn prop_integer_serial_type_class(value: i64) {
983 let st = serial_type_for_integer(value);
984 let class = classify_serial_type(st);
985 prop_assert!(
986 matches!(class, SerialTypeClass::Integer | SerialTypeClass::Zero | SerialTypeClass::One),
987 "integer value {value} got unexpected class {class:?} for serial type {st}"
988 );
989 }
990
991 #[test]
993 fn prop_integer_serial_type_fits(value: i64) {
994 let st = serial_type_for_integer(value);
995 if let Some(size) = serial_type_len(st) {
996 if size == 0 {
998 prop_assert!(value == 0 || value == 1);
999 }
1000 }
1001 }
1002
1003 #[test]
1005 fn prop_text_serial_type(len in 0u64..=1_000_000) {
1006 let st = serial_type_for_text(len);
1007 prop_assert!(st >= 13, "text type {st} < 13");
1008 prop_assert!(st % 2 == 1, "text type {st} is even");
1009 prop_assert_eq!(classify_serial_type(st), SerialTypeClass::Text);
1010 prop_assert_eq!(serial_type_len(st), Some(len));
1012 }
1013
1014 #[test]
1016 fn prop_blob_serial_type(len in 0u64..=1_000_000) {
1017 let st = serial_type_for_blob(len);
1018 prop_assert!(st >= 12, "blob type {st} < 12");
1019 prop_assert!(st % 2 == 0, "blob type {st} is odd");
1020 prop_assert_eq!(classify_serial_type(st), SerialTypeClass::Blob);
1021 prop_assert_eq!(serial_type_len(st), Some(len));
1023 }
1024
1025 #[test]
1027 fn prop_classification_deterministic(st: u64) {
1028 let class = classify_serial_type(st);
1029 prop_assert_eq!(classify_serial_type(st), class);
1031 match class {
1036 SerialTypeClass::Reserved => {
1037 prop_assert_eq!(serial_type_len(st), Some(0));
1038 }
1039 _ => {
1040 prop_assert!(serial_type_len(st).is_some());
1041 }
1042 }
1043 }
1044
1045 #[test]
1047 #[allow(clippy::cast_possible_truncation)]
1048 fn prop_small_type_table_consistent(i in 0u64..128) {
1049 let expected = match serial_type_len(i) {
1050 Some(n) if n <= 255 => n as u8,
1051 _ => 0,
1052 };
1053 prop_assert_eq!(SMALL_TYPE_SIZES[usize::try_from(i).unwrap()], expected);
1054 }
1055
1056 #[test]
1058 fn prop_varint_len_bounds(value: u64) {
1059 let len = varint_len(value);
1060 prop_assert!((1..=9).contains(&len), "varint_len({value}) = {len}");
1061 }
1062
1063 #[test]
1065 fn prop_nine_byte_varint_continuation_bits(value in 0x0100_0000_0000_0000u64..=u64::MAX) {
1066 let mut buf = [0u8; 9];
1067 let written = write_varint(&mut buf, value);
1068 if written == 9 {
1069 for (i, &byte) in buf[..8].iter().enumerate() {
1070 prop_assert!(byte & 0x80 != 0, "byte {i} missing continuation bit for value {value}");
1071 }
1072 }
1073 }
1074
1075 #[test]
1077 fn prop_truncated_varint_returns_none(value: u64) {
1078 let mut buf = [0u8; 9];
1079 let written = write_varint(&mut buf, value);
1080 if written > 1 {
1081 prop_assert!(read_varint(&buf[..written - 1]).is_none() ||
1083 read_varint(&buf[..written - 1]).unwrap().0 != value,
1084 "truncated buffer should not decode to original value");
1085 }
1086 }
1087 }
1088}