<commit_before>use error::Error;
use ffi::GGeom;
use from_geo::TryInto;
use geo_types::{Geometry, GeometryCollection, Point, Polygon};

//TODO, change  &[] to IntoIterator
pub fn compute_voronoi(points: &[Point<f64>], tolerance: f64) -> Result<Vec<Polygon<f64>>, Error> {
    let geom_points: GGeom = points.try_into()?;

    geom_points
        .voronoi(None, tolerance, false)?
        .try_into()
        .and_then(|g: Geometry<f64>| match g {
            Geometry::GeometryCollection(gc) => Ok(gc),
            _ => Err(Error::ConversionError("invalid geometry type".into())),
        }).and_then(|gc: GeometryCollection<f64>| {
            gc.0.into_iter()
                .map(|g| {
                    g.as_polygon()
                        .ok_or(Error::ConversionError("invalid inner geometry type".into()))
                }).collect()
        })
}

#[cfg(test)]
mod test {
    use ffi::GGeom;
    use geo_types::{LineString, Point, Polygon, Coordinate};
    /// create a voronoi diagram. Same unit test as https://github.com/libgeos/geos/blob/master/tests/unit/triangulate/VoronoiTest.cpp#L118
    #[test]
    fn simple_voronoi() {
        let points = "MULTIPOINT ((150 200), (180 270), (275 163))";
        let input = GGeom::new(points).unwrap();

        let mut voronoi = input.voronoi(None, 0., false).unwrap();

        let expected_output = "GEOMETRYCOLLECTION (
            POLYGON ((25 38, 25 295, 221.20588235294116 210.91176470588235, 170.024 38, 25 38)),
            POLYGON ((400 369.6542056074766, 400 38, 170.024 38, 221.20588235294116 210.91176470588235, 400 369.6542056074766)),
            POLYGON ((25 295, 25 395, 400 395, 400 369.6542056074766, 221.20588235294116 210.91176470588235, 25 295)))";

        let mut expected_output = GGeom::new(expected_output).unwrap();

        expected_output.normalize().unwrap();
        voronoi.normalize().unwrap();

        let same = expected_output.equals_exact(&voronoi, 1e-3).unwrap();
        assert!(same);
    }

    /// test precision. Same unit test as https://github.com/libgeos/geos/blob/master/tests/unit/triangulate/VoronoiTest.cpp#L160
    #[test]
    fn wkt_voronoi_precision() {
        let points = "MULTIPOINT ((100 200), (105 202), (110 200), (140 230),
        (210 240), (220 190), (170 170), (170 260), (213 245), (220 190))";
        let input = GGeom::new(points).unwrap();

        let mut voronoi = input.voronoi(None, 6., false).unwrap();

        let expected_output = "GEOMETRYCOLLECTION (
        POLYGON ((-20 50, -20 380, -3.75 380, 105 235, 105 115, 77.14285714285714 50, -20 50)),
        POLYGON ((247 50, 77.14285714285714 50, 105 115, 145 195, 178.33333333333334 211.66666666666666, 183.51851851851853 208.7037037037037, 247 50)),
        POLYGON ((-3.75 380, 20.000000000000007 380, 176.66666666666666 223.33333333333334, 178.33333333333334 211.66666666666666, 145 195, 105 235, -3.75 380)),
        POLYGON ((105 115, 105 235, 145 195, 105 115)),
        POLYGON ((20.000000000000007 380, 255 380, 176.66666666666666 223.33333333333334, 20.000000000000007 380)),
        POLYGON ((255 380, 340 380, 340 240, 183.51851851851853 208.7037037037037, 178.33333333333334 211.66666666666666, 176.66666666666666 223.33333333333334, 255 380)),
        POLYGON ((340 240, 340 50, 247 50, 183.51851851851853 208.7037037037037, 340 240)))";

        let mut expected_output = GGeom::new(expected_output).unwrap();

        expected_output.normalize().unwrap();
        voronoi.normalize().unwrap();

        let same = expected_output.equals_exact(&voronoi, 1e-3).unwrap();
        assert!(same);
    }

    fn coords(tuples: Vec<(f64, f64)>) -> Vec<Coordinate<f64>> {
        tuples.into_iter().map(Coordinate::from).collect()
    }

    // test the rust-geo voronoi wrapper
    #[test]
    fn geo_voronoi() {
        let points = vec![
            Point::new(0., 0.),
            Point::new(0., 1.),
            Point::new(1., 1.),
            Point::new(1., 0.),
        ];

        let voronoi = ::compute_voronoi(&points, 0.).unwrap();

        let poly = vec![
            Polygon::new(
                LineString(coords(vec![
                    (0.5, 2.0),
                    (2.0, 2.0),
                    (2.0, 0.5),
                    (0.5, 0.5),
                    (0.5, 2.0),
                ])),
                vec![],
            ),
            Polygon::new(
                LineString(coords(vec![
                    (-1.0, 0.5),
                    (-1.0, 2.0),
                    (0.5, 2.0),
                    (0.5, 0.5),
                    (-1.0, 0.5),
                ])),
                vec![],
            ),
            Polygon::new(
                LineString(coords(vec![
                    (0.5, -1.0),
                    (-1.0, -1.0),
                    (-1.0, 0.5),
                    (0.5, 0.5),
                    (0.5, -1.0),
                ])),
                vec![],
            ),
            Polygon::new(
                LineString(coords(vec![
                    (2.0, 0.5),
                    (2.0, -1.0),
                    (0.5, -1.0),
                    (0.5, 0.5),
                    (2.0, 0.5),
                ])),
                vec![],
            ),
        ];

        assert_eq!(poly, voronoi);
    }
}
<commit_msg>use equals, not equals_exact<commit_after>use error::Error;
use ffi::GGeom;
use from_geo::TryInto;
use geo_types::{Geometry, GeometryCollection, Point, Polygon};

//TODO, change  &[] to IntoIterator
pub fn compute_voronoi(points: &[Point<f64>], tolerance: f64) -> Result<Vec<Polygon<f64>>, Error> {
    let geom_points: GGeom = points.try_into()?;

    geom_points
        .voronoi(None, tolerance, false)?
        .try_into()
        .and_then(|g: Geometry<f64>| match g {
            Geometry::GeometryCollection(gc) => Ok(gc),
            _ => Err(Error::ConversionError("invalid geometry type".into())),
        }).and_then(|gc: GeometryCollection<f64>| {
            gc.0.into_iter()
                .map(|g| {
                    g.as_polygon()
                        .ok_or(Error::ConversionError("invalid inner geometry type".into()))
                }).collect()
        })
}

#[cfg(test)]
mod test {
    use ffi::GGeom;
    use geo_types::{LineString, Point, Polygon, Coordinate};
    /// create a voronoi diagram. Same unit test as https://github.com/libgeos/geos/blob/master/tests/unit/triangulate/VoronoiTest.cpp#L118
    #[test]
    fn simple_voronoi() {
        let points = "MULTIPOINT ((150 200), (180 270), (275 163))";
        let input = GGeom::new(points).unwrap();

        let mut voronoi = input.voronoi(None, 0., false).unwrap();

        let expected_output = "GEOMETRYCOLLECTION (
            POLYGON ((25 38, 25 295, 221.20588235294116 210.91176470588235, 170.024 38, 25 38)),
            POLYGON ((400 369.6542056074766, 400 38, 170.024 38, 221.20588235294116 210.91176470588235, 400 369.6542056074766)),
            POLYGON ((25 295, 25 395, 400 395, 400 369.6542056074766, 221.20588235294116 210.91176470588235, 25 295)))";

        let mut expected_output = GGeom::new(expected_output).unwrap();

        expected_output.normalize().unwrap();
        voronoi.normalize().unwrap();

        let same = expected_output.equals(&voronoi).unwrap();
        assert!(same);
    }

    /// test precision. Same unit test as https://github.com/libgeos/geos/blob/master/tests/unit/triangulate/VoronoiTest.cpp#L160
    #[test]
    fn wkt_voronoi_precision() {
        let points = "MULTIPOINT ((100 200), (105 202), (110 200), (140 230),
        (210 240), (220 190), (170 170), (170 260), (213 245), (220 190))";
        let input = GGeom::new(points).unwrap();

        let mut voronoi = input.voronoi(None, 6., false).unwrap();

        let expected_output = "GEOMETRYCOLLECTION (
        POLYGON ((-20 50, -20 380, -3.75 380, 105 235, 105 115, 77.14285714285714 50, -20 50)),
        POLYGON ((247 50, 77.14285714285714 50, 105 115, 145 195, 178.33333333333334 211.66666666666666, 183.51851851851853 208.7037037037037, 247 50)),
        POLYGON ((-3.75 380, 20.000000000000007 380, 176.66666666666666 223.33333333333334, 178.33333333333334 211.66666666666666, 145 195, 105 235, -3.75 380)),
        POLYGON ((105 115, 105 235, 145 195, 105 115)),
        POLYGON ((20.000000000000007 380, 255 380, 176.66666666666666 223.33333333333334, 20.000000000000007 380)),
        POLYGON ((255 380, 340 380, 340 240, 183.51851851851853 208.7037037037037, 178.33333333333334 211.66666666666666, 176.66666666666666 223.33333333333334, 255 380)),
        POLYGON ((340 240, 340 50, 247 50, 183.51851851851853 208.7037037037037, 340 240)))";

        let mut expected_output = GGeom::new(expected_output).unwrap();

        expected_output.normalize().unwrap();
        voronoi.normalize().unwrap();

        let same = expected_output.equals(&voronoi).unwrap();
        assert!(same);
    }

    fn coords(tuples: Vec<(f64, f64)>) -> Vec<Coordinate<f64>> {
        tuples.into_iter().map(Coordinate::from).collect()
    }

    // test the rust-geo voronoi wrapper
    #[test]
    fn geo_voronoi() {
        let points = vec![
            Point::new(0., 0.),
            Point::new(0., 1.),
            Point::new(1., 1.),
            Point::new(1., 0.),
        ];

        let voronoi = ::compute_voronoi(&points, 0.).unwrap();

        let poly = vec![
            Polygon::new(
                LineString(coords(vec![
                    (0.5, 2.0),
                    (2.0, 2.0),
                    (2.0, 0.5),
                    (0.5, 0.5),
                    (0.5, 2.0),
                ])),
                vec![],
            ),
            Polygon::new(
                LineString(coords(vec![
                    (-1.0, 0.5),
                    (-1.0, 2.0),
                    (0.5, 2.0),
                    (0.5, 0.5),
                    (-1.0, 0.5),
                ])),
                vec![],
            ),
            Polygon::new(
                LineString(coords(vec![
                    (0.5, -1.0),
                    (-1.0, -1.0),
                    (-1.0, 0.5),
                    (0.5, 0.5),
                    (0.5, -1.0),
                ])),
                vec![],
            ),
            Polygon::new(
                LineString(coords(vec![
                    (2.0, 0.5),
                    (2.0, -1.0),
                    (0.5, -1.0),
                    (0.5, 0.5),
                    (2.0, 0.5),
                ])),
                vec![],
            ),
        ];

        assert_eq!(poly, voronoi);
    }
}
<|endoftext|>


