use std::{cmp, fmt, hash, ops};
use bytes::BufMut;
use ::bits::compose::Compose;
use ::bits::parse::ShortBuf;
pub struct Label([u8]);
impl Label {
pub(super) unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
&*(slice as *const [u8] as *const Self)
}
pub fn root() -> &'static Self {
unsafe { Self::from_slice_unchecked(b"") }
}
pub fn wildcard() -> &'static Self {
unsafe { Self::from_slice_unchecked(b"*") }
}
pub fn from_slice(slice: &[u8]) -> Result<&Self, LongLabelError> {
if slice.len() > 63 {
Err(LongLabelError)
}
else {
Ok(unsafe { Self::from_slice_unchecked(slice) })
}
}
pub fn split_from(slice: &[u8])
-> Result<(&Self, &[u8]), SplitLabelError> {
let head = match slice.get(0) {
Some(ch) => *ch,
None => return Err(SplitLabelError::ShortBuf)
};
let end = match head {
0 ... 0x3F => (head as usize) + 1,
0x40 ... 0x7F => {
return Err(
SplitLabelError::BadType(LabelTypeError::Extended(head))
)
}
0xC0 ... 0xFF => {
let res = match slice.get(1) {
Some(ch) => u16::from(*ch),
None => return Err(SplitLabelError::ShortBuf)
};
let res = res | ((u16::from(head) & 0x3F) << 8);
return Err(SplitLabelError::Pointer(res))
}
_ => {
return Err(
SplitLabelError::BadType(LabelTypeError::Undefined)
)
}
};
if slice.len() < end {
return Err(SplitLabelError::ShortBuf)
}
Ok((unsafe { Self::from_slice_unchecked(&slice[1..end]) },
&slice[end..]))
}
pub fn as_slice(&self) -> &[u8] {
self.as_ref()
}
pub fn as_slice_mut(&mut self) -> &mut [u8] {
self.as_mut()
}
}
impl Label {
pub fn is_root(&self) -> bool {
self.is_empty()
}
}
impl Compose for Label {
fn compose_len(&self) -> usize {
self.len() + 1
}
fn compose<B: BufMut>(&self, buf: &mut B) {
buf.put_u8(self.len() as u8);
buf.put_slice(self.as_ref());
}
}
impl ops::Deref for Label {
type Target = [u8];
fn deref(&self) -> &[u8] {
self.as_ref()
}
}
impl ops::DerefMut for Label {
fn deref_mut(&mut self) -> &mut [u8] {
self.as_mut()
}
}
impl AsRef<[u8]> for Label {
fn as_ref(&self) -> &[u8] {
unsafe { &*(self as *const Self as *const [u8]) }
}
}
impl AsMut<[u8]> for Label {
fn as_mut(&mut self) -> &mut [u8] {
unsafe { &mut *(self as *mut Label as *mut [u8]) }
}
}
impl PartialEq for Label {
fn eq(&self, other: &Self) -> bool {
self.eq_ignore_ascii_case(other)
}
}
impl Eq for Label { }
impl PartialOrd for Label {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
self.iter().map(u8::to_ascii_lowercase).partial_cmp(
other.iter().map(u8::to_ascii_lowercase)
)
}
}
impl Ord for Label {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.iter().map(u8::to_ascii_lowercase).cmp(
other.iter().map(u8::to_ascii_lowercase)
)
}
}
impl hash::Hash for Label {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
(self.len() as u8).hash(state);
for c in self.iter() {
c.to_ascii_lowercase().hash(state)
}
}
}
impl<'a> IntoIterator for &'a Label {
type Item = &'a u8;
type IntoIter = ::std::slice::Iter<'a, u8>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl fmt::Display for Label {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for &ch in self.iter() {
if ch == b' ' || ch == b'.' || ch == b'\\' {
write!(f, "\\{}", ch as char)?;
}
else if ch < b' ' || ch >= 0x7F {
write!(f, "\\{:03}", ch)?;
}
else {
write!(f, "{}", (ch as char))?;
}
}
Ok(())
}
}
impl fmt::Debug for Label {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("Label(")?;
fmt::Display::fmt(self, f)?;
f.write_str(")")
}
}
#[derive(Clone, Copy, Debug, Eq, Fail, PartialEq)]
pub enum LabelTypeError {
#[fail(display="undefined label type")]
Undefined,
#[fail(display="unknown extended label 0x{:02x}", _0)]
Extended(u8),
}
#[derive(Clone, Copy, Debug, Eq, Fail, PartialEq)]
#[fail(display="long label")]
pub struct LongLabelError;
#[derive(Clone, Copy, Debug, Eq, Fail, PartialEq)]
pub enum SplitLabelError {
#[fail(display="compressed domain name")]
Pointer(u16),
#[fail(display="{}", _0)]
BadType(LabelTypeError),
#[fail(display="unexpected end of input")]
ShortBuf,
}
impl From<LabelTypeError> for SplitLabelError {
fn from(err: LabelTypeError) -> SplitLabelError {
SplitLabelError::BadType(err)
}
}
impl From<ShortBuf> for SplitLabelError {
fn from(_: ShortBuf) -> SplitLabelError {
SplitLabelError::ShortBuf
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn from_slice() {
let x = [0u8; 10];
assert_eq!(Label::from_slice(&x[..]).unwrap().as_slice(),
&x[..]);
let x = [0u8; 63];
assert_eq!(Label::from_slice(&x[..]).unwrap().as_slice(),
&x[..]);
let x = [0u8; 64];
assert!(Label::from_slice(&x[..]).is_err());
}
#[test]
fn split_from() {
assert_eq!(Label::split_from(b"\x03www\x07example\x03com\0").unwrap(),
(Label::from_slice(b"www").unwrap(),
&b"\x07example\x03com\0"[..]));
assert_eq!(Label::split_from(b"\x03www").unwrap(),
(Label::from_slice(b"www").unwrap(),
&b""[..]));
assert_eq!(Label::split_from(b"\0some").unwrap(),
(Label::from_slice(b"").unwrap(),
&b"some"[..]));
assert_eq!(Label::split_from(b"\x03ww"),
Err(SplitLabelError::ShortBuf));
assert_eq!(Label::split_from(b""),
Err(SplitLabelError::ShortBuf));
assert_eq!(Label::split_from(b"\xc0\x05foo"),
Err(SplitLabelError::Pointer(5)));
assert_eq!(Label::split_from(b"\xb3foo"),
Err(LabelTypeError::Undefined.into()));
assert_eq!(Label::split_from(b"\x66foo"),
Err(LabelTypeError::Extended(0x66).into()));
}
#[test]
fn compose() {
use bytes::BytesMut;
let mut buf = BytesMut::with_capacity(64);
assert_eq!(Label::root().compose_len(), 1);
Label::root().compose(&mut buf);
assert_eq!(buf.freeze(), &b"\0"[..]);
let mut buf = BytesMut::with_capacity(64);
let label = Label::from_slice(b"123").unwrap();
assert_eq!(label.compose_len(), 4);
label.compose(&mut buf);
assert_eq!(buf.freeze(), &b"\x03123"[..]);
}
#[test]
fn eq() {
assert_eq!(Label::from_slice(b"example").unwrap(),
Label::from_slice(b"eXAMple").unwrap());
assert_ne!(Label::from_slice(b"example").unwrap(),
Label::from_slice(b"e4ample").unwrap());
}
#[test]
fn cmp() {
use std::cmp::Ordering;
let labels = [Label::root(),
Label::from_slice(b"\x01").unwrap(),
Label::from_slice(b"*").unwrap(),
Label::from_slice(b"\xc8").unwrap()];
for i in 0..labels.len() {
for j in 0..labels.len() {
let ord = if i < j { Ordering::Less }
else if i == j { Ordering::Equal }
else { Ordering::Greater };
assert_eq!(labels[i].partial_cmp(&labels[j]), Some(ord));
assert_eq!(labels[i].cmp(&labels[j]), ord);
}
}
let l1 = Label::from_slice(b"example").unwrap();
let l2 = Label::from_slice(b"eXAMple").unwrap();
assert_eq!(l1.partial_cmp(&l2), Some(Ordering::Equal));
assert_eq!(l1.cmp(&l2), Ordering::Equal);
}
#[test]
fn hash() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut s1 = DefaultHasher::new();
let mut s2 = DefaultHasher::new();
Label::from_slice(b"example").unwrap().hash(&mut s1);
Label::from_slice(b"eXAMple").unwrap().hash(&mut s2);
assert_eq!(s1.finish(), s2.finish());
}
}