Skip to content

Commit

Permalink
Merge #5756
Browse files Browse the repository at this point in the history
5756: Sophisticate Windows path encoding r=matklad a=pragmatrix

As discussed in #5475, path encoding should be agnostic of the drive letter casing on Windows.

Compared to the problem it solves, the code added seems a lot and may introduce other problems. But I've not found a simpler way basing this on the public API surface that Rust offers.

Fixes #5484.

cc @Emilgardis 

Co-authored-by: Armin Sander <armin@replicator.org>
  • Loading branch information
bors[bot] and pragmatrix committed Aug 14, 2020
2 parents f1f7364 + 8fc2545 commit 674af60
Showing 1 changed file with 132 additions and 7 deletions.
139 changes: 132 additions & 7 deletions crates/vfs/src/vfs_path.rs
Expand Up @@ -57,30 +57,155 @@ impl VfsPath {
};
buf.push(tag);
match &self.0 {
VfsPathRepr::PathBuf(it) => {
let path: &std::ffi::OsStr = it.as_os_str();
VfsPathRepr::PathBuf(path) => {
#[cfg(windows)]
{
use std::os::windows::ffi::OsStrExt;
for wchar in path.encode_wide() {
buf.extend(wchar.to_le_bytes().iter().copied());
use windows_paths::Encode;
let components = path.components();
let mut add_sep = false;
for component in components {
if add_sep {
windows_paths::SEP.encode(buf);
}
let len_before = buf.len();
match component {
std::path::Component::Prefix(prefix) => {
// kind() returns a normalized and comparable path prefix.
prefix.kind().encode(buf);
}
std::path::Component::RootDir => {
if !add_sep {
component.as_os_str().encode(buf);
}
}
_ => component.as_os_str().encode(buf),
}

// some components may be encoded empty
add_sep = len_before != buf.len();
}
}
#[cfg(unix)]
{
use std::os::unix::ffi::OsStrExt;
buf.extend(path.as_bytes());
buf.extend(path.as_os_str().as_bytes());
}
#[cfg(not(any(windows, unix)))]
{
buf.extend(path.to_string_lossy().as_bytes());
buf.extend(path.as_os_str().to_string_lossy().as_bytes());
}
}
VfsPathRepr::VirtualPath(VirtualPath(s)) => buf.extend(s.as_bytes()),
}
}
}

#[cfg(windows)]
mod windows_paths {
pub trait Encode {
fn encode(&self, buf: &mut Vec<u8>);
}

impl Encode for std::ffi::OsStr {
fn encode(&self, buf: &mut Vec<u8>) {
use std::os::windows::ffi::OsStrExt;
for wchar in self.encode_wide() {
buf.extend(wchar.to_le_bytes().iter().copied());
}
}
}

impl Encode for u8 {
fn encode(&self, buf: &mut Vec<u8>) {
let wide = *self as u16;
buf.extend(wide.to_le_bytes().iter().copied())
}
}

impl Encode for &str {
fn encode(&self, buf: &mut Vec<u8>) {
debug_assert!(self.is_ascii());
for b in self.as_bytes() {
b.encode(buf)
}
}
}

pub const SEP: &str = "\\";
const VERBATIM: &str = "\\\\?\\";
const UNC: &str = "UNC";
const DEVICE: &str = "\\\\.\\";
const COLON: &str = ":";

impl Encode for std::path::Prefix<'_> {
fn encode(&self, buf: &mut Vec<u8>) {
match self {
std::path::Prefix::Verbatim(c) => {
VERBATIM.encode(buf);
c.encode(buf);
}
std::path::Prefix::VerbatimUNC(server, share) => {
VERBATIM.encode(buf);
UNC.encode(buf);
SEP.encode(buf);
server.encode(buf);
SEP.encode(buf);
share.encode(buf);
}
std::path::Prefix::VerbatimDisk(d) => {
VERBATIM.encode(buf);
d.encode(buf);
COLON.encode(buf);
}
std::path::Prefix::DeviceNS(device) => {
DEVICE.encode(buf);
device.encode(buf);
}
std::path::Prefix::UNC(server, share) => {
SEP.encode(buf);
SEP.encode(buf);
server.encode(buf);
SEP.encode(buf);
share.encode(buf);
}
std::path::Prefix::Disk(d) => {
d.encode(buf);
COLON.encode(buf);
}
}
}
}
#[test]
fn paths_encoding() {
// drive letter casing agnostic
test_eq("C:/x.rs", "c:/x.rs");
// separator agnostic
test_eq("C:/x/y.rs", "C:\\x\\y.rs");

fn test_eq(a: &str, b: &str) {
let mut b1 = Vec::new();
let mut b2 = Vec::new();
vfs(a).encode(&mut b1);
vfs(b).encode(&mut b2);
assert_eq!(b1, b2);
}
}

#[test]
fn test_sep_root_dir_encoding() {
let mut buf = Vec::new();
vfs("C:/x/y").encode(&mut buf);
assert_eq!(&buf, &[0, 67, 0, 58, 0, 92, 0, 120, 0, 92, 0, 121, 0])
}

#[cfg(test)]
fn vfs(str: &str) -> super::VfsPath {
use super::{AbsPathBuf, VfsPath};
use std::convert::TryFrom;
VfsPath::from(AbsPathBuf::try_from(str).unwrap())
}
}

#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
enum VfsPathRepr {
PathBuf(AbsPathBuf),
Expand Down

0 comments on commit 674af60

Please sign in to comment.