1use std::{io, mem, str};
2
3#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
5pub struct ExtendedAttribute {
6 name: String,
7 value: Vec<u8>,
8}
9
10impl ExtendedAttribute {
11 #[inline]
20 pub const fn new(name: String, value: Vec<u8>) -> Self {
21 Self { name, value }
22 }
23
24 #[inline]
34 pub fn name(&self) -> &str {
35 &self.name
36 }
37
38 #[inline]
48 pub fn value(&self) -> &[u8] {
49 &self.value
50 }
51
52 pub(crate) fn try_from_bytes(value: &[u8]) -> io::Result<Self> {
53 let (len, value) = value
54 .split_first_chunk::<{ mem::size_of::<u32>() }>()
55 .ok_or(io::ErrorKind::UnexpectedEof)?;
56 let len = u32::from_be_bytes(*len) as usize;
57 let (name, value) = value
58 .split_at_checked(len)
59 .ok_or(io::ErrorKind::UnexpectedEof)?;
60 let name = str::from_utf8(name).map_err(|_| io::ErrorKind::InvalidData)?;
61
62 let (len, value) = value
63 .split_first_chunk::<{ mem::size_of::<u32>() }>()
64 .ok_or(io::ErrorKind::UnexpectedEof)?;
65 let len = u32::from_be_bytes(*len) as usize;
66 let value = value.get(..len).ok_or(io::ErrorKind::UnexpectedEof)?;
67 let value = value.to_vec();
68 let name = name.to_owned();
69 Ok(Self { name, value })
70 }
71
72 pub(crate) fn to_bytes(&self) -> Vec<u8> {
73 let mut vec =
74 Vec::with_capacity(self.name.len() + self.value.len() + mem::size_of::<u32>() * 2);
75 vec.extend_from_slice(&(self.name.len() as u32).to_be_bytes());
76 vec.extend_from_slice(self.name.as_bytes());
77 vec.extend_from_slice(&(self.value.len() as u32).to_be_bytes());
78 vec.extend_from_slice(&self.value);
79 vec
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 #[cfg(all(target_family = "wasm", target_os = "unknown"))]
87 use wasm_bindgen_test::wasm_bindgen_test as test;
88
89 #[test]
90 fn xattr() {
91 let xattr = ExtendedAttribute::new("name".into(), "value".into());
92 assert_eq!(
93 xattr,
94 ExtendedAttribute::try_from_bytes(&xattr.to_bytes()).unwrap()
95 );
96 }
97}