<commit_before>//! This is code from [Tungstenite project](https://github.com/snapview/tungstenite-rs)
#![cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))]
use std::cmp::min;
use std::mem::uninitialized;
use std::ptr::copy_nonoverlapping;

/// Faster version of `apply_mask()` which operates on 8-byte blocks.
///
/// unsafe because uses pointer math and bit operations for performance
#[inline]
#[cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
pub(crate) fn apply_mask(buf: &mut [u8], mask_u32: u32) {
    unsafe {
        let mut ptr = buf.as_mut_ptr();
        let mut len = buf.len();

        // Possible first unaligned block.
        let head = min(len, (8 - (ptr as usize & 0x7)) & 0x3);
        let mask_u32 = if head > 0 {
            let n = if head > 4 { head - 4 } else { head };

            let mask_u32 = if n > 0 {
                xor_mem(ptr, mask_u32, n);
                ptr = ptr.offset(head as isize);
                len -= n;
                if cfg!(target_endian = "big") {
                    mask_u32.rotate_left(8 * n as u32)
                } else {
                    mask_u32.rotate_right(8 * n as u32)
                }
            } else {
                mask_u32
            };

            if head > 4 {
                *(ptr as *mut u32) ^= mask_u32;
                ptr = ptr.offset(4);
                len -= 4;
            }
            mask_u32
        } else {
            mask_u32
        };

        if len > 0 {
            debug_assert_eq!(ptr as usize % 4, 0);
        }

        // Properly aligned middle of the data.
        if len >= 8 {
            let mut mask_u64 = mask_u32 as u64;
            mask_u64 = mask_u64 << 32 | mask_u32 as u64;

            while len >= 8 {
                *(ptr as *mut u64) ^= mask_u64;
                ptr = ptr.offset(8);
                len -= 8;
            }
        }

        while len >= 4 {
            *(ptr as *mut u32) ^= mask_u32;
            ptr = ptr.offset(4);
            len -= 4;
        }

        // Possible last block.
        if len > 0 {
            xor_mem(ptr, mask_u32, len);
        }
    }
}

#[inline]
// TODO: copy_nonoverlapping here compiles to call memcpy. While it is not so
// inefficient, it could be done better. The compiler does not see that len is
// limited to 3.
unsafe fn xor_mem(ptr: *mut u8, mask: u32, len: usize) {
    let mut b: u32 = uninitialized();
    #[allow(trivial_casts)]
    copy_nonoverlapping(ptr, &mut b as *mut _ as *mut u8, len);
    b ^= mask;
    #[allow(trivial_casts)]
    copy_nonoverlapping(&b as *const _ as *const u8, ptr, len);
}

#[cfg(test)]
mod tests {
    use super::apply_mask;
    use byteorder::{ByteOrder, LittleEndian};

    /// A safe unoptimized mask application.
    fn apply_mask_fallback(buf: &mut [u8], mask: &[u8; 4]) {
        for (i, byte) in buf.iter_mut().enumerate() {
            *byte ^= mask[i & 3];
        }
    }

    #[test]
    fn test_apply_mask() {
        let mask = [0x6d, 0xb6, 0xb2, 0x80];
        let mask_u32: u32 = LittleEndian::read_u32(&mask);

        let unmasked = vec![
            0xf3, 0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x82, 0xff, 0xfe, 0x00, 0x17,
            0x74, 0xf9, 0x12, 0x03,
        ];

        // Check masking with proper alignment.
        {
            let mut masked = unmasked.clone();
            apply_mask_fallback(&mut masked, &mask);

            let mut masked_fast = unmasked.clone();
            apply_mask(&mut masked_fast, mask_u32);

            assert_eq!(masked, masked_fast);
        }

        // Check masking without alignment.
        {
            let mut masked = unmasked.clone();
            apply_mask_fallback(&mut masked[1..], &mask);

            let mut masked_fast = unmasked.clone();
            apply_mask(&mut masked_fast[1..], mask_u32);

            assert_eq!(masked, masked_fast);
        }
    }
}
<commit_msg>Refactor `apply_mask` implementation, removing dead code paths and reducing scope of unsafety<commit_after>//! This is code from [Tungstenite project](https://github.com/snapview/tungstenite-rs)
#![cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))]
use std::slice;
use std::ptr::copy_nonoverlapping;

// Holds a slice guaranteed to be shorter than 8 bytes
struct ShortSlice<'a>(&'a mut [u8]);

impl<'a> ShortSlice<'a> {
    unsafe fn new(slice: &'a mut [u8]) -> Self {
        // Sanity check for debug builds
        debug_assert!(slice.len() < 8);
        ShortSlice(slice)
    }
    fn len(&self) -> usize {
        self.0.len()
    }
}

/// Faster version of `apply_mask()` which operates on 8-byte blocks.
#[inline]
#[cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
pub(crate) fn apply_mask(buf: &mut [u8], mask_u32: u32) {
    // Extend the mask to 64 bits
    let mut mask_u64 = ((mask_u32 as u64) << 32) | (mask_u32 as u64);
    // Split the buffer into three segments
    let (head, mid, tail) = align_buf(buf);

    // Initial unaligned segment
    let head_len = head.len();
    if head_len > 0 {
        xor_short(head, mask_u64);
        if cfg!(target_endian = "big") {
            mask_u64 = mask_u64.rotate_left(8 * head_len as u32);
        } else {
            mask_u64 = mask_u64.rotate_right(8 * head_len as u32);
        }
    }
    // Aligned segment
    for v in mid {
        *v ^= mask_u64;
    }
    // Final unaligned segment
    if tail.len() > 0 {
        xor_short(tail, mask_u64);
    }
}

#[inline]
// TODO: copy_nonoverlapping here compiles to call memcpy. While it is not so
// inefficient, it could be done better. The compiler does not understand that
// a `ShortSlice` must be smaller than a u64.
fn xor_short(buf: ShortSlice, mask: u64) {
    // Unsafe: we know that a `ShortSlice` fits in a u64
    unsafe {
        let (ptr, len) = (buf.0.as_mut_ptr(), buf.0.len());
        let mut b: u64 = 0;
        #[allow(trivial_casts)]
        copy_nonoverlapping(ptr, &mut b as *mut _ as *mut u8, len);
        b ^= mask;
        #[allow(trivial_casts)]
        copy_nonoverlapping(&b as *const _ as *const u8, ptr, len);
    }
}

#[inline]
// Unsafe: caller must ensure the buffer has the correct size and alignment
unsafe fn cast_slice(buf: &mut [u8]) -> &mut [u64] {
    // Assert correct size and alignment in debug builds
    debug_assert!(buf.len() & 0x7 == 0);
    debug_assert!(buf.as_ptr() as usize & 0x7 == 0);

    slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u64, buf.len() >> 3)
}

#[inline]
// Splits a slice into three parts: an unaligned short head and tail, plus an aligned
// u64 mid section.
fn align_buf(buf: &mut [u8]) -> (ShortSlice, &mut [u64], ShortSlice) {
    let start_ptr = buf.as_ptr() as usize;
    let end_ptr = start_ptr + buf.len();

    // Round *up* to next aligned boundary for start
    let start_aligned = (start_ptr+7) & !0x7;
    // Round *down* to last aligned boundary for end
    let end_aligned = end_ptr & !0x7;

    if end_aligned >= start_aligned {
        // We have our three segments (head, mid, tail)
        let (tmp, tail) = buf.split_at_mut(end_aligned - start_ptr);
        let (head, mid) = tmp.split_at_mut(start_aligned - start_ptr);

        // Unsafe: we know the middle section is correctly aligned, and the outer
        // sections are smaller than 8 bytes
        unsafe { (ShortSlice::new(head), cast_slice(mid), ShortSlice(tail)) }
    } else {
        // We didn't cross even one aligned boundary!

        // Unsafe: The outer sections are smaller than 8 bytes
        unsafe { (ShortSlice::new(buf), &mut [], ShortSlice::new(&mut [])) }
    }
}

#[cfg(test)]
mod tests {
    use super::apply_mask;
    use byteorder::{ByteOrder, LittleEndian};

    /// A safe unoptimized mask application.
    fn apply_mask_fallback(buf: &mut [u8], mask: &[u8; 4]) {
        for (i, byte) in buf.iter_mut().enumerate() {
            *byte ^= mask[i & 3];
        }
    }

