#![allow(unsafe_code)]
use winapi::shared::wtypes::BSTR;
use winapi::shared::wtypesbase::OLECHAR;
use winapi::um::oleauto::*;
use std::convert::TryInto;
use std::ops::{Deref, Drop};
use std::ptr::{null, null_mut, NonNull};
mod invariants {}
#[repr(transparent)] pub struct BString(NonNull<OLECHAR>);
unsafe impl Send for BString {}
impl Deref for BString {
type Target = BStr;
fn deref(&self) -> &BStr { unsafe { std::mem::transmute(self.0) } }
}
impl Drop for BString {
fn drop(&mut self) { unsafe { SysFreeString(self.0.as_ptr()) }; }
}
impl BString {
pub fn from_code_units(mut code_units: impl ExactSizeIterator + Iterator<Item = u16>) -> Option<BString> {
let len : usize = code_units.len();
let len32 : u32 = len.try_into().ok()?;
if len32 >= std::u32::MAX/2 { return None; }
let bstr = unsafe { SysAllocStringLen(null(), len32) };
let r = BString(NonNull::new(bstr)?);
for off in 0..len {
unsafe { *bstr.add(off) = code_units.next().unwrap_or(0u16) };
}
unsafe { *bstr.add(len) = 0u16 };
Some(r)
}
}
#[repr(transparent)] pub struct BStr(OLECHAR);
impl BStr {
#[doc(hidden)]
pub fn bstr_macro_impl_detail(data: &'static [u32]) -> &'static BStr {
let bytes_len32 = data[0];
let bytes_len = bytes_len32 as usize;
let cu_len = bytes_len/2;
let data : &'static [u16] = unsafe { std::slice::from_raw_parts(data.as_ptr().add(1).cast(), 2*(data.len()-1)) };
assert!(data[cu_len] == 0u16, "`data` was supposed to be `\0`-terminated");
let r : &'static BStr = unsafe { std::mem::transmute(data.as_ptr()) };
r
}
pub unsafe fn from_bstr(bstr: &BSTR) -> Option<&BStr> {
let s : Option<&BStr> = std::mem::transmute(*bstr);
if s?.len32() == std::u32::MAX { return None; } s
}
pub unsafe fn from_bstr_unbounded<'b>(bstr: BSTR) -> Option<&'b BStr> {
let s : Option<&BStr> = std::mem::transmute(bstr);
if s?.len32() == std::u32::MAX { return None; } s
}
pub fn as_bstr(&self) -> BSTR { unsafe { std::mem::transmute(self) } }
pub fn len32(&self) -> u32 { unsafe { SysStringLen(self.as_bstr()) } }
#[cfg(not(target_pointer_width = "16"))]
pub fn units0(&self) -> &[u16] { unsafe { std::slice::from_raw_parts(self.as_bstr(), self.len0()) } }
}
pub unsafe trait AsBStrPtr : AsRef<BStr> {
fn as_bstr_ptr(&self) -> BSTR { self.as_ref().as_bstr() }
}
unsafe impl<B: AsRef<BStr>> AsBStrPtr for B {}
pub unsafe trait AsOptBStrPtr {
fn as_opt_bstr_ptr(&self) -> BSTR;
}
unsafe impl<B: AsRef<BStr>> AsOptBStrPtr for Option<B> {
fn as_opt_bstr_ptr(&self) -> BSTR { self.as_ref().map_or(null_mut(), |s| s.as_ref().as_bstr()) }
}
unsafe impl<B: AsRef<BStr>> AsOptBStrPtr for B {
fn as_opt_bstr_ptr(&self) -> BSTR { self.as_ref().as_bstr() }
}
#[test] fn layout() {
use std::mem::align_of;
use std::mem::size_of;
assert_eq!(align_of::<&BStr>(), align_of::<BSTR>());
assert_eq!(align_of::<&BString>(), align_of::<BSTR>());
assert_eq!(align_of::< BString>(), align_of::<BSTR>());
assert_eq!(align_of::<Option<&BStr>>(), align_of::<BSTR>());
assert_eq!(align_of::<Option<&BString>>(), align_of::<BSTR>());
assert_eq!(align_of::<Option< BString>>(), align_of::<BSTR>());
assert_eq!(size_of::<&BStr>(), size_of::<BSTR>());
assert_eq!(size_of::<&BString>(), size_of::<BSTR>());
assert_eq!(size_of::< BString>(), size_of::<BSTR>());
assert_eq!(size_of::<Option<&BStr>>(), size_of::<BSTR>());
assert_eq!(size_of::<Option<&BString>>(), size_of::<BSTR>());
assert_eq!(size_of::<Option< BString>>(), size_of::<BSTR>());
}
#[test] fn core_apis() {
fn dbg<T: std::fmt::Debug>(v: &T) -> String { format!("{:?}", v) }
let hello_world = "Hello, world!\0\r\n\t\x12\u{1234}\u{10000}©™";
let a = BString::from_code_units(hello_world.encode_utf16().collect::<Vec<_>>().into_iter()).unwrap();
let b : &BStr = &a;
let c = b.as_bstr();
let d = unsafe { BStr::from_bstr(&c) }.unwrap();
let e = unsafe { BStr::from_bstr_unbounded(c) }.unwrap();
let f = bstr!("Hello, world!\0\r\n\t\x12\u{1234}\u{10000}©™");
assert_eq!(dbg(&hello_world), dbg(&a));
assert_eq!(dbg(&hello_world), dbg(&b));
assert_eq!(dbg(&hello_world), dbg(&d));
assert_eq!(dbg(&hello_world), dbg(&e));
assert_eq!(dbg(&hello_world), dbg(&f));
assert_eq!(a, a);
assert_eq!(a, b);
assert_eq!(a, d);
assert_eq!(a, e);
assert_eq!(a, f);
assert_eq!(c, a.as_bstr());
assert_eq!(c, b.as_bstr());
assert_eq!(c, d.as_bstr());
assert_eq!(c, e.as_bstr());
assert_eq!(a.len(), b.len()); assert_eq!(a.len(), d.len()); assert_eq!(a.len(), e.len()); assert_eq!(a.len(), f.len()); assert_eq!(a.len(), d.len0()-1);
assert_eq!(a.len(), e.len0()-1);
assert_eq!(a.len(), f.len0()-1);
assert_eq!(d.len(), d.len32() as usize);
assert_eq!(e.len(), e.len32() as usize);
assert_eq!(e.len(), f.len32() as usize);
assert_eq!(d.len0(), d.len320() as usize);
assert_eq!(e.len0(), e.len320() as usize);
assert_eq!(e.len0(), f.len320() as usize);
assert!(hello_world.encode_utf16().eq(d.units().iter().copied()));
assert!(hello_world.encode_utf16().eq(e.units().iter().copied()));
assert!(hello_world.encode_utf16().eq(f.units().iter().copied()));
assert!(hello_world.encode_utf16().chain(Some(0)).eq(d.units0().iter().copied()));
assert!(hello_world.encode_utf16().chain(Some(0)).eq(e.units0().iter().copied()));
assert!(hello_world.encode_utf16().chain(Some(0)).eq(f.units0().iter().copied()));
}