Skip to content

Commit d134832

Browse files
authored
Consolidate unsafe byte conversions in valence_nbt (#465)
# Objective When using valence_nbt, it's often necessary to convert to and from slices and vecs of `i8` and `u8`. But this requires unsafe code if you want to avoid copying things. # Solution Expose the following functions in valence_nbt: - `u8_vec_into_i8_vec(vec: Vec<u8>) -> Vec<i8>` - `i8_vec_into_u8_vec(vec: Vec<i8>) -> Vec<u8>` - `u8_slice_as_i8_slice(slice: &[u8]) -> &[i8]` - `i8_slice_as_u8_slice(slice: &[i8]) -> &[u8]` We've also made use of these functions ourselves to reduce the total amount of unsafe code. Should also help in #263
1 parent 7186073 commit d134832

File tree

6 files changed

+56
-64
lines changed

6 files changed

+56
-64
lines changed

crates/valence_nbt/src/binary/decode.rs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
use std::io::Write;
2-
use std::slice;
32

43
use byteorder::{BigEndian, WriteBytesExt};
54

65
use super::{modified_utf8, Error, Result};
76
use crate::tag::Tag;
8-
use crate::{Compound, List, Value};
7+
use crate::{i8_slice_as_u8_slice, Compound, List, Value};
98

109
impl Compound {
1110
/// Encodes uncompressed NBT binary data to the provided writer.
@@ -146,10 +145,7 @@ impl<W: Write> EncodeState<W> {
146145
}
147146
}
148147

149-
// SAFETY: i8 has the same layout as u8.
150-
let bytes = unsafe { slice::from_raw_parts(bytes.as_ptr() as *const u8, bytes.len()) };
151-
152-
Ok(self.writer.write_all(bytes)?)
148+
Ok(self.writer.write_all(i8_slice_as_u8_slice(bytes))?)
153149
}
154150

155151
fn write_string(&mut self, s: &str) -> Result<()> {
@@ -197,10 +193,7 @@ impl<W: Write> EncodeState<W> {
197193
}
198194
}
199195

200-
// SAFETY: i8 has the same layout as u8.
201-
let bytes = unsafe { slice::from_raw_parts(bl.as_ptr() as *const u8, bl.len()) };
202-
203-
Ok(self.writer.write_all(bytes)?)
196+
Ok(self.writer.write_all(i8_slice_as_u8_slice(bl))?)
204197
}
205198
List::Short(sl) => self.write_list(sl, Tag::Short, |st, s| st.write_short(*s)),
206199
List::Int(il) => self.write_list(il, Tag::Int, |st, i| st.write_int(*i)),

crates/valence_nbt/src/lib.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
clippy::dbg_macro
1818
)]
1919

20+
use std::mem::ManuallyDrop;
21+
2022
pub use compound::Compound;
2123
pub use tag::Tag;
2224
pub use value::{List, Value};
@@ -75,3 +77,39 @@ macro_rules! compound {
7577
])
7678
}
7779
}
80+
81+
/// Converts a `Vec<u8>` into a `Vec<i8>` without cloning.
82+
#[inline]
83+
pub fn u8_vec_into_i8_vec(vec: Vec<u8>) -> Vec<i8> {
84+
// SAFETY: Layouts of u8 and i8 are the same and we're being careful not to drop
85+
// the original vec after calling Vec::from_raw_parts.
86+
unsafe {
87+
let mut vec = ManuallyDrop::new(vec);
88+
Vec::from_raw_parts(vec.as_mut_ptr() as *mut i8, vec.len(), vec.capacity())
89+
}
90+
}
91+
92+
/// Converts a `Vec<i8>` into a `Vec<u8>` without cloning.
93+
#[inline]
94+
pub fn i8_vec_into_u8_vec(vec: Vec<i8>) -> Vec<u8> {
95+
// SAFETY: Layouts of u8 and i8 are the same and we're being careful not to drop
96+
// the original vec after calling Vec::from_raw_parts.
97+
unsafe {
98+
let mut vec = ManuallyDrop::new(vec);
99+
Vec::from_raw_parts(vec.as_mut_ptr() as *mut u8, vec.len(), vec.capacity())
100+
}
101+
}
102+
103+
/// Converts a `&[u8]` into a `&[i8]`.
104+
#[inline]
105+
pub fn u8_slice_as_i8_slice(slice: &[u8]) -> &[i8] {
106+
// SAFETY: i8 has the same layout as u8.
107+
unsafe { std::slice::from_raw_parts(slice.as_ptr() as *const i8, slice.len()) }
108+
}
109+
110+
/// Converts a `&[i8]` into a `&[u8]`.
111+
#[inline]
112+
pub fn i8_slice_as_u8_slice(slice: &[i8]) -> &[u8] {
113+
// SAFETY: i8 has the same layout as u8.
114+
unsafe { std::slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len()) }
115+
}

