Skip to content

Commit f382b30

Browse files
committed
Add support for str and &[mut] str
1 parent f488405 commit f382b30

File tree

2 files changed

+64
-11
lines changed

2 files changed

+64
-11
lines changed

src/lib.rs

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ unsafe impl Abomonation<'_> for bool { }
345345
unsafe impl Abomonation<'_> for () { }
346346

347347
unsafe impl Abomonation<'_> for char { }
348+
unsafe impl Abomonation<'_> for str { }
348349

349350
unsafe impl Abomonation<'_> for ::std::time::Duration { }
350351

@@ -489,26 +490,63 @@ array_abomonate!(30);
489490
array_abomonate!(31);
490491
array_abomonate!(32);
491492

492-
unsafe impl<'bytes> Abomonation<'bytes> for String {
493+
unsafe impl<'target, 'bytes: 'target> Abomonation<'bytes> for &'target str {
493494
unsafe fn entomb<W: Write>(&self, write: &mut W) -> IOResult<()> {
494495
write.write_all(self.as_bytes())
495496
}
496497

498+
#[inline]
499+
unsafe fn exhume(self_: NonNull<Self>, bytes: &'bytes mut [u8]) -> Option<&'bytes mut [u8]> {
500+
// FIXME: This (briefly) constructs an &str to invalid data, which is UB.
501+
// I'm not sure if this can be fully resolved without relying on &str implementation details.
502+
let self_len = self_.as_ref().len();
503+
let (s, rest) = exhume_str_ref(self_len, bytes)?;
504+
self_.as_ptr().write(s);
505+
Some(rest)
506+
}
507+
508+
fn extent(&self) -> usize {
509+
self.len()
510+
}
511+
}
512+
513+
unsafe impl<'target, 'bytes: 'target> Abomonation<'bytes> for &'target mut str {
514+
unsafe fn entomb<W: Write>(&self, write: &mut W) -> IOResult<()> {
515+
<&str>::entomb(&self.as_ref(), write)
516+
}
517+
518+
#[inline]
519+
unsafe fn exhume(self_: NonNull<Self>, bytes: &'bytes mut [u8]) -> Option<&'bytes mut [u8]> {
520+
// FIXME: This (briefly) constructs an &mut str to invalid data, which is UB.
521+
// I'm not sure if this can be fully resolved without relying on &str implementation details.
522+
let self_len = self_.as_ref().len();
523+
let (s, rest) = exhume_str_ref(self_len, bytes)?;
524+
self_.as_ptr().write(s);
525+
Some(rest)
526+
}
527+
528+
fn extent(&self) -> usize {
529+
<&str>::extent(&self.as_ref())
530+
}
531+
}
532+
533+
unsafe impl<'bytes> Abomonation<'bytes> for String {
534+
unsafe fn entomb<W: Write>(&self, write: &mut W) -> IOResult<()> {
535+
<&str>::entomb(&self.as_ref(), write)
536+
}
537+
497538
#[inline]
498539
unsafe fn exhume(self_: NonNull<Self>, bytes: &'bytes mut [u8]) -> Option<&'bytes mut [u8]> {
499540
// FIXME: This (briefly) constructs an &String to invalid data, which is UB.
500541
// I'm not sure if this can be fully resolved without relying on String implementation details.
501542
let self_len = self_.as_ref().len();
502-
if self_len > bytes.len() { None }
503-
else {
504-
let (mine, rest) = bytes.split_at_mut(self_len);
505-
self_.as_ptr().write(String::from_raw_parts(mine.as_mut_ptr(), self_len, self_len));
506-
Some(rest)
507-
}
543+
let (s, rest) = exhume_str_ref(self_len, bytes)?;
544+
self_.as_ptr().write(String::from_raw_parts(s.as_mut_ptr(), s.len(), s.len()));
545+
Some(rest)
508546
}
509547

510548
fn extent(&self) -> usize {
511-
self.len()
549+
<&str>::extent(&self.as_ref())
512550
}
513551
}
514552

@@ -631,6 +669,15 @@ unsafe fn exhume_ref<'target, 'bytes: 'target, T: Abomonation<'bytes>>(bytes: &'
631669
}
632670
}
633671

672+
// Common subset of "exhume" for all &str-like types
673+
unsafe fn exhume_str_ref<'target, 'bytes: 'target>(length: usize, bytes: &'bytes mut [u8]) -> Option<(&'target mut str, &'bytes mut [u8])> {
674+
if length > bytes.len() { None }
675+
else {
676+
let (mine, rest) = bytes.split_at_mut(length);
677+
Some((std::str::from_utf8_unchecked_mut(mine), rest))
678+
}
679+
}
680+
634681
mod network {
635682
use Abomonation;
636683
use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6, IpAddr, Ipv4Addr, Ipv6Addr};

tests/tests.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ use std::fmt::Debug;
55

66
// Test struct for the unsafe_abomonate macro
77
#[derive(Clone, Debug, Eq, PartialEq)]
8-
struct MyStruct {
8+
struct MyStruct<'d> {
99
a: String,
1010
b: u64,
1111
c: Vec<u8>,
12+
d: &'d str,
1213
}
13-
unsafe_abomonate!(MyStruct : a, b, c);
14+
unsafe_abomonate!(MyStruct<'_> : a, b, c, d);
1415

1516
// Test for PhantomData abomonation, which has no Abomonation bound
1617
struct NotAbomonatable;
@@ -75,7 +76,8 @@ gen_tests!{
7576

7677
MyStruct{ a: "test".to_owned(),
7778
b: 0,
78-
c: vec![0, 1, 2] } => (test_macro_pass,
79+
c: vec![0, 1, 2],
80+
d: &"grawwwwrr!" } => (test_macro_pass,
7981
test_macro_fail,
8082
test_macro_size),
8183

@@ -86,6 +88,10 @@ gen_tests!{
8688
Some(&42u64) => (test_ref_u64_pass,
8789
test_ref_u64_fail,
8890
test_ref_u64_size),
91+
92+
&"grawwwwrr!" => (test_str_pass,
93+
test_str_fail,
94+
test_str_size),
8995
}
9096

9197
fn _test_pass<'bytes, T: Abomonation<'bytes> + Debug + Eq>(

0 commit comments

Comments
 (0)