diff options
| author | mo khan <mo@mokhan.ca> | 2025-07-02 18:36:06 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-07-02 18:36:06 -0600 |
| commit | 8cdfa445d6629ffef4cb84967ff7017654045bc2 (patch) | |
| tree | 22f0b0907c024c78d26a731e2e1f5219407d8102 /vendor/windows-strings/src | |
| parent | 4351c74c7c5f97156bc94d3a8549b9940ac80e3f (diff) | |
chore: add vendor directory
Diffstat (limited to 'vendor/windows-strings/src')
| -rw-r--r-- | vendor/windows-strings/src/bindings.rs | 19 | ||||
| -rw-r--r-- | vendor/windows-strings/src/bstr.rs | 166 | ||||
| -rw-r--r-- | vendor/windows-strings/src/decode.rs | 58 | ||||
| -rw-r--r-- | vendor/windows-strings/src/hstring.rs | 392 | ||||
| -rw-r--r-- | vendor/windows-strings/src/hstring_builder.rs | 100 | ||||
| -rw-r--r-- | vendor/windows-strings/src/hstring_header.rs | 70 | ||||
| -rw-r--r-- | vendor/windows-strings/src/lib.rs | 47 | ||||
| -rw-r--r-- | vendor/windows-strings/src/literals.rs | 169 | ||||
| -rw-r--r-- | vendor/windows-strings/src/pcstr.rs | 64 | ||||
| -rw-r--r-- | vendor/windows-strings/src/pcwstr.rs | 97 | ||||
| -rw-r--r-- | vendor/windows-strings/src/pstr.rs | 64 | ||||
| -rw-r--r-- | vendor/windows-strings/src/pwstr.rs | 88 | ||||
| -rw-r--r-- | vendor/windows-strings/src/ref_count.rs | 27 |
13 files changed, 1361 insertions, 0 deletions
diff --git a/vendor/windows-strings/src/bindings.rs b/vendor/windows-strings/src/bindings.rs new file mode 100644 index 00000000..2e0ad17d --- /dev/null +++ b/vendor/windows-strings/src/bindings.rs @@ -0,0 +1,19 @@ +#![allow( + non_snake_case, + non_upper_case_globals, + non_camel_case_types, + dead_code, + clippy::all +)] + +windows_link::link!("kernel32.dll" "system" fn GetProcessHeap() -> HANDLE); +windows_link::link!("kernel32.dll" "system" fn HeapAlloc(hheap : HANDLE, dwflags : HEAP_FLAGS, dwbytes : usize) -> *mut core::ffi::c_void); +windows_link::link!("kernel32.dll" "system" fn HeapFree(hheap : HANDLE, dwflags : HEAP_FLAGS, lpmem : *const core::ffi::c_void) -> BOOL); +windows_link::link!("oleaut32.dll" "system" fn SysAllocStringLen(strin : PCWSTR, ui : u32) -> BSTR); +windows_link::link!("oleaut32.dll" "system" fn SysFreeString(bstrstring : BSTR)); +windows_link::link!("oleaut32.dll" "system" fn SysStringLen(pbstr : BSTR) -> u32); +pub type BOOL = i32; +pub type BSTR = *const u16; +pub type HANDLE = *mut core::ffi::c_void; +pub type HEAP_FLAGS = u32; +pub type PCWSTR = *const u16; diff --git a/vendor/windows-strings/src/bstr.rs b/vendor/windows-strings/src/bstr.rs new file mode 100644 index 00000000..486b2602 --- /dev/null +++ b/vendor/windows-strings/src/bstr.rs @@ -0,0 +1,166 @@ +use super::*; +use core::ops::Deref; + +/// A BSTR string ([BSTR](https://learn.microsoft.com/en-us/previous-versions/windows/desktop/automat/string-manipulation-functions)) +/// is a length-prefixed wide string. +#[repr(transparent)] +pub struct BSTR(*const u16); + +impl BSTR { + /// Create an empty `BSTR`. + /// + /// This function does not allocate memory. + pub const fn new() -> Self { + Self(core::ptr::null_mut()) + } + + /// Create a `BSTR` from a slice of 16 bit characters (wchars). + pub fn from_wide(value: &[u16]) -> Self { + if value.is_empty() { + return Self::new(); + } + + let result = unsafe { + Self(bindings::SysAllocStringLen( + value.as_ptr(), + value.len().try_into().unwrap(), + )) + }; + + if result.is_empty() { + panic!("allocation failed"); + } + + result + } + + /// # Safety + #[doc(hidden)] + pub unsafe fn from_raw(raw: *const u16) -> Self { + Self(raw) + } + + /// # Safety + #[doc(hidden)] + pub fn into_raw(self) -> *const u16 { + unsafe { core::mem::transmute(self) } + } +} + +impl Deref for BSTR { + type Target = [u16]; + + fn deref(&self) -> &[u16] { + let len = if self.0.is_null() { + 0 + } else { + unsafe { bindings::SysStringLen(self.0) as usize } + }; + + if len > 0 { + unsafe { core::slice::from_raw_parts(self.0, len) } + } else { + // This ensures that if `as_ptr` is called on the slice that the resulting pointer + // will still refer to a null-terminated string. + const EMPTY: [u16; 1] = [0]; + &EMPTY[..0] + } + } +} + +impl Clone for BSTR { + fn clone(&self) -> Self { + Self::from_wide(self) + } +} + +impl From<&str> for BSTR { + fn from(value: &str) -> Self { + let value: alloc::vec::Vec<u16> = value.encode_utf16().collect(); + Self::from_wide(&value) + } +} + +impl From<String> for BSTR { + fn from(value: String) -> Self { + value.as_str().into() + } +} + +impl From<&String> for BSTR { + fn from(value: &String) -> Self { + value.as_str().into() + } +} + +impl TryFrom<&BSTR> for String { + type Error = alloc::string::FromUtf16Error; + + fn try_from(value: &BSTR) -> core::result::Result<Self, Self::Error> { + String::from_utf16(value) + } +} + +impl TryFrom<BSTR> for String { + type Error = alloc::string::FromUtf16Error; + + fn try_from(value: BSTR) -> core::result::Result<Self, Self::Error> { + String::try_from(&value) + } +} + +impl Default for BSTR { + fn default() -> Self { + Self(core::ptr::null_mut()) + } +} + +impl core::fmt::Display for BSTR { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::write!( + f, + "{}", + Decode(|| core::char::decode_utf16(self.iter().cloned())) + ) + } +} + +impl core::fmt::Debug for BSTR { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::write!(f, "{self}") + } +} + +impl PartialEq for BSTR { + fn eq(&self, other: &Self) -> bool { + self.deref() == other.deref() + } +} + +impl Eq for BSTR {} + +impl PartialEq<BSTR> for &str { + fn eq(&self, other: &BSTR) -> bool { + other == self + } +} + +impl PartialEq<BSTR> for String { + fn eq(&self, other: &BSTR) -> bool { + other == self + } +} + +impl<T: AsRef<str> + ?Sized> PartialEq<T> for BSTR { + fn eq(&self, other: &T) -> bool { + self.iter().copied().eq(other.as_ref().encode_utf16()) + } +} + +impl Drop for BSTR { + fn drop(&mut self) { + if !self.0.is_null() { + unsafe { bindings::SysFreeString(self.0) } + } + } +} diff --git a/vendor/windows-strings/src/decode.rs b/vendor/windows-strings/src/decode.rs new file mode 100644 index 00000000..dbc9e2ea --- /dev/null +++ b/vendor/windows-strings/src/decode.rs @@ -0,0 +1,58 @@ +/// An internal helper for decoding an iterator of chars and displaying them +pub struct Decode<F>(pub F); + +impl<F, R, E> core::fmt::Display for Decode<F> +where + F: Clone + FnOnce() -> R, + R: IntoIterator<Item = core::result::Result<char, E>>, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + use core::fmt::Write; + let iter = self.0.clone(); + for c in iter().into_iter() { + f.write_char(c.unwrap_or(core::char::REPLACEMENT_CHARACTER))? + } + Ok(()) + } +} + +/// Mirror of `std::char::decode_utf16` for utf-8. +pub fn decode_utf8( + mut buffer: &[u8], +) -> impl Iterator<Item = core::result::Result<char, core::str::Utf8Error>> + '_ { + let mut current = "".chars(); + let mut previous_error = None; + core::iter::from_fn(move || { + loop { + match (current.next(), previous_error) { + (Some(c), _) => return Some(Ok(c)), + // Return the previous error + (None, Some(e)) => { + previous_error = None; + return Some(Err(e)); + } + // We're completely done + (None, None) if buffer.is_empty() => return None, + (None, None) => { + match core::str::from_utf8(buffer) { + Ok(s) => { + current = s.chars(); + buffer = &[]; + } + Err(e) => { + let (valid, rest) = buffer.split_at(e.valid_up_to()); + // Skip the invalid sequence and stop completely if we ended early + let invalid_sequence_length = e.error_len()?; + buffer = &rest[invalid_sequence_length..]; + + // Set the current iterator to the valid section and indicate previous error + // SAFETY: `valid` is known to be valid utf-8 from error + current = unsafe { core::str::from_utf8_unchecked(valid) }.chars(); + previous_error = Some(e); + } + } + } + } + } + }) +} diff --git a/vendor/windows-strings/src/hstring.rs b/vendor/windows-strings/src/hstring.rs new file mode 100644 index 00000000..a8e359f0 --- /dev/null +++ b/vendor/windows-strings/src/hstring.rs @@ -0,0 +1,392 @@ +use super::*; +use core::ops::Deref; + +/// An ([HSTRING](https://docs.microsoft.com/en-us/windows/win32/winrt/hstring)) +/// is a reference-counted and immutable UTF-16 string type. +#[repr(transparent)] +pub struct HSTRING(pub(crate) *mut HStringHeader); + +impl HSTRING { + /// Create an empty `HSTRING`. + /// + /// This function does not allocate memory. + pub const fn new() -> Self { + Self(core::ptr::null_mut()) + } + + /// Create a `HSTRING` from a slice of 16 bit characters (wchars). + pub fn from_wide(value: &[u16]) -> Self { + unsafe { Self::from_wide_iter(value.iter().copied(), value.len()) } + } + + /// Get the contents of this `HSTRING` as a String lossily. + pub fn to_string_lossy(&self) -> String { + String::from_utf16_lossy(self) + } + + /// Get the contents of this `HSTRING` as a OsString. + #[cfg(feature = "std")] + pub fn to_os_string(&self) -> std::ffi::OsString { + std::os::windows::ffi::OsStringExt::from_wide(self) + } + + /// # Safety + /// len must not be less than the number of items in the iterator. + unsafe fn from_wide_iter<I: Iterator<Item = u16>>(iter: I, len: usize) -> Self { + if len == 0 { + return Self::new(); + } + + let ptr = HStringHeader::alloc(len.try_into().unwrap()); + + // Place each utf-16 character into the buffer and + // increase len as we go along. + for (index, wide) in iter.enumerate() { + debug_assert!(index < len); + + unsafe { + (*ptr).data.add(index).write(wide); + (*ptr).len = index as u32 + 1; + } + } + + unsafe { + // Write a 0 byte to the end of the buffer. + (*ptr).data.offset((*ptr).len as isize).write(0); + } + Self(ptr) + } + + fn as_header(&self) -> Option<&HStringHeader> { + unsafe { self.0.as_ref() } + } +} + +impl Deref for HSTRING { + type Target = [u16]; + + fn deref(&self) -> &[u16] { + if let Some(header) = self.as_header() { + unsafe { core::slice::from_raw_parts(header.data, header.len as usize) } + } else { + // This ensures that if `as_ptr` is called on the slice that the resulting pointer + // will still refer to a null-terminated string. + const EMPTY: [u16; 1] = [0]; + &EMPTY[..0] + } + } +} + +impl Default for HSTRING { + fn default() -> Self { + Self::new() + } +} + +impl Clone for HSTRING { + fn clone(&self) -> Self { + if let Some(header) = self.as_header() { + Self(header.duplicate()) + } else { + Self::new() + } + } +} + +impl Drop for HSTRING { + fn drop(&mut self) { + if let Some(header) = self.as_header() { + // HSTRING_REFERENCE_FLAG indicates a string backed by static or stack memory that is + // thus not reference-counted and does not need to be freed. + unsafe { + if header.flags & HSTRING_REFERENCE_FLAG == 0 && header.count.release() == 0 { + HStringHeader::free(self.0); + } + } + } + } +} + +unsafe impl Send for HSTRING {} +unsafe impl Sync for HSTRING {} + +impl core::fmt::Display for HSTRING { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "{}", + Decode(|| core::char::decode_utf16(self.iter().cloned())) + ) + } +} + +impl core::fmt::Debug for HSTRING { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "\"{self}\"") + } +} + +impl From<&str> for HSTRING { + fn from(value: &str) -> Self { + unsafe { Self::from_wide_iter(value.encode_utf16(), value.len()) } + } +} + +impl From<String> for HSTRING { + fn from(value: String) -> Self { + value.as_str().into() + } +} + +impl From<&String> for HSTRING { + fn from(value: &String) -> Self { + value.as_str().into() + } +} + +#[cfg(feature = "std")] +impl From<&std::path::Path> for HSTRING { + fn from(value: &std::path::Path) -> Self { + value.as_os_str().into() + } +} + +#[cfg(feature = "std")] +impl From<&std::ffi::OsStr> for HSTRING { + fn from(value: &std::ffi::OsStr) -> Self { + unsafe { + Self::from_wide_iter( + std::os::windows::ffi::OsStrExt::encode_wide(value), + value.len(), + ) + } + } +} + +#[cfg(feature = "std")] +impl From<std::ffi::OsString> for HSTRING { + fn from(value: std::ffi::OsString) -> Self { + value.as_os_str().into() + } +} + +#[cfg(feature = "std")] +impl From<&std::ffi::OsString> for HSTRING { + fn from(value: &std::ffi::OsString) -> Self { + value.as_os_str().into() + } +} + +impl Eq for HSTRING {} + +impl Ord for HSTRING { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.deref().cmp(other) + } +} + +impl core::hash::Hash for HSTRING { + fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) { + self.deref().hash(hasher) + } +} + +impl PartialOrd for HSTRING { + fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { + Some(self.cmp(other)) + } +} + +impl PartialEq for HSTRING { + fn eq(&self, other: &Self) -> bool { + self.deref() == other.deref() + } +} + +impl PartialEq<String> for HSTRING { + fn eq(&self, other: &String) -> bool { + *self == **other + } +} + +impl PartialEq<String> for &HSTRING { + fn eq(&self, other: &String) -> bool { + **self == **other + } +} + +impl PartialEq<&String> for HSTRING { + fn eq(&self, other: &&String) -> bool { + *self == ***other + } +} + +impl PartialEq<str> for HSTRING { + fn eq(&self, other: &str) -> bool { + self.iter().copied().eq(other.encode_utf16()) + } +} + +impl PartialEq<str> for &HSTRING { + fn eq(&self, other: &str) -> bool { + **self == *other + } +} + +impl PartialEq<&str> for HSTRING { + fn eq(&self, other: &&str) -> bool { + *self == **other + } +} + +impl PartialEq<HSTRING> for str { + fn eq(&self, other: &HSTRING) -> bool { + *other == *self + } +} + +impl PartialEq<HSTRING> for &str { + fn eq(&self, other: &HSTRING) -> bool { + *other == **self + } +} + +impl PartialEq<&HSTRING> for str { + fn eq(&self, other: &&HSTRING) -> bool { + **other == *self + } +} + +impl PartialEq<HSTRING> for String { + fn eq(&self, other: &HSTRING) -> bool { + *other == **self + } +} + +impl PartialEq<HSTRING> for &String { + fn eq(&self, other: &HSTRING) -> bool { + *other == ***self + } +} + +impl PartialEq<&HSTRING> for String { + fn eq(&self, other: &&HSTRING) -> bool { + **other == **self + } +} + +#[cfg(feature = "std")] +impl PartialEq<std::ffi::OsString> for HSTRING { + fn eq(&self, other: &std::ffi::OsString) -> bool { + *self == **other + } +} + +#[cfg(feature = "std")] +impl PartialEq<std::ffi::OsString> for &HSTRING { + fn eq(&self, other: &std::ffi::OsString) -> bool { + **self == **other + } +} + +#[cfg(feature = "std")] +impl PartialEq<&std::ffi::OsString> for HSTRING { + fn eq(&self, other: &&std::ffi::OsString) -> bool { + *self == ***other + } +} + +#[cfg(feature = "std")] +impl PartialEq<std::ffi::OsStr> for HSTRING { + fn eq(&self, other: &std::ffi::OsStr) -> bool { + self.iter() + .copied() + .eq(std::os::windows::ffi::OsStrExt::encode_wide(other)) + } +} + +#[cfg(feature = "std")] +impl PartialEq<std::ffi::OsStr> for &HSTRING { + fn eq(&self, other: &std::ffi::OsStr) -> bool { + **self == *other + } +} + +#[cfg(feature = "std")] +impl PartialEq<&std::ffi::OsStr> for HSTRING { + fn eq(&self, other: &&std::ffi::OsStr) -> bool { + *self == **other + } +} + +#[cfg(feature = "std")] +impl PartialEq<HSTRING> for std::ffi::OsStr { + fn eq(&self, other: &HSTRING) -> bool { + *other == *self + } +} + +#[cfg(feature = "std")] +impl PartialEq<HSTRING> for &std::ffi::OsStr { + fn eq(&self, other: &HSTRING) -> bool { + *other == **self + } +} + +#[cfg(feature = "std")] +impl PartialEq<&HSTRING> for std::ffi::OsStr { + fn eq(&self, other: &&HSTRING) -> bool { + **other == *self + } +} + +#[cfg(feature = "std")] +impl PartialEq<HSTRING> for std::ffi::OsString { + fn eq(&self, other: &HSTRING) -> bool { + *other == **self + } +} + +#[cfg(feature = "std")] +impl PartialEq<HSTRING> for &std::ffi::OsString { + fn eq(&self, other: &HSTRING) -> bool { + *other == ***self + } +} + +#[cfg(feature = "std")] +impl PartialEq<&HSTRING> for std::ffi::OsString { + fn eq(&self, other: &&HSTRING) -> bool { + **other == **self + } +} + +impl TryFrom<&HSTRING> for String { + type Error = alloc::string::FromUtf16Error; + + fn try_from(hstring: &HSTRING) -> core::result::Result<Self, Self::Error> { + String::from_utf16(hstring) + } +} + +impl TryFrom<HSTRING> for String { + type Error = alloc::string::FromUtf16Error; + + fn try_from(hstring: HSTRING) -> core::result::Result<Self, Self::Error> { + String::try_from(&hstring) + } +} + +#[cfg(feature = "std")] +impl From<&HSTRING> for std::ffi::OsString { + fn from(hstring: &HSTRING) -> Self { + hstring.to_os_string() + } +} + +#[cfg(feature = "std")] +impl From<HSTRING> for std::ffi::OsString { + fn from(hstring: HSTRING) -> Self { + Self::from(&hstring) + } +} diff --git a/vendor/windows-strings/src/hstring_builder.rs b/vendor/windows-strings/src/hstring_builder.rs new file mode 100644 index 00000000..fcbe4d98 --- /dev/null +++ b/vendor/windows-strings/src/hstring_builder.rs @@ -0,0 +1,100 @@ +use super::*; + +/// An [HSTRING] builder that supports preallocating the `HSTRING` to avoid extra allocations and copies. +/// +/// This is similar to the `WindowsPreallocateStringBuffer` function but implemented directly in Rust for efficiency. +/// It is implemented as a separate type since [HSTRING] values are immutable. +pub struct HStringBuilder(*mut HStringHeader); + +impl HStringBuilder { + /// Creates a preallocated `HSTRING` value. + pub fn new(len: usize) -> Self { + let header = HStringHeader::alloc(len.try_into().unwrap()); + + if len > 0 { + unsafe { core::ptr::write_bytes((*header).data, 0, len) }; + } + + Self(header) + } + + /// Shortens the string by removing any trailing 0 characters. + pub fn trim_end(&mut self) { + if let Some(header) = self.as_header_mut() { + while header.len > 0 + && unsafe { header.data.offset(header.len as isize - 1).read() == 0 } + { + header.len -= 1; + } + + if header.len == 0 { + unsafe { + HStringHeader::free(self.0); + } + self.0 = core::ptr::null_mut(); + } + } + } + + /// Allows the `HSTRING` to be constructed from bytes. + pub fn as_bytes_mut(&mut self) -> &mut [u8] { + if let Some(header) = self.as_header() { + unsafe { + core::slice::from_raw_parts_mut(header.data as *mut _, header.len as usize * 2) + } + } else { + &mut [] + } + } + + fn as_header(&self) -> Option<&HStringHeader> { + unsafe { self.0.as_ref() } + } + + fn as_header_mut(&mut self) -> Option<&mut HStringHeader> { + unsafe { self.0.as_mut() } + } +} + +impl From<HStringBuilder> for HSTRING { + fn from(value: HStringBuilder) -> Self { + if let Some(header) = value.as_header() { + unsafe { header.data.offset(header.len as isize).write(0) }; + let result = Self(value.0); + core::mem::forget(value); + result + } else { + Self::new() + } + } +} + +impl core::ops::Deref for HStringBuilder { + type Target = [u16]; + + fn deref(&self) -> &[u16] { + if let Some(header) = self.as_header() { + unsafe { core::slice::from_raw_parts(header.data, header.len as usize) } + } else { + &[] + } + } +} + +impl core::ops::DerefMut for HStringBuilder { + fn deref_mut(&mut self) -> &mut [u16] { + if let Some(header) = self.as_header() { + unsafe { core::slice::from_raw_parts_mut(header.data, header.len as usize) } + } else { + &mut [] + } + } +} + +impl Drop for HStringBuilder { + fn drop(&mut self) { + unsafe { + HStringHeader::free(self.0); + } + } +} diff --git a/vendor/windows-strings/src/hstring_header.rs b/vendor/windows-strings/src/hstring_header.rs new file mode 100644 index 00000000..39401102 --- /dev/null +++ b/vendor/windows-strings/src/hstring_header.rs @@ -0,0 +1,70 @@ +use super::*; + +pub const HSTRING_REFERENCE_FLAG: u32 = 1; + +#[repr(C)] +pub struct HStringHeader { + pub flags: u32, + pub len: u32, + pub _0: u32, + pub _1: u32, + pub data: *mut u16, + pub count: RefCount, + pub buffer_start: u16, +} + +impl HStringHeader { + pub fn alloc(len: u32) -> *mut Self { + if len == 0 { + return core::ptr::null_mut(); + } + + // Allocate enough space for header and two bytes per character. + // The space for the terminating null character is already accounted for inside of `HStringHeader`. + let bytes = core::mem::size_of::<Self>() + 2 * len as usize; + + let header = + unsafe { bindings::HeapAlloc(bindings::GetProcessHeap(), 0, bytes) } as *mut Self; + + if header.is_null() { + panic!("allocation failed"); + } + + unsafe { + // Use `ptr::write` (since `header` is uninitialized). `HStringHeader` is safe to be all zeros. + header.write(core::mem::MaybeUninit::<Self>::zeroed().assume_init()); + (*header).len = len; + (*header).count = RefCount::new(1); + (*header).data = &mut (*header).buffer_start; + } + + header + } + + pub unsafe fn free(header: *mut Self) { + if header.is_null() { + return; + } + + unsafe { + bindings::HeapFree(bindings::GetProcessHeap(), 0, header as *mut _); + } + } + + pub fn duplicate(&self) -> *mut Self { + if self.flags & HSTRING_REFERENCE_FLAG == 0 { + // If this is not a "fast pass" string then simply increment the reference count. + self.count.add_ref(); + self as *const Self as *mut Self + } else { + // Otherwise, allocate a new string and copy the value into the new string. + let copy = Self::alloc(self.len); + // SAFETY: since we are duplicating the string it is safe to copy all data from self to the initialized `copy`. + // We copy `len + 1` characters since `len` does not account for the terminating null character. + unsafe { + core::ptr::copy_nonoverlapping(self.data, (*copy).data, self.len as usize + 1); + } + copy + } + } +} diff --git a/vendor/windows-strings/src/lib.rs b/vendor/windows-strings/src/lib.rs new file mode 100644 index 00000000..ba12bb17 --- /dev/null +++ b/vendor/windows-strings/src/lib.rs @@ -0,0 +1,47 @@ +#![doc = include_str!("../readme.md")] +#![cfg(windows)] +#![allow(non_snake_case)] +#![debugger_visualizer(natvis_file = "../windows-strings.natvis")] +#![cfg_attr(all(not(feature = "std")), no_std)] + +extern crate alloc; +use alloc::string::String; + +mod bstr; +pub use bstr::*; + +mod hstring; +pub use hstring::*; + +mod hstring_builder; +pub use hstring_builder::*; + +mod hstring_header; +use hstring_header::*; + +mod bindings; + +mod decode; +use decode::*; + +mod ref_count; +use ref_count::*; + +mod literals; +pub use literals::*; + +mod pcstr; +pub use pcstr::*; + +mod pcwstr; +pub use pcwstr::*; + +mod pstr; +pub use pstr::*; + +mod pwstr; +pub use pwstr::*; + +extern "C" { + fn strlen(s: PCSTR) -> usize; +} diff --git a/vendor/windows-strings/src/literals.rs b/vendor/windows-strings/src/literals.rs new file mode 100644 index 00000000..bb27e79a --- /dev/null +++ b/vendor/windows-strings/src/literals.rs @@ -0,0 +1,169 @@ +/// A literal UTF-8 string with a trailing null terminator. +#[macro_export] +macro_rules! s { + ($s:literal) => { + $crate::PCSTR::from_raw(::core::concat!($s, '\0').as_ptr()) + }; +} + +/// A literal UTF-16 wide string with a trailing null terminator. +#[macro_export] +macro_rules! w { + ($s:literal) => {{ + const INPUT: &[u8] = $s.as_bytes(); + const OUTPUT_LEN: usize = $crate::utf16_len(INPUT) + 1; + const OUTPUT: &[u16; OUTPUT_LEN] = { + let mut buffer = [0; OUTPUT_LEN]; + let mut input_pos = 0; + let mut output_pos = 0; + while let Some((mut code_point, new_pos)) = $crate::decode_utf8_char(INPUT, input_pos) { + input_pos = new_pos; + if code_point <= 0xffff { + buffer[output_pos] = code_point as u16; + output_pos += 1; + } else { + code_point -= 0x10000; + buffer[output_pos] = 0xd800 + (code_point >> 10) as u16; + output_pos += 1; + buffer[output_pos] = 0xdc00 + (code_point & 0x3ff) as u16; + output_pos += 1; + } + } + &{ buffer } + }; + $crate::PCWSTR::from_raw(OUTPUT.as_ptr()) + }}; +} + +/// A literal HSTRING, length-prefixed wide string with a trailing null terminator. +#[macro_export] +macro_rules! h { + ($s:literal) => {{ + const INPUT: &[u8] = $s.as_bytes(); + const OUTPUT_LEN: usize = $crate::utf16_len(INPUT) + 1; + static RESULT: $crate::HSTRING = { + if OUTPUT_LEN == 1 { + unsafe { ::core::mem::transmute(::core::ptr::null::<u16>()) } + } else { + const OUTPUT: $crate::PCWSTR = $crate::w!($s); + const HEADER: $crate::HSTRING_HEADER = $crate::HSTRING_HEADER { + flags: 0x11, + len: (OUTPUT_LEN - 1) as u32, + padding1: 0, + padding2: 0, + ptr: OUTPUT.as_ptr(), + padding3: 0, + padding4: 0, + }; + // SAFETY: an `HSTRING` is exactly equivalent to a pointer to an `HSTRING_HEADER` + unsafe { + ::core::mem::transmute::<&$crate::HSTRING_HEADER, $crate::HSTRING>(&HEADER) + } + } + }; + &RESULT + }}; +} + +#[doc(hidden)] +pub const fn decode_utf8_char(bytes: &[u8], mut pos: usize) -> Option<(u32, usize)> { + if bytes.len() == pos { + return None; + } + let ch = bytes[pos] as u32; + pos += 1; + if ch <= 0x7f { + return Some((ch, pos)); + } + if (ch & 0xe0) == 0xc0 { + if bytes.len() - pos < 1 { + return None; + } + let ch2 = bytes[pos] as u32; + pos += 1; + if (ch2 & 0xc0) != 0x80 { + return None; + } + let result: u32 = ((ch & 0x1f) << 6) | (ch2 & 0x3f); + if result <= 0x7f { + return None; + } + return Some((result, pos)); + } + if (ch & 0xf0) == 0xe0 { + if bytes.len() - pos < 2 { + return None; + } + let ch2 = bytes[pos] as u32; + pos += 1; + let ch3 = bytes[pos] as u32; + pos += 1; + if (ch2 & 0xc0) != 0x80 || (ch3 & 0xc0) != 0x80 { + return None; + } + let result = ((ch & 0x0f) << 12) | ((ch2 & 0x3f) << 6) | (ch3 & 0x3f); + if result <= 0x7ff || (0xd800 <= result && result <= 0xdfff) { + return None; + } + return Some((result, pos)); + } + if (ch & 0xf8) == 0xf0 { + if bytes.len() - pos < 3 { + return None; + } + let ch2 = bytes[pos] as u32; + pos += 1; + let ch3 = bytes[pos] as u32; + pos += 1; + let ch4 = bytes[pos] as u32; + pos += 1; + if (ch2 & 0xc0) != 0x80 || (ch3 & 0xc0) != 0x80 || (ch4 & 0xc0) != 0x80 { + return None; + } + let result = + ((ch & 0x07) << 18) | ((ch2 & 0x3f) << 12) | ((ch3 & 0x3f) << 6) | (ch4 & 0x3f); + if result <= 0xffff || 0x10ffff < result { + return None; + } + return Some((result, pos)); + } + None +} + +#[doc(hidden)] +#[repr(C)] +pub struct HSTRING_HEADER { + pub flags: u32, + pub len: u32, + pub padding1: u32, + pub padding2: u32, + pub ptr: *const u16, + pub padding3: i32, + pub padding4: u16, +} + +#[doc(hidden)] +pub const fn utf16_len(bytes: &[u8]) -> usize { + let mut pos = 0; + let mut len = 0; + while let Some((code_point, new_pos)) = decode_utf8_char(bytes, pos) { + pos = new_pos; + len += if code_point <= 0xffff { 1 } else { 2 }; + } + len +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test() { + assert_eq!(decode_utf8_char(b"123", 0), Some((0x31, 1))); + assert_eq!(decode_utf8_char(b"123", 1), Some((0x32, 2))); + assert_eq!(decode_utf8_char(b"123", 2), Some((0x33, 3))); + assert_eq!(decode_utf8_char(b"123", 3), None); + assert_eq!(utf16_len(b"123"), 3); + assert_eq!(utf16_len("α & ω".as_bytes()), 5); + } +} diff --git a/vendor/windows-strings/src/pcstr.rs b/vendor/windows-strings/src/pcstr.rs new file mode 100644 index 00000000..cd752854 --- /dev/null +++ b/vendor/windows-strings/src/pcstr.rs @@ -0,0 +1,64 @@ +use super::*; + +/// A pointer to a constant null-terminated string of 8-bit Windows (ANSI) characters. +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct PCSTR(pub *const u8); + +impl PCSTR { + /// Construct a new `PCSTR` from a raw pointer + pub const fn from_raw(ptr: *const u8) -> Self { + Self(ptr) + } + + /// Construct a null `PCSTR` + pub const fn null() -> Self { + Self(core::ptr::null()) + } + + /// Returns a raw pointer to the `PCSTR` + pub const fn as_ptr(&self) -> *const u8 { + self.0 + } + + /// Checks whether the `PCSTR` is null + pub fn is_null(&self) -> bool { + self.0.is_null() + } + + /// String data without the trailing 0 + /// + /// # Safety + /// + /// The `PCSTR`'s pointer needs to be valid for reads up until and including the next `\0`. + pub unsafe fn as_bytes(&self) -> &[u8] { + unsafe { + let len = strlen(*self); + core::slice::from_raw_parts(self.0, len) + } + } + + /// Copy the `PCSTR` into a Rust `String`. + /// + /// # Safety + /// + /// See the safety information for `PCSTR::as_bytes`. + pub unsafe fn to_string(&self) -> core::result::Result<String, alloc::string::FromUtf8Error> { + unsafe { String::from_utf8(self.as_bytes().into()) } + } + + /// Allow this string to be displayed. + /// + /// # Safety + /// + /// See the safety information for `PCSTR::as_bytes`. + pub unsafe fn display(&self) -> impl core::fmt::Display + '_ { + unsafe { Decode(move || decode_utf8(self.as_bytes())) } + } +} + +impl Default for PCSTR { + fn default() -> Self { + Self::null() + } +} diff --git a/vendor/windows-strings/src/pcwstr.rs b/vendor/windows-strings/src/pcwstr.rs new file mode 100644 index 00000000..221bfd9b --- /dev/null +++ b/vendor/windows-strings/src/pcwstr.rs @@ -0,0 +1,97 @@ +use super::*; + +/// A pointer to a constant null-terminated string of 16-bit Unicode characters. +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct PCWSTR(pub *const u16); + +impl PCWSTR { + /// Construct a new `PCWSTR` from a raw pointer + pub const fn from_raw(ptr: *const u16) -> Self { + Self(ptr) + } + + /// Construct a null `PCWSTR` + pub const fn null() -> Self { + Self(core::ptr::null()) + } + + /// Returns a raw pointer to the `PCWSTR` + pub const fn as_ptr(&self) -> *const u16 { + self.0 + } + + /// Checks whether the `PCWSTR` is null + pub fn is_null(&self) -> bool { + self.0.is_null() + } + + /// String length without the trailing 0 + /// + /// # Safety + /// + /// The `PCWSTR`'s pointer needs to be valid for reads up until and including the next `\0`. + pub unsafe fn len(&self) -> usize { + extern "C" { + fn wcslen(s: *const u16) -> usize; + } + unsafe { wcslen(self.0) } + } + + /// Returns `true` if the string length is zero, and `false` otherwise. + /// + /// # Safety + /// + /// The `PCWSTR`'s pointer needs to be valid for reads up until and including the next `\0`. + pub unsafe fn is_empty(&self) -> bool { + unsafe { self.len() == 0 } + } + + /// String data without the trailing 0 + /// + /// # Safety + /// + /// The `PCWSTR`'s pointer needs to be valid for reads up until and including the next `\0`. + pub unsafe fn as_wide(&self) -> &[u16] { + unsafe { core::slice::from_raw_parts(self.0, self.len()) } + } + + /// Copy the `PCWSTR` into a Rust `String`. + /// + /// # Safety + /// + /// See the safety information for `PCWSTR::as_wide`. + pub unsafe fn to_string(&self) -> core::result::Result<String, alloc::string::FromUtf16Error> { + unsafe { String::from_utf16(self.as_wide()) } + } + + /// Copy the `PCWSTR` into an `HSTRING`. + /// + /// # Safety + /// + /// See the safety information for `PCWSTR::as_wide`. + pub unsafe fn to_hstring(&self) -> HSTRING { + unsafe { HSTRING::from_wide(self.as_wide()) } + } + + /// Allow this string to be displayed. + /// + /// # Safety + /// + /// See the safety information for `PCWSTR::as_wide`. + pub unsafe fn display(&self) -> impl core::fmt::Display + '_ { + unsafe { Decode(move || core::char::decode_utf16(self.as_wide().iter().cloned())) } + } +} + +impl Default for PCWSTR { + fn default() -> Self { + Self::null() + } +} + +impl AsRef<PCWSTR> for PCWSTR { + fn as_ref(&self) -> &Self { + self + } +} diff --git a/vendor/windows-strings/src/pstr.rs b/vendor/windows-strings/src/pstr.rs new file mode 100644 index 00000000..141d481b --- /dev/null +++ b/vendor/windows-strings/src/pstr.rs @@ -0,0 +1,64 @@ +use super::*; + +/// A pointer to a null-terminated string of 8-bit Windows (ANSI) characters. +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct PSTR(pub *mut u8); + +impl PSTR { + /// Construct a new `PSTR` from a raw pointer + pub const fn from_raw(ptr: *mut u8) -> Self { + Self(ptr) + } + + /// Construct a null `PSTR` + pub const fn null() -> Self { + Self(core::ptr::null_mut()) + } + + /// Returns a raw pointer to the `PSTR` + pub const fn as_ptr(&self) -> *mut u8 { + self.0 + } + + /// Checks whether the `PSTR` is null + pub fn is_null(&self) -> bool { + self.0.is_null() + } + + /// String data without the trailing 0 + /// + /// # Safety + /// + /// The `PSTR`'s pointer needs to be valid for reads up until and including the next `\0`. + pub unsafe fn as_bytes(&self) -> &[u8] { + unsafe { + let len = strlen(PCSTR::from_raw(self.0)); + core::slice::from_raw_parts(self.0, len) + } + } + + /// Copy the `PSTR` into a Rust `String`. + /// + /// # Safety + /// + /// See the safety information for `PSTR::as_bytes`. + pub unsafe fn to_string(&self) -> core::result::Result<String, alloc::string::FromUtf8Error> { + unsafe { String::from_utf8(self.as_bytes().into()) } + } + + /// Allow this string to be displayed. + /// + /// # Safety + /// + /// See the safety information for `PSTR::as_bytes`. + pub unsafe fn display(&self) -> impl core::fmt::Display + '_ { + unsafe { Decode(move || decode_utf8(self.as_bytes())) } + } +} + +impl Default for PSTR { + fn default() -> Self { + Self::null() + } +} diff --git a/vendor/windows-strings/src/pwstr.rs b/vendor/windows-strings/src/pwstr.rs new file mode 100644 index 00000000..db4d74ac --- /dev/null +++ b/vendor/windows-strings/src/pwstr.rs @@ -0,0 +1,88 @@ +use super::*; + +/// A pointer to a null-terminated string of 16-bit Unicode characters. +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct PWSTR(pub *mut u16); + +impl PWSTR { + /// Construct a new `PWSTR` from a raw pointer. + pub const fn from_raw(ptr: *mut u16) -> Self { + Self(ptr) + } + + /// Construct a null `PWSTR`. + pub const fn null() -> Self { + Self(core::ptr::null_mut()) + } + + /// Returns a raw pointer to the `PWSTR`. + pub const fn as_ptr(&self) -> *mut u16 { + self.0 + } + + /// Checks whether the `PWSTR` is null. + pub fn is_null(&self) -> bool { + self.0.is_null() + } + + /// String length without the trailing 0 + /// + /// # Safety + /// + /// The `PWSTR`'s pointer needs to be valid for reads up until and including the next `\0`. + pub unsafe fn len(&self) -> usize { + unsafe { PCWSTR(self.0).len() } + } + + /// Returns `true` if the string length is zero, and `false` otherwise. + /// + /// # Safety + /// + /// The `PWSTR`'s pointer needs to be valid for reads up until and including the next `\0`. + pub unsafe fn is_empty(&self) -> bool { + unsafe { self.len() == 0 } + } + + /// String data without the trailing 0. + /// + /// # Safety + /// + /// The `PWSTR`'s pointer needs to be valid for reads up until and including the next `\0`. + pub unsafe fn as_wide(&self) -> &[u16] { + unsafe { core::slice::from_raw_parts(self.0, self.len()) } + } + + /// Copy the `PWSTR` into a Rust `String`. + /// + /// # Safety + /// + /// See the safety information for `PWSTR::as_wide`. + pub unsafe fn to_string(&self) -> core::result::Result<String, alloc::string::FromUtf16Error> { + unsafe { String::from_utf16(self.as_wide()) } + } + + /// Copy the `PWSTR` into an `HSTRING`. + /// + /// # Safety + /// + /// See the safety information for `PWSTR::as_wide`. + pub unsafe fn to_hstring(&self) -> HSTRING { + unsafe { HSTRING::from_wide(self.as_wide()) } + } + + /// Allow this string to be displayed. + /// + /// # Safety + /// + /// See the safety information for `PWSTR::as_wide`. + pub unsafe fn display(&self) -> impl core::fmt::Display + '_ { + unsafe { Decode(move || core::char::decode_utf16(self.as_wide().iter().cloned())) } + } +} + +impl Default for PWSTR { + fn default() -> Self { + Self::null() + } +} diff --git a/vendor/windows-strings/src/ref_count.rs b/vendor/windows-strings/src/ref_count.rs new file mode 100644 index 00000000..c6309f9f --- /dev/null +++ b/vendor/windows-strings/src/ref_count.rs @@ -0,0 +1,27 @@ +use core::sync::atomic::{fence, AtomicI32, Ordering}; + +#[repr(transparent)] +#[derive(Default)] +pub struct RefCount(pub(crate) AtomicI32); + +impl RefCount { + pub fn new(count: u32) -> Self { + Self(AtomicI32::new(count as i32)) + } + + pub fn add_ref(&self) -> u32 { + (self.0.fetch_add(1, Ordering::Relaxed) + 1) as u32 + } + + pub fn release(&self) -> u32 { + let remaining = self.0.fetch_sub(1, Ordering::Release) - 1; + + match remaining.cmp(&0) { + core::cmp::Ordering::Equal => fence(Ordering::Acquire), + core::cmp::Ordering::Less => panic!("Object has been over-released."), + core::cmp::Ordering::Greater => {} + } + + remaining as u32 + } +} |