crates/valence_nbt/src/serde.rs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use std::fmt;
2-
use std::mem::ManuallyDrop;
32

43
pub use ser::*;
54
use thiserror::Error;
@@ -38,23 +37,3 @@ impl serde::ser::Error for Error {
3837
Self::new(format!("{msg}"))
3938
}
4039
}
41-
42-
#[inline]
43-
fn u8_vec_to_i8_vec(vec: Vec<u8>) -> Vec<i8> {
44-
// SAFETY: Layouts of u8 and i8 are the same and we're being careful not to drop
45-
// the original vec after calling Vec::from_raw_parts.
46-
unsafe {
47-
let mut vec = ManuallyDrop::new(vec);
48-
Vec::from_raw_parts(vec.as_mut_ptr() as *mut i8, vec.len(), vec.capacity())
49-
}
50-
}
51-
52-
#[inline]
53-
fn i8_vec_to_u8_vec(vec: Vec<i8>) -> Vec<u8> {
54-
// SAFETY: Layouts of u8 and i8 are the same and we're being careful not to drop
55-
// the original vec after calling Vec::from_raw_parts.
56-
unsafe {
57-
let mut vec = ManuallyDrop::new(vec);
58-
Vec::from_raw_parts(vec.as_mut_ptr() as *mut u8, vec.len(), vec.capacity())
59-
}
60-
}

crates/valence_nbt/src/serde/de.rs

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
use std::{fmt, slice};
1+
use std::fmt;
22

33
use serde::de::value::{MapAccessDeserializer, MapDeserializer, SeqAccessDeserializer};
44
use serde::de::{self, IntoDeserializer, SeqAccess, Visitor};
55
use serde::{forward_to_deserialize_any, Deserialize, Deserializer};
66

77
use super::Error;
8-
use crate::serde::{i8_vec_to_u8_vec, u8_vec_to_i8_vec};
9-
use crate::{Compound, List, Value};
8+
use crate::{i8_vec_into_u8_vec, u8_slice_as_i8_slice, u8_vec_into_i8_vec, Compound, List, Value};
109