    #[test]
    fn test_apply_mask() {
        let mask = [0x6d, 0xb6, 0xb2, 0x80];
        let mask_u32: u32 = LittleEndian::read_u32(&mask);

        let unmasked = vec![
            0xf3, 0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x82, 0xff, 0xfe, 0x00, 0x17,
            0x74, 0xf9, 0x12, 0x03,
        ];

        // Check masking with proper alignment.
        {
            let mut masked = unmasked.clone();
            apply_mask_fallback(&mut masked, &mask);

            let mut masked_fast = unmasked.clone();
            apply_mask(&mut masked_fast, mask_u32);

            assert_eq!(masked, masked_fast);
        }

        // Check masking without alignment.
        {
            let mut masked = unmasked.clone();
            apply_mask_fallback(&mut masked[1..], &mask);

            let mut masked_fast = unmasked.clone();
            apply_mask(&mut masked_fast[1..], mask_u32);

            assert_eq!(masked, masked_fast);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

//! A generic Serde-based `Transport` that can serialize anything supported by `tokio-serde` via any medium that implements `AsyncRead` and `AsyncWrite`.

#![deny(missing_docs)]

use futures::{prelude::*, task::*};
use pin_project::pin_project;
use serde::{Deserialize, Serialize};
use std::{error::Error, io, pin::Pin};
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_serde::{Framed as SerdeFramed, *};
use tokio_util::codec::{length_delimited::LengthDelimitedCodec, Framed};

/// A transport that serializes to, and deserializes from, a byte stream.
#[pin_project]
pub struct Transport<S, Item, SinkItem, Codec> {
    #[pin]
    inner: SerdeFramed<Framed<S, LengthDelimitedCodec>, Item, SinkItem, Codec>,
}

impl<S, Item, SinkItem, Codec> Transport<S, Item, SinkItem, Codec> {
    /// Returns the inner transport over which messages are sent and received.
    pub fn get_ref(&self) -> &S {
        self.inner.get_ref().get_ref()
    }
}

impl<S, Item, SinkItem, Codec, CodecError> Stream for Transport<S, Item, SinkItem, Codec>
where
    S: AsyncWrite + AsyncRead,
    Item: for<'a> Deserialize<'a>,
    Codec: Deserializer<Item>,
    CodecError: Into<Box<dyn std::error::Error + Send + Sync>>,
    SerdeFramed<Framed<S, LengthDelimitedCodec>, Item, SinkItem, Codec>:
        Stream<Item = Result<Item, CodecError>>,
{
    type Item = io::Result<Item>;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<io::Result<Item>>> {
        match self.project().inner.poll_next(cx) {
            Poll::Pending => Poll::Pending,
            Poll::Ready(None) => Poll::Ready(None),
            Poll::Ready(Some(Ok::<_, CodecError>(next))) => Poll::Ready(Some(Ok(next))),
            Poll::Ready(Some(Err::<_, CodecError>(e))) => {
                Poll::Ready(Some(Err(io::Error::new(io::ErrorKind::Other, e))))
            }
        }
    }
}

impl<S, Item, SinkItem, Codec, CodecError> Sink<SinkItem> for Transport<S, Item, SinkItem, Codec>
where
    S: AsyncWrite,
    SinkItem: Serialize,
    Codec: Serializer<SinkItem>,
    CodecError: Into<Box<dyn Error + Send + Sync>>,
    SerdeFramed<Framed<S, LengthDelimitedCodec>, Item, SinkItem, Codec>:
        Sink<SinkItem, Error = CodecError>,
{
    type Error = io::Error;

    fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        convert(self.project().inner.poll_ready(cx))
    }

    fn start_send(self: Pin<&mut Self>, item: SinkItem) -> io::Result<()> {
        self.project()
            .inner
            .start_send(item)
            .map_err(|e| io::Error::new(io::ErrorKind::Other, e))
    }

    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        convert(self.project().inner.poll_flush(cx))
    }

    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        convert(self.project().inner.poll_close(cx))
    }
}

fn convert<E: Into<Box<dyn Error + Send + Sync>>>(
    poll: Poll<Result<(), E>>,
) -> Poll<io::Result<()>> {
    poll.map(|ready| ready.map_err(|e| io::Error::new(io::ErrorKind::Other, e)))
}

/// Constructs a new transport from a framed transport and a serialization codec.
pub fn new<S, Item, SinkItem, Codec>(
    framed_io: Framed<S, LengthDelimitedCodec>,
    codec: Codec,
) -> Transport<S, Item, SinkItem, Codec>
where
    S: AsyncWrite + AsyncRead,
    Item: for<'de> Deserialize<'de>,
    SinkItem: Serialize,
    Codec: Serializer<SinkItem> + Deserializer<Item>,
{
    Transport {
        inner: SerdeFramed::new(framed_io, codec),
    }
}

impl<S, Item, SinkItem, Codec> From<(S, Codec)> for Transport<S, Item, SinkItem, Codec>
where
    S: AsyncWrite + AsyncRead,
    Item: for<'de> Deserialize<'de>,
    SinkItem: Serialize,
    Codec: Serializer<SinkItem> + Deserializer<Item>,
{
    fn from((io, codec): (S, Codec)) -> Self {
        new(Framed::new(io, LengthDelimitedCodec::new()), codec)
    }
}

#[cfg(feature = "tcp")]
#[cfg_attr(docsrs, doc(cfg(feature = "tcp")))]
/// TCP support for generic transport using Tokio.
pub mod tcp {
    use {
        super::*,
        futures::ready,
        std::{marker::PhantomData, net::SocketAddr},
        tokio::net::{TcpListener, TcpStream, ToSocketAddrs},
        tokio_util::codec::length_delimited,
    };

    mod private {
        use super::*;

        pub trait Sealed {}

        impl<Item, SinkItem, Codec> Sealed for Transport<TcpStream, Item, SinkItem, Codec> {}
    }

    impl<Item, SinkItem, Codec> Transport<TcpStream, Item, SinkItem, Codec> {
        /// Returns the peer address of the underlying TcpStream.
        pub fn peer_addr(&self) -> io::Result<SocketAddr> {
            self.inner.get_ref().get_ref().peer_addr()
        }
        /// Returns the local address of the underlying TcpStream.
        pub fn local_addr(&self) -> io::Result<SocketAddr> {
            self.inner.get_ref().get_ref().local_addr()
        }
    }

    /// A connection Future that also exposes the length-delimited framing config.
    #[pin_project]
    pub struct Connect<T, Item, SinkItem, CodecFn> {
        #[pin]
        inner: T,
        codec_fn: CodecFn,
        config: length_delimited::Builder,
        ghost: PhantomData<(fn(SinkItem), fn() -> Item)>,
    }

    impl<T, Item, SinkItem, Codec, CodecFn> Future for Connect<T, Item, SinkItem, CodecFn>
    where
        T: Future<Output = io::Result<TcpStream>>,
        Item: for<'de> Deserialize<'de>,
        SinkItem: Serialize,
        Codec: Serializer<SinkItem> + Deserializer<Item>,
        CodecFn: Fn() -> Codec,
    {
        type Output = io::Result<Transport<TcpStream, Item, SinkItem, Codec>>;

        fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
            let io = ready!(self.as_mut().project().inner.poll(cx))?;
            Poll::Ready(Ok(new(self.config.new_framed(io), (self.codec_fn)())))
        }
    }

    impl<T, Item, SinkItem, CodecFn> Connect<T, Item, SinkItem, CodecFn> {
        /// Returns an immutable reference to the length-delimited codec's config.
        pub fn config(&self) -> &length_delimited::Builder {
            &self.config
        }

        /// Returns a mutable reference to the length-delimited codec's config.
        pub fn config_mut(&mut self) -> &mut length_delimited::Builder {
            &mut self.config
        }
    }

    /// Connects to `addr`, wrapping the connection in a TCP transport.
    pub fn connect<A, Item, SinkItem, Codec, CodecFn>(
        addr: A,
        codec_fn: CodecFn,
    ) -> Connect<impl Future<Output = io::Result<TcpStream>>, Item, SinkItem, CodecFn>
    where
        A: ToSocketAddrs,
        Item: for<'de> Deserialize<'de>,
        SinkItem: Serialize,
        Codec: Serializer<SinkItem> + Deserializer<Item>,
        CodecFn: Fn() -> Codec,
    {
        Connect {
            inner: TcpStream::connect(addr),
            codec_fn,
            config: LengthDelimitedCodec::builder(),
            ghost: PhantomData,
        }
    }

    /// Listens on `addr`, wrapping accepted connections in TCP transports.
    pub async fn listen<A, Item, SinkItem, Codec, CodecFn>(
        addr: A,
        codec_fn: CodecFn,
    ) -> io::Result<Incoming<Item, SinkItem, Codec, CodecFn>>
    where
        A: ToSocketAddrs,
        Item: for<'de> Deserialize<'de>,
        Codec: Serializer<SinkItem> + Deserializer<Item>,
        CodecFn: Fn() -> Codec,
    {
        let listener = TcpListener::bind(addr).await?;
        let local_addr = listener.local_addr()?;
        Ok(Incoming {
            listener,
            codec_fn,
            local_addr,
            config: LengthDelimitedCodec::builder(),
            ghost: PhantomData,
        })
    }

    /// A [`TcpListener`] that wraps connections in [transports](Transport).
    #[pin_project]
    #[derive(Debug)]
    pub struct Incoming<Item, SinkItem, Codec, CodecFn> {
        listener: TcpListener,
        local_addr: SocketAddr,
        codec_fn: CodecFn,
        config: length_delimited::Builder,
        ghost: PhantomData<(fn() -> Item, fn(SinkItem), Codec)>,
    }

    impl<Item, SinkItem, Codec, CodecFn> Incoming<Item, SinkItem, Codec, CodecFn> {
        /// Returns the address being listened on.
        pub fn local_addr(&self) -> SocketAddr {
            self.local_addr
        }

        /// Returns an immutable reference to the length-delimited codec's config.
        pub fn config(&self) -> &length_delimited::Builder {
            &self.config
        }

        /// Returns a mutable reference to the length-delimited codec's config.
        pub fn config_mut(&mut self) -> &mut length_delimited::Builder {
            &mut self.config
        }
    }

    impl<Item, SinkItem, Codec, CodecFn> Stream for Incoming<Item, SinkItem, Codec, CodecFn>
    where
        Item: for<'de> Deserialize<'de>,
        SinkItem: Serialize,
        Codec: Serializer<SinkItem> + Deserializer<Item>,
        CodecFn: Fn() -> Codec,
    {
        type Item = io::Result<Transport<TcpStream, Item, SinkItem, Codec>>;

        fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
            let conn: TcpStream =
                ready!(Pin::new(&mut self.as_mut().project().listener).poll_accept(cx)?).0;
            Poll::Ready(Some(Ok(new(
                self.config.new_framed(conn),
                (self.codec_fn)(),
            ))))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::Transport;
    use assert_matches::assert_matches;
    use futures::{task::*, Sink, Stream};
    use pin_utils::pin_mut;
    use std::{
        io::{self, Cursor},
        pin::Pin,
    };
    use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
    use tokio_serde::formats::SymmetricalJson;

    fn ctx() -> Context<'static> {
        Context::from_waker(&noop_waker_ref())
    }

    #[test]
    fn test_stream() {
        struct TestIo(Cursor<&'static [u8]>);

        impl AsyncRead for TestIo {
            fn poll_read(
                mut self: Pin<&mut Self>,
                cx: &mut Context<'_>,
                buf: &mut ReadBuf<'_>,
            ) -> Poll<io::Result<()>> {
                AsyncRead::poll_read(Pin::new(self.0.get_mut()), cx, buf)
            }
        }

        impl AsyncWrite for TestIo {
            fn poll_write(
                self: Pin<&mut Self>,
                _cx: &mut Context<'_>,
                _buf: &[u8],
            ) -> Poll<io::Result<usize>> {
                unreachable!()
            }

            fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
                unreachable!()
            }

            fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
                unreachable!()
            }
        }

        let data = b"\x00\x00\x00\x18\"Test one, check check.\"";
        let transport = Transport::from((
            TestIo(Cursor::new(data)),
            SymmetricalJson::<String>::default(),
        ));
        pin_mut!(transport);

        assert_matches!(
            transport.poll_next(&mut ctx()),
            Poll::Ready(Some(Ok(ref s))) if s == "Test one, check check.");
    }

    #[test]
    fn test_sink() {
        struct TestIo<'a>(&'a mut Vec<u8>);

        impl<'a> AsyncRead for TestIo<'a> {
            fn poll_read(
                self: Pin<&mut Self>,
                _cx: &mut Context<'_>,
                _buf: &mut ReadBuf<'_>,
            ) -> Poll<io::Result<()>> {
                unreachable!()
            }
        }

        impl<'a> AsyncWrite for TestIo<'a> {
            fn poll_write(
                mut self: Pin<&mut Self>,
                cx: &mut Context<'_>,
                buf: &[u8],
            ) -> Poll<io::Result<usize>> {
                AsyncWrite::poll_write(Pin::new(&mut *self.0), cx, buf)
            }

            fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
                AsyncWrite::poll_flush(Pin::new(&mut *self.0), cx)
            }

            fn poll_shutdown(
                mut self: Pin<&mut Self>,
                cx: &mut Context<'_>,
            ) -> Poll<io::Result<()>> {
                AsyncWrite::poll_shutdown(Pin::new(&mut *self.0), cx)
            }
        }

        let mut writer = vec![];
        let transport =
            Transport::from((TestIo(&mut writer), SymmetricalJson::<String>::default()));
        pin_mut!(transport);

        assert_matches!(
            transport.as_mut().poll_ready(&mut ctx()),
            Poll::Ready(Ok(()))
        );
        assert_matches!(
            transport
                .as_mut()
                .start_send("Test one, check check.".into()),
            Ok(())
        );
        assert_matches!(transport.poll_flush(&mut ctx()), Poll::Ready(Ok(())));
        assert_eq!(writer, b"\x00\x00\x00\x18\"Test one, check check.\"");
    }
}
<commit_msg>Improve test coverage of serde_transport<commit_after>// Copyright 2019 Google LLC
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

