Adopt physfs-rs lib and adapt it to recent std library, remove global mutex
authorunc0rr
Thu, 13 Dec 2018 23:44:46 +0100
changeset 14435 a1613788130d
parent 14434 632dfc73cf83
child 14437 cef0bb953ddf
Adopt physfs-rs lib and adapt it to recent std library, remove global mutex
rust/physfs-rs/Cargo.toml
rust/physfs-rs/LICENSE.txt
rust/physfs-rs/README.md
rust/physfs-rs/build.rs
rust/physfs-rs/src/lib.rs
rust/physfs-rs/src/physfs/file.rs
rust/physfs-rs/src/physfs/mod.rs
rust/physfs-rs/src/physfs/util.rs
rust/physfs-rs/src/primitives.rs
rust/physfs-rs/tests/directory/mod.rs
rust/physfs-rs/tests/directory/read.txt
rust/physfs-rs/tests/test.rs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/physfs-rs/Cargo.toml	Thu Dec 13 23:44:46 2018 +0100
@@ -0,0 +1,15 @@
+[package]
+
+name = "physfs-rs"
+version = "0.1.0"
+license = "zlib"
+authors = [ "Robert Habermeier" ]
+
+[lib]
+
+name = "physfs"
+
+[dependencies]
+
+libc = "0.1"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/physfs-rs/LICENSE.txt	Thu Dec 13 23:44:46 2018 +0100
@@ -0,0 +1,20 @@
+Copyright (c) 2014 Robert Habermeier
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+   1. The origin of this software must not be misrepresented; you must not
+   claim that you wrote the original software. If you use this software
+   in a product, an acknowledgment in the product documentation would be
+   appreciated but is not required.
+
+   2. Altered source versions must be plainly marked as such, and must not be
+   misrepresented as being the original software.
+
+   3. This notice may not be removed or altered from any source
+   distribution.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/physfs-rs/README.md	Thu Dec 13 23:44:46 2018 +0100
@@ -0,0 +1,7 @@
+# physfs-rs [![Build Status](https://travis-ci.org/PistonDevelopers/physfs-rs.svg?branch=master)](https://travis-ci.org/PistonDevelopers/physfs-rs)
+
+## Rust bindings for PhysFS
+
+This project is built against the Rust nightly and PhysFS 2.0.3
+
+[How to contribute](https://github.com/PistonDevelopers/piston/blob/master/CONTRIBUTING.md)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/physfs-rs/build.rs	Thu Dec 13 23:44:46 2018 +0100
@@ -0,0 +1,4 @@
+fn main() {
+    #[cfg(target_os = "freebsd")]
+    println!("cargo:rustc-link-search=native=/usr/local/lib");
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/physfs-rs/src/lib.rs	Thu Dec 13 23:44:46 2018 +0100
@@ -0,0 +1,13 @@
+//! PhysFS bindings for Rust
+
+#![deny(missing_docs)]
+
+extern crate libc;
+
+pub use physfs::*;
+pub use physfs::file::*;
+
+/// PhysFS bindings
+mod physfs;
+/// Definitions for the PhysFS primitives
+mod primitives;
--- /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<File<'g>> {
+        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<u64> {
+        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<u64> {
+        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<usize> {
+        let ret = unsafe {
+            PHYSFS_read(
+                self.raw,
+                buf.as_ptr() as *mut c_void,
+                mem::size_of::<u8>() 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<usize> {
+        let ret = unsafe {
+            PHYSFS_write(
+                self.raw,
+                buf.as_ptr() as *const c_void,
+                mem::size_of::<u8>() 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<u64> {
+        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();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/physfs-rs/src/physfs/mod.rs	Thu Dec 13 23:44:46 2018 +0100
@@ -0,0 +1,190 @@
+use std::ffi::{ CString, CStr, OsStr };
+use std::io::Result;
+use std::sync::{ Mutex };
+use libc::{ c_int, c_char };
+
+/// Keep track of the number of global contexts.
+static mut NUM_CONTEXTS: usize = 0;
+
+/// Utility
+mod util;
+/// File operations
+pub mod file;
+
+#[link(name = "physfs")]
+extern {
+    // nonzero on success, zero on error.
+    fn PHYSFS_init(arg0: *const c_char) -> c_int;
+    // nonzero if initialized, zero if not.
+    fn PHYSFS_isInit() -> c_int;
+    // nonzero if success, zero if error.
+    fn PHYSFS_deinit() -> c_int;
+    // string if success, NULL if error.
+    fn PHYSFS_getLastError() -> *const c_char;
+    // nonzero if success, zero if error
+    fn PHYSFS_mount(new_dir: *const c_char, mount_point: *const c_char, append_to_path: c_int) -> c_int;
+    // nonzero if success, zero if error.
+    fn PHYSFS_setWriteDir(write_dir: *const c_char) -> c_int;
+    // nonzero on success, zero on error.
+    fn PHYSFS_mkdir(dir_name: *const c_char) -> c_int;
+    // Checks if a given path exists; returns nonzero if true
+    fn PHYSFS_exists(path: *const c_char) -> c_int;
+    // Checks if a given path is a directory; returns nonzero if true
+    fn PHYSFS_isDirectory(path: *const c_char) -> c_int;
+}
+
+/// The access point for PhysFS function calls.
+pub struct PhysFSContext;
+
+unsafe impl Send for PhysFSContext {}
+
+impl PhysFSContext {
+    /// Creates a new PhysFS context.
+    pub fn new() -> Result<Self> {
+        let con = PhysFSContext;
+        match PhysFSContext::init() {
+            Err(e) => Err(e),
+            _ => {
+                // Everything's gone right so far
+                // now, increment the instance counter
+                println!("Inc");
+                unsafe {
+                    NUM_CONTEXTS += 1;
+                }
+                // and return the newly created context
+                Ok(con)
+            }
+        }
+    }
+
+    /// initializes the PhysFS library.
+    fn init() -> Result<()> {
+        // Initializing multiple times throws an error. So let's not!
+        if PhysFSContext::is_init() { return Ok(()); }
+
+        let mut args = ::std::env::args();
+        let default_arg0 = "".to_string();
+        let arg0 = args.next().unwrap_or(default_arg0);
+        let c_arg0 = try!(CString::new(arg0));
+        let ret = unsafe { PHYSFS_init(c_arg0.as_ptr()) };
+
+        match ret {
+            0 => Err(util::physfs_error_as_io_error()),
+            _ => Ok(())
+        }
+    }
+
+    /// Checks if PhysFS is initialized
+    pub fn is_init() -> bool {
+        unsafe { PHYSFS_isInit() != 0 }
+    }
+
+    /// De-initializes PhysFS. It is recommended to close
+    /// all file handles manually before calling this.
+    fn de_init() {
+        // de_init'ing more than once can cause a double-free -- do not want.
+        if !PhysFSContext::is_init() { return }
+        unsafe {
+            PHYSFS_deinit();
+        }
+    }
+    /// Adds an archive or directory to the search path.
+    /// mount_point is the location in the tree to mount it to.
+    pub fn mount<P>(&self, new_dir: P, mount_point: String, append_to_path: bool) -> Result<()>
+        where P: AsRef<OsStr>
+    {
+        let c_new_dir = CString::new(new_dir.as_ref().to_string_lossy().as_bytes()).unwrap();
+        let c_mount_point = try!(CString::new(mount_point));
+        match unsafe {
+            PHYSFS_mount(
+                c_new_dir.as_c_str().as_ptr(),
+                c_mount_point.as_ptr(),
+                append_to_path as c_int
+            )
+        } {
+            0 => Err(util::physfs_error_as_io_error()),
+            _ => Ok(())
+        }
+    }
+
+    /// Gets the last error message in a human-readable format
+    /// This message may be localized, so do not expect it to
+    /// match a specific string of characters.
+    pub fn get_last_error() -> String {
+        let ptr: *const c_char = unsafe {
+            PHYSFS_getLastError()
+        };
+        if ptr.is_null() {
+            return "".to_string()
+        }
+
+        let buf = unsafe { CStr::from_ptr(ptr).to_bytes().to_vec() };
+
+        String::from_utf8(buf).unwrap()
+    }
+
+    /// Sets a new write directory.
+    /// This method will fail if the current write dir
+    /// still has open files in it.
+    pub fn set_write_dir<P>(&self, write_dir: P) -> Result<()>
+        where P: AsRef<OsStr>
+    {
+        let write_dir = CStr::from_bytes_with_nul(write_dir.as_ref().to_str().unwrap().as_bytes()).unwrap();
+        let ret = unsafe {
+            PHYSFS_setWriteDir(write_dir.as_ptr())
+        };
+
+        match ret {
+            0 => Err(util::physfs_error_as_io_error()),
+            _ => Ok(())
+        }
+    }
+
+    /// Creates a new dir relative to the write_dir.
+    pub fn mkdir(&self, dir_name: &str) -> Result<()> {
+        let c_dir_name = try!(CString::new(dir_name));
+        let ret = unsafe {
+            PHYSFS_mkdir(c_dir_name.as_ptr())
+        };
+
+        match ret {
+            0 => Err(util::physfs_error_as_io_error()),
+            _ => Ok(())
+        }
+    }
+
+    /// Checks if given path exists
+    pub fn exists(&self, path: &str) -> Result<()> {
+        let c_path = try!(CString::new(path));
+        let ret = unsafe { PHYSFS_exists(c_path.as_ptr()) };
+
+        match ret {
+            0 => Err(util::physfs_error_as_io_error()),
+            _ => Ok(())
+        }
+    }
+
+    /// Checks if given path is a directory
+    pub fn is_directory(&self, path: &str) -> Result<()> {
+        let c_path = try!(CString::new(path));
+        let ret = unsafe { PHYSFS_isDirectory(c_path.as_ptr()) };
+
+        match ret {
+            0 => Err(util::physfs_error_as_io_error()),
+            _ => Ok(())
+        }
+    }
+}
+
+impl Drop for PhysFSContext {
+    fn drop(&mut self) {
+        // decrement NUM_CONTEXTS
+        unsafe {
+            NUM_CONTEXTS -= 1;
+        }
+        // and de_init if there aren't any contexts left.
+        if unsafe { NUM_CONTEXTS == 0 } {
+            PhysFSContext::de_init();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/physfs-rs/src/physfs/util.rs	Thu Dec 13 23:44:46 2018 +0100
@@ -0,0 +1,7 @@
+use std::io::{ Error, ErrorKind };
+use super::PhysFSContext;
+
+pub fn physfs_error_as_io_error() -> Error {
+    Error::new(ErrorKind::Other,
+               &format!("PhysicsFS Error: `{}`", PhysFSContext::get_last_error())[..])
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/physfs-rs/src/primitives.rs	Thu Dec 13 23:44:46 2018 +0100
@@ -0,0 +1,19 @@
+#![allow(non_camel_case_types)]
+
+pub type PHYSFS_uint8  = ::libc::c_uchar;
+pub type PHYSFS_sint8  = ::libc::c_char;
+pub type PHYSFS_uint16 = ::libc::c_ushort;
+pub type PHYSFS_sint16 = ::libc::c_short;
+pub type PHYSFS_uint32 = ::libc::c_uint;
+pub type PHYSFS_sint32 = ::libc::c_int;
+
+#[cfg(target_pointer_width = "64")]
+pub type PHYSFS_uint64 = ::libc::c_ulonglong;
+#[cfg(target_pointer_width = "64")]
+pub type PHYSFS_sint64 = ::libc::c_longlong;
+
+// PhysFS defines the 64-bit types to 32 bits on 32-bit systems.
+#[cfg(target_pointer_width = "32")]
+pub type PHYSFS_uint64 = ::libc::c_uint;
+#[cfg(target_pointer_width = "32")]
+pub type PHYSFS_sint64 = ::libc::c_int;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/physfs-rs/tests/directory/mod.rs	Thu Dec 13 23:44:46 2018 +0100
@@ -0,0 +1,40 @@
+use std::io::Read;
+use std::path::Path;
+
+use physfs::{ PhysFSContext, file };
+
+#[test]
+fn read_file_from_directory() {
+    let con = match PhysFSContext::new() {
+        Err(e) => panic!(e),
+        Ok(con) => con
+    };
+
+    assert!(PhysFSContext::is_init());
+
+    match con.mount(&Path::new(super::PATH_TO_HERE), "/test/".to_string(), true) {
+        Err(e) => panic!(e),
+        _ => ()
+    }
+
+    let mut file = match file::File::open(&con, "/test/directory/read.txt".to_string(), file::Mode::Read) {
+        Ok(f) => f,
+        Err(e) => panic!(e)
+    };
+
+    let buf = &mut [0; 32];
+
+    match file.read(buf) {
+        Err(e) => panic!(e),
+        _ => ()
+    }
+
+    let mut contents = String::new();
+    for &mut byte in buf {
+        if byte == 0 { break }
+        contents.push(byte as char);
+    }
+
+    assert!(contents == "Read from me.");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/physfs-rs/tests/directory/read.txt	Thu Dec 13 23:44:46 2018 +0100
@@ -0,0 +1,1 @@
+Read from me.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/physfs-rs/tests/test.rs	Thu Dec 13 23:44:46 2018 +0100
@@ -0,0 +1,15 @@
+extern crate physfs;
+
+use physfs::PhysFSContext;
+
+mod directory;
+
+// from project_root
+const PATH_TO_HERE: &'static str = "tests/";
+
+//#[test]
+fn test_create_physfs_context() {
+    let _c = PhysFSContext::new().unwrap();
+    assert!(PhysFSContext::is_init());
+}
+