1use std::convert::AsRef;
2use std::fs::Metadata;
3use std::io;
4use std::mem;
5use std::path::PathBuf;
6use std::str;
7
8cfg_if! {
9 if #[cfg(unix)] {
10 use std::os::unix::io::{AsRawFd, IntoRawFd};
11 } else {
12 use std::ffi::OsString;
13 use std::ptr::NonNull;
14 use std::os::windows::ffi::OsStringExt;
15 use std::os::windows::io::{AsRawHandle, IntoRawHandle};
16 }
17}
18
19use foreign_types::ForeignTypeRef;
20
21use crate::{CFile, CFileRef};
22
23pub trait Stream: io::Read + io::Write + io::Seek {
25 fn position(&self) -> io::Result<u64>;
27
28 fn eof(&self) -> bool;
30
31 fn errno(&self) -> i32;
33
34 fn last_error(&self) -> Option<io::Error>;
36
37 fn clear_error(&self);
39
40 fn file_name(&self) -> io::Result<PathBuf>;
42
43 fn metadata(&self) -> io::Result<Metadata>;
45
46 fn read_slice<T: Sized>(&mut self, elements: &mut [T]) -> io::Result<usize>;
48
49 fn write_slice<T: Sized>(&mut self, elements: &[T]) -> io::Result<usize>;
51}
52
53cfg_if! {
54 if #[cfg(unix)] {
55 pub trait AsStream: AsRawFd + Sized {
57 fn as_stream<S: AsRef<str>>(&self, mode: S) -> io::Result<CFile> {
59 CFile::fdopen(self.as_raw_fd(), mode)
60 }
61 }
62
63 impl<S: AsRawFd + Sized> AsStream for S {}
64
65 pub trait IntoStream: IntoRawFd + Sized {
67 fn into_stream<S: AsRef<str>>(self, mode: S) -> io::Result<CFile> {
69 CFile::fdopen(self.into_raw_fd(), mode)
70 }
71 }
72
73 impl<S: IntoRawFd + Sized> IntoStream for S {}
74 } else {
75 pub trait AsStream: AsRawHandle + Sized {
77 fn as_stream<S: AsRef<str>>(&self, mode: S) -> io::Result<CFile> {
79 CFile::fdopen(self.as_raw_handle(), mode)
80 }
81 }
82
83 impl<S: AsRawHandle + Sized> AsStream for S {}
84
85 pub trait IntoStream: IntoRawHandle + Sized {
87 fn into_stream<S: AsRef<str>>(self, mode: S) -> io::Result<CFile> {
89 CFile::fdopen(self.into_raw_handle(), mode)
90 }
91 }
92
93 impl<S: IntoRawHandle + Sized> IntoStream for S {}
94 }
95}
96
97impl io::Read for CFile {
98 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
99 self.as_mut().read_slice(buf)
100 }
101}
102
103impl io::Write for CFile {
104 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
105 self.as_mut().write(buf)
106 }
107
108 fn flush(&mut self) -> io::Result<()> {
109 self.as_mut().flush()
110 }
111}
112
113impl io::Seek for CFile {
114 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
115 self.as_mut().seek(pos)
116 }
117}
118
119impl io::Read for CFileRef {
120 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
121 self.read_slice(buf)
122 }
123}
124
125impl io::Write for CFileRef {
126 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
127 self.write_slice(buf)
128 }
129
130 fn flush(&mut self) -> io::Result<()> {
131 if unsafe { libc::fflush(self.as_ptr()) } != 0 {
132 if let Some(err) = self.last_error() {
133 return Err(err);
134 }
135 }
136
137 Ok(())
138 }
139}
140
141cfg_if! {
142 if #[cfg(unix)] {
143 use libc::fseek as fseek64;
144 } else {
145 extern "C" {
146 #[link_name = "_fseeki64"]
147 fn fseek64(file: *mut libc::FILE, offset: i64, origin: i32) -> i32;
148 }
149 }
150}
151
152impl io::Seek for CFileRef {
153 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
154 let ret = unsafe {
155 match pos {
156 io::SeekFrom::Start(off) => fseek64(self.as_ptr(), off as i64, libc::SEEK_SET),
157 io::SeekFrom::End(off) => fseek64(self.as_ptr(), off, libc::SEEK_END),
158 io::SeekFrom::Current(off) => fseek64(self.as_ptr(), off, libc::SEEK_CUR),
159 }
160 };
161
162 if ret != 0 {
163 if let Some(err) = self.last_error() {
164 return Err(err);
165 }
166 }
167
168 self.position()
169 }
170}
171
172impl Stream for CFileRef {
173 fn position(&self) -> io::Result<u64> {
174 let off = unsafe { libc::ftell(self.as_ptr()) };
175
176 if off < 0 {
177 if let Some(err) = self.last_error() {
178 return Err(err);
179 }
180 }
181
182 Ok(off as u64)
183 }
184
185 #[inline]
186 fn eof(&self) -> bool {
187 unsafe { libc::feof(self.as_ptr()) != 0 }
188 }
189
190 #[inline]
191 fn errno(&self) -> i32 {
192 unsafe { libc::ferror(self.as_ptr()) }
193 }
194
195 fn last_error(&self) -> Option<io::Error> {
196 let errno = self.errno();
197
198 if errno != 0 {
199 return Some(io::Error::from_raw_os_error(errno));
200 }
201
202 let err = io::Error::last_os_error();
203
204 match err.raw_os_error() {
205 Some(errno) if errno != 0 => Some(err),
206 _ => None,
207 }
208 }
209
210 fn clear_error(&self) {
211 unsafe { clearerr(self.as_ptr()) }
212 }
213
214 #[cfg(target_os = "linux")]
215 fn file_name(&self) -> io::Result<PathBuf> {
216 use std::path::Path;
217
218 let s = format!("/proc/self/fd/{}", self.as_raw_fd());
219 let p = Path::new(&s);
220
221 if p.exists() {
222 p.read_link()
223 } else {
224 Err(io::Error::new(io::ErrorKind::NotFound, "fd not found"))
225 }
226 }
227
228 #[cfg(target_os = "macos")]
229 fn file_name(&self) -> io::Result<PathBuf> {
230 use std::ffi::CStr;
231
232 let mut buf = Vec::with_capacity(libc::PATH_MAX as usize);
233
234 let ret = unsafe { libc::fcntl(self.as_raw_fd(), libc::F_GETPATH, buf.as_mut_ptr()) };
235
236 let filename = str::from_utf8(unsafe { CStr::from_ptr(buf.as_ptr()).to_bytes() }).unwrap();
237
238 println!("{}, {}", filename, ret);
239
240 if ret < 0 {
241 Err(io::Error::last_os_error())
242 } else {
243 Ok(PathBuf::from(filename))
244 }
245 }
246
247 #[cfg(target_os = "windows")]
248 fn file_name(&self) -> io::Result<PathBuf> {
249 use winapi::shared::minwindef::MAX_PATH;
250 use winapi::um::fileapi::FILE_NAME_INFO;
251 use winapi::um::minwinbase::FileNameInfo;
252 use winapi::um::winbase::GetFileInformationByHandleEx;
253 use winapi::um::winnt::WCHAR;
254
255 let wchar_size = mem::size_of::<WCHAR>();
256 let bufsize = mem::size_of::<FILE_NAME_INFO>() + MAX_PATH * wchar_size;
257 let mut buf = vec![0u8; bufsize];
258
259 unsafe {
260 if GetFileInformationByHandleEx(
261 self.as_raw_handle() as *mut _,
262 FileNameInfo,
263 buf.as_mut_ptr() as *mut _,
264 buf.len() as u32,
265 ) == 0
266 {
267 Err(io::Error::last_os_error())
268 } else {
269 let fi = NonNull::new_unchecked(buf.as_mut_ptr()).cast::<FILE_NAME_INFO>();
270 let fi = fi.as_ref();
271 let filename = std::slice::from_raw_parts(
272 fi.FileName.as_ptr(),
273 fi.FileNameLength as usize / wchar_size,
274 );
275
276 Ok(PathBuf::from(OsString::from_wide(filename)))
277 }
278 }
279 }
280
281 fn metadata(&self) -> io::Result<Metadata> {
282 self.file_name()?.as_path().metadata()
283 }
284
285 fn read_slice<T: Sized>(&mut self, elements: &mut [T]) -> io::Result<usize> {
286 if elements.is_empty() {
287 return Ok(0);
288 }
289
290 let read = unsafe {
291 libc::fread(
292 elements.as_mut_ptr() as *mut libc::c_void,
293 mem::size_of::<T>(),
294 elements.len(),
295 self.as_ptr(),
296 )
297 };
298
299 if let Some(err) = self.last_error() {
300 if read == 0 {
301 return Err(err);
302 }
303 }
304
305 Ok(read)
306 }
307
308 fn write_slice<T: Sized>(&mut self, elements: &[T]) -> io::Result<usize> {
309 if elements.is_empty() {
310 return Ok(0);
311 }
312
313 let wrote = unsafe {
314 libc::fwrite(
315 elements.as_ptr() as *const libc::c_void,
316 mem::size_of::<T>(),
317 elements.len(),
318 self.as_ptr(),
319 )
320 };
321
322 if let Some(err) = self.last_error() {
323 if wrote == 0 {
324 return Err(err);
325 }
326 }
327
328 Ok(wrote)
329 }
330}
331
332extern "C" {
333 fn clearerr(file: *mut libc::FILE);
334}