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 Some((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 Some((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)?;
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        .map(|(value, _)| value)
62        .filter(|value| !decoded_is_empty(value))
63}
64
65fn decode_put_body_value_only(
66    body: &[u8],
67    desc: &StructureDesc,
68    is_be: bool,
69) -> Option<DecodedValue> {
70    let decoder = PvdDecoder::new(is_be);
71    if let Some((size, consumed)) = decoder.decode_size(body) {
72        if consumed + size <= body.len() {
73            let data = &body[consumed + size..];
74            if let Some(value) = decode_value_only_from_data(data, desc, &decoder) {
75                return Some(value);
76            }
77        }
78    }
79    decode_value_only_from_data(body, desc, &decoder)
80}
81
82fn decode_value_only_from_data(
83    data: &[u8],
84    desc: &StructureDesc,
85    decoder: &PvdDecoder,
86) -> Option<DecodedValue> {
87    let value_field = desc.fields.iter().find(|f| f.name == "value")?;
88    decoder
89        .decode_value(data, &value_field.field_type)
90        .map(|(value, _)| DecodedValue::Structure(vec![("value".to_string(), value)]))
91}
92
93/// Shift a bitset left by `shift` bit positions.
94pub fn shift_bitset_left(bitset: &[u8], shift: usize) -> Vec<u8> {
95    if shift == 0 {
96        return bitset.to_vec();
97    }
98    let total_bits = bitset.len() * 8;
99    let new_bits = total_bits + shift;
100    let mut out = vec![0u8; (new_bits + 7) / 8];
101    for bit in 0..total_bits {
102        if (bitset[bit / 8] & (1 << (bit % 8))) != 0 {
103            let new_bit = bit + shift;
104            out[new_bit / 8] |= 1 << (new_bit % 8);
105        }
106    }
107    out
108}
109
110/// Reassemble a segmented PVA message from the first header and accumulated
111/// payload fragments.
112pub fn assemble_segmented_message(first_header: [u8; 8], payloads: Vec<Vec<u8>>) -> Vec<u8> {
113    let mut header = first_header;
114    let is_be = (header[2] & 0x80) != 0;
115    header[2] &= !0x30;
116    let total_len: usize = payloads.iter().map(|p| p.len()).sum();
117    let len_bytes = if is_be {
118        (total_len as u32).to_be_bytes()
119    } else {
120        (total_len as u32).to_le_bytes()
121    };
122    header[4..8].copy_from_slice(&len_bytes);
123    let mut out = header.to_vec();
124    for payload in payloads {
125        out.extend_from_slice(&payload);
126    }
127    out
128}