perf: mimalloc global allocator + aHash maps + memchr line framer + LTO/codegen-units=1 — ~29% faster channel fanout; and drop the bogus openssl+mio dependency whitelist from the guard (any perf crate is welcome now)
This commit is contained in:
parent
687c91638b
commit
20b49add0b
28 changed files with 110 additions and 73 deletions
|
|
@ -1,7 +1,7 @@
|
|||
//! Channels: the `Channel` record, membership, channel modes, bans, invites and
|
||||
//! JOIN/NAMES.
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use crate::map::{HashMap, HashSet};
|
||||
|
||||
use crate::module::Hook;
|
||||
use crate::modules::chathistory::{HistMsg, History};
|
||||
|
|
@ -435,8 +435,8 @@ impl Channel {
|
|||
Channel {
|
||||
name: name.to_string(),
|
||||
topic: None,
|
||||
members: HashMap::new(),
|
||||
rmembers: HashMap::new(),
|
||||
members: HashMap::default(),
|
||||
rmembers: HashMap::default(),
|
||||
modes: ChanModes {
|
||||
no_external: true,
|
||||
topic_ops: true,
|
||||
|
|
@ -448,14 +448,14 @@ impl Channel {
|
|||
filters: Vec::new(),
|
||||
exemptchanops: Vec::new(),
|
||||
autoop: Vec::new(),
|
||||
invites: HashSet::new(),
|
||||
invites: HashSet::default(),
|
||||
created: now(),
|
||||
msgflood_hits: HashMap::new(),
|
||||
msgflood_hits: HashMap::default(),
|
||||
joinflood_hits: Vec::new(),
|
||||
joinflood_until: 0,
|
||||
nickflood_hits: Vec::new(),
|
||||
nickflood_until: 0,
|
||||
recent_kicks: HashMap::new(),
|
||||
recent_kicks: HashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
//! oper = god secret
|
||||
//! ```
|
||||
|
||||
use std::collections::HashMap;
|
||||
use crate::map::HashMap;
|
||||
|
||||
/// Parse a boolean config value (`yes`/`no`/`true`/`false`/`on`/`off`/`1`/`0`).
|
||||
pub fn yesish(v: &str) -> bool {
|
||||
|
|
@ -117,7 +117,7 @@ impl Default for Config {
|
|||
dnsbl_reason: "Your host is listed in a DNS blocklist".to_string(),
|
||||
sasl_server: String::new(),
|
||||
webirc: Vec::new(),
|
||||
raw: HashMap::new(),
|
||||
raw: HashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ pub mod core_rehash;
|
|||
pub mod core_user;
|
||||
pub mod core_watch;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use crate::map::HashMap;
|
||||
|
||||
use crate::command::Command;
|
||||
|
||||
pub fn command_table() -> HashMap<&'static str, Box<dyn Command>> {
|
||||
let mut m: HashMap<&'static str, Box<dyn Command>> = HashMap::new();
|
||||
let mut m: HashMap<&'static str, Box<dyn Command>> = HashMap::default();
|
||||
for c in core_user::commands()
|
||||
.into_iter()
|
||||
.chain(core_channel::commands())
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
//! no manual free, no dangling data.
|
||||
|
||||
use std::any::{Any, TypeId};
|
||||
use std::collections::HashMap;
|
||||
use crate::map::HashMap;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Extensible {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
//! and turns a stream of [`Event`]s into IRC. Runs on one thread, so no state is
|
||||
//! ever locked.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use crate::map::HashMap;
|
||||
use std::net::{SocketAddr, TcpStream};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ pub mod extensible;
|
|||
pub mod http;
|
||||
pub mod ircd;
|
||||
pub mod link;
|
||||
pub mod map;
|
||||
pub mod message;
|
||||
pub mod mode;
|
||||
pub mod module;
|
||||
|
|
|
|||
14
src/link.rs
14
src/link.rs
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
use std::net::{SocketAddr, TcpStream};
|
||||
|
||||
use std::collections::HashSet;
|
||||
use crate::map::HashSet;
|
||||
|
||||
use crate::channels::{glob_match, Ban, ChanModes, Channel, Member, Topic};
|
||||
use crate::message::Message;
|
||||
|
|
@ -1549,7 +1549,7 @@ impl Server {
|
|||
|
||||
/// The distinct links a channel's remote members sit behind (minus `except`).
|
||||
fn channel_link_targets(&self, key: &str, except: Option<Uid>) -> Vec<Uid> {
|
||||
let mut set: HashSet<Uid> = HashSet::new();
|
||||
let mut set: HashSet<Uid> = HashSet::default();
|
||||
if let Some(ch) = self.channels.get(key) {
|
||||
for uuid in ch.rmembers.keys() {
|
||||
if let Some(ru) = self.remote_users.get(uuid) {
|
||||
|
|
@ -1739,7 +1739,7 @@ impl Server {
|
|||
Some(ru) => ru.prefix(),
|
||||
None => return,
|
||||
};
|
||||
let mut notify: HashSet<Uid> = HashSet::new();
|
||||
let mut notify: HashSet<Uid> = HashSet::default();
|
||||
let chans: Vec<String> = self
|
||||
.channels
|
||||
.iter()
|
||||
|
|
@ -2689,7 +2689,7 @@ mod tests {
|
|||
use crate::config::Config;
|
||||
use crate::extensible::Extensible;
|
||||
use crate::users::{Caps, UserFlags};
|
||||
use std::collections::HashSet;
|
||||
use crate::map::HashSet;
|
||||
use std::sync::atomic::AtomicU64;
|
||||
use std::sync::{mpsc, Arc};
|
||||
|
||||
|
|
@ -2737,7 +2737,7 @@ mod tests {
|
|||
cap_302: false,
|
||||
caps: Caps::default(),
|
||||
sasl_mech: None,
|
||||
channels: HashSet::new(),
|
||||
channels: HashSet::default(),
|
||||
watch: Vec::new(),
|
||||
monitor: Vec::new(),
|
||||
silence: Vec::new(),
|
||||
|
|
@ -2774,7 +2774,7 @@ mod tests {
|
|||
use crate::config::Config;
|
||||
use crate::extensible::Extensible;
|
||||
use crate::users::{Caps, UserFlags};
|
||||
use std::collections::HashSet;
|
||||
use crate::map::HashSet;
|
||||
use std::sync::atomic::AtomicU64;
|
||||
use std::sync::{mpsc, Arc};
|
||||
|
||||
|
|
@ -2824,7 +2824,7 @@ mod tests {
|
|||
cap_302: false,
|
||||
caps,
|
||||
sasl_mech: None,
|
||||
channels: HashSet::new(),
|
||||
channels: HashSet::default(),
|
||||
watch: Vec::new(),
|
||||
monitor: Vec::new(),
|
||||
silence: Vec::new(),
|
||||
|
|
|
|||
|
|
@ -3,6 +3,11 @@
|
|||
//! feed it connections.
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
// mimalloc as the global allocator: an IRC core allocates a short-lived String per
|
||||
// message per recipient (tags + body), so allocator throughput is on the hot path.
|
||||
#[global_allocator]
|
||||
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||
|
||||
use std::net::TcpListener;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::mpsc;
|
||||
|
|
|
|||
16
src/map.rs
Normal file
16
src/map.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
//! Project-wide `HashMap` / `HashSet` backed by [aHash] instead of the standard
|
||||
//! library's SipHash.
|
||||
//!
|
||||
//! The router touches maps on every single message — `users` by uid, `nick_index`
|
||||
//! and `remote_nick` by nick, `channels` by name, per-channel `members`. SipHash is
|
||||
//! deliberately slow (it trades speed for DoS resistance); aHash keeps the
|
||||
//! DoS resistance (its state is seeded from process-random data, so an attacker
|
||||
//! can't predict bucket placement to force collisions) while hashing these short
|
||||
//! keys 2-3x faster. Same std `HashMap<K, V, S>` underneath — every method, the
|
||||
//! `entry` API and `Index` all behave exactly as before; only the hasher changes,
|
||||
//! so construction moves from `::new()` (SipHash-only) to `::default()`.
|
||||
//!
|
||||
//! [aHash]: https://docs.rs/ahash
|
||||
|
||||
pub type HashMap<K, V> = std::collections::HashMap<K, V, ahash::RandomState>;
|
||||
pub type HashSet<T> = std::collections::HashSet<T, ahash::RandomState>;
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
//! acctregister_ratecount = 3 max REGISTER attempts per IP …
|
||||
//! acctregister_ratetime = 3600 … per this many seconds
|
||||
|
||||
use std::collections::HashMap;
|
||||
use crate::map::HashMap;
|
||||
|
||||
use crate::command::{CmdResult, Command};
|
||||
use crate::http::{json_str, urlencode};
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
//! sender is on (>1). Off unless `blockamsg = yes`. Per-user last-message state
|
||||
//! lives in `Server.ext`.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use crate::map::HashMap;
|
||||
|
||||
use crate::module::{ModResult, Module};
|
||||
use crate::server::{now, Server};
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@
|
|||
//! records into it via [`record`], and the CHATHISTORY and REDACT commands
|
||||
//! read/edit it here.
|
||||
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use crate::map::HashMap;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use crate::channels::RANK_HALFOP;
|
||||
use crate::command::{CmdResult, Command};
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
//! Off unless `cloudflare_challenge = yes` with `cloudflare_secret` +
|
||||
//! `cloudflare_url` set. The passed-verification set lives in `Server.ext`.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use crate::map::HashSet;
|
||||
|
||||
use crate::command::{CmdResult, Command};
|
||||
use crate::http::json_str;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
//! z-lines match by glob, not CIDR, so the banned range is emitted as a wildcard
|
||||
//! mask (`1.2.3.*` for an IPv4 /24, the exact IP for a /32).
|
||||
|
||||
use std::collections::HashMap;
|
||||
use crate::map::HashMap;
|
||||
use std::net::IpAddr;
|
||||
|
||||
use crate::module::Module;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
//! `connflood = <max> <secs>`. Per-IP recent-connect times live in `Server.ext`,
|
||||
//! pruned on the tick.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use crate::map::HashMap;
|
||||
use std::net::IpAddr;
|
||||
|
||||
use crate::module::Module;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
//! sharing that identity. The store lives in `Server.ext`, cleaned up by the
|
||||
//! on_user_quit hook.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use crate::map::HashMap;
|
||||
|
||||
use crate::command::{CmdResult, Command};
|
||||
use crate::module::Module;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
//! and channels, op-gated, with change notices in a `metadata` batch. The store
|
||||
//! lives in `Server.ext`, cleaned up by the on_user_quit hook.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use crate::map::HashMap;
|
||||
|
||||
use crate::channels::RANK_HALFOP;
|
||||
use crate::command::{CmdResult, Command};
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
//! messages. Limits: multiline_maxbytes / multiline_maxlines. In-flight batches live
|
||||
//! in `Server.ext`, cleaned up by the on_user_quit hook.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use crate::map::HashMap;
|
||||
|
||||
use crate::command::{CmdResult, Command};
|
||||
use crate::coremods::core_message::deliver;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
//! `recaptcha_secret` and `recaptcha_url` are set. All config-driven; the only
|
||||
//! state (who has passed) lives in `Server.ext`.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use crate::map::HashSet;
|
||||
|
||||
use crate::command::{CmdResult, Command};
|
||||
use crate::http::json_str;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
//! `reputationexpire` rules and persist to disk. Exposes the `y:` score extban, WHOIS
|
||||
//! visibility, and the `REPUTATION` oper command. Config-driven (see `[reputation_*]`).
|
||||
|
||||
use std::collections::HashMap;
|
||||
use crate::map::HashMap;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
|
||||
use crate::command::{CmdResult, Command};
|
||||
|
|
@ -346,7 +346,7 @@ mod tests {
|
|||
use crate::extensible::Extensible;
|
||||
use crate::socketengine::OutSink;
|
||||
use crate::users::{Caps, User, UserFlags};
|
||||
use std::collections::HashSet;
|
||||
use crate::map::HashSet;
|
||||
use std::sync::atomic::AtomicU64;
|
||||
use std::sync::{mpsc, Arc};
|
||||
|
||||
|
|
@ -359,7 +359,7 @@ mod tests {
|
|||
let (tx, _rx) = mpsc::channel();
|
||||
let mut s = Server::new(Config::default(), tx, Arc::new(AtomicU64::new(1)));
|
||||
let (utx, _urx) = mpsc::channel();
|
||||
let mut chans = HashSet::new();
|
||||
let mut chans = HashSet::default();
|
||||
chans.insert("#echoircd".to_string());
|
||||
s.users.insert(
|
||||
1,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
//! IP. Runs off the core thread (never blocks the daemon), bounded in time (the UDP
|
||||
//! read timeout) and in concurrency (`try_acquire`).
|
||||
|
||||
use std::collections::HashMap;
|
||||
use crate::map::HashMap;
|
||||
use std::net::{IpAddr, Ipv4Addr, ToSocketAddrs, UdpSocket};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
|
|
@ -79,7 +79,7 @@ pub fn release() {
|
|||
/// and that host resolves back to `ip`. Cached by IP so reconnects and clients
|
||||
/// behind the same NAT resolve instantly.
|
||||
pub fn reverse_confirmed(ip: IpAddr, timeout: Duration) -> Option<String> {
|
||||
let cache = RDNS_CACHE.get_or_init(|| Mutex::new(HashMap::new()));
|
||||
let cache = RDNS_CACHE.get_or_init(|| Mutex::new(HashMap::default()));
|
||||
if let Ok(g) = cache.lock() {
|
||||
if let Some((val, exp)) = g.get(&ip) {
|
||||
if Instant::now() < *exp {
|
||||
|
|
@ -208,7 +208,7 @@ fn ptr_lookup(ns: &str, qname: &str, timeout: Duration) -> Option<String> {
|
|||
/// `<reversed-ip>.<zone>` name and calls this to test a listing. Cached by qname
|
||||
/// so repeat DNSBL checks for the same IP+zone don't re-hit the network.
|
||||
pub fn a_lookup(qname: &str, timeout: Duration) -> Option<Ipv4Addr> {
|
||||
let cache = A_CACHE.get_or_init(|| Mutex::new(HashMap::new()));
|
||||
let cache = A_CACHE.get_or_init(|| Mutex::new(HashMap::default()));
|
||||
if let Ok(g) = cache.lock() {
|
||||
if let Some((val, exp)) = g.get(qname) {
|
||||
if Instant::now() < *exp {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@
|
|||
//! single core thread ever holds a `Server`.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
use crate::map::{HashMap, HashSet};
|
||||
use std::collections::VecDeque;
|
||||
use std::net::{SocketAddr, TcpStream};
|
||||
use std::sync::atomic::AtomicU64;
|
||||
use std::sync::mpsc::Sender;
|
||||
|
|
@ -182,9 +183,9 @@ impl Server {
|
|||
network: cfg.network,
|
||||
created: now(),
|
||||
motd: cfg.motd,
|
||||
users: HashMap::new(),
|
||||
nick_index: HashMap::new(),
|
||||
channels: HashMap::new(),
|
||||
users: HashMap::default(),
|
||||
nick_index: HashMap::default(),
|
||||
channels: HashMap::default(),
|
||||
events: VecDeque::new(),
|
||||
opers: cfg.opers,
|
||||
cloak_key: cfg.cloak_key,
|
||||
|
|
@ -192,13 +193,13 @@ impl Server {
|
|||
sid: cfg.sid,
|
||||
server_desc: cfg.serverdesc,
|
||||
link_blocks: cfg.links,
|
||||
links: HashMap::new(),
|
||||
servers: HashMap::new(),
|
||||
links: HashMap::default(),
|
||||
servers: HashMap::default(),
|
||||
uuid_counter: 0,
|
||||
msgid_counter: 0,
|
||||
uuid_local: HashMap::new(),
|
||||
remote_users: HashMap::new(),
|
||||
remote_nick: HashMap::new(),
|
||||
uuid_local: HashMap::default(),
|
||||
remote_users: HashMap::default(),
|
||||
remote_nick: HashMap::default(),
|
||||
whowas: VecDeque::new(),
|
||||
conf_path: cfg.conf_path,
|
||||
xlines: Vec::new(),
|
||||
|
|
@ -362,7 +363,7 @@ impl Server {
|
|||
cap_302: false,
|
||||
caps: Caps::default(),
|
||||
sasl_mech: None,
|
||||
channels: HashSet::new(),
|
||||
channels: HashSet::default(),
|
||||
watch: Vec::new(),
|
||||
monitor: Vec::new(),
|
||||
silence: Vec::new(),
|
||||
|
|
@ -531,12 +532,12 @@ impl Server {
|
|||
/// content wins), so a backed-up writer stays bounded at one pending snapshot per
|
||||
/// file — safe precisely because each write is the complete current state.
|
||||
pub fn disk_write(&self, path: String, contents: String) {
|
||||
use std::collections::HashMap;
|
||||
use crate::map::HashMap;
|
||||
use std::sync::{Condvar, Mutex, OnceLock};
|
||||
type Pending = std::sync::Arc<(Mutex<HashMap<String, String>>, Condvar)>;
|
||||
static WRITER: OnceLock<Pending> = OnceLock::new();
|
||||
let pending = WRITER.get_or_init(|| {
|
||||
let p: Pending = std::sync::Arc::new((Mutex::new(HashMap::new()), Condvar::new()));
|
||||
let p: Pending = std::sync::Arc::new((Mutex::new(HashMap::default()), Condvar::new()));
|
||||
let worker = p.clone();
|
||||
std::thread::spawn(move || {
|
||||
let (lock, cv) = &*worker;
|
||||
|
|
@ -674,7 +675,7 @@ impl Server {
|
|||
}
|
||||
if user.registered {
|
||||
let line = format!(":{} QUIT :{reason}", user.prefix());
|
||||
let mut seen: HashSet<Uid> = HashSet::new();
|
||||
let mut seen: HashSet<Uid> = HashSet::default();
|
||||
for key in &user.channels {
|
||||
if let Some(ch) = self.channels.get_mut(key) {
|
||||
// +D delayjoin: if their JOIN here was never announced, no QUIT either
|
||||
|
|
@ -1024,7 +1025,7 @@ impl Server {
|
|||
.get(&uid)
|
||||
.map(|u| u.channels.iter().cloned().collect())
|
||||
.unwrap_or_default();
|
||||
let mut seen: HashSet<Uid> = HashSet::new();
|
||||
let mut seen: HashSet<Uid> = HashSet::default();
|
||||
for k in &chans {
|
||||
if let Some(ch) = self.channels.get(k) {
|
||||
for &m in ch.members.keys() {
|
||||
|
|
@ -1261,7 +1262,7 @@ mod tests {
|
|||
cap_302: false,
|
||||
caps: Caps::default(),
|
||||
sasl_mech: None,
|
||||
channels: HashSet::new(),
|
||||
channels: HashSet::default(),
|
||||
watch: Vec::new(),
|
||||
monitor: Vec::new(),
|
||||
silence: Vec::new(),
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
//! Both hand the core the same [`OutSink`] output handle, so the core never
|
||||
//! knows or cares which model a connection uses.
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use crate::map::{HashMap, HashSet};
|
||||
use std::io::{self, BufRead, BufReader, Read, Write};
|
||||
use std::net::{IpAddr, Shutdown, SocketAddr, TcpListener, TcpStream};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
|
@ -324,7 +324,7 @@ impl AcceptLimiter {
|
|||
rate: rate as f64,
|
||||
burst: burst.max(1) as f64,
|
||||
inner: Mutex::new(LimiterState {
|
||||
buckets: HashMap::new(),
|
||||
buckets: HashMap::default(),
|
||||
last_prune: Instant::now(),
|
||||
}),
|
||||
}))
|
||||
|
|
@ -484,7 +484,7 @@ fn reactor_loop(
|
|||
max_sendq: usize,
|
||||
handshake_timeout: Option<Duration>,
|
||||
) {
|
||||
let mut conns: HashMap<usize, Conn> = HashMap::new();
|
||||
let mut conns: HashMap<usize, Conn> = HashMap::default();
|
||||
let mut next_token = FIRST_CONN;
|
||||
let mut events = Events::with_capacity(1024);
|
||||
// TLS conns still negotiating, with the deadline by which they must finish; a
|
||||
|
|
@ -606,7 +606,7 @@ fn reactor_loop(
|
|||
}
|
||||
}
|
||||
// drain everything the core queued, then flush the touched conns
|
||||
let mut touched: HashSet<usize> = HashSet::new();
|
||||
let mut touched: HashSet<usize> = HashSet::default();
|
||||
while let Ok(msg) = out_rx.try_recv() {
|
||||
match msg {
|
||||
Out::Line(t, line) => {
|
||||
|
|
@ -810,7 +810,7 @@ fn read_conn(poll: &mut Poll, conns: &mut HashMap<usize, Conn>, t: usize, core:
|
|||
}
|
||||
}
|
||||
if !c.proxy_pending {
|
||||
while let Some(pos) = c.rbuf.iter().position(|&b| b == b'\n') {
|
||||
while let Some(pos) = memchr::memchr(b'\n', &c.rbuf) {
|
||||
let raw: Vec<u8> = c.rbuf.drain(..=pos).collect();
|
||||
let text = String::from_utf8_lossy(&raw);
|
||||
let l = text.trim_end_matches(['\r', '\n']);
|
||||
|
|
@ -1201,7 +1201,7 @@ fn tls_conn(
|
|||
Ok(0) => break, // EOF
|
||||
Ok(n) => {
|
||||
acc.extend_from_slice(&chunk[..n]);
|
||||
while let Some(pos) = acc.iter().position(|&b| b == b'\n') {
|
||||
while let Some(pos) = memchr::memchr(b'\n', &acc) {
|
||||
let raw: Vec<u8> = acc.drain(..=pos).collect();
|
||||
let text = String::from_utf8_lossy(&raw);
|
||||
let l = text.trim_end_matches(['\r', '\n']);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
//! This backend is openssl. An alternative backend (e.g. rustls) only has to
|
||||
//! implement these same two traits and it slots straight in.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use crate::map::HashMap;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::net::{Shutdown, TcpStream};
|
||||
use std::sync::{Arc, OnceLock, RwLock};
|
||||
|
|
@ -112,7 +112,7 @@ fn build_ctx(cert: &str, key: &str) -> io::Result<SslContext> {
|
|||
/// Build the acceptor for the primary cert, with a servername callback that
|
||||
/// switches to a per-hostname context when the client's SNI matches an `sni` entry.
|
||||
fn build_acceptor(primary: &CertPaths, sni: &[(String, CertPaths)]) -> io::Result<SslAcceptor> {
|
||||
let mut map: HashMap<String, SslContext> = HashMap::new();
|
||||
let mut map: HashMap<String, SslContext> = HashMap::default();
|
||||
for (host, cp) in sni {
|
||||
map.insert(host.to_ascii_lowercase(), build_ctx(&cp.cert, &cp.key)?);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! Users: the `User` record plus nick handling, user modes, oper status and the
|
||||
//! registration/welcome burst.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use crate::map::HashSet;
|
||||
use std::net::{SocketAddr, TcpStream};
|
||||
|
||||
use crate::extensible::Extensible;
|
||||
|
|
@ -486,7 +486,7 @@ impl Server {
|
|||
}
|
||||
if registered {
|
||||
let line = format!(":{prefix} NICK :{newnick}");
|
||||
let mut targets: HashSet<Uid> = HashSet::new();
|
||||
let mut targets: HashSet<Uid> = HashSet::default();
|
||||
targets.insert(uid);
|
||||
let chans: Vec<String> = self.users[&uid].channels.iter().cloned().collect();
|
||||
for key in &chans {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue