Skip to content

std: Add trait ToCU16Str #14242

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/libnative/io/c_win32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ extern "system" {
pub mod compat {
use std::intrinsics::{atomic_store_relaxed, transmute};
use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID};
use std::os::win32::as_utf16_p;
use std::c_str::ToCU16Str;

extern "system" {
fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
Expand All @@ -80,7 +80,7 @@ pub mod compat {
// This way, calling a function in this compatibility layer (after it's loaded) shouldn't
// be any slower than a regular DLL call.
unsafe fn store_func<T: Copy>(ptr: *mut T, module: &str, symbol: &str, fallback: T) {
as_utf16_p(module, |module| {
module.with_c_u16_str(|module| {
symbol.with_c_str(|symbol| {
let handle = GetModuleHandleW(module);
let func: Option<T> = transmute(GetProcAddress(handle, symbol));
Expand Down
33 changes: 17 additions & 16 deletions src/libnative/io/file_win32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ use std::io;
use libc::{c_int, c_void};
use libc;
use std::mem;
use std::os::win32::{as_utf16_p, fill_utf16_buf_and_decode};
use std::os::win32::fill_utf16_buf_and_decode;
use std::ptr;
use std::rt::rtio;
use std::str;
use std::c_str::ToCU16Str;
use std::sync::arc::UnsafeArc;
use std::vec;

Expand Down Expand Up @@ -312,7 +313,7 @@ pub fn open(path: &CString, fm: io::FileMode, fa: io::FileAccess)
// Compat with unix, this allows opening directories (see libuv)
dwFlagsAndAttributes |= libc::FILE_FLAG_BACKUP_SEMANTICS;

let handle = as_utf16_p(path.as_str().unwrap(), |buf| unsafe {
let handle = path.as_str().unwrap().with_c_u16_str(|buf| unsafe {
libc::CreateFileW(buf,
dwDesiredAccess,
dwShareMode,
Expand All @@ -339,7 +340,7 @@ pub fn open(path: &CString, fm: io::FileMode, fa: io::FileAccess)
pub fn mkdir(p: &CString, _mode: io::FilePermission) -> IoResult<()> {
super::mkerr_winbool(unsafe {
// FIXME: turn mode into something useful? #2623
as_utf16_p(p.as_str().unwrap(), |buf| {
p.as_str().unwrap().with_c_u16_str(|buf| {
libc::CreateDirectoryW(buf, ptr::mut_null())
})
})
Expand All @@ -364,7 +365,7 @@ pub fn readdir(p: &CString) -> IoResult<Vec<Path>> {
let star = Path::new(unsafe {
CString::new(p.with_ref(|p| p), false)
}).join("*");
as_utf16_p(star.as_str().unwrap(), |path_ptr| unsafe {
star.as_str().unwrap().with_c_u16_str(|path_ptr| unsafe {
let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint);
let find_handle = libc::FindFirstFileW(path_ptr, wfd_ptr as libc::HANDLE);
if find_handle as libc::c_int != libc::INVALID_HANDLE_VALUE {
Expand Down Expand Up @@ -395,30 +396,30 @@ pub fn readdir(p: &CString) -> IoResult<Vec<Path>> {

pub fn unlink(p: &CString) -> IoResult<()> {
super::mkerr_winbool(unsafe {
as_utf16_p(p.as_str().unwrap(), |buf| {
p.as_str().unwrap().with_c_u16_str(|buf| {
libc::DeleteFileW(buf)
})
})
}

pub fn rename(old: &CString, new: &CString) -> IoResult<()> {
super::mkerr_winbool(unsafe {
as_utf16_p(old.as_str().unwrap(), |old| {
as_utf16_p(new.as_str().unwrap(), |new| {
old.as_str().unwrap().with_c_u16_str(|old| {
new.as_str().unwrap().with_c_u16_str(|new| {
libc::MoveFileExW(old, new, libc::MOVEFILE_REPLACE_EXISTING)
})
})
})
}

pub fn chmod(p: &CString, mode: io::FilePermission) -> IoResult<()> {
super::mkerr_libc(as_utf16_p(p.as_str().unwrap(), |p| unsafe {
super::mkerr_libc(p.as_str().unwrap().with_c_u16_str(|p| unsafe {
libc::wchmod(p, mode.bits() as libc::c_int)
}))
}

pub fn rmdir(p: &CString) -> IoResult<()> {
super::mkerr_libc(as_utf16_p(p.as_str().unwrap(), |p| unsafe {
super::mkerr_libc(p.as_str().unwrap().with_c_u16_str(|p| unsafe {
libc::wrmdir(p)
}))
}
Expand All @@ -432,7 +433,7 @@ pub fn readlink(p: &CString) -> IoResult<Path> {
// FIXME: I have a feeling that this reads intermediate symlinks as well.
use io::c::compat::kernel32::GetFinalPathNameByHandleW;
let handle = unsafe {
as_utf16_p(p.as_str().unwrap(), |p| {
p.as_str().unwrap().with_c_u16_str(|p| {
libc::CreateFileW(p,
libc::GENERIC_READ,
libc::FILE_SHARE_READ,
Expand Down Expand Up @@ -464,16 +465,16 @@ pub fn readlink(p: &CString) -> IoResult<Path> {

pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> {
use io::c::compat::kernel32::CreateSymbolicLinkW;
super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| {
as_utf16_p(dst.as_str().unwrap(), |dst| {
super::mkerr_winbool(src.as_str().unwrap().with_c_u16_str(|src| {
dst.as_str().unwrap().with_c_u16_str(|dst| {
unsafe { CreateSymbolicLinkW(dst, src, 0) }
}) as libc::BOOL
}))
}

pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| {
as_utf16_p(dst.as_str().unwrap(), |dst| {
super::mkerr_winbool(src.as_str().unwrap().with_c_u16_str(|src| {
dst.as_str().unwrap().with_c_u16_str(|dst| {
unsafe { libc::CreateHardLinkW(dst, src, ptr::mut_null()) }
})
}))
Expand Down Expand Up @@ -513,7 +514,7 @@ fn mkstat(stat: &libc::stat) -> io::FileStat {

pub fn stat(p: &CString) -> IoResult<io::FileStat> {
let mut stat: libc::stat = unsafe { mem::uninit() };
as_utf16_p(p.as_str().unwrap(), |up| {
p.as_str().unwrap().with_c_u16_str(|up| {
match unsafe { libc::wstat(up, &mut stat) } {
0 => Ok(mkstat(&stat)),
_ => Err(super::last_error()),
Expand All @@ -531,7 +532,7 @@ pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> {
actime: (atime / 1000) as libc::time64_t,
modtime: (mtime / 1000) as libc::time64_t,
};
super::mkerr_libc(as_utf16_p(p.as_str().unwrap(), |p| unsafe {
super::mkerr_libc(p.as_str().unwrap().with_c_u16_str(|p| unsafe {
libc::wutime(p, &buf)
}))
}
9 changes: 4 additions & 5 deletions src/libnative/io/pipe_win32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,9 @@
//! me!

use libc;
use std::c_str::CString;
use std::c_str::{CString, ToCU16Str};
use std::intrinsics;
use std::io;
use std::os::win32::as_utf16_p;
use std::os;
use std::ptr;
use std::rt::rtio;
Expand Down Expand Up @@ -254,7 +253,7 @@ impl UnixStream {
}

pub fn connect(addr: &CString, timeout: Option<u64>) -> IoResult<UnixStream> {
as_utf16_p(addr.as_str().unwrap(), |p| {
addr.as_str().unwrap().with_c_u16_str(|p| {
let start = ::io::timer::now();
loop {
match UnixStream::try_connect(p) {
Expand Down Expand Up @@ -553,7 +552,7 @@ impl UnixListener {
// Although we technically don't need the pipe until much later, we
// create the initial handle up front to test the validity of the name
// and such.
as_utf16_p(addr.as_str().unwrap(), |p| {
addr.as_str().unwrap().with_c_u16_str(|p| {
let ret = unsafe { pipe(p, true) };
if ret == libc::INVALID_HANDLE_VALUE as libc::HANDLE {
Err(super::last_error())
Expand Down Expand Up @@ -667,7 +666,7 @@ impl UnixAcceptor {
// Now that we've got a connected client to our handle, we need to
// create a second server pipe. If this fails, we disconnect the
// connected client and return an error (see comments above).
let new_handle = as_utf16_p(self.listener.name.as_str().unwrap(), |p| {
let new_handle = self.listener.name.as_str().unwrap().with_c_u16_str(|p| {
unsafe { pipe(p, false) }
});
if new_handle == libc::INVALID_HANDLE_VALUE as libc::HANDLE {
Expand Down
26 changes: 13 additions & 13 deletions src/libnative/io/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use super::file;
use super::util;

#[cfg(windows)] use std::strbuf::StrBuf;
#[cfg(windows)] use std::c_str::ToCU16Str;
#[cfg(unix)] use super::c;
#[cfg(unix)] use super::retry;
#[cfg(unix)] use io::helper_thread::Helper;
Expand Down Expand Up @@ -323,18 +324,17 @@ fn spawn_process_os(cfg: ProcessConfig, in_fd: c_int, out_fd: c_int, err_fd: c_i

with_envp(cfg.env, |envp| {
with_dirp(cfg.cwd, |dirp| {
os::win32::as_mut_utf16_p(cmd_str, |cmdp| {
let created = CreateProcessW(ptr::null(),
cmdp,
ptr::mut_null(),
ptr::mut_null(),
TRUE,
flags, envp, dirp,
&mut si, &mut pi);
if created == FALSE {
create_err = Some(super::last_error());
}
})
let mut cmd_str_16 = cmd_str.to_c_u16_str();
let created = CreateProcessW(ptr::null(),
cmd_str_16.as_mut_ptr(),
ptr::mut_null(),
ptr::mut_null(),
TRUE,
flags, envp, dirp,
&mut si, &mut pi);
if created == FALSE {
create_err = Some(super::last_error());
}
})
});

Expand Down Expand Up @@ -712,7 +712,7 @@ fn with_dirp<T>(d: Option<&CString>, cb: |*u16| -> T) -> T {
Some(dir) => {
let dir_str = dir.as_str()
.expect("expected workingdirectory to be utf-8 encoded");
os::win32::as_utf16_p(dir_str, cb)
dir_str.with_c_u16_str(cb)
},
None => cb(ptr::null())
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustdoc/flock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ mod imp {
mod imp {
use libc;
use std::mem;
use std::os::win32::as_utf16_p;
use std::c_str::ToCU16Str;
use std::os;
use std::ptr;

Expand All @@ -161,7 +161,7 @@ mod imp {

impl Lock {
pub fn new(p: &Path) -> Lock {
let handle = as_utf16_p(p.as_str().unwrap(), |p| unsafe {
let handle = p.as_str().unwrap().with_c_u16_str(|p| unsafe {
libc::CreateFileW(p,
libc::FILE_GENERIC_READ |
libc::FILE_GENERIC_WRITE,
Expand Down
77 changes: 76 additions & 1 deletion src/libstd/c_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,10 @@ use raw::Slice;
use rt::libc_heap::malloc_raw;
use slice::{ImmutableVector, MutableVector};
use slice;
use str::StrSlice;
use str::{StrSlice, StrAllocating};
use str;
use strbuf::StrBuf;
use vec::Vec;

/// The representation of a C String.
///
Expand Down Expand Up @@ -475,13 +476,59 @@ pub unsafe fn from_c_multistring(buf: *libc::c_char,
return ctr;
}

/// A generic trait for converting a value to a vector of null-terminated UTF-16 string.
pub trait ToCU16Str {
/// Convert the receiver into a vector of null-terminated UTF-16 string.
fn to_c_u16_str(&self) -> Vec<u16>;

/// Temporarily convert the receiver into null-terminated UTF-16 string and
/// pass it to the closure.
fn with_c_u16_str<T>(&self, f: |*u16| -> T) -> T {
let t = self.to_c_u16_str();
f(t.as_ptr())
}
}

impl<'a> ToCU16Str for &'a str {
fn to_c_u16_str(&self) -> Vec<u16> {
let mut t = self.to_utf16();
t.push(0u16);
t
}
}

/// Parses a C UTF-16 "multistring", eg windows env values.
///
/// Optionally, a `count` can be passed in, limiting the
/// parsing to only being done `count`-times.
///
/// The specified closure is invoked with each string that
/// is found, and the number of strings found is returned.
pub unsafe fn from_c_u16_multistring(buf: *u16, count: Option<uint>, f: |&[u16]|) -> uint {
let mut curr_ptr: uint = buf as uint;
let mut ctr = 0;
let (limited_count, limit) = match count {
Some(limit) => (true, limit),
None => (false, 0)
};
while ((limited_count && ctr < limit) || !limited_count) && *(curr_ptr as *u16) != 0 as u16 {
let len = ptr::position(curr_ptr as *u16, |c| *c == 0);
let slice: &[u16] = mem::transmute((curr_ptr, len));
f(slice);
curr_ptr += (len + 1) * 2;
ctr += 1;
}
return ctr;
}

#[cfg(test)]
mod tests {
use prelude::*;
use super::*;
use libc;
use ptr;
use str::StrSlice;
use str::from_utf16;

#[test]
fn test_str_multistring_parsing() {
Expand Down Expand Up @@ -719,6 +766,34 @@ mod tests {
let y = x.clone();
assert!(x == y);
}

#[test]
fn test_from_c_u16_multistring() {
unsafe {
let input: &[u16] = &[
0xac00, 0x00,
0xac00, 0xac00, 0x00,
0xac00, 0xac00, 0xac00, 0x00,
0x00,
];
let ptr = input.as_ptr() as *u16;
let expected = ["가", "가가", "가가가"];
let mut it = expected.iter();
let result = from_c_u16_multistring(ptr, None, |c| {
let cbytes = from_utf16(c).expect("failed to decode utf-16 string?");
println!("cbytes: {}", cbytes);
assert_eq!(cbytes.as_slice(), *it.next().unwrap());
});
assert_eq!(result, 3);
assert!(it.next().is_none());
}
}

#[test]
fn test_str_to_c_u16_str() {
assert_eq!("".to_c_u16_str(), vec!(0u16));
assert_eq!("ab".to_c_u16_str(), vec!('a' as u16, 'b' as u16, 0u16));
}
}

#[cfg(test)]
Expand Down
Loading