Rust backend (#185)

This commit is contained in:
Maciej Hirsz
2019-11-07 10:52:38 +01:00
committed by GitHub
parent 31784131d6
commit a3b6f6a5a1
26 changed files with 3194 additions and 808 deletions
+78
View File
@@ -0,0 +1,78 @@
pub type Id = usize;
pub struct DenseMap<T> {
/// List of retired indexes that can be re-used
retired: Vec<Id>,
/// All items
items: Vec<Option<T>>,
}
impl<T> DenseMap<T> {
pub fn new() -> Self {
DenseMap {
retired: Vec::new(),
items: Vec::new(),
}
}
pub fn add(&mut self, item: T) -> Id {
self.add_with(|_| item)
}
pub fn add_with<F>(&mut self, f: F) -> Id
where
F: FnOnce(Id) -> T,
{
match self.retired.pop() {
Some(id) => {
self.items[id] = Some(f(id));
id
},
None => {
let id = self.items.len();
self.items.push(Some(f(id)));
id
},
}
}
pub fn get(&self, id: Id) -> Option<&T> {
self.items.get(id).and_then(|item| item.as_ref())
}
pub fn get_mut(&mut self, id: Id) -> Option<&mut T> {
self.items.get_mut(id).and_then(|item| item.as_mut())
}
pub fn remove(&mut self, id: Id) -> Option<T> {
let old = self.items.get_mut(id).and_then(|item| item.take());
if old.is_some() {
// something was actually removed, so lets add the id to
// the list of retired ids!
self.retired.push(id);
}
old
}
pub fn iter(&self) -> impl Iterator<Item = (Id, &T)> + '_ {
self.items.iter().enumerate().filter_map(|(id, item)| {
Some((id, item.as_ref()?))
})
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (Id, &mut T)> + '_ {
self.items.iter_mut().enumerate().filter_map(|(id, item)| {
Some((id, item.as_mut()?))
})
}
pub fn len(&self) -> usize {
self.items.len() - self.retired.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
+89
View File
@@ -0,0 +1,89 @@
use std::net::Ipv4Addr;
use std::sync::Arc;
use actix::prelude::*;
use rustc_hash::FxHashMap;
use parking_lot::RwLock;
use crate::chain::{Chain, LocateNode};
use crate::types::{NodeId, NodeLocation};
#[derive(Clone)]
pub struct Locator {
client: reqwest::Client,
cache: Arc<RwLock<FxHashMap<Ipv4Addr, Option<Arc<NodeLocation>>>>>,
}
pub struct LocatorFactory {
cache: Arc<RwLock<FxHashMap<Ipv4Addr, Option<Arc<NodeLocation>>>>>,
}
impl LocatorFactory {
pub fn new() -> Self {
let mut cache = FxHashMap::default();
// Default entry for localhost
cache.insert(
Ipv4Addr::new(127, 0, 0, 1),
Some(Arc::new(NodeLocation { latitude: 52.5166667, longitude: 13.4, city: "Berlin".into() })),
);
LocatorFactory {
cache: Arc::new(RwLock::new(cache)),
}
}
pub fn create(&self) -> Locator {
Locator {
client: reqwest::Client::new(),
cache: self.cache.clone(),
}
}
}
impl Actor for Locator {
type Context = SyncContext<Self>;
}
#[derive(Message)]
pub struct LocateRequest {
pub ip: Ipv4Addr,
pub nid: NodeId,
pub chain: Addr<Chain>,
}
impl Handler<LocateRequest> for Locator {
type Result = ();
fn handle(&mut self, msg: LocateRequest, _: &mut Self::Context) {
let LocateRequest { ip, nid, chain } = msg;
if let Some(item) = self.cache.read().get(&ip) {
if let Some(location) = item {
return chain.do_send(LocateNode { nid, location: location.clone() });
}
return
}
let ip_req = format!("https://ipapi.co/{}/json", ip);
let mut response = match self.client.post(&ip_req).send() {
Ok(response) => response,
Err(err) => return debug!("POST error for ip location: {:?}", err),
};
let location = match response.json::<NodeLocation>() {
Ok(location) => Some(Arc::new(location)),
Err(err) => {
debug!("JSON error for ip location: {:?}", err);
None
}
};
self.cache.write().insert(ip, location.clone());
if let Some(location) = location {
chain.do_send(LocateNode { nid, location });
}
}
}
+68
View File
@@ -0,0 +1,68 @@
use num_traits::{Float, Zero};
use std::ops::AddAssign;
pub struct MeanList<T> where T: Float + AddAssign + Zero + From<u8> {
period_sum: T,
period_count: u8,
mean_index: u8,
means: [T; 20],
ticks_per_mean: u8,
}
impl<T> MeanList<T> where T: Float + AddAssign + Zero + From<u8> {
pub fn new() -> MeanList<T> {
MeanList {
period_sum: T::zero(),
period_count: 0,
mean_index: 0,
means: [T::zero(); 20],
ticks_per_mean: 1,
}
}
pub fn slice(&self) -> &[T] {
&self.means[..usize::from(self.mean_index)]
}
pub fn push(&mut self, val: T) -> bool {
if self.mean_index == 20 && self.ticks_per_mean < 32 {
self.squash_means();
}
self.period_sum += val;
self.period_count += 1;
if self.period_count == self.ticks_per_mean {
self.push_mean();
true
} else {
false
}
}
fn push_mean(&mut self) {
let mean = self.period_sum / std::convert::From::from(self.period_count);
if self.mean_index == 20 && self.ticks_per_mean == 32 {
self.means.rotate_left(1);
self.means[19] = mean;
} else {
self.means[usize::from(self.mean_index)] = mean;
self.mean_index += 1;
}
self.period_sum = T::zero();
self.period_count = 0;
}
fn squash_means(&mut self) {
self.ticks_per_mean *= 2;
self.mean_index = 10;
for i in 0..10 {
let i2 = i * 2;
self.means[i] = (self.means[i2] + self.means[i2 + 1]) / std::convert::From::from(2)
}
}
}
+43
View File
@@ -0,0 +1,43 @@
use num_traits::{Zero, NumOps, Bounded};
use std::iter::Sum;
use std::convert::TryFrom;
/// Keep track of last N numbers pushed onto internal stack.
/// Provides means to get an average of said numbers.
pub struct NumStats<T> {
stack: Box<[T]>,
index: usize,
sum: T,
}
impl<T: NumOps + Zero + Bounded + Copy + Sum + TryFrom<usize>> NumStats<T> {
pub fn new(size: usize) -> Self {
NumStats {
stack: vec![T::zero(); size].into_boxed_slice(),
index: 0,
sum: T::zero(),
}
}
pub fn push(&mut self, val: T) {
let slot = &mut self.stack[self.index % self.stack.len()];
self.sum = self.sum - *slot + val;
*slot = val;
self.index += 1;
}
pub fn average(&self) -> T {
let cap = std::cmp::min(self.index, self.stack.len());
let cap = T::try_from(cap).unwrap_or_else(|_| T::max_value());
self.sum / cap
}
pub fn reset(&mut self) {
self.index = 0;
self.sum = T::zero();
}
}