diff --git a/src/ffi.rs b/src/ffi.rs index 1351076..7706cc3 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -1,7 +1,38 @@ use mpz::*; +use std::{mem, slice, str}; +use libc::{c_char, strlen}; + +type AllocFunc = extern "C" fn(usize) -> *mut c_char; +type ReallocFunc = extern "C" fn(*mut c_char, usize, usize) -> *mut c_char; +type FreeFunc = extern "C" fn(*mut c_char, usize); #[link(name = "gmp")] extern "C" { pub fn __gmpz_fdiv_q(q: mpz_ptr, n: mpz_srcptr, d: mpz_srcptr); pub fn __gmpz_cdiv_q(q: mpz_ptr, n: mpz_srcptr, d: mpz_srcptr); + pub fn __gmp_get_memory_functions(alloc: *mut AllocFunc, relloc: *mut ReallocFunc, free: *mut FreeFunc); +} + +pub struct GString(*mut u8, usize); + +impl GString { + pub unsafe fn from_raw(raw: *mut c_char) -> GString { + GString(raw as *mut u8, strlen(raw) as usize + 1) + } + + pub fn to_str(&self) -> Result<&str, str::Utf8Error> { + let bytes: &[u8] = unsafe { slice::from_raw_parts(self.0, self.1) }; + str::from_utf8(&bytes[..bytes.len() - 1]) + } +} + +impl Drop for GString { + fn drop(&mut self) { + use std::ptr::null_mut; + unsafe { + let mut free_func: FreeFunc = mem::uninitialized(); + __gmp_get_memory_functions(null_mut(), null_mut(), &mut free_func); + free_func(self.0 as *mut c_char, self.1); + } + } } diff --git a/src/mpf.rs b/src/mpf.rs index a5f7108..301a08a 100644 --- a/src/mpf.rs +++ b/src/mpf.rs @@ -1,6 +1,6 @@ -use libc::{c_double, c_int, c_long, c_ulong, c_void,c_char}; +use libc::{c_double, c_int, c_long, c_ulong, c_void, c_char, strlen}; use std::mem::uninitialized; -use std::cmp; +use std::{cmp, str}; use std::cmp::Ordering::{self, Greater, Less, Equal}; use std::ops::{Div, DivAssign, Mul, MulAssign, Add, AddAssign, Sub, SubAssign, Neg}; use std::ffi::CString; @@ -10,6 +10,7 @@ use super::mpz::{Mpz, mpz_srcptr}; use super::mpq::{Mpq, mpq_srcptr}; use super::sign::Sign; use num_traits::{Zero, One}; +use ffi::GString; type mp_exp_t = c_long; @@ -37,7 +38,7 @@ extern "C" { fn __gmpf_set_str(rop: mpf_ptr, str: *const c_char, base: c_int); fn __gmpf_set_si(rop: mpf_ptr, op: c_long); - fn __gmpf_get_str(str: *const c_char, expptr: *const mp_exp_t, base: i32, n_digits: i32, op: mpf_ptr) -> *mut c_char; + fn __gmpf_get_str(str: *mut c_char, expptr: *const mp_exp_t, base: i32, n_digits: i32, op: mpf_srcptr) -> *mut c_char; fn __gmpf_cmp(op1: mpf_srcptr, op2: mpf_srcptr) -> c_int; fn __gmpf_cmp_d(op1: mpf_srcptr, op2: c_double) -> c_int; @@ -119,13 +120,27 @@ impl Mpf { } } - pub fn get_str(&mut self, n_digits: i32, base: i32, exp: &mut c_long) -> String{ - let c_str = CString::new("").unwrap(); - let out; - unsafe{ - out = CString::from_raw(__gmpf_get_str(c_str.into_raw(), exp, base, n_digits, &mut self.mpf)); + /// For values of zero, this will return an empty string. + pub fn get_str(&self, n_digits: i32, base: i32, exp: &mut c_long) -> String { + use std::ptr::null_mut; + if n_digits == 0 { + // Maximal significant digits requested. Let GMP compute the length + // and allocate the space required. + unsafe { + let p = __gmpf_get_str(null_mut(), exp, base, n_digits, &self.mpf); + // De-allocates the GMP string on drop. + GString::from_raw(p).to_str().unwrap().to_string() + } + } else { + unsafe { + // n_digits + 2 from mpf/get_str.c. + let mut bytes: Vec = vec![0; n_digits as usize + 2]; + let c_str: *mut c_char = bytes.as_mut_ptr() as *mut c_char; + __gmpf_get_str(c_str, exp, base, n_digits, &self.mpf); + // Don't include null bytes. + String::from(str::from_utf8(&bytes[..strlen(c_str)]).unwrap()) + } } - out.to_str().unwrap().to_string() } pub fn abs(&self) -> Mpf { diff --git a/src/test.rs b/src/test.rs index 3af1382..a264c1e 100644 --- a/src/test.rs +++ b/src/test.rs @@ -713,4 +713,16 @@ mod mpf { assert_eq!(five.sign(), Sign::Positive); assert_eq!(minus_five.sign(), Sign::Negative); } + + #[test] + fn test_get_str() { + use libc::c_long; + let mut tmp = 0 as c_long; + let mut pi: Mpf = Mpf::zero(); + assert_eq!(pi.get_str(4, 10, &mut tmp), ""); + pi.set_from_str("3.141592653589", 10); + + assert_eq!(&pi.get_str(4, 10, &mut tmp), "3142"); + assert_eq!((-&pi).get_str(4, 10, &mut tmp), "-3142"); + } }