// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Unsafe pointer utility functions use cast; use libc; use libc::{c_void, size_t}; use unstable::intrinsics::{memmove32,memmove64}; use sys; #[cfg(test)] use vec; #[cfg(test)] use str; #[cfg(notest)] use cmp::{Eq, Ord}; pub mod libc_ { use libc::c_void; use libc; #[nolink] #[abi = "cdecl"] pub extern { #[rust_stack] unsafe fn memmove(dest: *mut c_void, src: *const c_void, n: libc::size_t) -> *c_void; #[rust_stack] unsafe fn memset(dest: *mut c_void, c: libc::c_int, len: libc::size_t) -> *c_void; } } pub mod rusti { #[abi = "rust-intrinsic"] pub extern { fn addr_of(&&val: T) -> *T; } } /// Get an unsafe pointer to a value #[inline(always)] pub pure fn addr_of(val: &T) -> *T { unsafe { rusti::addr_of(*val) } } /// Calculate the offset from a pointer #[inline(always)] pub pure fn offset(ptr: *T, count: uint) -> *T { unsafe { (ptr as uint + count * sys::size_of::()) as *T } } /// Calculate the offset from a const pointer #[inline(always)] pub pure fn const_offset(ptr: *const T, count: uint) -> *const T { unsafe { (ptr as uint + count * sys::size_of::()) as *T } } /// Calculate the offset from a mut pointer #[inline(always)] pub pure fn mut_offset(ptr: *mut T, count: uint) -> *mut T { (ptr as uint + count * sys::size_of::()) as *mut T } /// Return the offset of the first null pointer in `buf`. #[inline(always)] pub unsafe fn buf_len(buf: **T) -> uint { position(buf, |i| *i == null()) } /// Return the first offset `i` such that `f(buf[i]) == true`. #[inline(always)] pub unsafe fn position(buf: *T, f: fn(&T) -> bool) -> uint { let mut i = 0; loop { if f(&(*offset(buf, i))) { return i; } else { i += 1; } } } /// Create an unsafe null pointer #[inline(always)] pub pure fn null() -> *T { unsafe { cast::reinterpret_cast(&0u) } } /// Create an unsafe mutable null pointer #[inline(always)] pub pure fn mut_null() -> *mut T { unsafe { cast::reinterpret_cast(&0u) } } /// Returns true if the pointer is equal to the null pointer. #[inline(always)] pub pure fn is_null(ptr: *const T) -> bool { ptr == null() } /// Returns true if the pointer is not equal to the null pointer. #[inline(always)] pub pure fn is_not_null(ptr: *const T) -> bool { !is_null(ptr) } /** * Copies data from one location to another * * Copies `count` elements (not bytes) from `src` to `dst`. The source * and destination may overlap. */ #[inline(always)] #[cfg(target_word_size = "32")] pub unsafe fn copy_memory(dst: *mut T, src: *const T, count: uint) { let n = count * sys::size_of::(); memmove32(dst as *mut u8, src as *u8, n as u32); } #[inline(always)] #[cfg(target_word_size = "64")] pub unsafe fn copy_memory(dst: *mut T, src: *const T, count: uint) { let n = count * sys::size_of::(); memmove64(dst as *mut u8, src as *u8, n as u64); } #[inline(always)] pub unsafe fn set_memory(dst: *mut T, c: int, count: uint) { let n = count * sys::size_of::(); libc_::memset(dst as *mut c_void, c as libc::c_int, n as size_t); } /** Transform a region pointer - &T - to an unsafe pointer - *T. This is safe, but is implemented with an unsafe block due to reinterpret_cast. */ #[inline(always)] pub pure fn to_unsafe_ptr(thing: &T) -> *T { unsafe { cast::reinterpret_cast(&thing) } } /** Transform a const region pointer - &const T - to a const unsafe pointer - *const T. This is safe, but is implemented with an unsafe block due to reinterpret_cast. */ #[inline(always)] pub pure fn to_const_unsafe_ptr(thing: &const T) -> *const T { unsafe { cast::reinterpret_cast(&thing) } } /** Transform a mutable region pointer - &mut T - to a mutable unsafe pointer - *mut T. This is safe, but is implemented with an unsafe block due to reinterpret_cast. */ #[inline(always)] pub pure fn to_mut_unsafe_ptr(thing: &mut T) -> *mut T { unsafe { cast::reinterpret_cast(&thing) } } /** Cast a region pointer - &T - to a uint. This is safe, but is implemented with an unsafe block due to reinterpret_cast. (I couldn't think of a cutesy name for this one.) */ #[inline(always)] pub pure fn to_uint(thing: &T) -> uint { unsafe { cast::reinterpret_cast(&thing) } } /// Determine if two borrowed pointers point to the same thing. #[inline(always)] pub pure fn ref_eq(thing: &a/T, other: &b/T) -> bool { to_uint(thing) == to_uint(other) } pub trait Ptr { pure fn is_null(&self) -> bool; pure fn is_not_null(&self) -> bool; pure fn offset(&self, count: uint) -> Self; } /// Extension methods for immutable pointers impl Ptr for *T { /// Returns true if the pointer is equal to the null pointer. #[inline(always)] pure fn is_null(&self) -> bool { is_null(*self) } /// Returns true if the pointer is not equal to the null pointer. #[inline(always)] pure fn is_not_null(&self) -> bool { is_not_null(*self) } /// Calculates the offset from a pointer. #[inline(always)] pure fn offset(&self, count: uint) -> *T { offset(*self, count) } } /// Extension methods for mutable pointers impl Ptr for *mut T { /// Returns true if the pointer is equal to the null pointer. #[inline(always)] pure fn is_null(&self) -> bool { is_null(*self) } /// Returns true if the pointer is not equal to the null pointer. #[inline(always)] pure fn is_not_null(&self) -> bool { is_not_null(*self) } /// Calculates the offset from a mutable pointer. #[inline(always)] pure fn offset(&self, count: uint) -> *mut T { mut_offset(*self, count) } } // Equality for pointers #[cfg(notest)] impl Eq for *const T { #[inline(always)] pure fn eq(&self, other: &*const T) -> bool { unsafe { let a: uint = cast::reinterpret_cast(&(*self)); let b: uint = cast::reinterpret_cast(&(*other)); return a == b; } } #[inline(always)] pure fn ne(&self, other: &*const T) -> bool { !(*self).eq(other) } } // Comparison for pointers #[cfg(notest)] impl Ord for *const T { #[inline(always)] pure fn lt(&self, other: &*const T) -> bool { unsafe { let a: uint = cast::reinterpret_cast(&(*self)); let b: uint = cast::reinterpret_cast(&(*other)); return a < b; } } #[inline(always)] pure fn le(&self, other: &*const T) -> bool { unsafe { let a: uint = cast::reinterpret_cast(&(*self)); let b: uint = cast::reinterpret_cast(&(*other)); return a <= b; } } #[inline(always)] pure fn ge(&self, other: &*const T) -> bool { unsafe { let a: uint = cast::reinterpret_cast(&(*self)); let b: uint = cast::reinterpret_cast(&(*other)); return a >= b; } } #[inline(always)] pure fn gt(&self, other: &*const T) -> bool { unsafe { let a: uint = cast::reinterpret_cast(&(*self)); let b: uint = cast::reinterpret_cast(&(*other)); return a > b; } } } // Equality for region pointers #[cfg(notest)] impl Eq for &self/const T { #[inline(always)] pure fn eq(&self, other: & &self/const T) -> bool { return *(*self) == *(*other); } #[inline(always)] pure fn ne(&self, other: & &self/const T) -> bool { return *(*self) != *(*other); } } // Comparison for region pointers #[cfg(notest)] impl Ord for &self/const T { #[inline(always)] pure fn lt(&self, other: & &self/const T) -> bool { *(*self) < *(*other) } #[inline(always)] pure fn le(&self, other: & &self/const T) -> bool { *(*self) <= *(*other) } #[inline(always)] pure fn ge(&self, other: & &self/const T) -> bool { *(*self) >= *(*other) } #[inline(always)] pure fn gt(&self, other: & &self/const T) -> bool { *(*self) > *(*other) } } #[test] pub fn test() { unsafe { struct Pair {mut fst: int, mut snd: int}; let mut p = Pair {fst: 10, snd: 20}; let pptr: *mut Pair = &mut p; let iptr: *mut int = cast::reinterpret_cast(&pptr); fail_unless!((*iptr == 10));; *iptr = 30; fail_unless!((*iptr == 30)); fail_unless!((p.fst == 30));; *pptr = Pair {fst: 50, snd: 60}; fail_unless!((*iptr == 50)); fail_unless!((p.fst == 50)); fail_unless!((p.snd == 60)); let mut v0 = ~[32000u16, 32001u16, 32002u16]; let mut v1 = ~[0u16, 0u16, 0u16]; copy_memory(mut_offset(vec::raw::to_mut_ptr(v1), 1u), offset(vec::raw::to_ptr(v0), 1u), 1u); fail_unless!((v1[0] == 0u16 && v1[1] == 32001u16 && v1[2] == 0u16)); copy_memory(vec::raw::to_mut_ptr(v1), offset(vec::raw::to_ptr(v0), 2u), 1u); fail_unless!((v1[0] == 32002u16 && v1[1] == 32001u16 && v1[2] == 0u16)); copy_memory(mut_offset(vec::raw::to_mut_ptr(v1), 2u), vec::raw::to_ptr(v0), 1u); fail_unless!((v1[0] == 32002u16 && v1[1] == 32001u16 && v1[2] == 32000u16)); } } #[test] pub fn test_position() { use str::as_c_str; use libc::c_char; let s = ~"hello"; unsafe { fail_unless!(2u == as_c_str(s, |p| position(p, |c| *c == 'l' as c_char))); fail_unless!(4u == as_c_str(s, |p| position(p, |c| *c == 'o' as c_char))); fail_unless!(5u == as_c_str(s, |p| position(p, |c| *c == 0 as c_char))); } } #[test] pub fn test_buf_len() { let s0 = ~"hello"; let s1 = ~"there"; let s2 = ~"thing"; do str::as_c_str(s0) |p0| { do str::as_c_str(s1) |p1| { do str::as_c_str(s2) |p2| { let v = ~[p0, p1, p2, null()]; do vec::as_imm_buf(v) |vp, len| { fail_unless!(unsafe { buf_len(vp) } == 3u); fail_unless!(len == 4u); } } } } } #[test] pub fn test_is_null() { let p: *int = null(); fail_unless!(p.is_null()); fail_unless!(!p.is_not_null()); let q = offset(p, 1u); fail_unless!(!q.is_null()); fail_unless!(q.is_not_null()); let mp: *mut int = mut_null(); fail_unless!(mp.is_null()); fail_unless!(!mp.is_not_null()); let mq = mp.offset(1u); fail_unless!(!mq.is_null()); fail_unless!(mq.is_not_null()); }