http_tiny/
percent_coder.rs1use crate::{ error::Result, helpers::BufReadExt };
2use std::{
3 slice, marker::PhantomData,
4 io::{ BufRead, Write },
5 ops::{ Deref, DerefMut }
6};
7
8
9const ALLOWED_CHARS: &[u8] = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
11
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
15struct Buf {
16 buf: [u8; 3],
18 len: usize
20}
21impl Buf {
22 pub const fn new1() -> Self {
24 Self { buf: [0; 3], len: 1 }
25 }
26 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#[derive(Debug, Default)]
57pub struct PercentEncoder {
58 _private: PhantomData<()>
59}
60impl PercentEncoder {
61 pub const fn new() -> Self {
63 Self { _private: PhantomData }
64 }
65
66 pub fn copy<I, O>(&self, source: &mut I, sink: &mut O) -> Result<usize> where I: BufRead, O: Write {
68 let mut written = 0;
70 while let Some(byte) = source.read_one()? {
71 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 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 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#[derive(Debug, Default)]
100pub struct PercentDecoder {
101 _private: PhantomData<()>
102}
103impl PercentDecoder {
104 pub const fn new() -> Self {
106 Self { _private: PhantomData }
107 }
108
109 pub fn copy<I, O>(&self, source: &mut I, sink: &mut O) -> Result<usize> where I: BufRead, O: Write {
111 let mut written = 0;
113 while let Some(byte) = source.peek_one()? {
114 let mut buf = match byte {
116 b'%' => Buf::new3(),
117 _ => Buf::new1()
118 };
119 source.read_exact(&mut buf)?;
120
121 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 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 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}