Skip to content

Commit 625c84f

Browse files
committed
Replace CStr::from_ptr() with CStr::from_bytes_with_nul()
`CStr::from_ptr()` is `unsafe` because it reads a raw pointer, and searches for a terminating nul character in the pointed region of memory. This is unnecessary as both calls already initialize a given number of characters and terminate with a nul, allowing us to pass a sized and initialized slice (without casting `*const MaybeUninit<u8>` to `*const u8`) directly to `CStr::from_bytes_with_nul()` (available since Rust 1.10, unlike `CStr::from_bytes_until_nul()` which was only stabilized in 1.69). Unfortunately all `std` helper APIs to initialize slices of `MaybeUninit` are still unstable, making this less ideal to write at the moment.
1 parent 4280f2e commit 625c84f

File tree

1 file changed

+73
-55
lines changed

1 file changed

+73
-55
lines changed

src/lib.rs

Lines changed: 73 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -227,10 +227,8 @@ impl Log for AndroidLogger {
227227
// In case we end up allocating, keep the CString alive.
228228
let _owned_tag;
229229
let tag: &CStr = if tag.len() < tag_bytes.len() {
230-
// use stack array as C string
231-
self.fill_tag_bytes(&mut tag_bytes, tag);
232-
// SAFETY: fill_tag_bytes always puts a nullbyte in tag_bytes.
233-
unsafe { CStr::from_ptr(mem::transmute(tag_bytes.as_ptr())) }
230+
// truncate the tag here to fit into LOGGING_TAG_MAX_LEN
231+
fill_tag_bytes(&mut tag_bytes, tag)
234232
} else {
235233
// Tag longer than available stack buffer; allocate.
236234
// We're using either
@@ -264,23 +262,38 @@ impl Log for AndroidLogger {
264262
fn flush(&self) {}
265263
}
266264

267-
impl AndroidLogger {
268-
fn fill_tag_bytes(&self, array: &mut [MaybeUninit<u8>], tag: &[u8]) {
269-
if tag.len() > LOGGING_TAG_MAX_LEN {
270-
for (input, output) in tag
271-
.iter()
272-
.take(LOGGING_TAG_MAX_LEN - 2)
273-
.chain(b"..\0".iter())
274-
.zip(array.iter_mut())
275-
{
276-
output.write(*input);
277-
}
278-
} else {
279-
for (input, output) in tag.iter().chain(b"\0".iter()).zip(array.iter_mut()) {
280-
output.write(*input);
281-
}
265+
/// Fills up `storage` with `tag` and a necessary NUL terminator, optionally ellipsizing the input
266+
/// `tag` if it's too large.
267+
///
268+
/// Returns a [`CStr`] containing the initialized portion of `storage`, including its NUL
269+
/// terminator.
270+
fn fill_tag_bytes<'a>(
271+
storage: &'a mut [MaybeUninit<u8>; LOGGING_TAG_MAX_LEN + 1],
272+
tag: &[u8],
273+
) -> &'a CStr {
274+
// FIXME: Simplify when maybe_uninit_fill with MaybeUninit::fill_from() is stabilized
275+
let initialized = if tag.len() > LOGGING_TAG_MAX_LEN {
276+
for (input, output) in tag
277+
.iter()
278+
// Elipsize the last two characters (TODO: use special … character)?
279+
.take(LOGGING_TAG_MAX_LEN - 2)
280+
.chain(b"..\0")
281+
.zip(storage.iter_mut())
282+
{
283+
output.write(*input);
282284
}
283-
}
285+
storage.as_slice()
286+
} else {
287+
for (input, output) in tag.iter().chain(b"\0").zip(storage.iter_mut()) {
288+
output.write(*input);
289+
}
290+
&storage[..tag.len() + 1]
291+
};
292+
293+
// SAFETY: The above code ensures that `initialized` only refers to a portion of the `array`
294+
// slice that was initialized, thus it is safe to cast those `MaybeUninit<u8>`s to `u8`:
295+
let initialized = unsafe { slice_assume_init_ref(initialized) };
296+
CStr::from_bytes_with_nul(initialized).expect("Unreachable: we wrote a nul terminator")
284297
}
285298

286299
/// Filter for android logger.
@@ -369,7 +382,7 @@ pub struct PlatformLogWriter<'a> {
369382
buffer: [MaybeUninit<u8>; LOGGING_MSG_MAX_LEN + 1],
370383
}
371384

372-
impl<'a> PlatformLogWriter<'a> {
385+
impl PlatformLogWriter<'_> {
373386
#[cfg(target_os = "android")]
374387
pub fn new_with_priority(
375388
buf_id: Option<LogId>,
@@ -432,11 +445,11 @@ impl<'a> PlatformLogWriter<'a> {
432445
let copy_from_index = self.last_newline_index;
433446
let remaining_chunk_len = total_len - copy_from_index;
434447

435-
self.output_specified_len(copy_from_index);
448+
unsafe { self.output_specified_len(copy_from_index) };
436449
self.copy_bytes_to_start(copy_from_index, remaining_chunk_len);
437450
self.len = remaining_chunk_len;
438451
} else {
439-
self.output_specified_len(total_len);
452+
unsafe { self.output_specified_len(total_len) };
440453
self.len = 0;
441454
}
442455
self.last_newline_index = 0;
@@ -450,20 +463,26 @@ impl<'a> PlatformLogWriter<'a> {
450463
return;
451464
}
452465

453-
self.output_specified_len(total_len);
466+
unsafe { self.output_specified_len(total_len) };
454467
self.len = 0;
455468
self.last_newline_index = 0;
456469
}
457470

458471
/// Output buffer up until the \0 which will be placed at `len` position.
459-
fn output_specified_len(&mut self, len: usize) {
472+
///
473+
/// # Safety
474+
/// The first `len` bytes of `self.buffer` must be initialized.
475+
unsafe fn output_specified_len(&mut self, len: usize) {
460476
let mut last_byte = MaybeUninit::new(b'\0');
461477

462-
mem::swap(&mut last_byte, unsafe {
463-
self.buffer.get_unchecked_mut(len)
464-
});
478+
mem::swap(
479+
&mut last_byte,
480+
self.buffer.get_mut(len).expect("`len` is out of bounds"),
481+
);
465482

466-
let msg: &CStr = unsafe { CStr::from_ptr(self.buffer.as_ptr().cast()) };
483+
let initialized = unsafe { slice_assume_init_ref(&self.buffer[..len + 1]) };
484+
let msg = CStr::from_bytes_with_nul(initialized)
485+
.expect("Unreachable: nul terminator was placed at `len`");
467486
android_log(self.buf_id, self.priority, self.tag, msg);
468487

469488
unsafe { *self.buffer.get_unchecked_mut(len) = last_byte };
@@ -477,18 +496,18 @@ impl<'a> PlatformLogWriter<'a> {
477496
}
478497
}
479498

480-
impl<'a> fmt::Write for PlatformLogWriter<'a> {
499+
impl fmt::Write for PlatformLogWriter<'_> {
481500
fn write_str(&mut self, s: &str) -> fmt::Result {
482-
let mut incomming_bytes = s.as_bytes();
501+
let mut incoming_bytes = s.as_bytes();
483502

484-
while !incomming_bytes.is_empty() {
503+
while !incoming_bytes.is_empty() {
485504
let len = self.len;
486505

487506
// write everything possible to buffer and mark last \n
488-
let new_len = len + incomming_bytes.len();
507+
let new_len = len + incoming_bytes.len();
489508
let last_newline = self.buffer[len..LOGGING_MSG_MAX_LEN]
490509
.iter_mut()
491-
.zip(incomming_bytes)
510+
.zip(incoming_bytes)
492511
.enumerate()
493512
.fold(None, |acc, (i, (output, input))| {
494513
output.write(*input);
@@ -517,7 +536,7 @@ impl<'a> fmt::Write for PlatformLogWriter<'a> {
517536
LOGGING_MSG_MAX_LEN - len // written len
518537
};
519538

520-
incomming_bytes = &incomming_bytes[written_len..];
539+
incoming_bytes = &incoming_bytes[written_len..];
521540
}
522541

523542
Ok(())
@@ -558,6 +577,11 @@ fn uninit_array<const N: usize, T>() -> [MaybeUninit<T>; N] {
558577
unsafe { MaybeUninit::uninit().assume_init() }
559578
}
560579

580+
// FIXME: Remove when maybe_uninit_slice is stabilized to provide MaybeUninit::slice_assume_init_ref()
581+
unsafe fn slice_assume_init_ref<T>(slice: &[MaybeUninit<T>]) -> &[T] {
582+
&*(slice as *const [MaybeUninit<T>] as *const [T])
583+
}
584+
561585
#[cfg(test)]
562586
mod tests {
563587
use super::*;
@@ -618,28 +642,26 @@ mod tests {
618642

619643
#[test]
620644
fn fill_tag_bytes_truncates_long_tag() {
621-
let logger = AndroidLogger::new(Config::default());
622-
let too_long_tag: [u8; LOGGING_TAG_MAX_LEN + 20] = [b'a'; LOGGING_TAG_MAX_LEN + 20];
645+
let too_long_tag = [b'a'; LOGGING_TAG_MAX_LEN + 20];
623646

624-
let mut result: [MaybeUninit<u8>; LOGGING_TAG_MAX_LEN + 1] = uninit_array();
625-
logger.fill_tag_bytes(&mut result, &too_long_tag);
647+
let mut result = uninit_array();
648+
let tag = fill_tag_bytes(&mut result, &too_long_tag);
626649

627-
let mut expected_result = [b'a'; LOGGING_TAG_MAX_LEN - 2].to_vec();
650+
let mut expected_result = vec![b'a'; LOGGING_TAG_MAX_LEN - 2];
628651
expected_result.extend("..\0".as_bytes());
629-
assert_eq!(unsafe { assume_init_slice(&result) }, expected_result);
652+
assert_eq!(tag.to_bytes_with_nul(), expected_result);
630653
}
631654

632655
#[test]
633656
fn fill_tag_bytes_keeps_short_tag() {
634-
let logger = AndroidLogger::new(Config::default());
635-
let short_tag: [u8; 3] = [b'a'; 3];
657+
let short_tag = [b'a'; 3];
636658

637-
let mut result: [MaybeUninit<u8>; LOGGING_TAG_MAX_LEN + 1] = uninit_array();
638-
logger.fill_tag_bytes(&mut result, &short_tag);
659+
let mut result = uninit_array();
660+
let tag = fill_tag_bytes(&mut result, &short_tag);
639661

640662
let mut expected_result = short_tag.to_vec();
641663
expected_result.push(0);
642-
assert_eq!(unsafe { assume_init_slice(&result[..4]) }, expected_result);
664+
assert_eq!(tag.to_bytes_with_nul(), expected_result);
643665
}
644666

645667
#[test]
@@ -668,7 +690,7 @@ mod tests {
668690
assert_eq!(writer.len, 3);
669691
assert_eq!(writer.last_newline_index, 0);
670692
assert_eq!(
671-
unsafe { assume_init_slice(&writer.buffer[..writer.len]) },
693+
unsafe { slice_assume_init_ref(&writer.buffer[..writer.len]) },
672694
"\n90".as_bytes()
673695
);
674696

@@ -710,10 +732,10 @@ mod tests {
710732
.write_str(log_string)
711733
.expect("Unable to write to PlatformLogWriter");
712734

713-
writer.output_specified_len(5);
735+
unsafe { writer.output_specified_len(5) };
714736

715737
assert_eq!(
716-
unsafe { assume_init_slice(&writer.buffer[..log_string.len()]) },
738+
unsafe { slice_assume_init_ref(&writer.buffer[..log_string.len()]) },
717739
log_string.as_bytes()
718740
);
719741
}
@@ -728,7 +750,7 @@ mod tests {
728750
writer.copy_bytes_to_start(3, 2);
729751

730752
assert_eq!(
731-
unsafe { assume_init_slice(&writer.buffer[..10]) },
753+
unsafe { slice_assume_init_ref(&writer.buffer[..10]) },
732754
"3423456789".as_bytes()
733755
);
734756
}
@@ -745,7 +767,7 @@ mod tests {
745767
writer.copy_bytes_to_start(10, 0);
746768

747769
assert_eq!(
748-
unsafe { assume_init_slice(&writer.buffer[..test_string.len()]) },
770+
unsafe { slice_assume_init_ref(&writer.buffer[..test_string.len()]) },
749771
test_string.as_bytes()
750772
);
751773
}
@@ -757,8 +779,4 @@ mod tests {
757779
CStr::from_bytes_with_nul(b"tag\0").unwrap(),
758780
)
759781
}
760-
761-
unsafe fn assume_init_slice<T>(slice: &[MaybeUninit<T>]) -> &[T] {
762-
&*(slice as *const [MaybeUninit<T>] as *const [T])
763-
}
764782
}

0 commit comments

Comments
 (0)