mirror of
https://github.com/italicsjenga/valence.git
synced 2024-12-24 06:51:30 +11:00
Expand on the "encoded buf" idea in valence_protocol (#155)
Adds a `Cached<T>` type and renames some things.
This commit is contained in:
parent
b8e41cbb9a
commit
4b155f4c9b
143
valence_protocol/src/cache.rs
Normal file
143
valence_protocol/src/cache.rs
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
use std::io::Write;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
|
|
||||||
|
use crate::{Decode, Encode, Result};
|
||||||
|
|
||||||
|
/// Contains both a value of `T` and an [`EncodedBuf<T>`] to ensure the
|
||||||
|
/// buffer is updated when `T` is modified[^note].
|
||||||
|
///
|
||||||
|
/// The `Encode` implementation for `Cached<T>` encodes only the contained
|
||||||
|
/// `EncodedBuf`.
|
||||||
|
///
|
||||||
|
/// Use this type when you want `Encode` to be cached but you also need to read
|
||||||
|
/// from the value of `T`. If the value of `T` is write-only, consider using
|
||||||
|
/// `EncodedBuf` instead.
|
||||||
|
///
|
||||||
|
/// [`EncodedBuf<T>`]: EncodedBuf
|
||||||
|
/// [^note]: Assuming `T` does not use internal mutability.
|
||||||
|
pub struct Cached<T> {
|
||||||
|
val: T,
|
||||||
|
buf: EncodedBuf<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Encode> Cached<T> {
|
||||||
|
pub fn new(val: T) -> Self {
|
||||||
|
let buf = EncodedBuf::new(&val);
|
||||||
|
|
||||||
|
Self { val, buf }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self) -> &T {
|
||||||
|
&self.val
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn buf(&self) -> &EncodedBuf<T> {
|
||||||
|
&self.buf
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides a mutable reference to the contained `T` for modification. The
|
||||||
|
/// buffer is re-encoded when the closure returns.
|
||||||
|
pub fn modify<U>(&mut self, f: impl FnOnce(&mut T) -> U) -> U {
|
||||||
|
let u = f(&mut self.val);
|
||||||
|
self.buf.set(&self.val);
|
||||||
|
u
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn replace(&mut self, new: T) -> T {
|
||||||
|
self.modify(|old| mem::replace(old, new))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_inner(self) -> (T, EncodedBuf<T>) {
|
||||||
|
(self.val, self.buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Encode for Cached<T>
|
||||||
|
where
|
||||||
|
T: Encode,
|
||||||
|
{
|
||||||
|
fn encode(&self, w: impl Write) -> Result<()> {
|
||||||
|
self.buf.encode(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `Decode` implementation for `Cached` exists for the sake of
|
||||||
|
/// completeness, but you probably shouldn't need to use it.
|
||||||
|
impl<'a, T> Decode<'a> for Cached<T>
|
||||||
|
where
|
||||||
|
T: Encode + Decode<'a>,
|
||||||
|
{
|
||||||
|
fn decode(r: &mut &'a [u8]) -> Result<Self> {
|
||||||
|
let val = T::decode(r)?;
|
||||||
|
Ok(Self::new(val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Caches the result of `T`'s [`Encode`] implementation into an owned buffer.
|
||||||
|
///
|
||||||
|
/// This is useful for types with expensive [`Encode`] implementations such as
|
||||||
|
/// [`Text`] or [`Compound`]. It has little to no benefit for primitive types
|
||||||
|
/// such as `i32`, `VarInt`, `&str`, `&[u8]`, etc.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use valence_protocol::cache::EncodedBuf;
|
||||||
|
/// use valence_protocol::Encode;
|
||||||
|
///
|
||||||
|
/// let mut buf1 = Vec::new();
|
||||||
|
/// let mut buf2 = Vec::new();
|
||||||
|
///
|
||||||
|
/// "hello".encode(&mut buf1).unwrap();
|
||||||
|
///
|
||||||
|
/// let cache = EncodedBuf::new("hello");
|
||||||
|
/// cache.encode(&mut buf2).unwrap();
|
||||||
|
///
|
||||||
|
/// assert_eq!(buf1, buf2);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`Text`]: crate::text::Text
|
||||||
|
/// [`Compound`]: valence_nbt::Compound
|
||||||
|
pub struct EncodedBuf<T: ?Sized> {
|
||||||
|
buf: Vec<u8>,
|
||||||
|
res: Result<()>,
|
||||||
|
_marker: PhantomData<fn(T) -> T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Encode + ?Sized> EncodedBuf<T> {
|
||||||
|
pub fn new(t: &T) -> Self {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
let res = t.encode(&mut buf);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
buf,
|
||||||
|
res,
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, t: &T) {
|
||||||
|
self.buf.clear();
|
||||||
|
self.res = t.encode(&mut self.buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_inner(self) -> Result<Vec<u8>> {
|
||||||
|
self.res.map(|()| self.buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Encode for EncodedBuf<T> {
|
||||||
|
fn encode(&self, mut w: impl Write) -> Result<()> {
|
||||||
|
match &self.res {
|
||||||
|
Ok(()) => Ok(w.write_all(&self.buf)?),
|
||||||
|
Err(e) => Err(anyhow!("{e:#}")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encoded_len(&self) -> usize {
|
||||||
|
self.buf.len()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,47 +0,0 @@
|
||||||
use std::io::Write;
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use anyhow::anyhow;
|
|
||||||
|
|
||||||
use crate::{Encode, Result};
|
|
||||||
|
|
||||||
pub struct CachedEncode<T: ?Sized> {
|
|
||||||
buf: Vec<u8>,
|
|
||||||
res: Result<()>,
|
|
||||||
_marker: PhantomData<fn(T) -> T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Encode + ?Sized> CachedEncode<T> {
|
|
||||||
pub fn new(t: &T) -> Self {
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
let res = t.encode(&mut buf);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
buf,
|
|
||||||
res,
|
|
||||||
_marker: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set(&mut self, t: &T) {
|
|
||||||
self.buf.clear();
|
|
||||||
self.res = t.encode(&mut self.buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_inner(self) -> Result<Vec<u8>> {
|
|
||||||
self.res.map(|()| self.buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized> Encode for CachedEncode<T> {
|
|
||||||
fn encode(&self, mut w: impl Write) -> Result<()> {
|
|
||||||
match &self.res {
|
|
||||||
Ok(()) => Ok(w.write_all(&self.buf)?),
|
|
||||||
Err(e) => Err(anyhow!("{e:#}")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encoded_len(&self) -> usize {
|
|
||||||
self.buf.len()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -91,9 +91,9 @@ pub mod block_pos;
|
||||||
pub mod bounded;
|
pub mod bounded;
|
||||||
pub mod byte_angle;
|
pub mod byte_angle;
|
||||||
mod byte_counter;
|
mod byte_counter;
|
||||||
|
pub mod cache;
|
||||||
pub mod codec;
|
pub mod codec;
|
||||||
pub mod enchant;
|
pub mod enchant;
|
||||||
pub mod encoded_buf;
|
|
||||||
pub mod entity_meta;
|
pub mod entity_meta;
|
||||||
pub mod ident;
|
pub mod ident;
|
||||||
mod impls;
|
mod impls;
|
||||||
|
|
Loading…
Reference in a new issue