Skip to main content

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}