#[cfg(feature = "alloc")]
extern crate alloc;
use core::cmp::min;
#[cfg(feature = "std")]
use std::fmt::{Debug, Formatter};
use crate::consts::PAYLOAD_MAX_SIZE;
use crate::error::SpecError;
use crate::types::{MavLinkVersion, MessageId};
#[cfg(feature = "alloc")]
type PayloadContainer = alloc::vec::Vec<u8>;
#[cfg(not(feature = "alloc"))]
use no_alloc_payload_container::PayloadContainer;
#[derive(Clone)]
#[cfg_attr(feature = "specta", derive(specta::Type))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(not(feature = "std"), derive(Debug))]
pub struct Payload {
id: MessageId,
payload: PayloadContainer,
length: usize,
version: MavLinkVersion,
}
#[allow(clippy::derivable_impls)]
impl Default for Payload {
fn default() -> Self {
Self {
id: MessageId::default(),
payload: Payload::container_default(),
version: MavLinkVersion::default(),
length: PAYLOAD_MAX_SIZE,
}
}
}
#[cfg(feature = "std")]
impl Debug for Payload {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let payload = match self.version {
MavLinkVersion::V1 => &self.payload[0..min(self.payload.len(), self.length)],
MavLinkVersion::V2 => {
&self.payload[0..min(self.payload.len(), Self::truncated_length(&self.payload))]
}
};
f.debug_struct("payload")
.field("id", &self.id)
.field("payload", &payload)
.field("length", &self.length)
.field("version", &self.version)
.finish()
}
}
impl Payload {
pub fn new(id: MessageId, payload: &[u8], version: MavLinkVersion) -> Self {
let max_size = min(PAYLOAD_MAX_SIZE, payload.len());
let length = match version {
MavLinkVersion::V1 => max_size,
MavLinkVersion::V2 => Self::truncated_length(&payload[0..max_size]),
};
let payload = Self::container_from_slice(payload, length);
Self {
id,
payload,
length,
version,
}
}
pub fn id(&self) -> MessageId {
self.id
}
pub fn bytes(&self) -> &[u8] {
&self.payload[0..self.length]
}
pub fn bytes_mut(&mut self) -> &mut [u8] {
#[cfg(feature = "alloc")]
let slice = &mut self.payload.as_mut_slice()[0..self.length];
#[cfg(not(feature = "alloc"))]
let slice = &mut self.payload.content.as_mut_slice()[0..self.length];
slice
}
pub fn version(&self) -> MavLinkVersion {
self.version
}
pub fn length(&self) -> u8 {
self.length as u8
}
pub fn upgrade(&mut self) {
self.version = MavLinkVersion::V2;
self.length = Self::truncated_length(self.bytes());
}
pub fn upgraded(self) -> Self {
Self::new(self.id, self.bytes(), MavLinkVersion::V2)
}
fn truncated_length(slice: &[u8]) -> usize {
let n: usize = slice.len();
let mut num_non_zero = 0usize;
for i in 1..=n {
if slice[n - i] != 0u8 {
num_non_zero = n - i + 1;
break;
}
}
core::cmp::max(num_non_zero, 1)
}
fn container_from_slice(value: &[u8], max_size: usize) -> PayloadContainer {
let value: &[u8] = if value.len() > max_size {
&value[0..max_size]
} else {
value
};
#[cfg(not(feature = "alloc"))]
let payload: PayloadContainer = PayloadContainer {
content: {
let mut no_std_payload = [0u8; PAYLOAD_MAX_SIZE];
let max_len = if PAYLOAD_MAX_SIZE < value.len() {
PAYLOAD_MAX_SIZE
} else {
value.len()
};
no_std_payload[0..max_len].copy_from_slice(&value[0..max_len]);
no_std_payload
},
};
#[cfg(feature = "alloc")]
let payload: PayloadContainer = if value.len() < max_size {
let mut payload = alloc::vec![0u8; max_size];
payload[0..value.len()].copy_from_slice(value);
payload
} else {
PayloadContainer::from(value)
};
payload
}
fn container_default() -> PayloadContainer {
PayloadContainer::default()
}
}
pub trait IntoPayload {
fn encode(&self, version: MavLinkVersion) -> Result<Payload, SpecError>;
}
#[cfg(not(feature = "alloc"))]
mod no_alloc_payload_container {
use crate::payload::PAYLOAD_MAX_SIZE;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PayloadContainer {
#[cfg_attr(feature = "serde", serde(with = "serde_arrays"))]
pub(super) content: [u8; PAYLOAD_MAX_SIZE],
}
impl Default for PayloadContainer {
fn default() -> Self {
Self {
content: [0u8; PAYLOAD_MAX_SIZE],
}
}
}
impl<Idx> core::ops::Index<Idx> for PayloadContainer
where
Idx: core::slice::SliceIndex<[u8]>,
{
type Output = Idx::Output;
fn index(&self, index: Idx) -> &Self::Output {
&self.content[index]
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_payload() {
let payload = Payload::new(0, &[1, 2, 3, 4, 5, 6u8], MavLinkVersion::V1);
assert_eq!(payload.length(), 6);
assert_eq!(payload.bytes().len(), 6);
assert_eq!(payload.bytes(), &[1, 2, 3, 4, 5, 6u8]);
let payload = Payload::new(0, &[1, 2, 3, 4, 0, 0u8], MavLinkVersion::V1);
assert_eq!(payload.length(), 6);
assert_eq!(payload.bytes().len(), 6);
let payload = Payload::new(0, &[1, 2, 3, 4, 0, 0u8], MavLinkVersion::V2);
assert_eq!(payload.length(), 4);
assert_eq!(payload.bytes().len(), 4);
let payload = Payload::new(0, &[0, 0, 0, 0, 0, 0u8], MavLinkVersion::V2);
assert_eq!(payload.length(), 1);
assert_eq!(payload.bytes().len(), 1);
let payload = Payload::new(0, &[1u8; PAYLOAD_MAX_SIZE * 2], MavLinkVersion::V1);
assert_eq!(payload.length() as usize, PAYLOAD_MAX_SIZE);
assert_eq!(payload.bytes().len(), PAYLOAD_MAX_SIZE);
}
#[test]
fn truncated_length() {
assert_eq!(Payload::truncated_length(&[1, 2, 3, 4, 5, 6u8]), 6);
assert_eq!(Payload::truncated_length(&[1, 2, 3, 4, 0, 0u8]), 4);
assert_eq!(Payload::truncated_length(&[0, 0, 0, 0, 0, 0u8]), 1);
}
}