http_tiny/
percent_coder.rs

1use crate::{ error::Result, helpers::BufReadExt };
2use std::{
3    slice, marker::PhantomData,
4    io::{ BufRead, Write },
5    ops::{ Deref, DerefMut }
6};
7
8
9/// The allowwed chars
10const ALLOWED_CHARS: &[u8] = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
11
12
13/// A buffer that can hold up to three bytes
14#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
15struct Buf {
16    /// The buffer
17    buf: [u8; 3],
18    /// The length
19    len: usize
20}
21impl Buf {
22    /// Creates a new 1-byte buffer
23    pub const fn new1() -> Self {
24        Self { buf: [0; 3], len: 1 }
25    }
26    /// Creates a new 3-byte buffer
27    pub const fn new3() -> Self {
28        Self { buf: [0; 3], len: 3 }
29    }
30}
31impl From<u8> for Buf {
32    fn from(byte: u8) -> Self {
33        Self { buf: [byte, 0, 0], len: 1 }
34    }
35}
36impl From<[u8; 3]> for Buf {
37    fn from(bytes: [u8; 3]) -> Self {
38        Self { buf: bytes, len: bytes.len() }
39    }
40}
41impl Deref for Buf {
42    type Target = [u8];
43
44    fn deref(&self) -> &Self::Target {
45        &self.buf[..self.len]
46    }
47}
48impl DerefMut for Buf {
49    fn deref_mut(&mut self) -> &mut Self::Target {
50        &mut self.buf[..self.len]
51    }
52}
53
54
55/// A percent encoder
56#[derive(Debug, Default)]
57pub struct PercentEncoder {
58    _private: PhantomData<()>
59}
60impl PercentEncoder {
61    /// Creates a new percent encoder
62    pub const fn new() -> Self {
63        Self { _private: PhantomData }
64    }
65    
66    /// Percent-encodes some data
67    pub fn copy<I, O>(&self, source: &mut I, sink: &mut O) -> Result<usize> where I: BufRead, O: Write {
68        // Copy-encode all bytes
69        let mut written = 0;
70        while let Some(byte) = source.read_one()? {
71            // Encode the byte
72            let encoded = match ALLOWED_CHARS.contains(&byte) {
73                true => Buf::from(byte),
74                false => self.encode_byte(byte)
75            };
76            sink.write_all(&encoded)?;
77            written += encoded.len();
78        }
79        Ok(written)
80    }
81
82    /// Encodes a nibble into a hex char
83    fn encode_nibble(&self, nibble: u8) -> u8 {
84        match nibble {
85            0x0..=0x9 => nibble + b'0',
86            0xA..=0xF => (nibble - 0xA) + b'A',
87            nibble => unreachable!("Invalid nibble value: {nibble}")
88        }
89    }
90    /// Encodes a byte
91    fn encode_byte(&self, byte: u8) -> Buf {
92        let (high, low) = (byte >> 4, byte & 0xF);
93        Buf::from([b'%', self.encode_nibble(high), self.encode_nibble(low)])
94    }
95}
96
97
98/// A percent decoder
99#[derive(Debug, Default)]
100pub struct PercentDecoder {
101    _private: PhantomData<()>
102}
103impl PercentDecoder {
104    /// Creates a new percent decoder
105    pub const fn new() -> Self {
106        Self { _private: PhantomData }
107    }
108    
109    /// Percent-encodes some data
110    pub fn copy<I, O>(&self, source: &mut I, sink: &mut O) -> Result<usize> where I: BufRead, O: Write {
111        // Copy-decode all bytes
112        let mut written = 0;
113        while let Some(byte) = source.peek_one()? {
114            // Fill the buffer
115            let mut buf = match byte {
116                b'%' => Buf::new3(),
117                _ => Buf::new1()
118            };
119            source.read_exact(&mut buf)?;
120
121            // Decode the buffer if necessary and write it
122            let decoded = match byte {
123                b'%' => self.decode_buf(&buf)?,
124                _ => buf[0]
125            };
126            sink.write_all(slice::from_ref(&decoded))?;
127            written += 1;
128        }
129        Ok(written)
130    }
131
132    /// Encodes a nibble into a hex char
133    fn decode_nibble(&self, nibble: u8) -> Result<u8> {
134        match nibble {
135            b'0'..=b'9' => Ok(nibble - b'0'),
136            b'a'..=b'f' => Ok((nibble - b'a') + 0xA),
137            b'A'..=b'F' => Ok((nibble - b'A') + 0xA),
138            nibble => Err(einval!("Invalid hex value: {nibble}"))
139        }
140    }
141    /// Encodes a byte
142    fn decode_buf(&self, buf: &Buf) -> Result<u8> {
143        let (high, low) = (buf[1], buf[2]);
144        Ok(self.decode_nibble(high)? << 4 | self.decode_nibble(low)?)
145    }
146}