<commit_before>// Copyright 2017-2019 `multipart-async` Crate Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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 std::fmt;
use std::pin::Pin;
use std::rc::Rc;
use std::task::Poll::*;
use std::{mem, str};

use futures_core::{Future, Poll, Stream, TryStream};
//pub use self::collect::{ReadTextField, TextField};
use futures_core::task::Context;

use crate::server::Error::Utf8;
use crate::server::{Error, PushChunk};
use crate::BodyChunk;

use super::boundary::BoundaryFinder;
use super::Multipart;

pub use self::headers::FieldHeaders;
pub(crate) use self::headers::ReadHeaders;
use futures_util::TryStreamExt;

// mod collect;
mod headers;

/// A `Future` potentially yielding the next field in the multipart stream.
///
/// If there are no more fields in the stream, `Ok(None)` is returned.
///
/// See [`Multipart::next_field()`](../struct.Multipart.html#method.next_field) for usage.
pub struct NextField<'a, S: TryStream + 'a> {
    multipart: Option<Pin<&'a mut Multipart<S>>>,
    has_next_field: bool,
}

impl<'a, S: TryStream + 'a> NextField<'a, S> {
    pub(crate) fn new(multipart: Pin<&'a mut Multipart<S>>) -> Self {
        NextField {
            multipart: Some(multipart),
            has_next_field: false,
        }
    }

    fn multipart(&mut self) -> Option<Pin<&mut Multipart<S>>> {
        Some(self.multipart.as_mut()?.as_mut())
    }
}

impl<'a, S: 'a> Future for NextField<'a, S>
where
    S: TryStream,
    S::Ok: BodyChunk,
    Error<S::Error>: From<S::Error>,
{
    type Output = super::Result<Option<Field<'a, S>>, S::Error>;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        // since we can't use `?` with `Option<...>` in this context
        macro_rules! multipart {
            (get) => {
                if let Some(ref mut multipart) = self.multipart {
                    multipart.as_mut()
                } else {
                    return Ready(Ok(None));
                }
            };
            (take) => {
                if let Some(multipart) = self.multipart.take() {
                    multipart
                } else {
                    return Ready(Ok(None));
                }
            };
        }

        // `false` and `multipart = Some` means we haven't polled for next field yet
        self.has_next_field =
            self.has_next_field || ready!(multipart!(get).poll_has_next_field(cx)?);

        if !self.has_next_field {
            // end of stream, next `?` will return
            self.multipart = None;
        }

        Ready(Ok(Some(Field {
            headers: ready!(multipart!(get).poll_field_headers(cx)?),
            data: FieldData {
                multipart: multipart!(take),
            },
            _priv: (),
        })))
    }
}

/// A single field in a multipart stream.
///
/// The data of the field is provided as a `Stream` impl in the `data` field.
pub struct Field<'a, S: TryStream + 'a> {
    /// The headers of this field, including the name, filename, and `Content-Type`, if provided.
    pub headers: FieldHeaders,
    /// The data of this field in the request, represented as a stream of chunks.
    pub data: FieldData<'a, S>,
    _priv: (),
}

impl<S: TryStream> fmt::Debug for Field<'_, S> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Field")
            .field("headers", &self.headers)
            .field("data", &"<FieldData>")
            .finish()
    }
}

/// The data of a field in a multipart stream, as a stream of chunks.
///
/// It may be read to completion via the `Stream` impl, or collected to a string with
/// `.read_to_string()`.
pub struct FieldData<'a, S: TryStream + 'a> {
    multipart: Pin<&'a mut Multipart<S>>,
}

impl<S: TryStream> FieldData<'_, S>
where
    S::Ok: BodyChunk,
    Error<S::Error>: From<S::Error>,
{
    /// Return a `Future` which yields the result of reading this field's data to a `String`.
    ///
    /// ### Note: UTF-8 Only
    /// Reading to a string using a non-UTF-8 charset is currently outside of the scope of this
    /// crate. Most browsers send form requests using the same charset as the page
    /// the form resides in, so as long as you only serve UTF-8 encoded pages, this would only
    /// realistically happen in one of two cases:
    ///
    /// * a non-browser client like cURL was specifically instructed by the user to
    /// use a non-UTF-8 charset, or:
    /// * the field is actually a text file encoded in a charset that is not UTF-8
    /// (most likely Windows-1252 or UTF-16).
    pub fn read_to_string(self) -> ReadToString<Self> {
        ReadToString::new(self)
    }
}

impl<S: TryStream> Stream for FieldData<'_, S>
where
    S::Ok: BodyChunk,
    Error<S::Error>: From<S::Error>,
{
    type Item = super::Result<S::Ok, S::Error>;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
        self.multipart.as_mut().poll_field_chunk(cx)
    }
}

/// A `Future` that yields the body of a field read to a `String`.
pub struct ReadToString<S: TryStream + Unpin> {
    stream: S,
    string: String,
    surrogate: Option<([u8; 3], u8)>,
}

impl<S: TryStream + Unpin> ReadToString<S> {
    pub(crate) fn new(stream: S) -> Self {
        ReadToString {
            stream,
            string: String::new(),
            surrogate: None,
        }
    }
}

impl<S: TryStream + Unpin> Future for ReadToString<S>
where
    S::Ok: BodyChunk,
    Error<S::Error>: From<S::Error>,
{
    type Output = super::Result<String, S::Error>;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
        while let Some(mut data) = ready!(self.stream.try_poll_next_unpin(cx)?) {
            if let Some((mut start, start_len)) = self.surrogate {
                assert!(
                    start_len > 0 && start_len < 4,
                    "start_len out of range: {:?}",
                    start_len
                );

                let start_len = start_len as usize;

                let (width, needed) = if let Some(width) = utf8_char_width(start[0]) {
                    (
                        width,
                        width.checked_sub(start_len).expect("start_len >= width"),
                    )
                } else {
                    return Ready(fmt_err!(
                        "unexpected start of UTF-8 surrogate: {:X}",
                        start[0]
                    ));
                };

                if data.len() < needed {
                    start[start_len..start_len + data.len()].copy_from_slice(data.slice(..));
                    self.surrogate = Some((start, (start_len + data.len()) as u8));
                    continue;
                }

                let mut surrogate = [0u8; 4];
                surrogate[..start_len].copy_from_slice(&start[..start_len]);
                surrogate[start_len..width].copy_from_slice(data.slice(..needed));

                trace!("decoding surrogate: {:?}", &surrogate[..width]);

                self.string
                    .push_str(str::from_utf8(&surrogate[..width]).map_err(Utf8)?);

                let (_, rem) = data.split_into(needed);
                data = rem;
                self.surrogate = None;
            }

            match str::from_utf8(data.as_slice()) {
                Ok(s) => self.string.push_str(s),
                Err(e) => {
                    if e.error_len().is_some() {
                        trace!("ReadToString failed to decode; string: {:?}, surrogate: {:?}, data: {:?}",
                           self.string, self.surrogate, data.as_slice());
                        // we encountered an invalid surrogate
                        return Ready(Err(Utf8(e)));
                    } else {
                        self.string.push_str(unsafe {
                            // Utf8Error specifies that `..e.valid_up_to()` is valid UTF-8
                            str::from_utf8_unchecked(data.slice(..e.valid_up_to()))
                        });

                        let start_len = data.len() - e.valid_up_to();
                        let mut start = [0u8; 3];
                        start[..start_len].copy_from_slice(data.slice(e.valid_up_to()..));

                        // `e.valid_up_to()` is specified to be `[-1, -3]` of `data.len()`
                        self.surrogate = Some((start, start_len as u8));
                    }
                }
            }
        }

        if let Some((start, _)) = self.surrogate {
            ret_err!("incomplete UTF-8 surrogate: {:?}", start);
        }

        Ready(Ok(mem::replace(&mut self.string, String::new())))
    }
}

fn utf8_char_width(first: u8) -> Option<usize> {
    // simplification of the LUT here:
    // https://github.com/rust-lang/rust/blob/fe6d05a/src/libcore/str/mod.rs#L1565
    match first {
        // ASCII characters are one byte
        0x00..=0x7F => Some(1),
        0xC2..=0xDF => Some(2),
        0xE0..=0xEF => Some(3),
        0xF0..=0xF4 => Some(4),
        _ => None,
    }
}

#[test]
fn assert_types_unpin() {
    use crate::test_util::assert_unpin;

    fn inner<'a, S: TryStream + 'a>() {
        assert_unpin::<FieldData<'a, S>>();
    }

    // `Unpin` is checked on `ReadToString` in `test_read_to_string()`.
}

#[test]
fn test_read_to_string() {
    use crate::test_util::mock_stream;
    use futures_util::TryFutureExt;

    let _ = ::env_logger::try_init();

    let test_data = mock_stream(&[b"Hello", b",", b" ", b"world!"]);

    let mut read_to_string = ReadToString::new(test_data);

    ready_assert_eq!(
        |cx| read_to_string.try_poll_unpin(cx),
        Ok("Hello, world!".to_string())
    );

    let test_data_unicode = mock_stream(&[
        &[40, 226, 149],
        &[175, 194, 176, 226, 150],
        &[161, 194, 176, 41, 226, 149],
        &[175, 239, 184, 181, 32],
        &[226, 148, 187, 226, 148, 129, 226, 148, 187],
    ]);

    let mut read_to_string = ReadToString::new(test_data_unicode);

    ready_assert_eq!(
        |cx| read_to_string.try_poll_unpin(cx),
        Ok("(╯°□°)╯︵ ┻━┻".to_string())
    );
}
<commit_msg>remove futures_util::TryStreamExt usage from server<commit_after>// Copyright 2017-2019 `multipart-async` Crate Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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 std::fmt;
use std::pin::Pin;
use std::rc::Rc;
use std::task::Poll::*;
use std::{mem, str};

use futures_core::{Future, Poll, Stream, TryStream};
//pub use self::collect::{ReadTextField, TextField};
use futures_core::task::Context;

use crate::server::Error::Utf8;
use crate::server::{Error, PushChunk};
use crate::BodyChunk;

use super::boundary::BoundaryFinder;
use super::Multipart;

pub use self::headers::FieldHeaders;
pub(crate) use self::headers::ReadHeaders;

// mod collect;
mod headers;

/// A `Future` potentially yielding the next field in the multipart stream.
///
/// If there are no more fields in the stream, `Ok(None)` is returned.
///
/// See [`Multipart::next_field()`](../struct.Multipart.html#method.next_field) for usage.
pub struct NextField<'a, S: TryStream + 'a> {
    multipart: Option<Pin<&'a mut Multipart<S>>>,
    has_next_field: bool,
}

impl<'a, S: TryStream + 'a> NextField<'a, S> {
    pub(crate) fn new(multipart: Pin<&'a mut Multipart<S>>) -> Self {
        NextField {
            multipart: Some(multipart),
            has_next_field: false,
        }
    }

    fn multipart(&mut self) -> Option<Pin<&mut Multipart<S>>> {
        Some(self.multipart.as_mut()?.as_mut())
    }
}

impl<'a, S: 'a> Future for NextField<'a, S>
where
    S: TryStream,
    S::Ok: BodyChunk,
    Error<S::Error>: From<S::Error>,
{
    type Output = super::Result<Option<Field<'a, S>>, S::Error>;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        // since we can't use `?` with `Option<...>` in this context
        macro_rules! multipart {
            (get) => {
                if let Some(ref mut multipart) = self.multipart {
                    multipart.as_mut()
                } else {
                    return Ready(Ok(None));
                }
            };
            (take) => {
                if let Some(multipart) = self.multipart.take() {
                    multipart
                } else {
                    return Ready(Ok(None));
                }
            };
        }

        // `false` and `multipart = Some` means we haven't polled for next field yet
        self.has_next_field =
            self.has_next_field || ready!(multipart!(get).poll_has_next_field(cx)?);

        if !self.has_next_field {
            // end of stream, next `?` will return
            self.multipart = None;
        }

        Ready(Ok(Some(Field {
            headers: ready!(multipart!(get).poll_field_headers(cx)?),
            data: FieldData {
                multipart: multipart!(take),
            },
            _priv: (),
        })))
    }
}