1110
impl<'de> Deserialize<'de> for Value {
1211
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
@@ -117,17 +116,14 @@ impl<'de> Deserialize<'de> for Value {
117116
where
118117
E: de::Error,
119118
{
120-
let slice: &[i8] =
121-
unsafe { slice::from_raw_parts(v.as_ptr() as *const i8, v.len()) };
122-
123-
Ok(Value::ByteArray(slice.into()))
119+
Ok(Value::ByteArray(u8_slice_as_i8_slice(v).into()))
124120
}
125121

126122
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
127123
where
128124
E: de::Error,
129125
{
130-
Ok(Value::ByteArray(u8_vec_to_i8_vec(v)))
126+
Ok(Value::ByteArray(u8_vec_into_i8_vec(v)))
131127
}
132128

133129
fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
@@ -190,17 +186,14 @@ impl<'de> Deserialize<'de> for List {
190186
where
191187
E: de::Error,
192188
{
193-
Ok(List::Byte(u8_vec_to_i8_vec(v)))
189+
Ok(List::Byte(u8_vec_into_i8_vec(v)))
194190
}
195191

196192
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
197193
where
198194
E: de::Error,
199195
{
200-
let bytes: &[i8] =
201-
unsafe { slice::from_raw_parts(v.as_ptr() as *const i8, v.len()) };
202-
203-
Ok(List::Byte(bytes.into()))
196+
Ok(List::Byte(u8_slice_as_i8_slice(v).into()))
204197
}
205198
}
206199

@@ -269,7 +262,7 @@ impl<'de> Deserializer<'de> for Value {
269262
Value::Long(v) => visitor.visit_i64(v),
270263
Value::Float(v) => visitor.visit_f32(v),
271264
Value::Double(v) => visitor.visit_f64(v),
272-
Value::ByteArray(v) => visitor.visit_byte_buf(i8_vec_to_u8_vec(v)),
265+
Value::ByteArray(v) => visitor.visit_byte_buf(i8_vec_into_u8_vec(v)),
273266
Value::String(v) => visitor.visit_string(v),
274267
Value::List(v) => v.deserialize_any(visitor),
275268
Value::Compound(v) => v.into_deserializer().deserialize_any(visitor),
@@ -347,7 +340,7 @@ impl<'de> Deserializer<'de> for List {
347340

348341
match self {
349342
List::End => visitor.visit_seq(EndSeqAccess),
350-
List::Byte(v) => visitor.visit_byte_buf(i8_vec_to_u8_vec(v)),
343+
List::Byte(v) => visitor.visit_byte_buf(i8_vec_into_u8_vec(v)),
351344
List::Short(v) => v.into_deserializer().deserialize_any(visitor),
352345
List::Int(v) => v.into_deserializer().deserialize_any(visitor),
353346
List::Long(v) => v.into_deserializer().deserialize_any(visitor),

crates/valence_nbt/src/serde/ser.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
use core::slice;
21
use std::marker::PhantomData;
32

43
use serde::ser::{Impossible, SerializeMap, SerializeSeq, SerializeStruct};
54
use serde::{Serialize, Serializer};
65

7-
use super::{u8_vec_to_i8_vec, Error};
8-
use crate::{Compound, List, Value};
6+
use super::Error;
7+
use crate::{i8_slice_as_u8_slice, u8_vec_into_i8_vec, Compound, List, Value};
98

109
impl Serialize for Value {
1110
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@@ -19,12 +18,7 @@ impl Serialize for Value {
1918
Value::Long(v) => serializer.serialize_i64(*v),
2019
Value::Float(v) => serializer.serialize_f32(*v),
2120
Value::Double(v) => serializer.serialize_f64(*v),
22-
Value::ByteArray(v) => {
23-
// SAFETY: i8 has the same layout as u8.
24-
let bytes = unsafe { slice::from_raw_parts(v.as_ptr() as *const u8, v.len()) };
25-
26-
serializer.serialize_bytes(bytes)
27-
}
21+
Value::ByteArray(v) => serializer.serialize_bytes(i8_slice_as_u8_slice(v)),
2822
Value::String(v) => serializer.serialize_str(v),
2923
Value::List(v) => v.serialize(serializer),
3024
Value::Compound(v) => v.serialize(serializer),
@@ -317,7 +311,7 @@ impl Serializer for ValueSerializer {
317311
}
318312

319313
fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
320-
Ok(Value::ByteArray(u8_vec_to_i8_vec(v.into())))
314+
Ok(Value::ByteArray(u8_vec_into_i8_vec(v.into())))
321315
}
322316

323317
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {

crates/valence_protocol/src/impls.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use valence_generated::block::{BlockEntityKind, BlockKind, BlockState};
1818
use valence_generated::item::ItemKind;
1919
use valence_ident::{Ident, IdentError};
2020
use valence_math::*;
21-
use valence_nbt::Compound;
21+
use valence_nbt::{i8_slice_as_u8_slice, u8_slice_as_i8_slice, Compound};
2222
use valence_text::Text;
2323

2424
use super::var_int::VarInt;
@@ -69,9 +69,7 @@ impl Encode for i8 {
6969
}
7070

7171
fn encode_slice(slice: &[i8], mut w: impl Write) -> Result<()> {
72-
// SAFETY: i8 has the same layout as u8.
73-
let bytes = unsafe { slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len()) };
74-
Ok(w.write_all(bytes)?)
72+
Ok(w.write_all(i8_slice_as_u8_slice(slice))?)
7573
}
7674
}
7775

@@ -546,10 +544,7 @@ impl<'a> Decode<'a> for &'a [i8] {
546544
fn decode(r: &mut &'a [u8]) -> Result<Self> {
547545
let bytes = <&[u8]>::decode(r)?;
548546

549-
// SAFETY: i8 and u8 have the same layout.
550-
let bytes = unsafe { slice::from_raw_parts(bytes.as_ptr() as *const i8, bytes.len()) };
551-
552-
Ok(bytes)
547+
Ok(u8_slice_as_i8_slice(bytes))
553548
}
554549
}
555550

0 commit comments

Comments
 (0)