zero_postgres/conversion/
bytes.rs1use crate::error::{Error, Result};
4use crate::protocol::types::{Oid, oid};
5
6use super::{FromWireValue, ToWireValue};
7
8impl<'a> FromWireValue<'a> for &'a [u8] {
9 fn from_text(oid: Oid, bytes: &'a [u8]) -> Result<Self> {
10 if oid != oid::BYTEA {
11 return Err(Error::Decode(format!("cannot decode oid {} as bytes", oid)));
12 }
13 Ok(bytes)
16 }
17
18 fn from_binary(oid: Oid, bytes: &'a [u8]) -> Result<Self> {
19 if oid != oid::BYTEA {
20 return Err(Error::Decode(format!("cannot decode oid {} as bytes", oid)));
21 }
22 Ok(bytes)
23 }
24}
25
26impl FromWireValue<'_> for Vec<u8> {
27 fn from_text(oid: Oid, bytes: &[u8]) -> Result<Self> {
28 if oid != oid::BYTEA {
29 return Err(Error::Decode(format!(
30 "cannot decode oid {} as Vec<u8>",
31 oid
32 )));
33 }
34 if bytes.starts_with(b"\\x") {
36 decode_hex(&bytes[2..])
37 } else {
38 Ok(bytes.to_vec())
40 }
41 }
42
43 fn from_binary(oid: Oid, bytes: &[u8]) -> Result<Self> {
44 if oid != oid::BYTEA {
45 return Err(Error::Decode(format!(
46 "cannot decode oid {} as Vec<u8>",
47 oid
48 )));
49 }
50 Ok(bytes.to_vec())
51 }
52}
53
54impl ToWireValue for [u8] {
55 fn natural_oid(&self) -> Oid {
56 oid::BYTEA
57 }
58
59 fn encode(&self, target_oid: Oid, buf: &mut Vec<u8>) -> Result<()> {
60 match target_oid {
61 oid::BYTEA => {
62 buf.extend_from_slice(&(self.len() as i32).to_be_bytes());
63 buf.extend_from_slice(self);
64 Ok(())
65 }
66 _ => Err(Error::type_mismatch(self.natural_oid(), target_oid)),
67 }
68 }
69}
70
71impl ToWireValue for Vec<u8> {
72 fn natural_oid(&self) -> Oid {
73 oid::BYTEA
74 }
75
76 fn encode(&self, target_oid: Oid, buf: &mut Vec<u8>) -> Result<()> {
77 self.as_slice().encode(target_oid, buf)
78 }
79}
80
81static HEX_LOOKUP: [u8; 256] = {
83 let mut table = [0xFF_u8; 256];
84 let mut i: usize = 0;
85 while i < 256 {
86 table[i] = match i as u8 {
87 b'0'..=b'9' => i as u8 - b'0',
88 b'a'..=b'f' => i as u8 - b'a' + 10,
89 b'A'..=b'F' => i as u8 - b'A' + 10,
90 _ => 0xFF,
91 };
92 i += 1;
93 }
94 table
95};
96
97fn decode_hex(hex: &[u8]) -> Result<Vec<u8>> {
99 if !hex.len().is_multiple_of(2) {
100 return Err(Error::Decode("invalid hex length".into()));
101 }
102
103 let mut result = Vec::with_capacity(hex.len() >> 1);
104 for pair in hex.chunks_exact(2) {
105 let &[hi, lo] = pair else {
106 return Err(Error::Decode("invalid hex length".into()));
107 };
108 let high = HEX_LOOKUP[hi as usize];
109 let low = HEX_LOOKUP[lo as usize];
110 if (high | low) > 0x0F {
111 return Err(Error::Decode("invalid hex digit".into()));
112 }
113 result.push((high << 4) | low);
114 }
115 Ok(result)
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn bytea_hex() {
124 assert_eq!(
125 Vec::<u8>::from_text(oid::BYTEA, b"\\xDEADBEEF").unwrap(),
126 vec![0xDE, 0xAD, 0xBE, 0xEF]
127 );
128 }
129}