/// A single field in a multipart stream.
///
/// The data of the field is provided as a `Stream` impl in the `data` field.
pub struct Field<'a, S: TryStream + 'a> {
    /// The headers of this field, including the name, filename, and `Content-Type`, if provided.
    pub headers: FieldHeaders,
    /// The data of this field in the request, represented as a stream of chunks.
    pub data: FieldData<'a, S>,
    _priv: (),
}

impl<S: TryStream> fmt::Debug for Field<'_, S> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Field")
            .field("headers", &self.headers)
            .field("data", &"<FieldData>")
            .finish()
    }
}

/// The data of a field in a multipart stream, as a stream of chunks.
///
/// It may be read to completion via the `Stream` impl, or collected to a string with
/// `.read_to_string()`.
pub struct FieldData<'a, S: TryStream + 'a> {
    multipart: Pin<&'a mut Multipart<S>>,
}

impl<S: TryStream> FieldData<'_, S>
where
    S::Ok: BodyChunk,
    Error<S::Error>: From<S::Error>,
{
    /// Return a `Future` which yields the result of reading this field's data to a `String`.
    ///
    /// ### Note: UTF-8 Only
    /// Reading to a string using a non-UTF-8 charset is currently outside of the scope of this
    /// crate. Most browsers send form requests using the same charset as the page
    /// the form resides in, so as long as you only serve UTF-8 encoded pages, this would only
    /// realistically happen in one of two cases:
    ///
    /// * a non-browser client like cURL was specifically instructed by the user to
    /// use a non-UTF-8 charset, or:
    /// * the field is actually a text file encoded in a charset that is not UTF-8
    /// (most likely Windows-1252 or UTF-16).
    pub fn read_to_string(self) -> ReadToString<Self> {
        ReadToString::new(self)
    }
}

impl<S: TryStream> Stream for FieldData<'_, S>
where
    S::Ok: BodyChunk,
    Error<S::Error>: From<S::Error>,
{
    type Item = super::Result<S::Ok, S::Error>;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
        self.multipart.as_mut().poll_field_chunk(cx)
    }
}

/// A `Future` that yields the body of a field read to a `String`.
pub struct ReadToString<S: TryStream + Unpin> {
    stream: S,
    string: String,
    surrogate: Option<([u8; 3], u8)>,
}

impl<S: TryStream + Unpin> ReadToString<S> {
    pub(crate) fn new(stream: S) -> Self {
        ReadToString {
            stream,
            string: String::new(),
            surrogate: None,
        }
    }
}

impl<S: TryStream + Unpin> Future for ReadToString<S>
where
    S::Ok: BodyChunk,
    Error<S::Error>: From<S::Error>,
{
    type Output = super::Result<String, S::Error>;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
        while let Some(mut data) = ready!(Pin::new(&mut self.stream).try_poll_next(cx)?) {
            if let Some((mut start, start_len)) = self.surrogate {
                assert!(
                    start_len > 0 && start_len < 4,
                    "start_len out of range: {:?}",
                    start_len
                );

                let start_len = start_len as usize;

                let (width, needed) = if let Some(width) = utf8_char_width(start[0]) {
                    (
                        width,
                        width.checked_sub(start_len).expect("start_len >= width"),
                    )
                } else {
                    return Ready(fmt_err!(
                        "unexpected start of UTF-8 surrogate: {:X}",
                        start[0]
                    ));
                };

                if data.len() < needed {
                    start[start_len..start_len + data.len()].copy_from_slice(data.slice(..));
                    self.surrogate = Some((start, (start_len + data.len()) as u8));
                    continue;
                }

                let mut surrogate = [0u8; 4];
                surrogate[..start_len].copy_from_slice(&start[..start_len]);
                surrogate[start_len..width].copy_from_slice(data.slice(..needed));

                trace!("decoding surrogate: {:?}", &surrogate[..width]);

                self.string
                    .push_str(str::from_utf8(&surrogate[..width]).map_err(Utf8)?);

                let (_, rem) = data.split_into(needed);
                data = rem;
                self.surrogate = None;
            }

            match str::from_utf8(data.as_slice()) {
                Ok(s) => self.string.push_str(s),
                Err(e) => {
                    if e.error_len().is_some() {
                        trace!("ReadToString failed to decode; string: {:?}, surrogate: {:?}, data: {:?}",
                           self.string, self.surrogate, data.as_slice());
                        // we encountered an invalid surrogate
                        return Ready(Err(Utf8(e)));
                    } else {
                        self.string.push_str(unsafe {
                            // Utf8Error specifies that `..e.valid_up_to()` is valid UTF-8
                            str::from_utf8_unchecked(data.slice(..e.valid_up_to()))
                        });

                        let start_len = data.len() - e.valid_up_to();
                        let mut start = [0u8; 3];
                        start[..start_len].copy_from_slice(data.slice(e.valid_up_to()..));

                        // `e.valid_up_to()` is specified to be `[-1, -3]` of `data.len()`
                        self.surrogate = Some((start, start_len as u8));
                    }
                }
            }
        }

        if let Some((start, _)) = self.surrogate {
            ret_err!("incomplete UTF-8 surrogate: {:?}", start);
        }

        Ready(Ok(mem::replace(&mut self.string, String::new())))
    }
}

fn utf8_char_width(first: u8) -> Option<usize> {
    // simplification of the LUT here:
    // https://github.com/rust-lang/rust/blob/fe6d05a/src/libcore/str/mod.rs#L1565
    match first {
        // ASCII characters are one byte
        0x00..=0x7F => Some(1),
        0xC2..=0xDF => Some(2),
        0xE0..=0xEF => Some(3),
        0xF0..=0xF4 => Some(4),
        _ => None,
    }
}

#[test]
fn assert_types_unpin() {
    use crate::test_util::assert_unpin;

    fn inner<'a, S: TryStream + 'a>() {
        assert_unpin::<FieldData<'a, S>>();
    }

    // `Unpin` is checked on `ReadToString` in `test_read_to_string()`.
}

#[test]
fn test_read_to_string() {
    use crate::test_util::mock_stream;
    use futures_util::TryFutureExt;

    let _ = ::env_logger::try_init();

    let test_data = mock_stream(&[b"Hello", b",", b" ", b"world!"]);

    let mut read_to_string = ReadToString::new(test_data);

    ready_assert_eq!(
        |cx| read_to_string.try_poll_unpin(cx),
        Ok("Hello, world!".to_string())
    );

    let test_data_unicode = mock_stream(&[
        &[40, 226, 149],
        &[175, 194, 176, 226, 150],
        &[161, 194, 176, 41, 226, 149],
        &[175, 239, 184, 181, 32],
        &[226, 148, 187, 226, 148, 129, 226, 148, 187],
    ]);

    let mut read_to_string = ReadToString::new(test_data_unicode);

    ready_assert_eq!(
        |cx| read_to_string.try_poll_unpin(cx),
        Ok("(╯°□°)╯︵ ┻━┻".to_string())
    );
}
<|endoftext|>


<commit_before>//
// Copyright (c) Pirmin Kalberer. All rights reserved.
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
//

use datasource::PostgisInput;
use core::grid::Grid;
use core::grid::Extent;
use core::layer::Layer;
use core::Config;
use cache::{Tilecache, Nocache};
use service::mvt::{Tileset, MvtService};

fn mvt_service() -> MvtService {
    use std::env;

    let pg: PostgisInput = match env::var("DBCONN") {
            Result::Ok(val) => Some(PostgisInput::new(&val).connected()),
            Result::Err(_) => panic!("DBCONN undefined"),
        }
        .unwrap();
    let grid = Grid::web_mercator();
    let mut layer = Layer::new("points");
    layer.table_name = Some(String::from("ne_10m_populated_places"));
    layer.geometry_field = Some(String::from("wkb_geometry"));
    layer.geometry_type = Some(String::from("POINT"));
    layer.query_limit = Some(1);
    let tileset = Tileset {
        name: "points".to_string(),
        extent: Some(Extent {
                         minx: -179.58998,
                         miny: -90.00000,
                         maxx: 179.38330,
                         maxy: 82.48332,
                     }),
        layers: vec![layer],
    };
    let mut service = MvtService {
        input: pg,
        grid: grid,
        tilesets: vec![tileset],
        cache: Tilecache::Nocache(Nocache),
    };
    service.prepare_feature_queries();
    service
}

#[test]
#[ignore]
fn test_tile_query() {
    let service = mvt_service();

    let mvt_tile = service.tile("points", 33, 41, 6);
    println!("{:#?}", mvt_tile);
    let expected = r#"Tile {
    layers: [
        Tile_Layer {
            version: Some(
                2
            ),
            name: Some("points"),
            features: [
                Tile_Feature {
                    id: None,
                    tags: [
                        0,
                        0,
                        1,
                        1,
                        2,
                        2,
                        3,
                        3
                    ],
                    field_type: Some(
                        POINT
                    ),
                    geometry: [
                        9,
                        2504,
                        3390
                    ],
                    unknown_fields: UnknownFields {
                        fields: None
                    },
                    cached_size: Cell {
                        value: 0
                    }
                }
            ],
            keys: [
                "fid",
                "scalerank",
                "name",
                "pop_max"
            ],
            values: [
                Tile_Value {
                    string_value: None,
                    float_value: None,
                    double_value: None,
                    int_value: Some(
                        106
                    ),
                    uint_value: None,
                    sint_value: None,
                    bool_value: None,
                    unknown_fields: UnknownFields {
                        fields: None
                    },
                    cached_size: Cell {
                        value: 0
                    }
                },
                Tile_Value {
                    string_value: None,
                    float_value: None,
                    double_value: Some(
                        10
                    ),
                    int_value: None,
                    uint_value: None,
                    sint_value: None,
                    bool_value: None,
                    unknown_fields: UnknownFields {
                        fields: None
                    },
                    cached_size: Cell {
                        value: 0
                    }
                },
                Tile_Value {
                    string_value: Some("Delemont"),
                    float_value: None,
                    double_value: None,
                    int_value: None,
                    uint_value: None,
                    sint_value: None,
                    bool_value: None,
                    unknown_fields: UnknownFields {
                        fields: None
                    },
                    cached_size: Cell {
                        value: 0
                    }
                },
                Tile_Value {
                    string_value: None,
                    float_value: None,
                    double_value: Some(
                        11315
                    ),
                    int_value: None,
                    uint_value: None,
                    sint_value: None,
                    bool_value: None,
                    unknown_fields: UnknownFields {
                        fields: None
                    },
                    cached_size: Cell {
                        value: 0
                    }
                }
            ],
            extent: Some(
                4096
            ),
            unknown_fields: UnknownFields {
                fields: None
            },
            cached_size: Cell {
                value: 0
            }
        }
    ],
    unknown_fields: UnknownFields {
        fields: None
    },
    cached_size: Cell {
        value: 0
    }
}"#;
    assert_eq!(expected, &*format!("{:#?}", mvt_tile));
}

#[test]
#[ignore]
fn test_projected_extent() {
    let service = mvt_service();

    let extent_wgs84 = Extent {
        minx: 4.0,
        miny: 52.0,
        maxx: 5.0,
        maxy: 53.0,
    };
    let extent_3857 = Extent {
        minx: 445277.9631730949,
        miny: 6800125.454397307,
        maxx: 556597.4539663672,
        maxy: 6982997.920389788,
    };

    assert_eq!(service.extent_from_wgs84(&extent_wgs84), extent_3857);
}

