1use rand::distributions::{Distribution, Uniform};
2use std::collections::{BTreeMap, BTreeSet};
3use std::ops::RangeInclusive;
4
5use crate::{Error, Protocol, Result};
6
7#[derive(Default)]
8pub struct Allocator {
9 taken: BTreeMap<Protocol, BTreeSet<u16>>,
10}
11
12impl Allocator {
13 const RANGE: RangeInclusive<u16> = 1000..=65535;
14
15 pub fn next(&mut self, protocol: Protocol) -> Result<u16> {
16 let mut rng = rand::thread_rng();
17 let mut port = Uniform::from(Self::RANGE).sample(&mut rng);
18 let taken = self.taken.entry(protocol).or_insert_with(Default::default);
19
20 let range_start = *Self::RANGE.start();
21 let mut num = Self::RANGE.len() as i32;
22
23 while num > 0 {
24 if !taken.contains(&port) {
25 taken.insert(port);
26 return Ok(port);
27 }
28 port = range_start.max(port.overflowing_add(1).0);
29 num -= 1;
30 }
31
32 Err(Error::Other("no ports available".into()))
33 }
34
35 #[allow(unused)]
36 pub fn reserve(&mut self, protocol: Protocol, port: u16) -> Result<()> {
37 let entry = self.taken.entry(protocol).or_insert_with(Default::default);
38 if entry.contains(&port) {
39 return Err(Error::Other(format!("port {} is unavailable", port)));
40 }
41 entry.insert(port);
42 Ok(())
43 }
44
45 pub fn free(&mut self, protocol: Protocol, port: u16) {
46 self.taken
47 .entry(protocol)
48 .or_insert_with(Default::default)
49 .remove(&port);
50 }
51}