Skip to main content

Wtf16String

Type Alias Wtf16String 

Source
pub type Wtf16String = WtfString<Wtf16>;
Expand description

A WtfString whose storage is WTF-16 (u16 code units).

Aliased Type§

pub struct Wtf16String { /* private fields */ }

Implementations§

Source§

impl Wtf16String

Source

pub fn from_os_str(s: &OsStr) -> Self

Encode an OsStr into owned WTF-16, converting once at the boundary.

Lossless: unpaired surrogates survive, so from_os_str(x).to_os_string() == x. This is the owning analog of collecting OsStrExt::encode_wide.

Source

pub fn from_wide(units: &[u16]) -> Self

Build from already-wide code units: the OsStringExt::from_wide analog.

Identical to from_units, named for drop-in familiarity when replacing an OsString::from_wide call site.

Source§

impl Wtf16String

Source

pub fn as_terminated_ptr(&self) -> *const u16

A NUL-terminated *const u16 (LPCWSTR/PCWSTR) over the whole buffer.

The always-present terminator makes this allocation-free (D-7). It is a valid C string only when has_interior_nul is false; otherwise a reader stops at the first interior NUL. The pointer is valid while self is borrowed and unmodified.

Examples found in repository?
examples/win32_round_trip.rs (line 99)
92    fn full_path(input: &Wtf16String) -> Option<Wtf16String> {
93        // Pass 1: ask for the size. The terminator is already in the buffer, so
94        // handing over an `LPCWSTR` costs nothing.
95        // SAFETY: `as_terminated_ptr` is NUL-terminated and valid while
96        // `input` is borrowed; a zero length asks for the required size only.
97        let needed = unsafe {
98            GetFullPathNameW(
99                input.as_terminated_ptr(),
100                0,
101                core::ptr::null_mut(),
102                core::ptr::null_mut(),
103            )
104        };
105        if needed == 0 {
106            return None;
107        }
108
109        // `needed` counts the terminator; our capacity is a *content* length,
110        // and `with_capacity` reserves the terminator slot itself.
111        let mut out = Wtf16String::with_capacity(needed as usize - 1);
112
113        // Pass 2: let the API write straight into our buffer.
114        // SAFETY: the buffer has room for `needed` units (content + the
115        // reserved terminator slot), which is exactly what pass 1 asked for.
116        let written = unsafe {
117            GetFullPathNameW(
118                input.as_terminated_ptr(),
119                needed,
120                out.as_mut_ptr(),
121                core::ptr::null_mut(),
122            )
123        };
124        if written == 0 || written >= needed {
125            // Failed, or raced a directory change and now wants more room.
126            // `out`'s invariant is still broken here, so republish an empty
127            // string before dropping it (see `as_mut_ptr`'s contract).
128            // SAFETY: publishing zero content units is always in bounds.
129            unsafe { out.set_len_from_ffi(0) };
130            return None;
131        }
132
133        // `written` excludes the terminator, which is precisely the content
134        // length `set_len_from_ffi` wants -- no guessing about conventions.
135        // SAFETY: the API initialized `written` units and `written < needed`,
136        // so the appended terminator still fits.
137        unsafe { out.set_len_from_ffi(written as usize) };
138        Some(out)
139    }
Source

pub fn with_capacity(units: usize) -> Self

An empty string with room for units content code units to be filled in place via as_mut_ptr plus set_len_from_ffi.

The reserved capacity also covers the always-present terminator, so a later set_len_from_ffi of up to units content units re-establishes the invariant without reallocating (D-9).

