1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use std::fs::Metadata;
use std::path::Path;
#[cfg(unix)]
mod imp {
use std::fs::Metadata;
use std::os::unix::fs::MetadataExt;
use std::path::Path;
pub fn file_real_size<P: AsRef<Path>>(path: P) -> std::io::Result<u64> {
Ok(std::fs::symlink_metadata(path)?.blocks() * 512)
}
pub fn file_real_size_fast<P: AsRef<Path>>(_path: P, metadata: &Metadata) -> std::io::Result<u64> {
Ok(metadata.blocks() * 512)
}
}
#[cfg(windows)]
mod imp {
use std::fs::Metadata;
use std::os::windows::ffi::OsStrExt;
use std::path::Path;
use winapi::shared::winerror::NO_ERROR;
use winapi::um::fileapi::{GetCompressedFileSizeW, INVALID_FILE_SIZE};
pub fn file_real_size<P: AsRef<Path>>(path: P) -> std::io::Result<u64> {
let path = std::fs::canonicalize(path)?.into_os_string();
let mut pathw: Vec<u16> = Vec::with_capacity(path.len() + 1);
pathw.extend(path.encode_wide());
pathw.push(0);
let mut high: u32 = 0;
let low = unsafe { GetCompressedFileSizeW(pathw.as_ptr(), &mut high) };
if low == INVALID_FILE_SIZE {
let err = std::io::Error::last_os_error();
if err.raw_os_error().map(|e| e as u32).unwrap_or(NO_ERROR) != NO_ERROR {
return Err(err);
}
}
Ok(u64::from(high) << 32 | u64::from(low))
}
pub fn file_real_size_fast<P: AsRef<Path>>(path: P, _metadata: &Metadata) -> std::io::Result<u64> {
file_real_size(path)
}
}
#[cfg(not(any(windows, unix)))]
mod imp {
use std::fs::Metadata;
use std::path::Path;
pub fn file_real_size<P: AsRef<Path>>(path: P) -> std::io::Result<u64> {
std::fs::symlink_metadata(path)?.len()
}
pub fn file_real_size_fast<P: AsRef<Path>>(_path: P, metadata: &Metadata) -> std::io::Result<u64> {
Ok(metadata.len())
}
}
pub fn file_real_size<P: AsRef<Path>>(path: P) -> std::io::Result<u64> {
self::imp::file_real_size(path)
}
pub fn file_real_size_fast<P: AsRef<Path>>(path: P, metadata: &Metadata) -> std::io::Result<u64> {
self::imp::file_real_size_fast(path, metadata)
}
#[test]
fn it_seems_to_work() {
assert!(
file_real_size("Cargo.toml").expect("file_real_size")
== file_real_size_fast(
"Cargo.toml",
&std::fs::symlink_metadata("Cargo.toml").expect("stat")
)
.expect("file_real_size_fast")
);
}