Commit b835a485 authored by Nick Mathewson's avatar Nick Mathewson 🦀
Browse files

Merge branch 'smaller_routerstatus' into 'main'

tor-netdoc: Save allocation space in GenericRouterstatus

Closes #387

See merge request tpo/core/arti!400
parents d3204139 718a1ee3
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1326,6 +1326,7 @@ impl<RS: RouterStatus + ParseRouterStatus> Consensus<RS> {
            }
            relays.push(routerstatus);
        }
        relays.shrink_to_fit();

        let footer = Self::take_footer(r)?;

+49 −14
Original line number Diff line number Diff line
@@ -12,7 +12,10 @@ mod ns;
use super::{NetstatusKwd, RelayFlags, RelayWeight};
use crate::parse::parser::Section;
use crate::types::misc::*;
use crate::{ParseErrorKind as EK, Result};
use crate::types::version::TorVersion;
use crate::util::intern::InternCache;
use crate::{Error, ParseErrorKind as EK, Result};
use std::sync::Arc;
use std::{net, time};

use tor_llcrypto::pk::rsa::RsaIdentity;
@@ -34,15 +37,12 @@ struct GenericRouterStatus<D> {
    identity: RsaIdentity,
    /// A list of address:port values where this relay can be reached.
    addrs: Vec<net::SocketAddr>,
    /// Declared OR port for this relay.
    #[allow(dead_code)] // This value is never used; we look at addrs instead.
    or_port: u16,
    /// Digest of the document for this relay.
    doc_digest: D,
    /// Flags applied by the authorities to this relay.
    flags: RelayFlags,
    /// Version of the software that this relay is running.
    version: Option<String>,
    version: Option<Version>,
    /// List of subprotocol versions supported by this relay.
    protos: Protocols,
    /// Information about how to weight this relay when choosing a
@@ -50,6 +50,41 @@ struct GenericRouterStatus<D> {
    weight: RelayWeight,
}

/// A version as presented in a router status.
///
/// This can either be a parsed Tor version, or an unparsed string.
//
// TODO: This might want to merge, at some point, with routerdesc::RelayPlatform.
#[derive(Clone, Debug, Eq, PartialEq, Hash, derive_more::Display)]
#[non_exhaustive]
pub enum Version {
    /// A Tor version
    Tor(TorVersion),
    /// A string we couldn't parse.
    Other(Arc<str>),
}

/// A cache of unparsable version strings.
///
/// We use this because we expect there not to be very many distinct versions of
/// relay software in existence.
static OTHER_VERSION_CACHE: InternCache<str> = InternCache::new();

impl std::str::FromStr for Version {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self> {
        let mut elts = s.splitn(3, ' ');
        if elts.next() == Some("Tor") {
            if let Some(Ok(v)) = elts.next().map(str::parse) {
                return Ok(Version::Tor(v));
            }
        }

        Ok(Version::Other(OTHER_VERSION_CACHE.intern_ref(s)))
    }
}

/// Implement a set of accessor functions on a given routerstatus type.
// TODO: These methods should probably become, in whole or in part,
// methods on the RouterStatus trait.
@@ -81,8 +116,8 @@ macro_rules! implement_accessors {
                &self.rs.flags
            }
            /// Return the version of this routerstatus.
            pub fn version(&self) -> &Option<String> {
                &self.rs.version
            pub fn version(&self) -> Option<&crate::doc::netstatus::rs::Version> {
                self.rs.version.as_ref()
            }
            /// Return true if the ed25519 identity on this relay reflects a
            /// true consensus among the authorities.
@@ -158,12 +193,13 @@ where
        let or_port = r_item.required_arg(5 + skip)?.parse::<u16>()?;
        let _ = r_item.required_arg(6 + skip)?.parse::<u16>()?;

        let mut addrs: Vec<net::SocketAddr> = vec![net::SocketAddr::V4(net::SocketAddrV4::new(
        // main address and A lines.
        let a_items = sec.slice(RS_A);
        let mut addrs = Vec::with_capacity(1 + a_items.len());
        addrs.push(net::SocketAddr::V4(net::SocketAddrV4::new(
            ipv4addr, or_port,
        ))];

        // A lines
        for a_item in sec.slice(RS_A) {
        )));
        for a_item in a_items {
            addrs.push(a_item.required_arg(0)?.parse::<net::SocketAddr>()?);
        }

@@ -171,7 +207,7 @@ where
        let flags = RelayFlags::from_item(sec.required(RS_S)?)?;

        // V line
        let version = sec.maybe(RS_V).args_as_str().map(str::to_string);
        let version = sec.maybe(RS_V).args_as_str().map(str::parse).transpose()?;

        // PR line
        let protos = {
@@ -205,7 +241,6 @@ where
            nickname,
            identity,
            addrs,
            or_port,
            doc_digest,
            flags,
            version,
+2 −3
Original line number Diff line number Diff line
@@ -124,7 +124,6 @@ impl<D: Clone> RouterStatusBuilder<D> {
        if self.addrs.is_empty() {
            return Err(Error::CannotBuild("No addresses"));
        }
        let or_port = self.addrs[0].port();
        let doc_digest = self
            .doc_digest
            .as_ref()
@@ -136,14 +135,14 @@ impl<D: Clone> RouterStatusBuilder<D> {
            .ok_or(Error::CannotBuild("Missing protocols"))?
            .clone();
        let weight = self.weight.unwrap_or(RelayWeight::Unmeasured(0));
        let version = self.version.as_deref().map(str::parse).transpose()?;

        Ok(GenericRouterStatus {
            nickname,
            identity,
            addrs: self.addrs.clone(),
            or_port,
            doc_digest,
            version: self.version.clone(),
            version,
            protos,
            flags: self.flags,
            weight,
+2 −2
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@ use crate::{ParseErrorKind as EK, Pos};
/// a release candidate (rc), or stable.
///
/// We accept unrecognized tags, and store them as "Other".
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(u8)]
enum TorVerStatus {
    /// An unknown release status
@@ -80,7 +80,7 @@ impl TorVerStatus {
}

/// A parsed Tor version number.
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct TorVersion {
    /// Major version number.  This has been zero since Tor was created.
    major: u8,
+27 −3
Original line number Diff line number Diff line
@@ -15,12 +15,12 @@ use weak_table::WeakHashSet;
/// It's "weak" because it only holds weak references to its objects;
/// once every strong reference is gone, the object is unallocated.
/// Later, the hash entry is (lazily) removed.
pub(crate) struct InternCache<T> {
pub(crate) struct InternCache<T: ?Sized> {
    /// Underlying hashset for interned objects
    cache: OnceCell<Mutex<WeakHashSet<Weak<T>>>>,
}

impl<T> InternCache<T> {
impl<T: ?Sized> InternCache<T> {
    /// Create a new, empty, InternCache.
    pub(crate) const fn new() -> Self {
        InternCache {
@@ -29,13 +29,15 @@ impl<T> InternCache<T> {
    }
}

impl<T: Eq + Hash> InternCache<T> {
impl<T: Eq + Hash + ?Sized> InternCache<T> {
    /// Helper: initialize the cache if needed, then lock it.
    fn cache(&self) -> MutexGuard<'_, WeakHashSet<Weak<T>>> {
        let cache = self.cache.get_or_init(|| Mutex::new(WeakHashSet::new()));
        cache.lock().expect("Poisoned lock lock for cache")
    }
}

impl<T: Eq + Hash> InternCache<T> {
    /// Intern a given value into this cache.
    ///
    /// If `value` is already stored in this cache, we return a
@@ -52,3 +54,25 @@ impl<T: Eq + Hash> InternCache<T> {
        }
    }
}

impl<T: Hash + Eq + ?Sized> InternCache<T> {
    /// Intern an object by reference.
    ///
    /// Works with unsized types, but requires that the reference implements
    /// `Into<Arc<T>>`.
    pub(crate) fn intern_ref<'a, V>(&self, value: &'a V) -> Arc<T>
    where
        V: Hash + Eq + ?Sized,
        &'a V: Into<Arc<T>>,
        T: std::borrow::Borrow<V>,
    {
        let mut cache = self.cache();
        if let Some(arc) = cache.get(value) {
            arc
        } else {
            let arc = value.into();
            cache.insert(Arc::clone(&arc));
            arc
        }
    }
}
Loading