use crate::EVENT_DATA_DESCRIPTOR;
use std::marker;
pub use safe_sid::{Sid, SidBuf};
#[repr(transparent)]
#[derive(Default)]
pub struct EventDataDescriptor<'a> {
inner: EVENT_DATA_DESCRIPTOR,
_marker: marker::PhantomData<&'a ()>,
}
impl<'a> EventDataDescriptor<'a> {
pub unsafe fn new(ptr: u64, size: u32) -> Self {
Self {
inner: EVENT_DATA_DESCRIPTOR {
Ptr: ptr,
Size: size,
..Default::default()
},
_marker: marker::PhantomData,
}
}
}
#[inline]
pub fn scalar<T: Copy>(v: &T) -> EventDataDescriptor<'_> {
unsafe { EventDataDescriptor::new(v as *const T as u64, size_of::<T>() as u32) }
}
#[inline]
pub fn str8(buf: &[u8]) -> EventDataDescriptor<'_> {
assert_eq!(buf.last(), Some(&0));
bytes(buf)
}
#[inline]
pub fn bytes(b: &[u8]) -> EventDataDescriptor<'_> {
unsafe { EventDataDescriptor::new(b.as_ptr() as u64, b.len() as u32) }
}
#[inline]
pub fn checked_len<T: TryFrom<usize>>(len: usize) -> crate::Result<T> {
T::try_from(len).map_err(|_| {
windows::Win32::Foundation::ERROR_ARITHMETIC_OVERFLOW
.to_hresult()
.into()
})
}
#[inline]
pub fn str16(buf: &[u16]) -> EventDataDescriptor<'_> {
assert_eq!(buf.last(), Some(&0));
unsafe { EventDataDescriptor::new(buf.as_ptr() as u64, size_of_val(buf) as u32) }
}
#[inline]
pub fn to_u16cstring(s: &str) -> Vec<u16> {
let mut buf = Vec::with_capacity(s.len() + 1);
let mut units = [0u16; 2];
for ch in s.chars() {
let ch = if ch == '\0' { ' ' } else { ch };
buf.extend_from_slice(ch.encode_utf16(&mut units));
}
buf.push(0);
buf
}
#[inline]
pub fn to_u16cstring_fixed_len(s: &str, len: usize) -> Vec<u16> {
assert!(len > 0, "len must include space for the NUL terminator");
let content_limit = len - 1;
let mut buf = Vec::with_capacity(len);
let mut units = [0u16; 2];
for ch in s.chars() {
let ch = if ch == '\0' { ' ' } else { ch };
let width = ch.len_utf16();
if buf.len() + width > content_limit {
break;
}
buf.extend_from_slice(ch.encode_utf16(&mut units));
}
buf.resize(content_limit, b' ' as u16);
buf.push(0);
buf
}
#[inline]
pub fn to_cstring(s: &str) -> Vec<u8> {
s.bytes()
.map(|byte| if byte == 0 { b' ' } else { byte })
.chain(std::iter::once(0))
.collect()
}
#[inline]
pub fn to_cstring_fixed_len(s: &str, len: usize) -> Vec<u8> {
assert!(len > 0, "len must include space for the NUL terminator");
let content_limit = len - 1;
let mut buf = Vec::with_capacity(len);
let mut bytes = [0u8; 4];
for ch in s.chars() {
let ch = if ch == '\0' { ' ' } else { ch };
let width = ch.len_utf8();
if buf.len() + width > content_limit {
break;
}
buf.extend_from_slice(ch.encode_utf8(&mut bytes).as_bytes());
}
buf.resize(content_limit, b' ');
buf.push(0);
buf
}
#[inline]
pub fn sid(sid: &Sid) -> EventDataDescriptor<'_> {
let bytes = sid.as_bytes();
unsafe { EventDataDescriptor::new(bytes.as_ptr() as u64, bytes.len() as u32) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn to_u16cstring_replaces_interior_nul_with_space() {
assert_eq!(
to_u16cstring("a\0b"),
vec![b'a' as u16, b' ' as u16, b'b' as u16, 0]
);
assert_eq!(to_u16cstring("hi"), vec![b'h' as u16, b'i' as u16, 0]);
}
#[test]
fn to_u16cstring_fixed_len_matches_etw_layout() {
assert_eq!(
to_u16cstring_fixed_len("hello", 3),
vec![b'h' as u16, b'e' as u16, 0]
);
assert_eq!(
to_u16cstring_fixed_len("hello", 10),
vec![
b'h' as u16,
b'e' as u16,
b'l' as u16,
b'l' as u16,
b'o' as u16,
b' ' as u16,
b' ' as u16,
b' ' as u16,
b' ' as u16,
0,
]
);
assert_eq!(
to_u16cstring_fixed_len("a\u{1F600}b", 3),
vec![b'a' as u16, b' ' as u16, 0]
);
assert_eq!(
to_u16cstring_fixed_len("a\u{1F600}b", 4),
vec![b'a' as u16, 0xD83D, 0xDE00, 0]
);
assert_eq!(
to_u16cstring_fixed_len("a\0b", 3),
vec![b'a' as u16, b' ' as u16, 0]
);
assert_eq!(to_u16cstring_fixed_len("ignored", 1), vec![0]);
}
#[test]
#[should_panic(expected = "len must include space for the NUL terminator")]
fn to_u16cstring_fixed_len_rejects_zero_length() {
to_u16cstring_fixed_len("", 0);
}
#[test]
fn to_cstring_replaces_interior_nul_with_space() {
assert_eq!(to_cstring("a\0b"), b"a b\0");
assert_eq!(to_cstring("hi"), b"hi\0");
}
#[test]
fn to_cstring_fixed_len_matches_etw_layout() {
assert_eq!(to_cstring_fixed_len("hello", 3), b"he\0");
assert_eq!(to_cstring_fixed_len("hello", 10), b"hello \0");
assert_eq!(to_cstring_fixed_len("a\u{00E9}b", 3), b"a \0");
assert_eq!(to_cstring_fixed_len("a\u{00E9}b", 4), b"a\xC3\xA9\0");
assert_eq!(to_cstring_fixed_len("a\0b", 3), b"a \0");
assert_eq!(to_cstring_fixed_len("ignored", 1), b"\0");
}
#[test]
#[should_panic(expected = "len must include space for the NUL terminator")]
fn to_cstring_fixed_len_rejects_zero_length() {
to_cstring_fixed_len("", 0);
}
}