//! A generic Serde-based `Transport` that can serialize anything supported by `tokio-serde` via any medium that implements `AsyncRead` and `AsyncWrite`.

#![deny(missing_docs)]

use futures::{prelude::*, task::*};
use pin_project::pin_project;
use serde::{Deserialize, Serialize};
use std::{error::Error, io, pin::Pin};
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_serde::{Framed as SerdeFramed, *};
use tokio_util::codec::{length_delimited::LengthDelimitedCodec, Framed};

/// A transport that serializes to, and deserializes from, a byte stream.
#[pin_project]
pub struct Transport<S, Item, SinkItem, Codec> {
    #[pin]
    inner: SerdeFramed<Framed<S, LengthDelimitedCodec>, Item, SinkItem, Codec>,
}

impl<S, Item, SinkItem, Codec> Transport<S, Item, SinkItem, Codec> {
    /// Returns the inner transport over which messages are sent and received.
    pub fn get_ref(&self) -> &S {
        self.inner.get_ref().get_ref()
    }
}

impl<S, Item, SinkItem, Codec, CodecError> Stream for Transport<S, Item, SinkItem, Codec>
where
    S: AsyncWrite + AsyncRead,
    Item: for<'a> Deserialize<'a>,
    Codec: Deserializer<Item>,
    CodecError: Into<Box<dyn std::error::Error + Send + Sync>>,
    SerdeFramed<Framed<S, LengthDelimitedCodec>, Item, SinkItem, Codec>:
        Stream<Item = Result<Item, CodecError>>,
{
    type Item = io::Result<Item>;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<io::Result<Item>>> {
        match self.project().inner.poll_next(cx) {
            Poll::Pending => Poll::Pending,
            Poll::Ready(None) => Poll::Ready(None),
            Poll::Ready(Some(Ok::<_, CodecError>(next))) => Poll::Ready(Some(Ok(next))),
            Poll::Ready(Some(Err::<_, CodecError>(e))) => {
                Poll::Ready(Some(Err(io::Error::new(io::ErrorKind::Other, e))))
            }
        }
    }
}

impl<S, Item, SinkItem, Codec, CodecError> Sink<SinkItem> for Transport<S, Item, SinkItem, Codec>
where
    S: AsyncWrite,
    SinkItem: Serialize,
    Codec: Serializer<SinkItem>,
    CodecError: Into<Box<dyn Error + Send + Sync>>,
    SerdeFramed<Framed<S, LengthDelimitedCodec>, Item, SinkItem, Codec>:
        Sink<SinkItem, Error = CodecError>,
{
    type Error = io::Error;

    fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        convert(self.project().inner.poll_ready(cx))
    }

    fn start_send(self: Pin<&mut Self>, item: SinkItem) -> io::Result<()> {
        self.project()
            .inner
            .start_send(item)
            .map_err(|e| io::Error::new(io::ErrorKind::Other, e))
    }

    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        convert(self.project().inner.poll_flush(cx))
    }

    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        convert(self.project().inner.poll_close(cx))
    }
}

fn convert<E: Into<Box<dyn Error + Send + Sync>>>(
    poll: Poll<Result<(), E>>,
) -> Poll<io::Result<()>> {
    poll.map(|ready| ready.map_err(|e| io::Error::new(io::ErrorKind::Other, e)))
}

/// Constructs a new transport from a framed transport and a serialization codec.
pub fn new<S, Item, SinkItem, Codec>(
    framed_io: Framed<S, LengthDelimitedCodec>,
    codec: Codec,
) -> Transport<S, Item, SinkItem, Codec>
where
    S: AsyncWrite + AsyncRead,
    Item: for<'de> Deserialize<'de>,
    SinkItem: Serialize,
    Codec: Serializer<SinkItem> + Deserializer<Item>,
{
    Transport {
        inner: SerdeFramed::new(framed_io, codec),
    }
}

impl<S, Item, SinkItem, Codec> From<(S, Codec)> for Transport<S, Item, SinkItem, Codec>
where
    S: AsyncWrite + AsyncRead,
    Item: for<'de> Deserialize<'de>,
    SinkItem: Serialize,
    Codec: Serializer<SinkItem> + Deserializer<Item>,
{
    fn from((io, codec): (S, Codec)) -> Self {
        new(Framed::new(io, LengthDelimitedCodec::new()), codec)
    }
}

#[cfg(feature = "tcp")]
#[cfg_attr(docsrs, doc(cfg(feature = "tcp")))]
/// TCP support for generic transport using Tokio.
pub mod tcp {
    use {
        super::*,
        futures::ready,
        std::{marker::PhantomData, net::SocketAddr},
        tokio::net::{TcpListener, TcpStream, ToSocketAddrs},
        tokio_util::codec::length_delimited,
    };

    mod private {
        use super::*;

        pub trait Sealed {}

        impl<Item, SinkItem, Codec> Sealed for Transport<TcpStream, Item, SinkItem, Codec> {}
    }

    impl<Item, SinkItem, Codec> Transport<TcpStream, Item, SinkItem, Codec> {
        /// Returns the peer address of the underlying TcpStream.
        pub fn peer_addr(&self) -> io::Result<SocketAddr> {
            self.inner.get_ref().get_ref().peer_addr()
        }
        /// Returns the local address of the underlying TcpStream.
        pub fn local_addr(&self) -> io::Result<SocketAddr> {
            self.inner.get_ref().get_ref().local_addr()
        }
    }

    /// A connection Future that also exposes the length-delimited framing config.
    #[pin_project]
    pub struct Connect<T, Item, SinkItem, CodecFn> {
        #[pin]
        inner: T,
        codec_fn: CodecFn,
        config: length_delimited::Builder,
        ghost: PhantomData<(fn(SinkItem), fn() -> Item)>,
    }