Examples found in repository?
examples/win32_round_trip.rs (line 111)
92    fn full_path(input: &Wtf16String) -> Option<Wtf16String> {
93        // Pass 1: ask for the size. The terminator is already in the buffer, so
94        // handing over an `LPCWSTR` costs nothing.
95        // SAFETY: `as_terminated_ptr` is NUL-terminated and valid while
96        // `input` is borrowed; a zero length asks for the required size only.
97        let needed = unsafe {
98            GetFullPathNameW(
99                input.as_terminated_ptr(),
100                0,
101                core::ptr::null_mut(),
102                core::ptr::null_mut(),
103            )
104        };
105        if needed == 0 {
106            return None;
107        }
108
109        // `needed` counts the terminator; our capacity is a *content* length,
110        // and `with_capacity` reserves the terminator slot itself.
111        let mut out = Wtf16String::with_capacity(needed as usize - 1);
112
113        // Pass 2: let the API write straight into our buffer.
114        // SAFETY: the buffer has room for `needed` units (content + the
115        // reserved terminator slot), which is exactly what pass 1 asked for.
116        let written = unsafe {
117            GetFullPathNameW(
118                input.as_terminated_ptr(),
119                needed,
120                out.as_mut_ptr(),
121                core::ptr::null_mut(),
122            )
123        };
124        if written == 0 || written >= needed {
125            // Failed, or raced a directory change and now wants more room.
126            // `out`'s invariant is still broken here, so republish an empty
127            // string before dropping it (see `as_mut_ptr`'s contract).
128            // SAFETY: publishing zero content units is always in bounds.
129            unsafe { out.set_len_from_ffi(0) };
130            return None;
131        }
132
133        // `written` excludes the terminator, which is precisely the content
134        // length `set_len_from_ffi` wants -- no guessing about conventions.
135        // SAFETY: the API initialized `written` units and `written < needed`,
136        // so the appended terminator still fits.
137        unsafe { out.set_len_from_ffi(written as usize) };
138        Some(out)
139    }
Source

pub fn as_mut_ptr(&mut self) -> *mut u16

A mutable pointer to the start of the buffer, for a foreign buffer-fill.

with_capacity(n) reserves n + 1 units: room for n content units plus the terminator slot. A foreign API may fill up to n content units, and one more if it writes its own terminator into the reserved slot (n + 1 units total). Either way, pass only the content length to set_len_from_ffi, which publishes that length and re-establishes the terminator.

Writing through this pointer overwrites the buffer – including element 0, which is the sole terminator of a fresh with_capacity – so it breaks the always-terminated invariant until set_len_from_ffi restores it. Between the write and that call the value must not be observed through any other method (as_terminated_ptr, Deref content access, Clone, Debug, PartialEq, …): they could read a non-terminated or partially written buffer. This holds on failure paths too – if the foreign call fails, restore the invariant with set_len_from_ffi(0) (the empty string) or drop the value before any other use. The pointer is valid while self is borrowed and not reallocated.

Examples found in repository?
examples/win32_round_trip.rs (line 120)
92    fn full_path(input: &Wtf16String) -> Option<Wtf16String> {
93        // Pass 1: ask for the size. The terminator is already in the buffer, so
94        // handing over an `LPCWSTR` costs nothing.
95        // SAFETY: `as_terminated_ptr` is NUL-terminated and valid while
96        // `input` is borrowed; a zero length asks for the required size only.
97        let needed = unsafe {
98            GetFullPathNameW(
99                input.as_terminated_ptr(),
100                0,
101                core::ptr::null_mut(),
102                core::ptr::null_mut(),
103            )
104        };
105        if needed == 0 {
106            return None;
107        }
108
109        // `needed` counts the terminator; our capacity is a *content* length,
110        // and `with_capacity` reserves the terminator slot itself.
111        let mut out = Wtf16String::with_capacity(needed as usize - 1);
112
113        // Pass 2: let the API write straight into our buffer.
114        // SAFETY: the buffer has room for `needed` units (content + the
115        // reserved terminator slot), which is exactly what pass 1 asked for.
116        let written = unsafe {
117            GetFullPathNameW(
118                input.as_terminated_ptr(),
119                needed,
120                out.as_mut_ptr(),
121                core::ptr::null_mut(),
122            )
123        };
124        if written == 0 || written >= needed {
125            // Failed, or raced a directory change and now wants more room.
126            // `out`'s invariant is still broken here, so republish an empty
127            // string before dropping it (see `as_mut_ptr`'s contract).
128            // SAFETY: publishing zero content units is always in bounds.
129            unsafe { out.set_len_from_ffi(0) };
130            return None;
131        }
132
133        // `written` excludes the terminator, which is precisely the content
134        // length `set_len_from_ffi` wants -- no guessing about conventions.
135        // SAFETY: the API initialized `written` units and `written < needed`,
136        // so the appended terminator still fits.
137        unsafe { out.set_len_from_ffi(written as usize) };
138        Some(out)
139    }
Source

pub unsafe fn set_len_from_ffi(&mut self, content_units: usize)

Publish content_units content code units written into the buffer from as_mut_ptr, then append the terminator.

content_units counts content only and never includes a terminator. The written units are taken verbatim – they may themselves end in NUL, since interior NULs are permitted (see has_interior_nul) – and exactly one terminator is appended. A foreign API that reports a count including the terminator it wrote must subtract one and pass the content length; this method never inspects the buffer to guess the convention, so a genuine trailing content NUL is never mistaken for the terminator.

§Safety

