Skip to content

Commit

Permalink
Change internal representation of the Multiaddr main struct to `Vec…
Browse files Browse the repository at this point in the history
…<Addr>` instead of `Vec<u8>` (multiformats#19)
  • Loading branch information
ntninja committed Oct 28, 2017
1 parent 6079746 commit bc2b532
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 119 deletions.
86 changes: 48 additions & 38 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6, Ipv4Addr, Ipv6Addr};
/// Representation of a Multiaddr.
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct Multiaddr {
bytes: Vec<u8>,
addr: Vec<Addr>,

#[deprecated]
bytes: Vec<u8>
}

impl ToString for Multiaddr {
Expand All @@ -34,10 +37,11 @@ impl ToString for Multiaddr {
/// ```
///
fn to_string(&self) -> String {
parser::address_from_bytes(self.as_slice()).expect("failed to validate at construction")
parser::multiaddr_to_str(&self.addr)
}
}

#[allow(deprecated)] // We have to access our own deprecated `bytes` field
impl Multiaddr {
/// Create a new multiaddr based on a string representation, like
/// `/ip4/127.0.0.1/udp/1234`.
Expand All @@ -57,17 +61,26 @@ impl Multiaddr {
/// ```
///
pub fn new(input: &str) -> Result<Multiaddr> {
let bytes = parser::multiaddr_from_str(input)?;
let addr = parser::multiaddr_from_str(input)?;

Ok(Multiaddr { bytes: Self::_addr_to_bytes(&addr), addr: addr })
}

Ok(Multiaddr { bytes: bytes })
fn _addr_to_bytes(addr: &Vec<Addr>) -> Vec<u8> {
let mut bytes = Vec::new();
for addr_segment in addr {
addr_segment.to_stream(&mut bytes).unwrap();
}
bytes
}

/// Return a copy to disallow changing the bytes directly
pub fn to_bytes(&self) -> Vec<u8> {
self.bytes.to_owned()
Self::_addr_to_bytes(&self.addr)
}

/// Extracts a slice containing the entire underlying vector.
#[deprecated(note="Use `.to_bytes()` instead")]
pub fn as_slice(&self) -> &[u8] {
&self.bytes
}
Expand All @@ -85,8 +98,27 @@ impl Multiaddr {
/// assert_eq!(address.protocol(), vec![Protocol::IP4]);
/// ```
///
#[deprecated(note="Use `.segments()` instead")]
pub fn protocol(&self) -> Vec<Protocol> {
parser::protocol_from_bytes(&self.bytes[..]).expect("failed to validate at construction")
self.addr.iter().map(|s| s.protocol()).collect()
}

/// Return the individual address segments of this multiaddr
///
/// # Examples
///
/// A single protocol
///
/// ```
/// use std::net::Ipv4Addr;
/// use multiaddr::{Multiaddr, protocol};
///
/// let address = Multiaddr::new("/ip4/127.0.0.1").unwrap();
/// assert_eq!(address.segments(), [protocol::Addr::IP4(protocol::IP4Addr(Ipv4Addr::new(127, 0, 0, 1)))]);
/// ```
///
pub fn segments(&self) -> &[Addr] {
&self.addr
}

/// Wrap a given Multiaddr and return the combination.
Expand All @@ -102,12 +134,10 @@ impl Multiaddr {
/// ```
///
pub fn encapsulate<T: ToMultiaddr>(&self, input: T) -> Result<Multiaddr> {
let new = input.to_multiaddr()?;
let mut bytes = self.bytes.clone();

bytes.extend(new.to_bytes());

Ok(Multiaddr { bytes: bytes })
let mut multiaddr = self.clone();
multiaddr.addr.extend(input.to_multiaddr()?.addr);
multiaddr.bytes = Self::_addr_to_bytes(&multiaddr.addr);
Ok(multiaddr)
}

/// Remove the outer most address from itself.
Expand Down Expand Up @@ -138,36 +168,16 @@ impl Multiaddr {
/// ```
///
pub fn decapsulate<T: ToMultiaddr>(&self, input: T) -> Result<Multiaddr> {
let input = input.to_multiaddr()?.to_bytes();

let bytes_len = self.bytes.len();
let input_length = input.len();

let mut input_pos = 0;
let mut matches = false;

for (i, _) in self.bytes.iter().enumerate() {
let next = i + input_length;
let input = input.to_multiaddr()?;

if next > bytes_len {
continue;
for (idx, addr_window) in self.addr.windows(input.addr.len()).enumerate() {
if addr_window == input.addr.as_slice() {
let addr = self.addr.iter().take(idx).map(|s| s.clone()).collect();
return Ok(Multiaddr { bytes: Self::_addr_to_bytes(&addr), addr: addr });
}

if &self.bytes[i..next] == input.as_slice() {
matches = true;
input_pos = i;
break;
}
}

if !matches {
return Ok(Multiaddr { bytes: self.bytes.clone() });
}

let mut bytes = self.bytes.clone();
bytes.truncate(input_pos);

Ok(Multiaddr { bytes: bytes })
Ok(self.clone())
}
}

Expand Down
100 changes: 23 additions & 77 deletions src/parser.rs
Original file line number Diff line number Diff line change
@@ -1,102 +1,48 @@
use std::str::FromStr;
use std::fmt::Write;

use integer_encoding::{VarInt, VarIntWriter};

use protocol::Protocol;
use protocol::{Addr, AddressSegment, Protocol};
use {Result, Error};

pub fn multiaddr_from_str(input: &str) -> Result<Vec<u8>> {
// drdop trailing slashes
pub fn multiaddr_from_str(input: &str) -> Result<Vec<Addr>> {
// Drop trailing slashes then split address into segment parts
let input = input.trim_right_matches('/');

let mut bytes = vec![];
let mut parts = input.split('/');
let next = parts.next().ok_or(Error::InvalidMultiaddr)?;

if !next.is_empty() {
// Expect address to start with just a slash ('/')
let first = parts.next().ok_or(Error::InvalidMultiaddr)?;
if !first.is_empty() {
return Err(Error::InvalidMultiaddr);
}

let mut multiaddr = Vec::with_capacity(input.split('/').count());
while let Some(n) = parts.next() {
// Determine segment protocol number and possible extra data
let p = Protocol::from_str(n)?;

bytes.write_varint(p as u64)?;

if p.size() == 0 {
continue;
}

let next = match parts.next() {
Some(path) => path,
None => return Err(Error::MissingAddress),
let s = match p.size() {
0 => &"",
_ => parts.next().ok_or(Error::MissingAddress)?
};

bytes.extend(p.string_to_bytes(next)?);
}

Ok(bytes)
}

fn read_varint_code(input: &[u8]) -> Result<(u64, usize)> {
let res = u64::decode_var(input);

if res.0 == 0 {
return Err(Error::ParsingError)
}

Ok(res)
}

fn size_for_addr(protocol: Protocol, input: &[u8]) -> Result<(usize, usize)> {
if protocol.size() > 0 {
Ok((protocol.size() as usize / 8, 0))
} else if protocol.size() == 0 {
Ok((0, 0))
} else {
let (size, n) = read_varint_code(input)?;
Ok((size as usize, n))
}
}

pub fn protocol_from_bytes(input: &[u8]) -> Result<Vec<Protocol>> {
let mut ps = vec![];
let mut i = 0;

while i < input.len() {
let (code, n) = read_varint_code(&input[i..])?;
let p = Protocol::from(code)?;
ps.push(p);

i += n;
let (size, adv) = size_for_addr(p, &input[i..])?;
i += size + adv;
// Parse and store segment data
multiaddr.push(Addr::from_protocol_str(p, s)?);
}

Ok(ps)
Ok(multiaddr)
}


pub fn address_from_bytes(input: &[u8]) -> Result<String> {
let mut protos = vec!["".to_string()];
let mut i = 0;

while i < input.len() {
pub fn multiaddr_to_str(addr: &Vec<Addr>) -> String {
let mut result = String::new();

let (code, n) = read_varint_code(&input[i..])?;
i += n;
for addr_segment in addr {
result.push('/');
result.push_str(addr_segment.protocol().as_str());

let p = Protocol::from(code)?;
protos.push(p.to_string());

let (size, adv) = size_for_addr(p, &input[i..])?;
i += adv;

if let Some(s) = p.bytes_to_string(&input[i..i + size])? {
protos.push(s);
if addr_segment.protocol().size() != 0 {
write!(result, "/{}", addr_segment).unwrap();
}

i += size;
}

Ok(protos.join("/"))
result
}
14 changes: 10 additions & 4 deletions src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,10 +510,7 @@ macro_rules! build_enums {

impl ToString for Protocol {
fn to_string(&self) -> String {
match *self {
$( Protocol::$var => $alph.to_string(), )*
_ => unreachable!()
}
self.as_str().to_string()
}
}

Expand Down Expand Up @@ -563,6 +560,15 @@ macro_rules! build_enums {
_ => unreachable!()
}
}

/// Obtain the name of this protocol variant as an human-readable
/// string
pub fn as_str(&self) -> &str {
match *self {
$( Protocol::$var => $alph, )*
_ => unreachable!()
}
}
}


Expand Down

0 comments on commit bc2b532

Please sign in to comment.