#[test]
#[ignore]
fn test_generate() {
    let service = mvt_service();

    // Single tile level 23
    let extent = Extent {
        minx: 9.43743,
        miny: 47.05001,
        maxx: 9.43751,
        maxy: 47.05006,
    };

    assert_eq!(service.grid.maxzoom(), 22);
    service.generate(Some("points"),
                     Some(20),
                     Some(23),
                     Some(extent),
                     None,
                     None,
                     false,
                     false);
}

#[test]
fn test_mvt_metadata() {
    use core::read_config;

    let config = read_config("src/test/example.toml").unwrap();
    let service = MvtService::from_config(&config).unwrap();

    let metadata = format!("{:#}", service.get_mvt_metadata().unwrap());
    let expected = r#"{
  "tilesets": [
    {
      "bounds": [
        -180.0,
        -90.0,
        180.0,
        90.0
      ],
      "layers": [
        {
          "geometry_type": "POINT",
          "name": "points"
        },
        {
          "geometry_type": "POLYGON",
          "name": "buildings"
        },
        {
          "geometry_type": "POLYGON",
          "name": "admin_0_countries"
        }
      ],
      "name": "osm",
      "supported": true,
      "tilejson": "osm.json",
      "tileurl": "/osm/{z}/{x}/{y}.pbf"
    }
  ]
}"#;
    println!("{}", metadata);
    assert_eq!(metadata, expected);
}

#[test]
#[ignore]
fn test_tilejson() {
    use core::read_config;
    use std::env;

    if env::var("DBCONN").is_err() {
        panic!("DBCONN undefined");
    }

    let config = read_config("src/test/example.toml").unwrap();
    let mut service = MvtService::from_config(&config).unwrap();
    service.connect();
    service.prepare_feature_queries();
    let metadata = format!("{:#}", service.get_tilejson("http://127.0.0.1", "osm").unwrap());
    println!("{}", metadata);
    let expected = r#"{
  "attribution": "",
  "basename": "osm",
  "bounds": [
    -180.0,
    -90.0,
    180.0,
    90.0
  ],
  "center": [
    0.0,
    0.0,
    2
  ],
  "description": "osm",
  "format": "pbf",
  "id": "osm",
  "maxzoom": 22,
  "minzoom": 0,
  "name": "osm",
  "scheme": "xyz",
  "tiles": [
    "http://127.0.0.1/osm/{z}/{x}/{y}.pbf"
  ],
  "vector_layers": [
    {
      "description": "",
      "fields": {
        "fid": "",
        "name": "",
        "pop_max": "",
        "scalerank": ""
      },
      "id": "points",
      "maxzoom": 22,
      "minzoom": 0
    },
    {
      "description": "",
      "fields": {},
      "id": "buildings",
      "maxzoom": 22,
      "minzoom": 0
    },
    {
      "description": "",
      "fields": {
        "fid": "",
        "iso_a3": "",
        "name": ""
      },
      "id": "admin_0_countries",
      "maxzoom": 22,
      "minzoom": 0
    }
  ],
  "version": "2.0.0"
}"#;
    assert_eq!(metadata, expected);
}

#[test]
fn test_stylejson() {
    use core::read_config;

    let config = read_config("src/test/example.toml").unwrap();
    let service = MvtService::from_config(&config).unwrap();
    let json = format!("{:#}", service.get_stylejson("http://127.0.0.1", "osm").unwrap());
    println!("{}", json);
    let expected = r#"
  "name": "t-rex",
  "sources": {
    "osm": {
      "type": "vector",
      "url": "http://127.0.0.1/osm.json"
    }
  },
  "version": 8
"#;
    assert!(json.contains(expected));
    let expected = r#"
  "layers": [
    {
      "id": "background_",
      "paint": {
        "background-color": "rgba(255, 255, 255, 1)"
      },
      "type": "background"
    },
    {
      "id": "points","#;
    assert!(json.contains(expected));

    let expected = r##"
      "paint": {
        "fill-color": "#d8e8c8",
        "fill-opacity": 0.5
      },"##;
    assert!(json.contains(expected));

    let expected = r#"
      "id": "buildings","#;
    assert!(json.contains(expected));
}

#[test]
#[ignore]
fn test_mbtiles_metadata() {
    use core::read_config;
    use std::env;

    if env::var("DBCONN").is_err() {
        panic!("DBCONN undefined");
    }

    let config = read_config("src/test/example.toml").unwrap();
    let mut service = MvtService::from_config(&config).unwrap();
    service.connect();
    let metadata = format!("{:#}", service.get_mbtiles_metadata("osm").unwrap());
    println!("{}", metadata);
    let expected = r#"{
  "attribution": "",
  "basename": "osm",
  "bounds": "[-180.0,-90.0,180.0,90.0]",
  "center": "[0.0,0.0,2]",
  "description": "osm",
  "format": "pbf",
  "id": "osm",
  "json": "{\"Layer\":[{\"description\":\"\",\"fields\":{\"fid\":\"\",\"name\":\"\",\"pop_max\":\"\",\"scalerank\":\"\"},\"id\":\"points\",\"name\":\"points\",\"properties\":{\"buffer-size\":0,\"maxzoom\":22,\"minzoom\":0},\"srs\":\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over\"},{\"description\":\"\",\"fields\":{},\"id\":\"buildings\",\"name\":\"buildings\",\"properties\":{\"buffer-size\":10,\"maxzoom\":22,\"minzoom\":0},\"srs\":\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over\"},{\"description\":\"\",\"fields\":{\"fid\":\"\",\"iso_a3\":\"\",\"name\":\"\"},\"id\":\"admin_0_countries\",\"name\":\"admin_0_countries\",\"properties\":{\"buffer-size\":1,\"maxzoom\":22,\"minzoom\":0},\"srs\":\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over\"}],\"vector_layers\":[{\"description\":\"\",\"fields\":{\"fid\":\"\",\"name\":\"\",\"pop_max\":\"\",\"scalerank\":\"\"},\"id\":\"points\",\"maxzoom\":22,\"minzoom\":0},{\"description\":\"\",\"fields\":{},\"id\":\"buildings\",\"maxzoom\":22,\"minzoom\":0},{\"description\":\"\",\"fields\":{\"fid\":\"\",\"iso_a3\":\"\",\"name\":\"\"},\"id\":\"admin_0_countries\",\"maxzoom\":22,\"minzoom\":0}]}",
  "maxzoom": 22,
  "minzoom": 0,
  "name": "osm",
  "scheme": "xyz",
  "version": "2.0.0"
}"#;
    assert_eq!(metadata, expected);
}

#[test]
fn test_gen_config() {
    let expected = r#"# t-rex configuration

[service.mvt]
viewer = true

[datasource]
type = "postgis"
# Connection specification (https://github.com/sfackler/rust-postgres#connecting)
url = "postgresql://user:pass@host/database"

[grid]
# Predefined grids: web_mercator, wgs84
predefined = "web_mercator"

[[tileset]]
name = "points"

[[tileset.layer]]
name = "points"
table_name = "mytable"
geometry_field = "wkb_geometry"
geometry_type = "POINT"
#fid_field = "id"
#simplify = true
#buffer_size = 10
#[[tileset.layer.query]]
#minzoom = 0
#maxzoom = 22
#sql = "SELECT name,wkb_geometry FROM mytable"

#[cache.file]
#base = "/tmp/mvtcache"
#baseurl = "http://example.com/tiles"
"#;
    println!("{}", &MvtService::gen_config());
    assert_eq!(expected, &MvtService::gen_config());
}
<commit_msg>Handle floating point difference on x86_64-apple-darwin<commit_after>//
// Copyright (c) Pirmin Kalberer. All rights reserved.
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
//

use datasource::PostgisInput;
use core::grid::Grid;
use core::grid::Extent;
use core::layer::Layer;
use core::Config;
use cache::{Tilecache, Nocache};
use service::mvt::{Tileset, MvtService};

fn mvt_service() -> MvtService {
    use std::env;

    let pg: PostgisInput = match env::var("DBCONN") {
            Result::Ok(val) => Some(PostgisInput::new(&val).connected()),
            Result::Err(_) => panic!("DBCONN undefined"),
        }
        .unwrap();
    let grid = Grid::web_mercator();
    let mut layer = Layer::new("points");
    layer.table_name = Some(String::from("ne_10m_populated_places"));
    layer.geometry_field = Some(String::from("wkb_geometry"));
    layer.geometry_type = Some(String::from("POINT"));
    layer.query_limit = Some(1);
    let tileset = Tileset {
        name: "points".to_string(),
        extent: Some(Extent {
                         minx: -179.58998,
                         miny: -90.00000,
                         maxx: 179.38330,
                         maxy: 82.48332,
                     }),
        layers: vec![layer],
    };
    let mut service = MvtService {
        input: pg,
        grid: grid,
        tilesets: vec![tileset],
        cache: Tilecache::Nocache(Nocache),
    };
    service.prepare_feature_queries();
    service
}

#[test]
#[ignore]
fn test_tile_query() {
    let service = mvt_service();

    let mvt_tile = service.tile("points", 33, 41, 6);
    println!("{:#?}", mvt_tile);
    let expected = r#"Tile {
    layers: [
        Tile_Layer {
            version: Some(
                2
            ),
            name: Some("points"),
            features: [
                Tile_Feature {
                    id: None,
                    tags: [
                        0,
                        0,
                        1,
                        1,
                        2,
                        2,
                        3,
                        3
                    ],
                    field_type: Some(
                        POINT
                    ),
                    geometry: [
                        9,
                        2504,
                        3390
                    ],
                    unknown_fields: UnknownFields {
                        fields: None
                    },
                    cached_size: Cell {
                        value: 0
                    }
                }
            ],
            keys: [
                "fid",
                "scalerank",
                "name",
                "pop_max"
            ],
            values: [
                Tile_Value {
                    string_value: None,
                    float_value: None,
                    double_value: None,
                    int_value: Some(
                        106
                    ),
                    uint_value: None,
                    sint_value: None,
                    bool_value: None,
                    unknown_fields: UnknownFields {
                        fields: None
                    },
                    cached_size: Cell {
                        value: 0
                    }
                },
                Tile_Value {
                    string_value: None,
                    float_value: None,
                    double_value: Some(
                        10
                    ),
                    int_value: None,
                    uint_value: None,
                    sint_value: None,
                    bool_value: None,
                    unknown_fields: UnknownFields {
                        fields: None
                    },
                    cached_size: Cell {
                        value: 0
                    }
                },
                Tile_Value {
                    string_value: Some("Delemont"),
                    float_value: None,
                    double_value: None,
                    int_value: None,
                    uint_value: None,
                    sint_value: None,
                    bool_value: None,
                    unknown_fields: UnknownFields {
                        fields: None
                    },
                    cached_size: Cell {
                        value: 0
                    }
                },
                Tile_Value {
                    string_value: None,
                    float_value: None,
                    double_value: Some(
                        11315
                    ),
                    int_value: None,
                    uint_value: None,
                    sint_value: None,
                    bool_value: None,
                    unknown_fields: UnknownFields {
                        fields: None
                    },
                    cached_size: Cell {
                        value: 0
                    }
                }
            ],
            extent: Some(
                4096
            ),
            unknown_fields: UnknownFields {
                fields: None
            },
            cached_size: Cell {
                value: 0
            }
        }
    ],
    unknown_fields: UnknownFields {
        fields: None
    },
    cached_size: Cell {
        value: 0
    }
}"#;
    assert_eq!(expected, &*format!("{:#?}", mvt_tile));
}