The caller must guarantee that:

  • the first content_units code units at as_mut_ptr are initialized u16 values, and
  • content_units does not exceed the count requested via with_capacity, so the appended terminator fits without reallocating a buffer whose pointer the caller may still hold.
Examples found in repository?
examples/win32_round_trip.rs (line 129)
92    fn full_path(input: &Wtf16String) -> Option<Wtf16String> {
93        // Pass 1: ask for the size. The terminator is already in the buffer, so
94        // handing over an `LPCWSTR` costs nothing.
95        // SAFETY: `as_terminated_ptr` is NUL-terminated and valid while
96        // `input` is borrowed; a zero length asks for the required size only.
97        let needed = unsafe {
98            GetFullPathNameW(
99                input.as_terminated_ptr(),
100                0,
101                core::ptr::null_mut(),
102                core::ptr::null_mut(),
103            )
104        };
105        if needed == 0 {
106            return None;
107        }
108
109        // `needed` counts the terminator; our capacity is a *content* length,
110        // and `with_capacity` reserves the terminator slot itself.
111        let mut out = Wtf16String::with_capacity(needed as usize - 1);
112
113        // Pass 2: let the API write straight into our buffer.
114        // SAFETY: the buffer has room for `needed` units (content + the
115        // reserved terminator slot), which is exactly what pass 1 asked for.
116        let written = unsafe {
117            GetFullPathNameW(
118                input.as_terminated_ptr(),
119                needed,
120                out.as_mut_ptr(),
121                core::ptr::null_mut(),
122            )
123        };
124        if written == 0 || written >= needed {
125            // Failed, or raced a directory change and now wants more room.
126            // `out`'s invariant is still broken here, so republish an empty
127            // string before dropping it (see `as_mut_ptr`'s contract).
128            // SAFETY: publishing zero content units is always in bounds.
129            unsafe { out.set_len_from_ffi(0) };
130            return None;
131        }
132
133        // `written` excludes the terminator, which is precisely the content
134        // length `set_len_from_ffi` wants -- no guessing about conventions.
135        // SAFETY: the API initialized `written` units and `written < needed`,
136        // so the appended terminator still fits.
137        unsafe { out.set_len_from_ffi(written as usize) };
138        Some(out)
139    }
Source

pub unsafe fn from_wide_ptr(ptr: *const u16, len: usize) -> Self

Copy len content code units from a foreign *const u16 into a new owned string, appending the terminator.

For callee-allocated Win32 output: the bytes are copied, so the caller keeps ownership of (and remains responsible for freeing) the source buffer. The copy is lossless — arbitrary WTF-16, including unpaired surrogates, is preserved (D-4/D-9).

§Safety

This copies the range through core::slice::from_raw_parts and shares its preconditions. When len > 0 the caller must guarantee that:

  • ptr is non-null and properly aligned for u16;
  • ptr is valid for reads of len consecutive, initialized u16 values, all contained within a single allocated object;
  • the total size len * size_of::<u16>() is no larger than isize::MAX, and adding it to ptr does not wrap the address space; and
  • that region stays unmutated for the duration of the call.

When len == 0 the pointer is not dereferenced, so it may be null or dangling. No reference to ptr is retained past the call. len is a count of code units, not bytes, and excludes any terminator the callee may have written (pass the content length).

Trait Implementations§

Source§

impl From<&OsStr> for Wtf16String

Source§

fn from(s: &OsStr) -> Self

Converts to this type from the input type.
Source§

impl From<&OsString> for Wtf16String

Source§

fn from(s: &OsString) -> Self

Converts to this type from the input type.
Source§

impl From<OsString> for Wtf16String

Source§

fn from(s: OsString) -> Self

Converts to this type from the input type.
Source§

impl Param<PCWSTR> for &Wtf16String

Pass a &Wtf16String directly to a windows API taking impl Param<PCWSTR>.

The pointer handed over is as_terminated_ptr: no conversion, no allocation, and no copy. It stays valid for the call because the borrow keeps the owning Wtf16String alive and unmodified.

This carries exactly the C-string caveat of the pointer it wraps (D-7): the callee stops at the first NUL, so a value with an interior NUL is seen truncated. Check has_interior_nul first when the content may contain one. &HSTRING’s own Param<PCWSTR> impl has the same property, so this is parity with the ecosystem, not a new hazard.

There is deliberately no impl for &Wtf16Str: a borrowed slice carries no terminator (D-7), so it has no valid PCWSTR to give. Borrowed content reaches Win32 through the counted pair as_ptr + len instead.