Skip to main content

qail_qdrant/
decoder.rs

1//! Zero-copy Protobuf decoder for Qdrant gRPC responses.
2//!
3//! Decodes protobuf wire format directly without intermediate allocations.
4//! Matches the zero-copy pattern of proto_encoder.rs.
5//!
6//! ## Supported Responses
7//! - SearchResponse → `Vec<ScoredPoint>`
8//! - GetResponse → `Vec<RetrievedPoint>` (same as ScoredPoint with score=0)
9//! - ScrollResponse → `ScrollResult { points, next_offset }`
10
11use crate::error::{QdrantError, QdrantResult};
12use crate::point::{Payload, PayloadValue, PointId, ScoredPoint};
13
14// ============================================================================
15// Wire Type Constants
16// ============================================================================
17
18const WIRE_VARINT: u8 = 0;
19const WIRE_FIXED64: u8 = 1;
20const WIRE_LEN: u8 = 2;
21const WIRE_FIXED32: u8 = 5;
22
23/// Maximum recursion depth for nested protobuf Value decoding.
24/// Prevents stack overflow from crafted deeply nested Struct/List payloads.
25const MAX_DECODE_DEPTH: usize = 32;
26/// Maximum protobuf field number allowed by the wire format.
27const MAX_PROTO_FIELD_NUMBER: u64 = 536_870_911;
28
29fn ensure_non_empty_decoded_name(value: &str, label: &str) -> QdrantResult<()> {
30    if value.trim().is_empty() {
31        return Err(QdrantError::Decode(format!("{label} must not be empty")));
32    }
33    Ok(())
34}
35
36fn duplicate_decode_error(label: &str) -> QdrantError {
37    QdrantError::Decode(format!("Duplicate {label} fields in Qdrant response"))
38}
39
40fn set_decoded_point_id(
41    slot: &mut Option<PointId>,
42    next: PointId,
43    label: &str,
44) -> QdrantResult<()> {
45    if slot.is_some() {
46        return Err(duplicate_decode_error(label));
47    }
48    *slot = Some(next);
49    Ok(())
50}
51
52fn set_decoded_score(slot: &mut Option<f32>, next: f32, label: &str) -> QdrantResult<()> {
53    if slot.is_some() {
54        return Err(duplicate_decode_error(label));
55    }
56    *slot = Some(next);
57    Ok(())
58}
59
60fn set_decoded_string(slot: &mut Option<String>, next: String, label: &str) -> QdrantResult<()> {
61    if slot.is_some() {
62        return Err(duplicate_decode_error(label));
63    }
64    *slot = Some(next);
65    Ok(())
66}
67
68// ============================================================================
69// SearchResponse Field Numbers
70// ============================================================================
71
72const SEARCH_RESULT: u32 = 1;
73
74// ============================================================================
75// ScoredPoint Field Numbers
76// ============================================================================
77
78const SCORED_POINT_ID: u32 = 1;
79const SCORED_POINT_PAYLOAD: u32 = 2;
80const SCORED_POINT_SCORE: u32 = 3;
81const SCORED_POINT_VECTORS: u32 = 6;
82
83// ============================================================================
84// PointId Field Numbers
85// ============================================================================
86
87const POINT_ID_NUM: u32 = 1;
88const POINT_ID_UUID: u32 = 2;
89
90// ============================================================================
91// GetResponse / ScrollResponse Field Numbers
92// ============================================================================
93
94/// GetResponse.result (field 1, repeated RetrievedPoint)
95const GET_RESULT: u32 = 1;
96
97/// ScrollResponse.next_page_offset (field 1, PointId)
98const SCROLL_NEXT_OFFSET: u32 = 1;
99/// ScrollResponse.result (field 2, repeated RetrievedPoint)
100const SCROLL_RESULT: u32 = 2;
101
102/// RetrievedPoint field numbers (same structure as ScoredPoint but no score)
103const RETRIEVED_POINT_ID: u32 = 1;
104const RETRIEVED_POINT_PAYLOAD: u32 = 2;
105const RETRIEVED_POINT_VECTORS: u32 = 4;
106
107// ============================================================================
108// Value message field numbers (for payload decoding)
109// ============================================================================
110
111const VALUE_NULL: u32 = 1;
112const VALUE_DOUBLE: u32 = 2;
113const VALUE_INTEGER: u32 = 3;
114const VALUE_STRING: u32 = 4;
115const VALUE_BOOL: u32 = 5;
116const VALUE_STRUCT: u32 = 6;
117const VALUE_LIST: u32 = 7;
118
119// ============================================================================
120// Vector output field numbers
121// ============================================================================
122
123const VECTORS_OUTPUT_VECTOR: u32 = 1;
124const VECTORS_OUTPUT_NAMED: u32 = 2;
125const NAMED_VECTORS_OUTPUT_ENTRY: u32 = 1;
126const NAMED_VECTOR_ENTRY_KEY: u32 = 1;
127const NAMED_VECTOR_ENTRY_VALUE: u32 = 2;
128const VECTOR_OUTPUT_DEPRECATED_DATA: u32 = 1;
129const VECTOR_OUTPUT_DENSE: u32 = 101;
130const DENSE_VECTOR_DATA: u32 = 1;
131
132// ============================================================================
133// Varint Decoding
134// ============================================================================
135
136/// Decode a varint from the buffer, advancing the cursor.
137#[inline]
138fn decode_varint(buf: &mut &[u8]) -> QdrantResult<u64> {
139    let mut result: u64 = 0;
140
141    for byte_index in 0..10 {
142        if buf.is_empty() {
143            return Err(QdrantError::Decode(
144                "Unexpected end of data in varint".to_string(),
145            ));
146        }
147
148        let byte = buf[0];
149        *buf = &buf[1..];
150        let payload = (byte & 0x7F) as u64;
151
152        if byte_index == 9 && payload > 1 {
153            return Err(QdrantError::Decode("Varint overflows u64".to_string()));
154        }
155
156        result |= payload << (byte_index * 7);
157
158        if byte & 0x80 == 0 {
159            return Ok(result);
160        }
161    }
162
163    Err(QdrantError::Decode("Varint too long".to_string()))
164}
165
166/// Decode a field tag (field_number << 3 | wire_type).
167#[inline]
168fn decode_tag(buf: &mut &[u8]) -> QdrantResult<(u32, u8)> {
169    let tag = decode_varint(buf)?;
170    let field_number = tag >> 3;
171    if field_number == 0 || field_number > MAX_PROTO_FIELD_NUMBER {
172        return Err(QdrantError::Decode(format!(
173            "Invalid protobuf field number: {field_number}"
174        )));
175    }
176    let wire_type = (tag & 0x07) as u8;
177    Ok((field_number as u32, wire_type))
178}
179
180/// Skip a field value based on wire type.
181#[inline]
182fn skip_field(buf: &mut &[u8], wire_type: u8) -> QdrantResult<()> {
183    match wire_type {
184        WIRE_VARINT => {
185            decode_varint(buf)?;
186        }
187        WIRE_FIXED64 => {
188            if buf.len() < 8 {
189                return Err(QdrantError::Decode("Unexpected end of data".to_string()));
190            }
191            *buf = &buf[8..];
192        }
193        WIRE_LEN => {
194            let len = decode_varint(buf)? as usize;
195            if buf.len() < len {
196                return Err(QdrantError::Decode("Unexpected end of data".to_string()));
197            }
198            *buf = &buf[len..];
199        }
200        WIRE_FIXED32 => {
201            if buf.len() < 4 {
202                return Err(QdrantError::Decode("Unexpected end of data".to_string()));
203            }
204            *buf = &buf[4..];
205        }
206        _ => {
207            return Err(QdrantError::Decode(format!(
208                "Unknown wire type: {}",
209                wire_type
210            )));
211        }
212    }
213    Ok(())
214}
215
216/// Read a length-delimited submessage, returning its data slice.
217#[inline]
218fn read_submessage<'a>(buf: &mut &'a [u8]) -> QdrantResult<&'a [u8]> {
219    let len = decode_varint(buf)? as usize;
220    if buf.len() < len {
221        return Err(QdrantError::Decode("Truncated submessage".to_string()));
222    }
223    let data = &buf[..len];
224    *buf = &buf[len..];
225    Ok(data)
226}
227
228fn read_fixed32_f32(buf: &mut &[u8], label: &str) -> QdrantResult<f32> {
229    if buf.len() < 4 {
230        return Err(QdrantError::Decode(format!("Truncated {label}")));
231    }
232    let bytes: [u8; 4] = buf[..4]
233        .try_into()
234        .map_err(|_| QdrantError::Decode(format!("Truncated {label}")))?;
235    let value = f32::from_le_bytes(bytes);
236    if !value.is_finite() {
237        return Err(QdrantError::Decode(format!("Invalid non-finite {label}")));
238    }
239    *buf = &buf[4..];
240    Ok(value)
241}
242
243// ============================================================================
244// SearchResponse Decoder
245// ============================================================================
246
247/// Decode a SearchResponse protobuf message.
248///
249/// # Zero-Copy Pattern
250/// - Parses in a single pass through the buffer
251/// - Minimal allocations (only for result Vec and PointId strings)
252/// - No intermediate struct copies
253pub fn decode_search_response(data: &[u8]) -> QdrantResult<Vec<ScoredPoint>> {
254    let mut results = Vec::new();
255    let mut buf = data;
256
257    while !buf.is_empty() {
258        let (field_number, wire_type) = decode_tag(&mut buf)?;
259
260        match field_number {
261            SEARCH_RESULT => {
262                if wire_type != WIRE_LEN {
263                    return Err(QdrantError::Decode(
264                        "Expected length-delimited for ScoredPoint".to_string(),
265                    ));
266                }
267                let point_data = read_submessage(&mut buf)?;
268                let point = decode_scored_point(point_data)?;
269                results.push(point);
270            }
271            _ => {
272                skip_field(&mut buf, wire_type)?;
273            }
274        }
275    }
276
277    Ok(results)
278}
279
280/// Decode a single ScoredPoint message (with payload support).
281fn decode_scored_point(data: &[u8]) -> QdrantResult<ScoredPoint> {
282    let mut id = None;
283    let mut score = None;
284    let mut payload = Payload::new();
285    let mut vector = None;
286    let mut buf = data;
287
288    while !buf.is_empty() {
289        let (field_number, wire_type) = decode_tag(&mut buf)?;
290
291        match field_number {
292            SCORED_POINT_ID => {
293                if wire_type != WIRE_LEN {
294                    skip_field(&mut buf, wire_type)?;
295                    continue;
296                }
297                let id_data = read_submessage(&mut buf)?;
298                set_decoded_point_id(&mut id, decode_point_id(id_data)?, "scored point id")?;
299            }
300            SCORED_POINT_PAYLOAD => {
301                if wire_type != WIRE_LEN {
302                    skip_field(&mut buf, wire_type)?;
303                    continue;
304                }
305                let entry_data = read_submessage(&mut buf)?;
306                // Payload is map<string, Value> — each entry is a MapEntry message
307                let (key, value) = decode_map_entry(entry_data, 0)?;
308                insert_payload_entry(&mut payload, key, value, "scored point payload")?;
309            }
310            SCORED_POINT_SCORE => {
311                if wire_type != WIRE_FIXED32 {
312                    skip_field(&mut buf, wire_type)?;
313                    continue;
314                }
315                let decoded_score = read_fixed32_f32(&mut buf, "score value")?;
316                set_decoded_score(&mut score, decoded_score, "scored point score")?;
317            }
318            SCORED_POINT_VECTORS => {
319                if wire_type != WIRE_LEN {
320                    skip_field(&mut buf, wire_type)?;
321                    continue;
322                }
323                let vec_data = read_submessage(&mut buf)?;
324                if let Some(next) = decode_vectors(vec_data)? {
325                    set_decoded_vector(&mut vector, next)?;
326                }
327            }
328            _ => {
329                skip_field(&mut buf, wire_type)?;
330            }
331        }
332    }
333
334    let id = id.ok_or_else(|| QdrantError::Decode("Missing point id".to_string()))?;
335
336    Ok(ScoredPoint {
337        id,
338        score: score.unwrap_or(0.0),
339        payload,
340        vector,
341    })
342}
343
344// ============================================================================
345// GetResponse Decoder
346// ============================================================================
347
348/// Decode a GetResponse protobuf message.
349///
350/// Returns `Vec<ScoredPoint>` with score = 0.0 for convenience (Get doesn't
351/// have scores, but we reuse the same struct to keep the API simple).
352pub fn decode_get_response(data: &[u8]) -> QdrantResult<Vec<ScoredPoint>> {
353    let mut results = Vec::new();
354    let mut buf = data;
355
356    while !buf.is_empty() {
357        let (field_number, wire_type) = decode_tag(&mut buf)?;
358
359        match field_number {
360            GET_RESULT => {
361                if wire_type != WIRE_LEN {
362                    skip_field(&mut buf, wire_type)?;
363                    continue;
364                }
365                let point_data = read_submessage(&mut buf)?;
366                let point = decode_retrieved_point(point_data)?;
367                results.push(point);
368            }
369            _ => {
370                skip_field(&mut buf, wire_type)?;
371            }
372        }
373    }
374
375    Ok(results)
376}
377
378// ============================================================================
379// ScrollResponse Decoder
380// ============================================================================
381
382/// Result of a Scroll operation.
383pub struct ScrollResult {
384    /// Points in this page.
385    pub points: Vec<ScoredPoint>,
386    /// Offset for the next page (None when no more pages).
387    pub next_offset: Option<PointId>,
388}
389
390/// Decode a ScrollResponse protobuf message.
391pub fn decode_scroll_response(data: &[u8]) -> QdrantResult<ScrollResult> {
392    let mut points = Vec::new();
393    let mut next_offset = None;
394    let mut buf = data;
395
396    while !buf.is_empty() {
397        let (field_number, wire_type) = decode_tag(&mut buf)?;
398
399        match field_number {
400            SCROLL_RESULT => {
401                if wire_type != WIRE_LEN {
402                    skip_field(&mut buf, wire_type)?;
403                    continue;
404                }
405                let point_data = read_submessage(&mut buf)?;
406                let point = decode_retrieved_point(point_data)?;
407                points.push(point);
408            }
409            SCROLL_NEXT_OFFSET => {
410                if wire_type != WIRE_LEN {
411                    skip_field(&mut buf, wire_type)?;
412                    continue;
413                }
414                let id_data = read_submessage(&mut buf)?;
415                set_decoded_point_id(
416                    &mut next_offset,
417                    decode_point_id(id_data)?,
418                    "scroll next offset",
419                )?;
420            }
421            _ => {
422                skip_field(&mut buf, wire_type)?;
423            }
424        }
425    }
426
427    Ok(ScrollResult {
428        points,
429        next_offset,
430    })
431}
432
433/// Decode a RetrievedPoint message (same shape as ScoredPoint, score = 0).
434fn decode_retrieved_point(data: &[u8]) -> QdrantResult<ScoredPoint> {
435    let mut id = None;
436    let mut payload = Payload::new();
437    let mut vector = None;
438    let mut buf = data;
439
440    while !buf.is_empty() {
441        let (field_number, wire_type) = decode_tag(&mut buf)?;
442
443        match field_number {
444            RETRIEVED_POINT_ID => {
445                if wire_type != WIRE_LEN {
446                    skip_field(&mut buf, wire_type)?;
447                    continue;
448                }
449                let id_data = read_submessage(&mut buf)?;
450                set_decoded_point_id(&mut id, decode_point_id(id_data)?, "retrieved point id")?;
451            }
452            RETRIEVED_POINT_PAYLOAD => {
453                if wire_type != WIRE_LEN {
454                    skip_field(&mut buf, wire_type)?;
455                    continue;
456                }
457                let entry_data = read_submessage(&mut buf)?;
458                let (key, value) = decode_map_entry(entry_data, 0)?;
459                insert_payload_entry(&mut payload, key, value, "retrieved point payload")?;
460            }
461            RETRIEVED_POINT_VECTORS => {
462                if wire_type != WIRE_LEN {
463                    skip_field(&mut buf, wire_type)?;
464                    continue;
465                }
466                let vec_data = read_submessage(&mut buf)?;
467                if let Some(next) = decode_vectors(vec_data)? {
468                    set_decoded_vector(&mut vector, next)?;
469                }
470            }
471            _ => {
472                skip_field(&mut buf, wire_type)?;
473            }
474        }
475    }
476
477    let id = id.ok_or_else(|| QdrantError::Decode("Missing point id".to_string()))?;
478
479    Ok(ScoredPoint {
480        id,
481        score: 0.0,
482        payload,
483        vector,
484    })
485}
486
487// ============================================================================
488// Payload Decoder (map<string, Value>)
489// ============================================================================
490
491/// Decode a map entry (MapFieldEntry with string key, Value value).
492///
493/// Protobuf maps are encoded as repeated fields where each entry is:
494/// ```text
495/// message MapEntry {
496///   string key = 1;
497///   Value value = 2;
498/// }
499/// ```
500fn decode_map_entry(data: &[u8], depth: usize) -> QdrantResult<(String, PayloadValue)> {
501    let mut key = None;
502    let mut value = None;
503    let mut buf = data;
504
505    while !buf.is_empty() {
506        let (field_number, wire_type) = decode_tag(&mut buf)?;
507
508        match field_number {
509            1 => {
510                // key (string)
511                if wire_type != WIRE_LEN {
512                    return Err(QdrantError::Decode(
513                        "Invalid wire type for payload map key".to_string(),
514                    ));
515                }
516                let s_data = read_submessage(&mut buf)?;
517                let decoded_key = std::str::from_utf8(s_data).map_err(|e| {
518                    QdrantError::Decode(format!("Invalid UTF-8 payload map key: {}", e))
519                })?;
520                set_decoded_string(&mut key, decoded_key.to_string(), "payload map key")?;
521            }
522            2 => {
523                // value (Value message)
524                if wire_type != WIRE_LEN {
525                    return Err(QdrantError::Decode(
526                        "Invalid wire type for payload map value".to_string(),
527                    ));
528                }
529                let v_data = read_submessage(&mut buf)?;
530                set_decoded_payload_value(
531                    &mut value,
532                    decode_value_with_depth(v_data, depth)?,
533                    "payload map value",
534                )?;
535            }
536            _ => {
537                skip_field(&mut buf, wire_type)?;
538            }
539        }
540    }
541
542    let key = key.ok_or_else(|| QdrantError::Decode("Missing payload map key".to_string()))?;
543    ensure_non_empty_decoded_name(&key, "Payload map key")?;
544    let value =
545        value.ok_or_else(|| QdrantError::Decode("Missing payload map value".to_string()))?;
546    Ok((key, value))
547}
548
549fn insert_payload_entry(
550    payload: &mut Payload,
551    key: String,
552    value: PayloadValue,
553    label: &str,
554) -> QdrantResult<()> {
555    if payload.contains_key(&key) {
556        return Err(QdrantError::Decode(format!(
557            "Duplicate payload key in {label}: {key}"
558        )));
559    }
560    payload.insert(key, value);
561    Ok(())
562}
563
564fn set_decoded_payload_value(
565    slot: &mut Option<PayloadValue>,
566    next: PayloadValue,
567    label: &str,
568) -> QdrantResult<()> {
569    if slot.is_some() {
570        return Err(duplicate_decode_error(label));
571    }
572    *slot = Some(next);
573    Ok(())
574}
575
576/// Decode a protobuf Value message into PayloadValue.
577///
578/// ```text
579/// message Value {
580///   oneof kind {
581///     NullValue null_value = 1;
582///     double double_value = 2;
583///     int64 integer_value = 3;
584///     string string_value = 4;
585///     bool bool_value = 5;
586///     Struct struct_value = 6;
587///     ListValue list_value = 7;
588///   }
589/// }
590/// ```
591#[cfg(test)]
592fn decode_value(data: &[u8]) -> QdrantResult<PayloadValue> {
593    decode_value_with_depth(data, 0)
594}
595
596/// Decode a protobuf Value message into PayloadValue with depth tracking.
597fn decode_value_with_depth(data: &[u8], depth: usize) -> QdrantResult<PayloadValue> {
598    if depth > MAX_DECODE_DEPTH {
599        return Err(QdrantError::Decode(
600            "Payload value nesting exceeds maximum depth".to_string(),
601        ));
602    }
603
604    let mut value = None;
605    let mut buf = data;
606
607    while !buf.is_empty() {
608        let (field_number, wire_type) = decode_tag(&mut buf)?;
609
610        match field_number {
611            VALUE_NULL => {
612                // NullValue enum (varint)
613                if wire_type == WIRE_VARINT {
614                    let v = decode_varint(&mut buf)?;
615                    if v != 0 {
616                        return Err(QdrantError::Decode(format!(
617                            "Invalid payload null enum value: {v}"
618                        )));
619                    }
620                } else {
621                    return Err(QdrantError::Decode(
622                        "Invalid wire type for payload null value".to_string(),
623                    ));
624                }
625                set_decoded_payload_value(&mut value, PayloadValue::Null, "payload value kind")?;
626            }
627            VALUE_DOUBLE => {
628                // double (fixed64)
629                if wire_type != WIRE_FIXED64 {
630                    return Err(QdrantError::Decode(
631                        "Invalid wire type for payload double value".to_string(),
632                    ));
633                }
634                if buf.len() < 8 {
635                    return Err(QdrantError::Decode(
636                        "Truncated payload double value".to_string(),
637                    ));
638                }
639                let bytes: [u8; 8] = buf[..8].try_into().map_err(|_| {
640                    QdrantError::Decode("Truncated payload double value".to_string())
641                })?;
642                let float_value = f64::from_le_bytes(bytes);
643                buf = &buf[8..];
644                if !float_value.is_finite() {
645                    return Err(QdrantError::Decode(
646                        "Invalid non-finite payload float value".to_string(),
647                    ));
648                }
649                set_decoded_payload_value(
650                    &mut value,
651                    PayloadValue::Float(float_value),
652                    "payload value kind",
653                )?;
654            }
655            VALUE_INTEGER => {
656                // int64 (varint)
657                if wire_type != WIRE_VARINT {
658                    return Err(QdrantError::Decode(
659                        "Invalid wire type for payload integer value".to_string(),
660                    ));
661                }
662                let n = decode_varint(&mut buf)? as i64;
663                set_decoded_payload_value(
664                    &mut value,
665                    PayloadValue::Integer(n),
666                    "payload value kind",
667                )?;
668            }
669            VALUE_STRING => {
670                // string (len-delimited)
671                if wire_type != WIRE_LEN {
672                    return Err(QdrantError::Decode(
673                        "Invalid wire type for payload string value".to_string(),
674                    ));
675                }
676                let s_data = read_submessage(&mut buf)?;
677                let s = std::str::from_utf8(s_data)
678                    .map_err(|e| {
679                        QdrantError::Decode(format!("Invalid UTF-8 payload string: {}", e))
680                    })?
681                    .to_string();
682                set_decoded_payload_value(
683                    &mut value,
684                    PayloadValue::String(s),
685                    "payload value kind",
686                )?;
687            }
688            VALUE_BOOL => {
689                // bool (varint)
690                if wire_type != WIRE_VARINT {
691                    return Err(QdrantError::Decode(
692                        "Invalid wire type for payload bool value".to_string(),
693                    ));
694                }
695                let v = decode_varint(&mut buf)?;
696                if v > 1 {
697                    return Err(QdrantError::Decode(format!(
698                        "Invalid payload bool value: {v}"
699                    )));
700                }
701                set_decoded_payload_value(
702                    &mut value,
703                    PayloadValue::Bool(v != 0),
704                    "payload value kind",
705                )?;
706            }
707            VALUE_STRUCT => {
708                // Struct (len-delimited) — map<string, Value>
709                if wire_type != WIRE_LEN {
710                    return Err(QdrantError::Decode(
711                        "Invalid wire type for payload object value".to_string(),
712                    ));
713                }
714                let struct_data = read_submessage(&mut buf)?;
715                let map = decode_struct_fields_with_depth(struct_data, depth + 1)?;
716                set_decoded_payload_value(
717                    &mut value,
718                    PayloadValue::Object(map),
719                    "payload value kind",
720                )?;
721            }
722            VALUE_LIST => {
723                // ListValue (len-delimited) — repeated Value
724                if wire_type != WIRE_LEN {
725                    return Err(QdrantError::Decode(
726                        "Invalid wire type for payload list value".to_string(),
727                    ));
728                }
729                let list_data = read_submessage(&mut buf)?;
730                let items = decode_list_values_with_depth(list_data, depth + 1)?;
731                set_decoded_payload_value(
732                    &mut value,
733                    PayloadValue::List(items),
734                    "payload value kind",
735                )?;
736            }
737            _ => {
738                skip_field(&mut buf, wire_type)?;
739            }
740        }
741    }
742
743    value.ok_or_else(|| QdrantError::Decode("Missing payload value kind".to_string()))
744}
745
746/// Decode Struct.fields with depth tracking.
747fn decode_struct_fields_with_depth(
748    data: &[u8],
749    depth: usize,
750) -> QdrantResult<std::collections::HashMap<String, PayloadValue>> {
751    let mut map = std::collections::HashMap::new();
752    if depth > MAX_DECODE_DEPTH {
753        return Err(QdrantError::Decode(
754            "Payload object nesting exceeds maximum depth".to_string(),
755        ));
756    }
757    let mut buf = data;
758
759    while !buf.is_empty() {
760        let (field_number, wire_type) = decode_tag(&mut buf)?;
761        match field_number {
762            1 => {
763                // Struct.fields (field 1, repeated map entry)
764                if wire_type != WIRE_LEN {
765                    return Err(QdrantError::Decode(
766                        "Invalid wire type for payload object field".to_string(),
767                    ));
768                }
769                let entry_data = read_submessage(&mut buf)?;
770                let (key, value) = decode_map_entry(entry_data, depth)?;
771                insert_payload_entry(&mut map, key, value, "payload object")?;
772            }
773            _ => {
774                skip_field(&mut buf, wire_type)?;
775            }
776        }
777    }
778
779    Ok(map)
780}
781
782/// Decode ListValue.values with depth tracking.
783fn decode_list_values_with_depth(data: &[u8], depth: usize) -> QdrantResult<Vec<PayloadValue>> {
784    let mut items = Vec::new();
785    if depth > MAX_DECODE_DEPTH {
786        return Err(QdrantError::Decode(
787            "Payload list nesting exceeds maximum depth".to_string(),
788        ));
789    }
790    let mut buf = data;
791
792    while !buf.is_empty() {
793        let (field_number, wire_type) = decode_tag(&mut buf)?;
794        match field_number {
795            1 => {
796                // ListValue.values (field 1, repeated Value)
797                if wire_type != WIRE_LEN {
798                    return Err(QdrantError::Decode(
799                        "Invalid wire type for payload list item".to_string(),
800                    ));
801                }
802                let v_data = read_submessage(&mut buf)?;
803                let value = decode_value_with_depth(v_data, depth)?;
804                items.push(value);
805            }
806            _ => {
807                skip_field(&mut buf, wire_type)?;
808            }
809        }
810    }
811
812    Ok(items)
813}
814
815// ============================================================================
816// Vectors Decoder
817// ============================================================================
818
819/// Decode VectorsOutput message → Vec<f32>.
820///
821/// ```text
822/// message VectorsOutput {
823///   oneof vectors_options {
824///     VectorOutput vector = 1;
825///     NamedVectorsOutput vectors = 2;
826///   }
827/// }
828/// message VectorOutput {
829///   repeated float data = 1 [deprecated = true];
830///   DenseVector dense = 101;
831/// }
832/// ```
833fn decode_vectors(data: &[u8]) -> QdrantResult<Option<Vec<f32>>> {
834    let mut vector = None;
835    let mut buf = data;
836
837    while !buf.is_empty() {
838        let (field_number, wire_type) = decode_tag(&mut buf)?;
839        match field_number {
840            VECTORS_OUTPUT_VECTOR => {
841                if wire_type != WIRE_LEN {
842                    skip_field(&mut buf, wire_type)?;
843                    continue;
844                }
845                let vec_data = read_submessage(&mut buf)?;
846                if let Some(next) = decode_vector_output(vec_data)? {
847                    set_decoded_vector(&mut vector, next)?;
848                }
849            }
850            VECTORS_OUTPUT_NAMED => {
851                if wire_type != WIRE_LEN {
852                    skip_field(&mut buf, wire_type)?;
853                    continue;
854                }
855                let vectors_data = read_submessage(&mut buf)?;
856                if let Some(next) = decode_named_vectors_output(vectors_data)? {
857                    set_decoded_vector(&mut vector, next)?;
858                }
859            }
860            _ => {
861                skip_field(&mut buf, wire_type)?;
862            }
863        }
864    }
865
866    Ok(vector)
867}
868
869fn set_decoded_vector(slot: &mut Option<Vec<f32>>, next: Vec<f32>) -> QdrantResult<()> {
870    if let Some(existing) = slot
871        && existing != &next
872    {
873        return Err(QdrantError::Decode(
874            "Conflicting vector outputs in Qdrant response".to_string(),
875        ));
876    }
877    *slot = Some(next);
878    Ok(())
879}
880
881fn append_decoded_vector(slot: &mut Option<Vec<f32>>, mut next: Vec<f32>) {
882    slot.get_or_insert_with(Vec::new).append(&mut next);
883}
884
885fn append_decoded_vector_value(slot: &mut Option<Vec<f32>>, next: f32) {
886    slot.get_or_insert_with(Vec::new).push(next);
887}
888
889/// Decode VectorOutput.
890fn decode_vector_output(data: &[u8]) -> QdrantResult<Option<Vec<f32>>> {
891    let mut deprecated_vector = None;
892    let mut dense_vector = None;
893    let mut buf = data;
894
895    while !buf.is_empty() {
896        let (field_number, wire_type) = decode_tag(&mut buf)?;
897        match field_number {
898            VECTOR_OUTPUT_DEPRECATED_DATA => match wire_type {
899                WIRE_LEN => {
900                    let float_data = read_submessage(&mut buf)?;
901                    append_decoded_vector(
902                        &mut deprecated_vector,
903                        decode_packed_f32_vector(float_data)?,
904                    );
905                }
906                WIRE_FIXED32 => {
907                    let value = read_fixed32_f32(&mut buf, "vector value")?;
908                    append_decoded_vector_value(&mut deprecated_vector, value);
909                }
910                _ => {
911                    skip_field(&mut buf, wire_type)?;
912                }
913            },
914            VECTOR_OUTPUT_DENSE => {
915                if wire_type != WIRE_LEN {
916                    skip_field(&mut buf, wire_type)?;
917                    continue;
918                }
919                let dense_data = read_submessage(&mut buf)?;
920                if let Some(next) = decode_dense_vector(dense_data)? {
921                    set_decoded_vector(&mut dense_vector, next)?;
922                }
923            }
924            _ => {
925                skip_field(&mut buf, wire_type)?;
926            }
927        }
928    }
929
930    let mut vector = None;
931    if let Some(next) = deprecated_vector {
932        set_decoded_vector(&mut vector, next)?;
933    }
934    if let Some(next) = dense_vector {
935        set_decoded_vector(&mut vector, next)?;
936    }
937    Ok(vector)
938}
939
940/// Decode DenseVector.data (packed repeated float).
941fn decode_dense_vector(data: &[u8]) -> QdrantResult<Option<Vec<f32>>> {
942    let mut vector = None;
943    let mut buf = data;
944
945    while !buf.is_empty() {
946        let (field_number, wire_type) = decode_tag(&mut buf)?;
947        match field_number {
948            DENSE_VECTOR_DATA => match wire_type {
949                WIRE_LEN => {
950                    let float_data = read_submessage(&mut buf)?;
951                    append_decoded_vector(&mut vector, decode_packed_f32_vector(float_data)?);
952                }
953                WIRE_FIXED32 => {
954                    let value = read_fixed32_f32(&mut buf, "vector value")?;
955                    append_decoded_vector_value(&mut vector, value);
956                }
957                _ => {
958                    skip_field(&mut buf, wire_type)?;
959                }
960            },
961            _ => {
962                skip_field(&mut buf, wire_type)?;
963            }
964        }
965    }
966
967    Ok(vector)
968}
969
970fn decode_named_vectors_output(data: &[u8]) -> QdrantResult<Option<Vec<f32>>> {
971    let mut vector = None;
972    let mut buf = data;
973
974    while !buf.is_empty() {
975        let (field_number, wire_type) = decode_tag(&mut buf)?;
976        match field_number {
977            NAMED_VECTORS_OUTPUT_ENTRY => {
978                if wire_type != WIRE_LEN {
979                    skip_field(&mut buf, wire_type)?;
980                    continue;
981                }
982                let entry_data = read_submessage(&mut buf)?;
983                if let Some(next) = decode_named_vector_output_entry(entry_data)? {
984                    if vector.is_some() {
985                        return Err(QdrantError::Decode(
986                            "Multiple named vectors cannot be represented as a single dense vector"
987                                .to_string(),
988                        ));
989                    }
990                    vector = Some(next);
991                }
992            }
993            _ => {
994                skip_field(&mut buf, wire_type)?;
995            }
996        }
997    }
998
999    Ok(vector)
1000}
1001
1002fn decode_named_vector_output_entry(data: &[u8]) -> QdrantResult<Option<Vec<f32>>> {
1003    let mut key = None;
1004    let mut vector = None;
1005    let mut buf = data;
1006
1007    while !buf.is_empty() {
1008        let (field_number, wire_type) = decode_tag(&mut buf)?;
1009        match field_number {
1010            NAMED_VECTOR_ENTRY_KEY => {
1011                if wire_type != WIRE_LEN {
1012                    return Err(QdrantError::Decode(
1013                        "Invalid wire type for named vector key".to_string(),
1014                    ));
1015                }
1016                let key_data = read_submessage(&mut buf)?;
1017                let decoded_key = std::str::from_utf8(key_data).map_err(|e| {
1018                    QdrantError::Decode(format!("Invalid UTF-8 named vector key: {e}"))
1019                })?;
1020                ensure_non_empty_decoded_name(decoded_key, "Named vector key")?;
1021                set_decoded_string(&mut key, decoded_key.to_string(), "named vector key")?;
1022            }
1023            NAMED_VECTOR_ENTRY_VALUE => {
1024                if wire_type != WIRE_LEN {
1025                    return Err(QdrantError::Decode(
1026                        "Invalid wire type for named vector value".to_string(),
1027                    ));
1028                }
1029                let value_data = read_submessage(&mut buf)?;
1030                if let Some(next) = decode_vector_output(value_data)? {
1031                    set_decoded_vector(&mut vector, next)?;
1032                }
1033            }
1034            _ => {
1035                skip_field(&mut buf, wire_type)?;
1036            }
1037        }
1038    }
1039
1040    if vector.is_some() && key.is_none() {
1041        return Err(QdrantError::Decode("Missing named vector key".to_string()));
1042    }
1043
1044    Ok(vector)
1045}
1046
1047fn decode_packed_f32_vector(float_data: &[u8]) -> QdrantResult<Vec<f32>> {
1048    if float_data.is_empty() {
1049        return Err(QdrantError::Decode("Empty vector data".to_string()));
1050    }
1051    if !float_data.len().is_multiple_of(4) {
1052        return Err(QdrantError::Decode(
1053            "Invalid vector data length".to_string(),
1054        ));
1055    }
1056    let count = float_data.len() / 4;
1057    let mut result = Vec::with_capacity(count);
1058    for i in 0..count {
1059        let offset = i * 4;
1060        let bytes: [u8; 4] = float_data[offset..offset + 4]
1061            .try_into()
1062            .map_err(|_| QdrantError::Decode("Invalid vector data length".to_string()))?;
1063        let value = f32::from_le_bytes(bytes);
1064        if !value.is_finite() {
1065            return Err(QdrantError::Decode(
1066                "Invalid non-finite vector value".to_string(),
1067            ));
1068        }
1069        result.push(value);
1070    }
1071    Ok(result)
1072}
1073
1074// ============================================================================
1075// PointId Decoder
1076// ============================================================================
1077
1078/// Decode a PointId message.
1079fn decode_point_id(data: &[u8]) -> QdrantResult<PointId> {
1080    let mut id = None;
1081    let mut buf = data;
1082
1083    while !buf.is_empty() {
1084        let (field_number, wire_type) = decode_tag(&mut buf)?;
1085
1086        match field_number {
1087            POINT_ID_NUM => {
1088                if wire_type != WIRE_VARINT {
1089                    skip_field(&mut buf, wire_type)?;
1090                    continue;
1091                }
1092                let num = decode_varint(&mut buf)?;
1093                set_decoded_point_id(&mut id, PointId::Num(num), "point id")?;
1094            }
1095            POINT_ID_UUID => {
1096                if wire_type != WIRE_LEN {
1097                    skip_field(&mut buf, wire_type)?;
1098                    continue;
1099                }
1100                let uuid_data = read_submessage(&mut buf)?;
1101                let uuid_str = std::str::from_utf8(uuid_data)
1102                    .map_err(|e| QdrantError::Decode(format!("Invalid UTF-8: {}", e)))?;
1103                ensure_non_empty_decoded_name(uuid_str, "Point UUID")?;
1104                set_decoded_point_id(&mut id, PointId::Uuid(uuid_str.to_string()), "point id")?;
1105            }
1106            _ => {
1107                skip_field(&mut buf, wire_type)?;
1108            }
1109        }
1110    }
1111
1112    id.ok_or_else(|| QdrantError::Decode("Missing point id".to_string()))
1113}
1114
1115// ============================================================================
1116// Tests
1117// ============================================================================
1118
1119#[cfg(test)]
1120mod tests {
1121    use super::*;
1122
1123    #[test]
1124    fn test_decode_varint() {
1125        let mut buf: &[u8] = &[0x01];
1126        assert_eq!(decode_varint(&mut buf).unwrap(), 1);
1127        assert!(buf.is_empty());
1128
1129        let mut buf: &[u8] = &[0xAC, 0x02];
1130        assert_eq!(decode_varint(&mut buf).unwrap(), 300);
1131        assert!(buf.is_empty());
1132    }
1133
1134    #[test]
1135    fn test_decode_varint_rejects_u64_overflow() {
1136        let mut data = [0xFF; 10];
1137        data[9] = 0x7F;
1138        let mut buf: &[u8] = &data;
1139
1140        let err = decode_varint(&mut buf).unwrap_err();
1141        assert!(err.to_string().contains("overflows u64"));
1142    }
1143
1144    #[test]
1145    fn test_decode_varint_rejects_unterminated_tenth_byte() {
1146        let data = [0x80; 10];
1147        let mut buf: &[u8] = &data;
1148
1149        let err = decode_varint(&mut buf).unwrap_err();
1150        assert!(err.to_string().contains("Varint too long"));
1151    }
1152
1153    #[test]
1154    fn test_decode_tag() {
1155        let mut buf: &[u8] = &[0x0A];
1156        let (field, wire) = decode_tag(&mut buf).unwrap();
1157        assert_eq!(field, 1);
1158        assert_eq!(wire, WIRE_LEN);
1159
1160        let mut buf: &[u8] = &[0x1D];
1161        let (field, wire) = decode_tag(&mut buf).unwrap();
1162        assert_eq!(field, 3);
1163        assert_eq!(wire, WIRE_FIXED32);
1164    }
1165
1166    #[test]
1167    fn test_decode_tag_rejects_zero_field_number() {
1168        let mut buf: &[u8] = &[0x00];
1169
1170        let err = decode_tag(&mut buf).unwrap_err();
1171        assert!(err.to_string().contains("Invalid protobuf field number"));
1172    }
1173
1174    #[test]
1175    fn test_decode_tag_rejects_oversized_field_number() {
1176        let mut value = ((MAX_PROTO_FIELD_NUMBER + 1) << 3) | u64::from(WIRE_LEN);
1177        let mut encoded = Vec::new();
1178        while value >= 0x80 {
1179            encoded.push(((value as u8) & 0x7F) | 0x80);
1180            value >>= 7;
1181        }
1182        encoded.push(value as u8);
1183
1184        let mut buf: &[u8] = &encoded;
1185        let err = decode_tag(&mut buf).unwrap_err();
1186        assert!(err.to_string().contains("Invalid protobuf field number"));
1187    }
1188
1189    #[test]
1190    fn test_decode_point_id_num() {
1191        let data = &[0x08, 0x2A];
1192        let id = decode_point_id(data).unwrap();
1193        assert_eq!(id, PointId::Num(42));
1194    }
1195
1196    #[test]
1197    fn test_decode_point_id_explicit_zero() {
1198        let data = &[0x08, 0x00];
1199        let id = decode_point_id(data).unwrap();
1200        assert_eq!(id, PointId::Num(0));
1201    }
1202
1203    #[test]
1204    fn test_decode_point_id_rejects_empty_message() {
1205        let err = decode_point_id(&[]).unwrap_err();
1206        assert!(err.to_string().contains("Missing point id"));
1207    }
1208
1209    #[test]
1210    fn test_decode_point_id_uuid() {
1211        let data = &[0x12, 0x03, b'a', b'b', b'c'];
1212        let id = decode_point_id(data).unwrap();
1213        assert_eq!(id, PointId::Uuid("abc".to_string()));
1214    }
1215
1216    #[test]
1217    fn test_decode_point_id_rejects_duplicate_fields() {
1218        let duplicate_num = &[0x08, 0x01, 0x08, 0x02];
1219        let err = decode_point_id(duplicate_num).unwrap_err();
1220        assert!(err.to_string().contains("Duplicate point id"));
1221
1222        let conflicting_oneof = &[0x08, 0x01, 0x12, 0x03, b'a', b'b', b'c'];
1223        let err = decode_point_id(conflicting_oneof).unwrap_err();
1224        assert!(err.to_string().contains("Duplicate point id"));
1225    }
1226
1227    #[test]
1228    fn test_decode_point_id_rejects_empty_uuid() {
1229        let empty = &[0x12, 0x00];
1230        let err = decode_point_id(empty).unwrap_err();
1231        assert!(err.to_string().contains("Point UUID"));
1232
1233        let blank = &[0x12, 0x01, b' '];
1234        let err = decode_point_id(blank).unwrap_err();
1235        assert!(err.to_string().contains("Point UUID"));
1236    }
1237
1238    #[test]
1239    fn test_decode_scored_point() {
1240        let score_bytes = 0.5f32.to_le_bytes();
1241        let data = &[
1242            0x0A,
1243            0x02,
1244            0x08,
1245            0x01, // id = PointId { num = 1 }
1246            0x1D,
1247            score_bytes[0],
1248            score_bytes[1],
1249            score_bytes[2],
1250            score_bytes[3],
1251        ];
1252
1253        let point = decode_scored_point(data).unwrap();
1254        assert_eq!(point.id, PointId::Num(1));
1255        assert!((point.score - 0.5).abs() < 0.0001);
1256    }
1257
1258    fn push_len_field(out: &mut Vec<u8>, tag: u8, body: &[u8]) {
1259        assert!(body.len() < 128, "test helper only handles short bodies");
1260        out.push(tag);
1261        out.push(body.len() as u8);
1262        out.extend_from_slice(body);
1263    }
1264
1265    fn packed_f32(values: &[f32]) -> Vec<u8> {
1266        let mut bytes = Vec::with_capacity(values.len() * 4);
1267        for value in values {
1268            bytes.extend_from_slice(&value.to_le_bytes());
1269        }
1270        bytes
1271    }
1272
1273    fn current_dense_vectors_output(values: &[f32]) -> Vec<u8> {
1274        let floats = packed_f32(values);
1275
1276        let mut dense = Vec::new();
1277        push_len_field(&mut dense, 0x0A, &floats);
1278
1279        let mut vector_output = Vec::new();
1280        vector_output.extend_from_slice(&[0xAA, 0x06]);
1281        vector_output.push(dense.len() as u8);
1282        vector_output.extend_from_slice(&dense);
1283
1284        let mut vectors_output = Vec::new();
1285        push_len_field(&mut vectors_output, 0x0A, &vector_output);
1286        vectors_output
1287    }
1288
1289    fn deprecated_dense_vectors_output(values: &[f32]) -> Vec<u8> {
1290        let floats = packed_f32(values);
1291
1292        let mut vector_output = Vec::new();
1293        push_len_field(&mut vector_output, 0x0A, &floats);
1294
1295        let mut vectors_output = Vec::new();
1296        push_len_field(&mut vectors_output, 0x0A, &vector_output);
1297        vectors_output
1298    }
1299
1300    fn current_dense_vectors_output_unpacked(values: &[f32]) -> Vec<u8> {
1301        let mut dense = Vec::new();
1302        for value in values {
1303            dense.push(0x0D);
1304            dense.extend_from_slice(&value.to_le_bytes());
1305        }
1306
1307        let mut vector_output = Vec::new();
1308        vector_output.extend_from_slice(&[0xAA, 0x06]);
1309        vector_output.push(dense.len() as u8);
1310        vector_output.extend_from_slice(&dense);
1311
1312        let mut vectors_output = Vec::new();
1313        push_len_field(&mut vectors_output, 0x0A, &vector_output);
1314        vectors_output
1315    }
1316
1317    fn deprecated_dense_vectors_output_unpacked(values: &[f32]) -> Vec<u8> {
1318        let mut vector_output = Vec::new();
1319        for value in values {
1320            vector_output.push(0x0D);
1321            vector_output.extend_from_slice(&value.to_le_bytes());
1322        }
1323
1324        let mut vectors_output = Vec::new();
1325        push_len_field(&mut vectors_output, 0x0A, &vector_output);
1326        vectors_output
1327    }
1328
1329    fn named_dense_vectors_output(name: &str, values: &[f32]) -> Vec<u8> {
1330        let vectors_output = current_dense_vectors_output(values);
1331        let vector_output = &vectors_output[2..];
1332
1333        let mut entry = Vec::new();
1334        push_len_field(&mut entry, 0x0A, name.as_bytes());
1335        push_len_field(&mut entry, 0x12, vector_output);
1336
1337        let mut named = Vec::new();
1338        push_len_field(&mut named, 0x0A, &entry);
1339
1340        let mut output = Vec::new();
1341        push_len_field(&mut output, 0x12, &named);
1342        output
1343    }
1344
1345    fn scored_point_with_vectors(vectors_output: &[u8]) -> Vec<u8> {
1346        let score_bytes = 0.5f32.to_le_bytes();
1347        let mut data = vec![
1348            0x0A,
1349            0x02,
1350            0x08,
1351            0x01, // id = PointId { num = 1 }
1352            0x1D,
1353            score_bytes[0],
1354            score_bytes[1],
1355            score_bytes[2],
1356            score_bytes[3],
1357        ];
1358        push_len_field(&mut data, 0x32, vectors_output);
1359        data
1360    }
1361
1362    fn payload_string_entry(key: &str, value: &str) -> Vec<u8> {
1363        let mut value_message = Vec::new();
1364        push_len_field(&mut value_message, 0x22, value.as_bytes());
1365
1366        let mut entry = Vec::new();
1367        push_len_field(&mut entry, 0x0A, key.as_bytes());
1368        push_len_field(&mut entry, 0x12, &value_message);
1369        entry
1370    }
1371
1372    #[test]
1373    fn test_decode_search_response_accepts_current_dense_vector_output() {
1374        let scored_point = scored_point_with_vectors(&current_dense_vectors_output(&[0.25, 0.75]));
1375        let mut data = Vec::new();
1376        push_len_field(&mut data, 0x0A, &scored_point);
1377
1378        let points = decode_search_response(&data).unwrap();
1379
1380        assert_eq!(points.len(), 1);
1381        assert_eq!(points[0].vector, Some(vec![0.25, 0.75]));
1382    }
1383
1384    #[test]
1385    fn test_decode_search_response_accepts_unpacked_current_dense_vector_output() {
1386        let scored_point =
1387            scored_point_with_vectors(&current_dense_vectors_output_unpacked(&[0.25, 0.75]));
1388        let mut data = Vec::new();
1389        push_len_field(&mut data, 0x0A, &scored_point);
1390
1391        let points = decode_search_response(&data).unwrap();
1392
1393        assert_eq!(points.len(), 1);
1394        assert_eq!(points[0].vector, Some(vec![0.25, 0.75]));
1395    }
1396
1397    #[test]
1398    fn test_decode_scored_point_accepts_deprecated_dense_vector_output() {
1399        let data = scored_point_with_vectors(&deprecated_dense_vectors_output(&[0.25, 0.75]));
1400
1401        let point = decode_scored_point(&data).unwrap();
1402
1403        assert_eq!(point.vector, Some(vec![0.25, 0.75]));
1404    }
1405
1406    #[test]
1407    fn test_decode_scored_point_accepts_unpacked_deprecated_dense_vector_output() {
1408        let data =
1409            scored_point_with_vectors(&deprecated_dense_vectors_output_unpacked(&[0.25, 0.75]));
1410
1411        let point = decode_scored_point(&data).unwrap();
1412
1413        assert_eq!(point.vector, Some(vec![0.25, 0.75]));
1414    }
1415
1416    #[test]
1417    fn test_decode_scored_point_accepts_split_packed_dense_vector_chunks() {
1418        let mut dense = Vec::new();
1419        push_len_field(&mut dense, 0x0A, &packed_f32(&[0.25]));
1420        push_len_field(&mut dense, 0x0A, &packed_f32(&[0.75]));
1421
1422        let mut vector_output = Vec::new();
1423        vector_output.extend_from_slice(&[0xAA, 0x06]);
1424        vector_output.push(dense.len() as u8);
1425        vector_output.extend_from_slice(&dense);
1426
1427        let mut vectors_output = Vec::new();
1428        push_len_field(&mut vectors_output, 0x0A, &vector_output);
1429        let data = scored_point_with_vectors(&vectors_output);
1430
1431        let point = decode_scored_point(&data).unwrap();
1432
1433        assert_eq!(point.vector, Some(vec![0.25, 0.75]));
1434    }
1435
1436    #[test]
1437    fn test_decode_scored_point_accepts_single_named_dense_vector_output() {
1438        let data = scored_point_with_vectors(&named_dense_vectors_output("image", &[0.25, 0.75]));
1439
1440        let point = decode_scored_point(&data).unwrap();
1441
1442        assert_eq!(point.vector, Some(vec![0.25, 0.75]));
1443    }
1444
1445    #[test]
1446    fn test_decode_scored_point_rejects_multiple_named_vectors() {
1447        let mut first = named_dense_vectors_output("image", &[0.25, 0.75]);
1448        let second = named_dense_vectors_output("text", &[0.5, 0.5]);
1449        let second_named_body = &second[2..];
1450        let mut named_body = first.split_off(2);
1451        named_body.extend_from_slice(second_named_body);
1452        let mut output = Vec::new();
1453        push_len_field(&mut output, 0x12, &named_body);
1454        let data = scored_point_with_vectors(&output);
1455
1456        let err = decode_scored_point(&data).unwrap_err();
1457
1458        assert!(err.to_string().contains("Multiple named vectors"));
1459    }
1460
1461    #[test]
1462    fn test_decode_named_vector_entry_rejects_duplicate_key_fields() {
1463        let vector_output = current_dense_vectors_output(&[0.25, 0.75]);
1464        let vector_output = &vector_output[2..];
1465
1466        let mut entry = Vec::new();
1467        push_len_field(&mut entry, 0x0A, b"image");
1468        push_len_field(&mut entry, 0x0A, b"text");
1469        push_len_field(&mut entry, 0x12, vector_output);
1470
1471        let err = decode_named_vector_output_entry(&entry).unwrap_err();
1472
1473        assert!(err.to_string().contains("Duplicate named vector key"));
1474    }
1475
1476    #[test]
1477    fn test_decode_named_vector_entry_rejects_conflicting_value_fields() {
1478        let first = current_dense_vectors_output(&[0.25]);
1479        let second = current_dense_vectors_output(&[0.75]);
1480
1481        let mut entry = Vec::new();
1482        push_len_field(&mut entry, 0x0A, b"image");
1483        push_len_field(&mut entry, 0x12, &first[2..]);
1484        push_len_field(&mut entry, 0x12, &second[2..]);
1485
1486        let err = decode_named_vector_output_entry(&entry).unwrap_err();
1487
1488        assert!(err.to_string().contains("Conflicting vector outputs"));
1489    }
1490
1491    #[test]
1492    fn test_decode_scored_point_rejects_conflicting_vector_fields() {
1493        let mut data = vec![
1494            0x0A, 0x02, 0x08, 0x01, // id = PointId { num = 1 }
1495        ];
1496        push_len_field(&mut data, 0x32, &current_dense_vectors_output(&[0.25]));
1497        push_len_field(&mut data, 0x32, &current_dense_vectors_output(&[0.75]));
1498
1499        let err = decode_scored_point(&data).unwrap_err();
1500
1501        assert!(err.to_string().contains("Conflicting vector outputs"));
1502    }
1503
1504    #[test]
1505    fn test_decode_vector_output_rejects_conflicting_deprecated_and_dense_vectors() {
1506        let deprecated = deprecated_dense_vectors_output(&[0.25]);
1507        let dense = current_dense_vectors_output(&[0.75]);
1508
1509        let mut vector_output = deprecated[2..].to_vec();
1510        vector_output.extend_from_slice(&dense[2..]);
1511
1512        let err = decode_vector_output(&vector_output).unwrap_err();
1513
1514        assert!(err.to_string().contains("Conflicting vector outputs"));
1515    }
1516
1517    #[test]
1518    fn test_decode_scored_point_rejects_duplicate_score_fields() {
1519        let first_score = 0.5f32.to_le_bytes();
1520        let second_score = 0.75f32.to_le_bytes();
1521        let data = &[
1522            0x0A,
1523            0x02,
1524            0x08,
1525            0x01, // id = PointId { num = 1 }
1526            0x1D,
1527            first_score[0],
1528            first_score[1],
1529            first_score[2],
1530            first_score[3],
1531            0x1D,
1532            second_score[0],
1533            second_score[1],
1534            second_score[2],
1535            second_score[3],
1536        ];
1537
1538        let err = decode_scored_point(data).unwrap_err();
1539
1540        assert!(err.to_string().contains("Duplicate scored point score"));
1541    }
1542
1543    #[test]
1544    fn test_decode_scored_point_rejects_duplicate_id_fields() {
1545        let data = &[
1546            0x0A, 0x02, 0x08, 0x01, // id = PointId { num = 1 }
1547            0x0A, 0x02, 0x08, 0x02, // second id = PointId { num = 2 }
1548        ];
1549
1550        let err = decode_scored_point(data).unwrap_err();
1551
1552        assert!(err.to_string().contains("Duplicate scored point id"));
1553    }
1554
1555    #[test]
1556    fn test_decode_scored_point_rejects_duplicate_payload_keys() {
1557        let mut data = vec![
1558            0x0A, 0x02, 0x08, 0x01, // id = PointId { num = 1 }
1559        ];
1560        push_len_field(&mut data, 0x12, &payload_string_entry("tenant_id", "first"));
1561        push_len_field(
1562            &mut data,
1563            0x12,
1564            &payload_string_entry("tenant_id", "second"),
1565        );
1566
1567        let err = decode_scored_point(&data).unwrap_err();
1568
1569        assert!(err.to_string().contains("Duplicate payload key"));
1570    }
1571
1572    #[test]
1573    fn test_decode_scored_point_rejects_malformed_vector() {
1574        let data = &[
1575            0x0A, 0x02, 0x08, 0x01, // id = PointId { num = 1 }
1576            0x32, 0x07, // vectors message length
1577            0x0A, 0x05, // vector message length
1578            0x0A, 0x03, // packed float data length is not divisible by 4
1579            0x00, 0x00, 0x00,
1580        ];
1581
1582        let err = decode_scored_point(data).unwrap_err();
1583        assert!(err.to_string().contains("Invalid vector data length"));
1584    }
1585
1586    #[test]
1587    fn test_decode_scored_point_rejects_non_finite_score() {
1588        let nan = f32::NAN.to_le_bytes();
1589        let data = &[
1590            0x0A, 0x02, 0x08, 0x01, // id = PointId { num = 1 }
1591            0x1D, nan[0], nan[1], nan[2], nan[3],
1592        ];
1593
1594        let err = decode_scored_point(data).unwrap_err();
1595        assert!(err.to_string().contains("non-finite score"));
1596    }
1597
1598    #[test]
1599    fn test_decode_scored_point_rejects_non_finite_vector() {
1600        let nan = f32::NAN.to_le_bytes();
1601        let data = &[
1602            0x0A, 0x02, 0x08, 0x01, // id = PointId { num = 1 }
1603            0x32, 0x08, // vectors message length
1604            0x0A, 0x06, // vector message length
1605            0x0A, 0x04, // packed float data length
1606            nan[0], nan[1], nan[2], nan[3],
1607        ];
1608
1609        let err = decode_scored_point(data).unwrap_err();
1610        assert!(err.to_string().contains("non-finite vector value"));
1611    }
1612
1613    #[test]
1614    fn test_decode_scored_point_rejects_empty_vector_data() {
1615        let data = &[
1616            0x0A, 0x02, 0x08, 0x01, // id = PointId { num = 1 }
1617            0x32, 0x04, // vectors message length
1618            0x0A, 0x02, // vector message length
1619            0x0A, 0x00, // packed float data length = 0
1620        ];
1621
1622        let err = decode_scored_point(data).unwrap_err();
1623        assert!(err.to_string().contains("Empty vector data"));
1624    }
1625
1626    #[test]
1627    fn test_decode_search_response_rejects_point_without_id() {
1628        let score_bytes = 1.0f32.to_le_bytes();
1629        let data = &[
1630            0x0A,
1631            0x05,
1632            0x1D,
1633            score_bytes[0],
1634            score_bytes[1],
1635            score_bytes[2],
1636            score_bytes[3],
1637        ];
1638
1639        let err = decode_search_response(data).unwrap_err();
1640        assert!(err.to_string().contains("Missing point id"));
1641    }
1642
1643    #[test]
1644    fn test_decode_get_response_rejects_point_without_id() {
1645        let data = &[0x0A, 0x00];
1646
1647        let err = decode_get_response(data).unwrap_err();
1648        assert!(err.to_string().contains("Missing point id"));
1649    }
1650
1651    #[test]
1652    fn test_decode_get_response_rejects_duplicate_retrieved_id_fields() {
1653        let retrieved_point = &[
1654            0x0A, 0x02, 0x08, 0x01, // id = PointId { num = 1 }
1655            0x0A, 0x02, 0x08, 0x02, // second id = PointId { num = 2 }
1656        ];
1657        let mut data = Vec::new();
1658        push_len_field(&mut data, 0x0A, retrieved_point);
1659
1660        let err = decode_get_response(&data).unwrap_err();
1661
1662        assert!(err.to_string().contains("Duplicate retrieved point id"));
1663    }
1664
1665    #[test]
1666    fn test_decode_get_response_rejects_duplicate_retrieved_payload_keys() {
1667        let mut retrieved_point = vec![
1668            0x0A, 0x02, 0x08, 0x01, // id = PointId { num = 1 }
1669        ];
1670        push_len_field(
1671            &mut retrieved_point,
1672            0x12,
1673            &payload_string_entry("tenant_id", "first"),
1674        );
1675        push_len_field(
1676            &mut retrieved_point,
1677            0x12,
1678            &payload_string_entry("tenant_id", "second"),
1679        );
1680
1681        let mut data = Vec::new();
1682        push_len_field(&mut data, 0x0A, &retrieved_point);
1683
1684        let err = decode_get_response(&data).unwrap_err();
1685
1686        assert!(err.to_string().contains("Duplicate payload key"));
1687    }
1688
1689    #[test]
1690    fn test_decode_scroll_response_rejects_point_without_id() {
1691        let data = &[0x12, 0x00];
1692
1693        let err = match decode_scroll_response(data) {
1694            Ok(_) => panic!("scroll response without point id must fail"),
1695            Err(err) => err,
1696        };
1697        assert!(err.to_string().contains("Missing point id"));
1698    }
1699
1700    #[test]
1701    fn test_decode_scroll_response_rejects_empty_next_offset() {
1702        let data = &[0x0A, 0x00];
1703
1704        let err = match decode_scroll_response(data) {
1705            Ok(_) => panic!("scroll response with empty next offset must fail"),
1706            Err(err) => err,
1707        };
1708        assert!(err.to_string().contains("Missing point id"));
1709    }
1710
1711    #[test]
1712    fn test_decode_scroll_response_rejects_duplicate_next_offset() {
1713        let data = &[
1714            0x0A, 0x02, 0x08, 0x01, // next_page_offset = 1
1715            0x0A, 0x02, 0x08, 0x02, // next_page_offset = 2
1716        ];
1717
1718        let err = match decode_scroll_response(data) {
1719            Ok(_) => panic!("scroll response with duplicate next offset must fail"),
1720            Err(err) => err,
1721        };
1722
1723        assert!(err.to_string().contains("Duplicate scroll next offset"));
1724    }
1725
1726    #[test]
1727    fn test_decode_search_response_empty() {
1728        let data: &[u8] = &[];
1729        let results = decode_search_response(data).unwrap();
1730        assert!(results.is_empty());
1731    }
1732
1733    #[test]
1734    fn test_decode_value_string() {
1735        // Value { string_value = "hello" }
1736        // field 4 (string_value), wire LEN: tag 0x22, len 5, "hello"
1737        let data = &[0x22, 0x05, b'h', b'e', b'l', b'l', b'o'];
1738        let val = decode_value(data).unwrap();
1739        assert_eq!(val, PayloadValue::String("hello".to_string()));
1740    }
1741
1742    #[test]
1743    fn test_decode_value_integer() {
1744        // Value { integer_value = 42 }
1745        // field 3 (integer_value), wire VARINT: tag 0x18, value 42
1746        let data = &[0x18, 0x2A];
1747        let val = decode_value(data).unwrap();
1748        assert_eq!(val, PayloadValue::Integer(42));
1749    }
1750
1751    #[test]
1752    fn test_decode_value_bool() {
1753        // Value { bool_value = true }
1754        // field 5, wire VARINT: tag 0x28, value 1
1755        let data = &[0x28, 0x01];
1756        let val = decode_value(data).unwrap();
1757        assert_eq!(val, PayloadValue::Bool(true));
1758    }
1759
1760    #[test]
1761    fn test_decode_value_rejects_malformed_bool_varint() {
1762        let data = &[0x28, 0x02];
1763
1764        let err = decode_value(data).unwrap_err();
1765        assert!(err.to_string().contains("Invalid payload bool value"));
1766    }
1767
1768    #[test]
1769    fn test_decode_value_double() {
1770        // Value { double_value = 3.14 }
1771        // field 2, wire FIXED64: tag 0x11
1772        let f_bytes = std::f64::consts::PI.to_le_bytes();
1773        let mut data = vec![0x11];
1774        data.extend_from_slice(&f_bytes);
1775        let val = decode_value(&data).unwrap();
1776        match val {
1777            PayloadValue::Float(f) => assert!((f - std::f64::consts::PI).abs() < 0.001),
1778            _ => panic!("Expected Float"),
1779        }
1780    }
1781
1782    #[test]
1783    fn test_decode_value_null() {
1784        // Value { null_value = 0 }
1785        // field 1, wire VARINT: tag 0x08, value 0
1786        let data = &[0x08, 0x00];
1787        let val = decode_value(data).unwrap();
1788        assert_eq!(val, PayloadValue::Null);
1789    }
1790
1791    #[test]
1792    fn test_decode_value_rejects_malformed_null_enum() {
1793        let data = &[0x08, 0x01];
1794
1795        let err = decode_value(data).unwrap_err();
1796        assert!(err.to_string().contains("Invalid payload null enum value"));
1797    }
1798
1799    #[test]
1800    fn test_decode_value_rejects_duplicate_value_kinds() {
1801        let data = &[
1802            0x18, 0x2A, // integer_value = 42
1803            0x22, 0x02, b'o', b'k', // string_value = "ok"
1804        ];
1805
1806        let err = decode_value(data).unwrap_err();
1807
1808        assert!(err.to_string().contains("Duplicate payload value kind"));
1809    }
1810
1811    #[test]
1812    fn test_decode_value_rejects_duplicate_same_value_kind() {
1813        let data = &[
1814            0x22, 0x05, b'f', b'i', b'r', b's', b't', // string_value = "first"
1815            0x22, 0x06, b's', b'e', b'c', b'o', b'n', b'd', // string_value = "second"
1816        ];
1817
1818        let err = decode_value(data).unwrap_err();
1819
1820        assert!(err.to_string().contains("Duplicate payload value kind"));
1821    }
1822
1823    #[test]
1824    fn test_decode_map_entry_rejects_duplicate_key_fields() {
1825        let mut entry = Vec::new();
1826        push_len_field(&mut entry, 0x0A, b"first");
1827        push_len_field(&mut entry, 0x0A, b"second");
1828        push_len_field(&mut entry, 0x12, &{
1829            let mut value = Vec::new();
1830            push_len_field(&mut value, 0x22, b"ok");
1831            value
1832        });
1833
1834        let err = decode_map_entry(&entry, 0).unwrap_err();
1835
1836        assert!(err.to_string().contains("Duplicate payload map key"));
1837    }
1838
1839    #[test]
1840    fn test_decode_map_entry_rejects_duplicate_value_fields() {
1841        let mut entry = Vec::new();
1842        push_len_field(&mut entry, 0x0A, b"key");
1843        push_len_field(&mut entry, 0x12, &{
1844            let mut value = Vec::new();
1845            push_len_field(&mut value, 0x22, b"first");
1846            value
1847        });
1848        push_len_field(&mut entry, 0x12, &{
1849            let mut value = Vec::new();
1850            push_len_field(&mut value, 0x22, b"second");
1851            value
1852        });
1853
1854        let err = decode_map_entry(&entry, 0).unwrap_err();
1855
1856        assert!(err.to_string().contains("Duplicate payload map value"));
1857    }
1858
1859    #[test]
1860    fn test_decode_scored_point_rejects_invalid_payload_key_utf8() {
1861        let data = &[
1862            0x0A, 0x02, 0x08, 0x01, // id = PointId { num = 1 }
1863            0x12, 0x09, // payload map entry length
1864            0x0A, 0x01, 0xFF, // key = invalid UTF-8
1865            0x12, 0x04, // value message length
1866            0x22, 0x02, b'o', b'k', // string_value = "ok"
1867        ];
1868
1869        let err = decode_scored_point(data).unwrap_err();
1870        assert!(err.to_string().contains("Invalid UTF-8 payload map key"));
1871    }
1872
1873    #[test]
1874    fn test_decode_scored_point_rejects_empty_payload_key() {
1875        let data = &[
1876            0x0A, 0x02, 0x08, 0x01, // id = PointId { num = 1 }
1877            0x12, 0x08, // payload map entry length
1878            0x0A, 0x00, // key = ""
1879            0x12, 0x04, // value message length
1880            0x22, 0x02, b'o', b'k', // string_value = "ok"
1881        ];
1882
1883        let err = decode_scored_point(data).unwrap_err();
1884        assert!(err.to_string().contains("Payload map key"));
1885    }
1886
1887    #[test]
1888    fn test_decode_scored_point_rejects_payload_entry_without_value() {
1889        let data = &[
1890            0x0A, 0x02, 0x08, 0x01, // id = PointId { num = 1 }
1891            0x12, 0x05, // payload map entry length
1892            0x0A, 0x03, b'b', b'a', b'd', // key = "bad", missing value
1893        ];
1894
1895        let err = decode_scored_point(data).unwrap_err();
1896        assert!(err.to_string().contains("Missing payload map value"));
1897    }
1898
1899    #[test]
1900    fn test_decode_value_rejects_non_finite_payload_float() {
1901        let nan = f64::NAN.to_le_bytes();
1902        let mut data = vec![0x11];
1903        data.extend_from_slice(&nan);
1904
1905        let err = decode_value(&data).unwrap_err();
1906        assert!(err.to_string().contains("non-finite payload float"));
1907    }
1908
1909    #[test]
1910    fn test_decode_value_rejects_malformed_nested_object_entry() {
1911        let data = &[
1912            0x32, 0x07, // struct_value length
1913            0x0A, 0x05, // Struct.fields map entry length
1914            0x0A, 0x03, b'b', b'a', b'd', // key = "bad", missing value
1915        ];
1916
1917        let err = decode_value(data).unwrap_err();
1918        assert!(err.to_string().contains("Missing payload map value"));
1919    }
1920
1921    #[test]
1922    fn test_decode_value_rejects_empty_nested_object_key() {
1923        let data = &[
1924            0x32, 0x0A, // struct_value length
1925            0x0A, 0x08, // Struct.fields map entry length
1926            0x0A, 0x00, // key = ""
1927            0x12, 0x04, // value message length
1928            0x22, 0x02, b'o', b'k', // string_value = "ok"
1929        ];
1930
1931        let err = decode_value(data).unwrap_err();
1932        assert!(err.to_string().contains("Payload map key"));
1933    }
1934
1935    #[test]
1936    fn test_decode_value_rejects_duplicate_nested_object_key() {
1937        let first = payload_string_entry("tenant_id", "first");
1938        let second = payload_string_entry("tenant_id", "second");
1939
1940        let mut struct_value = Vec::new();
1941        push_len_field(&mut struct_value, 0x0A, &first);
1942        push_len_field(&mut struct_value, 0x0A, &second);
1943
1944        let mut data = Vec::new();
1945        push_len_field(&mut data, 0x32, &struct_value);
1946
1947        let err = decode_value(&data).unwrap_err();
1948
1949        assert!(err.to_string().contains("Duplicate payload key"));
1950    }
1951
1952    #[test]
1953    fn test_decode_value_rejects_malformed_nested_list_item() {
1954        let data = &[
1955            0x3A, 0x02, // list_value length
1956            0x0A, 0x00, // ListValue.values has an empty Value message
1957        ];
1958
1959        let err = decode_value(data).unwrap_err();
1960        assert!(err.to_string().contains("Missing payload value kind"));
1961    }
1962
1963    #[test]
1964    fn test_decode_scroll_result_empty() {
1965        let data: &[u8] = &[];
1966        let result = decode_scroll_response(data).unwrap();
1967        assert!(result.points.is_empty());
1968        assert!(result.next_offset.is_none());
1969    }
1970}