    impl<T, Item, SinkItem, Codec, CodecFn> Future for Connect<T, Item, SinkItem, CodecFn>
    where
        T: Future<Output = io::Result<TcpStream>>,
        Item: for<'de> Deserialize<'de>,
        SinkItem: Serialize,
        Codec: Serializer<SinkItem> + Deserializer<Item>,
        CodecFn: Fn() -> Codec,
    {
        type Output = io::Result<Transport<TcpStream, Item, SinkItem, Codec>>;

        fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
            let io = ready!(self.as_mut().project().inner.poll(cx))?;
            Poll::Ready(Ok(new(self.config.new_framed(io), (self.codec_fn)())))
        }
    }

    impl<T, Item, SinkItem, CodecFn> Connect<T, Item, SinkItem, CodecFn> {
        /// Returns an immutable reference to the length-delimited codec's config.
        pub fn config(&self) -> &length_delimited::Builder {
            &self.config
        }

        /// Returns a mutable reference to the length-delimited codec's config.
        pub fn config_mut(&mut self) -> &mut length_delimited::Builder {
            &mut self.config
        }
    }

    /// Connects to `addr`, wrapping the connection in a TCP transport.
    pub fn connect<A, Item, SinkItem, Codec, CodecFn>(
        addr: A,
        codec_fn: CodecFn,
    ) -> Connect<impl Future<Output = io::Result<TcpStream>>, Item, SinkItem, CodecFn>
    where
        A: ToSocketAddrs,
        Item: for<'de> Deserialize<'de>,
        SinkItem: Serialize,
        Codec: Serializer<SinkItem> + Deserializer<Item>,
        CodecFn: Fn() -> Codec,
    {
        Connect {
            inner: TcpStream::connect(addr),
            codec_fn,
            config: LengthDelimitedCodec::builder(),
            ghost: PhantomData,
        }
    }

    /// Listens on `addr`, wrapping accepted connections in TCP transports.
    pub async fn listen<A, Item, SinkItem, Codec, CodecFn>(
        addr: A,
        codec_fn: CodecFn,
    ) -> io::Result<Incoming<Item, SinkItem, Codec, CodecFn>>
    where
        A: ToSocketAddrs,
        Item: for<'de> Deserialize<'de>,
        Codec: Serializer<SinkItem> + Deserializer<Item>,
        CodecFn: Fn() -> Codec,
    {
        let listener = TcpListener::bind(addr).await?;
        let local_addr = listener.local_addr()?;
        Ok(Incoming {
            listener,
            codec_fn,
            local_addr,
            config: LengthDelimitedCodec::builder(),
            ghost: PhantomData,
        })
    }

    /// A [`TcpListener`] that wraps connections in [transports](Transport).
    #[pin_project]
    #[derive(Debug)]
    pub struct Incoming<Item, SinkItem, Codec, CodecFn> {
        listener: TcpListener,
        local_addr: SocketAddr,
        codec_fn: CodecFn,
        config: length_delimited::Builder,
        ghost: PhantomData<(fn() -> Item, fn(SinkItem), Codec)>,
    }

    impl<Item, SinkItem, Codec, CodecFn> Incoming<Item, SinkItem, Codec, CodecFn> {
        /// Returns the address being listened on.
        pub fn local_addr(&self) -> SocketAddr {
            self.local_addr
        }

        /// Returns an immutable reference to the length-delimited codec's config.
        pub fn config(&self) -> &length_delimited::Builder {
            &self.config
        }

        /// Returns a mutable reference to the length-delimited codec's config.
        pub fn config_mut(&mut self) -> &mut length_delimited::Builder {
            &mut self.config
        }
    }

    impl<Item, SinkItem, Codec, CodecFn> Stream for Incoming<Item, SinkItem, Codec, CodecFn>
    where
        Item: for<'de> Deserialize<'de>,
        SinkItem: Serialize,
        Codec: Serializer<SinkItem> + Deserializer<Item>,
        CodecFn: Fn() -> Codec,
    {
        type Item = io::Result<Transport<TcpStream, Item, SinkItem, Codec>>;

        fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
            let conn: TcpStream =
                ready!(Pin::new(&mut self.as_mut().project().listener).poll_accept(cx)?).0;
            Poll::Ready(Some(Ok(new(
                self.config.new_framed(conn),
                (self.codec_fn)(),
            ))))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::Transport;
    use assert_matches::assert_matches;
    use futures::{task::*, Sink, Stream};
    use pin_utils::pin_mut;
    use std::{
        io::{self, Cursor},
        pin::Pin,
    };
    use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
    use tokio_serde::formats::SymmetricalJson;

    fn ctx() -> Context<'static> {
        Context::from_waker(&noop_waker_ref())
    }

    struct TestIo(Cursor<Vec<u8>>);

    impl AsyncRead for TestIo {
        fn poll_read(
            mut self: Pin<&mut Self>,
            cx: &mut Context<'_>,
            buf: &mut ReadBuf<'_>,
        ) -> Poll<io::Result<()>> {
            AsyncRead::poll_read(Pin::new(&mut self.0), cx, buf)
        }
    }

    impl AsyncWrite for TestIo {
        fn poll_write(
            mut self: Pin<&mut Self>,
            cx: &mut Context<'_>,
            buf: &[u8],
        ) -> Poll<io::Result<usize>> {
            AsyncWrite::poll_write(Pin::new(&mut self.0), cx, buf)
        }

        fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
            AsyncWrite::poll_flush(Pin::new(&mut self.0), cx)
        }

        fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
            AsyncWrite::poll_shutdown(Pin::new(&mut self.0), cx)
        }
    }

    #[test]
    fn close() {
        let (tx, _rx) = crate::transport::channel::bounded::<(), ()>(0);
        pin_mut!(tx);
        assert_matches!(tx.as_mut().poll_close(&mut ctx()), Poll::Ready(Ok(())));
        assert_matches!(tx.as_mut().start_send(()), Err(_));
    }

    #[test]
    fn test_stream() {
        let data: &[u8] = b"\x00\x00\x00\x18\"Test one, check check.\"";
        let transport = Transport::from((
            TestIo(Cursor::new(Vec::from(data))),
            SymmetricalJson::<String>::default(),
        ));
        pin_mut!(transport);

        assert_matches!(
            transport.as_mut().poll_next(&mut ctx()),
            Poll::Ready(Some(Ok(ref s))) if s == "Test one, check check.");
        assert_matches!(transport.as_mut().poll_next(&mut ctx()), Poll::Ready(None));
    }

    #[test]
    fn test_sink() {
        let writer = Cursor::new(vec![]);
        let mut transport = Box::pin(Transport::from((
            TestIo(writer),
            SymmetricalJson::<String>::default(),
        )));

        assert_matches!(
            transport.as_mut().poll_ready(&mut ctx()),
            Poll::Ready(Ok(()))
        );
        assert_matches!(
            transport
                .as_mut()
                .start_send("Test one, check check.".into()),
            Ok(())
        );
        assert_matches!(
            transport.as_mut().poll_flush(&mut ctx()),
            Poll::Ready(Ok(()))
        );
        assert_eq!(
            transport.get_ref().0.get_ref(),
            b"\x00\x00\x00\x18\"Test one, check check.\""
        );
    }

    #[cfg(tcp)]
    #[tokio::test]
    async fn tcp() -> io::Result<()> {
        use super::tcp;

        let mut listener = tcp::listen("0.0.0.0:0", SymmetricalJson::<String>::default).await?;
        let addr = listener.local_addr();
        tokio::spawn(async move {
            let mut transport = listener.next().await.unwrap().unwrap();
            let message = transport.next().await.unwrap().unwrap();
            transport.send(message).await.unwrap();
        });
        let mut transport = tcp::connect(addr, SymmetricalJson::<String>::default).await?;
        transport.send(String::from("test")).await?;
        assert_matches!(transport.next().await, Some(Ok(s)) if s == "test");
        assert_matches!(transport.next().await, None);
        Ok(())
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::env;
use std::io::Error;
use std::ptr::null_mut;

use widestring::WideCString;
use winapi::winerror::*;
use winapi::{BOOL, LPCWSTR, LPDWORD, PSID, PSID_NAME_USE, SID_NAME_USE};

use super::sid::Sid;

extern "system" {
    fn LookupAccountNameW(
        lpSystemName: LPCWSTR,
        lpAccountName: LPCWSTR,
        Sid: PSID,
        cbSid: LPDWORD,
        ReferencedDomainName: LPCWSTR,
        cchReferencedDomainName: LPDWORD,
        peUse: PSID_NAME_USE,
    ) -> BOOL;
}

pub struct Account {
    pub name: String,
    pub system_name: Option<String>,
    pub domain: String,
    pub account_type: SID_NAME_USE,
    pub sid: Sid,
}

impl Account {
    pub fn from_name(name: &str) -> Option<Account> {
        lookup_account(name, None)
    }

    pub fn from_name_and_system(name: &str, system_name: &str) -> Option<Account> {
        lookup_account(name, Some(system_name.to_string()))
    }
}

fn lookup_account(name: &str, system_name: Option<String>) -> Option<Account> {
    // if this is a machine account, strip the terminating '$'
    // LookupAccountName will return the sid of the computer account
    // given the computer name. Windows forbids usernames to match the
    // computer name
    let stripped_name =
        if name.to_lowercase() == (env::var("COMPUTERNAME").unwrap().to_lowercase() + "$") {
            &name[..name.len() - 1]
        } else {
            name
        };
    let mut sid_size: u32 = 0;
    let mut domain_size: u32 = 0;
    let wide = WideCString::from_str(stripped_name).unwrap();
    unsafe {
        LookupAccountNameW(
            null_mut(),
            wide.as_ptr(),
            null_mut(),
            &mut sid_size as LPDWORD,
            null_mut(),
            &mut domain_size as LPDWORD,
            null_mut(),
        )
    };
    match Error::last_os_error().raw_os_error().unwrap() as u32 {
        ERROR_INSUFFICIENT_BUFFER => {}
        ERROR_NONE_MAPPED => return None,
        _ => panic!(
            "Error while looking up account for {}: {}",
            name,
            Error::last_os_error()
        ),
    }

    let mut sid: Vec<u8> = Vec::with_capacity(sid_size as usize);
    let mut domain: Vec<u16> = Vec::with_capacity(domain_size as usize);
    let mut sid_type = SID_NAME_USE(0);

    let ret = unsafe {
        LookupAccountNameW(
            null_mut(),
            wide.as_ptr(),
            sid.as_mut_ptr() as PSID,
            &mut sid_size as LPDWORD,
            domain.as_mut_ptr(),
            &mut domain_size as LPDWORD,
            &mut sid_type as PSID_NAME_USE,
        )
    };
    if ret == 0 {
        panic!(
            "Failed to retrieve SID for {}: {}",
            name,
            Error::last_os_error()
        );
    }
    unsafe {
        domain.set_len(domain_size as usize);
        sid.set_len(sid_size as usize);
    }
    let domain_str = WideCString::from_vec(domain).unwrap().to_string_lossy();
    Some(Account {
        name: name.to_string(),
        system_name: system_name,
        domain: domain_str,
        account_type: sid_type,
        sid: Sid { raw: sid },
    })
}

#[cfg(test)]
mod tests {
    use std::env;

    use winapi::winnt::{SidTypeUser, SidTypeWellKnownGroup};

    use super::*;

    #[test]
    fn real_account_returns_some() {
        assert_eq!(Account::from_name("Administrator").is_some(), true)
    }

    #[test]
    fn bogus_account_returns_none() {
        assert_eq!(Account::from_name("bogus").is_none(), true)
    }

    #[test]
    fn user_account_returns_user_type() {
        let acct_type = Account::from_name("Administrator").unwrap().account_type;
        assert_eq!(acct_type, SidTypeUser)
    }

    #[test]
    fn local_user_account_returns_local_machine_as_domain() {
        let acct_domain = Account::from_name("Administrator").unwrap().domain;
        assert_eq!(acct_domain, env::var("COMPUTERNAME").unwrap())
    }

    #[test]
    fn well_known_group_account_returns_correct_type() {
        let acct_type = Account::from_name("Everyone").unwrap().account_type;
        assert_eq!(acct_type, SidTypeWellKnownGroup)
    }

    #[test]
    fn well_known_group_account_has_well_known_sid() {
        let sid = Account::from_name("Everyone").unwrap().sid;
        assert_eq!(sid.to_string().unwrap(), "S-1-1-0")
    }

    #[test]
    fn mixing_case_returns_same_account() {
        let current_user = env::var("USERNAME").unwrap();
        let lower_sid = Account::from_name(current_user.to_lowercase().as_str())
            .unwrap()
            .sid;
        let upper_sid = Account::from_name(current_user.to_uppercase().as_str())
            .unwrap()
            .sid;
        assert_eq!(
            lower_sid.to_string().unwrap(),
            upper_sid.to_string().unwrap()
        )
    }

    #[test]
    fn machine_account_returns_some() {
        assert_eq!(
            Account::from_name((env::var("COMPUTERNAME").unwrap() + "$").as_str()).is_some(),
            true
        )
    }
}
<commit_msg>update all cargo deps<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::env;
use std::io::Error;
use std::ptr::null_mut;

use widestring::WideCString;
use winapi::winerror::*;
use winapi::{BOOL, LPCWSTR, LPDWORD, PSID, PSID_NAME_USE, SID_NAME_USE};

use super::sid::Sid;

extern "system" {
    fn LookupAccountNameW(
        lpSystemName: LPCWSTR,
        lpAccountName: LPCWSTR,
        Sid: PSID,
        cbSid: LPDWORD,
        ReferencedDomainName: LPCWSTR,
        cchReferencedDomainName: LPDWORD,
        peUse: PSID_NAME_USE,
    ) -> BOOL;
}

pub struct Account {
    pub name: String,
    pub system_name: Option<String>,
    pub domain: String,
    pub account_type: SID_NAME_USE,
    pub sid: Sid,
}

impl Account {
    pub fn from_name(name: &str) -> Option<Account> {
        lookup_account(name, None)
    }

    pub fn from_name_and_system(name: &str, system_name: &str) -> Option<Account> {
        lookup_account(name, Some(system_name.to_string()))
    }
}

fn lookup_account(name: &str, system_name: Option<String>) -> Option<Account> {
    // if this is a machine account, strip the terminating '$'
    // LookupAccountName will return the sid of the computer account
    // given the computer name. Windows forbids usernames to match the
    // computer name
    let stripped_name =
        if name.to_lowercase() == (env::var("COMPUTERNAME").unwrap().to_lowercase() + "$") {
            &name[..name.len() - 1]
        } else {
            name
        };
    let mut sid_size: u32 = 0;
    let mut domain_size: u32 = 0;
    let wide = WideCString::from_str(stripped_name).unwrap();
    unsafe {
        LookupAccountNameW(
            null_mut(),
            wide.as_ptr(),
            null_mut(),
            &mut sid_size as LPDWORD,
            null_mut(),
            &mut domain_size as LPDWORD,
            null_mut(),
        )
    };
    match Error::last_os_error().raw_os_error().unwrap() as u32 {
        ERROR_INSUFFICIENT_BUFFER => {}
        ERROR_NONE_MAPPED => return None,
        _ => panic!(
            "Error while looking up account for {}: {}",
            name,
            Error::last_os_error()
        ),
    }

    let mut sid: Vec<u8> = Vec::with_capacity(sid_size as usize);
    let mut domain: Vec<u16> = Vec::with_capacity(domain_size as usize);
    let mut sid_type = SID_NAME_USE(0);

    let ret = unsafe {
        LookupAccountNameW(
            null_mut(),
            wide.as_ptr(),
            sid.as_mut_ptr() as PSID,
            &mut sid_size as LPDWORD,
            domain.as_mut_ptr(),
            &mut domain_size as LPDWORD,
            &mut sid_type as PSID_NAME_USE,
        )
    };
    if ret == 0 {
        panic!(
            "Failed to retrieve SID for {}: {}",
            name,
            Error::last_os_error()
        );
    }
    unsafe {
        domain.set_len(domain_size as usize);
        sid.set_len(sid_size as usize);
    }
    let domain_str = WideCString::new(domain).unwrap().to_string_lossy();
    Some(Account {
        name: name.to_string(),
        system_name: system_name,
        domain: domain_str,
        account_type: sid_type,
        sid: Sid { raw: sid },
    })
}

#[cfg(test)]
mod tests {
    use std::env;

    use winapi::winnt::{SidTypeUser, SidTypeWellKnownGroup};

    use super::*;

    #[test]
    fn real_account_returns_some() {
        assert_eq!(Account::from_name("Administrator").is_some(), true)
    }

    #[test]
    fn bogus_account_returns_none() {
        assert_eq!(Account::from_name("bogus").is_none(), true)
    }

    #[test]
    fn user_account_returns_user_type() {
        let acct_type = Account::from_name("Administrator").unwrap().account_type;
        assert_eq!(acct_type, SidTypeUser)
    }

    #[test]
    fn local_user_account_returns_local_machine_as_domain() {
        let acct_domain = Account::from_name("Administrator").unwrap().domain;
        assert_eq!(acct_domain, env::var("COMPUTERNAME").unwrap())
    }

    #[test]
    fn well_known_group_account_returns_correct_type() {
        let acct_type = Account::from_name("Everyone").unwrap().account_type;
        assert_eq!(acct_type, SidTypeWellKnownGroup)
    }

    #[test]
    fn well_known_group_account_has_well_known_sid() {
        let sid = Account::from_name("Everyone").unwrap().sid;
        assert_eq!(sid.to_string().unwrap(), "S-1-1-0")
    }

    #[test]
    fn mixing_case_returns_same_account() {
        let current_user = env::var("USERNAME").unwrap();
        let lower_sid = Account::from_name(current_user.to_lowercase().as_str())
            .unwrap()
            .sid;
        let upper_sid = Account::from_name(current_user.to_uppercase().as_str())
            .unwrap()
            .sid;
        assert_eq!(
            lower_sid.to_string().unwrap(),
            upper_sid.to_string().unwrap()
        )
    }

    #[test]
    fn machine_account_returns_some() {
        assert_eq!(
            Account::from_name((env::var("COMPUTERNAME").unwrap() + "$").as_str()).is_some(),
            true
        )
    }
}
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use super::block::SBBlock;
use super::compileunit::SBCompileUnit;
use super::function::SBFunction;
use super::lineentry::SBLineEntry;
use super::lldb_addr_t;
use super::module::SBModule;
use super::section::SBSection;
use super::stream::SBStream;
use super::symbol::SBSymbol;
use super::symbolcontext::SBSymbolContext;
use super::target::SBTarget;
use std::fmt;
use sys;

/// A section + offset based address class.
///
/// The `SBAddress` class allows addresses to be relative to a section
/// that can move during runtime due to images (executables, shared
/// libraries, bundles, frameworks) being loaded at different
/// addresses than the addresses found in the object file that
/// represents them on disk. There are currently two types of addresses
/// for a section:
///
/// * file addresses
/// * load addresses
///
/// File addresses represents the virtual addresses that are in the 'on
/// disk' object files. These virtual addresses are converted to be
/// relative to unique sections scoped to the object file so that
/// when/if the addresses slide when the images are loaded/unloaded
/// in memory, we can easily track these changes without having to
/// update every object (compile unit ranges, line tables, function
/// address ranges, lexical block and inlined subroutine address
/// ranges, global and static variables) each time an image is loaded or
/// unloaded.
///
/// Load addresses represents the virtual addresses where each section
/// ends up getting loaded at runtime. Before executing a program, it
/// is common for all of the load addresses to be unresolved. When a
/// `DynamicLoader` plug-in receives notification that shared libraries
/// have been loaded/unloaded, the load addresses of the main executable
/// and any images (shared libraries) will be  resolved/unresolved. When
/// this happens, breakpoints that are in one of these sections can be
/// set/cleared.
pub struct SBAddress {
    /// The underlying raw `SBAddressRef`.
    pub raw: sys::SBAddressRef,
}

impl SBAddress {
    /// Construct a new `Some(SBAddress)` or `None`.
    pub fn maybe_wrap(raw: sys::SBAddressRef) -> Option<SBAddress> {
        if unsafe { sys::SBAddressIsValid(raw) } {
            Some(SBAddress { raw })
        } else {
            None
        }
    }

    /// Check whether or not this is a valid `SBAddress` value.
    pub fn is_valid(&self) -> bool {
        unsafe { sys::SBAddressIsValid(self.raw) }
    }

    /// Construct a new `SBAddress` from the given section and offset.
    pub fn from_section_offset(section: &SBSection, offset: lldb_addr_t) -> SBAddress {
        let a = unsafe { sys::CreateSBAddress2(section.raw, offset) };
        SBAddress::from(a)
    }

    /// Create a new `SBAddress` from the given load address within the target.
    pub fn from_load_address(load_addr: lldb_addr_t, target: &SBTarget) -> SBAddress {
        let a = unsafe { sys::CreateSBAddress3(load_addr, target.raw) };
        SBAddress::from(a)
    }

    /// The address that represents the address as it is found in the
    /// object file that defines it.
    pub fn file_address(&self) -> u64 {
        unsafe { sys::SBAddressGetFileAddress(self.raw) }
    }

    /// The address as it has been loaded into memory by a target.
    pub fn load_address(&self, target: &SBTarget) -> u64 {
        unsafe { sys::SBAddressGetLoadAddress(self.raw, target.raw) }
    }

    /// Get the `SBSymbolContext` for a given address.
    ///
    /// An address might refer to code or data from an existing
    /// module, or it might refer to something on the stack or heap.
    /// This will only return valid values if the address has been
    /// resolved to a code or data address using
    /// [`SBAddress::from_load_address`] or [`SBTarget::resolve_load_address`].
    ///
    /// * `resolve_scope`: Flags that specify what type of symbol context
    ///   is needed by the caller. These flags have constants starting
    ///   with `SYMBOL_CONTEXT_ITEM_`.
    pub fn symbol_context(&self, resolve_scope: u32) -> SBSymbolContext {
        SBSymbolContext::from(unsafe { sys::SBAddressGetSymbolContext(self.raw, resolve_scope) })
    }

    /// Get the `SBModule` for a given address.
    ///
    /// An address might refer to code or data from an existing
    /// module, or it might refer to something on the stack or heap.
    /// This will only return valid values if the address has been
    /// resolved to a code or data address using
    /// [`SBAddress::from_load_address`] or [`SBTarget::resolve_load_address`].
    ///
    /// This grabs an individual object for a given address and
    /// is less efficient if you want more than one symbol related objects.
    /// Use one of the following when you want multiple debug symbol related
    /// objects for an address:
    ///
    /// * `SBAddress::symbol_context`
    /// * `SBTarget::resolve_symbol_context_for_address`
    ///
    /// One or more bits from the `SymbolContextItem` enumerations can be logically
    /// OR'ed together to more efficiently retrieve multiple symbol objects.
    pub fn module(&self) -> Option<SBModule> {
        SBModule::maybe_wrap(unsafe { sys::SBAddressGetModule(self.raw) })
    }

    /// Get the `SBCompileUnit` for a given address.
    ///
    /// An address might refer to code or data from an existing
    /// module, or it might refer to something on the stack or heap.
    /// This will only return valid values if the address has been
    /// resolved to a code or data address using
    /// [`SBAddress::from_load_address`] or [`SBTarget::resolve_load_address`].
    ///
    /// This grabs an individual object for a given address and
    /// is less efficient if you want more than one symbol related objects.
    /// Use one of the following when you want multiple debug symbol related
    /// objects for an address:
    ///
    /// * `SBAddress::symbol_context`
    /// * `SBTarget::resolve_symbol_context_for_address`
    ///
    /// One or more bits from the `SymbolContextItem` enumerations can be logically
    /// OR'ed together to more efficiently retrieve multiple symbol objects.
    pub fn compile_unit(&self) -> Option<SBCompileUnit> {
        SBCompileUnit::maybe_wrap(unsafe { sys::SBAddressGetCompileUnit(self.raw) })
    }

    /// Get the `SBFunction` for a given address.
    ///
    /// An address might refer to code or data from an existing
    /// module, or it might refer to something on the stack or heap.
    /// This will only return valid values if the address has been
    /// resolved to a code or data address using
    /// [`SBAddress::from_load_address`] or [`SBTarget::resolve_load_address`].
    ///
    /// This grabs an individual object for a given address and
    /// is less efficient if you want more than one symbol related objects.
    /// Use one of the following when you want multiple debug symbol related
    /// objects for an address:
    ///
    /// * `SBAddress::symbol_context`
    /// * `SBTarget::resolve_symbol_context_for_address`
    ///
    /// One or more bits from the `SymbolContextItem` enumerations can be logically
    /// OR'ed together to more efficiently retrieve multiple symbol objects.
    pub fn function(&self) -> Option<SBFunction> {
        SBFunction::maybe_wrap(unsafe { sys::SBAddressGetFunction(self.raw) })
    }

    /// Get the `SBBlock` for a given address.
    ///
    /// An address might refer to code or data from an existing
    /// module, or it might refer to something on the stack or heap.
    /// This will only return valid values if the address has been
    /// resolved to a code or data address using
    /// [`SBAddress::from_load_address`] or [`SBTarget::resolve_load_address`].
    ///
    /// This grabs an individual object for a given address and
    /// is less efficient if you want more than one symbol related objects.
    /// Use one of the following when you want multiple debug symbol related
    /// objects for an address:
    ///
    /// * `SBAddress::symbol_context`
    /// * `SBTarget::resolve_symbol_context_for_address`
    ///
    /// One or more bits from the `SymbolContextItem` enumerations can be logically
    /// OR'ed together to more efficiently retrieve multiple symbol objects.
    pub fn block(&self) -> Option<SBBlock> {
        SBBlock::maybe_wrap(unsafe { sys::SBAddressGetBlock(self.raw) })
    }

    /// Get the `SBSymbol` for a given address.
    ///
    /// An address might refer to code or data from an existing
    /// module, or it might refer to something on the stack or heap.
    /// This will only return valid values if the address has been
    /// resolved to a code or data address using
    /// [`SBAddress::from_load_address`] or [`SBTarget::resolve_load_address`].
    ///
    /// This grabs an individual object for a given address and
    /// is less efficient if you want more than one symbol related objects.
    /// Use one of the following when you want multiple debug symbol related
    /// objects for an address:
    ///
    /// * `SBAddress::symbol_context`
    /// * `SBTarget::resolve_symbol_context_for_address`
    ///
    /// One or more bits from the `SymbolContextItem` enumerations can be logically
    /// OR'ed together to more efficiently retrieve multiple symbol objects.
    pub fn symbol(&self) -> Option<SBSymbol> {
        SBSymbol::maybe_wrap(unsafe { sys::SBAddressGetSymbol(self.raw) })
    }

    /// Get the `SBLineEntry` for a given address.
    ///
    /// An address might refer to code or data from an existing
    /// module, or it might refer to something on the stack or heap.
    /// This will only return valid values if the address has been
    /// resolved to a code or data address using
    /// [`SBAddress::from_load_address`] or [`SBTarget::resolve_load_address`].
    ///
    /// This grabs an individual object for a given address and
    /// is less efficient if you want more than one symbol related objects.
    /// Use one of the following when you want multiple debug symbol related
    /// objects for an address:
    ///
    /// * `SBAddress::symbol_context`
    /// * `SBTarget::resolve_symbol_context_for_address`
    ///
    /// One or more bits from the `SymbolContextItem` enumerations can be logically
    /// OR'ed together to more efficiently retrieve multiple symbol objects.
    pub fn line_entry(&self) -> Option<SBLineEntry> {
        SBLineEntry::maybe_wrap(unsafe { sys::SBAddressGetLineEntry(self.raw) })
    }
}

impl Clone for SBAddress {
    fn clone(&self) -> SBAddress {
        SBAddress {
            raw: unsafe { sys::CloneSBAddress(self.raw) },
        }
    }
}

impl fmt::Debug for SBAddress {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let stream = SBStream::new();
        unsafe { sys::SBAddressGetDescription(self.raw, stream.raw) };
        write!(fmt, "SBAddress {{ {} }}", stream.data())
    }
}