#[test]
#[ignore]
fn test_projected_extent() {
    let service = mvt_service();

    let extent_wgs84 = Extent {
        minx: 4.0,
        miny: 52.0,
        maxx: 5.0,
        maxy: 53.0,
    };
    #[cfg(not(target_os = "macos"))]
    let extent_3857 = Extent {
        minx: 445277.9631730949,
        miny: 6800125.454397307,
        maxx: 556597.4539663672,
        maxy: 6982997.920389788,
    };
    #[cfg(target_os = "macos")]
    let extent_3857 = Extent {
        minx: 445277.9631730949,
        miny: 6800125.454397305,
        maxx: 556597.4539663672,
        maxy: 6982997.920389788,
    };

    assert_eq!(service.extent_from_wgs84(&extent_wgs84), extent_3857);
}

#[test]
#[ignore]
fn test_generate() {
    let service = mvt_service();

    // Single tile level 23
    let extent = Extent {
        minx: 9.43743,
        miny: 47.05001,
        maxx: 9.43751,
        maxy: 47.05006,
    };

    assert_eq!(service.grid.maxzoom(), 22);
    service.generate(Some("points"),
                     Some(20),
                     Some(23),
                     Some(extent),
                     None,
                     None,
                     false,
                     false);
}

#[test]
fn test_mvt_metadata() {
    use core::read_config;

    let config = read_config("src/test/example.toml").unwrap();
    let service = MvtService::from_config(&config).unwrap();

    let metadata = format!("{:#}", service.get_mvt_metadata().unwrap());
    let expected = r#"{
  "tilesets": [
    {
      "bounds": [
        -180.0,
        -90.0,
        180.0,
        90.0
      ],
      "layers": [
        {
          "geometry_type": "POINT",
          "name": "points"
        },
        {
          "geometry_type": "POLYGON",
          "name": "buildings"
        },
        {
          "geometry_type": "POLYGON",
          "name": "admin_0_countries"
        }
      ],
      "name": "osm",
      "supported": true,
      "tilejson": "osm.json",
      "tileurl": "/osm/{z}/{x}/{y}.pbf"
    }
  ]
}"#;
    println!("{}", metadata);
    assert_eq!(metadata, expected);
}

#[test]
#[ignore]
fn test_tilejson() {
    use core::read_config;
    use std::env;

    if env::var("DBCONN").is_err() {
        panic!("DBCONN undefined");
    }

    let config = read_config("src/test/example.toml").unwrap();
    let mut service = MvtService::from_config(&config).unwrap();
    service.connect();
    service.prepare_feature_queries();
    let metadata = format!("{:#}", service.get_tilejson("http://127.0.0.1", "osm").unwrap());
    println!("{}", metadata);
    let expected = r#"{
  "attribution": "",
  "basename": "osm",
  "bounds": [
    -180.0,
    -90.0,
    180.0,
    90.0
  ],
  "center": [
    0.0,
    0.0,
    2
  ],
  "description": "osm",
  "format": "pbf",
  "id": "osm",
  "maxzoom": 22,
  "minzoom": 0,
  "name": "osm",
  "scheme": "xyz",
  "tiles": [
    "http://127.0.0.1/osm/{z}/{x}/{y}.pbf"
  ],
  "vector_layers": [
    {
      "description": "",
      "fields": {
        "fid": "",
        "name": "",
        "pop_max": "",
        "scalerank": ""
      },
      "id": "points",
      "maxzoom": 22,
      "minzoom": 0
    },
    {
      "description": "",
      "fields": {},
      "id": "buildings",
      "maxzoom": 22,
      "minzoom": 0
    },
    {
      "description": "",
      "fields": {
        "fid": "",
        "iso_a3": "",
        "name": ""
      },
      "id": "admin_0_countries",
      "maxzoom": 22,
      "minzoom": 0
    }
  ],
  "version": "2.0.0"
}"#;
    assert_eq!(metadata, expected);
}

#[test]
fn test_stylejson() {
    use core::read_config;

    let config = read_config("src/test/example.toml").unwrap();
    let service = MvtService::from_config(&config).unwrap();
    let json = format!("{:#}", service.get_stylejson("http://127.0.0.1", "osm").unwrap());
    println!("{}", json);
    let expected = r#"
  "name": "t-rex",
  "sources": {
    "osm": {
      "type": "vector",
      "url": "http://127.0.0.1/osm.json"
    }
  },
  "version": 8
"#;
    assert!(json.contains(expected));
    let expected = r#"
  "layers": [
    {
      "id": "background_",
      "paint": {
        "background-color": "rgba(255, 255, 255, 1)"
      },
      "type": "background"
    },
    {
      "id": "points","#;
    assert!(json.contains(expected));

    let expected = r##"
      "paint": {
        "fill-color": "#d8e8c8",
        "fill-opacity": 0.5
      },"##;
    assert!(json.contains(expected));

    let expected = r#"
      "id": "buildings","#;
    assert!(json.contains(expected));
}

#[test]
#[ignore]
fn test_mbtiles_metadata() {
    use core::read_config;
    use std::env;

    if env::var("DBCONN").is_err() {
        panic!("DBCONN undefined");
    }

    let config = read_config("src/test/example.toml").unwrap();
    let mut service = MvtService::from_config(&config).unwrap();
    service.connect();
    let metadata = format!("{:#}", service.get_mbtiles_metadata("osm").unwrap());
    println!("{}", metadata);
    let expected = r#"{
  "attribution": "",
  "basename": "osm",
  "bounds": "[-180.0,-90.0,180.0,90.0]",
  "center": "[0.0,0.0,2]",
  "description": "osm",
  "format": "pbf",
  "id": "osm",
  "json": "{\"Layer\":[{\"description\":\"\",\"fields\":{\"fid\":\"\",\"name\":\"\",\"pop_max\":\"\",\"scalerank\":\"\"},\"id\":\"points\",\"name\":\"points\",\"properties\":{\"buffer-size\":0,\"maxzoom\":22,\"minzoom\":0},\"srs\":\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over\"},{\"description\":\"\",\"fields\":{},\"id\":\"buildings\",\"name\":\"buildings\",\"properties\":{\"buffer-size\":10,\"maxzoom\":22,\"minzoom\":0},\"srs\":\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over\"},{\"description\":\"\",\"fields\":{\"fid\":\"\",\"iso_a3\":\"\",\"name\":\"\"},\"id\":\"admin_0_countries\",\"name\":\"admin_0_countries\",\"properties\":{\"buffer-size\":1,\"maxzoom\":22,\"minzoom\":0},\"srs\":\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over\"}],\"vector_layers\":[{\"description\":\"\",\"fields\":{\"fid\":\"\",\"name\":\"\",\"pop_max\":\"\",\"scalerank\":\"\"},\"id\":\"points\",\"maxzoom\":22,\"minzoom\":0},{\"description\":\"\",\"fields\":{},\"id\":\"buildings\",\"maxzoom\":22,\"minzoom\":0},{\"description\":\"\",\"fields\":{\"fid\":\"\",\"iso_a3\":\"\",\"name\":\"\"},\"id\":\"admin_0_countries\",\"maxzoom\":22,\"minzoom\":0}]}",
  "maxzoom": 22,
  "minzoom": 0,
  "name": "osm",
  "scheme": "xyz",
  "version": "2.0.0"
}"#;
    assert_eq!(metadata, expected);
}

#[test]
fn test_gen_config() {
    let expected = r#"# t-rex configuration

[service.mvt]
viewer = true

[datasource]
type = "postgis"
# Connection specification (https://github.com/sfackler/rust-postgres#connecting)
url = "postgresql://user:pass@host/database"

[grid]
# Predefined grids: web_mercator, wgs84
predefined = "web_mercator"

[[tileset]]
name = "points"

[[tileset.layer]]
name = "points"
table_name = "mytable"
geometry_field = "wkb_geometry"
geometry_type = "POINT"
#fid_field = "id"
#simplify = true
#buffer_size = 10
#[[tileset.layer.query]]
#minzoom = 0
#maxzoom = 22
#sql = "SELECT name,wkb_geometry FROM mytable"

#[cache.file]
#base = "/tmp/mvtcache"
#baseurl = "http://example.com/tiles"
"#;
    println!("{}", &MvtService::gen_config());
    assert_eq!(expected, &MvtService::gen_config());
}
<|endoftext|>


<commit_before>use rustc_middle::mir;
use rustc_target::spec::abi::Abi;

use crate::*;
use shims::unix::freebsd::dlsym as freebsd;
use shims::unix::linux::dlsym as linux;
use shims::unix::macos::dlsym as macos;

#[derive(Debug, Copy, Clone)]
pub enum Dlsym {
    Linux(linux::Dlsym),
    MacOs(macos::Dlsym),
    FreeBSD(freebsd::Dlsym),
}

impl Dlsym {
    // Returns an error for unsupported symbols, and None if this symbol
    // should become a NULL pointer (pretend it does not exist).
    pub fn from_str<'tcx>(name: &str, target_os: &str) -> InterpResult<'tcx, Option<Dlsym>> {
        Ok(match target_os {
            "linux" => linux::Dlsym::from_str(name)?.map(Dlsym::Linux),
            "macos" => macos::Dlsym::from_str(name)?.map(Dlsym::MacOs),
            "freebsd" => freebsd::Dlsym::from_str(name)?.map(Dlsym::FreeBSD),
            _ => unreachable!(),
        })
    }
}

impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn call_dlsym(
        &mut self,
        dlsym: Dlsym,
        abi: Abi,
        args: &[OpTy<'tcx, Tag>],
        dest: &PlaceTy<'tcx, Tag>,
        ret: Option<mir::BasicBlock>,
    ) -> InterpResult<'tcx> {
        let this = self.eval_context_mut();

        this.check_abi(abi, Abi::C { unwind: false })?;

        match dlsym {
            Dlsym::Linux(dlsym) => linux::EvalContextExt::call_dlsym(this, dlsym, args, dest, ret),
            Dlsym::MacOs(dlsym) => macos::EvalContextExt::call_dlsym(this, dlsym, args, dest, ret),
            Dlsym::FreeBSD(dlsym) =>
                freebsd::EvalContextExt::call_dlsym(this, dlsym, args, dest, ret),
        }
    }
}
<commit_msg>use Rust SnakeCase<commit_after>use rustc_middle::mir;
use rustc_target::spec::abi::Abi;

use crate::*;
use shims::unix::freebsd::dlsym as freebsd;
use shims::unix::linux::dlsym as linux;
use shims::unix::macos::dlsym as macos;

#[derive(Debug, Copy, Clone)]
pub enum Dlsym {
    Linux(linux::Dlsym),
    MacOs(macos::Dlsym),
    FreeBsd(freebsd::Dlsym),
}

impl Dlsym {
    // Returns an error for unsupported symbols, and None if this symbol
    // should become a NULL pointer (pretend it does not exist).
    pub fn from_str<'tcx>(name: &str, target_os: &str) -> InterpResult<'tcx, Option<Dlsym>> {
        Ok(match target_os {
            "linux" => linux::Dlsym::from_str(name)?.map(Dlsym::Linux),
            "macos" => macos::Dlsym::from_str(name)?.map(Dlsym::MacOs),
            "freebsd" => freebsd::Dlsym::from_str(name)?.map(Dlsym::FreeBsd),
            _ => unreachable!(),
        })
    }
}

impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn call_dlsym(
        &mut self,
        dlsym: Dlsym,
        abi: Abi,
        args: &[OpTy<'tcx, Tag>],
        dest: &PlaceTy<'tcx, Tag>,
        ret: Option<mir::BasicBlock>,
    ) -> InterpResult<'tcx> {
        let this = self.eval_context_mut();

        this.check_abi(abi, Abi::C { unwind: false })?;

        match dlsym {
            Dlsym::Linux(dlsym) => linux::EvalContextExt::call_dlsym(this, dlsym, args, dest, ret),
            Dlsym::MacOs(dlsym) => macos::EvalContextExt::call_dlsym(this, dlsym, args, dest, ret),
            Dlsym::FreeBsd(dlsym) =>
                freebsd::EvalContextExt::call_dlsym(this, dlsym, args, dest, ret),
        }
    }
}
<|endoftext|>


