diff -r 632dfc73cf83 -r a1613788130d rust/physfs-rs/src/physfs/file.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/physfs-rs/src/physfs/file.rs Thu Dec 13 23:44:46 2018 +0100 @@ -0,0 +1,216 @@ +use std::ffi::CString; +use std::io::{ Read, Write, Seek, SeekFrom, Result }; +use std::mem; +use libc::{ c_int, c_char, c_void }; +use primitives::*; +use super::{ PhysFSContext }; +use super::util::physfs_error_as_io_error; + +#[link(name = "physfs")] +extern { + // valid filehandle on success, NULL on failure + fn PHYSFS_openAppend(filename: *const c_char) -> *const RawFile; + fn PHYSFS_openRead(filename: *const c_char) -> *const RawFile; + fn PHYSFS_openWrite(filename: *const c_char) -> *const RawFile; + + // nonzero on success, 0 on failure (and the handle stays open) + // The docs make it sound like failure is rare. + fn PHYSFS_close(file: *const RawFile) -> c_int; + + // Number of bytes read on success, -1 on failure. + fn PHYSFS_read(file: *const RawFile, buffer: *mut c_void, + obj_size: PHYSFS_uint32, obj_count: PHYSFS_uint32) -> PHYSFS_sint64; + + // Number of bytes written on success, -1 on failure. + fn PHYSFS_write(file: *const RawFile, buffer: *const c_void, + obj_size: PHYSFS_uint32, obj_count: PHYSFS_uint32) -> PHYSFS_sint64; + + // Flush buffered file; no-op for unbuffered files. + fn PHYSFS_flush(file: *const RawFile) -> c_int; + + // Seek to position in file; nonzero on succss, zero on error. + fn PHYSFS_seek(file: *const RawFile, pos: PHYSFS_uint64) -> c_int; + + // Current position in file, -1 on failure. + fn PHYSFS_tell(file: *const RawFile) -> PHYSFS_sint64; + + // nonzero if EOF, zero if not. + fn PHYSFS_eof(file: *const RawFile) -> c_int; + + // Determine file size; returns -1 if impossible + fn PHYSFS_fileLength(file: *const RawFile) -> PHYSFS_sint64; +} + +/// Possible ways to open a file. +#[derive(Copy, Clone)] +pub enum Mode { + /// Append to the end of the file. + Append, + /// Read from the file. + Read, + /// Write to the file, overwriting previous data. + Write, +} + +/// A wrapper for the PHYSFS_File type. +#[repr(C)] +struct RawFile { + opaque: *const c_void, +} + +/// A file handle. +#[allow(dead_code)] +pub struct File<'f> { + raw: *const RawFile, + mode: Mode, + context: &'f PhysFSContext, +} + +impl<'f> File<'f> { + /// Opens a file with a specific mode. + pub fn open<'g>(context: &'g PhysFSContext, filename: String, mode: Mode) -> Result> { + let c_filename = try!(CString::new(filename)); + let raw = unsafe { match mode { + Mode::Append => PHYSFS_openAppend(c_filename.as_ptr()), + Mode::Read => PHYSFS_openRead(c_filename.as_ptr()), + Mode::Write => PHYSFS_openWrite(c_filename.as_ptr()) + }}; + + if raw.is_null() { + Err(physfs_error_as_io_error()) + } + else { + Ok(File{raw: raw, mode: mode, context: context}) + } + } + + /// Closes a file handle. + fn close(&self) -> Result<()> { + match unsafe { + PHYSFS_close(self.raw) + } { + 0 => Err(physfs_error_as_io_error()), + _ => Ok(()) + } + } + + /// Checks whether eof is reached or not. + pub fn eof(&self) -> bool { + let ret = unsafe { + PHYSFS_eof(self.raw) + }; + + ret != 0 + } + + /// Determine length of file, if possible + pub fn len(&self) -> Result { + let len = unsafe { PHYSFS_fileLength(self.raw) }; + + if len >= 0 { + Ok(len as u64) + } else { + Err(physfs_error_as_io_error()) + } + } + + /// Determines current position within a file + pub fn tell(&self) -> Result { + let ret = unsafe { + PHYSFS_tell(self.raw) + }; + + match ret { + -1 => Err(physfs_error_as_io_error()), + _ => Ok(ret as u64) + } + } +} + +impl<'f> Read for File<'f> { + /// Reads from a file + fn read(&mut self, buf: &mut [u8]) -> Result { + let ret = unsafe { + PHYSFS_read( + self.raw, + buf.as_ptr() as *mut c_void, + mem::size_of::() as PHYSFS_uint32, + buf.len() as PHYSFS_uint32 + ) + }; + + match ret { + -1 => Err(physfs_error_as_io_error()), + _ => Ok(ret as usize) + } + } +} + +impl<'f> Write for File<'f> { + /// Writes to a file. + /// This code performs no safety checks to ensure + /// that the buffer is the correct length. + fn write(&mut self, buf: &[u8]) -> Result { + let ret = unsafe { + PHYSFS_write( + self.raw, + buf.as_ptr() as *const c_void, + mem::size_of::() as PHYSFS_uint32, + buf.len() as PHYSFS_uint32 + ) + }; + + match ret { + -1 => Err(physfs_error_as_io_error()), + _ => Ok(ret as usize) + } + } + + /// Flushes a file if buffered; no-op if unbuffered. + fn flush(&mut self) -> Result<()> { + let ret = unsafe { + PHYSFS_flush(self.raw) + }; + + match ret { + 0 => Err(physfs_error_as_io_error()), + _ => Ok(()) + } + } +} + +impl<'f> Seek for File<'f> { + /// Seek to a new position within a file + fn seek(&mut self, pos: SeekFrom) -> Result { + let seek_pos = match pos { + SeekFrom::Start(n) => n as i64, + SeekFrom::End(n) => { + let len = try!(self.len()); + n + len as i64 + } + SeekFrom::Current(n) => { + let curr_pos = try!(self.tell()); + n + curr_pos as i64 + } + }; + + let result = unsafe { + PHYSFS_seek( + self.raw, + seek_pos as PHYSFS_uint64 + ) + }; + + if result == -1 { + return Err(physfs_error_as_io_error()); + } + + self.tell() + } +} + +impl<'f> Drop for File<'f> { + fn drop(&mut self) { + let _ = self.close(); + } +}