Conversation
from https://gmplib.org/manual/Converting-Floats.html : `Function: char * mpf_get_str (char *str, mp_exp_t *expptr, int base, size_t n_digits, const mpf_t op)`
1113816 to
e2c9bde
Compare
src/mpf.rs
Outdated
| } | ||
| } else { | ||
| // From mpf/get_str.c. | ||
| let c_str = CString::new(vec![37; n_digits as usize + 2]).unwrap(); |
There was a problem hiding this comment.
This intermediate CString is just overhead—why not use Vec<c_char>::as_ptr directly?
src/mpf.rs
Outdated
| let c_str = CString::new(vec![37; n_digits as usize + 2]).unwrap(); | ||
| unsafe { | ||
| let p = __gmpf_get_str(c_str.as_ptr(), exp, base, n_digits, &self.mpf); | ||
| CString::from_raw(p).to_str().unwrap().to_string() |
There was a problem hiding this comment.
Doesn’t this double-free the string, once via c_str and once via the CString? I think you want CStr::from_ptr.
|
|
||
| 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]) |
There was a problem hiding this comment.
CStr::from_ptr is more direct and avoids the separate strlen.
There was a problem hiding this comment.
no matter what, the length needs to be known in order to call the free func, so it's either strlen or CStr::to_bytes().len(). i think strlen communicates more clearly how the length param to free_func is computed, whereas the alternate method relies on knowing that CStr implicitly calls strlen behind the scenes.
src/mpf.rs
Outdated
| } else { | ||
| // From mpf/get_str.c. | ||
| let bytes = unsafe { | ||
| let mut bytes: Vec<u8> = uninitialized(); |
There was a problem hiding this comment.
You need a Vec with uninitialized data, not an uninitialized Vec! I think Vec::with_capacity is the right thing.
src/mpf.rs
Outdated
| // From mpf/get_str.c. | ||
| let bytes = unsafe { | ||
| let mut bytes: Vec<u8> = uninitialized(); | ||
| let c_str: *mut c_char = transmute(bytes.as_mut_ptr()); |
There was a problem hiding this comment.
No need for a worrying transmute—you can just bytes.as_mut_ptr() as *mut c_char.
There was a problem hiding this comment.
pretty sure that as_mut_ptr will return a * mut u8.
src/mpf.rs
Outdated
| __gmpf_get_str(c_str, exp, base, n_digits, &self.mpf); | ||
| bytes | ||
| }; | ||
| String::from_utf8(bytes).unwrap() |
There was a problem hiding this comment.
yep. from mpf/get_str.c:
if (dbuf == 0)
{
/* We didn't get a string from the user. Allocate one (and return
a pointer to it) with space for `-' and terminating null. */
alloc_size = n_digits + 2;
dbuf = (char *) (*__gmp_allocate_func) (n_digits + 2);
src/mpf.rs
Outdated
| } else { | ||
| let bytes = unsafe { | ||
| // n_digits + 2 from mpf/get_str.c. | ||
| let mut bytes: Vec<u8> = vec![uninitialized(); n_digits as usize + 2]; |
There was a problem hiding this comment.
This create a single uninitialized byte, then copies it n_digits + 2 times, so you haven’t really saved any work.
I think what you want is
let mut buf = Vec::with_capacity(n_digits as usize + 2);
let ptr = __gmpf_get_str(buf.as_mut_ptr(), exp, base, n_digits, &self.mpf);
CStr::from_ptr(ptr).to_str().unwrap().to_string()There was a problem hiding this comment.
i think i'll revert to vec![0; n_digits + 2], which will call rust_allocate_zeroed which i'm guessing is more or less calloc. the with_capacity trick is neat but seems a little unsafe, and to be honest, i'm not sure that enough digits are requested for an appreciable amount of work to be saved.
src/mpf.rs
Outdated
| __gmpf_get_str(c_str, exp, base, n_digits, &self.mpf); | ||
| bytes | ||
| }; | ||
| String::from_utf8(bytes).unwrap() |
There was a problem hiding this comment.
No no, my point here was that String::from_utf8 includes the trailing NUL in its return value.
There was a problem hiding this comment.
yeah, i realized what you meant a few minutes after hitting send. this branch should be tested anyway, and it shows up in the test.
this incorrectly created an uninitialized Vec, and using uninitialized isn't worth it anyway.
851473b to
9483bf6
Compare
src/ffi.rs
Outdated
| use std::ptr::null_mut; | ||
| unsafe { | ||
| let mut free_func: FreeFunc = mem::uninitialized(); | ||
| __gmp_get_memory_functions(null_mut(), null_mut(), mem::transmute(&mut free_func)); |
There was a problem hiding this comment.
This transmute is unnecessary because &mut FreeFunc converts implicitly to *mut FreeFunc.
9483bf6 to
58d47c2
Compare
bug was introduced in 877104f.
the intent here is to allocate enough space for gmpf_get_str to plop a string. if all digits are requested (n_digits == 0), we defer to gmpf_get_str itself to figure out how much space is needed (because the limb nonsense is too tedious to deal with) by passing nullptr to the first argument, as documented in the manual:
if the number of digits requested is known (non-zero n_digits), then simply allocate a correctly sized cstring (unlike 877104f, which allocates a 0-byte string).
to avoid memory leaks from repeated get_str(n_digits=0) calls, a wrapper is used to de-allocate on drop the gmp-allocated string.
with this patch, #24 passes.