<commit_before>extern crate libc;

use std::ffi::OsStr;
use std::io;
use std::mem;
use std::ptr;
use std::time::Duration;

use std::os::windows::prelude::*;

use self::libc::c_void;

use super::ffi::*;
use ::{SerialDevice,SerialPortSettings};

/// A serial port implementation for Windows COM ports.
pub struct COMPort {
    handle: HANDLE,
    timeout: Duration
}

impl COMPort {
    /// Opens a COM port as a serial device.
    ///
    /// `port` should be the name of a COM port, e.g., `COM1`.
    ///
    /// ```no_run
    /// serial::windows::COMPort::open("COM1").unwrap();
    /// ```
    ///
    /// ## Errors
    ///
    /// * `NoDevice` if the device could not be opened. This could indicate that the device is
    ///   already in use.
    /// * `InvalidInput` if `port` is not a valid device name.
    /// * `Io` for any other I/O error while opening or initializing the device.
    pub fn open<T: AsRef<OsStr> + ?Sized>(port: &T) -> ::Result<Self> {
        let mut name: Vec<u16> = port.as_ref().encode_wide().collect();
        name.push(0);

        let handle = unsafe {
            CreateFileW(name.as_ptr(), GENERIC_READ | GENERIC_WRITE, 0, ptr::null_mut(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 as HANDLE)
        };

        let timeout = Duration::from_millis(100);

        if handle != INVALID_HANDLE_VALUE {
            let mut port = COMPort {
                handle: handle,
                timeout: timeout
            };

            try!(port.set_timeout(timeout));
            Ok(port)
        }
        else {
            Err(super::error::last_os_error())
        }
    }

    fn escape_comm_function(&mut self, function: DWORD) -> ::Result<()> {
        match unsafe { EscapeCommFunction(self.handle, function) } {
            0 => Err(super::error::last_os_error()),
            _ => Ok(())
        }
    }

    fn read_pin(&mut self, pin: DWORD) -> ::Result<bool> {
        let mut status: DWORD = unsafe { mem::uninitialized() };

        match unsafe { GetCommModemStatus(self.handle, &mut status) } {
            0 => Err(super::error::last_os_error()),
            _ => Ok(status & pin != 0)
        }
    }
}

impl Drop for COMPort {
    fn drop(&mut self) {
        unsafe {
            CloseHandle(self.handle);
        }
    }
}

impl AsRawHandle for COMPort {
    fn as_raw_handle(&self) -> RawHandle {
        unsafe {
            mem::transmute(self.handle)
        }
    }
}

impl io::Read for COMPort {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let mut len: DWORD = 0;

        match unsafe { ReadFile(self.handle, buf.as_mut_ptr() as *mut c_void, buf.len() as DWORD, &mut len, ptr::null_mut()) } {
            0 => Err(io::Error::last_os_error()),
            _ => {
                if len != 0 {
                    Ok(len as usize)
                }
                else {
                    Err(io::Error::new(io::ErrorKind::TimedOut, "Operation timed out"))
                }
            }
        }
    }
}

impl io::Write for COMPort {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let mut len: DWORD = 0;

        match unsafe { WriteFile(self.handle, buf.as_ptr() as *mut c_void, buf.len() as DWORD, &mut len, ptr::null_mut()) } {
            0 => Err(io::Error::last_os_error()),
            _ => Ok(len as usize)
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match unsafe { FlushFileBuffers(self.handle) } {
            0 => Err(io::Error::last_os_error()),
            _ => Ok(())
        }
    }
}

impl SerialDevice for COMPort {
    type Settings = COMSettings;

    fn read_settings(&self) -> ::Result<COMSettings> {
        let mut dcb = DCB::new();

        match unsafe { GetCommState(self.handle, &mut dcb) } {
            0 => Err(super::error::last_os_error()),
            _ => Ok(COMSettings { inner: dcb })

        }
    }

    fn write_settings(&mut self, settings: &COMSettings) -> ::Result<()> {
        match unsafe { SetCommState(self.handle, &settings.inner) } {
            0 => Err(super::error::last_os_error()),
            _ => Ok(())
        }
    }

    fn timeout(&self) -> Duration {
        self.timeout
    }

    fn set_timeout(&mut self, timeout: Duration) -> ::Result<()> {
        let milliseconds = timeout.as_secs() * 1000 + timeout.subsec_nanos() as u64 / 1_000_000;

        let timeouts = COMMTIMEOUTS {
            ReadIntervalTimeout: 0,
            ReadTotalTimeoutMultiplier: 0,
            ReadTotalTimeoutConstant: milliseconds as DWORD,
            WriteTotalTimeoutMultiplier: 0,
            WriteTotalTimeoutConstant: 0
        };

        if unsafe { SetCommTimeouts(self.handle, &timeouts) } == 0 {
            return Err(super::error::last_os_error());
        }

        self.timeout = timeout;
        Ok(())
    }

    fn set_rts(&mut self, level: bool) -> ::Result<()> {
        if level {
            self.escape_comm_function(SETRTS)
        }
        else {
            self.escape_comm_function(CLRRTS)
        }
    }

    fn set_dtr(&mut self, level: bool) -> ::Result<()> {
        if level {
            self.escape_comm_function(SETDTR)
        }
        else {
            self.escape_comm_function(CLRDTR)
        }
    }

    fn read_cts(&mut self) -> ::Result<bool> {
        self.read_pin(MS_CTS_ON)
    }

    fn read_dsr(&mut self) -> ::Result<bool> {
        self.read_pin(MS_DSR_ON)
    }

    fn read_ri(&mut self) -> ::Result<bool> {
        self.read_pin(MS_RING_ON)
    }

    fn read_cd(&mut self) -> ::Result<bool> {
        self.read_pin(MS_RLSD_ON)
    }
}

/// Serial port settings for COM ports.
#[derive(Copy,Clone,Debug)]
pub struct COMSettings {
    inner: DCB
}

impl SerialPortSettings for COMSettings {
    fn baud_rate(&self) -> Option<::BaudRate> {
        match self.inner.BaudRate {
            CBR_110    => Some(::Baud110),
            CBR_300    => Some(::Baud300),
            CBR_600    => Some(::Baud600),
            CBR_1200   => Some(::Baud1200),
            CBR_2400   => Some(::Baud2400),
            CBR_4800   => Some(::Baud4800),
            CBR_9600   => Some(::Baud9600),
            CBR_14400  => Some(::BaudOther(14400)),
            CBR_19200  => Some(::Baud19200),
            CBR_38400  => Some(::Baud38400),
            CBR_56000  => Some(::BaudOther(56000)),
            CBR_57600  => Some(::Baud57600),
            CBR_115200 => Some(::Baud115200),
            CBR_128000 => Some(::BaudOther(128000)),
            CBR_256000 => Some(::BaudOther(256000)),
            n          => Some(::BaudOther(n as usize))
        }
    }

    fn char_size(&self) -> Option<::CharSize> {
        match self.inner.ByteSize {
            5 => Some(::Bits5),
            6 => Some(::Bits6),
            7 => Some(::Bits7),
            8 => Some(::Bits8),
            _ => None
        }
    }

    fn parity(&self) -> Option<::Parity> {
        match self.inner.Parity {
            ODDPARITY  => Some(::ParityOdd),
            EVENPARITY => Some(::ParityEven),
            NOPARITY   => Some(::ParityNone),
            _          => None
        }
    }

    fn stop_bits(&self) -> Option<::StopBits> {
        match self.inner.StopBits {
            TWOSTOPBITS => Some(::Stop2),
            ONESTOPBIT  => Some(::Stop1),
            _           => None
        }
    }

    fn flow_control(&self) -> Option<::FlowControl> {
        if self.inner.fBits & (fOutxCtsFlow | fRtsControl) != 0 {
            Some(::FlowHardware)
        }
        else if self.inner.fBits & (fOutX | fInX) != 0 {
            Some(::FlowSoftware)
        }
        else {
            Some(::FlowNone)
        }
    }

    fn set_baud_rate(&mut self, baud_rate: ::BaudRate) -> ::Result<()> {
        self.inner.BaudRate = match baud_rate {
            ::Baud110      => CBR_110,
            ::Baud300      => CBR_300,
            ::Baud600      => CBR_600,
            ::Baud1200     => CBR_1200,
            ::Baud2400     => CBR_2400,
            ::Baud4800     => CBR_4800,
            ::Baud9600     => CBR_9600,
            ::Baud19200    => CBR_19200,
            ::Baud38400    => CBR_38400,
            ::Baud57600    => CBR_57600,
            ::Baud115200   => CBR_115200,
            ::BaudOther(n) => n as DWORD
        };

        Ok(())
    }

    fn set_char_size(&mut self, char_size: ::CharSize) {
        self.inner.ByteSize = match char_size {
            ::Bits5 => 5,
            ::Bits6 => 6,
            ::Bits7 => 7,
            ::Bits8 => 8
        }
    }

    fn set_parity(&mut self, parity: ::Parity) {
        self.inner.Parity = match parity {
            ::ParityNone => NOPARITY,
            ::ParityOdd  => ODDPARITY,
            ::ParityEven => EVENPARITY
        }
    }

    fn set_stop_bits(&mut self, stop_bits: ::StopBits) {
        self.inner.StopBits = match stop_bits {
            ::Stop1 => ONESTOPBIT,
            ::Stop2 => TWOSTOPBITS
        }
    }