impl Drop for SBAddress {
    fn drop(&mut self) {
        unsafe { sys::DisposeSBAddress(self.raw) };
    }
}

impl From<sys::SBAddressRef> for SBAddress {
    fn from(raw: sys::SBAddressRef) -> SBAddress {
        SBAddress { raw }
    }
}

unsafe impl Send for SBAddress {}
unsafe impl Sync for SBAddress {}

impl PartialEq for SBAddress {
    fn eq(&self, other: &Self) -> bool {
        unsafe { sys::SBAddressIsEqual(self.raw, other.raw) }
    }
}

impl Eq for SBAddress {}

#[cfg(feature = "graphql")]
graphql_object!(SBAddress: super::debugger::SBDebugger | &self | {
    field is_valid() -> bool {
        self.is_valid()
    }

    // TODO(bm) This should be u64
    field file_address() -> i32 {
        self.file_address() as i32
    }

    field module() -> Option<SBModule> {
        self.module()
    }

    field compile_unit() -> Option<SBCompileUnit> {
        self.compile_unit()
    }

    field function() -> Option<SBFunction> {
        self.function()
    }

    field block() -> Option<SBBlock> {
        self.block()
    }

    field symbol() -> Option<SBSymbol> {
        self.symbol()
    }

    field line_entry() -> Option<SBLineEntry> {
        self.line_entry()
    }
});

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_equal() {
        let sect = unsafe { sys::CreateSBSection() };
        let a = SBAddress::maybe_wrap(unsafe { sys::CreateSBAddress2(sect, 42) }).unwrap();
        let b = SBAddress::maybe_wrap(unsafe { sys::CreateSBAddress2(sect, 42) }).unwrap();
        assert!(a == b);
    }

    #[test]
    fn test_not_equal() {
        let sect = unsafe { sys::CreateSBSection() };
        let a = SBAddress::maybe_wrap(unsafe { sys::CreateSBAddress2(sect, 42) }).unwrap();
        let b = SBAddress::maybe_wrap(unsafe { sys::CreateSBAddress2(sect, 111) }).unwrap();
        assert!(a != b);
    }
}
<commit_msg>SBAddress: Link SBAddress::symbol_context in docs.<commit_after>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use super::block::SBBlock;
use super::compileunit::SBCompileUnit;
use super::function::SBFunction;
use super::lineentry::SBLineEntry;
use super::lldb_addr_t;
use super::module::SBModule;
use super::section::SBSection;
use super::stream::SBStream;
use super::symbol::SBSymbol;
use super::symbolcontext::SBSymbolContext;
use super::target::SBTarget;
use std::fmt;
use sys;

