Skip to content

Commit e4771d1

Browse files
committed
auto merge of #16853 : tbu-/rust/pr_mapinplace, r=aturon
This is implemented using a new struct PartialVec which implements the proper drop semantics in case the conversion is interrupted by an unwind. For the old pull requests, see #15302, #16369.
2 parents 793a366 + 2c7f6ee commit e4771d1

File tree

1 file changed

+275
-0
lines changed

1 file changed

+275
-0
lines changed

src/libcollections/vec.rs

+275
Original file line numberDiff line numberDiff line change
@@ -1710,6 +1710,268 @@ pub mod raw {
17101710
}
17111711
}
17121712

1713+
/// An owned, partially type-converted vector.
1714+
///
1715+
/// This struct takes two type parameters `T` and `U` which must be of the
1716+
/// same, non-zero size having the same minimal alignment.
1717+
///
1718+
/// No allocations are performed by usage, only a deallocation happens in the
1719+
/// destructor which should only run when unwinding.
1720+
///
1721+
/// It can be used to convert a vector of `T`s into a vector of `U`s, by
1722+
/// converting the individual elements one-by-one.
1723+
///
1724+
/// You may call the `push` method as often as you get a `Some(t)` from `pop`.
1725+
/// After pushing the same number of `U`s as you got `T`s, you can `unwrap` the
1726+
/// vector.
1727+
///
1728+
/// # Example
1729+
///
1730+
/// ```ignore
1731+
/// let pv = PartialVec::from_vec(vec![0u32, 1]);
1732+
/// assert_eq!(pv.pop(), Some(0));
1733+
/// assert_eq!(pv.pop(), Some(1));
1734+
/// assert_eq!(pv.pop(), None);
1735+
/// pv.push(2u32);
1736+
/// pv.push(3);
1737+
/// assert_eq!(pv.into_vec().as_slice(), &[2, 3]);
1738+
/// ```
1739+
//
1740+
// Upheld invariants:
1741+
//
1742+
// (a) `vec` isn't modified except when the `PartialVec` goes out of scope, the
1743+
// only thing it is used for is keeping the memory which the `PartialVec`
1744+
// uses for the inplace conversion.
1745+
//
1746+
// (b) `start_u` points to the start of the vector.
1747+
//
1748+
// (c) `end_u` points to one element beyond the vector.
1749+
//
1750+
// (d) `start_u` <= `end_u` <= `start_t` <= `end_t`.
1751+
//
1752+
// (e) From `start_u` (incl.) to `end_u` (excl.) there are sequential instances
1753+
// of type `U`.
1754+
//
1755+
// (f) From `start_t` (incl.) to `end_t` (excl.) there are sequential instances
1756+
// of type `T`.
1757+
//
1758+
// (g) The size of `T` and `U` is equal and non-zero.
1759+
//
1760+
// (h) The `min_align_of` of `T` and `U` is equal.
1761+
1762+
struct PartialVec<T,U> {
1763+
vec: Vec<T>,
1764+
1765+
start_u: *mut U,
1766+
end_u: *mut U,
1767+
start_t: *mut T,
1768+
end_t: *mut T,
1769+
}
1770+
1771+
impl<T,U> PartialVec<T,U> {
1772+
/// Creates a `PartialVec` from a `Vec`.
1773+
///
1774+
/// # Failure
1775+
///
1776+
/// Fails if `T` and `U` have differing sizes, are zero-sized or have
1777+
/// differing minimal alignments.
1778+
fn from_vec(mut vec: Vec<T>) -> PartialVec<T,U> {
1779+
// FIXME: Assert statically that the types `T` and `U` have the same
1780+
// size.
1781+
//
1782+
// These asserts make sure (g) and (h) are satisfied.
1783+
assert!(mem::size_of::<T>() != 0);
1784+
assert!(mem::size_of::<U>() != 0);
1785+
assert!(mem::size_of::<T>() == mem::size_of::<U>());
1786+
assert!(mem::min_align_of::<T>() == mem::min_align_of::<U>());
1787+
1788+
let start = vec.as_mut_ptr();
1789+
1790+
// This `as int` cast is safe, because the size of the elements of the
1791+
// vector is not 0, and:
1792+
//
1793+
// 1) If the size of the elements in the vector is 1, the `int` may
1794+
// overflow, but it has the correct bit pattern so that the
1795+
// `.offset()` function will work.
1796+
//
1797+
// Example:
1798+
// Address space 0x0-0xF.
1799+
// `u8` array at: 0x1.
1800+
// Size of `u8` array: 0x8.
1801+
// Calculated `offset`: -0x8.
1802+
// After `array.offset(offset)`: 0x9.
1803+
// (0x1 + 0x8 = 0x1 - 0x8)
1804+
//
1805+
// 2) If the size of the elements in the vector is >1, the `uint` ->
1806+
// `int` conversion can't overflow.
1807+
let offset = vec.len() as int;
1808+
1809+
let start_u = start as *mut U;
1810+
let end_u = start as *mut U;
1811+
let start_t = start;
1812+
1813+
// This points inside the vector, as the vector has length `offset`.
1814+
let end_t = unsafe { start_t.offset(offset) };
1815+
1816+
// (b) is satisfied, `start_u` points to the start of `vec`.
1817+
//
1818+
// (c) is also satisfied, `end_t` points to the end of `vec`.
1819+
//
1820+
// `start_u == end_u == start_t <= end_t`, so also `start_u <= end_u <=
1821+
// start_t <= end_t`, thus (b).
1822+
//
1823+
// As `start_u == end_u`, it is represented correctly that there are no
1824+
// instances of `U` in `vec`, thus (e) is satisfied.
1825+
//
1826+
// At start, there are only elements of type `T` in `vec`, so (f) is
1827+
// satisfied, as `start_t` points to the start of `vec` and `end_t` to
1828+
// the end of it.
1829+
1830+
PartialVec {
1831+
// (a) is satisfied, `vec` isn't modified in the function.
1832+
vec: vec,
1833+
start_u: start_u,
1834+
end_u: end_u,
1835+
start_t: start_t,
1836+
end_t: end_t,
1837+
}
1838+
}
1839+
1840+
/// Pops a `T` from the `PartialVec`.
1841+
///
1842+
/// Removes the next `T` from the vector and returns it as `Some(T)`, or
1843+
/// `None` if there are none left.
1844+
fn pop(&mut self) -> Option<T> {
1845+
// The `if` ensures that there are more `T`s in `vec`.
1846+
if self.start_t < self.end_t {
1847+
let result;
1848+
unsafe {
1849+
// (f) is satisfied before, so in this if branch there actually
1850+
// is a `T` at `start_t`. After shifting the pointer by one,
1851+
// (f) is again satisfied.
1852+
result = ptr::read(self.start_t as *const T);
1853+
self.start_t = self.start_t.offset(1);
1854+
}
1855+
Some(result)
1856+
} else {
1857+
None
1858+
}
1859+
}
1860+
1861+
/// Pushes a new `U` to the `PartialVec`.
1862+
///
1863+
/// # Failure
1864+
///
1865+
/// Fails if not enough `T`s were popped to have enough space for the new
1866+
/// `U`.
1867+
fn push(&mut self, value: U) {
1868+
// The assert assures that still `end_u <= start_t` (d) after
1869+
// the function.
1870+
assert!(self.end_u as *const () < self.start_t as *const (),
1871+
"writing more elements to PartialVec than reading from it")
1872+
unsafe {
1873+
// (e) is satisfied before, and after writing one `U`
1874+
// to `end_u` and shifting it by one, it's again
1875+
// satisfied.
1876+
ptr::write(self.end_u, value);
1877+
self.end_u = self.end_u.offset(1);
1878+
}
1879+
}
1880+
1881+
/// Unwraps the new `Vec` of `U`s after having pushed enough `U`s and
1882+
/// popped all `T`s.
1883+
///
1884+
/// # Failure
1885+
///
1886+
/// Fails if not all `T`s were popped, also fails if not the same amount of
1887+
/// `U`s was pushed before calling `unwrap`.
1888+
fn into_vec(mut self) -> Vec<U> {
1889+
// If `self.end_u == self.end_t`, we know from (e) that there are no
1890+
// more `T`s in `vec`, we also know that the whole length of `vec` is
1891+
// now used by `U`s, thus we can just interpret `vec` as a vector of
1892+
// `U` safely.
1893+
1894+
assert!(self.end_u as *const () == self.end_t as *const (),
1895+
"trying to unwrap a PartialVec before completing the writes to it");
1896+
1897+
// Extract `vec` and prevent the destructor of `PartialVec` from
1898+
// running. Note that none of the function calls can fail, thus no
1899+
// resources can be leaked (as the `vec` member of `PartialVec` is the
1900+
// only one which holds allocations -- and it is returned from this
1901+
// function.
1902+
unsafe {
1903+
let vec_len = self.vec.len();
1904+
let vec_cap = self.vec.capacity();
1905+
let vec_ptr = self.vec.as_mut_ptr() as *mut U;
1906+
mem::forget(self);
1907+
Vec::from_raw_parts(vec_len, vec_cap, vec_ptr)
1908+
}
1909+
}
1910+
}
1911+
1912+
#[unsafe_destructor]
1913+
impl<T,U> Drop for PartialVec<T,U> {
1914+
fn drop(&mut self) {
1915+
unsafe {
1916+
// As per (a) `vec` hasn't been modified until now. As it has a
1917+
// length currently, this would run destructors of `T`s which might
1918+
// not be there. So at first, set `vec`s length to `0`. This must
1919+
// be done at first to remain memory-safe as the destructors of `U`
1920+
// or `T` might cause unwinding where `vec`s destructor would be
1921+
// executed.
1922+
self.vec.set_len(0);
1923+
1924+
// As per (e) and (f) we have instances of `U`s and `T`s in `vec`.
1925+
// Destruct them.
1926+
while self.start_u < self.end_u {
1927+
let _ = ptr::read(self.start_u as *const U); // Run a `U` destructor.
1928+
self.start_u = self.start_u.offset(1);
1929+
}
1930+
while self.start_t < self.end_t {
1931+
let _ = ptr::read(self.start_t as *const T); // Run a `T` destructor.
1932+
self.start_t = self.start_t.offset(1);
1933+
}
1934+
// After this destructor ran, the destructor of `vec` will run,
1935+
// deallocating the underlying memory.
1936+
}
1937+
}
1938+
}
1939+
1940+
impl<T> Vec<T> {
1941+
/// Converts a `Vec<T>` to a `Vec<U>` where `T` and `U` have the same
1942+
/// non-zero size and the same minimal alignment.
1943+
///
1944+
/// # Failure
1945+
///
1946+
/// Fails if `T` and `U` have differing sizes, are zero-sized or have
1947+
/// differing minimal alignments.
1948+
///
1949+
/// # Example
1950+
///
1951+
/// ```
1952+
/// let v = vec![0u, 1, 2];
1953+
/// let w = v.map_in_place(|i| i + 3);
1954+
/// assert_eq!(w.as_slice(), [3, 4, 5].as_slice());
1955+
///
1956+
/// #[deriving(PartialEq, Show)]
1957+
/// struct Newtype(u8);
1958+
/// let bytes = vec![0x11, 0x22];
1959+
/// let newtyped_bytes = bytes.map_in_place(|x| Newtype(x));
1960+
/// assert_eq!(newtyped_bytes.as_slice(), [Newtype(0x11), Newtype(0x22)].as_slice());
1961+
/// ```
1962+
pub fn map_in_place<U>(self, f: |T| -> U) -> Vec<U> {
1963+
let mut pv = PartialVec::from_vec(self);
1964+
loop {
1965+
let maybe_t = pv.pop();
1966+
match maybe_t {
1967+
Some(t) => pv.push(f(t)),
1968+
None => return pv.into_vec(),
1969+
};
1970+
}
1971+
}
1972+
}
1973+
1974+
17131975
#[cfg(test)]
17141976
mod tests {
17151977
extern crate test;
@@ -2041,6 +2303,19 @@ mod tests {
20412303
assert_eq!(vec.len(), 0);
20422304
}
20432305

2306+
#[test]
2307+
#[should_fail]
2308+
fn test_map_inp_lace_incompatible_types_fail() {
2309+
let v = vec![0u, 1, 2];
2310+
v.map_in_place(|_| ());
2311+
}
2312+
2313+
#[test]
2314+
fn test_map_in_place() {
2315+
let v = vec![0u, 1, 2];
2316+
assert_eq!(v.map_in_place(|i: uint| i as int - 1).as_slice(), [-1i, 0, 1].as_slice());
2317+
}
2318+
20442319
#[bench]
20452320
fn bench_new(b: &mut Bencher) {
20462321
b.iter(|| {

0 commit comments

Comments
 (0)