    fn set_flow_control(&mut self, flow_control: ::FlowControl) {
        match flow_control {
            ::FlowNone => {
                self.inner.fBits &= !(fOutxCtsFlow | fRtsControl);
                self.inner.fBits &= !(fOutX | fInX);
            },
            ::FlowSoftware => {
                self.inner.fBits &= !(fOutxCtsFlow | fRtsControl);
                self.inner.fBits |= fOutX | fInX;
            },
            ::FlowHardware => {
                self.inner.fBits |= fOutxCtsFlow | fRtsControl;
                self.inner.fBits &= !(fOutX | fInX);
            }
        }
    }
}
<commit_msg>[#16] implements Send for COMPort<commit_after>extern crate libc;

use std::ffi::OsStr;
use std::io;
use std::mem;
use std::ptr;
use std::time::Duration;

use std::os::windows::prelude::*;

use self::libc::c_void;

use super::ffi::*;
use ::{SerialDevice,SerialPortSettings};

/// A serial port implementation for Windows COM ports.
pub struct COMPort {
    handle: HANDLE,
    timeout: Duration
}

unsafe impl Send for COMPort {}

impl COMPort {
    /// Opens a COM port as a serial device.
    ///
    /// `port` should be the name of a COM port, e.g., `COM1`.
    ///
    /// ```no_run
    /// serial::windows::COMPort::open("COM1").unwrap();
    /// ```
    ///
    /// ## Errors
    ///
    /// * `NoDevice` if the device could not be opened. This could indicate that the device is
    ///   already in use.
    /// * `InvalidInput` if `port` is not a valid device name.
    /// * `Io` for any other I/O error while opening or initializing the device.
    pub fn open<T: AsRef<OsStr> + ?Sized>(port: &T) -> ::Result<Self> {
        let mut name: Vec<u16> = port.as_ref().encode_wide().collect();
        name.push(0);

        let handle = unsafe {
            CreateFileW(name.as_ptr(), GENERIC_READ | GENERIC_WRITE, 0, ptr::null_mut(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 as HANDLE)
        };

        let timeout = Duration::from_millis(100);

        if handle != INVALID_HANDLE_VALUE {
            let mut port = COMPort {
                handle: handle,
                timeout: timeout
            };

            try!(port.set_timeout(timeout));
            Ok(port)
        }
        else {
            Err(super::error::last_os_error())
        }
    }

    fn escape_comm_function(&mut self, function: DWORD) -> ::Result<()> {
        match unsafe { EscapeCommFunction(self.handle, function) } {
            0 => Err(super::error::last_os_error()),
            _ => Ok(())
        }
    }

    fn read_pin(&mut self, pin: DWORD) -> ::Result<bool> {
        let mut status: DWORD = unsafe { mem::uninitialized() };

        match unsafe { GetCommModemStatus(self.handle, &mut status) } {
            0 => Err(super::error::last_os_error()),
            _ => Ok(status & pin != 0)
        }
    }
}

impl Drop for COMPort {
    fn drop(&mut self) {
        unsafe {
            CloseHandle(self.handle);
        }
    }
}

impl AsRawHandle for COMPort {
    fn as_raw_handle(&self) -> RawHandle {
        unsafe {
            mem::transmute(self.handle)
        }
    }
}

impl io::Read for COMPort {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let mut len: DWORD = 0;

        match unsafe { ReadFile(self.handle, buf.as_mut_ptr() as *mut c_void, buf.len() as DWORD, &mut len, ptr::null_mut()) } {
            0 => Err(io::Error::last_os_error()),
            _ => {
                if len != 0 {
                    Ok(len as usize)
                }
                else {
                    Err(io::Error::new(io::ErrorKind::TimedOut, "Operation timed out"))
                }
            }
        }
    }
}

impl io::Write for COMPort {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let mut len: DWORD = 0;

        match unsafe { WriteFile(self.handle, buf.as_ptr() as *mut c_void, buf.len() as DWORD, &mut len, ptr::null_mut()) } {
            0 => Err(io::Error::last_os_error()),
            _ => Ok(len as usize)
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match unsafe { FlushFileBuffers(self.handle) } {
            0 => Err(io::Error::last_os_error()),
            _ => Ok(())
        }
    }
}

impl SerialDevice for COMPort {
    type Settings = COMSettings;

    fn read_settings(&self) -> ::Result<COMSettings> {
        let mut dcb = DCB::new();

        match unsafe { GetCommState(self.handle, &mut dcb) } {
            0 => Err(super::error::last_os_error()),
            _ => Ok(COMSettings { inner: dcb })

        }
    }

    fn write_settings(&mut self, settings: &COMSettings) -> ::Result<()> {
        match unsafe { SetCommState(self.handle, &settings.inner) } {
            0 => Err(super::error::last_os_error()),
            _ => Ok(())
        }
    }

    fn timeout(&self) -> Duration {
        self.timeout
    }

    fn set_timeout(&mut self, timeout: Duration) -> ::Result<()> {
        let milliseconds = timeout.as_secs() * 1000 + timeout.subsec_nanos() as u64 / 1_000_000;

        let timeouts = COMMTIMEOUTS {
            ReadIntervalTimeout: 0,
            ReadTotalTimeoutMultiplier: 0,
            ReadTotalTimeoutConstant: milliseconds as DWORD,
            WriteTotalTimeoutMultiplier: 0,
            WriteTotalTimeoutConstant: 0
        };

        if unsafe { SetCommTimeouts(self.handle, &timeouts) } == 0 {
            return Err(super::error::last_os_error());
        }

        self.timeout = timeout;
        Ok(())
    }

    fn set_rts(&mut self, level: bool) -> ::Result<()> {
        if level {
            self.escape_comm_function(SETRTS)
        }
        else {
            self.escape_comm_function(CLRRTS)
        }
    }

    fn set_dtr(&mut self, level: bool) -> ::Result<()> {
        if level {
            self.escape_comm_function(SETDTR)
        }
        else {
            self.escape_comm_function(CLRDTR)
        }
    }

    fn read_cts(&mut self) -> ::Result<bool> {
        self.read_pin(MS_CTS_ON)
    }

    fn read_dsr(&mut self) -> ::Result<bool> {
        self.read_pin(MS_DSR_ON)
    }

    fn read_ri(&mut self) -> ::Result<bool> {
        self.read_pin(MS_RING_ON)
    }

    fn read_cd(&mut self) -> ::Result<bool> {
        self.read_pin(MS_RLSD_ON)
    }
}

/// Serial port settings for COM ports.
#[derive(Copy,Clone,Debug)]
pub struct COMSettings {
    inner: DCB
}

impl SerialPortSettings for COMSettings {
    fn baud_rate(&self) -> Option<::BaudRate> {
        match self.inner.BaudRate {
            CBR_110    => Some(::Baud110),
            CBR_300    => Some(::Baud300),
            CBR_600    => Some(::Baud600),
            CBR_1200   => Some(::Baud1200),
            CBR_2400   => Some(::Baud2400),
            CBR_4800   => Some(::Baud4800),
            CBR_9600   => Some(::Baud9600),
            CBR_14400  => Some(::BaudOther(14400)),
            CBR_19200  => Some(::Baud19200),
            CBR_38400  => Some(::Baud38400),
            CBR_56000  => Some(::BaudOther(56000)),
            CBR_57600  => Some(::Baud57600),
            CBR_115200 => Some(::Baud115200),
            CBR_128000 => Some(::BaudOther(128000)),
            CBR_256000 => Some(::BaudOther(256000)),
            n          => Some(::BaudOther(n as usize))
        }
    }

    fn char_size(&self) -> Option<::CharSize> {
        match self.inner.ByteSize {
            5 => Some(::Bits5),
            6 => Some(::Bits6),
            7 => Some(::Bits7),
            8 => Some(::Bits8),
            _ => None
        }
    }

    fn parity(&self) -> Option<::Parity> {
        match self.inner.Parity {
            ODDPARITY  => Some(::ParityOdd),
            EVENPARITY => Some(::ParityEven),
            NOPARITY   => Some(::ParityNone),
            _          => None
        }
    }

    fn stop_bits(&self) -> Option<::StopBits> {
        match self.inner.StopBits {
            TWOSTOPBITS => Some(::Stop2),
            ONESTOPBIT  => Some(::Stop1),
            _           => None
        }
    }

    fn flow_control(&self) -> Option<::FlowControl> {
        if self.inner.fBits & (fOutxCtsFlow | fRtsControl) != 0 {
            Some(::FlowHardware)
        }
        else if self.inner.fBits & (fOutX | fInX) != 0 {
            Some(::FlowSoftware)
        }
        else {
            Some(::FlowNone)
        }
    }

    fn set_baud_rate(&mut self, baud_rate: ::BaudRate) -> ::Result<()> {
        self.inner.BaudRate = match baud_rate {
            ::Baud110      => CBR_110,
            ::Baud300      => CBR_300,
            ::Baud600      => CBR_600,
            ::Baud1200     => CBR_1200,
            ::Baud2400     => CBR_2400,
            ::Baud4800     => CBR_4800,
            ::Baud9600     => CBR_9600,
            ::Baud19200    => CBR_19200,
            ::Baud38400    => CBR_38400,
            ::Baud57600    => CBR_57600,
            ::Baud115200   => CBR_115200,
            ::BaudOther(n) => n as DWORD
        };

        Ok(())
    }

    fn set_char_size(&mut self, char_size: ::CharSize) {
        self.inner.ByteSize = match char_size {
            ::Bits5 => 5,
            ::Bits6 => 6,
            ::Bits7 => 7,
            ::Bits8 => 8
        }
    }

    fn set_parity(&mut self, parity: ::Parity) {
        self.inner.Parity = match parity {
            ::ParityNone => NOPARITY,
            ::ParityOdd  => ODDPARITY,
            ::ParityEven => EVENPARITY
        }
    }

    fn set_stop_bits(&mut self, stop_bits: ::StopBits) {
        self.inner.StopBits = match stop_bits {
            ::Stop1 => ONESTOPBIT,
            ::Stop2 => TWOSTOPBITS
        }
    }

    fn set_flow_control(&mut self, flow_control: ::FlowControl) {
        match flow_control {
            ::FlowNone => {
                self.inner.fBits &= !(fOutxCtsFlow | fRtsControl);
                self.inner.fBits &= !(fOutX | fInX);
            },
            ::FlowSoftware => {
                self.inner.fBits &= !(fOutxCtsFlow | fRtsControl);
                self.inner.fBits |= fOutX | fInX;
            },
            ::FlowHardware => {
                self.inner.fBits |= fOutxCtsFlow | fRtsControl;
                self.inner.fBits &= !(fOutX | fInX);
            }
        }
    }
}
<|endoftext|>


<commit_before>//! This module implements minimal run-time feature detection for x86.
//!
//! The features are detected using the `detect_features` function below.
//! This function uses the CPUID instruction to read the feature flags from the
//! CPU and encodes them in an `usize` where each bit position represents
//! whether a feature is available (bit is set) or unavaiable (bit is cleared).
//!
//! The enum `__Feature` is used to map bit positions to feature names, and the
//! the `__unstable_detect_feature!` macro is used to map string literals (e.g.
//! "avx") to these bit positions (e.g. `__Feature::avx`).
//!
//!
//! The run-time feature detection is performed by the
//! `__unstable_detect_feature(__Feature) -> bool` function. On its first call,
//! this functions queries the CPU for the available features and stores them
//! in a global `AtomicUsize` variable. The query is performed by just checking
//! whether the feature bit in this global variable is set or cleared.
use std::sync::atomic::{AtomicUsize, Ordering};

/// This macro maps the string-literal feature names to values of the
/// `__Feature` enum at compile-time. The feature names used are the same as
/// those of rustc `target_feature` and `cfg_target_feature` features.
///
/// PLESE: do not use this, it is an implementation detail subjected to change.
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[macro_export]
#[doc(hidden)]
macro_rules! __unstable_detect_feature {
    ("sse") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::sse{})  };
    ("sse2") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::sse2{})
    };
    ("sse3") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::sse3{})
    };
    ("ssse3") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::ssse3{})
    };
    ("sse4.1") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::sse4_1{})
    };
    ("sse4.2") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::sse4_2{})
    };
    ("avx") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::avx{})
    };
    ("avx2") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::avx2{})
    };
    ("fma") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::fma{})
    };
    ("bmi") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::bmi{})
    };
    ("bmi2") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::bmi2{})
    };
    ("abm") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::abm{})
    };
    ("lzcnt") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::abm{})
    };
    ("tbm") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::tbm{})
    };
    ("popcnt") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::popcnt{})
    };
    ($t:tt) => {
        compile_error!(concat!("unknown target feature: ", $t))
    };
}

/// X86 CPU Feature enum. Each variant denotes a position in a bitset for a
/// particular feature.
///
/// PLEASE: do not use this, it is an implementation detail subject to change.
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[repr(u8)]
pub enum __Feature {
    /// SSE (Streaming SIMD Extensions)
    sse,
    /// SSE2 (Streaming SIMD Extensions 2)
    sse2,
    /// SSE3 (Streaming SIMD Extensions 3)
    sse3,
    /// SSSE3 (Supplemental Streaming SIMD Extensions 3)
    ssse3,
    /// SSE4.1 (Streaming SIMD Extensions 4.1)
    sse4_1,
    /// SSE4.2 (Streaming SIMD Extensions 4.2)
    sse4_2,
    /// AVX (Advanced Vector Extensions)
    avx,
    /// AVX2 (Advanced Vector Extensions 2)
    avx2,
    /// FMA (Fused Multiply Add)
    fma,
    /// BMI1 (Bit Manipulation Instructions 1)
    bmi,
    /// BMI1 (Bit Manipulation Instructions 2)
    bmi2,
    /// ABM (Advanced Bit Manipulation) on AMD / LZCNT (Leading Zero
    /// Count) on Intel
    abm,
    /// TBM (Trailing Bit Manipulation)
    tbm,
    /// POPCNT (Population Count)
    popcnt,

