Skip to main content

spvirit_codec/
spvirit_encode.rs

1//! PVA message encoding helpers.
2
3use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
4use spvirit_types::{NtPayload, NtScalar};
5use crate::spvd_decode::StructureDesc;
6use crate::spvd_encode::{
7    encode_nt_payload_bitset, encode_nt_payload_bitset_parts, encode_nt_payload_full,
8    encode_nt_scalar_bitset, encode_nt_scalar_full, encode_structure_desc,
9    nt_payload_desc,
10};
11
12pub fn encode_size_pva(size: usize, is_be: bool) -> Vec<u8> {
13    crate::encode_common::encode_size(size, is_be)
14}
15
16pub fn encode_string_pva(value: &str, is_be: bool) -> Vec<u8> {
17    crate::encode_common::encode_string(value, is_be)
18}
19
20fn encode_status_ok() -> Vec<u8> {
21    vec![0xFF]
22}
23
24fn encode_status_error(message: &str, is_be: bool) -> Vec<u8> {
25    let mut out = Vec::new();
26    out.push(0x02);
27    out.extend_from_slice(&encode_string_pva(message, is_be));
28    out.extend_from_slice(&encode_string_pva("", is_be));
29    out
30}
31
32pub fn encode_message_error(message: &str, version: u8, is_be: bool) -> Vec<u8> {
33    let payload = encode_status_error(message, is_be);
34    let mut out = encode_header(true, is_be, false, version, 18, payload.len() as u32);
35    out.extend_from_slice(&payload);
36    out
37}
38
39pub fn encode_header(
40    is_server: bool,
41    is_be: bool,
42    is_control: bool,
43    version: u8,
44    command: u8,
45    payload_length: u32,
46) -> Vec<u8> {
47    let magic = 0xCA;
48    let mut flags = 0u8;
49    if is_control {
50        flags |= 0x01;
51    }
52    if is_server {
53        flags |= 0x40;
54    }
55    if is_be {
56        flags |= 0x80;
57    }
58    let mut out = vec![magic, version, flags, command];
59    let len_bytes = if is_be {
60        payload_length.to_be_bytes()
61    } else {
62        payload_length.to_le_bytes()
63    };
64    out.extend_from_slice(&len_bytes);
65    out
66}
67
68pub fn encode_search_response(
69    guid: [u8; 12],
70    seq: u32,
71    addr: [u8; 16],
72    port: u16,
73    protocol: &str,
74    found: bool,
75    cids: &[u32],
76    version: u8,
77    is_be: bool,
78) -> Vec<u8> {
79    let mut payload = Vec::new();
80    payload.extend_from_slice(&guid);
81    payload.extend_from_slice(&if is_be {
82        seq.to_be_bytes()
83    } else {
84        seq.to_le_bytes()
85    });
86    payload.extend_from_slice(&addr);
87    payload.extend_from_slice(&if is_be {
88        port.to_be_bytes()
89    } else {
90        port.to_le_bytes()
91    });
92    payload.extend_from_slice(&encode_string_pva(protocol, is_be));
93    payload.push(if found { 1 } else { 0 });
94    let count = cids.len() as u16;
95    payload.extend_from_slice(&if is_be {
96        count.to_be_bytes()
97    } else {
98        count.to_le_bytes()
99    });
100    for cid in cids {
101        payload.extend_from_slice(&if is_be {
102            cid.to_be_bytes()
103        } else {
104            cid.to_le_bytes()
105        });
106    }
107
108    let mut out = encode_header(true, is_be, false, version, 4, payload.len() as u32);
109    out.extend_from_slice(&payload);
110    out
111}
112
113pub fn encode_connection_validated(version: u8, is_be: bool) -> Vec<u8> {
114    let payload = encode_status_ok();
115    let mut out = encode_header(true, is_be, false, version, 9, payload.len() as u32);
116    out.extend_from_slice(&payload);
117    out
118}
119
120pub fn encode_control_message(
121    is_server: bool,
122    is_be: bool,
123    version: u8,
124    command: u8,
125    data: u32,
126) -> Vec<u8> {
127    // Control messages: header only; size field carries data.
128    encode_header(is_server, is_be, true, version, command, data)
129}
130
131pub fn encode_connection_validation(
132    buffer_size: u32,
133    introspection_registry_size: u16,
134    qos: u16,
135    authz_name: &str,
136    version: u8,
137    is_be: bool,
138) -> Vec<u8> {
139    let mut payload = Vec::new();
140    payload.extend_from_slice(&if is_be {
141        buffer_size.to_be_bytes()
142    } else {
143        buffer_size.to_le_bytes()
144    });
145    payload.extend_from_slice(&if is_be {
146        introspection_registry_size.to_be_bytes()
147    } else {
148        introspection_registry_size.to_le_bytes()
149    });
150    payload.extend_from_slice(&if is_be {
151        qos.to_be_bytes()
152    } else {
153        qos.to_le_bytes()
154    });
155    payload.extend_from_slice(&encode_string_pva(authz_name, is_be));
156    let mut out = encode_header(true, is_be, false, version, 1, payload.len() as u32);
157    out.extend_from_slice(&payload);
158    out
159}
160
161pub fn encode_authnz_user_host(user: &str, host: &str, is_be: bool) -> Vec<u8> {
162    let mut out = Vec::new();
163    out.extend_from_slice(&[0xFD]);
164    if is_be {
165        out.extend_from_slice(&1u16.to_be_bytes());
166    } else {
167        out.extend_from_slice(&1u16.to_le_bytes());
168    }
169    out.extend_from_slice(&[0x80, 0x00]);
170    out.push(0x02);
171    out.push(0x04);
172    out.extend_from_slice(b"user");
173    out.push(0x60);
174    out.push(0x04);
175    out.extend_from_slice(b"host");
176    out.push(0x60);
177    out.extend_from_slice(&encode_string_pva(user, is_be));
178    out.extend_from_slice(&encode_string_pva(host, is_be));
179    out
180}
181
182pub fn encode_client_connection_validation(
183    buffer_size: u32,
184    introspection_registry_size: u16,
185    qos: u16,
186    authz: &str,
187    user: &str,
188    host: &str,
189    version: u8,
190    is_be: bool,
191) -> Vec<u8> {
192    let mut payload = Vec::new();
193    payload.extend_from_slice(&if is_be {
194        buffer_size.to_be_bytes()
195    } else {
196        buffer_size.to_le_bytes()
197    });
198    payload.extend_from_slice(&if is_be {
199        introspection_registry_size.to_be_bytes()
200    } else {
201        introspection_registry_size.to_le_bytes()
202    });
203    payload.extend_from_slice(&if is_be {
204        qos.to_be_bytes()
205    } else {
206        qos.to_le_bytes()
207    });
208    payload.extend_from_slice(&encode_string_pva(authz, is_be));
209    payload.extend_from_slice(&encode_authnz_user_host(user, host, is_be));
210    let mut out = encode_header(false, is_be, false, version, 1, payload.len() as u32);
211    out.extend_from_slice(&payload);
212    out
213}
214
215pub fn encode_create_channel_request(cid: u32, pv_name: &str, version: u8, is_be: bool) -> Vec<u8> {
216    let mut payload = Vec::new();
217    payload.extend_from_slice(&if is_be {
218        1u16.to_be_bytes()
219    } else {
220        1u16.to_le_bytes()
221    });
222    payload.extend_from_slice(&if is_be {
223        cid.to_be_bytes()
224    } else {
225        cid.to_le_bytes()
226    });
227    payload.extend_from_slice(&encode_string_pva(pv_name, is_be));
228    let mut out = encode_header(false, is_be, false, version, 7, payload.len() as u32);
229    out.extend_from_slice(&payload);
230    out
231}
232
233pub fn encode_get_field_request(
234    cid: u32,
235    field_name: Option<&str>,
236    version: u8,
237    is_be: bool,
238) -> Vec<u8> {
239    let mut payload = Vec::new();
240    payload.extend_from_slice(&if is_be {
241        cid.to_be_bytes()
242    } else {
243        cid.to_le_bytes()
244    });
245    payload.extend_from_slice(&encode_string_pva(field_name.unwrap_or(""), is_be));
246    let mut out = encode_header(false, is_be, false, version, 17, payload.len() as u32);
247    out.extend_from_slice(&payload);
248    out
249}
250
251pub fn encode_op_request(
252    command: u8,
253    sid: u32,
254    ioid: u32,
255    subcmd: u8,
256    extra: &[u8],
257    version: u8,
258    is_be: bool,
259) -> Vec<u8> {
260    let mut payload = Vec::new();
261    payload.extend_from_slice(&if is_be {
262        sid.to_be_bytes()
263    } else {
264        sid.to_le_bytes()
265    });
266    payload.extend_from_slice(&if is_be {
267        ioid.to_be_bytes()
268    } else {
269        ioid.to_le_bytes()
270    });
271    payload.push(subcmd);
272    payload.extend_from_slice(extra);
273    let mut out = encode_header(false, is_be, false, version, command, payload.len() as u32);
274    out.extend_from_slice(&payload);
275    out
276}
277
278pub fn encode_get_request(
279    sid: u32,
280    ioid: u32,
281    subcmd: u8,
282    extra: &[u8],
283    version: u8,
284    is_be: bool,
285) -> Vec<u8> {
286    encode_op_request(10, sid, ioid, subcmd, extra, version, is_be)
287}
288
289pub fn encode_put_request(
290    sid: u32,
291    ioid: u32,
292    subcmd: u8,
293    extra: &[u8],
294    version: u8,
295    is_be: bool,
296) -> Vec<u8> {
297    encode_op_request(11, sid, ioid, subcmd, extra, version, is_be)
298}
299
300pub fn encode_monitor_request(
301    sid: u32,
302    ioid: u32,
303    subcmd: u8,
304    extra: &[u8],
305    version: u8,
306    is_be: bool,
307) -> Vec<u8> {
308    encode_op_request(13, sid, ioid, subcmd, extra, version, is_be)
309}
310
311pub fn encode_rpc_request(
312    sid: u32,
313    ioid: u32,
314    subcmd: u8,
315    extra: &[u8],
316    version: u8,
317    is_be: bool,
318) -> Vec<u8> {
319    encode_op_request(20, sid, ioid, subcmd, extra, version, is_be)
320}
321
322pub fn encode_search_request(
323    seq: u32,
324    port: u16,
325    reply_addr: [u8; 16],
326    pv_requests: &[(u32, &str)],
327    version: u8,
328    is_be: bool,
329) -> Vec<u8> {
330    let mut payload = Vec::new();
331    payload.extend_from_slice(&if is_be {
332        seq.to_be_bytes()
333    } else {
334        seq.to_le_bytes()
335    });
336    payload.push(0x81);
337    payload.extend_from_slice(&[0u8; 3]);
338    payload.extend_from_slice(&reply_addr);
339    payload.extend_from_slice(&if is_be {
340        port.to_be_bytes()
341    } else {
342        port.to_le_bytes()
343    });
344    payload.extend_from_slice(&encode_size_pva(1, is_be));
345    payload.extend_from_slice(&encode_string_pva("tcp", is_be));
346    payload.extend_from_slice(&if is_be {
347        (pv_requests.len() as u16).to_be_bytes()
348    } else {
349        (pv_requests.len() as u16).to_le_bytes()
350    });
351    for (cid, pv_name) in pv_requests {
352        payload.extend_from_slice(&if is_be {
353            cid.to_be_bytes()
354        } else {
355            cid.to_le_bytes()
356        });
357        payload.extend_from_slice(&encode_string_pva(pv_name, is_be));
358    }
359
360    let mut out = encode_header(false, is_be, false, version, 3, payload.len() as u32);
361    out.extend_from_slice(&payload);
362    out
363}
364
365pub fn encode_create_channel_response(cid: u32, sid: u32, version: u8, is_be: bool) -> Vec<u8> {
366    let mut payload = Vec::new();
367    payload.extend_from_slice(&if is_be {
368        cid.to_be_bytes()
369    } else {
370        cid.to_le_bytes()
371    });
372    payload.extend_from_slice(&if is_be {
373        sid.to_be_bytes()
374    } else {
375        sid.to_le_bytes()
376    });
377    payload.extend_from_slice(&encode_status_ok());
378    let mut out = encode_header(true, is_be, false, version, 7, payload.len() as u32);
379    out.extend_from_slice(&payload);
380    out
381}
382
383pub fn encode_create_channel_error(cid: u32, message: &str, version: u8, is_be: bool) -> Vec<u8> {
384    let mut payload = Vec::new();
385    payload.extend_from_slice(&if is_be {
386        cid.to_be_bytes()
387    } else {
388        cid.to_le_bytes()
389    });
390    payload.extend_from_slice(&if is_be {
391        0u32.to_be_bytes()
392    } else {
393        0u32.to_le_bytes()
394    });
395    payload.push(0x01);
396    payload.extend_from_slice(&encode_string_pva(message, is_be));
397    payload.extend_from_slice(&encode_string_pva("", is_be));
398    let mut out = encode_header(true, is_be, false, version, 7, payload.len() as u32);
399    out.extend_from_slice(&payload);
400    out
401}
402
403pub fn encode_get_field_response(
404    request_id: u32,
405    desc: &StructureDesc,
406    version: u8,
407    is_be: bool,
408) -> Vec<u8> {
409    let mut payload = Vec::new();
410    payload.extend_from_slice(&if is_be {
411        request_id.to_be_bytes()
412    } else {
413        request_id.to_le_bytes()
414    });
415    payload.extend_from_slice(&encode_status_ok());
416    payload.push(0x80);
417    payload.extend_from_slice(&encode_structure_desc(desc, is_be));
418    let mut out = encode_header(true, is_be, false, version, 17, payload.len() as u32);
419    out.extend_from_slice(&payload);
420    out
421}
422
423pub fn encode_get_field_error(request_id: u32, message: &str, version: u8, is_be: bool) -> Vec<u8> {
424    let mut payload = Vec::new();
425    payload.extend_from_slice(&if is_be {
426        request_id.to_be_bytes()
427    } else {
428        request_id.to_le_bytes()
429    });
430    payload.extend_from_slice(&encode_status_error(message, is_be));
431    let mut out = encode_header(true, is_be, false, version, 17, payload.len() as u32);
432    out.extend_from_slice(&payload);
433    out
434}
435
436pub fn encode_op_init_response(
437    command: u8,
438    ioid: u32,
439    subcmd: u8,
440    desc: &StructureDesc,
441    nt: &NtScalar,
442    version: u8,
443    is_be: bool,
444) -> Vec<u8> {
445    let mut payload = Vec::new();
446    payload.extend_from_slice(&if is_be {
447        ioid.to_be_bytes()
448    } else {
449        ioid.to_le_bytes()
450    });
451    payload.push(subcmd);
452    payload.extend_from_slice(&encode_status_ok());
453    payload.push(0x80); // structure type for introspection
454    payload.extend_from_slice(&encode_structure_desc(desc, is_be));
455    payload.extend_from_slice(&encode_nt_scalar_full(nt, is_be));
456
457    let mut out = encode_header(true, is_be, false, version, command, payload.len() as u32);
458    out.extend_from_slice(&payload);
459    out
460}
461
462pub fn encode_op_init_response_desc(
463    command: u8,
464    ioid: u32,
465    subcmd: u8,
466    desc: &StructureDesc,
467    version: u8,
468    is_be: bool,
469) -> Vec<u8> {
470    let mut payload = Vec::new();
471    payload.extend_from_slice(&if is_be {
472        ioid.to_be_bytes()
473    } else {
474        ioid.to_le_bytes()
475    });
476    payload.push(subcmd);
477    payload.extend_from_slice(&encode_status_ok());
478    payload.push(0x80); // structure type for introspection
479    payload.extend_from_slice(&encode_structure_desc(desc, is_be));
480
481    let mut out = encode_header(true, is_be, false, version, command, payload.len() as u32);
482    out.extend_from_slice(&payload);
483    out
484}
485
486pub fn encode_op_data_response(
487    command: u8,
488    ioid: u32,
489    nt: &NtScalar,
490    version: u8,
491    is_be: bool,
492) -> Vec<u8> {
493    let mut payload = Vec::new();
494    payload.extend_from_slice(&if is_be {
495        ioid.to_be_bytes()
496    } else {
497        ioid.to_le_bytes()
498    });
499    payload.push(0x00);
500    payload.extend_from_slice(&encode_nt_scalar_bitset(nt, is_be));
501    let mut out = encode_header(true, is_be, false, version, command, payload.len() as u32);
502    out.extend_from_slice(&payload);
503    out
504}
505
506pub fn encode_op_get_data_response_payload(
507    ioid: u32,
508    payload_value: &NtPayload,
509    version: u8,
510    is_be: bool,
511) -> Vec<u8> {
512    encode_op_data_response_payload(10, ioid, payload_value, version, is_be)
513}
514
515pub fn encode_op_data_response_payload(
516    command: u8,
517    ioid: u32,
518    payload_value: &NtPayload,
519    version: u8,
520    is_be: bool,
521) -> Vec<u8> {
522    let mut payload = Vec::new();
523    payload.extend_from_slice(&if is_be {
524        ioid.to_be_bytes()
525    } else {
526        ioid.to_le_bytes()
527    });
528    payload.push(0x00);
529    payload.extend_from_slice(&encode_status_ok());
530    payload.extend_from_slice(&encode_nt_payload_bitset(payload_value, is_be));
531    let mut out = encode_header(true, is_be, false, version, command, payload.len() as u32);
532    out.extend_from_slice(&payload);
533    out
534}
535
536pub fn encode_op_status_response(
537    command: u8,
538    ioid: u32,
539    subcmd: u8,
540    version: u8,
541    is_be: bool,
542) -> Vec<u8> {
543    let mut payload = Vec::new();
544    payload.extend_from_slice(&if is_be {
545        ioid.to_be_bytes()
546    } else {
547        ioid.to_le_bytes()
548    });
549    payload.push(subcmd);
550    payload.extend_from_slice(&encode_status_ok());
551    let mut out = encode_header(true, is_be, false, version, command, payload.len() as u32);
552    out.extend_from_slice(&payload);
553    out
554}
555
556pub fn encode_op_status_error_response(
557    command: u8,
558    ioid: u32,
559    subcmd: u8,
560    message: &str,
561    version: u8,
562    is_be: bool,
563) -> Vec<u8> {
564    let mut payload = Vec::new();
565    payload.extend_from_slice(&if is_be {
566        ioid.to_be_bytes()
567    } else {
568        ioid.to_le_bytes()
569    });
570    payload.push(subcmd);
571    payload.extend_from_slice(&encode_status_error(message, is_be));
572    let mut out = encode_header(true, is_be, false, version, command, payload.len() as u32);
573    out.extend_from_slice(&payload);
574    out
575}
576
577pub fn encode_op_rpc_data_response_payload(
578    ioid: u32,
579    subcmd: u8,
580    payload_value: &NtPayload,
581    version: u8,
582    is_be: bool,
583) -> Vec<u8> {
584    let desc = nt_payload_desc(payload_value);
585    let mut payload = Vec::new();
586    payload.extend_from_slice(&if is_be {
587        ioid.to_be_bytes()
588    } else {
589        ioid.to_le_bytes()
590    });
591    payload.push(subcmd);
592    payload.extend_from_slice(&encode_status_ok());
593    payload.push(0x80);
594    payload.extend_from_slice(&encode_structure_desc(&desc, is_be));
595    payload.extend_from_slice(&encode_nt_payload_full(payload_value, is_be));
596    let mut out = encode_header(true, is_be, false, version, 20, payload.len() as u32);
597    out.extend_from_slice(&payload);
598    out
599}
600
601pub fn encode_op_put_get_init_response(
602    ioid: u32,
603    put_desc: &StructureDesc,
604    get_desc: &StructureDesc,
605    version: u8,
606    is_be: bool,
607) -> Vec<u8> {
608    let mut payload = Vec::new();
609    payload.extend_from_slice(&if is_be {
610        ioid.to_be_bytes()
611    } else {
612        ioid.to_le_bytes()
613    });
614    payload.push(0x08);
615    payload.extend_from_slice(&encode_status_ok());
616    payload.push(0x80);
617    payload.extend_from_slice(&encode_structure_desc(put_desc, is_be));
618    payload.push(0x80);
619    payload.extend_from_slice(&encode_structure_desc(get_desc, is_be));
620    let mut out = encode_header(true, is_be, false, version, 12, payload.len() as u32);
621    out.extend_from_slice(&payload);
622    out
623}
624
625pub fn encode_op_put_get_data_response(
626    ioid: u32,
627    nt: &NtScalar,
628    version: u8,
629    is_be: bool,
630) -> Vec<u8> {
631    encode_op_put_get_data_response_payload(ioid, &NtPayload::Scalar(nt.clone()), version, is_be)
632}
633
634pub fn encode_op_put_get_data_response_payload(
635    ioid: u32,
636    payload_value: &NtPayload,
637    version: u8,
638    is_be: bool,
639) -> Vec<u8> {
640    let mut payload = Vec::new();
641    payload.extend_from_slice(&if is_be {
642        ioid.to_be_bytes()
643    } else {
644        ioid.to_le_bytes()
645    });
646    payload.push(0x00);
647    payload.extend_from_slice(&encode_status_ok());
648    payload.extend_from_slice(&encode_nt_payload_bitset(payload_value, is_be));
649    let mut out = encode_header(true, is_be, false, version, 12, payload.len() as u32);
650    out.extend_from_slice(&payload);
651    out
652}
653
654pub fn encode_op_put_response(ioid: u32, version: u8, is_be: bool) -> Vec<u8> {
655    let mut payload = Vec::new();
656    payload.extend_from_slice(&if is_be {
657        ioid.to_be_bytes()
658    } else {
659        ioid.to_le_bytes()
660    });
661    payload.push(0x00);
662    payload.extend_from_slice(&encode_status_ok());
663    let mut out = encode_header(true, is_be, false, version, 11, payload.len() as u32);
664    out.extend_from_slice(&payload);
665    out
666}
667
668pub fn encode_op_put_status_response(
669    ioid: u32,
670    subcmd: u8,
671    message: &str,
672    version: u8,
673    is_be: bool,
674) -> Vec<u8> {
675    let mut payload = Vec::new();
676    payload.extend_from_slice(&if is_be {
677        ioid.to_be_bytes()
678    } else {
679        ioid.to_le_bytes()
680    });
681    payload.push(subcmd);
682    payload.extend_from_slice(&encode_status_error(message, is_be));
683    let mut out = encode_header(true, is_be, false, version, 11, payload.len() as u32);
684    out.extend_from_slice(&payload);
685    out
686}
687
688pub fn encode_op_put_getput_response(
689    ioid: u32,
690    nt: &NtScalar,
691    version: u8,
692    is_be: bool,
693) -> Vec<u8> {
694    encode_op_put_getput_response_payload(ioid, &NtPayload::Scalar(nt.clone()), version, is_be)
695}
696
697pub fn encode_op_put_getput_response_payload(
698    ioid: u32,
699    payload_value: &NtPayload,
700    version: u8,
701    is_be: bool,
702) -> Vec<u8> {
703    let mut payload = Vec::new();
704    payload.extend_from_slice(&if is_be {
705        ioid.to_be_bytes()
706    } else {
707        ioid.to_le_bytes()
708    });
709    payload.push(0x40);
710    payload.extend_from_slice(&encode_status_ok());
711    payload.extend_from_slice(&encode_nt_payload_bitset(payload_value, is_be));
712    let mut out = encode_header(true, is_be, false, version, 11, payload.len() as u32);
713    out.extend_from_slice(&payload);
714    out
715}
716
717pub fn encode_op_put_get_init_error_response(
718    ioid: u32,
719    message: &str,
720    version: u8,
721    is_be: bool,
722) -> Vec<u8> {
723    let mut payload = Vec::new();
724    payload.extend_from_slice(&if is_be {
725        ioid.to_be_bytes()
726    } else {
727        ioid.to_le_bytes()
728    });
729    payload.push(0x08);
730    payload.extend_from_slice(&encode_status_error(message, is_be));
731    let mut out = encode_header(true, is_be, false, version, 12, payload.len() as u32);
732    out.extend_from_slice(&payload);
733    out
734}
735
736pub fn encode_op_put_get_data_error_response(
737    ioid: u32,
738    message: &str,
739    version: u8,
740    is_be: bool,
741) -> Vec<u8> {
742    let mut payload = Vec::new();
743    payload.extend_from_slice(&if is_be {
744        ioid.to_be_bytes()
745    } else {
746        ioid.to_le_bytes()
747    });
748    payload.push(0x00);
749    payload.extend_from_slice(&encode_status_error(message, is_be));
750    let mut out = encode_header(true, is_be, false, version, 12, payload.len() as u32);
751    out.extend_from_slice(&payload);
752    out
753}
754
755pub fn encode_monitor_data_response(
756    ioid: u32,
757    subcmd: u8,
758    nt: &NtScalar,
759    version: u8,
760    is_be: bool,
761) -> Vec<u8> {
762    encode_monitor_data_response_payload(
763        ioid,
764        subcmd,
765        &NtPayload::Scalar(nt.clone()),
766        version,
767        is_be,
768    )
769}
770
771pub fn encode_monitor_data_response_payload(
772    ioid: u32,
773    subcmd: u8,
774    payload_value: &NtPayload,
775    version: u8,
776    is_be: bool,
777) -> Vec<u8> {
778    let (changed_bitset, values) = encode_nt_payload_bitset_parts(payload_value, is_be);
779    let mut payload = Vec::new();
780    payload.extend_from_slice(&if is_be {
781        ioid.to_be_bytes()
782    } else {
783        ioid.to_le_bytes()
784    });
785    payload.push(subcmd);
786    if (subcmd & 0x10) != 0 {
787        payload.extend_from_slice(&encode_status_ok());
788    }
789    payload.extend_from_slice(&changed_bitset);
790    payload.extend_from_slice(&values);
791    // overrun bitset: empty (after data per spec)
792    payload.extend_from_slice(&encode_size_pva(0, is_be));
793    let mut out = encode_header(true, is_be, false, version, 13, payload.len() as u32);
794    out.extend_from_slice(&payload);
795    out
796}
797
798pub fn encode_destroy_channel_response(sid: u32, cid: u32, version: u8, is_be: bool) -> Vec<u8> {
799    let mut payload = Vec::new();
800    payload.extend_from_slice(&if is_be {
801        sid.to_be_bytes()
802    } else {
803        sid.to_le_bytes()
804    });
805    payload.extend_from_slice(&if is_be {
806        cid.to_be_bytes()
807    } else {
808        cid.to_le_bytes()
809    });
810    let mut out = encode_header(true, is_be, false, version, 8, payload.len() as u32);
811    out.extend_from_slice(&payload);
812    out
813}
814
815pub fn encode_op_error(command: u8, ioid: u32, message: &str, version: u8, is_be: bool) -> Vec<u8> {
816    let mut payload = Vec::new();
817    payload.extend_from_slice(&if is_be {
818        ioid.to_be_bytes()
819    } else {
820        ioid.to_le_bytes()
821    });
822    payload.push(0x08);
823    payload.push(0x01); // error
824    payload.extend_from_slice(&encode_string_pva(message, is_be));
825    payload.extend_from_slice(&encode_string_pva("", is_be));
826    let mut out = encode_header(true, is_be, false, version, command, payload.len() as u32);
827    out.extend_from_slice(&payload);
828    out
829}
830
831pub fn encode_beacon(
832    guid: [u8; 12],
833    seq: u8,
834    change_count: u16,
835    addr: [u8; 16],
836    port: u16,
837    protocol: &str,
838    version: u8,
839    is_be: bool,
840) -> Vec<u8> {
841    let mut payload = Vec::new();
842    payload.extend_from_slice(&guid);
843    payload.push(0x00); // flags
844    payload.push(seq);
845    payload.extend_from_slice(&if is_be {
846        change_count.to_be_bytes()
847    } else {
848        change_count.to_le_bytes()
849    });
850    payload.extend_from_slice(&addr);
851    payload.extend_from_slice(&if is_be {
852        port.to_be_bytes()
853    } else {
854        port.to_le_bytes()
855    });
856    payload.extend_from_slice(&encode_string_pva(protocol, is_be));
857    // serverStatus: NULL FieldDesc (0xFF) means "no server status".
858    // Writing a PVA string here instead would be misinterpreted as a TypeCode
859    // by compliant clients (e.g. Phoebus), causing a BufferUnderflowException.
860    payload.push(0xFF);
861    let mut out = encode_header(true, is_be, false, version, 0, payload.len() as u32);
862    out.extend_from_slice(&payload);
863    out
864}
865
866// ---------------------------------------------------------------------------
867// IP address ↔ 16-byte PVA wire-format conversion helpers
868// ---------------------------------------------------------------------------
869
870/// Convert an [`IpAddr`] to the 16-byte PVA wire representation.
871///
872/// IPv4 addresses are stored as IPv4-mapped IPv6 (`::ffff:a.b.c.d`).
873/// Native IPv6 addresses are stored as-is.
874pub fn ip_to_bytes(ip: IpAddr) -> [u8; 16] {
875    match ip {
876        IpAddr::V4(v4) => {
877            let mut out = [0u8; 16];
878            out[10] = 0xFF;
879            out[11] = 0xFF;
880            out[12..16].copy_from_slice(&v4.octets());
881            out
882        }
883        IpAddr::V6(v6) => v6.octets(),
884    }
885}
886
887/// Decode a 16-byte PVA address field to an [`IpAddr`].
888///
889/// Returns `None` for all-zeros (unspecified).
890/// IPv4-mapped addresses (`::ffff:a.b.c.d`) are returned as [`IpAddr::V4`].
891pub fn ip_from_bytes(addr: &[u8; 16]) -> Option<IpAddr> {
892    if addr.iter().all(|&b| b == 0) {
893        return None;
894    }
895    // IPv4-mapped IPv6 address ::ffff:a.b.c.d
896    if addr[0..10].iter().all(|&b| b == 0) && addr[10] == 0xFF && addr[11] == 0xFF {
897        return Some(IpAddr::V4(Ipv4Addr::new(
898            addr[12], addr[13], addr[14], addr[15],
899        )));
900    }
901    Some(IpAddr::V6(Ipv6Addr::from(*addr)))
902}
903
904pub fn socket_addr_from_pva_bytes(addr: [u8; 16], port: u16) -> Option<SocketAddr> {
905    ip_from_bytes(&addr).map(|ip| SocketAddr::new(ip, port))
906}
907
908/// Format a 16-byte PVA address field as a human-readable IP string.
909///
910/// All-zeros → `"0.0.0.0"`, IPv4-mapped → dotted-quad, otherwise IPv6 notation.
911pub fn format_pva_address(addr: &[u8; 16]) -> String {
912    match ip_from_bytes(addr) {
913        Some(ip) => ip.to_string(),
914        None => "0.0.0.0".to_string(),
915    }
916}
917
918#[cfg(test)]
919mod tests {
920    use super::*;
921    use crate::epics_decode::{PvaPacket, PvaPacketCommand};
922
923    #[test]
924    fn encode_decode_connection_validation_roundtrip() {
925        let msg = encode_connection_validation(4096, 2, 0x10, "test", 2, true);
926        let mut pkt = PvaPacket::new(&msg);
927        let cmd = pkt.decode_payload().expect("decoded");
928        match cmd {
929            PvaPacketCommand::ConnectionValidation(payload) => {
930                assert_eq!(payload.buffer_size, 4096);
931                assert_eq!(payload.introspection_registry_size, 2);
932                assert_eq!(payload.qos, 0x10);
933                assert_eq!(payload.authz.as_deref(), Some("test"));
934            }
935            other => panic!("unexpected decode: {:?}", other),
936        }
937    }
938
939    #[test]
940    fn encode_decode_client_connection_validation_roundtrip() {
941        let msg =
942            encode_client_connection_validation(87_040, 32_767, 0, "ca", "alice", "host1", 2, false);
943        let mut pkt = PvaPacket::new(&msg);
944        let cmd = pkt.decode_payload().expect("decoded");
945        match cmd {
946            PvaPacketCommand::ConnectionValidation(payload) => {
947                assert!(!payload.is_server);
948                assert_eq!(payload.buffer_size, 87_040);
949                assert_eq!(payload.introspection_registry_size, 32_767);
950                assert_eq!(payload.qos, 0);
951            }
952            other => panic!("unexpected decode: {:?}", other),
953        }
954    }
955
956    #[test]
957    fn encode_decode_search_response_roundtrip() {
958        let guid = [1u8; 12];
959        let seq = 42;
960        let addr = [0u8; 16];
961        let port = 5075;
962        let cids = vec![100u32, 101u32];
963        let msg = encode_search_response(guid, seq, addr, port, "tcp", true, &cids, 2, false);
964        let mut pkt = PvaPacket::new(&msg);
965        let cmd = pkt.decode_payload().expect("decoded");
966        match cmd {
967            PvaPacketCommand::SearchResponse(payload) => {
968                assert_eq!(payload.guid, guid);
969                assert_eq!(payload.seq, seq);
970                assert_eq!(payload.port, port);
971                assert!(payload.found);
972                assert_eq!(payload.cids, cids);
973            }
974            other => panic!("unexpected decode: {:?}", other),
975        }
976    }
977
978    #[test]
979    fn encode_decode_connection_validated_roundtrip() {
980        let msg = encode_connection_validated(2, false);
981        let mut pkt = PvaPacket::new(&msg);
982        let cmd = pkt.decode_payload().expect("decoded");
983        match cmd {
984            PvaPacketCommand::ConnectionValidated(payload) => {
985                // 0xFF means "OK" which decodes to None in our decoder.
986                assert!(payload.status.is_none());
987            }
988            other => panic!("unexpected decode: {:?}", other),
989        }
990    }
991
992    #[test]
993    fn get_data_response_includes_status() {
994        let nt = NtScalar::from_value(spvirit_types::ScalarValue::F64(1.0));
995        let msg = encode_op_get_data_response_payload(
996            0x11223344,
997            &NtPayload::Scalar(nt),
998            2,
999            false,
1000        );
1001        assert!(msg.len() > 13);
1002        let status_offset = 8 + 4 + 1;
1003        assert_eq!(msg[status_offset], 0xFF);
1004
1005        let mut pkt = PvaPacket::new(&msg);
1006        let cmd = pkt.decode_payload().expect("decoded");
1007        match cmd {
1008            PvaPacketCommand::Op(op) => {
1009                assert_eq!(op.command, 10);
1010                assert_eq!(op.subcmd, 0x00);
1011                assert!(!op.body.is_empty());
1012            }
1013            other => panic!("unexpected decode: {:?}", other),
1014        }
1015    }
1016
1017    #[test]
1018    fn put_get_init_includes_two_descriptors() {
1019        let nt = NtScalar::from_value(spvirit_types::ScalarValue::F64(1.0));
1020        let desc = crate::spvd_encode::nt_scalar_desc(&nt.value);
1021        let msg = encode_op_put_get_init_response(0x01020304, &desc, &desc, 2, false);
1022
1023        let payload = &msg[8..];
1024        assert!(payload.len() > 6);
1025        // ioid(4) + subcmd(1) + status(1)
1026        assert_eq!(payload[5], 0xFF);
1027        let rest = &payload[6..];
1028        let first = rest.first().copied().unwrap_or(0);
1029        assert_eq!(first, 0x80);
1030        let second_pos = rest.iter().skip(1).position(|b| *b == 0x80);
1031        assert!(second_pos.is_some(), "expected second descriptor marker");
1032    }
1033
1034    #[test]
1035    fn put_get_data_includes_status() {
1036        let nt = NtScalar::from_value(spvirit_types::ScalarValue::F64(2.0));
1037        let msg = encode_op_put_get_data_response(0x55667788, &nt, 2, false);
1038        assert!(msg.len() > 13);
1039        let status_offset = 8 + 4 + 1;
1040        assert_eq!(msg[status_offset], 0xFF);
1041    }
1042
1043    #[test]
1044    fn put_getput_response_encodes_subcmd_0x40() {
1045        let nt = NtScalar::from_value(spvirit_types::ScalarValue::F64(2.0));
1046        let msg = encode_op_put_getput_response(0x01020304, &nt, 2, false);
1047        assert!(msg.len() > 13);
1048        let status_offset = 8 + 4 + 1;
1049        assert_eq!(msg[status_offset], 0xFF);
1050        let mut pkt = PvaPacket::new(&msg);
1051        let cmd = pkt.decode_payload().expect("decoded");
1052        match cmd {
1053            PvaPacketCommand::Op(op) => {
1054                assert_eq!(op.command, 11);
1055                assert_eq!(op.subcmd, 0x40);
1056            }
1057            other => panic!("unexpected decode: {:?}", other),
1058        }
1059    }
1060
1061    #[test]
1062    fn encode_get_field_response_roundtrip() {
1063        let desc = StructureDesc {
1064            struct_id: Some("epics:nt/NTScalar:1.0".to_string()),
1065            fields: vec![crate::spvd_decode::FieldDesc {
1066                name: "value".to_string(),
1067                field_type: crate::spvd_decode::FieldType::Scalar(
1068                    crate::spvd_decode::TypeCode::String,
1069                ),
1070            }],
1071        };
1072        let msg = encode_get_field_response(11, &desc, 2, false);
1073        let mut pkt = PvaPacket::new(&msg);
1074        let cmd = pkt.decode_payload().expect("decoded");
1075        match cmd {
1076            PvaPacketCommand::GetField(payload) => {
1077                assert!(payload.is_server);
1078                assert_eq!(payload.cid, 11);
1079                assert!(payload.status.is_none());
1080                let intro = payload.introspection.expect("introspection");
1081                assert_eq!(intro.fields.len(), 1);
1082                assert_eq!(intro.fields[0].name, "value");
1083            }
1084            other => panic!("unexpected decode: {:?}", other),
1085        }
1086    }
1087
1088    #[test]
1089    fn encode_get_field_error_roundtrip() {
1090        let msg = encode_get_field_error(7, "listing disabled", 2, false);
1091        let mut pkt = PvaPacket::new(&msg);
1092        let cmd = pkt.decode_payload().expect("decoded");
1093        match cmd {
1094            PvaPacketCommand::GetField(payload) => {
1095                assert!(payload.is_server);
1096                assert_eq!(payload.cid, 7);
1097                let status = payload.status.expect("status");
1098                assert_eq!(status.code, 0x02);
1099                assert_eq!(status.message.as_deref(), Some("listing disabled"));
1100            }
1101            other => panic!("unexpected decode: {:?}", other),
1102        }
1103    }
1104
1105    #[test]
1106    fn encode_decode_create_channel_request_roundtrip() {
1107        let msg = encode_create_channel_request(7, "TEST:PV", 2, false);
1108        let mut pkt = PvaPacket::new(&msg);
1109        let cmd = pkt.decode_payload().expect("decoded");
1110        match cmd {
1111            PvaPacketCommand::CreateChannel(payload) => {
1112                assert!(!payload.is_server);
1113                assert_eq!(payload.channels, vec![(7, "TEST:PV".to_string())]);
1114            }
1115            other => panic!("unexpected decode: {:?}", other),
1116        }
1117    }
1118
1119    #[test]
1120    fn encode_decode_get_field_request_roundtrip() {
1121        let msg = encode_get_field_request(9, Some("*"), 2, false);
1122        let mut pkt = PvaPacket::new(&msg);
1123        let cmd = pkt.decode_payload().expect("decoded");
1124        match cmd {
1125            PvaPacketCommand::GetField(payload) => {
1126                assert!(!payload.is_server);
1127                assert_eq!(payload.cid, 9);
1128                assert_eq!(payload.field_name.as_deref(), Some("*"));
1129            }
1130            other => panic!("unexpected decode: {:?}", other),
1131        }
1132    }
1133
1134    #[test]
1135    fn encode_decode_get_request_roundtrip() {
1136        let msg = encode_get_request(1, 2, 0x08, &[0xfd, 0x02, 0x00], 2, false);
1137        let mut pkt = PvaPacket::new(&msg);
1138        let cmd = pkt.decode_payload().expect("decoded");
1139        match cmd {
1140            PvaPacketCommand::Op(op) => {
1141                assert_eq!(op.command, 10);
1142                assert_eq!(op.sid_or_cid, 1);
1143                assert_eq!(op.ioid, 2);
1144                assert_eq!(op.subcmd, 0x08);
1145            }
1146            other => panic!("unexpected decode: {:?}", other),
1147        }
1148    }
1149
1150    #[test]
1151    fn encode_decode_put_request_roundtrip() {
1152        let msg = encode_put_request(3, 4, 0x40, &[0xAA], 2, false);
1153        let mut pkt = PvaPacket::new(&msg);
1154        let cmd = pkt.decode_payload().expect("decoded");
1155        match cmd {
1156            PvaPacketCommand::Op(op) => {
1157                assert_eq!(op.command, 11);
1158                assert_eq!(op.sid_or_cid, 3);
1159                assert_eq!(op.ioid, 4);
1160                assert_eq!(op.subcmd, 0x40);
1161            }
1162            other => panic!("unexpected decode: {:?}", other),
1163        }
1164    }
1165
1166    #[test]
1167    fn encode_decode_monitor_request_roundtrip() {
1168        let msg = encode_monitor_request(5, 6, 0x44, &[], 2, false);
1169        let mut pkt = PvaPacket::new(&msg);
1170        let cmd = pkt.decode_payload().expect("decoded");
1171        match cmd {
1172            PvaPacketCommand::Op(op) => {
1173                assert_eq!(op.command, 13);
1174                assert_eq!(op.sid_or_cid, 5);
1175                assert_eq!(op.ioid, 6);
1176                assert_eq!(op.subcmd, 0x44);
1177            }
1178            other => panic!("unexpected decode: {:?}", other),
1179        }
1180    }
1181
1182    #[test]
1183    fn encode_decode_rpc_request_roundtrip() {
1184        let msg = encode_rpc_request(7, 8, 0x00, &[0x80, 0x00], 2, false);
1185        let mut pkt = PvaPacket::new(&msg);
1186        let cmd = pkt.decode_payload().expect("decoded");
1187        match cmd {
1188            PvaPacketCommand::Op(op) => {
1189                assert_eq!(op.command, 20);
1190                assert_eq!(op.sid_or_cid, 7);
1191                assert_eq!(op.ioid, 8);
1192                assert_eq!(op.subcmd, 0x00);
1193            }
1194            other => panic!("unexpected decode: {:?}", other),
1195        }
1196    }
1197
1198    #[test]
1199    fn encode_decode_search_request_roundtrip() {
1200        let seq = 1234;
1201        let cid = 42;
1202        let port = 5076;
1203        let reply_addr = ip_to_bytes(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 20)));
1204        let requests = [(cid, "TEST:PV")];
1205        let msg = encode_search_request(seq, port, reply_addr, &requests, 2, false);
1206        let mut pkt = PvaPacket::new(&msg);
1207        let cmd = pkt.decode_payload().expect("decoded");
1208        match cmd {
1209            PvaPacketCommand::Search(payload) => {
1210                assert_eq!(payload.seq, seq);
1211                assert_eq!(payload.mask, 0x81);
1212                assert_eq!(payload.addr, reply_addr);
1213                assert_eq!(payload.port, port);
1214                assert_eq!(payload.protocols, vec!["tcp".to_string()]);
1215                assert_eq!(payload.pv_requests.len(), 1);
1216                assert_eq!(payload.pv_requests[0].0, cid);
1217                assert_eq!(payload.pv_requests[0].1, "TEST:PV");
1218            }
1219            other => panic!("unexpected decode: {:?}", other),
1220        }
1221    }
1222
1223    #[test]
1224    fn socket_addr_from_pva_bytes_decodes_ipv4_mapped() {
1225        let addr = ip_to_bytes(IpAddr::V4(Ipv4Addr::new(10, 20, 30, 40)));
1226        assert_eq!(
1227            socket_addr_from_pva_bytes(addr, 5075),
1228            Some("10.20.30.40:5075".parse().unwrap())
1229        );
1230    }
1231
1232    #[test]
1233    fn socket_addr_from_pva_bytes_decodes_ipv6() {
1234        let addr = ip_to_bytes(IpAddr::V6("2001:db8::1".parse().unwrap()));
1235        assert_eq!(
1236            socket_addr_from_pva_bytes(addr, 5075),
1237            Some("[2001:db8::1]:5075".parse().unwrap())
1238        );
1239    }
1240
1241    #[test]
1242    fn socket_addr_from_pva_bytes_returns_none_for_unspecified() {
1243        assert_eq!(socket_addr_from_pva_bytes([0u8; 16], 5075), None);
1244    }
1245}