1 use crate::{Error, Result};
2 use byteorder::{ByteOrder, WriteBytesExt, LE};
3 
4 // Used internally for GVariant encoding and decoding.
5 //
6 // GVariant containers keeps framing offsets at the end and size of these offsets is dependent on
7 // the size of the container (which includes offsets themselves.
8 
9 #[derive(Copy, Clone, Debug, PartialEq)]
10 #[repr(usize)]
11 pub(crate) enum FramingOffsetSize {
12     U8 = 1,
13     U16 = 2,
14     U32 = 4,
15     U64 = 8,
16     U128 = 16,
17 }
18 
19 impl FramingOffsetSize {
for_bare_container(container_len: usize, num_offsets: usize) -> Self20     pub(crate) fn for_bare_container(container_len: usize, num_offsets: usize) -> Self {
21         let mut offset_size = FramingOffsetSize::U8;
22 
23         loop {
24             if container_len + num_offsets * (offset_size as usize) <= offset_size.max() {
25                 return offset_size;
26             }
27 
28             offset_size = offset_size
29                 .bump_up()
30                 .expect("Can't handle container too large for a 128-bit pointer");
31         }
32     }
33 
for_encoded_container(container_len: usize) -> Self34     pub(crate) fn for_encoded_container(container_len: usize) -> Self {
35         Self::for_bare_container(container_len, 0)
36     }
37 
write_offset<W>(self, writer: &mut W, offset: usize) -> Result<()> where W: std::io::Write,38     pub(crate) fn write_offset<W>(self, writer: &mut W, offset: usize) -> Result<()>
39     where
40         W: std::io::Write,
41     {
42         match self {
43             FramingOffsetSize::U8 => writer.write_u8(offset as u8),
44             FramingOffsetSize::U16 => writer.write_u16::<LE>(offset as u16),
45             FramingOffsetSize::U32 => writer.write_u32::<LE>(offset as u32),
46             FramingOffsetSize::U64 => writer.write_u64::<LE>(offset as u64),
47             FramingOffsetSize::U128 => writer.write_u128::<LE>(offset as u128),
48         }
49         .map_err(Error::Io)
50     }
51 
read_last_offset_from_buffer(self, buffer: &[u8]) -> usize52     pub fn read_last_offset_from_buffer(self, buffer: &[u8]) -> usize {
53         if buffer.is_empty() {
54             return 0;
55         }
56 
57         let end = buffer.len();
58         match self {
59             FramingOffsetSize::U8 => buffer[end - 1] as usize,
60             FramingOffsetSize::U16 => LE::read_u16(&buffer[end - 2..end]) as usize,
61             FramingOffsetSize::U32 => LE::read_u32(&buffer[end - 4..end]) as usize,
62             FramingOffsetSize::U64 => LE::read_u64(&buffer[end - 8..end]) as usize,
63             FramingOffsetSize::U128 => LE::read_u128(&buffer[end - 16..end]) as usize,
64         }
65     }
66 
max(self) -> usize67     fn max(self) -> usize {
68         match self {
69             FramingOffsetSize::U8 => std::u8::MAX as usize,
70             FramingOffsetSize::U16 => std::u16::MAX as usize,
71             FramingOffsetSize::U32 => std::u32::MAX as usize,
72             FramingOffsetSize::U64 => std::u64::MAX as usize,
73             FramingOffsetSize::U128 => std::u128::MAX as usize,
74         }
75     }
76 
bump_up(self) -> Option<Self>77     fn bump_up(self) -> Option<Self> {
78         match self {
79             FramingOffsetSize::U8 => Some(FramingOffsetSize::U16),
80             FramingOffsetSize::U16 => Some(FramingOffsetSize::U32),
81             FramingOffsetSize::U32 => Some(FramingOffsetSize::U64),
82             FramingOffsetSize::U64 => Some(FramingOffsetSize::U128),
83             FramingOffsetSize::U128 => None,
84         }
85     }
86 }
87 
88 #[cfg(test)]
89 mod tests {
90     use crate::framing_offset_size::FramingOffsetSize;
91 
92     #[test]
framing_offset_size_bump()93     fn framing_offset_size_bump() {
94         assert_eq!(
95             FramingOffsetSize::for_bare_container(std::u8::MAX as usize - 3, 3),
96             FramingOffsetSize::U8
97         );
98         assert_eq!(
99             FramingOffsetSize::for_bare_container(std::u8::MAX as usize - 1, 2),
100             FramingOffsetSize::U16
101         );
102         assert_eq!(
103             FramingOffsetSize::for_bare_container(std::u16::MAX as usize - 4, 2),
104             FramingOffsetSize::U16
105         );
106         assert_eq!(
107             FramingOffsetSize::for_bare_container(std::u16::MAX as usize - 3, 2),
108             FramingOffsetSize::U32
109         );
110         assert_eq!(
111             FramingOffsetSize::for_bare_container(std::u32::MAX as usize - 12, 3),
112             FramingOffsetSize::U32
113         );
114         assert_eq!(
115             FramingOffsetSize::for_bare_container(std::u32::MAX as usize - 11, 3),
116             FramingOffsetSize::U64
117         );
118     }
119 }
120