/// A section + offset based address class.
///
/// The `SBAddress` class allows addresses to be relative to a section
/// that can move during runtime due to images (executables, shared
/// libraries, bundles, frameworks) being loaded at different
/// addresses than the addresses found in the object file that
/// represents them on disk. There are currently two types of addresses
/// for a section:
///
/// * file addresses
/// * load addresses
///
/// File addresses represents the virtual addresses that are in the 'on
/// disk' object files. These virtual addresses are converted to be
/// relative to unique sections scoped to the object file so that
/// when/if the addresses slide when the images are loaded/unloaded
/// in memory, we can easily track these changes without having to
/// update every object (compile unit ranges, line tables, function
/// address ranges, lexical block and inlined subroutine address
/// ranges, global and static variables) each time an image is loaded or
/// unloaded.
///
/// Load addresses represents the virtual addresses where each section
/// ends up getting loaded at runtime. Before executing a program, it
/// is common for all of the load addresses to be unresolved. When a
/// `DynamicLoader` plug-in receives notification that shared libraries
/// have been loaded/unloaded, the load addresses of the main executable
/// and any images (shared libraries) will be  resolved/unresolved. When
/// this happens, breakpoints that are in one of these sections can be
/// set/cleared.
pub struct SBAddress {
    /// The underlying raw `SBAddressRef`.
    pub raw: sys::SBAddressRef,
}

impl SBAddress {
    /// Construct a new `Some(SBAddress)` or `None`.
    pub fn maybe_wrap(raw: sys::SBAddressRef) -> Option<SBAddress> {
        if unsafe { sys::SBAddressIsValid(raw) } {
            Some(SBAddress { raw })
        } else {
            None
        }
    }

    /// Check whether or not this is a valid `SBAddress` value.
    pub fn is_valid(&self) -> bool {
        unsafe { sys::SBAddressIsValid(self.raw) }
    }

    /// Construct a new `SBAddress` from the given section and offset.
    pub fn from_section_offset(section: &SBSection, offset: lldb_addr_t) -> SBAddress {
        let a = unsafe { sys::CreateSBAddress2(section.raw, offset) };
        SBAddress::from(a)
    }

    /// Create a new `SBAddress` from the given load address within the target.
    pub fn from_load_address(load_addr: lldb_addr_t, target: &SBTarget) -> SBAddress {
        let a = unsafe { sys::CreateSBAddress3(load_addr, target.raw) };
        SBAddress::from(a)
    }

    /// The address that represents the address as it is found in the
    /// object file that defines it.
    pub fn file_address(&self) -> u64 {
        unsafe { sys::SBAddressGetFileAddress(self.raw) }
    }

    /// The address as it has been loaded into memory by a target.
    pub fn load_address(&self, target: &SBTarget) -> u64 {
        unsafe { sys::SBAddressGetLoadAddress(self.raw, target.raw) }
    }

    /// Get the `SBSymbolContext` for a given address.
    ///
    /// An address might refer to code or data from an existing
    /// module, or it might refer to something on the stack or heap.
    /// This will only return valid values if the address has been
    /// resolved to a code or data address using
    /// [`SBAddress::from_load_address`] or [`SBTarget::resolve_load_address`].
    ///
    /// * `resolve_scope`: Flags that specify what type of symbol context
    ///   is needed by the caller. These flags have constants starting
    ///   with `SYMBOL_CONTEXT_ITEM_`.
    pub fn symbol_context(&self, resolve_scope: u32) -> SBSymbolContext {
        SBSymbolContext::from(unsafe { sys::SBAddressGetSymbolContext(self.raw, resolve_scope) })
    }

    /// Get the `SBModule` for a given address.
    ///
    /// An address might refer to code or data from an existing
    /// module, or it might refer to something on the stack or heap.
    /// This will only return valid values if the address has been
    /// resolved to a code or data address using
    /// [`SBAddress::from_load_address`] or [`SBTarget::resolve_load_address`].
    ///
    /// This grabs an individual object for a given address and
    /// is less efficient if you want more than one symbol related objects.
    /// Use one of the following when you want multiple debug symbol related
    /// objects for an address:
    ///
    /// * [`SBAddress::symbol_context`]
    /// * `SBTarget::resolve_symbol_context_for_address`
    ///
    /// One or more bits from the `SymbolContextItem` enumerations can be logically
    /// OR'ed together to more efficiently retrieve multiple symbol objects.
    pub fn module(&self) -> Option<SBModule> {
        SBModule::maybe_wrap(unsafe { sys::SBAddressGetModule(self.raw) })
    }

