use crate::error::{Error, Result};
use crate::protocol::types::{Oid, oid};
use super::{FromWireValue, ToWireValue};
impl<'a> FromWireValue<'a> for &'a [u8] {
fn from_text(oid: Oid, bytes: &'a [u8]) -> Result<Self> {
if oid != oid::BYTEA {
return Err(Error::Decode(format!("cannot decode oid {} as bytes", oid)));
}
Ok(bytes)
}
fn from_binary(oid: Oid, bytes: &'a [u8]) -> Result<Self> {
if oid != oid::BYTEA {
return Err(Error::Decode(format!("cannot decode oid {} as bytes", oid)));
}
Ok(bytes)
}
}
impl FromWireValue<'_> for Vec<u8> {
fn from_text(oid: Oid, bytes: &[u8]) -> Result<Self> {
if oid != oid::BYTEA {
return Err(Error::Decode(format!(
"cannot decode oid {} as Vec<u8>",
oid
)));
}
if bytes.starts_with(b"\\x") {
decode_hex(&bytes[2..])
} else {
Ok(bytes.to_vec())
}
}
fn from_binary(oid: Oid, bytes: &[u8]) -> Result<Self> {
if oid != oid::BYTEA {
return Err(Error::Decode(format!(
"cannot decode oid {} as Vec<u8>",
oid
)));
}
Ok(bytes.to_vec())
}
}
impl ToWireValue for [u8] {
fn natural_oid(&self) -> Oid {
oid::BYTEA
}
fn encode(&self, target_oid: Oid, buf: &mut Vec<u8>) -> Result<()> {
match target_oid {
oid::BYTEA => {
buf.extend_from_slice(&(self.len() as i32).to_be_bytes());
buf.extend_from_slice(self);
Ok(())
}
_ => Err(Error::type_mismatch(self.natural_oid(), target_oid)),
}
}
}
impl ToWireValue for Vec<u8> {
fn natural_oid(&self) -> Oid {
oid::BYTEA
}
fn encode(&self, target_oid: Oid, buf: &mut Vec<u8>) -> Result<()> {
self.as_slice().encode(target_oid, buf)
}
}
static HEX_LOOKUP: [u8; 256] = {
let mut table = [0xFF_u8; 256];
let mut i: usize = 0;
while i < 256 {
table[i] = match i as u8 {
b'0'..=b'9' => i as u8 - b'0',
b'a'..=b'f' => i as u8 - b'a' + 10,
b'A'..=b'F' => i as u8 - b'A' + 10,
_ => 0xFF,
};
i += 1;
}
table
};
fn decode_hex(hex: &[u8]) -> Result<Vec<u8>> {
if !hex.len().is_multiple_of(2) {
return Err(Error::Decode("invalid hex length".into()));
}
let mut result = Vec::with_capacity(hex.len() >> 1);
for pair in hex.chunks_exact(2) {
let &[hi, lo] = pair else {
return Err(Error::Decode("invalid hex length".into()));
};
let high = HEX_LOOKUP[hi as usize];
let low = HEX_LOOKUP[lo as usize];
if (high | low) > 0x0F {
return Err(Error::Decode("invalid hex digit".into()));
}
result.push((high << 4) | low);
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bytea_hex() {
assert_eq!(
Vec::<u8>::from_text(oid::BYTEA, b"\\xDEADBEEF").unwrap(),
vec![0xDE, 0xAD, 0xBE, 0xEF]
);
}
}