Skip to main content

spvirit_server/
decode.rs

1//! PUT body decoding utilities.
2//!
3//! The PVA PUT command carries a variable-format payload that encodes field
4//! updates.  Different clients encode it in slightly different ways, so the
5//! decoder tries several strategies in order.
6
7use spvirit_codec::spvd_decode::{DecodedValue, PvdDecoder, StructureDesc};
8use spvirit_codec::spvd_encode::encode_size_pvd;
9
10/// Decode a PUT body payload using the known structure descriptor.
11///
12/// Tries multiple strategies to handle the various PUT encodings produced by
13/// different PVA clients (standard bitset, status-prefixed, shifted bitset,
14/// value-only).
15pub fn decode_put_body(body: &[u8], desc: &StructureDesc, is_be: bool) -> Option<DecodedValue> {
16    let decoder = PvdDecoder::new(is_be);
17    if let Ok((value, _)) = decoder.decode_structure_with_bitset(body, desc) {
18        if !decoded_is_empty(&value) {
19            return Some(value);
20        }
21    }
22    if !body.is_empty() && body[0] == 0xFF {
23        if let Ok((value, _)) = decoder.decode_structure_with_bitset(&body[1..], desc) {
24            if !decoded_is_empty(&value) {
25                return Some(value);
26            }
27        }
28    }
29    if let Some(value) = decode_put_body_shifted_bitset(body, desc, is_be) {
30        return Some(value);
31    }
32    if let Some(value) = decode_put_body_value_only(body, desc, is_be) {
33        return Some(value);
34    }
35    None
36}
37
38fn decoded_is_empty(value: &DecodedValue) -> bool {
39    matches!(value, DecodedValue::Structure(fields) if fields.is_empty())
40}
41
42fn decode_put_body_shifted_bitset(
43    body: &[u8],
44    desc: &StructureDesc,
45    is_be: bool,
46) -> Option<DecodedValue> {
47    let decoder = PvdDecoder::new(is_be);
48    let (size, consumed) = decoder.decode_size(body).ok()?;
49    if size == 0 || body.len() < consumed + size {
50        return None;
51    }
52    let bitset = &body[consumed..consumed + size];
53    let data = &body[consumed + size..];
54    let shifted = shift_bitset_left(bitset, 1);
55    let mut shifted_body = Vec::new();
56    shifted_body.extend_from_slice(&encode_size_pvd(shifted.len(), is_be));
57    shifted_body.extend_from_slice(&shifted);
58    shifted_body.extend_from_slice(data);
59    decoder
60        .decode_structure_with_bitset(&shifted_body, desc)
61        .ok()
62        .map(|(value, _)| value)
63        .filter(|value| !decoded_is_empty(value))
64}
65
66fn decode_put_body_value_only(
67    body: &[u8],
68    desc: &StructureDesc,
69    is_be: bool,
70) -> Option<DecodedValue> {
71    let decoder = PvdDecoder::new(is_be);
72    if let Ok((size, consumed)) = decoder.decode_size(body) {
73        if consumed + size <= body.len() {
74            let data = &body[consumed + size..];
75            if let Some(value) = decode_value_only_from_data(data, desc, &decoder) {
76                return Some(value);
77            }
78        }
79    }
80    decode_value_only_from_data(body, desc, &decoder)
81}
82
83fn decode_value_only_from_data(
84    data: &[u8],
85    desc: &StructureDesc,
86    decoder: &PvdDecoder,
87) -> Option<DecodedValue> {
88    let value_field = desc.fields.iter().find(|f| f.name == "value")?;
89    decoder
90        .decode_value(data, &value_field.field_type)
91        .ok()
92        .map(|(value, _)| DecodedValue::Structure(vec![("value".to_string(), value)]))
93}
94
95/// Shift a bitset left by `shift` bit positions.
96pub fn shift_bitset_left(bitset: &[u8], shift: usize) -> Vec<u8> {
97    if shift == 0 {
98        return bitset.to_vec();
99    }
100    let total_bits = bitset.len() * 8;
101    let new_bits = total_bits + shift;
102    let mut out = vec![0u8; (new_bits + 7) / 8];
103    for bit in 0..total_bits {
104        if (bitset[bit / 8] & (1 << (bit % 8))) != 0 {
105            let new_bit = bit + shift;
106            out[new_bit / 8] |= 1 << (new_bit % 8);
107        }
108    }
109    out
110}