diff --git a/Cargo.lock b/Cargo.lock index 70c20f76..e917710d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -98,8 +98,8 @@ checksum = "fb4b28c1e534d96213c8966bb9240095757aa0909128985f97d16afd2e7257a8" dependencies = [ "either", "owo-colors", - "proc-macro2 1.0.70", - "quote 1.0.23", + "proc-macro2 1.0.78", + "quote 1.0.35", "syn 1.0.109", ] @@ -215,6 +215,7 @@ dependencies = [ "file_offset", "quickcheck", "serde", + "zerocopy", ] [[package]] @@ -423,6 +424,7 @@ dependencies = [ "serde_derive", "uuid", "warn", + "zerocopy", ] [[package]] @@ -749,6 +751,7 @@ dependencies = [ "common", "datafile", "ndarray", + "zerocopy", ] [[package]] @@ -863,6 +866,8 @@ dependencies = [ "quickcheck", "void", "warn", + "zerocopy", + "zerocopy-derive", ] [[package]] @@ -1018,9 +1023,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -1047,11 +1052,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.23" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ - "proc-macro2 1.0.70", + "proc-macro2 1.0.78", ] [[package]] @@ -1400,6 +1405,8 @@ dependencies = [ "packer", "time", "warn", + "zerocopy", + "zerocopy-derive", ] [[package]] @@ -1471,6 +1478,7 @@ dependencies = [ "serverbrowse", "time", "uuid", + "zerocopy", ] [[package]] @@ -1496,8 +1504,19 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.23", + "proc-macro2 1.0.78", + "quote 1.0.35", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" +dependencies = [ + "proc-macro2 1.0.78", + "quote 1.0.35", "unicode-ident", ] @@ -1568,8 +1587,8 @@ version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.23", + "proc-macro2 1.0.78", + "quote 1.0.35", "syn 1.0.109", ] @@ -1788,6 +1807,7 @@ dependencies = [ "uuid", "warn", "wireshark-dissector-sys", + "zerocopy", ] [[package]] @@ -1812,6 +1832,27 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "byteorder 1.3.1", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2 1.0.78", + "quote 1.0.35", + "syn 2.0.50", +] + [[package]] name = "zlib_minimal" version = "0.0.1" diff --git a/common/Cargo.toml b/common/Cargo.toml index 39f752c8..5f0a8123 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -8,6 +8,7 @@ license = "MIT/Apache-2.0" arrayvec = "0.5.2" file_offset = { version = "0.1.0", optional = true } serde = { version = "1.0.23", optional = true } +zerocopy = "0.7.32" [dev-dependencies] bencher = "0.1.5" diff --git a/common/src/bytes.rs b/common/src/bytes.rs new file mode 100644 index 00000000..126274d4 --- /dev/null +++ b/common/src/bytes.rs @@ -0,0 +1,54 @@ +use std::array::TryFromSliceError; +use std::convert::TryInto; +use std::mem; +use zerocopy::byteorder::big_endian; + +pub trait TryFromByteSlice { + fn try_from_byte_slice(bytes: &[u8]) -> Result<&Self, TryFromSliceError>; +} + +impl TryFromByteSlice for [u8; N] { + fn try_from_byte_slice(bytes: &[u8]) -> Result<&[u8; N], TryFromSliceError> { + bytes.try_into() + } +} + +pub trait ByteArray { + type ByteArray: AsRef<[u8]> + TryFromByteSlice; +} + +pub trait AsBytesExt: ByteArray + zerocopy::AsBytes { + fn as_byte_array(&self) -> &Self::ByteArray { + TryFromByteSlice::try_from_byte_slice(self.as_bytes()).unwrap() + } +} + +pub trait FromBytesExt: ByteArray + zerocopy::FromBytes { + fn ref_and_rest_from(bytes: &[u8]) -> Option<(&Self, &[u8])> + where + Self: Sized, + { + if bytes.len() < mem::size_of::() { + return None; + } + let (result, rest) = bytes.split_at(mem::size_of::()); + Some((Self::ref_from(result).unwrap(), rest)) + } + fn ref_from_array(bytes: &Self::ByteArray) -> &Self + where + Self: Sized, + { + Self::ref_from(bytes.as_ref()).unwrap() + } + fn from_array(bytes: Self::ByteArray) -> Self + where + Self: Copy + Sized, + { + *Self::ref_from_array(&bytes) + } +} + +impl AsBytesExt for T {} +impl FromBytesExt for T {} + +boilerplate_packed_internal!(big_endian::U32, 4, test_be_u32_size); diff --git a/common/src/lib.rs b/common/src/lib.rs index 235b7eab..1b14c795 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -7,6 +7,7 @@ extern crate arrayvec; extern crate file_offset; #[cfg(feature = "serde")] extern crate serde; +extern crate zerocopy; pub use map_iter::MapIterator; pub use slice::relative_size_of; @@ -16,6 +17,7 @@ pub use takeable::Takeable; #[macro_use] mod macros; +pub mod bytes; pub mod digest; pub mod io; pub mod map_iter; diff --git a/common/src/macros.rs b/common/src/macros.rs index 97e27850..3663cfd5 100644 --- a/common/src/macros.rs +++ b/common/src/macros.rs @@ -22,33 +22,27 @@ macro_rules! unwrap_or { } #[macro_export] -macro_rules! unsafe_boilerplate_packed { - ($t:ty, $size:expr, $ts:ident, $ta:ident) => { +macro_rules! boilerplate_packed { + ($t:ty, $size:expr, $ts:ident) => { #[test] fn $ts() { assert_eq!(::std::mem::size_of::<$t>(), $size); } + impl ::common::bytes::ByteArray for $t { + type ByteArray = [u8; $size]; + } + }; +} + +#[macro_export] +macro_rules! boilerplate_packed_internal { + ($t:ty, $size:expr, $ts:ident) => { #[test] - fn $ta() { - assert_eq!(::std::mem::align_of::<$t>(), 1); + fn $ts() { + assert_eq!(::std::mem::size_of::<$t>(), $size); } - impl $t { - pub fn as_bytes(&self) -> &[u8; $size] { - unsafe { &*(self as *const _ as *const [u8; $size]) } - } - pub fn from_bytes(bytes: &[u8; $size]) -> &$t { - unsafe { &*(bytes as *const _ as *const $t) } - } - pub fn from_byte_slice(bytes: &[u8]) -> Option<(&$t, &[u8])> { - if bytes.len() < $size { - return None; - } - let (struct_bytes, rest) = bytes.split_at($size); - let struct_ = <$t>::from_bytes(unsafe { - &*(&struct_bytes[0] as *const _ as *const [u8; $size]) - }); - Some((struct_, rest)) - } + impl crate::bytes::ByteArray for $t { + type ByteArray = [u8; $size]; } }; } diff --git a/common/src/num/mod.rs b/common/src/num/mod.rs index 79ee4dad..a4f2157a 100644 --- a/common/src/num/mod.rs +++ b/common/src/num/mod.rs @@ -1,5 +1,3 @@ -use std::fmt; - #[rustfmt::skip] mod cast; @@ -20,220 +18,3 @@ impl CastFloat for f32 { self.trunc() as i32 } } - -/// Big-endian unsigned 32-bit integer -/// -/// Is internally represented as `[u8; 4]`. -#[repr(C, packed)] -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct BeU32([u8; 4]); - -/// Big-endian signed 32-bit integer -/// -/// Is internally represented as `[u8; 4]`. -#[repr(C, packed)] -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct BeI32([u8; 4]); - -/// Little-endian signed 32-bit integer -/// -/// Is internally represented as `[u8; 4]`. -#[repr(C, packed)] -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct LeI32([u8; 4]); - -/// Big-endian unsigned 16-bit integer -/// -/// Is internally represented as `[u8; 2]`. -#[repr(C, packed)] -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct BeU16([u8; 2]); - -/// Little-endian unsigned 16-bit integer -/// -/// Is internally represented as `[u8; 2]`. -#[repr(C, packed)] -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct LeU16([u8; 2]); - -/// Little-endian signed 16-bit integer -/// -/// Is internally represented as `[u8; 2]`. -#[repr(C, packed)] -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct LeI16([u8; 2]); - -// ====================== -// BOILERPLATE CODE BELOW -// ====================== - -impl BeI32 { - pub fn from_i32(value: i32) -> BeI32 { - BeI32([ - (value >> 24) as u8, - (value >> 16) as u8, - (value >> 8) as u8, - value as u8, - ]) - } - pub fn to_i32(self) -> i32 { - let BeI32(v) = self; - (v[0] as i32) << 24 | (v[1] as i32) << 16 | (v[2] as i32) << 8 | v[3] as i32 - } -} - -impl LeI32 { - pub fn from_i32(value: i32) -> LeI32 { - LeI32([ - value as u8, - (value >> 8) as u8, - (value >> 16) as u8, - (value >> 24) as u8, - ]) - } - pub fn to_i32(self) -> i32 { - let LeI32(v) = self; - (v[3] as i32) << 24 | (v[2] as i32) << 16 | (v[1] as i32) << 8 | v[0] as i32 - } -} - -impl BeU32 { - pub fn from_u32(value: u32) -> BeU32 { - BeU32([ - (value >> 24) as u8, - (value >> 16) as u8, - (value >> 8) as u8, - value as u8, - ]) - } - pub fn to_u32(self) -> u32 { - let BeU32(v) = self; - (v[0] as u32) << 24 | (v[1] as u32) << 16 | (v[2] as u32) << 8 | v[3] as u32 - } -} - -impl BeU16 { - pub fn from_u16(value: u16) -> BeU16 { - BeU16([(value >> 8) as u8, value as u8]) - } - pub fn to_u16(self) -> u16 { - let BeU16(v) = self; - (v[0] as u16) << 8 | v[1] as u16 - } -} - -impl fmt::Debug for BeU16 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.to_u16().fmt(f) - } -} - -impl LeU16 { - pub fn from_u16(value: u16) -> LeU16 { - LeU16([value as u8, (value >> 8) as u8]) - } - pub fn to_u16(self) -> u16 { - let LeU16(v) = self; - (v[1] as u16) << 8 | v[0] as u16 - } -} - -impl fmt::Debug for LeU16 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.to_u16().fmt(f) - } -} - -impl LeI16 { - pub fn from_i16(value: i16) -> LeI16 { - LeI16([value as u8, (value >> 8) as u8]) - } - pub fn to_i16(self) -> i16 { - let LeI16(v) = self; - (v[1] as i16) << 8 | v[0] as i16 - } -} - -impl fmt::Debug for LeI16 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.to_i16().fmt(f) - } -} - -unsafe_boilerplate_packed!(BeI32, 4, test_size_bei32, test_align_bei32); -unsafe_boilerplate_packed!(BeU16, 2, test_size_beu16, test_align_beu16); -unsafe_boilerplate_packed!(BeU32, 4, test_size_beu32, test_align_beu32); -unsafe_boilerplate_packed!(LeI32, 4, test_size_lei32, test_align_lei32); -unsafe_boilerplate_packed!(LeU16, 2, test_size_leu16, test_align_leu16); -unsafe_boilerplate_packed!(LeI16, 2, test_size_lei16, test_align_lei16); - -#[cfg(test)] -mod test { - use super::BeI32; - use super::BeU16; - use super::BeU32; - use super::LeI16; - use super::LeI32; - use super::LeU16; - - quickcheck! { - fn bei32_roundtrip(val: i32) -> bool { BeI32::from_i32(val).to_i32() == val } - fn beu16_roundtrip(val: u16) -> bool { BeU16::from_u16(val).to_u16() == val } - fn beu32_roundtrip(val: u32) -> bool { BeU32::from_u32(val).to_u32() == val } - fn lei32_roundtrip(val: i32) -> bool { LeI32::from_i32(val).to_i32() == val } - fn leu16_roundtrip(val: u16) -> bool { LeU16::from_u16(val).to_u16() == val } - fn lei16_roundtrip(val: i16) -> bool { LeI16::from_i16(val).to_i16() == val } - - fn bei32_unpack(v: (u8, u8, u8, u8)) -> bool { - let bytes = &[v.0, v.1, v.2, v.3]; - BeI32::from_i32(BeI32::from_bytes(bytes).to_i32()).as_bytes() == bytes - } - fn beu16_unpack(v: (u8, u8)) -> bool { - let bytes = &[v.0, v.1]; - BeU16::from_u16(BeU16::from_bytes(bytes).to_u16()).as_bytes() == bytes - } - fn beu32_unpack(v: (u8, u8, u8, u8)) -> bool { - let bytes = &[v.0, v.1, v.2, v.3]; - BeU32::from_u32(BeU32::from_bytes(bytes).to_u32()).as_bytes() == bytes - } - fn lei32_unpack(v: (u8, u8, u8, u8)) -> bool { - let bytes = &[v.0, v.1, v.2, v.3]; - LeI32::from_i32(LeI32::from_bytes(bytes).to_i32()).as_bytes() == bytes - } - fn leu16_unpack(v: (u8, u8)) -> bool { - let bytes = &[v.0, v.1]; - LeU16::from_u16(LeU16::from_bytes(bytes).to_u16()).as_bytes() == bytes - } - fn lei16_unpack(v: (u8, u8)) -> bool { - let bytes = &[v.0, v.1]; - LeI16::from_i16(LeI16::from_bytes(bytes).to_i16()).as_bytes() == bytes - } - } - #[test] - fn order_u16() { - let be = *BeU16::from_u16(0x1234).as_bytes(); - let le = *LeU16::from_u16(0x1234).as_bytes(); - assert_eq!(be, [0x12, 0x34]); - assert_eq!(le, [0x34, 0x12]); - } - - #[test] - fn order_i16() { - let le = *LeI16::from_i16(0x1234).as_bytes(); - assert_eq!(le, [0x34, 0x12]); - } - - #[test] - fn order_i32() { - let be = *BeI32::from_i32(0x12345678).as_bytes(); - let le = *LeI32::from_i32(0x12345678).as_bytes(); - assert_eq!(be, [0x12, 0x34, 0x56, 0x78]); - assert_eq!(le, [0x78, 0x56, 0x34, 0x12]); - } - - #[test] - fn order_u32() { - let be = *BeU32::from_u32(0x12345678).as_bytes(); - assert_eq!(be, [0x12, 0x34, 0x56, 0x78]); - } -} diff --git a/demo/src/writer.rs b/demo/src/writer.rs index e2a65eb5..f2c7be41 100644 --- a/demo/src/writer.rs +++ b/demo/src/writer.rs @@ -3,11 +3,9 @@ use binrw::BinWrite; use buffer; use common::digest::Sha256; use common::num::Cast; -use common::num::LeI32; use huffman::instances::TEEWORLDS as HUFFMAN; use packer::with_packer; use std::io; -use std::mem; use thiserror::Error; use crate::format::CappedString; @@ -144,13 +142,12 @@ impl Writer { with_packer( &mut self.buffer2, |mut p| -> Result<(), buffer::CapacityError> { - for b in msg.chunks(mem::size_of::()) { + for b in msg.chunks(4) { // Get or return 0. fn g(bytes: &[u8], idx: usize) -> u8 { bytes.get(idx).cloned().unwrap_or(0) } - let i = LeI32::from_bytes(&[g(b, 0), g(b, 1), g(b, 2), g(b, 3)]).to_i32(); - p.write_int(i)?; + p.write_int(i32::from_le_bytes([g(b, 0), g(b, 1), g(b, 2), g(b, 3)]))?; } Ok(()) }, diff --git a/gamenet/common/Cargo.toml b/gamenet/common/Cargo.toml index 3db9850e..d4beeb8e 100644 --- a/gamenet/common/Cargo.toml +++ b/gamenet/common/Cargo.toml @@ -13,3 +13,4 @@ serde = "1.0.23" serde_derive = "1.0.7" uuid = { version = "0.8.1", features = ["serde"] } warn = ">=0.1.1,<0.3.0" +zerocopy = "0.7.32" diff --git a/gamenet/common/src/lib.rs b/gamenet/common/src/lib.rs index 57bda6f5..fb8317b1 100644 --- a/gamenet/common/src/lib.rs +++ b/gamenet/common/src/lib.rs @@ -7,6 +7,7 @@ extern crate serde; extern crate serde_derive; extern crate uuid; extern crate warn; +extern crate zerocopy; pub mod debug; pub mod error; diff --git a/gamenet/common/src/msg.rs b/gamenet/common/src/msg.rs index 084f702c..97e4ab7e 100644 --- a/gamenet/common/src/msg.rs +++ b/gamenet/common/src/msg.rs @@ -1,6 +1,5 @@ use arrayvec::ArrayVec; use buffer::CapacityError; -use common::num::BeU16; use common::slice; use error::Error; use error::InvalidIntString; @@ -14,6 +13,7 @@ use std::mem; use std::str; use uuid::Uuid; use warn::Warn; +use zerocopy::byteorder::big_endian; pub const CLIENTS_DATA_NONE: ClientsData<'static> = ClientsData { inner: b"" }; @@ -35,7 +35,7 @@ impl<'a> ClientsData<'a> { #[derive(Clone, Copy, Debug)] pub struct AddrPacked { ip_address: [u8; 16], - port: BeU16, + port: big_endian::U16, } pub trait AddrPackedSliceExt { diff --git a/gamenet/ddnet/src/msg/connless.rs b/gamenet/ddnet/src/msg/connless.rs index 62856c05..de6b9b07 100644 --- a/gamenet/ddnet/src/msg/connless.rs +++ b/gamenet/ddnet/src/msg/connless.rs @@ -1,5 +1,4 @@ use buffer::CapacityError; -use common::num::BeU16; use common::pretty; use error::Error; use gamenet_common::msg::AddrPackedSliceExt; @@ -387,13 +386,13 @@ impl fmt::Debug for RequestCount { impl Count { pub fn decode>(warn: &mut W, _p: &mut Unpacker) -> Result { let result = Ok(Count { - count: { let s = _p.read_raw(2)?; BeU16::from_bytes(&[s[0], s[1]]).to_u16() }, + count: { let s = _p.read_raw(2)?; u16::from_be_bytes([s[0], s[1]]) }, }); _p.finish(warn); result } pub fn encode<'d, 's>(&self, mut _p: Packer<'d, 's>) -> Result<&'d [u8], CapacityError> { - _p.write_raw(BeU16::from_u16(self.count).as_bytes())?; + _p.write_raw(&self.count.to_be_bytes())?; Ok(_p.written()) } } @@ -580,13 +579,13 @@ impl<'a> fmt::Debug for InfoExtendedMore<'a> { impl Heartbeat { pub fn decode>(warn: &mut W, _p: &mut Unpacker) -> Result { let result = Ok(Heartbeat { - alt_port: { let s = _p.read_raw(2)?; BeU16::from_bytes(&[s[0], s[1]]).to_u16() }, + alt_port: { let s = _p.read_raw(2)?; u16::from_be_bytes([s[0], s[1]]) }, }); _p.finish(warn); result } pub fn encode<'d, 's>(&self, mut _p: Packer<'d, 's>) -> Result<&'d [u8], CapacityError> { - _p.write_raw(BeU16::from_u16(self.alt_port).as_bytes())?; + _p.write_raw(&self.alt_port.to_be_bytes())?; Ok(_p.written()) } } diff --git a/gamenet/generate/datatypes.py b/gamenet/generate/datatypes.py index 23bae740..23a59a3b 100644 --- a/gamenet/generate/datatypes.py +++ b/gamenet/generate/datatypes.py @@ -1451,11 +1451,9 @@ class NetBigEndianU16(Member): kind = "be_uint16" type_ = "u16" def decode_expr(self): - import_("common::num::BeU16") - return "{ let s = _p.read_raw(2)?; BeU16::from_bytes(&[s[0], s[1]]).to_u16() }" + return "{ let s = _p.read_raw(2)?; u16::from_be_bytes([s[0], s[1]]) }" def encode_expr(self, self_expr): - import_("common::num::BeU16") - return "_p.write_raw(BeU16::from_u16({}).as_bytes())".format(self_expr) + return "_p.write_raw(&{}.to_be_bytes())".format(self_expr) def serialize_type(self): return {"kind": self.kind} @staticmethod diff --git a/gamenet/teeworlds-0.6/src/msg/connless.rs b/gamenet/teeworlds-0.6/src/msg/connless.rs index 2e8116a0..a89b0c59 100644 --- a/gamenet/teeworlds-0.6/src/msg/connless.rs +++ b/gamenet/teeworlds-0.6/src/msg/connless.rs @@ -1,5 +1,4 @@ use buffer::CapacityError; -use common::num::BeU16; use common::pretty; use error::Error; use gamenet_common::msg::AddrPackedSliceExt; @@ -337,13 +336,13 @@ impl fmt::Debug for RequestCount { impl Count { pub fn decode>(warn: &mut W, _p: &mut Unpacker) -> Result { let result = Ok(Count { - count: { let s = _p.read_raw(2)?; BeU16::from_bytes(&[s[0], s[1]]).to_u16() }, + count: { let s = _p.read_raw(2)?; u16::from_be_bytes([s[0], s[1]]) }, }); _p.finish(warn); result } pub fn encode<'d, 's>(&self, mut _p: Packer<'d, 's>) -> Result<&'d [u8], CapacityError> { - _p.write_raw(BeU16::from_u16(self.count).as_bytes())?; + _p.write_raw(&self.count.to_be_bytes())?; Ok(_p.written()) } } @@ -434,13 +433,13 @@ impl<'a> fmt::Debug for Info<'a> { impl Heartbeat { pub fn decode>(warn: &mut W, _p: &mut Unpacker) -> Result { let result = Ok(Heartbeat { - alt_port: { let s = _p.read_raw(2)?; BeU16::from_bytes(&[s[0], s[1]]).to_u16() }, + alt_port: { let s = _p.read_raw(2)?; u16::from_be_bytes([s[0], s[1]]) }, }); _p.finish(warn); result } pub fn encode<'d, 's>(&self, mut _p: Packer<'d, 's>) -> Result<&'d [u8], CapacityError> { - _p.write_raw(BeU16::from_u16(self.alt_port).as_bytes())?; + _p.write_raw(&self.alt_port.to_be_bytes())?; Ok(_p.written()) } } diff --git a/gamenet/teeworlds-0.7/src/msg/connless.rs b/gamenet/teeworlds-0.7/src/msg/connless.rs index bff00303..21bffc2b 100644 --- a/gamenet/teeworlds-0.7/src/msg/connless.rs +++ b/gamenet/teeworlds-0.7/src/msg/connless.rs @@ -1,5 +1,4 @@ use buffer::CapacityError; -use common::num::BeU16; use common::pretty; use error::Error; use gamenet_common::msg::AddrPackedSliceExt; @@ -339,13 +338,13 @@ impl fmt::Debug for RequestCount { impl Count { pub fn decode>(warn: &mut W, _p: &mut Unpacker) -> Result { let result = Ok(Count { - count: { let s = _p.read_raw(2)?; BeU16::from_bytes(&[s[0], s[1]]).to_u16() }, + count: { let s = _p.read_raw(2)?; u16::from_be_bytes([s[0], s[1]]) }, }); _p.finish(warn); result } pub fn encode<'d, 's>(&self, mut _p: Packer<'d, 's>) -> Result<&'d [u8], CapacityError> { - _p.write_raw(BeU16::from_u16(self.count).as_bytes())?; + _p.write_raw(&self.count.to_be_bytes())?; Ok(_p.written()) } } @@ -444,13 +443,13 @@ impl<'a> fmt::Debug for Info<'a> { impl Heartbeat { pub fn decode>(warn: &mut W, _p: &mut Unpacker) -> Result { let result = Ok(Heartbeat { - alt_port: { let s = _p.read_raw(2)?; BeU16::from_bytes(&[s[0], s[1]]).to_u16() }, + alt_port: { let s = _p.read_raw(2)?; u16::from_be_bytes([s[0], s[1]]) }, }); _p.finish(warn); result } pub fn encode<'d, 's>(&self, mut _p: Packer<'d, 's>) -> Result<&'d [u8], CapacityError> { - _p.write_raw(BeU16::from_u16(self.alt_port).as_bytes())?; + _p.write_raw(&self.alt_port.to_be_bytes())?; Ok(_p.written()) } } diff --git a/map/Cargo.toml b/map/Cargo.toml index 72223509..ff75a13c 100644 --- a/map/Cargo.toml +++ b/map/Cargo.toml @@ -8,3 +8,4 @@ license = "MIT/Apache-2.0" common = { path = "../common/" } datafile = { path = "../datafile/" } ndarray = "0.9.1" +zerocopy = "0.7.32" diff --git a/map/src/format.rs b/map/src/format.rs index 93abe262..394e93e2 100644 --- a/map/src/format.rs +++ b/map/src/format.rs @@ -1,9 +1,9 @@ use common; -use common::num::LeI16; use datafile::OnlyI32; use std::fmt; use std::mem; use std::ops; +use zerocopy::byteorder::little_endian; pub trait MapItem: OnlyI32 { fn version() -> i32; @@ -291,7 +291,7 @@ pub struct SpeedupTile { pub max_speed: u8, pub index: u8, pub padding: u8, - pub angle: LeI16, + pub angle: little_endian::I16, } #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] diff --git a/map/src/generate_format.py b/map/src/generate_format.py index 71d53390..01a5f919 100644 --- a/map/src/generate_format.py +++ b/map/src/generate_format.py @@ -51,11 +51,11 @@ header = """\ use common; -use common::num::LeI16; use datafile::OnlyI32; use std::fmt; use std::mem; use std::ops; +use zerocopy::byteorder::little_endian; pub trait MapItem: OnlyI32 { fn version() -> i32; @@ -343,7 +343,7 @@ pub max_speed: u8, pub index: u8, pub padding: u8, - pub angle: LeI16, + pub angle: little_endian::I16, } #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] diff --git a/map/src/lib.rs b/map/src/lib.rs index 819b3bf0..36cd3616 100644 --- a/map/src/lib.rs +++ b/map/src/lib.rs @@ -2,6 +2,7 @@ extern crate common; extern crate datafile; extern crate ndarray; +extern crate zerocopy; pub use reader::Error; pub use reader::Reader; diff --git a/net/Cargo.toml b/net/Cargo.toml index 611fda60..ba89f5c7 100644 --- a/net/Cargo.toml +++ b/net/Cargo.toml @@ -15,6 +15,8 @@ matches = "0.1.2" optional = "0.0.12" void = ">=0.0.4,<2.0.0" warn = ">=0.1.1,<0.3.0" +zerocopy = "0.7.32" +zerocopy-derive = "0.7.32" [dev-dependencies] hexdump = "0.1.1" diff --git a/net/src/lib.rs b/net/src/lib.rs index 1520e032..5235d687 100644 --- a/net/src/lib.rs +++ b/net/src/lib.rs @@ -18,6 +18,8 @@ extern crate matches; extern crate optional; extern crate void; extern crate warn; +extern crate zerocopy; +extern crate zerocopy_derive; pub mod collections; pub mod connection; diff --git a/net/src/protocol.rs b/net/src/protocol.rs index bffd8f40..c4eb44fe 100644 --- a/net/src/protocol.rs +++ b/net/src/protocol.rs @@ -3,6 +3,7 @@ use buffer; use buffer::with_buffer; use buffer::Buffer; use buffer::BufferRef; +use common::bytes::FromBytesExt as _; use common::num::Cast; use common::pretty; use huffman; @@ -13,6 +14,10 @@ use std::io::Write as _; use std::str; use warn::Ignore; use warn::Warn; +use zerocopy::AsBytes as _; +use zerocopy_derive::AsBytes; +use zerocopy_derive::FromBytes; +use zerocopy_derive::FromZeroes; pub const CHUNK_HEADER_SIZE: usize = 2; pub const CHUNK_HEADER_SIZE_VITAL: usize = 3; @@ -363,7 +368,7 @@ impl<'a> Packet<'a> { return false; } let (header, payload) = - unwrap_or_return!(PacketHeaderPacked::from_byte_slice(packet), false); + unwrap_or_return!(PacketHeaderPacked::ref_and_rest_from(packet), false); let header = header.unpack_warn(&mut Ignore); let ctrl = payload.first().copied(); header.flags & !PACKETFLAG_REQUEST_RESEND == PACKETFLAG_CONTROL @@ -373,7 +378,7 @@ impl<'a> Packet<'a> { if packet.len() > MAX_PACKETSIZE { return false; } - let (header, _) = unwrap_or_return!(PacketHeaderPacked::from_byte_slice(packet), false); + let (header, _) = unwrap_or_return!(PacketHeaderPacked::ref_and_rest_from(packet), false); let header = header.unpack_warn(&mut Ignore); header.flags & PACKETFLAG_CONNLESS == 0 && header.flags & PACKETFLAG_COMPRESSION != 0 } @@ -428,7 +433,7 @@ impl<'a> Packet<'a> { return Err(TooLong); } let (header, payload) = - unwrap_or_return!(PacketHeaderPacked::from_byte_slice(bytes), Err(TooShort)); + unwrap_or_return!(PacketHeaderPacked::ref_and_rest_from(bytes), Err(TooShort)); let header = header.unpack_warn(warn); if header.flags & PACKETFLAG_CONNLESS != 0 { if payload.len() < PADDING_SIZE_CONNLESS { @@ -445,7 +450,7 @@ impl<'a> Packet<'a> { let mut buffer = buffer.expect("read_panic_on_decompression called on compressed packet"); let decompressed = Packet::decompress(bytes, &mut buffer).map_err(|_| Compression)?; - let (_, payload) = PacketHeaderPacked::from_byte_slice(decompressed).unwrap(); + let (_, payload) = PacketHeaderPacked::ref_and_rest_from(decompressed).unwrap(); payload } else { payload @@ -584,7 +589,7 @@ impl<'a> Packet<'a> { ) -> Result<&'d [u8], huffman::DecompressionError> { assert!(buffer.remaining() >= MAX_PACKETSIZE); assert!(Packet::needs_decompression(packet)); - let (header, payload) = PacketHeaderPacked::from_byte_slice(packet) + let (header, payload) = PacketHeaderPacked::ref_and_rest_from(packet) .expect("packet passed to decompress too short for header"); let header = header.unpack_warn(&mut Ignore); assert!(header.flags & PACKETFLAG_CONNLESS == 0); @@ -706,7 +711,7 @@ impl<'a> ControlPacket<'a> { } #[repr(C, packed)] -#[derive(Copy, Clone)] +#[derive(AsBytes, Clone, Copy, FromBytes, FromZeroes)] pub struct PacketHeaderPacked { flags_padding_ack: u8, // u4 u2 u2 ack: u8, @@ -773,14 +778,14 @@ pub struct ChunkHeaderVital { } #[repr(C, packed)] -#[derive(Copy, Clone)] +#[derive(AsBytes, Clone, Copy, FromBytes, FromZeroes)] pub struct ChunkHeaderPacked { flags_size: u8, // u2 u6 padding_size: u8, // u4 u4 } #[repr(C, packed)] -#[derive(Copy, Clone)] +#[derive(AsBytes, Clone, Copy, FromBytes, FromZeroes)] pub struct ChunkHeaderVitalPacked { flags_size: u8, // u2 u6 sequence_size: u8, // u4 u4 @@ -796,12 +801,12 @@ where W: Warn, { let (raw_header, chunk_data_and_rest) = - unwrap_or_return!(ChunkHeaderPacked::from_byte_slice(data)); + unwrap_or_return!(ChunkHeaderPacked::ref_and_rest_from(data)); let header = raw_header.unpack_warn(&mut Ignore); Some(if header.flags & CHUNKFLAG_VITAL != 0 { let (header, chunk_data_and_rest_vital) = - unwrap_or_return!(ChunkHeaderVitalPacked::from_byte_slice(data)); + unwrap_or_return!(ChunkHeaderVitalPacked::ref_and_rest_from(data)); let header = header.unpack_warn(warn); (header.h, Some(header.sequence), chunk_data_and_rest_vital) } else { @@ -884,18 +889,12 @@ impl ChunkHeaderVital { } } -unsafe_boilerplate_packed!(PacketHeaderPacked, HEADER_SIZE, test_ph_size, test_ph_align); -unsafe_boilerplate_packed!( - ChunkHeaderPacked, - CHUNK_HEADER_SIZE, - test_ch_size, - test_ch_align -); -unsafe_boilerplate_packed!( +boilerplate_packed!(PacketHeaderPacked, HEADER_SIZE, test_ph_size); +boilerplate_packed!(ChunkHeaderPacked, CHUNK_HEADER_SIZE, test_ch_size); +boilerplate_packed!( ChunkHeaderVitalPacked, CHUNK_HEADER_SIZE_VITAL, - test_chv_size, - test_chv_align + test_chv_size ); #[cfg(test)] @@ -998,7 +997,7 @@ mod test { // Two bits must be zeroed (see doc/packet.md). let v0 = v.0 & 0b1111_0011; let bytes = &[v0, v.1, v.2]; - PacketHeaderPacked::from_bytes(bytes).unpack().pack().as_bytes() == bytes + PacketHeaderPacked::ref_from(bytes).unpack().pack().as_bytes() == bytes } fn chunk_header_roundtrip(flags: u8, size: u16, sequence: u16) -> bool { @@ -1022,8 +1021,8 @@ mod test { let bytes3 = &[v.0, v.1, v.2]; let bytes2_result = &[v.0, v.1 & 0b0000_1111]; let bytes3_result = &[v.0, v.1 | ((v.2 & 0b1100_0000) >> 2), v.2 | ((v.1 & 0b0011_0000) << 2)]; - ChunkHeaderPacked::from_bytes(bytes2).unpack().pack().as_bytes() == bytes2_result - && ChunkHeaderVitalPacked::from_bytes(bytes3).unpack().pack().as_bytes() == bytes3_result + ChunkHeaderPacked::ref_from(bytes2).unpack().pack().as_bytes() == bytes2_result + && ChunkHeaderVitalPacked::ref_from(bytes3).unpack().pack().as_bytes() == bytes3_result } fn packet_read_no_panic(data: Vec) -> bool { diff --git a/net/src/protocol7.rs b/net/src/protocol7.rs index 2c43b238..26791da7 100644 --- a/net/src/protocol7.rs +++ b/net/src/protocol7.rs @@ -1,6 +1,7 @@ use buffer::with_buffer; use buffer::Buffer; use buffer::BufferRef; +use common::bytes::FromBytesExt as _; use common::num::Cast; use common::pretty; use huffman; @@ -9,6 +10,10 @@ use std::cmp; use std::fmt; use warn::Ignore; use warn::Warn; +use zerocopy::AsBytes; +use zerocopy_derive::AsBytes; +use zerocopy_derive::FromBytes; +use zerocopy_derive::FromZeroes; pub const CHUNK_HEADER_SIZE: usize = 2; pub const CHUNK_HEADER_SIZE_VITAL: usize = 3; @@ -129,7 +134,7 @@ impl<'a> Packet<'a> { if packet.len() > MAX_PACKETSIZE { return false; } - let (header, _) = unwrap_or_return!(PacketHeaderPacked::from_byte_slice(packet), false); + let (header, _) = unwrap_or_return!(PacketHeaderPacked::ref_and_rest_from(packet), false); let header = header.unpack_warn(&mut Ignore); header.flags & PACKETFLAG_CONNLESS == 0 && header.flags & PACKETFLAG_COMPRESSION != 0 } @@ -174,11 +179,11 @@ impl<'a> Packet<'a> { return Err(TooLong); } let (header, payload) = - unwrap_or_return!(PacketHeaderPacked::from_byte_slice(bytes), Err(TooShort)); + unwrap_or_return!(PacketHeaderPacked::ref_and_rest_from(bytes), Err(TooShort)); let header = header.unpack_warn(warn); if header.flags & PACKETFLAG_CONNLESS != 0 { let (header, payload) = unwrap_or_return!( - PacketHeaderConnlessPacked::from_byte_slice(bytes), + PacketHeaderConnlessPacked::ref_and_rest_from(bytes), Err(TooShort) ); let header = header.unpack_warn(warn); @@ -200,7 +205,7 @@ impl<'a> Packet<'a> { let mut buffer = buffer.expect("read_panic_on_decompression called on compressed packet"); let decompressed = Packet::decompress(bytes, &mut buffer).map_err(|_| Compression)?; - let (_, payload) = PacketHeaderPacked::from_byte_slice(decompressed).unwrap(); + let (_, payload) = PacketHeaderPacked::ref_and_rest_from(decompressed).unwrap(); payload } else { payload @@ -320,7 +325,7 @@ impl<'a> Packet<'a> { ) -> Result<&'d [u8], huffman::DecompressionError> { assert!(buffer.remaining() >= MAX_PACKETSIZE); assert!(Packet::needs_decompression(packet)); - let (header, payload) = PacketHeaderPacked::from_byte_slice(packet) + let (header, payload) = PacketHeaderPacked::ref_and_rest_from(packet) .expect("packet passed to decompress too short for header"); let header = header.unpack_warn(&mut Ignore); assert!(header.flags & PACKETFLAG_CONNLESS == 0); @@ -434,7 +439,7 @@ impl<'a> Iterator for ChunksIter<'a> { impl<'a> ExactSizeIterator for ChunksIter<'a> {} #[repr(C, packed)] -#[derive(Copy, Clone)] +#[derive(AsBytes, Clone, Copy, FromBytes, FromZeroes)] pub struct PacketHeaderPacked { padding_flags_ack: u8, // u2 u4 u2 ack: u8, @@ -451,7 +456,7 @@ pub struct PacketHeader { } #[repr(C, packed)] -#[derive(Copy, Clone)] +#[derive(AsBytes, Clone, Copy, FromBytes, FromZeroes)] pub struct PacketHeaderConnlessPacked { padding_flags_version: u8, // u2 u4 u2 token: [u8; 4], @@ -563,14 +568,14 @@ pub struct ChunkHeaderVital { } #[repr(C, packed)] -#[derive(Copy, Clone)] +#[derive(AsBytes, Clone, Copy, FromBytes, FromZeroes)] pub struct ChunkHeaderPacked { flags_size: u8, // u2 u6 padding_size: u8, // u2 u6 } #[repr(C, packed)] -#[derive(Copy, Clone)] +#[derive(AsBytes, Clone, Copy, FromBytes, FromZeroes)] pub struct ChunkHeaderVitalPacked { flags_size: u8, // u2 u6 sequence_size: u8, // u2 u6 @@ -586,12 +591,12 @@ where W: Warn, { let (raw_header, chunk_data_and_rest) = - unwrap_or_return!(ChunkHeaderPacked::from_byte_slice(data)); + unwrap_or_return!(ChunkHeaderPacked::ref_and_rest_from(data)); let header = raw_header.unpack_warn(&mut Ignore); Some(if header.flags & CHUNKFLAG_VITAL != 0 { let (header, chunk_data_and_rest_vital) = - unwrap_or_return!(ChunkHeaderVitalPacked::from_byte_slice(data)); + unwrap_or_return!(ChunkHeaderVitalPacked::ref_and_rest_from(data)); let header = header.unpack_warn(warn); (header.h, Some(header.sequence), chunk_data_and_rest_vital) } else { @@ -671,29 +676,23 @@ impl ChunkHeaderVital { } } -unsafe_boilerplate_packed!(PacketHeaderPacked, HEADER_SIZE, test_ph_size, test_ph_align); -unsafe_boilerplate_packed!( +boilerplate_packed!(PacketHeaderPacked, HEADER_SIZE, test_ph_size); +boilerplate_packed!( PacketHeaderConnlessPacked, HEADER_SIZE_CONNLESS, - test_phc_size, - test_phc_align + test_phc_size ); -unsafe_boilerplate_packed!( - ChunkHeaderPacked, - CHUNK_HEADER_SIZE, - test_ch_size, - test_ch_align -); -unsafe_boilerplate_packed!( +boilerplate_packed!(ChunkHeaderPacked, CHUNK_HEADER_SIZE, test_ch_size); +boilerplate_packed!( ChunkHeaderVitalPacked, CHUNK_HEADER_SIZE_VITAL, - test_chv_size, - test_chv_align + test_chv_size ); #[cfg(test)] #[rustfmt::skip] mod test { + use common::bytes::FromBytesExt as _; use super::CHUNK_FLAGS_BITS; use super::CHUNK_SIZE_BITS; use super::ChunkHeader; @@ -716,6 +715,7 @@ mod test { use super::Warning; use warn::Panic; use warn::Warn; + use zerocopy::FromBytes as _; struct WarnVec<'a>(&'a mut Vec); @@ -807,7 +807,7 @@ mod test { // Two bits must be zeroed (see doc/packet7.md). let v0 = v.0 & 0b0011_1111; let bytes = &[v0, v.1, v.2, v.3, v.4, v.5, v.6]; - PacketHeaderPacked::from_bytes(bytes).unpack().pack().as_bytes() == bytes + PacketHeaderPacked::ref_from(bytes).unpack().pack().as_bytes() == bytes } fn chunk_header_roundtrip(flags: u8, size: u16, sequence: u16) -> bool { @@ -831,8 +831,8 @@ mod test { let bytes3 = &[v.0, v.1, v.2]; let bytes2_result = &[v.0, v.1 & 0b0011_1111]; let bytes3_result = &[v.0, v.1, v.2]; - ChunkHeaderPacked::from_bytes(bytes2).unpack().pack().as_bytes() == bytes2_result - && ChunkHeaderVitalPacked::from_bytes(bytes3).unpack().pack().as_bytes() == bytes3_result + ChunkHeaderPacked::ref_from(bytes2).unpack().pack().as_bytes() == bytes2_result + && ChunkHeaderVitalPacked::ref_from(bytes3).unpack().pack().as_bytes() == bytes3_result } } } diff --git a/serverbrowse/Cargo.toml b/serverbrowse/Cargo.toml index b84f77da..607b8311 100644 --- a/serverbrowse/Cargo.toml +++ b/serverbrowse/Cargo.toml @@ -12,3 +12,5 @@ logger = { path = "../logger/" } packer = { path = "../packer/" } time = "0.1.25" warn = "0.2.2" +zerocopy = "0.7.32" +zerocopy-derive = "0.7.32" diff --git a/serverbrowse/src/lib.rs b/serverbrowse/src/lib.rs index 8bcf0150..f6ab7a08 100644 --- a/serverbrowse/src/lib.rs +++ b/serverbrowse/src/lib.rs @@ -5,5 +5,7 @@ extern crate common; extern crate log; extern crate packer; extern crate warn; +extern crate zerocopy; +extern crate zerocopy_derive; pub mod protocol; diff --git a/serverbrowse/src/protocol.rs b/serverbrowse/src/protocol.rs index 4ef584b4..9cf29fca 100644 --- a/serverbrowse/src/protocol.rs +++ b/serverbrowse/src/protocol.rs @@ -1,9 +1,6 @@ use arrayvec::ArrayString; use common; -use common::num::BeU16; -use common::num::BeU32; use common::num::Cast; -use common::num::LeU16; use common::str::truncated_arraystring; use packer::Unpacker; use std::default::Default; @@ -13,6 +10,12 @@ use std::net::Ipv4Addr; use std::net::Ipv6Addr; use std::str; use warn::Ignore; +use zerocopy::byteorder::big_endian; +use zerocopy::byteorder::little_endian; +use zerocopy::FromZeroes; +use zerocopy_derive::AsBytes; +use zerocopy_derive::FromBytes; +use zerocopy_derive::FromZeroes; const PLAYER_MAX_NAME_LENGTH: usize = 16 - 1; const PLAYER_MAX_CLAN_LENGTH: usize = 12 - 1; @@ -71,11 +74,11 @@ pub fn request_list_5() -> [u8; 14] { pub fn request_list_6() -> [u8; 14] { *REQUEST_LIST_6 } -pub fn request_list_7(own_token: u32, their_token: u32) -> [u8; 17] { +pub fn request_list_7(own_token: Token7, their_token: Token7) -> [u8; 17] { let mut request = [0; 17]; request.copy_from_slice(REQUEST_LIST_7); - request[1..5].copy_from_slice(BeU32::from_u32(their_token).as_bytes()); - request[5..9].copy_from_slice(BeU32::from_u32(own_token).as_bytes()); + request[1..5].copy_from_slice(&their_token.0); + request[5..9].copy_from_slice(&own_token.0); request } pub fn request_list_5_nobackcompat() -> [u8; 30] { @@ -90,7 +93,7 @@ pub fn request_list_6_nobackcompat() -> [u8; 30] { request[14..].copy_from_slice(NO_BACKCOMPAT); request } -pub fn request_list_7_nobackcompat(own_token: u32, their_token: u32) -> [u8; 33] { +pub fn request_list_7_nobackcompat(own_token: Token7, their_token: Token7) -> [u8; 33] { let mut request = [0; 33]; request[..17].copy_from_slice(&request_list_7(own_token, their_token)); request[17..].copy_from_slice(NO_BACKCOMPAT); @@ -118,22 +121,22 @@ pub fn request_info_6_ex(challenge: u32) -> [u8; 15] { request[HEADER_LEN] = ((challenge & 0x0000_00ff) >> 0) as u8; request } -pub fn request_token_7(own_token: u32) -> [u8; 520] { +pub fn request_token_7(own_token: Token7) -> [u8; 520] { let mut request = [0; 520]; request[..8].copy_from_slice(TOKEN_7); - request[3..7].copy_from_slice(BeU32::from_u32(0xffff_ffff).as_bytes()); - request[8..12].copy_from_slice(BeU32::from_u32(own_token).as_bytes()); + request[3..7].copy_from_slice(&[0xff, 0xff, 0xff, 0xff]); + request[8..12].copy_from_slice(&own_token.0); request } -pub fn request_info_7(own_token: u32, their_token: u32, challenge: u8) -> [u8; 18] { +pub fn request_info_7(own_token: Token7, their_token: Token7, challenge: u8) -> [u8; 18] { assert!( challenge & 0x3f == challenge, "only the lower 6 bits of challenge can be used with this implementation" ); let mut request = [0; 18]; request[..17].copy_from_slice(REQUEST_INFO_7); - request[1..5].copy_from_slice(BeU32::from_u32(their_token).as_bytes()); - request[5..9].copy_from_slice(BeU32::from_u32(own_token).as_bytes()); + request[1..5].copy_from_slice(&their_token.0); + request[5..9].copy_from_slice(&own_token.0); request[17] = challenge; request } @@ -147,14 +150,14 @@ pub fn request_count_nobackcompat() -> [u8; 30] { request[14..].copy_from_slice(NO_BACKCOMPAT); request } -pub fn request_count_7(own_token: u32, their_token: u32) -> [u8; 17] { +pub fn request_count_7(own_token: Token7, their_token: Token7) -> [u8; 17] { let mut request = [0; 17]; request.copy_from_slice(REQUEST_COUNT_7); - request[1..5].copy_from_slice(BeU32::from_u32(their_token).as_bytes()); - request[5..9].copy_from_slice(BeU32::from_u32(own_token).as_bytes()); + request[1..5].copy_from_slice(&their_token.0); + request[5..9].copy_from_slice(&own_token.0); request } -pub fn request_count_7_nobackcompat(own_token: u32, their_token: u32) -> [u8; 33] { +pub fn request_count_7_nobackcompat(own_token: Token7, their_token: Token7) -> [u8; 33] { let mut request = [0; 33]; request[..17].copy_from_slice(&request_count_7(own_token, their_token)); request[17..].copy_from_slice(NO_BACKCOMPAT); @@ -168,6 +171,26 @@ fn request_info(header: Header, challenge: u8) -> [u8; 15] { request } +#[derive(AsBytes, Clone, Copy, FromBytes, FromZeroes)] +#[repr(transparent)] +pub struct Token7(pub [u8; 4]); + +impl fmt::Debug for Token7 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{:02x}{:02x}{:02x}{:02x}", + self.0[0], self.0[1], self.0[2], self.0[3] + ) + } +} + +impl fmt::Display for Token7 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + #[derive(Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct ClientInfo { pub name: ArrayString<[u8; PLAYER_MAX_NAME_LENGTH]>, @@ -676,19 +699,19 @@ pub struct Info6ExResponse<'a>(pub &'a [u8]); #[derive(Copy, Clone)] pub struct Info6ExMoreResponse<'a>(pub &'a [u8]); #[derive(Copy, Clone)] -pub struct Info7Response<'a>(pub u32, pub u32, pub &'a [u8]); +pub struct Info7Response<'a>(pub Token7, pub Token7, pub &'a [u8]); #[derive(Copy, Clone)] pub struct CountResponse(pub u16); #[derive(Copy, Clone)] -pub struct Count7Response(pub u32, pub u32, pub u16); +pub struct Count7Response(pub Token7, pub Token7, pub u16); #[derive(Copy, Clone)] pub struct List5Response<'a>(pub &'a [Addr5Packed]); #[derive(Copy, Clone)] pub struct List6Response<'a>(pub &'a [Addr6Packed]); #[derive(Copy, Clone)] -pub struct List7Response<'a>(pub u32, pub u32, pub &'a [Addr6Packed]); +pub struct List7Response<'a>(pub Token7, pub Token7, pub &'a [Addr6Packed]); #[derive(Copy, Clone)] -pub struct Token7Response(pub u32, pub u32); +pub struct Token7Response(pub Token7, pub Token7); #[derive(Copy, Clone)] pub enum Response<'a> { @@ -724,9 +747,11 @@ fn parse_list6(data: &[u8]) -> &[Addr6Packed] { unsafe { common::slice::transmute(data) } } -fn parse_token(data: &[u8]) -> Option { - let (token, _) = BeU32::from_byte_slice(data)?; - Some(token.to_u32()) +fn parse_token7(data: &[u8]) -> Option { + if data.len() < 4 { + return None; + } + Some(Token7([data[0], data[1], data[2], data[3]])) } fn parse_count(data: &[u8]) -> Option { @@ -746,18 +771,17 @@ pub fn parse_response(data: &[u8]) -> Option { return None; } let mut header = [0; 8]; - let mut own_token = [0; 4]; + let mut own_token = Token7::new_zeroed(); let payload = &data[8..]; header.copy_from_slice(&data[..8]); - own_token.copy_from_slice(&header[3..7]); - let own_token = BeU32::from_bytes(&own_token).to_u32(); + own_token.0.copy_from_slice(&header[3..7]); for b in &mut header[3..7] { *b = 0xff; } return match &header { TOKEN_7 => Some(Response::Token7(Token7Response( own_token, - parse_token(payload)?, + parse_token7(payload)?, ))), _ => None, }; @@ -767,14 +791,12 @@ pub fn parse_response(data: &[u8]) -> Option { return None; } let mut header = [0; 17]; - let mut own_token = [0; 4]; - let mut their_token = [0; 4]; + let mut own_token = Token7::new_zeroed(); + let mut their_token = Token7::new_zeroed(); let payload = &data[17..]; header.copy_from_slice(&data[..17]); - own_token.copy_from_slice(&header[1..5]); - their_token.copy_from_slice(&header[5..9]); - let own_token = BeU32::from_bytes(&own_token).to_u32(); - let their_token = BeU32::from_bytes(&their_token).to_u32(); + own_token.0.copy_from_slice(&header[1..5]); + their_token.0.copy_from_slice(&header[5..9]); for b in &mut header[1..9] { *b = 0xff; } @@ -860,14 +882,14 @@ impl fmt::Debug for IpAddr { #[repr(C, packed)] pub struct Addr5Packed { ip_address: [u8; 4], - port: LeU16, + port: little_endian::U16, } #[derive(Clone, Copy)] #[repr(C, packed)] pub struct Addr6Packed { ip_address: [u8; 16], - port: BeU16, + port: big_endian::U16, } // --------------------------------------- @@ -896,7 +918,7 @@ impl Addr5Packed { let Addr5Packed { ip_address, port } = self; Addr { ip_address: IpAddr::new_v4(ip_address[0], ip_address[1], ip_address[2], ip_address[3]), - port: port.to_u16(), + port: port.get(), } } } @@ -911,16 +933,16 @@ impl Addr6Packed { let Addr6Packed { ip_address, port } = self; let (maybe_ipv4_mapping, ipv4_address) = ip_address.split_at(IPV4_MAPPING.len()); let new_address = if maybe_ipv4_mapping != IPV4_MAPPING { - let ip_address: [BeU16; 8] = unsafe { mem::transmute(ip_address) }; + let ip_address: [big_endian::U16; 8] = unsafe { mem::transmute(ip_address) }; IpAddr::new_v6( - ip_address[0].to_u16(), - ip_address[1].to_u16(), - ip_address[2].to_u16(), - ip_address[3].to_u16(), - ip_address[4].to_u16(), - ip_address[5].to_u16(), - ip_address[6].to_u16(), - ip_address[7].to_u16(), + ip_address[0].get(), + ip_address[1].get(), + ip_address[2].get(), + ip_address[3].get(), + ip_address[4].get(), + ip_address[5].get(), + ip_address[6].get(), + ip_address[7].get(), ) } else { IpAddr::new_v4( @@ -932,7 +954,7 @@ impl Addr6Packed { }; Addr { ip_address: new_address, - port: port.to_u16(), + port: port.get(), } } } diff --git a/stats_browser/Cargo.toml b/stats_browser/Cargo.toml index 8100269b..1c89260e 100644 --- a/stats_browser/Cargo.toml +++ b/stats_browser/Cargo.toml @@ -25,3 +25,4 @@ serde_json = "1.0.7" serverbrowse = { path = "../serverbrowse/" } time = "0.1.34" uuid = { version = "0.8.1", features = ["serde", "v4", "v5"] } +zerocopy = "0.7.32" diff --git a/stats_browser/src/entry.rs b/stats_browser/src/entry.rs index 91860a19..616c1ead 100644 --- a/stats_browser/src/entry.rs +++ b/stats_browser/src/entry.rs @@ -1,5 +1,6 @@ use serverbrowse::protocol::PartialServerInfo; use serverbrowse::protocol::ServerInfo; +use serverbrowse::protocol::Token7; use std::collections::HashSet; use std::fmt; @@ -7,9 +8,12 @@ use std::fmt; use addr::Addr; use addr::ServerAddr; use arrayvec::ArrayVec; +use common::bytes::AsBytesExt; +use common::bytes::FromBytesExt; use rand; use rand::distributions; use rand::distributions::Distribution; +use zerocopy::byteorder::big_endian; /// Describes a master server. #[derive(Clone)] @@ -115,30 +119,42 @@ impl ServerEntry { /// integer), DDNet tokens can use 24 bit, low-level 0.7 tokens can even use 32 /// bit. #[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct Token(u32); +pub struct Token(big_endian::U32); impl fmt::Display for Token { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::LowerHex::fmt(&self.u32(), f) + write!(f, "{:08x}", self.u32()) } } impl Token { + /// Creates a new token from a serverbrowse token. + pub fn from_serverbrowse7(v: Token7) -> Token { + Token(big_endian::U32::from_array(v.0)) + } /// Creates a new token from a 32-bit integer. pub fn from_u32(v: u32) -> Token { - Token(v) + Token(big_endian::U32::new(v)) } /// Retrieves the 32 bit token. pub fn u32(self) -> u32 { - self.0 + self.0.get() } /// Retrieves the 24 bit token. pub fn u24(self) -> u32 { - self.0 & 0x00ff_ffff + self.0.get() & 0x00ff_ffff } /// Retrieves the 8 bit token. pub fn u8(self) -> u8 { - self.0 as u8 + self.0.get() as u8 + } + /// Retrieves the token as serverbrowse token. + pub fn serverbrowse7(self) -> Token7 { + Token7(self.bytes()) + } + /// Retrieves the token as four bytes. + pub fn bytes(self) -> [u8; 4] { + *self.0.as_byte_array() } } diff --git a/stats_browser/src/lib.rs b/stats_browser/src/lib.rs index 4e71bd85..be73fe24 100644 --- a/stats_browser/src/lib.rs +++ b/stats_browser/src/lib.rs @@ -12,6 +12,7 @@ extern crate serde; extern crate serde_derive; extern crate time as rust_time; extern crate uuid; +extern crate zerocopy; #[macro_use] extern crate common; diff --git a/stats_browser/src/stats_browser.rs b/stats_browser/src/stats_browser.rs index 46ca9b49..a61b57d5 100644 --- a/stats_browser/src/stats_browser.rs +++ b/stats_browser/src/stats_browser.rs @@ -227,7 +227,11 @@ impl<'a> StatsBrowser<'a> { let mut send = |data: &[u8]| socket.send_to(data, master.addr_7.unwrap()).unwrap(); debug!("Requesting token from {}", master.domain); - if send(&protocol::request_token_7(master.own_token.unwrap().u32())).would_block() { + if send(&protocol::request_token_7( + master.own_token.unwrap().serverbrowse7(), + )) + .would_block() + { debug!("Failed to send count or list request, would block"); return Err(()); } @@ -277,7 +281,9 @@ impl<'a> StatsBrowser<'a> { let would_block = match server_addr.version { ProtocolVersion::V5 => send(&protocol::request_info_5(token.u8())).would_block(), ProtocolVersion::V6 => send(&protocol::request_info_6_ex(token.u24())).would_block(), - ProtocolVersion::V7 => send(&protocol::request_token_7(token.u32())).would_block(), + ProtocolVersion::V7 => { + send(&protocol::request_token_7(token.serverbrowse7())).would_block() + } }; if would_block { @@ -342,13 +348,13 @@ impl<'a> StatsBrowser<'a> { if !master.nobackcompat { debug!("Requesting 0.7 count and list from {}", master.domain); if send(&protocol::request_count_7( - master.own_token.unwrap().u32(), - their_token.u32(), + master.own_token.unwrap().serverbrowse7(), + their_token.serverbrowse7(), )) .would_block() || send(&protocol::request_list_7( - master.own_token.unwrap().u32(), - their_token.u32(), + master.own_token.unwrap().serverbrowse7(), + their_token.serverbrowse7(), )) .would_block() { @@ -360,13 +366,13 @@ impl<'a> StatsBrowser<'a> { master.domain ); if send(&protocol::request_count_7_nobackcompat( - master.own_token.unwrap().u32(), - their_token.u32(), + master.own_token.unwrap().serverbrowse7(), + their_token.serverbrowse7(), )) .would_block() || send(&protocol::request_list_7_nobackcompat( - master.own_token.unwrap().u32(), - their_token.u32(), + master.own_token.unwrap().serverbrowse7(), + their_token.serverbrowse7(), )) .would_block() { @@ -441,7 +447,7 @@ impl<'a> StatsBrowser<'a> { if !server .missing_resp .iter() - .any(|&t| t.u32() == own_token.u32()) + .any(|&t| t.bytes() == own_token.bytes()) { if server.num_invalid_token < config::MAX_INVALID_TOKEN { warn!("Received info with wrong token from {}", from); @@ -455,8 +461,8 @@ impl<'a> StatsBrowser<'a> { let mut send = |data: &[u8]| socket.send_to(data, from.addr).unwrap(); if send(&protocol::request_info_7( - own_token.u32(), - their_token.u32(), + own_token.serverbrowse7(), + their_token.serverbrowse7(), 0, )) .would_block() @@ -479,7 +485,7 @@ impl<'a> StatsBrowser<'a> { } }; if let Some(pt) = protocol_token { - if !server.missing_resp.iter().any(|&t| t.u32() == pt.u32()) { + if !server.missing_resp.iter().any(|&t| t.bytes() == pt.bytes()) { if server.num_invalid_resp < config::MAX_INVALID_RESP { warn!("Received info with wrong 0.7 token from {}", from); } @@ -618,8 +624,8 @@ impl<'a> StatsBrowser<'a> { fn process_packet(&mut self, from: Addr, data: &[u8]) { match protocol::parse_response(data) { Some(Response::Token7(Token7Response(own_token, their_token))) => { - let own_token = Token::from_u32(own_token); - let their_token = Token::from_u32(their_token); + let own_token = Token::from_serverbrowse7(own_token); + let their_token = Token::from_serverbrowse7(their_token); let mut found = false; if let Some(id) = self.get_master_id(from) { found = true; @@ -651,7 +657,7 @@ impl<'a> StatsBrowser<'a> { } }, Some(Response::Count7(Count7Response(own_token, _, count))) => { - let own_token = Token::from_u32(own_token); + let own_token = Token::from_serverbrowse7(own_token); if let Some(id) = self.get_master_id(from) { if Some(own_token) == self.master_servers[id].own_token { self.process_count_7(id, count); @@ -711,7 +717,7 @@ impl<'a> StatsBrowser<'a> { } }, Some(Response::List7(List7Response(own_token, _, servers))) => { - let own_token = Token::from_u32(own_token); + let own_token = Token::from_serverbrowse7(own_token); if let Some(id) = self.get_master_id(from) { if Some(own_token) == self.master_servers[id].own_token { self.process_list( @@ -774,7 +780,7 @@ impl<'a> StatsBrowser<'a> { } Some(Response::Info7(partial)) => { let Info7Response(own_token, _, raw_data) = partial; - let own_token = Token::from_u32(own_token); + let own_token = Token::from_serverbrowse7(own_token); self.process_info( ServerAddr::new(ProtocolVersion::V7, from), Some(own_token), diff --git a/tools/src/bin/servercount7.rs b/tools/src/bin/servercount7.rs index bdee9b16..f9674bc7 100644 --- a/tools/src/bin/servercount7.rs +++ b/tools/src/bin/servercount7.rs @@ -8,6 +8,7 @@ extern crate tools; use serverbrowse::protocol as browse_protocol; use serverbrowse::protocol::Count7Response; use serverbrowse::protocol::Response; +use serverbrowse::protocol::Token7; use serverbrowse::protocol::Token7Response; use std::net::SocketAddr; @@ -19,7 +20,7 @@ fn do_(socket: UdpSocket, addr: SocketAddr) { let mut buf = [0; BUFSIZE]; socket - .send_to(&browse_protocol::request_token_7(0), addr) + .send_to(&browse_protocol::request_token_7(Token7([0; 4])), addr) .unwrap(); loop { @@ -32,10 +33,13 @@ fn do_(socket: UdpSocket, addr: SocketAddr) { continue; } match browse_protocol::parse_response(&buf[..len]) { - Some(Response::Token7(Token7Response(0, their_token))) => { - info!("token={:08x}", their_token); + Some(Response::Token7(Token7Response(Token7([0, 0, 0, 0]), their_token))) => { + info!("token={}", their_token); socket - .send_to(&browse_protocol::request_count_7(0, their_token), addr) + .send_to( + &browse_protocol::request_count_7(Token7([0; 4]), their_token), + addr, + ) .unwrap(); break; } diff --git a/tools/src/bin/serverinfo7.rs b/tools/src/bin/serverinfo7.rs index e0aaaec0..ba367969 100644 --- a/tools/src/bin/serverinfo7.rs +++ b/tools/src/bin/serverinfo7.rs @@ -7,6 +7,7 @@ extern crate tools; use serverbrowse::protocol as browse_protocol; use serverbrowse::protocol::Response; +use serverbrowse::protocol::Token7; use serverbrowse::protocol::Token7Response; use std::net::SocketAddr; @@ -18,7 +19,7 @@ fn do_(socket: UdpSocket, addr: SocketAddr) { let mut buf = [0; BUFSIZE]; socket - .send_to(&browse_protocol::request_token_7(0), addr) + .send_to(&browse_protocol::request_token_7(Token7([0; 4])), addr) .unwrap(); loop { @@ -31,10 +32,13 @@ fn do_(socket: UdpSocket, addr: SocketAddr) { continue; } match browse_protocol::parse_response(&buf[..len]) { - Some(Response::Token7(Token7Response(0, their_token))) => { - info!("token={:08x}", their_token); + Some(Response::Token7(Token7Response(Token7([0, 0, 0, 0]), their_token))) => { + info!("token={}", their_token); socket - .send_to(&browse_protocol::request_info_7(0, their_token, 0), addr) + .send_to( + &browse_protocol::request_info_7(Token7([0; 4]), their_token, 0), + addr, + ) .unwrap(); break; } diff --git a/tools/src/bin/serverlist7.rs b/tools/src/bin/serverlist7.rs index 53b767b2..2c488898 100644 --- a/tools/src/bin/serverlist7.rs +++ b/tools/src/bin/serverlist7.rs @@ -8,6 +8,7 @@ extern crate tools; use serverbrowse::protocol as browse_protocol; use serverbrowse::protocol::List7Response; use serverbrowse::protocol::Response; +use serverbrowse::protocol::Token7; use serverbrowse::protocol::Token7Response; use std::net::SocketAddr; @@ -19,7 +20,7 @@ fn do_(socket: UdpSocket, addr: SocketAddr) { let mut buf = [0; BUFSIZE]; socket - .send_to(&browse_protocol::request_token_7(0), addr) + .send_to(&browse_protocol::request_token_7(Token7([0; 4])), addr) .unwrap(); loop { @@ -32,10 +33,13 @@ fn do_(socket: UdpSocket, addr: SocketAddr) { continue; } match browse_protocol::parse_response(&buf[..len]) { - Some(Response::Token7(Token7Response(0, their_token))) => { - info!("token={:08x}", their_token); + Some(Response::Token7(Token7Response(Token7([0, 0, 0, 0]), their_token))) => { + info!("token={}", their_token); socket - .send_to(&browse_protocol::request_list_7(0, their_token), addr) + .send_to( + &browse_protocol::request_list_7(Token7([0; 4]), their_token), + addr, + ) .unwrap(); break; } diff --git a/wireshark-dissector/Cargo.toml b/wireshark-dissector/Cargo.toml index 561d0230..8a8385db 100644 --- a/wireshark-dissector/Cargo.toml +++ b/wireshark-dissector/Cargo.toml @@ -20,6 +20,7 @@ serde_json = "1.0.7" uuid = "0.8.1" warn = "0.2.2" wireshark-dissector-sys = { path = "sys" } +zerocopy = "0.7.32" [build-dependencies] cc = "1.0.67" diff --git a/wireshark-dissector/src/lib.rs b/wireshark-dissector/src/lib.rs index e8295a73..881ee084 100644 --- a/wireshark-dissector/src/lib.rs +++ b/wireshark-dissector/src/lib.rs @@ -13,6 +13,7 @@ extern crate serde_json; extern crate uuid; extern crate warn; extern crate wireshark_dissector_sys as sys; +extern crate zerocopy; mod format; mod intern; diff --git a/wireshark-dissector/src/spec.rs b/wireshark-dissector/src/spec.rs index 1fc00bc7..36d80661 100644 --- a/wireshark-dissector/src/spec.rs +++ b/wireshark-dissector/src/spec.rs @@ -6,7 +6,6 @@ use anyhow::bail; use anyhow::Context as _; use arrayvec::ArrayVec; use common::digest; -use common::num::BeU16; use common::num::Cast; use common::pretty::AlmostString; use format::Bitfield; @@ -830,7 +829,7 @@ impl Type { } BeUint16(i) => { let v = p.read_raw(2).map_err(|_| ())?; - let v = BeU16::from_bytes(&[v[0], v[1]]).to_u16(); + let v = u16::from_be_bytes([v[0], v[1]]); sys::proto_tree_add_uint_format( tree, i.id.get(), diff --git a/wireshark-dissector/src/tw.rs b/wireshark-dissector/src/tw.rs index 5b8e0e06..7c66b5e2 100644 --- a/wireshark-dissector/src/tw.rs +++ b/wireshark-dissector/src/tw.rs @@ -20,6 +20,7 @@ use std::os::raw::c_void; use std::ptr; use std::slice; use warn::Ignore; +use zerocopy::FromBytes; const SERIALIZED_SPEC: &'static str = include_str!("../../gamenet/generate/spec/ddnet-17.2.1.json"); @@ -55,7 +56,7 @@ static mut HF_CHUNK_HEADER_SEQ: c_int = -1; static mut SPEC: Option = None; fn unpack_header(data: &[u8]) -> Option { - let (raw_header, _) = protocol::PacketHeaderPacked::from_byte_slice(data)?; + let raw_header = protocol::PacketHeaderPacked::ref_from_prefix(data)?; Some(raw_header.unpack_warn(&mut Ignore)) } diff --git a/wireshark-dissector/src/tw7.rs b/wireshark-dissector/src/tw7.rs index 949193d4..f6e2870a 100644 --- a/wireshark-dissector/src/tw7.rs +++ b/wireshark-dissector/src/tw7.rs @@ -20,6 +20,7 @@ use std::os::raw::c_void; use std::ptr; use std::slice; use warn::Ignore; +use zerocopy::FromBytes; const SERIALIZED_SPEC: &'static str = include_str!("../../gamenet/generate/spec/teeworlds-0.7.5.json"); @@ -57,12 +58,12 @@ static mut HF_CHUNK_HEADER_SEQ: c_int = -1; static mut SPEC: Option = None; fn unpack_header(data: &[u8]) -> Option { - let (raw_header, _) = protocol::PacketHeaderPacked::from_byte_slice(data)?; + let raw_header = protocol::PacketHeaderPacked::ref_from_prefix(data)?; Some(raw_header.unpack_warn(&mut Ignore)) } fn unpack_header_connless(data: &[u8]) -> Option { - let (raw_header, _) = protocol::PacketHeaderConnlessPacked::from_byte_slice(data)?; + let raw_header = protocol::PacketHeaderConnlessPacked::ref_from_prefix(data)?; Some(raw_header.unpack_warn(&mut Ignore)) }