Skip to content

Commit d34dc6f

Browse files
committed
refactor: consolidate similar code
1 parent 09d37c4 commit d34dc6f

File tree

2 files changed

+45
-66
lines changed

2 files changed

+45
-66
lines changed

libdd-profiling-ffi/src/profile_error.rs

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,48 @@ pub enum ProfileError {
2222
Other(Cow<'static, CStr>),
2323
}
2424

25+
impl ProfileError {
26+
/// Creates a ProfileError by formatting a Display-able error.
27+
pub fn from_display<E: fmt::Display>(err: E) -> Self {
28+
use core::fmt::Write;
29+
30+
let mut writer = FallibleStringWriter::new();
31+
if write!(writer, "{err}").is_err() {
32+
return ProfileError::AllocError;
33+
}
34+
35+
let mut string = String::from(writer);
36+
37+
// Check for interior null bytes using memchr
38+
let pos = unsafe { libc::memchr(string.as_ptr().cast(), 0, string.len()) };
39+
if !pos.is_null() {
40+
return ProfileError::from(
41+
c"encountered an interior null byte while formatting an error message",
42+
);
43+
}
44+
45+
// Reserve memory for the null terminator. We have to shrink later in
46+
// order to turn it into a CString, so we don't want any excess capacity.
47+
if string.try_reserve_exact(1).is_err() {
48+
return ProfileError::AllocError;
49+
}
50+
string.push('\0');
51+
52+
// Shrink to avoid potential panic in CString::from_vec_unchecked
53+
if string_try_shrink_to_fit(&mut string).is_err() {
54+
return ProfileError::AllocError;
55+
}
56+
57+
// Pop the null off because CString::from_vec_unchecked adds one.
58+
_ = string.pop();
59+
60+
// SAFETY: We checked for interior null bytes with memchr above,
61+
// and the string is valid UTF-8 because it came from formatting.
62+
let cstring = unsafe { CString::from_vec_unchecked(string.into_bytes()) };
63+
ProfileError::Other(Cow::Owned(cstring))
64+
}
65+
}
66+
2567
/// Represents an error that means the handle is empty, meaning it doesn't
2668
/// point to a resource.
2769
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
@@ -132,26 +174,7 @@ impl From<std::io::Error> for ProfileError {
132174
match err.kind() {
133175
ErrorKind::StorageFull => ProfileError::CapacityOverflow,
134176
ErrorKind::WriteZero | ErrorKind::OutOfMemory => ProfileError::AllocError,
135-
e => {
136-
let mut writer = FallibleStringWriter::new();
137-
use core::fmt::Write;
138-
// Add null terminator that from_vec_with_nul expects.
139-
if write!(&mut writer, "{e}\0").is_err() {
140-
return ProfileError::from(
141-
c"memory allocation failed while trying to create an error message",
142-
);
143-
}
144-
let mut string = String::from(writer);
145-
// We do this to avoid the potential panic case of failed
146-
// allocation in CString::from_vec_with_nul.
147-
if string_try_shrink_to_fit(&mut string).is_err() {
148-
return ProfileError::from(c"memory allocation failed while trying to shrink a vec to create an error message");
149-
}
150-
match CString::from_vec_with_nul(string.into_bytes()) {
151-
Ok(cstring) => ProfileError::Other(Cow::Owned(cstring)),
152-
Err(_) => ProfileError::from(c"encountered an interior null byte while converting a std::io::Error into a ProfileError")
153-
}
154-
}
177+
e => ProfileError::from_display(e),
155178
}
156179
}
157180
}

libdd-profiling-ffi/src/profile_status.rs

Lines changed: 2 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// Copyright 2025-Present Datadog, Inc. https://www.datadoghq.com/
22
// SPDX-License-Identifier: Apache-2.0
33

4+
use crate::ProfileError;
45
use allocator_api2::alloc::{AllocError, Allocator, Global, Layout};
5-
use libdd_profiling::profiles::FallibleStringWriter;
66
use std::borrow::Cow;
77
use std::ffi::{c_char, CStr, CString};
88
use std::fmt::Display;
@@ -259,58 +259,14 @@ impl ProfileStatus {
259259
err: null(),
260260
};
261261

262-
const OUT_OF_MEMORY: ProfileStatus = ProfileStatus {
263-
flags: FLAG_STATIC,
264-
err: c"out of memory while trying to display error".as_ptr(),
265-
};
266-
const NULL_BYTE_IN_ERROR_MESSAGE: ProfileStatus = ProfileStatus {
267-
flags: FLAG_STATIC,
268-
err: c"another error occured, but cannot be displayed because it has interior null bytes"
269-
.as_ptr(),
270-
};
271-
272262
pub fn from_ffi_safe_error_message<E: libdd_common::error::FfiSafeErrorMessage>(
273263
err: E,
274264
) -> Self {
275265
ProfileStatus::from(err.as_ffi_str())
276266
}
277267

278268
pub fn from_error<E: Display>(err: E) -> Self {
279-
use core::fmt::Write;
280-
let mut writer = FallibleStringWriter::new();
281-
if write!(writer, "{err}").is_err() {
282-
return ProfileStatus::OUT_OF_MEMORY;
283-
}
284-
285-
let mut str = String::from(writer);
286-
287-
// std doesn't expose memchr even though it has it, but fortunately
288-
// libc has it, and we use the libc crate already in FFI.
289-
let pos = unsafe { libc::memchr(str.as_ptr().cast(), 0, str.len()) };
290-
if !pos.is_null() {
291-
return ProfileStatus::NULL_BYTE_IN_ERROR_MESSAGE;
292-
}
293-
294-
// Reserve memory exactly. We have to shrink later in order to turn
295-
// it into a box, so we don't want any excess capacity.
296-
if str.try_reserve_exact(1).is_err() {
297-
return ProfileStatus::OUT_OF_MEMORY;
298-
}
299-
str.push('\0');
300-
301-
if string_try_shrink_to_fit(&mut str).is_err() {
302-
return ProfileStatus::OUT_OF_MEMORY;
303-
}
304-
305-
// Pop the null off because CString::from_vec_unchecked adds one.
306-
_ = str.pop();
307-
308-
// And finally, this is why we went through the pain of
309-
// string_try_shrink_to_fit: this method will call shrink_to_fit, so
310-
// to avoid an allocation failure here, we had to make a String with
311-
// no excess capacity.
312-
let cstring = unsafe { CString::from_vec_unchecked(str.into_bytes()) };
313-
ProfileStatus::from(cstring)
269+
ProfileStatus::from(ProfileError::from_display(err))
314270
}
315271
}
316272

0 commit comments

Comments
 (0)