    #[doc(hidden)] __NonExhaustive,
}

fn set_bit(x: usize, bit: u32) -> usize {
    debug_assert!(32 > bit);
    x | 1 << bit
}

fn test_bit(x: usize, bit: u32) -> bool {
    debug_assert!(32 > bit);
    x & (1 << bit) != 0
}

fn inv_test_bit(v: usize, idx: u32) -> bool {
    debug_assert!(32 > idx);
    ((v >> idx) & 1) != 0
}

/// Run-time feature detection on x86 works by using the CPUID instruction.
///
/// The [CPUID Wikipedia page][wiki_cpuid] contains
/// all the information about which flags to set to query which values, and in
/// which registers these are reported.
///
/// The definitive references are:
/// - [Intel 64 and IA-32 Architectures Software Developer's Manual Volume 2:
///   Instruction Set Reference, A-Z][intel64_ref].
/// - [AMD64 Architecture Programmer's Manual, Volume 3: General-Purpose and
///   System Instructions][amd64_ref].
///
/// [wiki_cpuid]: https://en.wikipedia.org/wiki/CPUID
/// [intel64_ref]: http://www.intel.de/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf
/// [amd64_ref]: http://support.amd.com/TechDocs/24594.pdf
fn detect_features() -> usize {
    let ebx;
    let ecx;
    let edx;

    unsafe {
        /// To obtain all feature flags we need two CPUID queries:

        /// 1. EAX=1, ECX=0: Queries "Processor Info and Feature Bits"
        /// This gives us most of the CPU features in ECX and EDX (see
        /// below),
        asm!("cpuid"
             : "={ecx}"(ecx), "={edx}"(edx)
             : "{eax}"(0x00000001u32), "{ecx}"(0 as u32)
             : :);

        /// 2. EAX=7, ECX=0: Queries "Extended Features"
        /// This gives us information about bmi,bmi2, and avx2 support
        /// (see below).
        asm!("cpuid"
             : "={ebx}"(ebx)
             : "{eax}"(0x00000007u32), "{ecx}"(0 as u32)
             : :);
    }

    let mut value: usize = 0;

    // CPUID call with EAX=7, ECX=0 => Extended Features in EBX and ECX
    // (the result in ECX is not currently needed):
    if inv_test_bit(ebx, 3) {
        value = set_bit(value, __Feature::bmi as u32);
    }
    if inv_test_bit(ebx, 5) {
        value = set_bit(value, __Feature::avx2 as u32);
    }
    if inv_test_bit(ebx, 8) {
        value = set_bit(value, __Feature::bmi2 as u32);
    }

    // CPUID call with EAX=1 => feature bits in ECX and EDX:
    if inv_test_bit(ecx, 0) {
        value = set_bit(value, __Feature::sse3 as u32);
    }
    if inv_test_bit(ecx, 5) {
        value = set_bit(value, __Feature::abm as u32);
    }
    if inv_test_bit(ecx, 9) {
        value = set_bit(value, __Feature::ssse3 as u32);
    }
    if inv_test_bit(ecx, 12) {
        value = set_bit(value, __Feature::fma as u32);
    }
    if inv_test_bit(ecx, 19) {
        value = set_bit(value, __Feature::sse4_1 as u32);
    }
    if inv_test_bit(ecx, 20) {
        value = set_bit(value, __Feature::sse4_2 as u32);
    }
    if inv_test_bit(ecx, 21) {
        value = set_bit(value, __Feature::tbm as u32);
    }
    if inv_test_bit(ecx, 23) {
        value = set_bit(value, __Feature::popcnt as u32);
    }
    if inv_test_bit(ecx, 28) {
        value = set_bit(value, __Feature::avx as u32);
    }

    if inv_test_bit(edx, 25) {
        value = set_bit(value, __Feature::sse as u32);
    }
    if inv_test_bit(edx, 26) {
        value = set_bit(value, __Feature::sse2 as u32);
    }

    value
}

/// This global variable is a bitset used to cache the features supported by
/// the CPU.
static FEATURES: AtomicUsize = AtomicUsize::new(::std::usize::MAX);

/// Performs run-time feature detection.
///
/// On its first invocation, it detects the CPU features and caches them
/// in the `FEATURES` global variable as an `AtomicUsize`.
///
/// It uses the `__Feature` variant to index into this variable as a bitset. If
/// the bit is set, the feature is enabled, and otherwise it is disabled.
///
/// PLEASE: do not use this, it is an implementation detail subject to change.
#[doc(hidden)]
pub fn __unstable_detect_feature(x: __Feature) -> bool {
    if FEATURES.load(Ordering::Relaxed) == ::std::usize::MAX {
        FEATURES.store(detect_features(), Ordering::Relaxed);
    }
    test_bit(FEATURES.load(Ordering::Relaxed), x as u32)
}

#[cfg(test)]
mod tests {
    #[test]
    fn runtime_detection_x86_nocapture() {
        println!("sse: {:?}", cfg_feature_enabled!("sse"));
        println!("sse2: {:?}", cfg_feature_enabled!("sse2"));
        println!("sse3: {:?}", cfg_feature_enabled!("sse3"));
        println!("ssse3: {:?}", cfg_feature_enabled!("ssse3"));
        println!("sse4.1: {:?}", cfg_feature_enabled!("sse4.1"));
        println!("sse4.2: {:?}", cfg_feature_enabled!("sse4.2"));
        println!("avx: {:?}", cfg_feature_enabled!("avx"));
        println!("avx2: {:?}", cfg_feature_enabled!("avx2"));
        println!("abm: {:?}", cfg_feature_enabled!("abm"));
        println!("bmi: {:?}", cfg_feature_enabled!("bmi"));
        println!("bmi2: {:?}", cfg_feature_enabled!("bmi2"));
        println!("tbm: {:?}", cfg_feature_enabled!("tbm"));
        println!("popcnt: {:?}", cfg_feature_enabled!("popcnt"));
        println!("lzcnt: {:?}", cfg_feature_enabled!("lzcnt"));
        println!("fma: {:?}", cfg_feature_enabled!("fma"));
    }
}
<commit_msg>[runtime-detection-x86] detect avx and avx2 only if osxsave is true (#154)<commit_after>//! This module implements minimal run-time feature detection for x86.
//!
//! The features are detected using the `detect_features` function below.
//! This function uses the CPUID instruction to read the feature flags from the
//! CPU and encodes them in an `usize` where each bit position represents
//! whether a feature is available (bit is set) or unavaiable (bit is cleared).
//!
//! The enum `__Feature` is used to map bit positions to feature names, and the
//! the `__unstable_detect_feature!` macro is used to map string literals (e.g.
//! "avx") to these bit positions (e.g. `__Feature::avx`).
//!
//!
//! The run-time feature detection is performed by the
//! `__unstable_detect_feature(__Feature) -> bool` function. On its first call,
//! this functions queries the CPU for the available features and stores them
//! in a global `AtomicUsize` variable. The query is performed by just checking
//! whether the feature bit in this global variable is set or cleared.
use std::sync::atomic::{AtomicUsize, Ordering};

/// This macro maps the string-literal feature names to values of the
/// `__Feature` enum at compile-time. The feature names used are the same as
/// those of rustc `target_feature` and `cfg_target_feature` features.
///
/// PLESE: do not use this, it is an implementation detail subjected to change.
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[macro_export]
#[doc(hidden)]
macro_rules! __unstable_detect_feature {
    ("sse") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::sse{})  };
    ("sse2") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::sse2{})
    };
    ("sse3") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::sse3{})
    };
    ("ssse3") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::ssse3{})
    };
    ("sse4.1") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::sse4_1{})
    };
    ("sse4.2") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::sse4_2{})
    };
    ("avx") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::avx{})
    };
    ("avx2") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::avx2{})
    };
    ("fma") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::fma{})
    };
    ("bmi") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::bmi{})
    };
    ("bmi2") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::bmi2{})
    };
    ("abm") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::abm{})
    };
    ("lzcnt") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::abm{})
    };
    ("tbm") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::tbm{})
    };
    ("popcnt") => {
        $crate::vendor::__unstable_detect_feature(
            $crate::vendor::__Feature::popcnt{})
    };
    ($t:tt) => {
        compile_error!(concat!("unknown target feature: ", $t))
    };
}

/// X86 CPU Feature enum. Each variant denotes a position in a bitset for a
/// particular feature.
///
/// PLEASE: do not use this, it is an implementation detail subject to change.
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[repr(u8)]
pub enum __Feature {
    /// SSE (Streaming SIMD Extensions)
    sse,
    /// SSE2 (Streaming SIMD Extensions 2)
    sse2,
    /// SSE3 (Streaming SIMD Extensions 3)
    sse3,
    /// SSSE3 (Supplemental Streaming SIMD Extensions 3)
    ssse3,
    /// SSE4.1 (Streaming SIMD Extensions 4.1)
    sse4_1,
    /// SSE4.2 (Streaming SIMD Extensions 4.2)
    sse4_2,
    /// AVX (Advanced Vector Extensions)
    avx,
    /// AVX2 (Advanced Vector Extensions 2)
    avx2,
    /// FMA (Fused Multiply Add)
    fma,
    /// BMI1 (Bit Manipulation Instructions 1)
    bmi,
    /// BMI1 (Bit Manipulation Instructions 2)
    bmi2,
    /// ABM (Advanced Bit Manipulation) on AMD / LZCNT (Leading Zero
    /// Count) on Intel
    abm,
    /// TBM (Trailing Bit Manipulation)
    tbm,
    /// POPCNT (Population Count)
    popcnt,

    #[doc(hidden)] __NonExhaustive,
}

fn set_bit(x: usize, bit: u32) -> usize {
    debug_assert!(32 > bit);
    x | 1 << bit
}

fn test_bit(x: usize, bit: u32) -> bool {
    debug_assert!(32 > bit);
    x & (1 << bit) != 0
}

fn inv_test_bit(v: usize, idx: u32) -> bool {
    debug_assert!(32 > idx);
    ((v >> idx) & 1) != 0
}

