use core::fmt;
use core::str;
use crate::Error;
#[derive(Debug, Clone)]
pub struct Name<'a> {
pub(crate) buf: &'a [u8],
pub(crate) start: usize,
}
impl<'a> Name<'a> {
#[cfg(test)]
pub(crate) fn from_bytes(bytes: &'a [u8]) -> Self {
Self { buf: bytes, start: 0 }
}
pub(crate) fn create_pointer(&self, sub_name: &Name<'_>) -> Option<[u8; 2]> {
let mut labels = self.labels();
loop {
let i = labels.buf_i;
if labels.next().is_some() {
if self.equal_from(i, sub_name) {
let [ptr_1, ptr_2] = (i as u16).to_be_bytes();
return Some([ptr_1 | PTR_MASK, ptr_2]);
}
continue;
}
break;
}
None
}
fn equal_from(&self, i: usize, sub_name: &Name<'_>) -> bool {
let mut this = Labels { buf: self.buf, buf_i: i };
let mut other = sub_name.labels();
loop {
match (this.next(), other.next()) {
(Some(t), Some(o)) => if !t.as_str().eq_ignore_ascii_case(o.as_str()) {
break;
},
(None, None) => return true,
_ => break,
}
}
false
}
pub(crate) fn read(buf: &'a [u8], i: &'_ mut usize) -> Result<Self, Error> {
let mut j = *i;
let mut maximum = *i;
let mut ptr = None;
let mut len: u8 = 0;
loop {
match LabelType::read(buf, ptr.as_mut().unwrap_or(&mut j))? {
LabelType::Pointer(p) => {
let p = p as usize;
if p >= maximum {
return Err(Error::Pointer)
}
maximum = p;
ptr = Some(p);
},
LabelType::Part(part_len) => {
if part_len == 0 {
let name = Name {
buf,
start: *i,
};
*i = j;
return Ok(name)
}
if let Some(l) = len.checked_add(part_len) {
len = l;
} else {
return Err(Error::NameTooLong)
};
},
}
}
}
pub(crate) fn split(&self) -> (Label<'a>, Option<Name<'a>>) {
let mut labels = self.labels();
let label = labels.next().unwrap();
let name = labels.next().map(|l| Name { buf: l.buf, start: l.buf_i });
(label, name)
}
pub(crate) fn labels(&self) -> Labels<'a> {
Labels {
buf: self.buf,
buf_i: self.start,
}
}
}
impl fmt::Display for Name<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut print_dot = false;
for label in self.labels() {
if print_dot {
'.'.fmt(f)?;
}
label.as_str().fmt(f)?;
print_dot = true;
}
Ok(())
}
}
impl PartialEq<str> for Name<'_> {
fn eq(&self, other: &str) -> bool {
let mut other_i = 0;
let other = other.as_bytes();
for label in self.labels() {
if other_i != 0 {
if other.get(other_i) != Some(&b'.') {
return false
} else {
other_i += 1;
}
}
if let Some(substring) = other.get(other_i..(other_i + label.len())) {
if !label.as_bytes().eq_ignore_ascii_case(substring) {
return false
}
} else {
return false
}
other_i += label.len();
}
other_i == other.len()
}
}
#[derive(Debug, Clone)]
pub(crate) struct Labels<'a> {
buf: &'a [u8],
buf_i: usize,
}
impl<'a> Iterator for Labels<'a> {
type Item = Label<'a>;
fn next(&mut self) -> Option<Self::Item> {
loop {
match LabelType::read(self.buf, &mut self.buf_i).ok()? {
LabelType::Pointer(ptr) => {
self.buf_i = ptr as usize;
continue;
},
LabelType::Part(len) => {
if len == 0 {
return None
}
let label = Label {
buf: self.buf,
buf_i: self.buf_i - len as usize - 1,
};
return Some(label)
},
}
}
}
}
#[derive(Debug)]
pub(crate) struct Label<'a> {
pub(crate) buf: &'a [u8],
pub(crate) buf_i: usize,
}
impl<'a> Label<'a> {
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.buf[(self.buf_i + 1)..(self.buf_i + 1 + self.len())]
}
#[inline]
pub fn as_str(&self) -> &str {
unsafe { str::from_utf8_unchecked(self.as_bytes()) }
}
#[inline]
pub fn len(&self) -> usize {
self.buf[self.buf_i] as usize
}
}
#[derive(Debug)]
pub(crate) enum LabelType {
Pointer(u16),
Part(u8),
}
const PTR_MASK: u8 = 0b11000000;
const LEN_MASK: u8 = !PTR_MASK;
impl LabelType {
pub(crate) fn read(buf: &[u8], i: &mut usize) -> Result<Self, Error> {
if let Some(&ptr_or_len) = buf.get(*i) {
if ptr_or_len & PTR_MASK == PTR_MASK {
if let Some(&ptr) = buf.get(*i + 1) {
*i += 1 + 1;
return Ok(Self::Pointer(u16::from_be_bytes([ptr_or_len & LEN_MASK, ptr])))
}
} else {
let len = ptr_or_len & LEN_MASK;
let j = *i + 1 + len as usize;
if j <= buf.len() {
*i = j;
return Ok(Self::Part(len))
}
}
}
Err(Error::MessageTooShort)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_name_create_pointer() {
let name = Name::from_bytes(&[1, b'a', 1, b'b', 1, b'c', 0]);
let name_uppercase = Name::from_bytes(&[1, b'A', 1, b'B', 1, b'C', 0]);
let sub_name = Name::from_bytes(&[1, b'b', 1, b'c', 0]);
let sub_sub_name = Name::from_bytes(&[1, b'c', 0]);
assert_eq!(name.create_pointer(&name), Some([0b11000000, 0]));
assert_eq!(name_uppercase.create_pointer(&name), Some([0b11000000, 0]));
assert_eq!(name.create_pointer(&name_uppercase), Some([0b11000000, 0]));
assert_eq!(name.create_pointer(&sub_name), Some([0b11000000, 2]));
assert_eq!(sub_name.create_pointer(&name), None);
assert_eq!(sub_name.create_pointer(&sub_sub_name), Some([0b11000000, 2]));
assert_eq!(name.create_pointer(&sub_sub_name), Some([0b11000000, 4]));
assert_eq!(sub_sub_name.create_pointer(&name), None);
assert_eq!(sub_sub_name.create_pointer(&sub_name), None);
}
}