diff options
-rw-r--r-- | Cargo.lock | 59 | ||||
-rw-r--r-- | Cargo.toml | 25 | ||||
-rw-r--r-- | README.md | 7 | ||||
-rw-r--r-- | src/chat.rs | 12 | ||||
-rw-r--r-- | src/deserialize.rs | 3 | ||||
-rw-r--r-- | src/lib.rs | 7 | ||||
-rw-r--r-- | src/nbt.rs | 10 | ||||
-rw-r--r-- | src/protocol.rs | 16 | ||||
-rw-r--r-- | src/serialize.rs | 2 | ||||
-rw-r--r-- | src/status.rs | 109 | ||||
-rw-r--r-- | src/test_macros.rs | 10 | ||||
-rw-r--r-- | src/types.rs | 46 | ||||
-rw-r--r-- | src/utils.rs | 1 | ||||
-rw-r--r-- | src/uuid.rs | 135 | ||||
-rw-r--r-- | src/v1_15_2.rs | 134 |
15 files changed, 277 insertions, 299 deletions
@@ -7,15 +7,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" [[package]] -name = "aho-corasick" -version = "0.7.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b476ce7103678b0c6d3d395dbbae31d48ff910bd28be979ba5d48c6351131d0d" -dependencies = [ - "memchr", -] - -[[package]] name = "autocfg" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -72,12 +63,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" [[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] name = "libc" version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -85,32 +70,17 @@ checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" [[package]] name = "mcproto-rs" -version = "0.1.0" +version = "0.2.0" dependencies = [ "base64", "flate2", - "lazy_static", - "md5", "paste", "rand", - "regex", "serde", "serde_json", ] [[package]] -name = "md5" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" - -[[package]] -name = "memchr" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" - -[[package]] name = "miniz_oxide" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -192,24 +162,6 @@ dependencies = [ ] [[package]] -name = "regex" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8963b85b8ce3074fecffde43b4b0dded83ce2f367dc8d363afc56679f3ee820b" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", - "thread_local", -] - -[[package]] -name = "regex-syntax" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cab7a364d15cde1e505267766a2d3c4e22a843e1a601f0fa7564c0f82ced11c" - -[[package]] name = "ryu" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -258,15 +210,6 @@ dependencies = [ ] [[package]] -name = "thread_local" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" -dependencies = [ - "lazy_static", -] - -[[package]] name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1,23 +1,26 @@ [package] name = "mcproto-rs" -version = "0.1.0" +version = "0.2.0" authors = ["Joey Sacchini <joey@sacchini.net>"] edition = "2018" license = "Apache-2.0" keywords = ["minecraft", "games", "protocol", "serialziers", "deserializers", "packets", "mc"] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] -serde_json = "1.0" -regex = "1" -lazy_static = "1.4" -rand = "0.7" -flate2 = "1.0.17" -base64 = "0.12.3" +serde_json = { version = "1.0", default-features = false, features = ["alloc"] } +base64 = { version = "0.12.3", default-features = false, features = ["alloc"] } +rand = { version = "0.7", optional = true } paste = "1.0.1" -md5 = "0.7.0" [dependencies.serde] version = "1.0.116" -features = [ "derive" ]
\ No newline at end of file +features = [ "derive", "alloc" ] +default-features = false + +[dev-dependencies] +flate2 = "1.0.17" + +[features] +default = [ "std" ] + +std = [ "rand" ]
\ No newline at end of file @@ -7,4 +7,9 @@ This crate can be used to implement any version of the minecraft protocol, and h To implement your own protocol, consult this example, and use the macros to define a protocol to your heart's content! -More documentation to come, just dumping the code since I finished it.
\ No newline at end of file +More documentation to come, just dumping the code since I finished it. + +## `#![no_std]` + +You can use this crate without the standard library (but requiring `alloc`) by setting `default-features = false` in +your Cargo.toml. This will only disable the `UUID4::random()` function, which requires `OsRandom` to generate a random UUID.
\ No newline at end of file diff --git a/src/chat.rs b/src/chat.rs index 198e231..3913a6c 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -1,7 +1,6 @@ -use std::{fmt, str}; +use alloc::{vec::Vec, string::{String, ToString}, fmt, collections::{BTreeMap}, boxed::Box, borrow::ToOwned}; use serde::{Serialize, Deserialize, Deserializer, de, Serializer}; use serde::de::{Visitor, Error, IntoDeserializer, MapAccess}; -use std::collections::BTreeMap; use serde::ser::SerializeMap; use serde_json::Value; use crate::{SerializeResult, DeserializeResult}; @@ -797,7 +796,8 @@ impl<'de> Deserialize<'de> for ColorCode { } fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where - E: Error, { + E: de::Error, + { if let Some(code) = ColorCode::from_name(v) { Ok(code) } else { @@ -1029,10 +1029,10 @@ impl super::Deserialize for Chat { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] use super::protocol::TestRandom; -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for Chat { fn test_gen_random() -> Self { let str = String::test_gen_random(); @@ -1040,7 +1040,7 @@ impl TestRandom for Chat { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] pub mod tests { use super::*; diff --git a/src/deserialize.rs b/src/deserialize.rs index 044560d..0fd32a6 100644 --- a/src/deserialize.rs +++ b/src/deserialize.rs @@ -1,6 +1,5 @@ use crate::types::VarInt; -use std::fmt; -use std::string::FromUtf8Error; +use alloc::{vec::Vec, string::{FromUtf8Error, String}, fmt}; pub enum DeserializeErr { Eof, @@ -1,7 +1,10 @@ #![feature(const_fn)] #![feature(test)] +#![cfg_attr(not(test), no_std)] -#[cfg(test)] +extern crate alloc; + +#[cfg(all(test, feature = "std"))] extern crate test; mod deserialize; @@ -17,6 +20,6 @@ pub mod v1_15_2; pub use deserialize::*; pub use serialize::*; -#[cfg(test)] +#[cfg(all(test, feature = "std"))] mod test_macros; mod chat; @@ -2,9 +2,9 @@ use crate::utils::{ read_int, read_long, read_one_byte, read_short, take, write_int, write_long, write_short, }; use crate::{DeserializeErr, DeserializeResult, Deserialized}; -use std::fmt; +use alloc::{string::{String, ToString}, borrow::ToOwned, fmt, vec::Vec}; -#[cfg(test)] +#[cfg(all(test, feature = "std"))] use crate::protocol::TestRandom; #[derive(Clone, Debug, PartialEq)] @@ -26,7 +26,7 @@ impl NamedTag { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for NamedTag { fn test_gen_random() -> Self { Self { @@ -125,7 +125,7 @@ impl Tag { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for Tag { fn test_gen_random() -> Self { let random_idx = rand::random::<usize>() % 8; @@ -518,7 +518,7 @@ impl Tag { } // test -#[cfg(test)] +#[cfg(all(test, feature = "std"))] mod tests { use super::*; use flate2::read::GzDecoder; diff --git a/src/protocol.rs b/src/protocol.rs index 5f2ea67..9e7307e 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -1,5 +1,5 @@ use crate::{Deserialize, DeserializeErr, Serialize}; -use std::fmt; +use alloc::{string::String, fmt, vec::Vec}; #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] pub struct ProtocolSpec { @@ -73,7 +73,7 @@ pub trait ProtocolType: Serialize + Deserialize {} impl<T: Serialize + Deserialize> ProtocolType for T {} -#[cfg(test)] +#[cfg(all(test, feature = "std"))] pub trait TestRandom { fn test_gen_random() -> Self; } @@ -115,7 +115,7 @@ macro_rules! __protocol_body_def_helper { } } - #[cfg(test)] + #[cfg(all(test, feature = "std"))] impl TestRandom for $bodyt { fn test_gen_random() -> Self { Self::default() @@ -147,7 +147,7 @@ macro_rules! __protocol_body_def_helper { } } - #[cfg(test)] + #[cfg(all(test, feature = "std"))] impl TestRandom for $bodyt { fn test_gen_random() -> Self { Self{ $($fname: <$ftyp>::test_gen_random()),+ } @@ -433,7 +433,7 @@ macro_rules! proto_enum_with_type { } } - #[cfg(test)] + #[cfg(all(test, feature = "std"))] impl TestRandom for $typname { fn test_gen_random() -> Self { let mut idx: usize = (rand::random::<usize>() % (count_num!($($bval),+))) + 1; @@ -537,7 +537,7 @@ macro_rules! proto_str_enum { } } - #[cfg(test)] + #[cfg(all(test, feature = "std"))] impl TestRandom for $typname { fn test_gen_random() -> Self { let mut idx: usize = (rand::random::<usize>() % (count_num!($($nam),+))) + 1; @@ -601,7 +601,7 @@ macro_rules! proto_byte_flag { } } - #[cfg(test)] + #[cfg(all(test, feature = "std"))] impl TestRandom for $typname { fn test_gen_random() -> Self { let mut out = <$typname>::default(); @@ -722,7 +722,7 @@ macro_rules! counted_array_type { } } - #[cfg(test)] + #[cfg(all(test, feature = "std"))] impl<T> TestRandom for $name<T> where T: TestRandom + Debug + Clone + PartialEq, diff --git a/src/serialize.rs b/src/serialize.rs index 6425e98..3b96a3f 100644 --- a/src/serialize.rs +++ b/src/serialize.rs @@ -1,4 +1,4 @@ -use std::fmt; +use alloc::{string::String, fmt}; pub enum SerializeErr { FailedJsonEncode(String), diff --git a/src/status.rs b/src/status.rs index a0068ee..6944e89 100644 --- a/src/status.rs +++ b/src/status.rs @@ -5,9 +5,9 @@ use crate::{ SerializeErr, SerializeResult, }; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::fmt; +use alloc::{string::String, fmt, vec::Vec}; -#[cfg(test)] +#[cfg(all(test, feature = "std"))] use crate::protocol::TestRandom; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] @@ -42,7 +42,7 @@ impl McDeserialize for StatusSpec { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for StatusSpec { fn test_gen_random() -> Self { Self { @@ -113,45 +113,78 @@ impl<'de> Deserialize<'de> for StatusFaviconSpec { } fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> { - use lazy_static::lazy_static; - use regex::Regex; - // regex to parse valid base64 content - const PATTERN: &str = r"^data:([A-Za-z/]+);base64,([-A-Za-z0-9+/]*={0,3})$"; - lazy_static! { - static ref RE: Regex = Regex::new(PATTERN).expect("regex is valid"); - } - - // try to use regex on the input - // RE.captures_iter(v).next() means "try to get the first capture iterator" - // then we take that iterator, get(1), and if 1 exists, get(2), and if both exist, - // then we try to parse the base64, and drop the error if one occurs. We then - // wrap the content_type and parsed data in StatusFaviconSpec - // then we convert the option to a result using map and unwrap_or_else - let mut captures: regex::CaptureMatches<'_, '_> = RE.captures_iter(v); - captures - .next() - .and_then(move |captures| { - captures.get(1).and_then(move |content_type| { - captures.get(2).and_then(move |raw_base64| { - base64::decode(raw_base64.as_str().as_bytes()) - .map(move |data| StatusFaviconSpec { - content_type: content_type.as_str().to_owned(), - data, - }) - .ok() - }) + // favicon syntax data:{content-type};base64,{} + let v = str_tag(v, "data:", &self)?; + let content_type = str_until_pat(v, ";", &self)?; + let rest = str_tag(v, "base64,", &self)?; + match base64::decode(rest) { + Ok(data) => { + Ok(StatusFaviconSpec{ + data, + content_type: content_type.to_owned(), }) - }) - .map(move |result| Ok(result)) - .unwrap_or_else(|| { - Err(serde::de::Error::invalid_value( - serde::de::Unexpected::Str(v), - &self, - )) - }) + }, + Err(err) => { + Err(E::custom(format_args!("failed base64 decode {:?}", err))) + } + } } } deserializer.deserialize_str(Visitor {}) } } + +fn str_tag<'a, E, V>( + target: &'a str, + expected: &str, + v: &V, +) -> Result<&'a str, E> where + E: serde::de::Error, + V: serde::de::Visitor<'a> +{ + let (front, back) = str_take(target, expected.len(), v)?; + if front != expected { + Err(E::invalid_value(serde::de::Unexpected::Str(target), v)) + } else { + Ok(back) + } +} + +fn str_until_pat<'a, E, V>( + target: &'a str, + pat: &str, + v: &V, +) -> Result<&'a str, E> where + E: serde::de::Error, + V: serde::de::Visitor<'a> +{ + let n_pat = pat.len(); + if target.len() < n_pat { + return Err(E::invalid_value(serde::de::Unexpected::Str(target), v)); + } + + for i in 0..=(target.len()-n_pat) { + let v = &target[i..i+n_pat]; + if v == pat { + return Ok(&target[..i]); + } + } + + Err(E::invalid_value(serde::de::Unexpected::Str(target), v)) +} + +fn str_take<'a, E, V>( + target: &'a str, + n: usize, + v: &V, +) -> Result<(&'a str, &'a str), E> where + E: serde::de::Error, + V: serde::de::Visitor<'a> +{ + if target.len() < n { + Err(E::invalid_value(serde::de::Unexpected::Str(target), v)) + } else { + Ok(target.split_at(n)) + } +}
\ No newline at end of file diff --git a/src/test_macros.rs b/src/test_macros.rs index 254c3bb..ead3208 100644 --- a/src/test_macros.rs +++ b/src/test_macros.rs @@ -1,5 +1,7 @@ use crate::{SerializeResult, Serializer}; -#[cfg(test)] +use alloc::vec::Vec; + +#[cfg(all(test, feature = "std"))] #[macro_export] macro_rules! packet_test_cases { ($pnam: ident, $varnam: ident, $bodnam: ident, $testnam: ident, $benchnams: ident, $benchnamd: ident) => { @@ -70,13 +72,13 @@ macro_rules! packet_test_cases { }; } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] #[derive(Clone, Debug, Default, PartialEq)] pub struct BenchSerializer { data: Vec<u8>, } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl Serializer for BenchSerializer { fn serialize_bytes(&mut self, data: &[u8]) -> SerializeResult { self.data.extend_from_slice(data); @@ -84,7 +86,7 @@ impl Serializer for BenchSerializer { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl BenchSerializer { pub fn reset(&mut self) { self.data.clear(); diff --git a/src/types.rs b/src/types.rs index 734f40f..47df47b 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,12 +1,13 @@ // ... PRIMITIVE TYPES ... +use alloc::{string::String, vec::Vec}; use crate::utils::*; use crate::uuid::UUID4; use crate::*; pub use super::chat::*; -#[cfg(test)] +#[cfg(all(test, feature = "std"))] use crate::protocol::TestRandom; // bool @@ -26,7 +27,7 @@ impl Deserialize for bool { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for bool { fn test_gen_random() -> Self { rand::random() @@ -46,7 +47,7 @@ impl Deserialize for u8 { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for u8 { fn test_gen_random() -> Self { rand::random() @@ -66,7 +67,7 @@ impl Deserialize for i8 { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for i8 { fn test_gen_random() -> Self { rand::random() @@ -87,7 +88,7 @@ impl Deserialize for u16 { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for u16 { fn test_gen_random() -> Self { rand::random() @@ -109,7 +110,7 @@ impl Deserialize for i16 { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for i16 { fn test_gen_random() -> Self { rand::random() @@ -130,7 +131,7 @@ impl Deserialize for i32 { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for i32 { fn test_gen_random() -> Self { rand::random() @@ -151,7 +152,7 @@ impl Deserialize for i64 { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for i64 { fn test_gen_random() -> Self { rand::random() @@ -175,7 +176,7 @@ impl Deserialize for f32 { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for f32 { fn test_gen_random() -> Self { rand::random() @@ -199,7 +200,7 @@ impl Deserialize for f64 { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for f64 { fn test_gen_random() -> Self { rand::random() @@ -256,7 +257,7 @@ impl std::fmt::Display for VarInt { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for VarInt { fn test_gen_random() -> Self { let out: i32 = rand::random(); @@ -280,7 +281,7 @@ impl Deserialize for VarLong { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for VarLong { fn test_gen_random() -> Self { let out: i64 = rand::random(); @@ -358,7 +359,7 @@ impl Deserialize for String { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for String { fn test_gen_random() -> Self { let raw_len: u8 = rand::random(); @@ -445,7 +446,7 @@ impl Deserialize for IntPosition { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for IntPosition { fn test_gen_random() -> Self { let x: i32 = ((rand::random::<u32>() % (1 << 26)) as i32) - (1 << 25); @@ -473,7 +474,7 @@ impl Deserialize for Angle { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for Angle { fn test_gen_random() -> Self { Self { @@ -517,7 +518,7 @@ impl Deserialize for UUID4 { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for UUID4 { fn test_gen_random() -> Self { UUID4::random() @@ -559,7 +560,7 @@ impl Into<nbt::NamedTag> for NamedNbtTag { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for NamedNbtTag { fn test_gen_random() -> Self { Self { @@ -597,7 +598,7 @@ impl FixedInt { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for FixedInt { fn test_gen_random() -> Self { FixedInt::new(f64::test_gen_random(), 16) @@ -658,7 +659,7 @@ where } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl<T> TestRandom for Option<T> where T: TestRandom, @@ -723,7 +724,7 @@ impl Deserialize for Slot { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for Slot { fn test_gen_random() -> Self { let item_id = VarInt::test_gen_random(); @@ -738,10 +739,11 @@ impl TestRandom for Slot { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] mod tests { use super::*; - use std::fmt::Debug; + use alloc::fmt::Debug; + use alloc::borrow::ToOwned; #[test] fn test_bool() { diff --git a/src/utils.rs b/src/utils.rs index 85642c9..bb8ea36 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,4 +1,5 @@ use crate::{DeserializeErr, DeserializeResult, Deserialized}; +use alloc::string::String; #[inline] pub fn read_one_byte(data: &[u8]) -> DeserializeResult<u8> { diff --git a/src/uuid.rs b/src/uuid.rs index 514fd61..0b9d8ba 100644 --- a/src/uuid.rs +++ b/src/uuid.rs @@ -1,8 +1,7 @@ use crate::utils::*; -use lazy_static::lazy_static; -use regex::Regex; use serde::{Deserializer, Serializer}; -use std::fmt::{Debug, Display, Formatter}; +use alloc::{fmt, string::{ToString, String}}; +use fmt::{Display, Debug, Formatter}; #[derive(Copy, Clone, PartialEq, Hash, Eq)] pub struct UUID4 { @@ -10,13 +9,13 @@ pub struct UUID4 { } impl Display for UUID4 { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.write_str(self.hex().as_str()) } } impl Debug for UUID4 { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.write_str("UUID4{")?; f.write_str(self.hex().as_str())?; f.write_str("}") @@ -72,6 +71,7 @@ impl UUID4 { RawUUID::from_str(from).and_then(move |raw| raw.parse4()) } + #[cfg(feature = "std")] pub fn random() -> Self { UUID4 { raw: rand::random(), @@ -101,27 +101,26 @@ struct RawUUID<'a> { impl<'a> RawUUID<'a> { fn from_str(from: &'a str) -> Option<RawUUID<'a>> { - const PATTERN: &str = r"^([A-Fa-f0-9]{8})-?([A-Fa-f0-9]{4})-?([A-Fa-f0-9]{4})-?([A-Fa-f0-9]{4})-?([A-Fa-f0-9]{12})$"; - // let re = Regex::new(PATTERN).expect("regex is valid"); - lazy_static! { - static ref RE: Regex = Regex::new(PATTERN).expect("regex is valid"); - } - - RE.captures_iter(from) - .filter_map(move |c| { - c.get(1).map(move |g| g.as_str()).and_then(move |g0| { - c.get(2).map(move |g| g.as_str()).and_then(move |g1| { - c.get(3).map(move |g| g.as_str()).and_then(move |g2| { - c.get(4).map(move |g| g.as_str()).and_then(move |g3| { - c.get(5).map(move |g4| RawUUID { - parts: [g0, g1, g2, g3, g4.as_str()], - }) - }) - }) - }) - }) - }) - .nth(0) + // 8-4-4-4-12 + let (s0, from) = str_split(from, 8)?; + str_check_hex(s0)?; + let from = str_tag(from, "-")?; + let (s1, from) = str_split(from, 4)?; + str_check_hex(s1)?; + let from = str_tag(from, "-")?; + let (s2, from) = str_split(from, 4)?; + str_check_hex(s2)?; + let from = str_tag(from, "-")?; + let (s3, from) = str_split(from, 4)?; + str_check_hex(s3)?; + let from = str_tag(from, "-")?; + let (s4, from) = str_split(from, 12)?; + str_check_hex(s4)?; + str_check_eof(from)?; + + Some(Self{ + parts: [s0, s1, s2, s3, s4], + }) } fn parse4(self) -> Option<UUID4> { @@ -141,30 +140,55 @@ impl<'a> RawUUID<'a> { Some(UUID4 { raw }) } - // - // fn parse3(self) -> Option<UUID3> { - // self.parse4().map(move |id| UUID3 { raw: id.raw }) - // } } -// #[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)] -// pub struct UUID3 { -// raw: u128, -// } -// -// impl UUID3 { -// pub fn parse(from: &str) -> Option<UUID3> { -// RawUUID::from_str(from).and_then(move |id| id.parse3()) -// } -// -// pub fn from(namespace: UUID4, data: &str) -> UUID3 { -// namespace.raw -// } -// } - -#[cfg(test)] +fn str_tag<'a>(source: &'a str, tag: &str) -> Option<&'a str> { + let (front, back) = str_split(source, tag.len())?; + if front != tag { + None + } else { + Some(back) + } +} + +fn str_check_eof(source: &str) -> Option<()> { + if source.is_empty() { + Some(()) + } else { + None + } +} + +fn str_check_hex(mut source: &str) -> Option<()> { + if source.is_empty() { + return None + } + + loop { + let (part, rest) = str_split(source, 2)?; + for c in part.chars() { + parse_hex_char(c as u8)?; + } + + source = rest; + if source.is_empty() { + return Some(()); + } + } +} + +fn str_split(source: &str, n: usize) -> Option<(&str, &str)> { + if source.len() < n { + None + } else { + Some(source.split_at(n)) + } +} + +#[cfg(all(test, feature = "std"))] mod tests { use super::UUID4; + use alloc::string::ToString; #[test] fn test_random_uuid4() { @@ -229,4 +253,23 @@ mod tests { let deserialized: UUID4 = serde_json::from_str(json.as_str()).expect("should read fine"); assert_eq!(deserialized, id); } + + #[bench] + fn bench_parse_uuid4(b: &mut test::Bencher) { + let rand = UUID4::random(); + let str = rand.to_string(); + b.bytes = str.bytes().len() as u64; + b.iter(|| { + UUID4::parse(str.as_str()).expect("should parse fine") + }) + } + + #[bench] + fn bench_uuid4_to_str(b: &mut test::Bencher) { + let rand = UUID4::random(); + b.bytes = 128; + b.iter(|| { + rand.to_string() + }) + } } diff --git a/src/v1_15_2.rs b/src/v1_15_2.rs index 602a7a3..77d01f9 100644 --- a/src/v1_15_2.rs +++ b/src/v1_15_2.rs @@ -1,8 +1,9 @@ use crate::{types::*, uuid::*, *}; -use std::fmt::Debug; -use std::cell::Cell; +use alloc::{string::{String, ToString}, vec::Vec, borrow::ToOwned, boxed::Box}; +use alloc::fmt; +use fmt::Debug; -#[cfg(test)] +#[cfg(all(test, feature = "std"))] use crate::protocol::TestRandom; #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -903,7 +904,7 @@ impl From<Vec<u8>> for RemainingBytes { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for RemainingBytes { fn test_gen_random() -> Self { let size: usize = rand::random::<usize>() % 256; @@ -1060,7 +1061,7 @@ impl Deserialize for BlockChangeHorizontalPosition { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for BlockChangeHorizontalPosition { fn test_gen_random() -> Self { BlockChangeHorizontalPosition { @@ -1219,7 +1220,7 @@ impl Deserialize for CommandNodeSpec { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for CommandNodeSpec { fn test_gen_random() -> Self { let children_indices = <VarIntCountedArray<VarInt>>::test_gen_random(); @@ -1278,7 +1279,7 @@ impl CommandArgumentNodeSpec { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for CommandArgumentNodeSpec { fn test_gen_random() -> Self { let name = String::test_gen_random(); @@ -1374,7 +1375,7 @@ impl<T> Clone for NumParserProps<T> where T: Clone { } impl<T> Debug for NumParserProps<T> where T: Debug { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "NumParserProps(min={:?}, max={:?})", self.min, self.max) } } @@ -1434,7 +1435,7 @@ impl<T> Deserialize for NumParserProps<T> where T: Deserialize { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl<T> TestRandom for NumParserProps<T> where T: TestRandom + std::cmp::PartialOrd, rand::distributions::Standard: rand::distributions::Distribution<T>, @@ -1525,7 +1526,7 @@ impl Deserialize for TeamMember { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for TeamMember { fn test_gen_random() -> Self { use TeamMember::*; @@ -1618,7 +1619,7 @@ impl Deserialize for UpdateScoreSpec { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for UpdateScoreSpec { fn test_gen_random() -> Self { Self { @@ -1715,7 +1716,7 @@ impl Deserialize for StopSoundSpec { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for StopSoundSpec { fn test_gen_random() -> Self { let source = if rand::random::<bool>() { @@ -1850,7 +1851,7 @@ impl Deserialize for GameChangeReason { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for GameChangeReason { fn test_gen_random() -> Self { // todo @@ -1950,7 +1951,7 @@ impl From<Option<MapColumnsSpec>> for MapColumns { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for MapColumns { fn test_gen_random() -> Self { <Option<MapColumnsSpec>>::test_gen_random().into() @@ -2051,7 +2052,7 @@ impl<A> Deserialize for PlayerInfoAction<A> } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl<A> TestRandom for PlayerInfoAction<A> where A: Clone + PartialEq + Debug + TestRandom @@ -2281,7 +2282,7 @@ impl Deserialize for AdvancementDisplayFlags { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for AdvancementDisplayFlags { fn test_gen_random() -> Self { let background_texture = if rand::random::<bool>() { @@ -2574,7 +2575,7 @@ impl Deserialize for RecipeSpec { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for RecipeSpec { fn test_gen_random() -> Self { RecipeSpec { @@ -2645,7 +2646,7 @@ impl Deserialize for RecipeCraftingShapedSpec { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for RecipeCraftingShapedSpec { fn test_gen_random() -> Self { use rand::distributions::Distribution; @@ -2768,7 +2769,7 @@ impl Deserialize for ChunkData { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for ChunkData { fn test_gen_random() -> Self { ChunkData { @@ -2786,15 +2787,13 @@ impl TestRandom for ChunkData { pub const LIGHT_DATA_LENGTH: usize = 2048; pub const LIGHT_DATA_SECTIONS: usize = 18; +#[derive(Clone, PartialEq)] pub struct LightingData { pub data: Box<[Option<[u8; LIGHT_DATA_LENGTH]>; LIGHT_DATA_SECTIONS]>, - - _update_mask: Cell<Option<VarInt>>, - _reset_mask: Cell<Option<VarInt>>, } impl LightingData { - fn deserialize(update_mask: VarInt, reset_mask: VarInt, mut data: &[u8]) -> DeserializeResult<Self> { + fn deserialize(update_mask: VarInt, mut data: &[u8]) -> DeserializeResult<Self> { let mut out = Box::new([None; LIGHT_DATA_SECTIONS]); for i in 0..LIGHT_DATA_SECTIONS { // gotta read the var int @@ -2819,39 +2818,16 @@ impl LightingData { let result = Self { data: out, - _update_mask: Cell::new(Some(update_mask)), - _reset_mask: Cell::new(Some(reset_mask)), }; Deserialized::ok(result, data) } - fn update_mask(&self) -> &VarInt { - Self::lazily_get(&self._update_mask, || self.compute_update_mask()) - } - - fn reset_mask(&self) -> &VarInt { - Self::lazily_get(&self._reset_mask, || self.compute_reset_mask()) - } - - fn lazily_get<T, F>(option: &Cell<Option<T>>, provider: F) -> &T where F: FnOnce() -> T { - unsafe { - let ptr = option.as_ptr().as_mut().expect("non-null"); - match ptr { - Some(data) => data, - None => { - ptr.replace(provider()); - ptr.as_ref().expect("it's there") - } - } - } - } - - fn compute_update_mask(&self) -> VarInt { + fn update_mask(&self) -> VarInt { self.compute_has_mask(true) } - fn compute_reset_mask(&self) -> VarInt { + fn reset_mask(&self) -> VarInt { self.compute_has_mask(false) } @@ -2878,8 +2854,8 @@ impl LightingData { } } -impl std::fmt::Debug for LightingData { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Debug for LightingData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "LightingData(update={:018b}, reset={:018b}, size={}, bytes={})", @@ -2893,33 +2869,13 @@ impl std::fmt::Debug for LightingData { } } -impl std::fmt::Display for LightingData { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - <dyn std::fmt::Debug>::fmt(self, f) +impl fmt::Display for LightingData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + <dyn fmt::Debug>::fmt(self, f) } } -impl Clone for LightingData { - fn clone(&self) -> Self { - Self { - data: self.data.clone(), - _update_mask: Cell::new(Some(self.update_mask().clone())), - _reset_mask: Cell::new(Some(self.reset_mask().clone())), - } - } -} - -impl PartialEq for LightingData { - fn eq(&self, other: &Self) -> bool { - self.data.eq(&other.data) && - self.update_mask().eq(other.update_mask()) && - self.reset_mask().eq(other.reset_mask()) - } -} - -unsafe impl Sync for LightingData {} - -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl LightingData { fn gen_random_mask() -> i32 { let rand: u32 = rand::random(); @@ -2927,7 +2883,7 @@ impl LightingData { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for LightingData { fn test_gen_random() -> Self { let set_mask = Self::gen_random_mask(); @@ -2942,20 +2898,8 @@ impl TestRandom for LightingData { } } - let cache_masks = rand::random::<bool>(); - - if cache_masks { - Self { - data, - _update_mask: Cell::new(Some(VarInt(set_mask))), - _reset_mask: Cell::new(None), - } - } else { - Self { - data, - _update_mask: Cell::new(None), - _reset_mask: Cell::new(None), - } + Self { + data, } } } @@ -2981,11 +2925,11 @@ impl Deserialize for LightingUpdateSpec { fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { let Deserialized { value: skylight_update_mask, data } = VarInt::mc_deserialize(data)?; let Deserialized { value: blocklight_update_mask, data } = VarInt::mc_deserialize(data)?; - let Deserialized { value: skylight_reset_mask, data } = VarInt::mc_deserialize(data)?; - let Deserialized { value: blocklight_reset_mask, data } = VarInt::mc_deserialize(data)?; + let Deserialized { value: _, data } = VarInt::mc_deserialize(data)?; + let Deserialized { value: _, data } = VarInt::mc_deserialize(data)?; - let Deserialized { value: skylight_data, data } = LightingData::deserialize(skylight_update_mask, skylight_reset_mask, data)?; - let Deserialized { value: blocklight_data, data } = LightingData::deserialize(blocklight_update_mask, blocklight_reset_mask, data)?; + let Deserialized { value: skylight_data, data } = LightingData::deserialize(skylight_update_mask, data)?; + let Deserialized { value: blocklight_data, data } = LightingData::deserialize(blocklight_update_mask, data)?; Deserialized::ok(Self { skylight_data, @@ -2994,7 +2938,7 @@ impl Deserialize for LightingUpdateSpec { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl TestRandom for LightingUpdateSpec { fn test_gen_random() -> Self { Self { @@ -3004,7 +2948,7 @@ impl TestRandom for LightingUpdateSpec { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] pub mod tests { use super::*; use crate::packet_test_cases; |