cqlite_core/parser/vint.rs
1//! Variable-length integer encoding/decoding for Cassandra SSTable format
2//!
3//! Cassandra uses a variable-length integer encoding scheme to save space.
4//! This module implements VInt encoding compatible with Cassandra 5+ format.
5//!
6//! VInt Encoding Specification (from Cassandra/ScyllaDB):
7//! - MSB-first encoding with consecutive 1-bits indicating extra bytes
8//! - First byte pattern: [number of extra bytes as 1-bits][0][value bits]
9//! - Example: 110xxxxx indicates 2 extra bytes follow
10//! - Uses ZigZag encoding for signed integers to efficiently encode small negative values
11//! - Maximum 9 bytes total length
12
13use nom::IResult;
14
15/// Maximum bytes a VInt can occupy (Cassandra supports up to 9 bytes total)
16pub const MAX_VINT_SIZE: usize = 9;
17
18/// Error returned by the canonical read-side VInt decoders.
19///
20/// The single failure mode is a buffer that is empty, or shorter than the width
21/// the lead byte's leading-ones count declares. There is no fabricated-value or
22/// framing-dependent path (Issue #1624).
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum VIntError {
25 /// Input ended before the full VInt could be read (covers empty input).
26 Truncated,
27}
28
29/// Decode an UNSIGNED VInt (Cassandra `writeUnsignedVInt`) — the ONE canonical
30/// read-side VInt bit-assembly (Issue #1638, Epic J / J4).
31///
32/// Mirrors the exemplary write-side `storage/serialization/vint.rs`: a single
33/// [`u8::leading_ones`] length computation, continuation bytes loaded with one
34/// [`u64::from_be_bytes`] on a copied array (no per-byte index loop), slice
35/// framing (no `nom::take`), and `#[inline]`. No hardcoded single-byte match
36/// table and no `fixed → zigzag` double-decode fallback.
37///
38/// Returns `(value, bytes_consumed)` where `bytes_consumed == leading_ones + 1`.
39#[inline]
40pub fn decode_unsigned(input: &[u8]) -> Result<(u64, usize), VIntError> {
41 let first = *input.first().ok_or(VIntError::Truncated)?;
42 // A u8 has at most 8 leading ones, so `extra ∈ 0..=8` and `total ∈ 1..=9`
43 // (== MAX_VINT_SIZE). No width can exceed the 9-byte VInt maximum.
44 let extra = first.leading_ones() as usize;
45 let total = extra + 1;
46 if input.len() < total {
47 return Err(VIntError::Truncated);
48 }
49 if extra == 0 {
50 // Single byte: 0xxxxxxx
51 return Ok((first as u64, 1));
52 }
53 // Load the `extra` continuation bytes as the low bits of a u64 via ONE
54 // big-endian read. `rest[..extra]` is in-bounds: `input.len() >= total`.
55 let (_, rest) = input.split_at(1);
56 let mut be = [0u8; 8];
57 be[8 - extra..].copy_from_slice(&rest[..extra]);
58 let tail = u64::from_be_bytes(be);
59 let value = if extra == 8 {
60 // 0xFF lead: no data bits in the first byte; all 8 continuation bytes.
61 tail
62 } else {
63 // Data bits carried in the first byte occupy the low `7 - extra` bits
64 // (extra ∈ 1..=7 ⇒ data_bits_first ∈ 0..=6, so `1 << data_bits_first`
65 // never overflows; extra == 7 ⇒ mask == 0, i.e. the 0xFE no-data case).
66 let data_bits_first = 7 - extra;
67 let mask = (1u64 << data_bits_first) - 1;
68 // extra*8 ≤ 56 and (first & mask) ≤ 6 bits ⇒ ≤ 62 significant bits: no
69 // shift overflow.
70 ((first as u64 & mask) << (extra * 8)) | tail
71 };
72 Ok((value, total))
73}
74
75/// Decode a SIGNED (ZigZag) VInt — [`decode_unsigned`] then ZigZag unmap.
76///
77/// Returns `(value, bytes_consumed)`.
78#[inline]
79pub fn decode_signed(input: &[u8]) -> Result<(i64, usize), VIntError> {
80 let (unsigned, consumed) = decode_unsigned(input)?;
81 Ok((zigzag_decode(unsigned), consumed))
82}
83
84/// Maximum length value accepted by parse_vint_length to prevent overflow attacks.
85/// Set to 1GB as a generous limit that won't cause allocation issues on any platform.
86/// This prevents memory exhaustion attacks via malicious input claiming huge lengths.
87pub const MAX_VINT_LENGTH: i64 = 1024 * 1024 * 1024; // 1GB safety limit
88
89/// Decode a variable-length signed integer from bytes with backward compatibility
90///
91/// This function supports both:
92/// 1. **ZigZag encoding** (legacy/test compatibility)
93/// 2. **BTI format** (Issue #36 compatibility)
94///
95/// # Arguments
96///
97/// * `input` - Input byte slice
98///
99/// # Returns
100///
101/// Tuple of (remaining_bytes, decoded_value)
102pub fn parse_vint(input: &[u8]) -> IResult<&[u8], i64> {
103 // Issue #1638 (J4): one canonical decoder. `parse_vint` is a thin nom
104 // adapter over `decode_signed` (unsigned leading-ones decode + ZigZag). This
105 // is behavior-identical to the old `parse_vint_fixed → parse_zigzag_vint`
106 // pair: `parse_vint_fixed` WAS exactly `decode_signed`, and its zigzag
107 // fallback only ever fired on truncation (where it also errored), so no
108 // complete-buffer result changes.
109 match decode_signed(input) {
110 Ok((value, consumed)) => Ok((&input[consumed..], value)),
111 Err(_) => Err(nom::Err::Error(nom::error::Error::new(
112 input,
113 nom::error::ErrorKind::Eof,
114 ))),
115 }
116}
117
118/// Encode using Cassandra-compatible VInt format
119#[allow(dead_code)]
120fn encode_cassandra_vint(value: i64) -> Vec<u8> {
121 // Handle negative values using two's complement representation
122 let _unsigned_value = if value >= 0 {
123 value as u64
124 } else {
125 // Use two's complement for negative values
126 value as u64 // This will wrap negative values correctly
127 };
128
129 // Determine the number of bytes needed
130 let bytes_needed = if value == 0 {
131 1
132 } else if (-63..=63).contains(&value) {
133 1 // Single byte range for small values
134 } else if (-8192..=8191).contains(&value) {
135 2 // Two bytes
136 } else if (-1048576..=1048575).contains(&value) {
137 3 // Three bytes
138 } else if (-134217728..=134217727).contains(&value) {
139 4 // Four bytes
140 } else {
141 // Calculate bytes needed for larger values
142 let abs_value = value.unsigned_abs();
143 if abs_value <= 0xFF {
144 2
145 } else if abs_value <= 0xFFFF {
146 3
147 } else if abs_value <= 0xFFFFFF {
148 4
149 } else if abs_value <= 0xFFFFFFFF {
150 5
151 } else {
152 8 // Maximum for i64
153 }
154 };
155
156 match bytes_needed {
157 1 => {
158 // Single byte: 1xxxxxxx for values 0-127, 0xxxxxxx for negative -1 to -63
159 if (0..=63).contains(&value) {
160 vec![0x80 | (value as u8)]
161 } else if value == -1 {
162 vec![0xFF]
163 } else if (-63..0).contains(&value) {
164 vec![0xC0 | ((-value) as u8)]
165 } else {
166 // fallback to two bytes
167 encode_cassandra_vint_multi_byte(value, 2)
168 }
169 }
170 2 => encode_cassandra_vint_multi_byte(value, 2),
171 3 => encode_cassandra_vint_multi_byte(value, 3),
172 4 => encode_cassandra_vint_multi_byte(value, 4),
173 _ => encode_cassandra_vint_multi_byte(value, bytes_needed),
174 }
175}
176
177/// Encode multi-byte Cassandra VInt with proper leading bit pattern
178#[allow(dead_code)]
179fn encode_cassandra_vint_multi_byte(value: i64, num_bytes: usize) -> Vec<u8> {
180 let mut result = vec![0u8; num_bytes];
181
182 // Set the leading bit pattern: n-1 leading ones followed by a zero
183 let leading_ones = num_bytes - 1;
184 let first_byte_mask = (0xFF << (8 - leading_ones)) & 0xFF;
185
186 // Convert value to bytes (using two's complement for negatives)
187 let value_bytes = if value >= 0 {
188 value.to_be_bytes()
189 } else {
190 (value as u64).to_be_bytes() // Two's complement representation
191 };
192
193 // Place the value in the remaining bits
194 let data_bits = (num_bytes * 8) - leading_ones - 1; // Total data bits available
195 let data_bytes = data_bits.div_ceil(8); // How many bytes we need for data
196
197 // Copy the relevant bytes from value_bytes
198 let start_idx = 8 - data_bytes;
199 for (i, &byte) in value_bytes[start_idx..].iter().enumerate() {
200 if i == 0 {
201 // First byte: combine leading pattern with data
202 let data_mask = (1u8 << (8 - leading_ones - 1)) - 1;
203 result[0] = first_byte_mask as u8 | (byte & data_mask);
204 } else {
205 result[i] = byte;
206 }
207 }
208
209 result
210}
211
212/// Encode a signed integer using ZigZag encoding (backward compatibility)
213#[allow(dead_code)]
214fn encode_zigzag_vint(value: i64) -> Vec<u8> {
215 let unsigned_value = zigzag_encode(value);
216
217 if unsigned_value <= 0x7F {
218 // Single byte: 0xxxxxxx
219 vec![unsigned_value as u8]
220 } else if unsigned_value <= 0x3FFF {
221 // Two bytes: 10xxxxxx xxxxxxxx
222 let high = ((unsigned_value >> 8) & 0x3F) | 0x80;
223 let low = unsigned_value & 0xFF;
224 vec![high as u8, low as u8]
225 } else if unsigned_value <= 0x1FFFFF {
226 // Three bytes: 110xxxxx xxxxxxxx xxxxxxxx
227 let high = ((unsigned_value >> 16) & 0x1F) | 0xC0;
228 let mid = (unsigned_value >> 8) & 0xFF;
229 let low = unsigned_value & 0xFF;
230 vec![high as u8, mid as u8, low as u8]
231 } else {
232 // For larger values, use a simplified multi-byte format
233 let bytes = unsigned_value.to_be_bytes();
234 let mut result = vec![0xF0]; // Marker for extended format
235
236 // Find the first non-zero byte and include remaining bytes
237 let start = bytes.iter().position(|&b| b != 0).unwrap_or(7);
238 result.extend_from_slice(&bytes[start..]);
239 result
240 }
241}
242
243/// ZigZag encode a signed integer to unsigned (for efficient small negative number encoding)
244///
245/// ZigZag encoding maps signed integers to unsigned integers so that numbers
246/// with small absolute values have small encodings:
247/// 0 -> 0, -1 -> 1, 1 -> 2, -2 -> 3, 2 -> 4, -3 -> 5, ...
248#[allow(dead_code)]
249pub fn zigzag_encode(value: i64) -> u64 {
250 ((value << 1) ^ (value >> 63)) as u64
251}
252
253/// ZigZag decode an unsigned integer back to signed
254#[allow(dead_code)]
255pub fn zigzag_decode(value: u64) -> i64 {
256 ((value >> 1) ^ ((!0u64).wrapping_mul(value & 1))) as i64
257}
258
259/// Calculate the number of bytes needed to encode a value
260///
261/// Cassandra VInt encoding boundaries:
262/// - 1 byte: 0xxxxxxx -> 0 to 127 (7 bits)
263/// - 2 bytes: 10xxxxxx xxxxxxxx -> 0 to 16383 (14 bits: 6+8)
264/// - 3 bytes: 110xxxxx xxxxxxxx xxxxxxxx -> 0 to 2097151 (21 bits: 5+16)
265/// - etc.
266#[allow(dead_code)]
267fn vint_size(value: u64) -> usize {
268 if value == 0 {
269 return 1;
270 }
271
272 // Cassandra VInt boundaries based on actual capacity
273 if value <= 127 {
274 // 2^7 - 1 (7 bits)
275 1
276 } else if value <= 16383 {
277 // 2^14 - 1 (14 bits)
278 2
279 } else if value <= 2097151 {
280 // 2^21 - 1 (21 bits)
281 3
282 } else if value <= 268435455 {
283 // 2^28 - 1 (28 bits)
284 4
285 } else if value <= 34359738367 {
286 // 2^35 - 1 (35 bits)
287 5
288 } else if value <= 4398046511103 {
289 // 2^42 - 1 (42 bits)
290 6
291 } else if value <= 562949953421311 {
292 // 2^49 - 1 (49 bits)
293 7
294 } else if value <= 72057594037927935 {
295 // 2^56 - 1 (56 bits)
296 8
297 } else {
298 9 // Maximum size
299 }
300}
301
302/// Encode a signed integer as a variable-length integer with backward compatibility
303///
304/// This function now prioritizes Cassandra-compatible format for better
305/// compatibility with standard Cassandra VInt encoding.
306///
307/// # Arguments
308///
309/// * `value` - The integer value to encode
310///
311/// # Returns
312///
313/// Vector of bytes representing the VInt-encoded value
314pub fn encode_vint(value: i64) -> Vec<u8> {
315 encode_vint_zigzag(value)
316}
317
318/// Encode VInt using original ZigZag format for backward compatibility
319pub fn encode_vint_zigzag(value: i64) -> Vec<u8> {
320 let unsigned_value = zigzag_encode(value);
321
322 if unsigned_value <= 0x7F {
323 // Single byte: 0xxxxxxx
324 vec![unsigned_value as u8]
325 } else if unsigned_value <= 0x3FFF {
326 // Two bytes: 10xxxxxx xxxxxxxx
327 let byte0 = 0x80 | ((unsigned_value >> 8) & 0x3F) as u8;
328 let byte1 = (unsigned_value & 0xFF) as u8;
329 vec![byte0, byte1]
330 } else if unsigned_value <= 0x1FFFFF {
331 // Three bytes: 110xxxxx xxxxxxxx xxxxxxxx
332 let byte0 = 0xC0 | ((unsigned_value >> 16) & 0x1F) as u8;
333 let byte1 = ((unsigned_value >> 8) & 0xFF) as u8;
334 let byte2 = (unsigned_value & 0xFF) as u8;
335 vec![byte0, byte1, byte2]
336 } else if unsigned_value <= 0xFFFFFFF {
337 // Four bytes: 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx
338 let byte0 = 0xE0 | ((unsigned_value >> 24) & 0x0F) as u8;
339 let byte1 = ((unsigned_value >> 16) & 0xFF) as u8;
340 let byte2 = ((unsigned_value >> 8) & 0xFF) as u8;
341 let byte3 = (unsigned_value & 0xFF) as u8;
342 vec![byte0, byte1, byte2, byte3]
343 } else if unsigned_value <= 0x7FFFFFFFF {
344 // Five bytes: 11110xxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
345 let byte0 = 0xF0 | ((unsigned_value >> 32) & 0x07) as u8;
346 let byte1 = ((unsigned_value >> 24) & 0xFF) as u8;
347 let byte2 = ((unsigned_value >> 16) & 0xFF) as u8;
348 let byte3 = ((unsigned_value >> 8) & 0xFF) as u8;
349 let byte4 = (unsigned_value & 0xFF) as u8;
350 vec![byte0, byte1, byte2, byte3, byte4]
351 } else if unsigned_value <= 0x3FFFFFFFFFF {
352 // Six bytes: 111110xx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
353 let byte0 = 0xF8 | ((unsigned_value >> 40) & 0x03) as u8;
354 let byte1 = ((unsigned_value >> 32) & 0xFF) as u8;
355 let byte2 = ((unsigned_value >> 24) & 0xFF) as u8;
356 let byte3 = ((unsigned_value >> 16) & 0xFF) as u8;
357 let byte4 = ((unsigned_value >> 8) & 0xFF) as u8;
358 let byte5 = (unsigned_value & 0xFF) as u8;
359 vec![byte0, byte1, byte2, byte3, byte4, byte5]
360 } else if unsigned_value <= 0x1FFFFFFFFFFFF {
361 // Seven bytes: 1111110x xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
362 let byte0 = 0xFC | ((unsigned_value >> 48) & 0x01) as u8;
363 let byte1 = ((unsigned_value >> 40) & 0xFF) as u8;
364 let byte2 = ((unsigned_value >> 32) & 0xFF) as u8;
365 let byte3 = ((unsigned_value >> 24) & 0xFF) as u8;
366 let byte4 = ((unsigned_value >> 16) & 0xFF) as u8;
367 let byte5 = ((unsigned_value >> 8) & 0xFF) as u8;
368 let byte6 = (unsigned_value & 0xFF) as u8;
369 vec![byte0, byte1, byte2, byte3, byte4, byte5, byte6]
370 } else if unsigned_value <= 0xFFFFFFFFFFFFFF {
371 // Eight bytes: 11111110 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
372 let byte0 = 0xFE;
373 let byte1 = ((unsigned_value >> 48) & 0xFF) as u8;
374 let byte2 = ((unsigned_value >> 40) & 0xFF) as u8;
375 let byte3 = ((unsigned_value >> 32) & 0xFF) as u8;
376 let byte4 = ((unsigned_value >> 24) & 0xFF) as u8;
377 let byte5 = ((unsigned_value >> 16) & 0xFF) as u8;
378 let byte6 = ((unsigned_value >> 8) & 0xFF) as u8;
379 let byte7 = (unsigned_value & 0xFF) as u8;
380 vec![byte0, byte1, byte2, byte3, byte4, byte5, byte6, byte7]
381 } else {
382 // Nine bytes: 11111111 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
383 let byte0 = 0xFF;
384 let byte1 = ((unsigned_value >> 56) & 0xFF) as u8;
385 let byte2 = ((unsigned_value >> 48) & 0xFF) as u8;
386 let byte3 = ((unsigned_value >> 40) & 0xFF) as u8;
387 let byte4 = ((unsigned_value >> 32) & 0xFF) as u8;
388 let byte5 = ((unsigned_value >> 24) & 0xFF) as u8;
389 let byte6 = ((unsigned_value >> 16) & 0xFF) as u8;
390 let byte7 = ((unsigned_value >> 8) & 0xFF) as u8;
391 let byte8 = (unsigned_value & 0xFF) as u8;
392 vec![
393 byte0, byte1, byte2, byte3, byte4, byte5, byte6, byte7, byte8,
394 ]
395 }
396}
397
398/// Parse unsigned VInt64 for Cassandra timestamps
399///
400/// Matches org/apache/cassandra/io/util/DataInputPlus.readUnsignedVInt()
401/// Used for timestamp deltas and other 64-bit unsigned values.
402///
403/// Encoding format:
404/// - Leading 1-bits indicate number of extra bytes
405/// - Pattern: [n ones][0][data bits]
406/// - Example: 0xxxxxxx = 1 byte, 10xxxxxx xxxxxxxx = 2 bytes
407/// - Maximum 8 extra bytes (9 bytes total) for full 64-bit range
408///
409/// # Arguments
410///
411/// * `input` - Input byte slice
412///
413/// # Returns
414///
415/// Tuple of (remaining_bytes, decoded_u64_value)
416pub fn parse_vuint(input: &[u8]) -> IResult<&[u8], u64> {
417 // Issue #1638 (J4): thin nom adapter over the one canonical `decode_unsigned`.
418 match decode_unsigned(input) {
419 Ok((value, consumed)) => Ok((&input[consumed..], value)),
420 Err(_) => Err(nom::Err::Error(nom::error::Error::new(
421 input,
422 nom::error::ErrorKind::Eof,
423 ))),
424 }
425}
426
427/// Encode an unsigned integer as a variable-length integer
428pub fn encode_vuint(value: u64) -> Vec<u8> {
429 if value <= 0x7F {
430 // Single byte: 0xxxxxxx
431 vec![value as u8]
432 } else if value <= 0x3FFF {
433 // Two bytes: 10xxxxxx xxxxxxxx
434 let byte0 = 0x80 | ((value >> 8) & 0x3F) as u8;
435 let byte1 = (value & 0xFF) as u8;
436 vec![byte0, byte1]
437 } else if value <= 0x1FFFFF {
438 // Three bytes: 110xxxxx xxxxxxxx xxxxxxxx
439 let byte0 = 0xC0 | ((value >> 16) & 0x1F) as u8;
440 let byte1 = ((value >> 8) & 0xFF) as u8;
441 let byte2 = (value & 0xFF) as u8;
442 vec![byte0, byte1, byte2]
443 } else if value <= 0xFFFFFFF {
444 // Four bytes: 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx
445 let byte0 = 0xE0 | ((value >> 24) & 0x0F) as u8;
446 let byte1 = ((value >> 16) & 0xFF) as u8;
447 let byte2 = ((value >> 8) & 0xFF) as u8;
448 let byte3 = (value & 0xFF) as u8;
449 vec![byte0, byte1, byte2, byte3]
450 } else if value <= 0x7FFFFFFFF {
451 // Five bytes: 11110xxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
452 let byte0 = 0xF0 | ((value >> 32) & 0x07) as u8;
453 let byte1 = ((value >> 24) & 0xFF) as u8;
454 let byte2 = ((value >> 16) & 0xFF) as u8;
455 let byte3 = ((value >> 8) & 0xFF) as u8;
456 let byte4 = (value & 0xFF) as u8;
457 vec![byte0, byte1, byte2, byte3, byte4]
458 } else if value <= 0x3FFFFFFFFFF {
459 // Six bytes: 111110xx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
460 let byte0 = 0xF8 | ((value >> 40) & 0x03) as u8;
461 let byte1 = ((value >> 32) & 0xFF) as u8;
462 let byte2 = ((value >> 24) & 0xFF) as u8;
463 let byte3 = ((value >> 16) & 0xFF) as u8;
464 let byte4 = ((value >> 8) & 0xFF) as u8;
465 let byte5 = (value & 0xFF) as u8;
466 vec![byte0, byte1, byte2, byte3, byte4, byte5]
467 } else if value <= 0x1FFFFFFFFFFFF {
468 // Seven bytes: 1111110x xxxxxxxx ... xxxxxxxx
469 let byte0 = 0xFC | ((value >> 48) & 0x01) as u8;
470 let byte1 = ((value >> 40) & 0xFF) as u8;
471 let byte2 = ((value >> 32) & 0xFF) as u8;
472 let byte3 = ((value >> 24) & 0xFF) as u8;
473 let byte4 = ((value >> 16) & 0xFF) as u8;
474 let byte5 = ((value >> 8) & 0xFF) as u8;
475 let byte6 = (value & 0xFF) as u8;
476 vec![byte0, byte1, byte2, byte3, byte4, byte5, byte6]
477 } else if value <= 0xFFFFFFFFFFFFFF {
478 // Eight bytes: 11111110 xxxxxxxx ... xxxxxxxx
479 let byte0 = 0xFE;
480 let byte1 = ((value >> 48) & 0xFF) as u8;
481 let byte2 = ((value >> 40) & 0xFF) as u8;
482 let byte3 = ((value >> 32) & 0xFF) as u8;
483 let byte4 = ((value >> 24) & 0xFF) as u8;
484 let byte5 = ((value >> 16) & 0xFF) as u8;
485 let byte6 = ((value >> 8) & 0xFF) as u8;
486 let byte7 = (value & 0xFF) as u8;
487 vec![byte0, byte1, byte2, byte3, byte4, byte5, byte6, byte7]
488 } else {
489 // Nine bytes: 11111111 xxxxxxxx ... xxxxxxxx (full 8 bytes follow)
490 let byte0 = 0xFF;
491 let byte1 = ((value >> 56) & 0xFF) as u8;
492 let byte2 = ((value >> 48) & 0xFF) as u8;
493 let byte3 = ((value >> 40) & 0xFF) as u8;
494 let byte4 = ((value >> 32) & 0xFF) as u8;
495 let byte5 = ((value >> 24) & 0xFF) as u8;
496 let byte6 = ((value >> 16) & 0xFF) as u8;
497 let byte7 = ((value >> 8) & 0xFF) as u8;
498 let byte8 = (value & 0xFF) as u8;
499 vec![
500 byte0, byte1, byte2, byte3, byte4, byte5, byte6, byte7, byte8,
501 ]
502 }
503}
504
505/// Parse an UNSIGNED VInt length/count field (Cassandra `writeUnsignedVInt`).
506///
507/// # Producer classification (Issue #1623)
508///
509/// Cassandra encodes length, count, and collection-element-count fields with
510/// `writeUnsignedVInt` — UNSIGNED, no ZigZag (see Appendix B, encodings cheat
511/// sheet). ZigZag is used ONLY for genuinely-signed deltas. This function
512/// therefore routes through [`parse_vuint`], NOT the signed [`parse_vint`].
513///
514/// The prior implementation routed through the signed ZigZag decoder, silently
515/// mis-reading any unsigned length whose bit pattern differs under ZigZag while
516/// consuming the same byte count (e.g. raw `0x05` is length 5, but ZigZag reads
517/// it as -3 → error; `0x04` is length 4 but ZigZag reads 2).
518///
519/// ## Call-site verdicts
520///
521/// FLIP-TO-UNSIGNED — bytes written by Cassandra, or read-only parsers that
522/// model a Cassandra on-disk structure (`writeUnsignedVInt`). These call
523/// [`parse_vint_length`]:
524/// - Data.db rows/cells: `storage/sstable/row_cell_state_machine` and
525/// `reader/parsing/{key_parsing,block_entries,value_parsing,comparator_value_parsing}`.
526/// - Cassandra v5 collection element counts/lengths: the `parse_*_v5_format`
527/// paths in `parser/types/collections.rs` (a mixed file — its legacy paths
528/// are KEEP-SIGNED, see below).
529/// - Header-spec structural length/count fields: `storage/.../header_spec.rs`
530/// `VBytes`/`Array`/`Map` (read-only Cassandra header spec; no CQLite ZigZag
531/// writer to pair with).
532///
533/// KEEP-SIGNED — CQLite-internal round-trips whose paired writer emits ZigZag
534/// `encode_vint`; flipping one side would corrupt the pair. These call
535/// [`parse_vint_length_signed`]:
536/// - Legacy/schema/registry collection counts in `parser/types/collections.rs`,
537/// plus `parser/types/{primitives,udt}` (all paired with `serialize_cql_value`
538/// in `parser/types/mod.rs`).
539/// - `parser/statistics.rs` (self-encoded round-trip; the production Statistics.db
540/// read path is `enhanced_statistics_parser`, not these functions).
541/// - `parser/header.rs` standard body (paired with `serialize_sstable_header`
542/// `encode_vint`; real V5 data is short-circuited via
543/// `parse_cassandra5_simplified_header`).
544/// - Range-tombstone `range_start`/`range_end` bounds in
545/// `parser/types/tombstones.rs` (paired with `serialize_cql_value`
546/// `encode_vint`).
547///
548/// # Safety
549/// Enforces a maximum length of 1GB ([`MAX_VINT_LENGTH`]) to prevent:
550/// - Overflow on 32-bit platforms where usize is 4 bytes
551/// - Memory exhaustion attacks via malicious input claiming huge lengths
552pub fn parse_vint_length(input: &[u8]) -> IResult<&[u8], usize> {
553 let (remaining, value) = parse_vuint(input)?;
554 // Safety: Prevent overflow on usize conversion and allocation attacks.
555 // `value` is u64; MAX_VINT_LENGTH (1GB) is far below usize::MAX on all
556 // supported platforms (>= 32-bit), so the cast below cannot truncate once
557 // the value is bounded.
558 if value > MAX_VINT_LENGTH as u64 {
559 return Err(nom::Err::Error(nom::error::Error::new(
560 input,
561 nom::error::ErrorKind::TooLarge,
562 )));
563 }
564 Ok((remaining, value as usize))
565}
566
567/// Parse a SIGNED (ZigZag) VInt length field written by CQLite's own
568/// serializers.
569///
570/// # CQLite-internal: encoder is ZigZag
571///
572/// A handful of CQLite-internal, self-consistent round-trips write length
573/// prefixes with the ZigZag encoder `parser::vint::encode_vint`
574/// (`serialize_sstable_header` and friends in `parser/header.rs`). Reading
575/// those back requires the SAME ZigZag decode — flipping them to unsigned would
576/// corrupt the round-trip. This helper preserves the historical signed decode
577/// while keeping the identical safety caps as [`parse_vint_length`].
578///
579/// Do NOT use this for Cassandra-produced bytes — those are unsigned; use
580/// [`parse_vint_length`].
581pub fn parse_vint_length_signed(input: &[u8]) -> IResult<&[u8], usize> {
582 let (remaining, value) = parse_vint(input)?;
583 if value < 0 {
584 return Err(nom::Err::Error(nom::error::Error::new(
585 input,
586 nom::error::ErrorKind::Verify,
587 )));
588 }
589 if value > MAX_VINT_LENGTH {
590 return Err(nom::Err::Error(nom::error::Error::new(
591 input,
592 nom::error::ErrorKind::TooLarge,
593 )));
594 }
595 Ok((remaining, value as usize))
596}
597
598#[cfg(test)]
599mod tests {
600 use super::*;
601
602 #[test]
603 fn test_zigzag_encoding() {
604 // Test ZigZag encoding mappings
605 assert_eq!(zigzag_encode(0), 0);
606 assert_eq!(zigzag_encode(-1), 1);
607 assert_eq!(zigzag_encode(1), 2);
608 assert_eq!(zigzag_encode(-2), 3);
609 assert_eq!(zigzag_encode(2), 4);
610 assert_eq!(zigzag_encode(-3), 5);
611 assert_eq!(zigzag_encode(i64::MAX), u64::MAX - 1);
612 assert_eq!(zigzag_encode(i64::MIN), u64::MAX);
613 }
614
615 #[test]
616 fn test_zigzag_roundtrip() {
617 let test_values = vec![0, 1, -1, 127, -128, 32767, -32768, i64::MAX, i64::MIN];
618 for value in test_values {
619 let encoded = zigzag_encode(value);
620 let decoded = zigzag_decode(encoded);
621 assert_eq!(decoded, value, "ZigZag roundtrip failed for {}", value);
622 }
623 }
624
625 #[test]
626 fn test_vint_size_calculation() {
627 assert_eq!(vint_size(0), 1);
628 assert_eq!(vint_size(0x7F), 1); // Max single byte value
629 assert_eq!(vint_size(0x80), 2); // Min two byte value
630 assert_eq!(vint_size(0x3FFF), 2); // Max two byte value
631 assert_eq!(vint_size(0x4000), 3); // Min three byte value
632 }
633
634 #[test]
635 fn test_vint_single_byte_encoding() {
636 // Test small values that fit in single byte (original ZigZag format)
637 for i in 0..=63 {
638 let encoded = encode_vint(i);
639 assert_eq!(encoded.len(), 1, "Value {} should encode to 1 byte", i);
640 assert_eq!(encoded[0] & 0x80, 0, "Single byte should have leading 0");
641
642 let (_, decoded) = parse_vint(&encoded).unwrap();
643 assert_eq!(decoded, i, "Roundtrip failed for {}", i);
644 }
645
646 // Test small negative values
647 for i in -63..=0 {
648 let encoded = encode_vint(i);
649 assert_eq!(encoded.len(), 1, "Value {} should encode to 1 byte", i);
650
651 let (_, decoded) = parse_vint(&encoded).unwrap();
652 assert_eq!(decoded, i, "Roundtrip failed for {}", i);
653 }
654 }
655
656 #[test]
657 fn test_vint_multi_byte_encoding() {
658 // Test two-byte encoding
659 let value = 128;
660 let encoded = encode_vint(value);
661 assert_eq!(encoded.len(), 2, "Value {} should encode to 2 bytes", value);
662 assert_eq!(
663 encoded[0] & 0x80,
664 0x80,
665 "Two-byte encoding should start with 10"
666 );
667 assert_eq!(
668 encoded[0] & 0x40,
669 0,
670 "Two-byte encoding should start with 10"
671 );
672
673 let (_, decoded) = parse_vint(&encoded).unwrap();
674 assert_eq!(decoded, value);
675
676 // Test three-byte encoding
677 let value = 16384; // 2^14
678 let encoded = encode_vint(value);
679 assert_eq!(encoded.len(), 3, "Value {} should encode to 3 bytes", value);
680 assert_eq!(
681 encoded[0] & 0xE0,
682 0xC0,
683 "Three-byte encoding should start with 110"
684 );
685
686 let (_, decoded) = parse_vint(&encoded).unwrap();
687 assert_eq!(decoded, value);
688 }
689
690 #[test]
691 fn test_vint_comprehensive_roundtrip() {
692 let test_values = vec![
693 // Edge cases around single/multi-byte boundaries
694 0,
695 1,
696 -1,
697 63,
698 -63,
699 64,
700 -64,
701 // Powers of 2 and their negatives
702 127,
703 -127,
704 128,
705 -128,
706 255,
707 -255,
708 256,
709 -256,
710 1023,
711 -1023,
712 1024,
713 -1024,
714 2047,
715 -2047,
716 2048,
717 -2048,
718 // Large values
719 32767,
720 -32768,
721 65535,
722 -65535,
723 1000000,
724 -1000000,
725 // Maximum values
726 i32::MAX as i64,
727 i32::MIN as i64,
728 // Very large values (but not max to avoid encoding issues)
729 i64::MAX / 2,
730 i64::MIN / 2,
731 ];
732
733 for value in test_values {
734 let encoded = encode_vint(value);
735 assert!(
736 encoded.len() <= MAX_VINT_SIZE,
737 "Encoded length {} exceeds maximum {} for value {}",
738 encoded.len(),
739 MAX_VINT_SIZE,
740 value
741 );
742
743 let (remaining, decoded) = parse_vint(&encoded).unwrap();
744 assert!(remaining.is_empty(), "Parsing should consume all bytes");
745 assert_eq!(decoded, value, "Roundtrip failed for value {}", value);
746 }
747 }
748
749 #[test]
750 fn test_vint_format_compliance() {
751 // Test specific bit patterns to ensure format compliance
752
753 // Single byte: 0xxxxxxx
754 let encoded = encode_vint(0);
755 assert_eq!(encoded, vec![0x00]);
756
757 let encoded = encode_vint(1);
758 assert_eq!(encoded, vec![0x02]); // ZigZag: 1 -> 2
759
760 let encoded = encode_vint(-1);
761 assert_eq!(encoded, vec![0x01]); // ZigZag: -1 -> 1
762
763 // Two bytes: 10xxxxxx xxxxxxxx
764 let encoded = encode_vint(64);
765 assert_eq!(encoded.len(), 2);
766 assert_eq!(encoded[0] & 0xC0, 0x80); // Should start with 10
767
768 // Verify we can parse back
769 let (_, decoded) = parse_vint(&encoded).unwrap();
770 assert_eq!(decoded, 64);
771 }
772
773 #[test]
774 fn test_vuint_positive() {
775 let value = 1000u64;
776 let encoded = encode_vuint(value);
777 let (_, decoded) = parse_vuint(&encoded).unwrap();
778 assert_eq!(decoded, value);
779 }
780
781 #[test]
782 fn test_vint_length() {
783 // Issue #1623: length fields are UNSIGNED. Encode with the unsigned
784 // encoder so the byte pattern matches what Cassandra writes.
785 let bytes = encode_vuint(42);
786 let (_, length) = parse_vint_length(&bytes).unwrap();
787 assert_eq!(length, 42);
788 }
789
790 /// Issue #1623 ACCEPTANCE TEST: a raw unsigned byte `0x05` is length 5.
791 ///
792 /// This MUST FAIL on `main` (commit 1) where `parse_vint_length` routes
793 /// through the signed ZigZag decoder: `0x05` ZigZag-decodes to -3, which is
794 /// rejected as a negative length. Commit 2 (the fix) routes through the
795 /// unsigned decoder and makes this pass.
796 #[test]
797 fn test_parse_vint_length_unsigned_single_byte_ac() {
798 assert_eq!(parse_vint_length(&[0x05]).unwrap().1, 5);
799 }
800
801 /// Issue #1623: additional unsigned single-byte checks. `0x04` is length 4
802 /// (the signed decoder silently returns 2), `0x7F` is the single-byte max.
803 #[test]
804 fn test_parse_vint_length_unsigned_bit_patterns() {
805 assert_eq!(parse_vint_length(&[0x04]).unwrap().1, 4);
806 assert_eq!(parse_vint_length(&[0x7F]).unwrap().1, 127);
807 assert_eq!(parse_vint_length(&[0x00]).unwrap().1, 0);
808 // Two-byte unsigned: 0x80 0x80 == 128
809 assert_eq!(parse_vint_length(&[0x80, 0x80]).unwrap().1, 128);
810 }
811
812 /// Issue #1623: unsigned length round-trip via the unsigned encoder for a
813 /// spread of values, including odd values whose ZigZag pattern would be
814 /// mis-read by the signed decoder.
815 #[test]
816 fn test_parse_vint_length_unsigned_roundtrip() {
817 for value in [
818 0u64, 1, 2, 3, 4, 5, 63, 64, 127, 128, 255, 256, 16383, 16384, 100_000,
819 ] {
820 let bytes = encode_vuint(value);
821 let (_, decoded) = parse_vint_length(&bytes)
822 .unwrap_or_else(|_| panic!("unsigned length decode failed for {value}"));
823 assert_eq!(decoded as u64, value, "roundtrip failed for {value}");
824 }
825 }
826
827 /// Issue #1623: the signed helper preserves the CQLite-internal ZigZag
828 /// round-trip used by `serialize_sstable_header` (`encode_vint`).
829 #[test]
830 fn test_parse_vint_length_signed_zigzag_roundtrip() {
831 for value in [0i64, 1, 5, 13, 42, 63, 64, 127, 1000] {
832 let bytes = encode_vint(value); // ZigZag encoder
833 let (_, decoded) = parse_vint_length_signed(&bytes)
834 .unwrap_or_else(|_| panic!("signed length decode failed for {value}"));
835 assert_eq!(decoded as i64, value, "signed roundtrip failed for {value}");
836 }
837 // Negative ZigZag values are rejected as lengths.
838 assert!(parse_vint_length_signed(&encode_vint(-10)).is_err());
839 }
840
841 #[test]
842 fn test_collection_vint_debug() {
843 // Debug the collection test issue
844 let encoded_4 = encode_vint(4);
845 println!("encode_vint(4) = {:?}", encoded_4);
846 let (_, decoded_4) = parse_vint(&encoded_4).unwrap();
847 println!("parse_vint({:?}) = {}", encoded_4, decoded_4);
848
849 // Check what [10] decodes to
850 let test_10 = [10u8];
851 let (_, decoded_10) = parse_vint(&test_10).unwrap();
852 println!("parse_vint([10]) = {}", decoded_10);
853
854 // Check what encodes to [10]
855 for i in 0..20 {
856 let encoded = encode_vint(i);
857 if encoded == vec![10] {
858 println!("Value {} encodes to [10]", i);
859 }
860 }
861
862 assert_eq!(decoded_4, 4, "Roundtrip test for 4");
863
864 // Debug the specific collection test issue
865 let long_string = "this is a longer string";
866 let encoded_23 = encode_vint(long_string.len() as i64);
867 println!("encode_vint(23) = {:?}", encoded_23);
868 println!(
869 "String length: {}, bytes: {:?}",
870 long_string.len(),
871 long_string.as_bytes()
872 );
873
874 // Check if the encoded length triggers ASCII corruption detection
875 match parse_vint(&encoded_23) {
876 Ok((_, decoded)) => println!("parse_vint({:?}) = {}", encoded_23, decoded),
877 Err(e) => println!("parse_vint({:?}) failed: {:?}", encoded_23, e),
878 }
879
880 // Debug the specific failing values
881 println!("\n🔍 Debug failing VInt cases:");
882 let failing_values = vec![256, 1048576, 64];
883 for value in failing_values {
884 let encoded = encode_vint(value);
885 println!(
886 "Value {}: encoded={:?}, hex={:02X?}, len={}",
887 value,
888 encoded,
889 encoded,
890 encoded.len()
891 );
892 if !encoded.is_empty() {
893 let first_byte = encoded[0];
894 println!(
895 " First byte: 0x{:02X} ({:08b}), leading_ones: {}",
896 first_byte,
897 first_byte,
898 first_byte.leading_ones()
899 );
900 if encoded.len() > 1 {
901 println!(
902 " Expected leading ones: {}, got: {}",
903 encoded.len() - 1,
904 first_byte.leading_ones()
905 );
906 }
907 }
908 }
909
910 // Test Cassandra expected bytes
911 println!("\n🔍 Testing Cassandra format:");
912 let cassandra_bytes = vec![0xE0, 0x01, 0x00]; // Should decode to 256
913 match parse_vint(&cassandra_bytes) {
914 Ok((_, decoded)) => println!("Cassandra bytes {:?} -> {}", cassandra_bytes, decoded),
915 Err(e) => println!(
916 "Failed to parse Cassandra bytes {:?}: {:?}",
917 cassandra_bytes, e
918 ),
919 }
920 }
921
922 #[test]
923 fn test_vint_errors() {
924 // Test empty input
925 assert!(parse_vint(&[]).is_err());
926
927 // Issue #1623: length fields are unsigned, so they cannot be negative.
928 // Negative-length rejection now lives on the signed helper.
929 assert!(parse_vint_length_signed(&encode_vint(-10)).is_err());
930 // An over-1GB unsigned length is rejected by the length cap.
931 assert!(parse_vint_length(&encode_vuint(2_000_000_000u64)).is_err());
932
933 // Test valid max length encoding (0xFF indicates 8 extra bytes = 9 total bytes)
934 assert!(parse_vint(&[0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).is_ok());
935
936 // Test valid extended formats - should succeed now with backward compatibility
937 // 0xF0 + 7 bytes (8 total) parses via the leading-ones 5-byte framing.
938 assert!(parse_vint(&[0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).is_ok()); // F0 extended format
939 // Issue #1624: 0xFF + 7 bytes (8 total) is a TRUNCATED 0xFF vint — it
940 // declares 8 continuation bytes (9 total) but only 7 are present, so it
941 // is now correctly rejected (was Ok under the buffer-swallowing bug).
942 assert!(parse_vint(&[0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).is_err());
943
944 // Test incomplete data - with backward compatibility, focus on truly invalid cases
945 assert!(parse_vint(&[0x80, 0x00]).is_ok()); // Two-byte format with data
946 assert!(parse_vint(&[0xC0, 0x00, 0x00]).is_ok()); // Three-byte format with data
947
948 // Test truly invalid sequences (corrupted data that shouldn't parse)
949 // Focus on patterns that should be rejected by corruption detection
950 let _corrupted_data = b"data"; // ASCII corruption
951 // Note: corruption detection should catch these, but if not, we accept them
952 // as the new format is more permissive for backward compatibility
953 }
954
955 /// Issue #1624: a bare multi-byte lead byte is a TRUNCATED vint and MUST
956 /// error, not decode to a framing-dependent value. Previously the
957 /// `vint_fixed` single-byte special case decoded `0xC0` as -64 and `0x80`
958 /// as 0, making the result depend on how the slice was framed.
959 #[test]
960 fn test_parse_vint_truncated_lead_byte_is_err_1624() {
961 // 0xC0 has leading_ones=2 → declares a 3-byte vint, 2 bytes missing.
962 assert!(
963 parse_vint(&[0xC0]).is_err(),
964 "bare 0xC0 is a truncated 3-byte vint, must be Err"
965 );
966 // 0x80 has leading_ones=1 → declares a 2-byte vint, 1 byte missing.
967 assert!(
968 parse_vint(&[0x80]).is_err(),
969 "bare 0x80 is a truncated 2-byte vint, must be Err"
970 );
971 }
972
973 /// Issue #1624: framing-agreement — a COMPLETE value decodes identically
974 /// with or without trailing junk, consuming exactly its encoded length. The
975 /// old `vint_fixed` special case made a bare `0x80` decode as 0 (framing
976 /// dependent); a complete `[0x80, 0x80]` must decode the same value whether
977 /// or not extra bytes follow.
978 #[test]
979 fn test_parse_vint_framing_agreement_1624() {
980 let (rem_a, val_a) = parse_vint(&[0x80, 0x80]).expect("complete 2-byte vint");
981 let (rem_b, val_b) =
982 parse_vint(&[0x80, 0x80, 0xAA, 0xBB]).expect("2-byte vint + trailing junk");
983 assert_eq!(val_a, val_b, "decoded value must not depend on framing");
984 assert!(rem_a.is_empty(), "first form consumes all bytes");
985 assert_eq!(rem_b, &[0xAA, 0xBB], "second form leaves exactly the junk");
986 // Truncation direction: bare 0x80 is now correctly an error.
987 assert!(parse_vint(&[0x80]).is_err());
988 }
989
990 #[test]
991 fn test_vint_edge_case_patterns() {
992 // Test maximum single-byte value
993 let max_single = 63;
994 let encoded = encode_vint(max_single);
995 assert_eq!(encoded.len(), 1);
996 assert_eq!(encoded[0] & 0x80, 0); // Original format has leading 0
997
998 // Test minimum two-byte value
999 let min_double = 64;
1000 let encoded = encode_vint(min_double);
1001 assert_eq!(encoded.len(), 2);
1002 assert_eq!(encoded[0] & 0xC0, 0x80); // Two-byte format starts with 10
1003 }
1004
1005 #[test]
1006 fn test_parse_vint_extended_formats() {
1007 // 0xF0 with 5 bytes parses via the leading-ones framing
1008 // (0xF0 has leading_ones=4 → 5-byte vint), so parse_vint succeeds.
1009 let bytes = [0xF0, 0x00, 0x00, 0x00, 0x10];
1010 let _ = parse_vint(&bytes).expect("0xF0 5-byte vint parses");
1011
1012 // Issue #1624: 0xFF declares 8 continuation bytes (9 total). With only
1013 // 4 continuation bytes present this is truncated: the leading-ones
1014 // decoder needs 9 bytes and returns Err.
1015 let bytes = [0xFF, 0x00, 0x00, 0x00, 0x05];
1016 assert!(
1017 parse_vint(&bytes).is_err(),
1018 "truncated 0xFF vint (5 bytes, needs 9) must be Err"
1019 );
1020 }
1021
1022 // Issue #264 / #1623: VInt overflow protection tests (unsigned lengths).
1023 #[test]
1024 fn test_vint_overflow_protection() {
1025 let max = MAX_VINT_LENGTH as u64;
1026
1027 // Test 1: Value exceeding 1GB limit should fail
1028 let large_value = encode_vuint(2_000_000_000u64); // 2GB - exceeds MAX_VINT_LENGTH
1029 let result = parse_vint_length(&large_value);
1030 assert!(
1031 result.is_err(),
1032 "Should reject values > 1GB for length fields"
1033 );
1034
1035 // Test 2: Value just under limit should succeed
1036 let safe_value = encode_vuint(max - 1);
1037 let result = parse_vint_length(&safe_value);
1038 assert!(result.is_ok(), "Should accept values < 1GB");
1039 let (_, length) = result.unwrap();
1040 assert_eq!(length as u64, max - 1);
1041
1042 // Test 3: Exact limit should succeed, one over should fail (> not >=)
1043 let at_limit = encode_vuint(max);
1044 assert!(
1045 parse_vint_length(&at_limit).is_ok(),
1046 "Should accept exactly MAX_VINT_LENGTH"
1047 );
1048 let over_limit = encode_vuint(max + 1);
1049 assert!(
1050 parse_vint_length(&over_limit).is_err(),
1051 "Should reject values > MAX_VINT_LENGTH"
1052 );
1053
1054 // Test 4: Zero should be valid
1055 let zero_value = encode_vuint(0u64);
1056 let result = parse_vint_length(&zero_value);
1057 assert!(result.is_ok(), "Should accept zero");
1058 let (_, length) = result.unwrap();
1059 assert_eq!(length, 0);
1060
1061 // Test 5: Reasonable values (16MB - existing limit in block_entries)
1062 let sixteen_mb = encode_vuint(16 * 1024 * 1024u64);
1063 let result = parse_vint_length(&sixteen_mb);
1064 assert!(result.is_ok(), "Should accept 16MB (common limit)");
1065 }
1066}