#[repr(transparent)]
pub struct CoString(*mut u16);
impl CoString {
pub fn new() -> Self {
Self(std::ptr::null_mut())
}
unsafe fn from_wide_iter<I>(iter: I, len: usize) -> Self
where
I: IntoIterator<Item = u16>,
{
debug_assert!(len > 0, "Trying to allocate an CoString with len 0");
let start = CoTaskMemAlloc(len * 2 + 2) as *mut u16;
assert!(!start.is_null(), "Could not allocate memory for CoString");
let mut cursor = start;
for (index, c) in iter.into_iter().enumerate() {
debug_assert!(index < len);
cursor.write(c);
cursor = cursor.add(1);
}
debug_assert!(
cursor != start,
"Trying to allocate a CoString with zero elements"
);
cursor.write(0);
Self(start)
}
pub fn as_wide(&self) -> impl Iterator<Item = u16> {
unsafe { WideStringIter::new(self.0) }
}
pub fn is_empty(&self) -> bool {
self.0.is_null()
}
}
impl Drop for CoString {
fn drop(&mut self) {
unsafe { CoTaskMemFree(self.0 as _) }
}
}
impl From<&CoString> for String {
fn from(cs: &CoString) -> Self {
std::char::decode_utf16(cs.as_wide())
.map(|r| r.unwrap())
.collect::<String>()
}
}
impl From<CoString> for String {
fn from(cs: CoString) -> Self {
(&cs).into()
}
}
impl From<&str> for CoString {
fn from(s: &str) -> Self {
if s.is_empty() {
return Self::new();
}
unsafe { Self::from_wide_iter(s.encode_utf16(), s.len()) }
}
}
impl From<String> for CoString {
fn from(s: String) -> Self {
s.as_str().into()
}
}
impl std::fmt::Display for CoString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::fmt::Write;
for c in std::char::decode_utf16(self.as_wide()) {
f.write_char(c.unwrap())?
}
Ok(())
}
}
impl std::fmt::Debug for CoString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self)
}
}
#[link(name = "OLE32")]
extern "system" {
pub fn CoTaskMemAlloc(size: usize) -> *mut std::ffi::c_void;
pub fn CoTaskMemFree(pv: *mut std::ffi::c_void);
}
struct WideStringIter {
ptr: *mut u16,
}
impl WideStringIter {
unsafe fn new(ptr: *mut u16) -> Self {
Self { ptr }
}
}
impl Iterator for WideStringIter {
type Item = u16;
fn next(&mut self) -> Option<Self::Item> {
if self.ptr.is_null() {
return None;
}
let val = unsafe { self.ptr.read() };
if val == 0 {
self.ptr = std::ptr::null_mut();
return None;
}
self.ptr = unsafe { self.ptr.add(1) };
Some(val)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn conversions() {
let original_string = "Hello, world!";
let co_string: CoString = original_string.into();
assert!(!co_string.is_empty());
let string: String = co_string.into();
assert_eq!(string, original_string);
let empty: CoString = "".into();
assert!(empty.is_empty());
assert_eq!(&String::from(empty), "");
}
}