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