use crate::spvd_decode::StructureDesc;
use crate::spvd_encode::{
encode_nt_payload_bitset, encode_nt_payload_bitset_parts, encode_nt_payload_filtered,
encode_nt_payload_full, encode_nt_scalar_bitset, encode_nt_scalar_full, encode_structure_desc,
nt_payload_desc,
};
use spvirit_types::{NtPayload, NtScalar};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
pub fn encode_size_pva(size: usize, is_be: bool) -> Vec<u8> {
crate::encode_common::encode_size(size, is_be)
}
pub fn encode_string_pva(value: &str, is_be: bool) -> Vec<u8> {
crate::encode_common::encode_string(value, is_be)
}
fn encode_status_ok() -> Vec<u8> {
vec![0xFF]
}
fn encode_status_error(message: &str, is_be: bool) -> Vec<u8> {
let mut out = Vec::new();
out.push(0x02);
out.extend_from_slice(&encode_string_pva(message, is_be));
out.extend_from_slice(&encode_string_pva("", is_be));
out
}
pub fn encode_status_warning(message: &str, is_be: bool) -> Vec<u8> {
let mut out = Vec::new();
out.push(0x01);
out.extend_from_slice(&encode_string_pva(message, is_be));
out.extend_from_slice(&encode_string_pva("", is_be));
out
}
pub fn encode_status_fatal(message: &str, is_be: bool) -> Vec<u8> {
let mut out = Vec::new();
out.push(0x03);
out.extend_from_slice(&encode_string_pva(message, is_be));
out.extend_from_slice(&encode_string_pva("", is_be));
out
}
pub fn encode_message_error(message: &str, version: u8, is_be: bool) -> Vec<u8> {
let payload = encode_status_error(message, is_be);
let mut out = encode_header(true, is_be, false, version, 18, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_header(
is_server: bool,
is_be: bool,
is_control: bool,
version: u8,
command: u8,
payload_length: u32,
) -> Vec<u8> {
let magic = 0xCA;
let mut flags = 0u8;
if is_control {
flags |= 0x01;
}
if is_server {
flags |= 0x40;
}
if is_be {
flags |= 0x80;
}
let mut out = vec![magic, version, flags, command];
let len_bytes = if is_be {
payload_length.to_be_bytes()
} else {
payload_length.to_le_bytes()
};
out.extend_from_slice(&len_bytes);
out
}
pub fn encode_search_response(
guid: [u8; 12],
seq: u32,
addr: [u8; 16],
port: u16,
protocol: &str,
found: bool,
cids: &[u32],
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&guid);
payload.extend_from_slice(&if is_be {
seq.to_be_bytes()
} else {
seq.to_le_bytes()
});
payload.extend_from_slice(&addr);
payload.extend_from_slice(&if is_be {
port.to_be_bytes()
} else {
port.to_le_bytes()
});
payload.extend_from_slice(&encode_string_pva(protocol, is_be));
payload.push(if found { 1 } else { 0 });
let count = cids.len() as u16;
payload.extend_from_slice(&if is_be {
count.to_be_bytes()
} else {
count.to_le_bytes()
});
for cid in cids {
payload.extend_from_slice(&if is_be {
cid.to_be_bytes()
} else {
cid.to_le_bytes()
});
}
let mut out = encode_header(true, is_be, false, version, 4, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_connection_validated(is_server: bool, version: u8, is_be: bool) -> Vec<u8> {
let payload = encode_status_ok();
let mut out = encode_header(is_server, is_be, false, version, 9, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_control_message(
is_server: bool,
is_be: bool,
version: u8,
command: u8,
data: u32,
) -> Vec<u8> {
encode_header(is_server, is_be, true, version, command, data)
}
pub fn encode_connection_validation(
buffer_size: u32,
introspection_registry_size: u16,
qos: u16,
authz_name: &str,
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
buffer_size.to_be_bytes()
} else {
buffer_size.to_le_bytes()
});
payload.extend_from_slice(&if is_be {
introspection_registry_size.to_be_bytes()
} else {
introspection_registry_size.to_le_bytes()
});
payload.extend_from_slice(&if is_be {
qos.to_be_bytes()
} else {
qos.to_le_bytes()
});
payload.extend_from_slice(&encode_string_pva(authz_name, is_be));
let mut out = encode_header(true, is_be, false, version, 1, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_authnz_user_host(user: &str, host: &str, is_be: bool) -> Vec<u8> {
let mut out = Vec::new();
out.extend_from_slice(&[0xFD]);
if is_be {
out.extend_from_slice(&1u16.to_be_bytes());
} else {
out.extend_from_slice(&1u16.to_le_bytes());
}
out.extend_from_slice(&[0x80, 0x00]);
out.push(0x02);
out.push(0x04);
out.extend_from_slice(b"user");
out.push(0x60);
out.push(0x04);
out.extend_from_slice(b"host");
out.push(0x60);
out.extend_from_slice(&encode_string_pva(user, is_be));
out.extend_from_slice(&encode_string_pva(host, is_be));
out
}
pub fn encode_client_connection_validation(
buffer_size: u32,
introspection_registry_size: u16,
qos: u16,
authz: &str,
user: &str,
host: &str,
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
buffer_size.to_be_bytes()
} else {
buffer_size.to_le_bytes()
});
payload.extend_from_slice(&if is_be {
introspection_registry_size.to_be_bytes()
} else {
introspection_registry_size.to_le_bytes()
});
payload.extend_from_slice(&if is_be {
qos.to_be_bytes()
} else {
qos.to_le_bytes()
});
payload.extend_from_slice(&encode_string_pva(authz, is_be));
payload.extend_from_slice(&encode_authnz_user_host(user, host, is_be));
let mut out = encode_header(false, is_be, false, version, 1, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_create_channel_request(cid: u32, pv_name: &str, version: u8, is_be: bool) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
1u16.to_be_bytes()
} else {
1u16.to_le_bytes()
});
payload.extend_from_slice(&if is_be {
cid.to_be_bytes()
} else {
cid.to_le_bytes()
});
payload.extend_from_slice(&encode_string_pva(pv_name, is_be));
let mut out = encode_header(false, is_be, false, version, 7, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_get_field_request(
sid: u32,
ioid: u32,
sub_field: Option<&str>,
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
sid.to_be_bytes()
} else {
sid.to_le_bytes()
});
payload.extend_from_slice(&if is_be {
ioid.to_be_bytes()
} else {
ioid.to_le_bytes()
});
payload.extend_from_slice(&encode_string_pva(sub_field.unwrap_or(""), is_be));
let mut out = encode_header(false, is_be, false, version, 17, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_op_request(
command: u8,
sid: u32,
ioid: u32,
subcmd: u8,
extra: &[u8],
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
sid.to_be_bytes()
} else {
sid.to_le_bytes()
});
payload.extend_from_slice(&if is_be {
ioid.to_be_bytes()
} else {
ioid.to_le_bytes()
});
payload.push(subcmd);
payload.extend_from_slice(extra);
let mut out = encode_header(false, is_be, false, version, command, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_get_request(
sid: u32,
ioid: u32,
subcmd: u8,
extra: &[u8],
version: u8,
is_be: bool,
) -> Vec<u8> {
encode_op_request(10, sid, ioid, subcmd, extra, version, is_be)
}
pub fn encode_put_request(
sid: u32,
ioid: u32,
subcmd: u8,
extra: &[u8],
version: u8,
is_be: bool,
) -> Vec<u8> {
encode_op_request(11, sid, ioid, subcmd, extra, version, is_be)
}
pub fn encode_monitor_request(
sid: u32,
ioid: u32,
subcmd: u8,
extra: &[u8],
version: u8,
is_be: bool,
) -> Vec<u8> {
encode_op_request(13, sid, ioid, subcmd, extra, version, is_be)
}
pub fn encode_rpc_request(
sid: u32,
ioid: u32,
subcmd: u8,
extra: &[u8],
version: u8,
is_be: bool,
) -> Vec<u8> {
encode_op_request(20, sid, ioid, subcmd, extra, version, is_be)
}
pub fn encode_search_request(
seq: u32,
flags: u8,
port: u16,
reply_addr: [u8; 16],
pv_requests: &[(u32, &str)],
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
seq.to_be_bytes()
} else {
seq.to_le_bytes()
});
payload.push(flags);
payload.extend_from_slice(&[0u8; 3]);
payload.extend_from_slice(&reply_addr);
payload.extend_from_slice(&if is_be {
port.to_be_bytes()
} else {
port.to_le_bytes()
});
payload.extend_from_slice(&encode_size_pva(1, is_be));
payload.extend_from_slice(&encode_string_pva("tcp", is_be));
payload.extend_from_slice(&if is_be {
(pv_requests.len() as u16).to_be_bytes()
} else {
(pv_requests.len() as u16).to_le_bytes()
});
for (cid, pv_name) in pv_requests {
payload.extend_from_slice(&if is_be {
cid.to_be_bytes()
} else {
cid.to_le_bytes()
});
payload.extend_from_slice(&encode_string_pva(pv_name, is_be));
}
let mut out = encode_header(false, is_be, false, version, 3, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_create_channel_response(cid: u32, sid: u32, version: u8, is_be: bool) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
cid.to_be_bytes()
} else {
cid.to_le_bytes()
});
payload.extend_from_slice(&if is_be {
sid.to_be_bytes()
} else {
sid.to_le_bytes()
});
payload.extend_from_slice(&encode_status_ok());
let mut out = encode_header(true, is_be, false, version, 7, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_create_channel_error(cid: u32, message: &str, version: u8, is_be: bool) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
cid.to_be_bytes()
} else {
cid.to_le_bytes()
});
payload.extend_from_slice(&if is_be {
0u32.to_be_bytes()
} else {
0u32.to_le_bytes()
});
payload.push(0x01);
payload.extend_from_slice(&encode_string_pva(message, is_be));
payload.extend_from_slice(&encode_string_pva("", is_be));
let mut out = encode_header(true, is_be, false, version, 7, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_get_field_response(
request_id: u32,
desc: &StructureDesc,
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
request_id.to_be_bytes()
} else {
request_id.to_le_bytes()
});
payload.extend_from_slice(&encode_status_ok());
payload.push(0x80);
payload.extend_from_slice(&encode_structure_desc(desc, is_be));
let mut out = encode_header(true, is_be, false, version, 17, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_get_field_error(request_id: u32, message: &str, version: u8, is_be: bool) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
request_id.to_be_bytes()
} else {
request_id.to_le_bytes()
});
payload.extend_from_slice(&encode_status_error(message, is_be));
let mut out = encode_header(true, is_be, false, version, 17, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_op_init_response(
command: u8,
ioid: u32,
subcmd: u8,
desc: &StructureDesc,
nt: &NtScalar,
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
ioid.to_be_bytes()
} else {
ioid.to_le_bytes()
});
payload.push(subcmd);
payload.extend_from_slice(&encode_status_ok());
payload.push(0x80); payload.extend_from_slice(&encode_structure_desc(desc, is_be));
payload.extend_from_slice(&encode_nt_scalar_full(nt, is_be));
let mut out = encode_header(true, is_be, false, version, command, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_op_init_response_desc(
command: u8,
ioid: u32,
subcmd: u8,
desc: &StructureDesc,
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
ioid.to_be_bytes()
} else {
ioid.to_le_bytes()
});
payload.push(subcmd);
payload.extend_from_slice(&encode_status_ok());
payload.push(0x80); payload.extend_from_slice(&encode_structure_desc(desc, is_be));
let mut out = encode_header(true, is_be, false, version, command, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_op_data_response(
command: u8,
ioid: u32,
nt: &NtScalar,
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
ioid.to_be_bytes()
} else {
ioid.to_le_bytes()
});
payload.push(0x00);
payload.extend_from_slice(&encode_nt_scalar_bitset(nt, is_be));
let mut out = encode_header(true, is_be, false, version, command, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_op_get_data_response_payload(
ioid: u32,
payload_value: &NtPayload,
version: u8,
is_be: bool,
) -> Vec<u8> {
encode_op_data_response_payload(10, ioid, payload_value, version, is_be)
}
pub fn encode_op_data_response_payload(
command: u8,
ioid: u32,
payload_value: &NtPayload,
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
ioid.to_be_bytes()
} else {
ioid.to_le_bytes()
});
payload.push(0x00);
payload.extend_from_slice(&encode_status_ok());
payload.extend_from_slice(&encode_nt_payload_bitset(payload_value, is_be));
let mut out = encode_header(true, is_be, false, version, command, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_op_data_response_filtered(
command: u8,
ioid: u32,
payload_value: &NtPayload,
filtered_desc: &StructureDesc,
version: u8,
is_be: bool,
) -> Vec<u8> {
let (bitset, values) = encode_nt_payload_filtered(payload_value, filtered_desc, is_be);
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
ioid.to_be_bytes()
} else {
ioid.to_le_bytes()
});
payload.push(0x00);
payload.extend_from_slice(&encode_status_ok());
payload.extend_from_slice(&bitset);
payload.extend_from_slice(&values);
let mut out = encode_header(true, is_be, false, version, command, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_op_status_response(
command: u8,
ioid: u32,
subcmd: u8,
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
ioid.to_be_bytes()
} else {
ioid.to_le_bytes()
});
payload.push(subcmd);
payload.extend_from_slice(&encode_status_ok());
let mut out = encode_header(true, is_be, false, version, command, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_op_status_error_response(
command: u8,
ioid: u32,
subcmd: u8,
message: &str,
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
ioid.to_be_bytes()
} else {
ioid.to_le_bytes()
});
payload.push(subcmd);
payload.extend_from_slice(&encode_status_error(message, is_be));
let mut out = encode_header(true, is_be, false, version, command, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_op_rpc_data_response_payload(
ioid: u32,
subcmd: u8,
payload_value: &NtPayload,
version: u8,
is_be: bool,
) -> Vec<u8> {
let desc = nt_payload_desc(payload_value);
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
ioid.to_be_bytes()
} else {
ioid.to_le_bytes()
});
payload.push(subcmd);
payload.extend_from_slice(&encode_status_ok());
payload.push(0x80);
payload.extend_from_slice(&encode_structure_desc(&desc, is_be));
payload.extend_from_slice(&encode_nt_payload_full(payload_value, is_be));
let mut out = encode_header(true, is_be, false, version, 20, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_op_put_get_init_response(
ioid: u32,
put_desc: &StructureDesc,
get_desc: &StructureDesc,
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
ioid.to_be_bytes()
} else {
ioid.to_le_bytes()
});
payload.push(0x08);
payload.extend_from_slice(&encode_status_ok());
payload.push(0x80);
payload.extend_from_slice(&encode_structure_desc(put_desc, is_be));
payload.push(0x80);
payload.extend_from_slice(&encode_structure_desc(get_desc, is_be));
let mut out = encode_header(true, is_be, false, version, 12, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_op_put_get_data_response(
ioid: u32,
nt: &NtScalar,
version: u8,
is_be: bool,
) -> Vec<u8> {
encode_op_put_get_data_response_payload(ioid, &NtPayload::Scalar(nt.clone()), version, is_be)
}
pub fn encode_op_put_get_data_response_payload(
ioid: u32,
payload_value: &NtPayload,
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
ioid.to_be_bytes()
} else {
ioid.to_le_bytes()
});
payload.push(0x00);
payload.extend_from_slice(&encode_status_ok());
payload.extend_from_slice(&encode_nt_payload_bitset(payload_value, is_be));
let mut out = encode_header(true, is_be, false, version, 12, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_op_put_response(ioid: u32, subcmd: u8, version: u8, is_be: bool) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
ioid.to_be_bytes()
} else {
ioid.to_le_bytes()
});
payload.push(subcmd);
payload.extend_from_slice(&encode_status_ok());
let mut out = encode_header(true, is_be, false, version, 11, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_op_put_status_response(
ioid: u32,
subcmd: u8,
message: &str,
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
ioid.to_be_bytes()
} else {
ioid.to_le_bytes()
});
payload.push(subcmd);
payload.extend_from_slice(&encode_status_error(message, is_be));
let mut out = encode_header(true, is_be, false, version, 11, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_op_put_getput_response(
ioid: u32,
nt: &NtScalar,
version: u8,
is_be: bool,
) -> Vec<u8> {
encode_op_put_getput_response_payload(ioid, &NtPayload::Scalar(nt.clone()), version, is_be)
}
pub fn encode_op_put_getput_response_payload(
ioid: u32,
payload_value: &NtPayload,
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
ioid.to_be_bytes()
} else {
ioid.to_le_bytes()
});
payload.push(0x40);
payload.extend_from_slice(&encode_status_ok());
payload.extend_from_slice(&encode_nt_payload_bitset(payload_value, is_be));
let mut out = encode_header(true, is_be, false, version, 11, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_op_put_get_init_error_response(
ioid: u32,
message: &str,
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
ioid.to_be_bytes()
} else {
ioid.to_le_bytes()
});
payload.push(0x08);
payload.extend_from_slice(&encode_status_error(message, is_be));
let mut out = encode_header(true, is_be, false, version, 12, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_op_put_get_data_error_response(
ioid: u32,
message: &str,
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
ioid.to_be_bytes()
} else {
ioid.to_le_bytes()
});
payload.push(0x00);
payload.extend_from_slice(&encode_status_error(message, is_be));
let mut out = encode_header(true, is_be, false, version, 12, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_monitor_data_response(
ioid: u32,
subcmd: u8,
nt: &NtScalar,
version: u8,
is_be: bool,
) -> Vec<u8> {
encode_monitor_data_response_payload(
ioid,
subcmd,
&NtPayload::Scalar(nt.clone()),
version,
is_be,
)
}
pub fn encode_monitor_data_response_payload(
ioid: u32,
subcmd: u8,
payload_value: &NtPayload,
version: u8,
is_be: bool,
) -> Vec<u8> {
let (changed_bitset, values) = encode_nt_payload_bitset_parts(payload_value, is_be);
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
ioid.to_be_bytes()
} else {
ioid.to_le_bytes()
});
payload.push(subcmd);
if (subcmd & 0x10) != 0 {
payload.extend_from_slice(&encode_status_ok());
}
payload.extend_from_slice(&changed_bitset);
payload.extend_from_slice(&values);
payload.extend_from_slice(&encode_size_pva(0, is_be));
let mut out = encode_header(true, is_be, false, version, 13, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_monitor_data_response_filtered(
ioid: u32,
subcmd: u8,
payload_value: &NtPayload,
filtered_desc: &StructureDesc,
version: u8,
is_be: bool,
) -> Vec<u8> {
let (bitset, values) = encode_nt_payload_filtered(payload_value, filtered_desc, is_be);
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
ioid.to_be_bytes()
} else {
ioid.to_le_bytes()
});
payload.push(subcmd);
if (subcmd & 0x10) != 0 {
payload.extend_from_slice(&encode_status_ok());
}
payload.extend_from_slice(&bitset);
payload.extend_from_slice(&values);
payload.extend_from_slice(&encode_size_pva(0, is_be));
let mut out = encode_header(true, is_be, false, version, 13, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_destroy_channel_response(sid: u32, cid: u32, version: u8, is_be: bool) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
sid.to_be_bytes()
} else {
sid.to_le_bytes()
});
payload.extend_from_slice(&if is_be {
cid.to_be_bytes()
} else {
cid.to_le_bytes()
});
let mut out = encode_header(true, is_be, false, version, 8, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_op_error(
command: u8,
subcmd: u8,
ioid: u32,
message: &str,
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&if is_be {
ioid.to_be_bytes()
} else {
ioid.to_le_bytes()
});
payload.push(subcmd);
payload.push(0x02); payload.extend_from_slice(&encode_string_pva(message, is_be));
payload.extend_from_slice(&encode_string_pva("", is_be));
let mut out = encode_header(true, is_be, false, version, command, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn encode_beacon(
guid: [u8; 12],
seq: u8,
change_count: u16,
addr: [u8; 16],
port: u16,
protocol: &str,
version: u8,
is_be: bool,
) -> Vec<u8> {
let mut payload = Vec::new();
payload.extend_from_slice(&guid);
payload.push(0x00); payload.push(seq);
payload.extend_from_slice(&if is_be {
change_count.to_be_bytes()
} else {
change_count.to_le_bytes()
});
payload.extend_from_slice(&addr);
payload.extend_from_slice(&if is_be {
port.to_be_bytes()
} else {
port.to_le_bytes()
});
payload.extend_from_slice(&encode_string_pva(protocol, is_be));
payload.push(0xFF);
let mut out = encode_header(true, is_be, false, version, 0, payload.len() as u32);
out.extend_from_slice(&payload);
out
}
pub fn ip_to_bytes(ip: IpAddr) -> [u8; 16] {
match ip {
IpAddr::V4(v4) => {
let mut out = [0u8; 16];
out[10] = 0xFF;
out[11] = 0xFF;
out[12..16].copy_from_slice(&v4.octets());
out
}
IpAddr::V6(v6) => v6.octets(),
}
}
pub fn ip_from_bytes(addr: &[u8; 16]) -> Option<IpAddr> {
if addr.iter().all(|&b| b == 0) {
return None;
}
if addr[0..10].iter().all(|&b| b == 0) && addr[10] == 0xFF && addr[11] == 0xFF {
return Some(IpAddr::V4(Ipv4Addr::new(
addr[12], addr[13], addr[14], addr[15],
)));
}
Some(IpAddr::V6(Ipv6Addr::from(*addr)))
}
pub fn socket_addr_from_pva_bytes(addr: [u8; 16], port: u16) -> Option<SocketAddr> {
ip_from_bytes(&addr).map(|ip| SocketAddr::new(ip, port))
}
pub fn format_pva_address(addr: &[u8; 16]) -> String {
match ip_from_bytes(addr) {
Some(ip) => ip.to_string(),
None => "0.0.0.0".to_string(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::epics_decode::{PvaPacket, PvaPacketCommand};
#[test]
fn encode_decode_connection_validation_roundtrip() {
let msg = encode_connection_validation(4096, 2, 0x10, "test", 2, true);
let mut pkt = PvaPacket::new(&msg);
let cmd = pkt.decode_payload().expect("decoded");
match cmd {
PvaPacketCommand::ConnectionValidation(payload) => {
assert_eq!(payload.buffer_size, 4096);
assert_eq!(payload.introspection_registry_size, 2);
assert_eq!(payload.qos, 0x10);
assert_eq!(payload.authz.as_deref(), Some("test"));
}
other => panic!("unexpected decode: {:?}", other),
}
}
#[test]
fn encode_decode_client_connection_validation_roundtrip() {
let msg = encode_client_connection_validation(
87_040, 32_767, 0, "ca", "alice", "host1", 2, false,
);
let mut pkt = PvaPacket::new(&msg);
let cmd = pkt.decode_payload().expect("decoded");
match cmd {
PvaPacketCommand::ConnectionValidation(payload) => {
assert!(!payload.is_server);
assert_eq!(payload.buffer_size, 87_040);
assert_eq!(payload.introspection_registry_size, 32_767);
assert_eq!(payload.qos, 0);
}
other => panic!("unexpected decode: {:?}", other),
}
}
#[test]
fn encode_decode_search_response_roundtrip() {
let guid = [1u8; 12];
let seq = 42;
let addr = [0u8; 16];
let port = 5075;
let cids = vec![100u32, 101u32];
let msg = encode_search_response(guid, seq, addr, port, "tcp", true, &cids, 2, false);
let mut pkt = PvaPacket::new(&msg);
let cmd = pkt.decode_payload().expect("decoded");
match cmd {
PvaPacketCommand::SearchResponse(payload) => {
assert_eq!(payload.guid, guid);
assert_eq!(payload.seq, seq);
assert_eq!(payload.port, port);
assert!(payload.found);
assert_eq!(payload.cids, cids);
}
other => panic!("unexpected decode: {:?}", other),
}
}
#[test]
fn encode_decode_connection_validated_roundtrip() {
let msg = encode_connection_validated(true, 2, false);
let mut pkt = PvaPacket::new(&msg);
let cmd = pkt.decode_payload().expect("decoded");
match cmd {
PvaPacketCommand::ConnectionValidated(payload) => {
assert!(payload.status.is_none());
}
other => panic!("unexpected decode: {:?}", other),
}
}
#[test]
fn get_data_response_includes_status() {
let nt = NtScalar::from_value(spvirit_types::ScalarValue::F64(1.0));
let msg = encode_op_get_data_response_payload(0x11223344, &NtPayload::Scalar(nt), 2, false);
assert!(msg.len() > 13);
let status_offset = 8 + 4 + 1;
assert_eq!(msg[status_offset], 0xFF);
let mut pkt = PvaPacket::new(&msg);
let cmd = pkt.decode_payload().expect("decoded");
match cmd {
PvaPacketCommand::Op(op) => {
assert_eq!(op.command, 10);
assert_eq!(op.subcmd, 0x00);
assert!(!op.body.is_empty());
}
other => panic!("unexpected decode: {:?}", other),
}
}
#[test]
fn put_get_init_includes_two_descriptors() {
let nt = NtScalar::from_value(spvirit_types::ScalarValue::F64(1.0));
let desc = crate::spvd_encode::nt_scalar_desc(&nt.value);
let msg = encode_op_put_get_init_response(0x01020304, &desc, &desc, 2, false);
let payload = &msg[8..];
assert!(payload.len() > 6);
assert_eq!(payload[5], 0xFF);
let rest = &payload[6..];
let first = rest.first().copied().unwrap_or(0);
assert_eq!(first, 0x80);
let second_pos = rest.iter().skip(1).position(|b| *b == 0x80);
assert!(second_pos.is_some(), "expected second descriptor marker");
}
#[test]
fn put_get_data_includes_status() {
let nt = NtScalar::from_value(spvirit_types::ScalarValue::F64(2.0));
let msg = encode_op_put_get_data_response(0x55667788, &nt, 2, false);
assert!(msg.len() > 13);
let status_offset = 8 + 4 + 1;
assert_eq!(msg[status_offset], 0xFF);
}
#[test]
fn put_getput_response_encodes_subcmd_0x40() {
let nt = NtScalar::from_value(spvirit_types::ScalarValue::F64(2.0));
let msg = encode_op_put_getput_response(0x01020304, &nt, 2, false);
assert!(msg.len() > 13);
let status_offset = 8 + 4 + 1;
assert_eq!(msg[status_offset], 0xFF);
let mut pkt = PvaPacket::new(&msg);
let cmd = pkt.decode_payload().expect("decoded");
match cmd {
PvaPacketCommand::Op(op) => {
assert_eq!(op.command, 11);
assert_eq!(op.subcmd, 0x40);
}
other => panic!("unexpected decode: {:?}", other),
}
}
#[test]
fn encode_get_field_response_roundtrip() {
let desc = StructureDesc {
struct_id: Some("epics:nt/NTScalar:1.0".to_string()),
fields: vec![crate::spvd_decode::FieldDesc {
name: "value".to_string(),
field_type: crate::spvd_decode::FieldType::Scalar(
crate::spvd_decode::TypeCode::String,
),
}],
};
let msg = encode_get_field_response(11, &desc, 2, false);
let mut pkt = PvaPacket::new(&msg);
let cmd = pkt.decode_payload().expect("decoded");
match cmd {
PvaPacketCommand::GetField(payload) => {
assert!(payload.is_server);
assert_eq!(payload.cid, 11);
assert!(payload.status.is_none());
let intro = payload.introspection.expect("introspection");
assert_eq!(intro.fields.len(), 1);
assert_eq!(intro.fields[0].name, "value");
}
other => panic!("unexpected decode: {:?}", other),
}
}
#[test]
fn encode_get_field_error_roundtrip() {
let msg = encode_get_field_error(7, "listing disabled", 2, false);
let mut pkt = PvaPacket::new(&msg);
let cmd = pkt.decode_payload().expect("decoded");
match cmd {
PvaPacketCommand::GetField(payload) => {
assert!(payload.is_server);
assert_eq!(payload.cid, 7);
let status = payload.status.expect("status");
assert_eq!(status.code, 0x02);
assert_eq!(status.message.as_deref(), Some("listing disabled"));
}
other => panic!("unexpected decode: {:?}", other),
}
}
#[test]
fn encode_decode_create_channel_request_roundtrip() {
let msg = encode_create_channel_request(7, "TEST:PV", 2, false);
let mut pkt = PvaPacket::new(&msg);
let cmd = pkt.decode_payload().expect("decoded");
match cmd {
PvaPacketCommand::CreateChannel(payload) => {
assert!(!payload.is_server);
assert_eq!(payload.channels, vec![(7, "TEST:PV".to_string())]);
}
other => panic!("unexpected decode: {:?}", other),
}
}
#[test]
fn encode_decode_get_field_request_roundtrip() {
let msg = encode_get_field_request(9, 1, Some("*"), 2, false);
let mut pkt = PvaPacket::new(&msg);
let cmd = pkt.decode_payload().expect("decoded");
match cmd {
PvaPacketCommand::GetField(payload) => {
assert!(!payload.is_server);
assert_eq!(payload.sid, Some(9));
assert_eq!(payload.ioid, Some(1));
assert_eq!(payload.field_name.as_deref(), Some("*"));
}
other => panic!("unexpected decode: {:?}", other),
}
}
#[test]
fn encode_decode_get_request_roundtrip() {
let msg = encode_get_request(1, 2, 0x08, &[0xfd, 0x02, 0x00], 2, false);
let mut pkt = PvaPacket::new(&msg);
let cmd = pkt.decode_payload().expect("decoded");
match cmd {
PvaPacketCommand::Op(op) => {
assert_eq!(op.command, 10);
assert_eq!(op.sid_or_cid, 1);
assert_eq!(op.ioid, 2);
assert_eq!(op.subcmd, 0x08);
}
other => panic!("unexpected decode: {:?}", other),
}
}
#[test]
fn encode_decode_put_request_roundtrip() {
let msg = encode_put_request(3, 4, 0x40, &[0xAA], 2, false);
let mut pkt = PvaPacket::new(&msg);
let cmd = pkt.decode_payload().expect("decoded");
match cmd {
PvaPacketCommand::Op(op) => {
assert_eq!(op.command, 11);
assert_eq!(op.sid_or_cid, 3);
assert_eq!(op.ioid, 4);
assert_eq!(op.subcmd, 0x40);
}
other => panic!("unexpected decode: {:?}", other),
}
}
#[test]
fn encode_decode_monitor_request_roundtrip() {
let msg = encode_monitor_request(5, 6, 0x44, &[], 2, false);
let mut pkt = PvaPacket::new(&msg);
let cmd = pkt.decode_payload().expect("decoded");
match cmd {
PvaPacketCommand::Op(op) => {
assert_eq!(op.command, 13);
assert_eq!(op.sid_or_cid, 5);
assert_eq!(op.ioid, 6);
assert_eq!(op.subcmd, 0x44);
}
other => panic!("unexpected decode: {:?}", other),
}
}
#[test]
fn encode_decode_rpc_request_roundtrip() {
let msg = encode_rpc_request(7, 8, 0x00, &[0x80, 0x00], 2, false);
let mut pkt = PvaPacket::new(&msg);
let cmd = pkt.decode_payload().expect("decoded");
match cmd {
PvaPacketCommand::Op(op) => {
assert_eq!(op.command, 20);
assert_eq!(op.sid_or_cid, 7);
assert_eq!(op.ioid, 8);
assert_eq!(op.subcmd, 0x00);
}
other => panic!("unexpected decode: {:?}", other),
}
}
#[test]
fn encode_decode_search_request_roundtrip() {
let seq = 1234;
let cid = 42;
let port = 5076;
let reply_addr = ip_to_bytes(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 20)));
let requests = [(cid, "TEST:PV")];
let msg = encode_search_request(seq, 0x81, port, reply_addr, &requests, 2, false);
let mut pkt = PvaPacket::new(&msg);
let cmd = pkt.decode_payload().expect("decoded");
match cmd {
PvaPacketCommand::Search(payload) => {
assert_eq!(payload.seq, seq);
assert_eq!(payload.mask, 0x81);
assert_eq!(payload.addr, reply_addr);
assert_eq!(payload.port, port);
assert_eq!(payload.protocols, vec!["tcp".to_string()]);
assert_eq!(payload.pv_requests.len(), 1);
assert_eq!(payload.pv_requests[0].0, cid);
assert_eq!(payload.pv_requests[0].1, "TEST:PV");
}
other => panic!("unexpected decode: {:?}", other),
}
}
#[test]
fn socket_addr_from_pva_bytes_decodes_ipv4_mapped() {
let addr = ip_to_bytes(IpAddr::V4(Ipv4Addr::new(10, 20, 30, 40)));
assert_eq!(
socket_addr_from_pva_bytes(addr, 5075),
Some("10.20.30.40:5075".parse().unwrap())
);
}
#[test]
fn socket_addr_from_pva_bytes_decodes_ipv6() {
let addr = ip_to_bytes(IpAddr::V6("2001:db8::1".parse().unwrap()));
assert_eq!(
socket_addr_from_pva_bytes(addr, 5075),
Some("[2001:db8::1]:5075".parse().unwrap())
);
}
#[test]
fn socket_addr_from_pva_bytes_returns_none_for_unspecified() {
assert_eq!(socket_addr_from_pva_bytes([0u8; 16], 5075), None);
}
}