From d5b1019d740b27ae011d5c9b3f37b7ae4181c2b3 Mon Sep 17 00:00:00 2001 From: Islam El-Ashi Date: Wed, 22 Apr 2026 15:30:01 +0200 Subject: [PATCH 1/3] feat: support unbounded 2-tuples --- src/storable/tests.rs | 23 +++++++++++++++++++ src/storable/tuples.rs | 50 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/src/storable/tests.rs b/src/storable/tests.rs index 84ce1f39..720815d4 100644 --- a/src/storable/tests.rs +++ b/src/storable/tests.rs @@ -122,6 +122,29 @@ proptest! { prop_assert_eq!(v.clone(), Storable::from_bytes(v.to_bytes())); } + // 2-tuple unbounded roundtrips + + #[test] + fn tuple_two_unbounded_elements_roundtrip(v1 in pvec(any::(), 0..16), v2 in pvec(any::(), 0..32)) { + // (Vec, Vec): both unbounded + let tuple = (v1, v2); + prop_assert_eq!(tuple.clone(), Storable::from_bytes(tuple.to_bytes())); + } + + #[test] + fn tuple_fixed_and_unbounded_roundtrip(x in any::(), v in pvec(any::(), 0..32)) { + // (u64, Vec): fixed A, unbounded B — no size_lengths overhead + let tuple = (x, v); + prop_assert_eq!(tuple.clone(), Storable::from_bytes(tuple.to_bytes())); + } + + #[test] + fn tuple_unbounded_and_fixed_roundtrip(v in pvec(any::(), 0..32), x in any::()) { + // (Vec, u64): unbounded A, fixed B — size_lengths overhead needed + let tuple = (v, x); + prop_assert_eq!(tuple.clone(), Storable::from_bytes(tuple.to_bytes())); + } + #[test] fn principal_roundtrip(mut bytes in pvec(any::(), 0..=28), tag in proptest::prop_oneof![Just(1),Just(2),Just(3),Just(4),Just(7)]) { bytes.push(tag); diff --git a/src/storable/tuples.rs b/src/storable/tuples.rs index 26cebe84..979e8093 100644 --- a/src/storable/tuples.rs +++ b/src/storable/tuples.rs @@ -42,7 +42,25 @@ where let b = B::from_bytes(Cow::Borrowed(&bytes[a_max_size..a_max_size + b_len])); (a, b) } - _ => todo!("Deserializing tuples with unbounded types is not yet supported."), + _ => { + let mut offset = 0; + let size_length_a = if A::BOUND.is_fixed_size() { + None + } else { + let lengths = decode_size_lengths(bytes[0], 1); + offset += 1; + Some(lengths[0]) + }; + + let (a, read) = + decode_tuple_element::(&bytes[offset..], size_length_a, false); + offset += read; + let (b, read) = decode_tuple_element::(&bytes[offset..], None, true); + offset += read; + + debug_assert_eq!(offset, bytes.len()); + (a, b) + } } } @@ -100,7 +118,35 @@ where bytes } - _ => todo!("Serializing tuples with unbounded types is not yet supported."), + _ => { + let a_bytes = a.to_bytes(); + let b_bytes = b.to_bytes(); + let a_size = a_bytes.len(); + let b_size = b_bytes.len(); + + // If A is variable-size we need a 1B size_lengths header and A's size prefix. + // B is the last element, so its size is always inferred from remaining bytes. + let sizes_overhead = if A::BOUND.is_fixed_size() { + 0 + } else { + 1 + bytes_to_store_size(a_size) + }; + + let output_size = a_size + b_size + sizes_overhead; + let mut bytes = vec![0; output_size]; + let mut offset = 0; + + if sizes_overhead != 0 { + bytes[offset] = encode_size_lengths(&[a_size]); + offset += 1; + } + + offset += encode_tuple_element::(&mut bytes[offset..], a_bytes.as_ref(), false); + offset += encode_tuple_element::(&mut bytes[offset..], b_bytes.as_ref(), true); + + debug_assert_eq!(offset, output_size); + bytes + } } } From 0436d80b2e083ce9550de1ba7337fb4b64ca5b6d Mon Sep 17 00:00:00 2001 From: Islam El-Ashi Date: Wed, 22 Apr 2026 15:34:08 +0200 Subject: [PATCH 2/3] . --- src/storable/tuples.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/storable/tuples.rs b/src/storable/tuples.rs index 979e8093..dd52374b 100644 --- a/src/storable/tuples.rs +++ b/src/storable/tuples.rs @@ -42,7 +42,7 @@ where let b = B::from_bytes(Cow::Borrowed(&bytes[a_max_size..a_max_size + b_len])); (a, b) } - _ => { + Bound::Unbounded => { let mut offset = 0; let size_length_a = if A::BOUND.is_fixed_size() { None @@ -118,7 +118,7 @@ where bytes } - _ => { + Bound::Unbounded => { let a_bytes = a.to_bytes(); let b_bytes = b.to_bytes(); let a_size = a_bytes.len(); From c994c7937ccf92be62ac0bf91f1659215a2c39dc Mon Sep 17 00:00:00 2001 From: Islam El-Ashi Date: Wed, 22 Apr 2026 15:39:10 +0200 Subject: [PATCH 3/3] . --- src/storable/tuples.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/storable/tuples.rs b/src/storable/tuples.rs index dd52374b..01a949d5 100644 --- a/src/storable/tuples.rs +++ b/src/storable/tuples.rs @@ -52,8 +52,7 @@ where Some(lengths[0]) }; - let (a, read) = - decode_tuple_element::(&bytes[offset..], size_length_a, false); + let (a, read) = decode_tuple_element::(&bytes[offset..], size_length_a, false); offset += read; let (b, read) = decode_tuple_element::(&bytes[offset..], None, true); offset += read;