/// Run-time feature detection on x86 works by using the CPUID instruction.
///
/// The [CPUID Wikipedia page][wiki_cpuid] contains
/// all the information about which flags to set to query which values, and in
/// which registers these are reported.
///
/// The definitive references are:
/// - [Intel 64 and IA-32 Architectures Software Developer's Manual Volume 2:
///   Instruction Set Reference, A-Z][intel64_ref].
/// - [AMD64 Architecture Programmer's Manual, Volume 3: General-Purpose and
///   System Instructions][amd64_ref].
///
/// [wiki_cpuid]: https://en.wikipedia.org/wiki/CPUID
/// [intel64_ref]: http://www.intel.de/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf
/// [amd64_ref]: http://support.amd.com/TechDocs/24594.pdf
fn detect_features() -> usize {
    let extended_features_ebx;
    let proc_info_ecx;
    let proc_info_edx;

    unsafe {
        /// To obtain all feature flags we need two CPUID queries:

        /// 1. EAX=1, ECX=0: Queries "Processor Info and Feature Bits"
        /// This gives us most of the CPU features in ECX and EDX (see
        /// below).
        asm!("cpuid"
             : "={ecx}"(proc_info_ecx), "={edx}"(proc_info_edx)
             : "{eax}"(0x00000001u32), "{ecx}"(0 as u32)
             : :);

        /// 2. EAX=7, ECX=0: Queries "Extended Features"
        /// This gives us information about bmi,bmi2, and avx2 support
        /// (see below); the result in ECX is not currently needed.
        asm!("cpuid"
             : "={ebx}"(extended_features_ebx)
             : "{eax}"(0x00000007u32), "{ecx}"(0 as u32)
             : :);
    }

    let mut value: usize = 0;

    if inv_test_bit(extended_features_ebx, 3) {
        value = set_bit(value, __Feature::bmi as u32);
    }
    if inv_test_bit(extended_features_ebx, 8) {
        value = set_bit(value, __Feature::bmi2 as u32);
    }

    if inv_test_bit(proc_info_ecx, 0) {
        value = set_bit(value, __Feature::sse3 as u32);
    }
    if inv_test_bit(proc_info_ecx, 5) {
        value = set_bit(value, __Feature::abm as u32);
    }
    if inv_test_bit(proc_info_ecx, 9) {
        value = set_bit(value, __Feature::ssse3 as u32);
    }
    if inv_test_bit(proc_info_ecx, 12) {
        value = set_bit(value, __Feature::fma as u32);
    }
    if inv_test_bit(proc_info_ecx, 19) {
        value = set_bit(value, __Feature::sse4_1 as u32);
    }
    if inv_test_bit(proc_info_ecx, 20) {
        value = set_bit(value, __Feature::sse4_2 as u32);
    }
    if inv_test_bit(proc_info_ecx, 21) {
        value = set_bit(value, __Feature::tbm as u32);
    }
    if inv_test_bit(proc_info_ecx, 23) {
        value = set_bit(value, __Feature::popcnt as u32);
    }

    if inv_test_bit(proc_info_edx, 25) {
        value = set_bit(value, __Feature::sse as u32);
    }
    if inv_test_bit(proc_info_edx, 26) {
        value = set_bit(value, __Feature::sse2 as u32);
    }

    // ECX[26] detects XSAVE and ECX[27] detects OSXSAVE, that is, whether the
    // OS is AVX enabled and supports saving the state of the AVX/AVX2 vector
    // registers on context-switches, see:
    //
    // - https://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled
    // - https://hg.mozilla.
    // org/mozilla-central/file/64bab5cbb9b6/mozglue/build/SSE.cpp#l190
    //
    if inv_test_bit(proc_info_ecx, 26) && inv_test_bit(proc_info_ecx, 27) {
        unsafe fn xgetbv(xcr_no: u32) -> u64 {
            let eax: u32;
            let edx: u32;
            // xgetbv
            asm!("xgetbv"
                 : "={eax}"(eax),  "={edx}"(edx)
                 : "{ecx}"(xcr_no)
                 : :);
            ((edx as u64) << 32) | (eax as u64)
        }

        // This is safe because on x86 `xgetbv` is always available.
        if unsafe { xgetbv(0) } & 6 == 6 {
            if inv_test_bit(proc_info_ecx, 28) {
                value = set_bit(value, __Feature::avx as u32);
            }
            if inv_test_bit(extended_features_ebx, 5) {
                value = set_bit(value, __Feature::avx2 as u32);
            }
        }
    }

    value
}

/// This global variable is a bitset used to cache the features supported by
/// the CPU.
static FEATURES: AtomicUsize = AtomicUsize::new(::std::usize::MAX);

/// Performs run-time feature detection.
///
/// On its first invocation, it detects the CPU features and caches them
/// in the `FEATURES` global variable as an `AtomicUsize`.
///
/// It uses the `__Feature` variant to index into this variable as a bitset. If
/// the bit is set, the feature is enabled, and otherwise it is disabled.
///
/// PLEASE: do not use this, it is an implementation detail subject to change.
#[doc(hidden)]
pub fn __unstable_detect_feature(x: __Feature) -> bool {
    if FEATURES.load(Ordering::Relaxed) == ::std::usize::MAX {
        FEATURES.store(detect_features(), Ordering::Relaxed);
    }
    test_bit(FEATURES.load(Ordering::Relaxed), x as u32)
}

#[cfg(test)]
mod tests {
    #[test]
    fn runtime_detection_x86_nocapture() {
        println!("sse: {:?}", cfg_feature_enabled!("sse"));
        println!("sse2: {:?}", cfg_feature_enabled!("sse2"));
        println!("sse3: {:?}", cfg_feature_enabled!("sse3"));
        println!("ssse3: {:?}", cfg_feature_enabled!("ssse3"));
        println!("sse4.1: {:?}", cfg_feature_enabled!("sse4.1"));
        println!("sse4.2: {:?}", cfg_feature_enabled!("sse4.2"));
        println!("avx: {:?}", cfg_feature_enabled!("avx"));
        println!("avx2: {:?}", cfg_feature_enabled!("avx2"));
        println!("abm: {:?}", cfg_feature_enabled!("abm"));
        println!("bmi: {:?}", cfg_feature_enabled!("bmi"));
        println!("bmi2: {:?}", cfg_feature_enabled!("bmi2"));
        println!("tbm: {:?}", cfg_feature_enabled!("tbm"));
        println!("popcnt: {:?}", cfg_feature_enabled!("popcnt"));
        println!("lzcnt: {:?}", cfg_feature_enabled!("lzcnt"));
        println!("fma: {:?}", cfg_feature_enabled!("fma"));
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

mod lock;
mod metrics;
mod reader;
mod txn;
mod write;

pub use self::lock::{Lock, LockType};
pub use self::reader::MvccReader;
pub use self::txn::{MvccTxn, MAX_TXN_WRITE_SIZE};
pub use self::write::{Write, WriteType};
use std::error;
use std::io;
use util::escape;

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        Engine(err: ::storage::engine::Error) {
            from()
            cause(err)
            description(err.description())
        }
        Io(err: io::Error) {
            from()
            cause(err)
            description(err.description())
        }
        Codec(err: ::util::codec::Error) {
            from()
            cause(err)
            description(err.description())
        }
        KeyIsLocked {key: Vec<u8>, primary: Vec<u8>, ts: u64, ttl: u64} {
            description("key is locked (backoff or cleanup)")
            display("key is locked (backoff or cleanup) {}-{}@{} ttl {}",
                        escape(key),
                        escape(primary),
                        ts,
                        ttl)
        }
        BadFormatLock {description("bad format lock data")}
        BadFormatWrite {description("bad format write data")}
        Committed {commit_ts: u64} {
            description("txn already committed")
            display("txn already committed @{}", commit_ts)
        }
        TxnLockNotFound {start_ts: u64, commit_ts: u64, key: Vec<u8> } {
            description("txn lock not found")
            display("txn lock not found {}-{} key:{:?}", start_ts, commit_ts, key)
        }
        WriteConflict { start_ts: u64, conflict_ts: u64, key: Vec<u8>, primary: Vec<u8> } {
            description("write conflict")
            display("write conflict {} with {}, key:{:?}, primary:{:?}",
             start_ts, conflict_ts, key, primary)
        }
        KeyVersion {description("bad format key(version)")}
        Other(err: Box<error::Error + Sync + Send>) {
            from()
            cause(err.as_ref())
            description(err.description())
            display("{:?}", err)
        }
    }
}

impl Error {
    pub fn maybe_clone(&self) -> Option<Error> {
        match *self {
            Error::Engine(ref e) => e.maybe_clone().map(Error::Engine),
            Error::Codec(ref e) => e.maybe_clone().map(Error::Codec),
            Error::KeyIsLocked {
                ref key,
                ref primary,
                ts,
                ttl,
            } => Some(Error::KeyIsLocked {
                key: key.clone(),
                primary: primary.clone(),
                ts,
                ttl,
            }),
            Error::BadFormatLock => Some(Error::BadFormatLock),
            Error::BadFormatWrite => Some(Error::BadFormatWrite),
            Error::TxnLockNotFound {
                start_ts,
                commit_ts,
                ref key,
            } => Some(Error::TxnLockNotFound {
                start_ts,
                commit_ts,
                key: key.to_owned(),
            }),
            Error::WriteConflict {
                start_ts,
                conflict_ts,
                ref key,
                ref primary,
            } => Some(Error::WriteConflict {
                start_ts,
                conflict_ts,
                key: key.to_owned(),
                primary: primary.to_owned(),
            }),
            Error::KeyVersion => Some(Error::KeyVersion),
            Error::Committed { commit_ts } => Some(Error::Committed { commit_ts }),
            Error::Io(_) | Error::Other(_) => None,
        }
    }
}

pub type Result<T> = ::std::result::Result<T, Error>;
<commit_msg>mvcc: escape keys in Error (#3361)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

mod lock;
mod metrics;
mod reader;
mod txn;
mod write;

pub use self::lock::{Lock, LockType};
pub use self::reader::MvccReader;
pub use self::txn::{MvccTxn, MAX_TXN_WRITE_SIZE};
pub use self::write::{Write, WriteType};
use std::error;
use std::io;
use util::escape;

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        Engine(err: ::storage::engine::Error) {
            from()
            cause(err)
            description(err.description())
        }
        Io(err: io::Error) {
            from()
            cause(err)
            description(err.description())
        }
        Codec(err: ::util::codec::Error) {
            from()
            cause(err)
            description(err.description())
        }
        KeyIsLocked {key: Vec<u8>, primary: Vec<u8>, ts: u64, ttl: u64} {
            description("key is locked (backoff or cleanup)")
            display("key is locked (backoff or cleanup) {}-{}@{} ttl {}",
                        escape(key),
                        escape(primary),
                        ts,
                        ttl)
        }
        BadFormatLock {description("bad format lock data")}
        BadFormatWrite {description("bad format write data")}
        Committed {commit_ts: u64} {
            description("txn already committed")
            display("txn already committed @{}", commit_ts)
        }
        TxnLockNotFound {start_ts: u64, commit_ts: u64, key: Vec<u8> } {
            description("txn lock not found")
            display("txn lock not found {}-{} key:{:?}", start_ts, commit_ts, escape(key))
        }
        WriteConflict { start_ts: u64, conflict_ts: u64, key: Vec<u8>, primary: Vec<u8> } {
            description("write conflict")
            display("write conflict {} with {}, key:{:?}, primary:{:?}",
             start_ts, conflict_ts, escape(key), escape(primary))
        }
        KeyVersion {description("bad format key(version)")}
        Other(err: Box<error::Error + Sync + Send>) {
            from()
            cause(err.as_ref())
            description(err.description())
            display("{:?}", err)
        }
    }
}

impl Error {
    pub fn maybe_clone(&self) -> Option<Error> {
        match *self {
            Error::Engine(ref e) => e.maybe_clone().map(Error::Engine),
            Error::Codec(ref e) => e.maybe_clone().map(Error::Codec),
            Error::KeyIsLocked {
                ref key,
                ref primary,
                ts,
                ttl,
            } => Some(Error::KeyIsLocked {
                key: key.clone(),
                primary: primary.clone(),
                ts,
                ttl,
            }),
            Error::BadFormatLock => Some(Error::BadFormatLock),
            Error::BadFormatWrite => Some(Error::BadFormatWrite),
            Error::TxnLockNotFound {
                start_ts,
                commit_ts,
                ref key,
            } => Some(Error::TxnLockNotFound {
                start_ts,
                commit_ts,
                key: key.to_owned(),
            }),
            Error::WriteConflict {
                start_ts,
                conflict_ts,
                ref key,
                ref primary,
            } => Some(Error::WriteConflict {
                start_ts,
                conflict_ts,
                key: key.to_owned(),
                primary: primary.to_owned(),
            }),
            Error::KeyVersion => Some(Error::KeyVersion),
            Error::Committed { commit_ts } => Some(Error::Committed { commit_ts }),
            Error::Io(_) | Error::Other(_) => None,
        }
    }
}

pub type Result<T> = ::std::result::Result<T, Error>;
<|endoftext|>