    /// Get the `SBCompileUnit` for a given address.
    ///
    /// An address might refer to code or data from an existing
    /// module, or it might refer to something on the stack or heap.
    /// This will only return valid values if the address has been
    /// resolved to a code or data address using
    /// [`SBAddress::from_load_address`] or [`SBTarget::resolve_load_address`].
    ///
    /// This grabs an individual object for a given address and
    /// is less efficient if you want more than one symbol related objects.
    /// Use one of the following when you want multiple debug symbol related
    /// objects for an address:
    ///
    /// * [`SBAddress::symbol_context`]
    /// * `SBTarget::resolve_symbol_context_for_address`
    ///
    /// One or more bits from the `SymbolContextItem` enumerations can be logically
    /// OR'ed together to more efficiently retrieve multiple symbol objects.
    pub fn compile_unit(&self) -> Option<SBCompileUnit> {
        SBCompileUnit::maybe_wrap(unsafe { sys::SBAddressGetCompileUnit(self.raw) })
    }

    /// Get the `SBFunction` for a given address.
    ///
    /// An address might refer to code or data from an existing
    /// module, or it might refer to something on the stack or heap.
    /// This will only return valid values if the address has been
    /// resolved to a code or data address using
    /// [`SBAddress::from_load_address`] or [`SBTarget::resolve_load_address`].
    ///
    /// This grabs an individual object for a given address and
    /// is less efficient if you want more than one symbol related objects.
    /// Use one of the following when you want multiple debug symbol related
    /// objects for an address:
    ///
    /// * [`SBAddress::symbol_context`]
    /// * `SBTarget::resolve_symbol_context_for_address`
    ///
    /// One or more bits from the `SymbolContextItem` enumerations can be logically
    /// OR'ed together to more efficiently retrieve multiple symbol objects.
    pub fn function(&self) -> Option<SBFunction> {
        SBFunction::maybe_wrap(unsafe { sys::SBAddressGetFunction(self.raw) })
    }

    /// Get the `SBBlock` for a given address.
    ///
    /// An address might refer to code or data from an existing
    /// module, or it might refer to something on the stack or heap.
    /// This will only return valid values if the address has been
    /// resolved to a code or data address using
    /// [`SBAddress::from_load_address`] or [`SBTarget::resolve_load_address`].
    ///
    /// This grabs an individual object for a given address and
    /// is less efficient if you want more than one symbol related objects.
    /// Use one of the following when you want multiple debug symbol related
    /// objects for an address:
    ///
    /// * [`SBAddress::symbol_context`]
    /// * `SBTarget::resolve_symbol_context_for_address`
    ///
    /// One or more bits from the `SymbolContextItem` enumerations can be logically
    /// OR'ed together to more efficiently retrieve multiple symbol objects.
    pub fn block(&self) -> Option<SBBlock> {
        SBBlock::maybe_wrap(unsafe { sys::SBAddressGetBlock(self.raw) })
    }

    /// Get the `SBSymbol` for a given address.
    ///
    /// An address might refer to code or data from an existing
    /// module, or it might refer to something on the stack or heap.
    /// This will only return valid values if the address has been
    /// resolved to a code or data address using
    /// [`SBAddress::from_load_address`] or [`SBTarget::resolve_load_address`].
    ///
    /// This grabs an individual object for a given address and
    /// is less efficient if you want more than one symbol related objects.
    /// Use one of the following when you want multiple debug symbol related
    /// objects for an address:
    ///
    /// * [`SBAddress::symbol_context`]
    /// * `SBTarget::resolve_symbol_context_for_address`
    ///
    /// One or more bits from the `SymbolContextItem` enumerations can be logically
    /// OR'ed together to more efficiently retrieve multiple symbol objects.
    pub fn symbol(&self) -> Option<SBSymbol> {
        SBSymbol::maybe_wrap(unsafe { sys::SBAddressGetSymbol(self.raw) })
    }

    /// Get the `SBLineEntry` for a given address.
    ///
    /// An address might refer to code or data from an existing
    /// module, or it might refer to something on the stack or heap.
    /// This will only return valid values if the address has been
    /// resolved to a code or data address using
    /// [`SBAddress::from_load_address`] or [`SBTarget::resolve_load_address`].
    ///
    /// This grabs an individual object for a given address and
    /// is less efficient if you want more than one symbol related objects.
    /// Use one of the following when you want multiple debug symbol related
    /// objects for an address:
    ///
    /// * [`SBAddress::symbol_context`]
    /// * `SBTarget::resolve_symbol_context_for_address`
    ///
    /// One or more bits from the `SymbolContextItem` enumerations can be logically
    /// OR'ed together to more efficiently retrieve multiple symbol objects.
    pub fn line_entry(&self) -> Option<SBLineEntry> {
        SBLineEntry::maybe_wrap(unsafe { sys::SBAddressGetLineEntry(self.raw) })
    }
}

impl Clone for SBAddress {
    fn clone(&self) -> SBAddress {
        SBAddress {
            raw: unsafe { sys::CloneSBAddress(self.raw) },
        }
    }
}

impl fmt::Debug for SBAddress {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let stream = SBStream::new();
        unsafe { sys::SBAddressGetDescription(self.raw, stream.raw) };
        write!(fmt, "SBAddress {{ {} }}", stream.data())
    }
}

impl Drop for SBAddress {
    fn drop(&mut self) {
        unsafe { sys::DisposeSBAddress(self.raw) };
    }
}

impl From<sys::SBAddressRef> for SBAddress {
    fn from(raw: sys::SBAddressRef) -> SBAddress {
        SBAddress { raw }
    }
}

unsafe impl Send for SBAddress {}
unsafe impl Sync for SBAddress {}

impl PartialEq for SBAddress {
    fn eq(&self, other: &Self) -> bool {
        unsafe { sys::SBAddressIsEqual(self.raw, other.raw) }
    }
}

impl Eq for SBAddress {}

#[cfg(feature = "graphql")]
graphql_object!(SBAddress: super::debugger::SBDebugger | &self | {
    field is_valid() -> bool {
        self.is_valid()
    }

    // TODO(bm) This should be u64
    field file_address() -> i32 {
        self.file_address() as i32
    }

    field module() -> Option<SBModule> {
        self.module()
    }

    field compile_unit() -> Option<SBCompileUnit> {
        self.compile_unit()
    }

    field function() -> Option<SBFunction> {
        self.function()
    }

    field block() -> Option<SBBlock> {
        self.block()
    }

    field symbol() -> Option<SBSymbol> {
        self.symbol()
    }

    field line_entry() -> Option<SBLineEntry> {
        self.line_entry()
    }
});

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_equal() {
        let sect = unsafe { sys::CreateSBSection() };
        let a = SBAddress::maybe_wrap(unsafe { sys::CreateSBAddress2(sect, 42) }).unwrap();
        let b = SBAddress::maybe_wrap(unsafe { sys::CreateSBAddress2(sect, 42) }).unwrap();
        assert!(a == b);
    }

    #[test]
    fn test_not_equal() {
        let sect = unsafe { sys::CreateSBSection() };
        let a = SBAddress::maybe_wrap(unsafe { sys::CreateSBAddress2(sect, 42) }).unwrap();
        let b = SBAddress::maybe_wrap(unsafe { sys::CreateSBAddress2(sect, 111) }).unwrap();
        assert!(a != b);
    }
}
<|endoftext|>


<commit_before>use crate::prelude::*;

use rustc::mir::StatementKind::*;
use rustc_index::vec::IndexVec;

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum SsaKind {
    NotSsa,
    Ssa,
}

pub fn analyze(fx: &FunctionCx<'_, '_, impl Backend>) -> IndexVec<Local, SsaKind> {
    let mut flag_map = fx.mir.local_decls.iter().map(|local_decl| {
        if fx.clif_type(local_decl.ty).is_some() {
            SsaKind::Ssa
        } else {
            SsaKind::NotSsa
        }
    }).collect::<IndexVec<Local, SsaKind>>();

    for bb in fx.mir.basic_blocks().iter() {
        for stmt in bb.statements.iter() {
            match &stmt.kind {
                Assign(place_and_rval) => match &place_and_rval.1 {
                    Rvalue::Ref(_, _, place) => {
                        analyze_non_ssa_place(&mut flag_map, place);
                    }
                    _ => {}
                },
                _ => {}
            }
        }

        match &bb.terminator().kind {
            TerminatorKind::Call {
                destination: Some((place, _)),
                ..
            } => analyze_non_ssa_place(&mut flag_map, place),
            _ => {}
        }
    }

    flag_map
}

fn analyze_non_ssa_place(flag_map: &mut IndexVec<Local, SsaKind>, place: &Place) {
    match place.base {
        PlaceBase::Local(local) => not_ssa(flag_map, local),
        _ => {}
    }
}

fn not_ssa(flag_map: &mut IndexVec<Local, SsaKind>, local: Local) {
    flag_map[local] = SsaKind::NotSsa;
}
<commit_msg>[OPT] Don't force return value of call to stack<commit_after>use crate::prelude::*;

use rustc::mir::StatementKind::*;
use rustc_index::vec::IndexVec;

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum SsaKind {
    NotSsa,
    Ssa,
}

pub fn analyze(fx: &FunctionCx<'_, '_, impl Backend>) -> IndexVec<Local, SsaKind> {
    let mut flag_map = fx.mir.local_decls.iter().map(|local_decl| {
        if fx.clif_type(local_decl.ty).is_some() {
            SsaKind::Ssa
        } else {
            SsaKind::NotSsa
        }
    }).collect::<IndexVec<Local, SsaKind>>();

    for bb in fx.mir.basic_blocks().iter() {
        for stmt in bb.statements.iter() {
            match &stmt.kind {
                Assign(place_and_rval) => match &place_and_rval.1 {
                    Rvalue::Ref(_, _, place) => {
                        analyze_non_ssa_place(&mut flag_map, place);
                    }
                    _ => {}
                },
                _ => {}
            }
        }
    }

    flag_map
}

fn analyze_non_ssa_place(flag_map: &mut IndexVec<Local, SsaKind>, place: &Place) {
    match place.base {
        PlaceBase::Local(local) => not_ssa(flag_map, local),
        _ => {}
    }
}

fn not_ssa(flag_map: &mut IndexVec<Local, SsaKind>, local: Local) {
    flag_map[local] = SsaKind::NotSsa;
}
<|endoftext|>