Skip to content

Commit

Permalink
Optimize Name parsing by using a pair of TinyVec
Browse files Browse the repository at this point in the history
The previous implementation used Rc to represent Label, then composed
those in an array to represent Name. That produced a large number of
small allocations in the parsing code path. This new implementation
avoids allocations entirely for small names, and unless the name has a
very large number of labels, it is stored entirely in one allocation.

This also removes the Index impl for Name. Since we no longer contain
any Labels, we cannot implement that (a common problem with Index leaking
implementation details).
  • Loading branch information
saethlin committed Mar 6, 2021
1 parent 116a4a8 commit 7b6b79a
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 109 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 1 addition & 10 deletions crates/client/src/rr/lower_name.rs
Expand Up @@ -11,14 +11,13 @@ use std::borrow::Borrow;
use std::cmp::{Ordering, PartialEq};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::Index;
use std::str::FromStr;

use crate::proto::error::*;
#[cfg(feature = "serde-config")]
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};

use crate::rr::{Label, Name};
use crate::rr::Name;
use crate::serialize::binary::*;

/// them should be through references. As a workaround the Strings are all Rc as well as the array
Expand Down Expand Up @@ -191,14 +190,6 @@ impl fmt::Display for LowerName {
}
}

impl Index<usize> for LowerName {
type Output = Label;

fn index(&self, _index: usize) -> &Label {
&(self.0[_index])
}
}

impl PartialOrd<LowerName> for LowerName {
fn partial_cmp(&self, other: &LowerName) -> Option<Ordering> {
Some(self.cmp(other))
Expand Down
1 change: 1 addition & 0 deletions crates/proto/Cargo.toml
Expand Up @@ -78,6 +78,7 @@ serde = { version = "1.0", features = ["derive"], optional = true }
smallvec = "1.6"
socket2 = { version = "0.3.16", optional = true }
thiserror = "1.0.20"
tinyvec = { version = "1.1.1", features = ["alloc"] }
tokio = { version = "1.0", optional = true }
url = "2.1.0"
wasm-bindgen-crate = { version = "0.2.58", optional = true, package = "wasm-bindgen" }
Expand Down
12 changes: 6 additions & 6 deletions crates/proto/src/rr/domain/label.rs
Expand Up @@ -17,7 +17,7 @@ use std::borrow::Borrow;
use std::cmp::{Ordering, PartialEq};
use std::fmt::{self, Debug, Display, Formatter, Write};
use std::hash::{Hash, Hasher};
use std::sync::Arc as Rc;
use tinyvec::TinyVec;

use idna;
use log::debug;
Expand All @@ -29,7 +29,7 @@ const IDNA_PREFIX: &[u8] = b"xn--";

/// Labels are always stored as ASCII, unicode characters must be encoded with punycode
#[derive(Clone, Eq)]
pub struct Label(Rc<[u8]>);
pub struct Label(TinyVec<[u8; 24]>);

impl Label {
/// These must only be ASCII, with unicode encoded to PunyCode, or other such transformation.
Expand All @@ -40,7 +40,7 @@ impl Label {
if bytes.len() > 63 {
return Err(format!("Label exceeds maximum length 63: {}", bytes.len()).into());
};
Ok(Label(Rc::from(bytes)))
Ok(Label(TinyVec::from(bytes)))
}

/// Translates this string into IDNA safe name, encoding to punycode as necessary.
Expand Down Expand Up @@ -86,7 +86,7 @@ impl Label {

/// Returns a new Label of the Wildcard, i.e. "*"
pub fn wildcard() -> Self {
Label(Rc::from(WILDCARD.to_vec()))
Label(TinyVec::from(WILDCARD))
}

/// Converts this label to lowercase
Expand All @@ -100,7 +100,7 @@ impl Label {
{
let mut lower_label: Vec<u8> = self.0.to_vec();
lower_label[idx..].make_ascii_lowercase();
Label(Rc::from(lower_label))
Label(TinyVec::from(lower_label.as_slice()))
} else {
self.clone()
}
Expand Down Expand Up @@ -201,7 +201,7 @@ impl Label {

impl AsRef<[u8]> for Label {
fn as_ref(&self) -> &[u8] {
&self.0
self.as_bytes()
}
}

Expand Down

0 comments on commit 7b6b79a

Please sign in to comment.