state small rework

clone-session-service-objects
Avril 4 years ago
parent 5b4fa57a9c
commit 3bd8ff68be
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -413,10 +413,10 @@ lazy_static! {
/// A wrapper for hashing with a specific salt. /// A wrapper for hashing with a specific salt.
#[derive(Debug, Hash)] #[derive(Debug, Hash)]
pub struct Salted<'a, T: std::hash::Hash>(&'a T, &'a [u8]); pub struct Salted<'a, T: ?Sized + std::hash::Hash>(&'a T, &'a [u8]);
impl<'a, T> Salted<'a, T> impl<'a, T> Salted<'a, T>
where T: std::hash::Hash where T: std::hash::Hash + ?Sized
{ {
/// Create a new wrapper. /// Create a new wrapper.
pub fn new(val: &'a T, salt: &'a [u8]) -> Self pub fn new(val: &'a T, salt: &'a [u8]) -> Self
@ -426,10 +426,10 @@ where T: std::hash::Hash
} }
/// A wrapper for hashing with the global salt. /// A wrapper for hashing with the global salt.
#[derive(Debug, Hash)] #[derive(Debug, Hash)]
pub struct GloballySalted<'a, T: std::hash::Hash>(&'a T, &'static [u8]); pub struct GloballySalted<'a, T: ?Sized + std::hash::Hash>(&'a T, &'static [u8]);
impl<'a, T> GloballySalted<'a, T> impl<'a, T> GloballySalted<'a, T>
where T: std::hash::Hash where T: std::hash::Hash + ?Sized
{ {
/// Create a new wrapper. /// Create a new wrapper.
pub fn new(val: &'a T) -> Self pub fn new(val: &'a T) -> Self
@ -438,6 +438,24 @@ where T: std::hash::Hash
} }
} }
pub trait HashWithSaltExt: std::hash::Hash
{
/// Create a hash wrapper around this instance that hashes with a specific salt.
fn hash_with_salt<'a>(&'a self, salt: &'a [u8]) -> Salted<'a, Self>;
/// Create a hash wrapper around this instance that hashes with the global salt.
#[inline] fn hash_with_global_salt(&self) -> GloballySalted<'_, Self>
{
GloballySalted::new(self)
}
}
impl<T> HashWithSaltExt for T where T: ?Sized + std::hash::Hash
{
#[inline] fn hash_with_salt<'a>(&'a self, salt: &'a [u8]) -> Salted<'a, Self> {
Salted::new(self, salt)
}
}
mod sha256_hasher { mod sha256_hasher {
use std::mem::size_of; use std::mem::size_of;
use sha2::{ use sha2::{
@ -489,3 +507,24 @@ pub use sha256_hasher::Sha256HashExt;
/// Value may hold one in place or allocate on the heap to hold many. /// Value may hold one in place or allocate on the heap to hold many.
pub type MaybeVec<T> = smallvec::SmallVec<[T; 1]>; pub type MaybeVec<T> = smallvec::SmallVec<[T; 1]>;
#[macro_export] macro_rules! impl_deref {
(for $($(frag:tt)*;)? $name:ident impl $to:ident as $expr:expr $(; mut $mut_expr:expr)?) => {
impl $($($frag)*)? ::std::ops::Deref for $name
{
type Target = $to;
fn deref(&self) -> &Self::Target
{
$expr
}
}
$(
impl $($($frag)*)? ::std::ops::DerefMut for $name
{
fn deref_mut(&mut self) -> &mut <Self as ::std::ops::Deref>::Target
{
$mut_expr
}
})?
};
}

@ -5,24 +5,24 @@ use std::{error,fmt};
/// An image of the entire post container /// An image of the entire post container
#[derive(Debug, Default, Serialize, Deserialize)] #[derive(Debug, Default, Serialize, Deserialize)]
pub struct Imouto pub struct Freeze
{ {
posts: Vec<post::Post>, posts: Vec<post::Post>,
} }
impl From<Imouto> for Oneesan impl From<Freeze> for Imouto
{ {
#[inline] fn from(from: Imouto) -> Self #[inline] fn from(from: Freeze) -> Self
{ {
Self::from_freeze(from) Self::from_freeze(from)
} }
} }
impl TryFrom<Oneesan> for Imouto impl TryFrom<Imouto> for Freeze
{ {
type Error = FreezeError; type Error = FreezeError;
#[inline] fn try_from(from: Oneesan) -> Result<Self, Self::Error> #[inline] fn try_from(from: Imouto) -> Result<Self, Self::Error>
{ {
from.try_into_freeze() from.try_into_freeze()
} }
@ -65,16 +65,16 @@ impl fmt::Display for FreezeError
} }
} }
impl Oneesan impl Imouto
{ {
/// Create a serialisable image of this store by cloning each post into it. /// Create a serialisable image of this store by cloning each post into it.
pub async fn freeze(&self) -> Imouto pub async fn freeze(&self) -> Freeze
{ {
let read = self.posts.read().await; let read = &self.all;
let mut sis = Imouto{ let mut sis = Freeze{
posts: Vec::with_capacity(read.0.len()), posts: Vec::with_capacity(read.len()),
}; };
for (_, post) in read.0.iter() for (_, post) in read.iter()
{ {
sis.posts.push(post.read().await.clone()); sis.posts.push(post.read().await.clone());
} }
@ -86,13 +86,13 @@ impl Oneesan
/// ///
/// # Fails /// # Fails
/// If references to any posts are still held elsewhere. /// If references to any posts are still held elsewhere.
pub fn try_into_freeze(self) -> Result<Imouto, FreezeError> pub fn try_into_freeze(self) -> Result<Freeze, FreezeError>
{ {
let read = self.posts.into_inner(); let read = self.all;
let mut sis = Imouto{ let mut sis = Freeze{
posts: Vec::with_capacity(read.0.len()), posts: Vec::with_capacity(read.len()),
}; };
for post in read.0.into_iter() for post in read.into_iter()
{ {
sis.posts.push(match Arc::try_unwrap(post) { sis.posts.push(match Arc::try_unwrap(post) {
Ok(val) => val.into_inner(), Ok(val) => val.into_inner(),
@ -108,13 +108,13 @@ impl Oneesan
/// ///
/// # Panics /// # Panics
/// If references to any posts are still held elsewhere. /// If references to any posts are still held elsewhere.
pub fn into_freeze(self) -> Imouto pub fn into_freeze(self) -> Freeze
{ {
self.try_into_freeze().expect("Failed to consume into freeze") self.try_into_freeze().expect("Failed to consume into freeze")
} }
/// Create a new store from a serialisable image of one by cloning each post in it /// Create a new store from a serialisable image of one by cloning each post in it
pub fn unfreeze(freeze: &Imouto) -> Self pub fn unfreeze(freeze: &Freeze) -> Self
{ {
let mut posts = Arena::new(); let mut posts = Arena::new();
let mut user_map = HashMap::new(); let mut user_map = HashMap::new();
@ -128,12 +128,13 @@ impl Oneesan
} }
Self { Self {
posts: RwLock::new((posts, user_map)) all: posts,
user_map,
} }
} }
/// Create a new store by consuming serialisable image of one by cloning each post in it /// Create a new store by consuming serialisable image of one by cloning each post in it
pub fn from_freeze(freeze: Imouto) -> Self pub fn from_freeze(freeze: Freeze) -> Self
{ {
let mut posts = Arena::new(); let mut posts = Arena::new();
let mut user_map = HashMap::new(); let mut user_map = HashMap::new();
@ -147,7 +148,8 @@ impl Oneesan
} }
Self { Self {
posts: RwLock::new((posts, user_map)) all: posts,
user_map,
} }
} }
} }

@ -5,6 +5,7 @@ use generational_arena::{
}; };
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::RwLock; use tokio::sync::RwLock;
use std::ops::{Deref, DerefMut};
pub mod session; pub mod session;
pub mod user; pub mod user;
@ -13,10 +14,55 @@ pub mod body;
mod freeze; pub use freeze::*; mod freeze; pub use freeze::*;
/// Entire post container /// Entire post state container
pub struct Oneesan #[derive(Debug)]
pub struct Imouto
{ {
posts: RwLock<(Arena<Arc<RwLock<post::Post>>> // All posts all: Arena<Arc<RwLock<post::Post>>>,
, HashMap<user::UserID, MaybeVec<ArenaIndex>> // Post lookup by user ID user_map: HashMap<user::UserID, MaybeVec<ArenaIndex>>,
)>, }
impl Imouto
{
/// Create a new empty container
pub fn new() -> Self
{
Self {
all: Arena::new(),
user_map: HashMap::new(),
}
}
}
#[derive(Debug)]
/// Entire program state
struct Oneesan
{
posts: RwLock<Imouto>,
}
/// Shares whole program state
#[derive(Debug, Clone)]
pub struct State(Arc<Oneesan>);
impl State
{
/// Create a new empty state.
pub fn new() -> Self
{
Self(Arc::new(Oneesan {
posts: RwLock::new(Imouto::new()),
}))
}
/// Get a reference to the post state container
pub async fn imouto(&self) -> tokio::sync::RwLockReadGuard<'_, Imouto>
{
self.0.posts.read().await
}
/// Get a mutable reference to the post state container
pub async fn imouto_mut(&self) -> tokio::sync::RwLockWriteGuard<'_, Imouto>
{
self.0.posts.write().await
}
} }

@ -3,6 +3,15 @@ use super::*;
id_type!(SessionID; "A unique session ID, not bound to a user."); id_type!(SessionID; "A unique session ID, not bound to a user.");
impl SessionID
{
/// Generate a random session ID.
#[inline] fn generate() -> Self
{
Self::id_new()
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct Session pub struct Session
{ {
@ -12,6 +21,14 @@ pub struct Session
impl Session impl Session
{ {
/// Create a new session object
pub fn create(user: user::User) -> Self
{
Self {
user,
id: SessionID::generate(),
}
}
/// The randomly generated ID of this session, irrespective of the user of this session. /// The randomly generated ID of this session, irrespective of the user of this session.
#[inline] pub fn session_id(&self) -> &SessionID #[inline] pub fn session_id(&self) -> &SessionID
{ {

@ -45,7 +45,7 @@ impl UserID
} }
/// A user not bound to a session. /// A user not bound to a session.
#[derive(Debug, PartialEq, Eq, Hash, Ord, PartialOrd)] #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct User pub struct User
{ {
addr: SocketAddr, addr: SocketAddr,
@ -59,3 +59,48 @@ impl User
UserID(self.addr, session.session_id().clone()) UserID(self.addr, session.session_id().clone())
} }
} }
#[cfg(test)]
mod tests
{
use super::*;
use tokio::sync::mpsc;
use tokio::time;
use std::net::SocketAddrV4;
use std::net::Ipv4Addr;
#[tokio::test]
async fn counter_tokens()
{
let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 80));
let usr = User{addr};
let ses = session::Session::create(usr);
let id = ses.user_id();
let (mut tx, mut rx) = mpsc::channel(5);
let task = tokio::spawn(async move {
let id = ses.user_id();
while let Some(token) = rx.recv().await {
if !id.validate_token(token) {
panic!("Failed to validate token {:x} for id {:?}", token, id);
} else {
eprintln!("Token {:x} valid for id {:?}", token, id);
}
}
});
for x in 1..=10
{
if x % 2 == 0 {
time::delay_for(time::Duration::from_millis(10 * x)).await;
}
if tx.send(id.generate_token()).await.is_err() {
eprintln!("Failed to send to task");
break;
}
}
drop(tx);
task.await.expect("Background validate task failed");
}
}

Loading…
Cancel
Save