rust/physfs-rs/src/physfs/file.rs
changeset 14435 a1613788130d
equal deleted inserted replaced
14434:632dfc73cf83 14435:a1613788130d
       
     1 use std::ffi::CString;
       
     2 use std::io::{ Read, Write, Seek, SeekFrom, Result };
       
     3 use std::mem;
       
     4 use libc::{ c_int, c_char, c_void };
       
     5 use primitives::*;
       
     6 use super::{ PhysFSContext };
       
     7 use super::util::physfs_error_as_io_error;
       
     8 
       
     9 #[link(name = "physfs")]
       
    10 extern {
       
    11     // valid filehandle on success, NULL on failure
       
    12     fn PHYSFS_openAppend(filename: *const c_char) -> *const RawFile;
       
    13     fn PHYSFS_openRead(filename: *const c_char) -> *const RawFile;
       
    14     fn PHYSFS_openWrite(filename: *const c_char) -> *const RawFile;
       
    15 
       
    16     // nonzero on success, 0 on failure (and the handle stays open)
       
    17     // The docs make it sound like failure is rare.
       
    18     fn PHYSFS_close(file: *const RawFile) -> c_int;
       
    19 
       
    20     // Number of bytes read on success, -1 on failure.
       
    21     fn PHYSFS_read(file: *const RawFile, buffer: *mut c_void,
       
    22                    obj_size: PHYSFS_uint32, obj_count: PHYSFS_uint32) -> PHYSFS_sint64;
       
    23 
       
    24     // Number of bytes written on success, -1 on failure.
       
    25     fn PHYSFS_write(file: *const RawFile, buffer: *const c_void,
       
    26                     obj_size: PHYSFS_uint32, obj_count: PHYSFS_uint32) -> PHYSFS_sint64;
       
    27 
       
    28     // Flush buffered file; no-op for unbuffered files.
       
    29     fn PHYSFS_flush(file: *const RawFile) -> c_int;
       
    30 
       
    31     // Seek to position in file; nonzero on succss, zero on error.
       
    32     fn PHYSFS_seek(file: *const RawFile, pos: PHYSFS_uint64) -> c_int;
       
    33 
       
    34     // Current position in file, -1 on failure.
       
    35     fn PHYSFS_tell(file: *const RawFile) -> PHYSFS_sint64;
       
    36 
       
    37     // nonzero if EOF, zero if not.
       
    38     fn PHYSFS_eof(file: *const RawFile) -> c_int;
       
    39 
       
    40     // Determine file size; returns -1 if impossible
       
    41     fn PHYSFS_fileLength(file: *const RawFile) -> PHYSFS_sint64;
       
    42 }
       
    43 
       
    44 /// Possible ways to open a file.
       
    45 #[derive(Copy, Clone)]
       
    46 pub enum Mode {
       
    47     /// Append to the end of the file.
       
    48     Append,
       
    49     /// Read from the file.
       
    50     Read,
       
    51     /// Write to the file, overwriting previous data.
       
    52     Write,
       
    53 }
       
    54 
       
    55 /// A wrapper for the PHYSFS_File type.
       
    56 #[repr(C)]
       
    57 struct RawFile {
       
    58     opaque: *const c_void,
       
    59 }
       
    60 
       
    61 /// A file handle.
       
    62 #[allow(dead_code)]
       
    63 pub struct File<'f> {
       
    64     raw: *const RawFile,
       
    65     mode: Mode,
       
    66     context: &'f PhysFSContext,
       
    67 }
       
    68 
       
    69 impl<'f> File<'f> {
       
    70     /// Opens a file with a specific mode.
       
    71     pub fn open<'g>(context: &'g PhysFSContext, filename: String, mode: Mode) -> Result<File<'g>> {
       
    72         let c_filename = try!(CString::new(filename));
       
    73         let raw = unsafe { match mode {
       
    74             Mode::Append => PHYSFS_openAppend(c_filename.as_ptr()),
       
    75             Mode::Read => PHYSFS_openRead(c_filename.as_ptr()),
       
    76             Mode::Write => PHYSFS_openWrite(c_filename.as_ptr())
       
    77         }};
       
    78 
       
    79         if raw.is_null() {
       
    80             Err(physfs_error_as_io_error())
       
    81         }
       
    82         else {
       
    83             Ok(File{raw: raw, mode: mode, context: context})
       
    84         }
       
    85     }
       
    86 
       
    87     /// Closes a file handle.
       
    88     fn close(&self) -> Result<()> {
       
    89         match unsafe {
       
    90             PHYSFS_close(self.raw)
       
    91         } {
       
    92             0 => Err(physfs_error_as_io_error()),
       
    93             _ => Ok(())
       
    94         }
       
    95     }
       
    96 
       
    97     /// Checks whether eof is reached or not.
       
    98     pub fn eof(&self) -> bool {
       
    99         let ret = unsafe {
       
   100             PHYSFS_eof(self.raw)
       
   101         };
       
   102 
       
   103         ret != 0
       
   104     }
       
   105 
       
   106     /// Determine length of file, if possible
       
   107     pub fn len(&self) -> Result<u64> {
       
   108         let len = unsafe { PHYSFS_fileLength(self.raw) };
       
   109 
       
   110         if len >= 0 {
       
   111             Ok(len as u64)
       
   112         } else {
       
   113             Err(physfs_error_as_io_error())
       
   114         }
       
   115     }
       
   116 
       
   117     /// Determines current position within a file
       
   118     pub fn tell(&self) -> Result<u64> {
       
   119         let ret = unsafe {
       
   120             PHYSFS_tell(self.raw)
       
   121         };
       
   122 
       
   123         match ret {
       
   124             -1 => Err(physfs_error_as_io_error()),
       
   125             _ => Ok(ret as u64)
       
   126         }
       
   127     }
       
   128 }
       
   129 
       
   130 impl<'f> Read for File<'f> {
       
   131     /// Reads from a file
       
   132     fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
       
   133         let ret = unsafe {
       
   134             PHYSFS_read(
       
   135                 self.raw,
       
   136                 buf.as_ptr() as *mut c_void,
       
   137                 mem::size_of::<u8>() as PHYSFS_uint32,
       
   138                 buf.len() as PHYSFS_uint32
       
   139             )
       
   140         };
       
   141 
       
   142         match ret {
       
   143             -1 => Err(physfs_error_as_io_error()),
       
   144             _ => Ok(ret as usize)
       
   145         }
       
   146     }
       
   147 }
       
   148 
       
   149 impl<'f> Write for File<'f> {
       
   150     /// Writes to a file.
       
   151     /// This code performs no safety checks to ensure
       
   152     /// that the buffer is the correct length.
       
   153     fn write(&mut self, buf: &[u8]) -> Result<usize> {
       
   154         let ret = unsafe {
       
   155             PHYSFS_write(
       
   156                 self.raw,
       
   157                 buf.as_ptr() as *const c_void,
       
   158                 mem::size_of::<u8>() as PHYSFS_uint32,
       
   159                 buf.len() as PHYSFS_uint32
       
   160             )
       
   161         };
       
   162 
       
   163         match ret {
       
   164             -1 => Err(physfs_error_as_io_error()),
       
   165             _ => Ok(ret as usize)
       
   166         }
       
   167     }
       
   168 
       
   169     /// Flushes a file if buffered; no-op if unbuffered.
       
   170     fn flush(&mut self) -> Result<()> {
       
   171         let ret = unsafe {
       
   172             PHYSFS_flush(self.raw)
       
   173         };
       
   174 
       
   175         match ret {
       
   176             0 => Err(physfs_error_as_io_error()),
       
   177             _ => Ok(())
       
   178         }
       
   179     }
       
   180 }
       
   181 
       
   182 impl<'f> Seek for File<'f> {
       
   183     /// Seek to a new position within a file
       
   184     fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
       
   185         let seek_pos = match pos {
       
   186             SeekFrom::Start(n) => n as i64,
       
   187             SeekFrom::End(n) => {
       
   188                 let len = try!(self.len());
       
   189                 n + len as i64
       
   190             }
       
   191             SeekFrom::Current(n) => {
       
   192                 let curr_pos = try!(self.tell());
       
   193                 n + curr_pos as i64
       
   194             }
       
   195         };
       
   196 
       
   197         let result = unsafe {
       
   198             PHYSFS_seek(
       
   199                 self.raw,
       
   200                 seek_pos as PHYSFS_uint64
       
   201             )
       
   202         };
       
   203 
       
   204         if result == -1 {
       
   205             return Err(physfs_error_as_io_error());
       
   206         }
       
   207 
       
   208         self.tell()
       
   209     }
       
   210 }
       
   211 
       
   212 impl<'f> Drop for File<'f> {
       
   213     fn drop(&mut self) {
       
   214         let _ = self.close();
       
   215     }
       
   216 }