<commit_before>use common::{Header, HTTPVersion, StatusCode};
use std::io::{IoResult, MemReader};
use std::io::fs::File;
use std::io::util;
use std::io::util::NullReader;

/// Object representing an HTTP response whose purpose is to be given to a `Request`.
///
/// Some headers cannot be changed. Trying to define the value
///  of one of these will have no effect:
///
///  - `Accept-Ranges`
///  - `Connection`
///  - `Content-Range`
///  - `Trailer`
///  - `Transfer-Encoding`
///  - `Upgrade`
///
/// Some headers have special behaviors:
///
///  - `Content-Encoding`: If you define this header, the library
///      will assume that the data from the `Reader` has the specified encoding
///      and will just pass-through.
///  - `Content-Length`: If you define this header to `N`, only the first `N` bytes
///      of the `Reader` will be read. If the `Reader` reaches `EOF` before `N` bytes have
///      been read, `0`s will be sent. Also, this header may not be passed to the final
///      output.
///
#[experimental]
pub struct Response<R> {
    reader: R,
    status_code: StatusCode,
    headers: Vec<Header>,
    data_length: Option<uint>,
}

/// Transfer encoding to use when sending the message.
/// Note that only *supported* encoding are listed here.
enum TransferEncoding {
    Identity,
    Chunked,
}

impl ::std::from_str::FromStr for TransferEncoding {
    fn from_str(input: &str) -> Option<TransferEncoding> {
        use std::ascii::StrAsciiExt;

        if input.eq_ignore_ascii_case("identity") {
            return Some(Identity);
        } else if input.eq_ignore_ascii_case("chunked") {
            return Some(Chunked);
        }

        None
    }
}

/// Builds a Date: header with the current date.
// TODO: this is optimisable
fn build_date_header() -> Header {
    use time;
    let date = time::now_utc().strftime("%a, %d %b %Y %H:%M:%S GMT");
    from_str(format!("Date: {}", date).as_slice()).unwrap()
}

fn write_message_header<W: Writer>(mut writer: W, http_version: &HTTPVersion,
                                   status_code: &StatusCode, headers: &[Header])
    -> IoResult<()>
{
    // writing status line
    try!(write!(writer, "HTTP/{} {} {}\r\n",
        http_version,
        status_code.as_uint(),
        status_code.get_default_reason_phrase()
    ));

    // writing headers
    for header in headers.iter() {
        try!(write!(writer, "{}: {}\r\n", header.field, header.value));
    }

    // separator between header and data
    try!(write!(writer, "\r\n"));

    Ok(())
}

fn choose_transfer_encoding(request_headers: &[Header], http_version: &HTTPVersion,
                            entity_length: &Option<uint>)
    -> TransferEncoding
{
    use util;

    // HTTP 1.0 doesn't support other encoding
    if *http_version <= HTTPVersion(1, 0) {
        return Identity;
    }

    // parsing the request's TE header
    let user_request = request_headers.iter()
        // finding TE
        .find(|h| h.field.equiv(&"TE"))

        // getting its value
        .map(|h| h.value.as_slice())

        // getting the corresponding TransferEncoding
        .and_then(|value| {
            // getting list of requested elements
            let mut parse = util::parse_header_value(value);

            // sorting elements by most priority
            parse.sort_by(|a, b| b.val1().partial_cmp(&a.val1()).unwrap_or(Equal));

            // trying to parse each requested encoding
            for value in parse.iter() {
                // q=0 are ignored
                if value.val1() <= 0.0 { continue }

                match from_str::<TransferEncoding>(value.val0()) {
                    Some(te) => return Some(te),
                    _ => ()     // unrecognized/unsupported encoding
                };
            }

            // encoding not found
            None
        });

    //
    if user_request.is_some() {
        return user_request.unwrap();
    }

    // if we don't have a Content-Length, or if the Content-Length is too big, using chunks writer
    let chunks_threshold = 32768;
    if entity_length.as_ref().filtered(|val| **val < chunks_threshold).is_none() {
        return Chunked;
    }

    // Identity by default
    Identity
}

impl<R: Reader> Response<R> {
    #[experimental]
    pub fn new(status_code: StatusCode, headers: Vec<Header>,
               data: R, data_length: Option<uint>,
               additional_headers: Option<Receiver<Header>>)
                -> Response<R>
    {
        let mut response = Response {
            reader: data,
            status_code: status_code,
            headers: Vec::new(),
            data_length: data_length,
        };

        for h in headers.move_iter() {
            response.add_header(h)
        }

        // dummy implementation
        if additional_headers.is_some() {
            for h in additional_headers.unwrap().iter() {
                response.add_header(h)
            }
        }

        response
    }

    /// Adds a header to the list.
    /// Does all the checks.
    #[experimental]
    pub fn add_header(&mut self, header: Header) {
        // ignoring forbidden headers
        if header.field.equiv(&"Accept-Ranges") ||
           header.field.equiv(&"Connection") ||
           header.field.equiv(&"Content-Range") ||
           header.field.equiv(&"Trailer") ||
           header.field.equiv(&"Transfer-Encoding") ||
           header.field.equiv(&"Upgrade")
        {
            return;
        }

        // if the header is Content-Length, setting the data length
        if header.field.equiv(&"Content-Length") {
            match from_str::<uint>(header.value.as_slice()) {
                Some(val) => self.data_length = Some(val),
                None => ()      // wrong value for content-length
            };

            return;
        }

        self.headers.push(header)
    }

    /// Returns the same request, but with an additional header.
    ///
    /// Some headers cannot be modified and some other have a
    ///  special behavior. See the documentation above.
    #[unstable]
    pub fn with_header(mut self, header: Header) -> Response<R> {
        self.add_header(header);
        self
    }

    /// Returns the same request, but with a different status code.
    #[unstable]
    pub fn with_status_code(mut self, code: StatusCode) -> Response<R> {
        self.status_code = code;
        self
    }

    /// Prints the HTTP response to a writer.
    ///
    /// This function is the one used to send the response to the client's socket.
    /// Therefore you shouldn't expect anything pretty-printed or even readable.
    ///
    /// The HTTP version and headers passed as arguments are used to
    ///  decide which features (most notably, encoding) to use.
    ///
    /// Note: does not flush the writer.
    #[unstable]
    pub fn raw_print<W: Writer>(mut self, mut writer: W, http_version: HTTPVersion,
                                request_headers: &[Header], do_not_send_body: bool)
                                -> IoResult<()>
    {
        let transfer_encoding = choose_transfer_encoding(request_headers,
                                    &http_version, &self.data_length);

        // add `Date` if not in the headers
        if self.headers.iter().find(|h| h.field.equiv(&"Date")).is_none() {
            self.headers.unshift(build_date_header());
        }

        // add `Server` if not in the headers
        if self.headers.iter().find(|h| h.field.equiv(&"Server")).is_none() {
            self.headers.unshift(
                from_str("Server: tiny-http (Rust)").unwrap()
            );
        }

        // checking whether to ignore the body of the response
        let do_not_send_body = do_not_send_body ||
            match self.status_code.as_uint() {
                // sattus code 1xx, 204 and 304 MUST not include a body
                100..199 | 204 | 304 => true,
                _ => false
            };

        // preparing headers for transfer
        match transfer_encoding {
            Chunked => {
                self.headers.push(
                    from_str("Transfer-Encoding: chunked").unwrap()
                )
            },

            Identity => {
                assert!(self.data_length.is_some());
                let data_length = self.data_length.unwrap();

                self.headers.push(
                    from_str(format!("Content-Length: {}", data_length).as_slice()).unwrap()
                )
            }
        };

        // sending headers
        try!(write_message_header(writer.by_ref(), &http_version,
            &self.status_code, self.headers.as_slice()));

        // sending the body
        if !do_not_send_body {
            match transfer_encoding {

                Chunked => {
                    use util::ChunksEncoder;

                    let mut writer = ChunksEncoder::new(writer);
                    try!(util::copy(&mut self.reader, &mut writer));
                },

                Identity => {
                    use util::EqualReader;

                    assert!(self.data_length.is_some());
                    let data_length = self.data_length.unwrap();

                    if data_length >= 1 {
                        let (mut equ_reader, _) =
                            EqualReader::new(self.reader.by_ref(), data_length);
                        try!(util::copy(&mut equ_reader, &mut writer));
                    }
                }

            }
        }

        Ok(())
    }
}

impl Response<File> {
    /// Builds a new `Response` from a `File`.
    ///
    /// The `Content-Type` will **not** be automatically detected,
    ///  you must set it yourself.
    #[experimental]
    pub fn from_file(mut file: File) -> Response<File> {
        let file_size = file.stat().ok().map(|v| v.size as uint);

        Response::new(
            StatusCode(200),
            Vec::new(),
            file,
            file_size,
            None,
        )
    }
}

impl Response<MemReader> {
    #[experimental]
    pub fn from_data(data: Vec<u8>) -> Response<MemReader> {
        let data_len = data.len();

        Response::new(
            StatusCode(200),
            Vec::new(),
            MemReader::new(data),
            Some(data_len),
            None,
        )
    }

    #[experimental]
    pub fn from_string(data: String) -> Response<MemReader> {
        let data_len = data.len();

        Response::new(
            StatusCode(200),
            vec!(
                from_str("Content-Type: text/plain; charset=UTF-8").unwrap()
            ),
            MemReader::new(data.into_bytes()),
            Some(data_len),
            None,
        )
    }
}

impl Response<NullReader> {
    /// Builds an empty `Response` with the given status code.
    #[experimental]
    pub fn new_empty(status_code: StatusCode) -> Response<NullReader> {
        Response::new(
            status_code,
            Vec::new(),
            NullReader,
            Some(0),
            None,
        )
    }
}
<commit_msg>Minor docs/API changes in Response<commit_after>use common::{Header, HTTPVersion, StatusCode};
use std::io::{IoResult, MemReader};
use std::io::fs::File;
use std::io::util;
use std::io::util::NullReader;

/// Object representing an HTTP response whose purpose is to be given to a `Request`.
///
/// Some headers cannot be changed. Trying to define the value
///  of one of these will have no effect:
///
///  - `Accept-Ranges`
///  - `Connection`
///  - `Content-Range`
///  - `Trailer`
///  - `Transfer-Encoding`
///  - `Upgrade`
///
/// Some headers have special behaviors:
///
///  - `Content-Encoding`: If you define this header, the library
///      will assume that the data from the `Reader` has the specified encoding
///      and will just pass-through.
///
///  - `Content-Length`: The length of the data should be set manually
///      using the `Reponse` object's API. Attempting to set the value of this
///      header will be equivalent to modifying the size of the data but the header
///      itself may not be present in the final result.
///
#[experimental]
pub struct Response<R> {
    reader: R,
    status_code: StatusCode,
    headers: Vec<Header>,
    data_length: Option<uint>,
}

/// Transfer encoding to use when sending the message.
/// Note that only *supported* encoding are listed here.
enum TransferEncoding {
    Identity,
    Chunked,
}

impl ::std::from_str::FromStr for TransferEncoding {
    fn from_str(input: &str) -> Option<TransferEncoding> {
        use std::ascii::StrAsciiExt;

        if input.eq_ignore_ascii_case("identity") {
            return Some(Identity);
        } else if input.eq_ignore_ascii_case("chunked") {
            return Some(Chunked);
        }

        None
    }
}

/// Builds a Date: header with the current date.
// TODO: this is optimisable
fn build_date_header() -> Header {
    use time;
    let date = time::now_utc().strftime("%a, %d %b %Y %H:%M:%S GMT");
    from_str(format!("Date: {}", date).as_slice()).unwrap()
}

fn write_message_header<W: Writer>(mut writer: W, http_version: &HTTPVersion,
                                   status_code: &StatusCode, headers: &[Header])
    -> IoResult<()>
{
    // writing status line
    try!(write!(writer, "HTTP/{} {} {}\r\n",
        http_version,
        status_code.as_uint(),
        status_code.get_default_reason_phrase()
    ));

    // writing headers
    for header in headers.iter() {
        try!(write!(writer, "{}: {}\r\n", header.field, header.value));
    }

    // separator between header and data
    try!(write!(writer, "\r\n"));

    Ok(())
}

fn choose_transfer_encoding(request_headers: &[Header], http_version: &HTTPVersion,
                            entity_length: &Option<uint>, has_additional_headers: bool)
    -> TransferEncoding
{
    use util;

    // HTTP 1.0 doesn't support other encoding
    if *http_version <= HTTPVersion(1, 0) {
        return Identity;
    }

    // parsing the request's TE header
    let user_request = request_headers.iter()
        // finding TE
        .find(|h| h.field.equiv(&"TE"))

        // getting its value
        .map(|h| h.value.as_slice())

        // getting the corresponding TransferEncoding
        .and_then(|value| {
            // getting list of requested elements
            let mut parse = util::parse_header_value(value);

            // sorting elements by most priority
            parse.sort_by(|a, b| b.val1().partial_cmp(&a.val1()).unwrap_or(Equal));

            // trying to parse each requested encoding
            for value in parse.iter() {
                // q=0 are ignored
                if value.val1() <= 0.0 { continue }

                match from_str::<TransferEncoding>(value.val0()) {
                    Some(te) => return Some(te),
                    _ => ()     // unrecognized/unsupported encoding
                };
            }

            // encoding not found
            None
        });

    //
    if user_request.is_some() {
        return user_request.unwrap();
    }

    // if we have additional headers, using chunked
    if has_additional_headers {
        return Chunked;
    }

    // if we don't have a Content-Length, or if the Content-Length is too big, using chunks writer
    let chunks_threshold = 32768;
    if entity_length.as_ref().filtered(|val| **val < chunks_threshold).is_none() {
        return Chunked;
    }

    // Identity by default
    Identity
}

impl<R: Reader> Response<R> {
    /// Creates a new Response object.
    ///
    /// The `additional_headers` argument is a receiver that
    ///  may provide headers even after the response has been sent.
    ///
    /// All the other arguments are straight-forward.
    #[experimental]
    pub fn new(status_code: StatusCode, headers: Vec<Header>,
               data: R, data_length: Option<uint>,
               additional_headers: Option<Receiver<Header>>)
                -> Response<R>
    {
        let mut response = Response {
            reader: data,
            status_code: status_code,
            headers: Vec::new(),
            data_length: data_length,
        };

        for h in headers.move_iter() {
            response.add_header(h)
        }

        // dummy implementation
        if additional_headers.is_some() {
            for h in additional_headers.unwrap().iter() {
                response.add_header(h)
            }
        }

        response
    }

    /// Adds a header to the list.
    /// Does all the checks.
    #[experimental]
    pub fn add_header(&mut self, header: Header) {
        // ignoring forbidden headers
        if header.field.equiv(&"Accept-Ranges") ||
           header.field.equiv(&"Connection") ||
           header.field.equiv(&"Content-Range") ||
           header.field.equiv(&"Trailer") ||
           header.field.equiv(&"Transfer-Encoding") ||
           header.field.equiv(&"Upgrade")
        {
            return;
        }

        // if the header is Content-Length, setting the data length
        if header.field.equiv(&"Content-Length") {
            match from_str::<uint>(header.value.as_slice()) {
                Some(val) => self.data_length = Some(val),
                None => ()      // wrong value for content-length
            };

            return;
        }

        self.headers.push(header)
    }

    /// Returns the same request, but with an additional header.
    ///
    /// Some headers cannot be modified and some other have a
    ///  special behavior. See the documentation above.
    #[unstable]
    pub fn with_header(mut self, header: Header) -> Response<R> {
        self.add_header(header);
        self
    }

    /// Returns the same request, but with a different status code.
    #[unstable]
    pub fn with_status_code(mut self, code: StatusCode) -> Response<R> {
        self.status_code = code;
        self
    }

    /// Prints the HTTP response to a writer.
    ///
    /// This function is the one used to send the response to the client's socket.
    /// Therefore you shouldn't expect anything pretty-printed or even readable.
    ///
    /// The HTTP version and headers passed as arguments are used to
    ///  decide which features (most notably, encoding) to use.
    ///
    /// Note: does not flush the writer.
    #[unstable]
    pub fn raw_print<W: Writer>(mut self, mut writer: W, http_version: HTTPVersion,
                                request_headers: &[Header], do_not_send_body: bool)
                                -> IoResult<()>
    {
        let transfer_encoding = choose_transfer_encoding(request_headers,
                                    &http_version, &self.data_length, false /* TODO */);

        // add `Date` if not in the headers
        if self.headers.iter().find(|h| h.field.equiv(&"Date")).is_none() {
            self.headers.unshift(build_date_header());
        }

        // add `Server` if not in the headers
        if self.headers.iter().find(|h| h.field.equiv(&"Server")).is_none() {
            self.headers.unshift(
                from_str("Server: tiny-http (Rust)").unwrap()
            );
        }

        // checking whether to ignore the body of the response
        let do_not_send_body = do_not_send_body ||
            match self.status_code.as_uint() {
                // sattus code 1xx, 204 and 304 MUST not include a body
                100..199 | 204 | 304 => true,
                _ => false
            };

        // preparing headers for transfer
        match transfer_encoding {
            Chunked => {
                self.headers.push(
                    from_str("Transfer-Encoding: chunked").unwrap()
                )
            },

            Identity => {
                assert!(self.data_length.is_some());
                let data_length = self.data_length.unwrap();

                self.headers.push(
                    from_str(format!("Content-Length: {}", data_length).as_slice()).unwrap()
                )
            }
        };

        // sending headers
        try!(write_message_header(writer.by_ref(), &http_version,
            &self.status_code, self.headers.as_slice()));

        // sending the body
        if !do_not_send_body {
            match transfer_encoding {

                Chunked => {
                    use util::ChunksEncoder;

                    let mut writer = ChunksEncoder::new(writer);
                    try!(util::copy(&mut self.reader, &mut writer));
                },

                Identity => {
                    use util::EqualReader;

                    assert!(self.data_length.is_some());
                    let data_length = self.data_length.unwrap();

                    if data_length >= 1 {
                        let (mut equ_reader, _) =
                            EqualReader::new(self.reader.by_ref(), data_length);
                        try!(util::copy(&mut equ_reader, &mut writer));
                    }
                }

            }
        }

        Ok(())
    }
}

impl Response<File> {
    /// Builds a new `Response` from a `File`.
    ///
    /// The `Content-Type` will **not** be automatically detected,
    ///  you must set it yourself.
    #[experimental]
    pub fn from_file(mut file: File) -> Response<File> {
        let file_size = file.stat().ok().map(|v| v.size as uint);

        Response::new(
            StatusCode(200),
            Vec::new(),
            file,
            file_size,
            None,
        )
    }
}

impl Response<MemReader> {
    #[experimental]
    pub fn from_data(data: Vec<u8>) -> Response<MemReader> {
        let data_len = data.len();

        Response::new(
            StatusCode(200),
            Vec::new(),
            MemReader::new(data),
            Some(data_len),
            None,
        )
    }

    #[experimental]
    pub fn from_string(data: String) -> Response<MemReader> {
        let data_len = data.len();

        Response::new(
            StatusCode(200),
            vec!(
                from_str("Content-Type: text/plain; charset=UTF-8").unwrap()
            ),
            MemReader::new(data.into_bytes()),
            Some(data_len),
            None,
        )
    }
}

impl Response<NullReader> {
    /// Builds an empty `Response` with the given status code.
    #[experimental]
    pub fn new_empty(status_code: StatusCode) -> Response<NullReader> {
        Response::new(
            status_code,
            Vec::new(),
            NullReader,
            Some(0),
            None,
        )
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::slice;

extern crate libc;
use self::libc::{c_void, c_double};

#[repr(C)]
pub struct Array {
    pub data: *const c_void,
    pub len: libc::size_t,
}

use super::convert_to_bng_threaded_vec;
use super::convert_to_lonlat_threaded_vec;
use super::convert_to_osgb36_threaded_vec;
use super::convert_to_etrs89_threaded_vec;
use super::convert_etrs89_to_osgb36_threaded_vec;
use super::convert_etrs89_to_ll_threaded_vec;
use super::convert_osgb36_to_ll_threaded_vec;
use super::convert_osgb36_to_etrs89_threaded_vec;
use super::convert_epsg3857_to_wgs84_threaded_vec;

/// Free memory which Rust has allocated across the FFI boundary (f64 values)
///
/// # Examples
///
/// ```
/// # extern crate libc;
/// let lon_vec: Vec<f64> = vec![-2.0183041005533306];
/// let lat_vec: Vec<f64> = vec![54.589097162646141];
/// let lon_arr = Array {
///     data: lon_vec.as_ptr() as *const libc::c_void,
///     len: lon_vec.len() as libc::size_t,
/// };
/// let lat_arr = Array {
///     data: lat_vec.as_ptr() as *const libc::c_void,
///     len: lat_vec.len() as libc::size_t,
/// };
/// let (eastings, northings) = convert_to_bng_threaded(lon_arr, lat_arr);
/// drop_float_array(eastings, northings);
/// ```
/// An example FFI implementation is available at [Convertbng](https://github.com/urschrei/convertbng/blob/master/convertbng/util.py), specifically in the `_void_array_to_list` function.
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn drop_float_array(lons: Array, lats: Array) {
    if lons.data.is_null() {
        return;
    }
    if lats.data.is_null() {
        return;
    }
    unsafe { Vec::from_raw_parts(lons.data as *mut c_double, lons.len, lons.len) };
    unsafe { Vec::from_raw_parts(lats.data as *mut c_double, lats.len, lats.len) };
}

impl Array {
    pub unsafe fn as_f64_slice(&self) -> &[f64] {
        assert!(!self.data.is_null());
        slice::from_raw_parts(self.data as *const f64, self.len as usize)
    }

    pub fn from_vec<T>(mut vec: Vec<T>) -> Array {
        // Important to make length and capacity match
        // A better solution is to track both length and capacity
        vec.shrink_to_fit();

        let array = Array {
            data: vec.as_ptr() as *const libc::c_void,
            len: vec.len() as libc::size_t,
        };

        // Leak the memory, and now the raw pointer (and
        // eventually the FFI parent process) is the owner
        mem::forget(vec);

        array
    }
}

/// A threaded, FFI-compatible wrapper for `lonlat_bng::convert_osgb36`
///
/// # Examples
///
/// ```
/// extern crate libc;
/// let lon_vec: Vec<f64> = vec![-2.0183041005533306,
///                              0.95511887434519682,
///                              0.44975855518383501,
///                              -0.096813621191803811,
///                              -0.36807065656416427,
///                              0.63486335458665621];
/// let lat_vec: Vec<f64> = vec![54.589097162646141,
///                              51.560873800587828,
///                              50.431429161121699,
///                              54.535021436247419,
///                              50.839059313135706,
///                              55.412189281234419];
/// let lon_arr = Array {
///     data: lon_vec.as_ptr() as *const libc::c_void,
///     len: lon_vec.len() as libc::size_t,
/// };
/// let lat_arr = Array {
///     data: lat_vec.as_ptr() as *const libc::c_void,
///     len: lat_vec.len() as libc::size_t,
/// };
/// let (eastings, northings) = convert_to_bng_threaded(lon_arr, lat_arr);
/// ```
/// For an FFI implementation, see the code at [Convertbng](https://github.com/urschrei/convertbng/blob/master/convertbng/util.py).
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn convert_to_bng_threaded(longitudes: Array, latitudes: Array) -> (Array, Array) {
    let mut longitudes_v = unsafe { longitudes.as_f64_slice().to_vec() };
    let mut latitudes_v = unsafe { latitudes.as_f64_slice().to_vec() };
    convert_to_bng_threaded_vec(&mut longitudes_v, &mut latitudes_v);
    (Array::from_vec(longitudes_v), Array::from_vec(latitudes_v))
}

/// A threaded, FFI-compatible wrapper for [`lonlat_bng::convert_osgb36_to_ll`](fn.convert_osgb36_to_ll.html)
///
/// # Examples
///
/// See [`lonlat_bng::convert_to_bng_threaded`](fn.convert_to_bng_threaded.html)
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn convert_to_lonlat_threaded(eastings: Array, northings: Array) -> (Array, Array) {
    let mut eastings_vec = unsafe { eastings.as_f64_slice().to_vec() };
    let mut northings_vec = unsafe { northings.as_f64_slice().to_vec() };
    convert_to_lonlat_threaded_vec(&mut eastings_vec, &mut northings_vec);
    (Array::from_vec(eastings_vec),
     Array::from_vec(northings_vec))
}

/// A threaded, FFI-compatible wrapper for [`lonlat_bng::convert_osgb36`](fn.convert_osgb36.html)
///
/// # Examples
///
/// See [`lonlat_bng::convert_to_bng_threaded`](fn.convert_to_bng_threaded.html) for examples, substituting f64 vectors
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn convert_to_osgb36_threaded(longitudes: Array,
                                             latitudes: Array)
                                             -> (Array, Array) {
    let mut longitudes_v = unsafe { longitudes.as_f64_slice().to_vec() };
    let mut latitudes_v = unsafe { latitudes.as_f64_slice().to_vec() };
    convert_to_osgb36_threaded_vec(&mut longitudes_v, &mut latitudes_v);
    (Array::from_vec(longitudes_v), Array::from_vec(latitudes_v))
}

/// A threaded, FFI-compatible wrapper for [`lonlat_bng::convert_etrs89`](fn.convert_etrs89.html)
///
/// # Examples
///
/// See `lonlat_bng::convert_to_bng_threaded` for examples, substituting f64 vectors
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn convert_to_etrs89_threaded(longitudes: Array,
                                             latitudes: Array)
                                             -> (Array, Array) {
    let mut longitudes_v = unsafe { longitudes.as_f64_slice().to_vec() };
    let mut latitudes_v = unsafe { latitudes.as_f64_slice().to_vec() };
    convert_to_etrs89_threaded_vec(&mut longitudes_v, &mut latitudes_v);
    (Array::from_vec(longitudes_v), Array::from_vec(latitudes_v))
}

/// A threaded, FFI-compatible wrapper for [`lonlat_bng::convert_etrs89_to_osgb36`](fn.convert_etrs89_to_osgb36.html)
///
/// # Examples
///
/// See `lonlat_bng::convert_to_bng_threaded` for examples
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn convert_etrs89_to_osgb36_threaded(eastings: Array,
                                                    northings: Array)
                                                    -> (Array, Array) {
    let mut eastings_vec = unsafe { eastings.as_f64_slice().to_vec() };
    let mut northings_vec = unsafe { northings.as_f64_slice().to_vec() };
    convert_etrs89_to_osgb36_threaded_vec(&mut eastings_vec, &mut northings_vec);
    (Array::from_vec(eastings_vec),
     Array::from_vec(northings_vec))
}

/// A threaded, FFI-compatible wrapper for [`lonlat_bng::convert_etrs89_to_ll`](fn.convert_etrs89_to_ll.html)
///
/// # Examples
///
/// See `lonlat_bng::convert_to_bng_threaded` for examples
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn convert_etrs89_to_ll_threaded(eastings: Array,
                                                northings: Array)
                                                -> (Array, Array) {
    let mut eastings_vec = unsafe { eastings.as_f64_slice().to_vec() };
    let mut northings_vec = unsafe { northings.as_f64_slice().to_vec() };
    convert_etrs89_to_ll_threaded_vec(&mut eastings_vec, &mut northings_vec);
    (Array::from_vec(eastings_vec),
     Array::from_vec(northings_vec))
}

/// A threaded, FFI-compatible wrapper for [`lonlat_bng::convert_osgb36_to_ll`](fn.convert_osgb36_to_ll.html)
///
/// # Examples
///
/// See `lonlat_bng::convert_to_bng_threaded` for examples
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn convert_osgb36_to_ll_threaded(eastings: Array,
                                                northings: Array)
                                                -> (Array, Array) {
    let mut eastings_vec = unsafe { eastings.as_f64_slice().to_vec() };
    let mut northings_vec = unsafe { northings.as_f64_slice().to_vec() };
    convert_osgb36_to_ll_threaded_vec(&mut eastings_vec, &mut northings_vec);
    (Array::from_vec(eastings_vec),
     Array::from_vec(northings_vec))
}

/// A threaded, FFI-compatible wrapper for [`lonlat_bng::convert_osgb36_to_etrs89`](fn.convert_osgb36_to_etrs89.html)
///
/// # Examples
///
/// See `lonlat_bng::convert_to_bng_threaded` for examples
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn convert_osgb36_to_etrs89_threaded(eastings: Array,
                                                    northings: Array)
                                                    -> (Array, Array) {
    let mut eastings_vec = unsafe { eastings.as_f64_slice().to_vec() };
    let mut northings_vec = unsafe { northings.as_f64_slice().to_vec() };
    convert_osgb36_to_etrs89_threaded_vec(&mut eastings_vec, &mut northings_vec);
    (Array::from_vec(eastings_vec),
     Array::from_vec(northings_vec))
}

/// A threaded, FFI-compatible wrapper for [`lonlat_bng::convert_epsg3857_to_wgs84`](fn.convert_epsg3857_to_wgs84.html)
///
/// # Examples
///
/// See `lonlat_bng::convert_to_bng_threaded` for examples
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn convert_epsg3857_to_wgs84_threaded(x: Array, y: Array) -> (Array, Array) {
    let mut x_vec = unsafe { x.as_f64_slice().to_vec() };
    let mut y_vec = unsafe { y.as_f64_slice().to_vec() };
    convert_epsg3857_to_wgs84_threaded_vec(&mut x_vec, &mut y_vec);
    (Array::from_vec(x_vec), Array::from_vec(y_vec))
}
<commit_msg>Rustfmt pass<commit_after>use std::mem;
use std::slice;

extern crate libc;
use self::libc::{c_void, c_double};

#[repr(C)]
pub struct Array {
    pub data: *const c_void,
    pub len: libc::size_t,
}

use super::convert_to_bng_threaded_vec;
use super::convert_to_lonlat_threaded_vec;
use super::convert_to_osgb36_threaded_vec;
use super::convert_to_etrs89_threaded_vec;
use super::convert_etrs89_to_osgb36_threaded_vec;
use super::convert_etrs89_to_ll_threaded_vec;
use super::convert_osgb36_to_ll_threaded_vec;
use super::convert_osgb36_to_etrs89_threaded_vec;
use super::convert_epsg3857_to_wgs84_threaded_vec;

/// Free memory which Rust has allocated across the FFI boundary (f64 values)
///
/// # Examples
///
/// ```
/// # extern crate libc;
/// let lon_vec: Vec<f64> = vec![-2.0183041005533306];
/// let lat_vec: Vec<f64> = vec![54.589097162646141];
/// let lon_arr = Array {
///     data: lon_vec.as_ptr() as *const libc::c_void,
///     len: lon_vec.len() as libc::size_t,
/// };
/// let lat_arr = Array {
///     data: lat_vec.as_ptr() as *const libc::c_void,
///     len: lat_vec.len() as libc::size_t,
/// };
/// let (eastings, northings) = convert_to_bng_threaded(lon_arr, lat_arr);
/// drop_float_array(eastings, northings);
/// ```
/// An example FFI implementation is available at [Convertbng](https://github.com/urschrei/convertbng/blob/master/convertbng/util.py), specifically in the `_void_array_to_list` function.
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn drop_float_array(lons: Array, lats: Array) {
    if lons.data.is_null() {
        return;
    }
    if lats.data.is_null() {
        return;
    }
    unsafe { Vec::from_raw_parts(lons.data as *mut c_double, lons.len, lons.len) };
    unsafe { Vec::from_raw_parts(lats.data as *mut c_double, lats.len, lats.len) };
}

impl Array {
    pub unsafe fn as_f64_slice(&self) -> &[f64] {
        assert!(!self.data.is_null());
        slice::from_raw_parts(self.data as *const f64, self.len as usize)
    }

    pub fn from_vec<T>(mut vec: Vec<T>) -> Array {
        // Important to make length and capacity match
        // A better solution is to track both length and capacity
        vec.shrink_to_fit();

        let array = Array {
            data: vec.as_ptr() as *const libc::c_void,
            len: vec.len() as libc::size_t,
        };

        // Leak the memory, and now the raw pointer (and
        // eventually the FFI parent process) is the owner
        mem::forget(vec);

        array
    }
}

/// A threaded, FFI-compatible wrapper for `lonlat_bng::convert_osgb36`
///
/// # Examples
///
/// ```
/// extern crate libc;
/// let lon_vec: Vec<f64> = vec![-2.0183041005533306,
///                              0.95511887434519682,
///                              0.44975855518383501,
///                              -0.096813621191803811,
///                              -0.36807065656416427,
///                              0.63486335458665621];
/// let lat_vec: Vec<f64> = vec![54.589097162646141,
///                              51.560873800587828,
///                              50.431429161121699,
///                              54.535021436247419,
///                              50.839059313135706,
///                              55.412189281234419];
/// let lon_arr = Array {
///     data: lon_vec.as_ptr() as *const libc::c_void,
///     len: lon_vec.len() as libc::size_t,
/// };
/// let lat_arr = Array {
///     data: lat_vec.as_ptr() as *const libc::c_void,
///     len: lat_vec.len() as libc::size_t,
/// };
/// let (eastings, northings) = convert_to_bng_threaded(lon_arr, lat_arr);
/// ```
/// For an FFI implementation, see the code at [Convertbng](https://github.com/urschrei/convertbng/blob/master/convertbng/util.py).
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn convert_to_bng_threaded(longitudes: Array, latitudes: Array) -> (Array, Array) {
    let mut longitudes_v = unsafe { longitudes.as_f64_slice().to_vec() };
    let mut latitudes_v = unsafe { latitudes.as_f64_slice().to_vec() };
    convert_to_bng_threaded_vec(&mut longitudes_v, &mut latitudes_v);
    (Array::from_vec(longitudes_v), Array::from_vec(latitudes_v))
}

/// A threaded, FFI-compatible wrapper for [`lonlat_bng::convert_osgb36_to_ll`](fn.convert_osgb36_to_ll.html)
///
/// # Examples
///
/// See [`lonlat_bng::convert_to_bng_threaded`](fn.convert_to_bng_threaded.html)
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn convert_to_lonlat_threaded(eastings: Array, northings: Array) -> (Array, Array) {
    let mut eastings_vec = unsafe { eastings.as_f64_slice().to_vec() };
    let mut northings_vec = unsafe { northings.as_f64_slice().to_vec() };
    convert_to_lonlat_threaded_vec(&mut eastings_vec, &mut northings_vec);
    (Array::from_vec(eastings_vec), Array::from_vec(northings_vec))
}

/// A threaded, FFI-compatible wrapper for [`lonlat_bng::convert_osgb36`](fn.convert_osgb36.html)
///
/// # Examples
///
/// See [`lonlat_bng::convert_to_bng_threaded`](fn.convert_to_bng_threaded.html) for examples, substituting f64 vectors
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn convert_to_osgb36_threaded(longitudes: Array,
                                             latitudes: Array)
                                             -> (Array, Array) {
    let mut longitudes_v = unsafe { longitudes.as_f64_slice().to_vec() };
    let mut latitudes_v = unsafe { latitudes.as_f64_slice().to_vec() };
    convert_to_osgb36_threaded_vec(&mut longitudes_v, &mut latitudes_v);
    (Array::from_vec(longitudes_v), Array::from_vec(latitudes_v))
}

/// A threaded, FFI-compatible wrapper for [`lonlat_bng::convert_etrs89`](fn.convert_etrs89.html)
///
/// # Examples
///
/// See `lonlat_bng::convert_to_bng_threaded` for examples, substituting f64 vectors
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn convert_to_etrs89_threaded(longitudes: Array,
                                             latitudes: Array)
                                             -> (Array, Array) {
    let mut longitudes_v = unsafe { longitudes.as_f64_slice().to_vec() };
    let mut latitudes_v = unsafe { latitudes.as_f64_slice().to_vec() };
    convert_to_etrs89_threaded_vec(&mut longitudes_v, &mut latitudes_v);
    (Array::from_vec(longitudes_v), Array::from_vec(latitudes_v))
}

/// A threaded, FFI-compatible wrapper for [`lonlat_bng::convert_etrs89_to_osgb36`](fn.convert_etrs89_to_osgb36.html)
///
/// # Examples
///
/// See `lonlat_bng::convert_to_bng_threaded` for examples
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn convert_etrs89_to_osgb36_threaded(eastings: Array,
                                                    northings: Array)
                                                    -> (Array, Array) {
    let mut eastings_vec = unsafe { eastings.as_f64_slice().to_vec() };
    let mut northings_vec = unsafe { northings.as_f64_slice().to_vec() };
    convert_etrs89_to_osgb36_threaded_vec(&mut eastings_vec, &mut northings_vec);
    (Array::from_vec(eastings_vec), Array::from_vec(northings_vec))
}

/// A threaded, FFI-compatible wrapper for [`lonlat_bng::convert_etrs89_to_ll`](fn.convert_etrs89_to_ll.html)
///
/// # Examples
///
/// See `lonlat_bng::convert_to_bng_threaded` for examples
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn convert_etrs89_to_ll_threaded(eastings: Array,
                                                northings: Array)
                                                -> (Array, Array) {
    let mut eastings_vec = unsafe { eastings.as_f64_slice().to_vec() };
    let mut northings_vec = unsafe { northings.as_f64_slice().to_vec() };
    convert_etrs89_to_ll_threaded_vec(&mut eastings_vec, &mut northings_vec);
    (Array::from_vec(eastings_vec), Array::from_vec(northings_vec))
}

/// A threaded, FFI-compatible wrapper for [`lonlat_bng::convert_osgb36_to_ll`](fn.convert_osgb36_to_ll.html)
///
/// # Examples
///
/// See `lonlat_bng::convert_to_bng_threaded` for examples
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn convert_osgb36_to_ll_threaded(eastings: Array,
                                                northings: Array)
                                                -> (Array, Array) {
    let mut eastings_vec = unsafe { eastings.as_f64_slice().to_vec() };
    let mut northings_vec = unsafe { northings.as_f64_slice().to_vec() };
    convert_osgb36_to_ll_threaded_vec(&mut eastings_vec, &mut northings_vec);
    (Array::from_vec(eastings_vec), Array::from_vec(northings_vec))
}

/// A threaded, FFI-compatible wrapper for [`lonlat_bng::convert_osgb36_to_etrs89`](fn.convert_osgb36_to_etrs89.html)
///
/// # Examples
///
/// See `lonlat_bng::convert_to_bng_threaded` for examples
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn convert_osgb36_to_etrs89_threaded(eastings: Array,
                                                    northings: Array)
                                                    -> (Array, Array) {
    let mut eastings_vec = unsafe { eastings.as_f64_slice().to_vec() };
    let mut northings_vec = unsafe { northings.as_f64_slice().to_vec() };
    convert_osgb36_to_etrs89_threaded_vec(&mut eastings_vec, &mut northings_vec);
    (Array::from_vec(eastings_vec), Array::from_vec(northings_vec))
}

/// A threaded, FFI-compatible wrapper for [`lonlat_bng::convert_epsg3857_to_wgs84`](fn.convert_epsg3857_to_wgs84.html)
///
/// # Examples
///
/// See `lonlat_bng::convert_to_bng_threaded` for examples
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn convert_epsg3857_to_wgs84_threaded(x: Array, y: Array) -> (Array, Array) {
    let mut x_vec = unsafe { x.as_f64_slice().to_vec() };
    let mut y_vec = unsafe { y.as_f64_slice().to_vec() };
    convert_epsg3857_to_wgs84_threaded_vec(&mut x_vec, &mut y_vec);
    (Array::from_vec(x_vec), Array::from_vec(y_vec))
}
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

use liquid_dsp::LiquidComplex32;

use num::complex::Complex;
use std::mem;
use std::ops::Rem;

// Rust does not support C complex numbers in the same way on 32 and 64 bit platforms:
// https://github.com/rust-lang/rfcs/issues/793
// Therefore this workaround is needed.
//
// Type `LiquidComplex32` is used here because standard `num::complex::Complex` does not have #[repr(C)].
// Function `ccexpf` is implemented in complex.c
//
// Notice that `ccexpf` does not return value, it just changes `z` value in place.
// For some reson it did not work correctly on ARM (BeagleBoneBlack) if `ccexpf` returned
// calculated `LiquidComplex32` struct. Although it worked on Mac OS X and 64/32 bit X86 Ubuntu.
extern {
    pub fn ccexpf(z: *mut LiquidComplex32);
}

use std::f32::consts::PI;

#[cfg(test)]
use std;

#[cfg(test)]
fn assert_eq_delta(a: f32, b: f32, delta: f32) {
    let relative_error = ((a - b) / b).abs();
    if relative_error >= delta {
        panic!("`(left == right)` (left: `{}`, right: `{}`)'", a, b);
    }
}

#[test]
fn test_cexpf() {
    // cargo test -- --nocapture
    // to see prints

    let mut a = Complex::<f32>::new(0.0, 0.0);
    unsafe {ccexpf(mem::transmute(&mut a))};
    assert_eq_delta(a.re, 1.0, 0.000001);
    assert_eq_delta(a.im, 0.0, 0.000001);

    let mut a = Complex::<f32>::new(1.0, 1.0);
    unsafe {ccexpf(mem::transmute(&mut a))};
    assert_eq_delta(a.re, 1.468694, 0.000001);
    assert_eq_delta(a.im, 2.2873552, 0.000001);

    let mut a = Complex::<f32>::new(70.0, 70.0);
    unsafe {ccexpf(mem::transmute(&mut a))};
    assert_eq_delta(a.re, 1593075600000000000000000000000f32, 0.000001);
    assert_eq_delta(a.im, 1946674600000000000000000000000f32, 0.000001);

    let mut a = Complex::<f32>::new(1_000_000.0, 1_000_000.0);
    unsafe {ccexpf(mem::transmute(&mut a))};
    assert_eq!(a.re, std::f32::INFINITY);
    assert_eq!(a.im, -std::f32::INFINITY);

    //println!("a={:?}", a);
}

pub fn convert_iqi16_to_complex(inbuf: &[u8]) -> Vec<Complex<f32>> {
    // inbuf consists of i16 IQ pairs that are represented as bytes here
    assert!(inbuf.len() % 4 == 0);

    let mut output = Vec::<Complex<f32>>::with_capacity(inbuf.len()/8);

    for b in inbuf.chunks(4) {
        let i: f32 = ((b[1] as i16) << 8 | b[0] as i16) as f32 / 32768.;
        let q: f32 = ((b[3] as i16) << 8 | b[2] as i16) as f32 / 32768.;

        output.push(Complex::<f32>::new(i, q));
    }

    output
}

pub fn convert_iqf32_to_complex(inbuf: &[u8]) -> Vec<Complex<f32>> {
    // inbuf consists of f32 IQ pairs that are represented as bytes here
    assert!(inbuf.len() % 8 == 0);

    let mut output = Vec::<Complex<f32>>::with_capacity(inbuf.len()/8);

    for b in inbuf.chunks(8) {
        let i: f32 = unsafe {mem::transmute::<u32, f32>(((b[3] as u32) << 24) | ((b[2] as u32) << 16) | ((b[1] as u32) << 8) | b[0] as u32)};
        let q: f32 = unsafe {mem::transmute::<u32, f32>(((b[7] as u32) << 24) | ((b[6] as u32) << 16) | ((b[5] as u32) << 8) | b[4] as u32)};

        output.push(Complex::<f32>::new(i, q));
    }

    output
}

pub fn shift_frequency(inbuf: &[Complex<f32>], samplenum: &mut u64, shift_hz: f64, samplerate: u32) -> Vec<Complex<f32>> {
    let mut output = Vec::<Complex<f32>>::with_capacity(inbuf.len());

    for sample in inbuf {
        let mut corrector = Complex::<f32>::new(0.0, -2. * PI * (shift_hz / samplerate as f64 * (*samplenum) as f64) as f32);
        unsafe { ccexpf(mem::transmute(&mut corrector))};
        output.push(sample * corrector);

        if (shift_hz / samplerate as f64 * *samplenum as f64).fract() == 0.0 {
            *samplenum = 1;
        }
        else {
            *samplenum += 1;
        }
    }

    output
}

#[test]
fn test_bench_shift_frequency() {
    // use as:
    // cargo test test_bench_shift_frequency -- release

    let mut samplenr: u64 = 0;
    let shift_hz: f64 = 815000.0;
    let samplerate: u32 = 2400000;

    let input: [u8; 1_000_000] = [0xAA; 1_000_000];
    let complex_input = convert_iqf32_to_complex(&input);

    let mut iterator = 0;
    loop {
        shift_frequency(&complex_input, &mut samplenr, shift_hz, samplerate);

        iterator += 1;
        if iterator > 300 {
            break;
        }
    }
}
<commit_msg>remove use std::ops::Rem<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

use liquid_dsp::LiquidComplex32;

use num::complex::Complex;
use std::mem;

// Rust does not support C complex numbers in the same way on 32 and 64 bit platforms:
// https://github.com/rust-lang/rfcs/issues/793
// Therefore this workaround is needed.
//
// Type `LiquidComplex32` is used here because standard `num::complex::Complex` does not have #[repr(C)].
// Function `ccexpf` is implemented in complex.c
//
// Notice that `ccexpf` does not return value, it just changes `z` value in place.
// For some reson it did not work correctly on ARM (BeagleBoneBlack) if `ccexpf` returned
// calculated `LiquidComplex32` struct. Although it worked on Mac OS X and 64/32 bit X86 Ubuntu.
extern {
    pub fn ccexpf(z: *mut LiquidComplex32);
}

use std::f32::consts::PI;

#[cfg(test)]
use std;

#[cfg(test)]
fn assert_eq_delta(a: f32, b: f32, delta: f32) {
    let relative_error = ((a - b) / b).abs();
    if relative_error >= delta {
        panic!("`(left == right)` (left: `{}`, right: `{}`)'", a, b);
    }
}

#[test]
fn test_cexpf() {
    // cargo test -- --nocapture
    // to see prints

    let mut a = Complex::<f32>::new(0.0, 0.0);
    unsafe {ccexpf(mem::transmute(&mut a))};
    assert_eq_delta(a.re, 1.0, 0.000001);
    assert_eq_delta(a.im, 0.0, 0.000001);

    let mut a = Complex::<f32>::new(1.0, 1.0);
    unsafe {ccexpf(mem::transmute(&mut a))};
    assert_eq_delta(a.re, 1.468694, 0.000001);
    assert_eq_delta(a.im, 2.2873552, 0.000001);

    let mut a = Complex::<f32>::new(70.0, 70.0);
    unsafe {ccexpf(mem::transmute(&mut a))};
    assert_eq_delta(a.re, 1593075600000000000000000000000f32, 0.000001);
    assert_eq_delta(a.im, 1946674600000000000000000000000f32, 0.000001);

    let mut a = Complex::<f32>::new(1_000_000.0, 1_000_000.0);
    unsafe {ccexpf(mem::transmute(&mut a))};
    assert_eq!(a.re, std::f32::INFINITY);
    assert_eq!(a.im, -std::f32::INFINITY);

    //println!("a={:?}", a);
}

pub fn convert_iqi16_to_complex(inbuf: &[u8]) -> Vec<Complex<f32>> {
    // inbuf consists of i16 IQ pairs that are represented as bytes here
    assert!(inbuf.len() % 4 == 0);

    let mut output = Vec::<Complex<f32>>::with_capacity(inbuf.len()/8);

    for b in inbuf.chunks(4) {
        let i: f32 = ((b[1] as i16) << 8 | b[0] as i16) as f32 / 32768.;
        let q: f32 = ((b[3] as i16) << 8 | b[2] as i16) as f32 / 32768.;

        output.push(Complex::<f32>::new(i, q));
    }

    output
}

pub fn convert_iqf32_to_complex(inbuf: &[u8]) -> Vec<Complex<f32>> {
    // inbuf consists of f32 IQ pairs that are represented as bytes here
    assert!(inbuf.len() % 8 == 0);

    let mut output = Vec::<Complex<f32>>::with_capacity(inbuf.len()/8);

    for b in inbuf.chunks(8) {
        let i: f32 = unsafe {mem::transmute::<u32, f32>(((b[3] as u32) << 24) | ((b[2] as u32) << 16) | ((b[1] as u32) << 8) | b[0] as u32)};
        let q: f32 = unsafe {mem::transmute::<u32, f32>(((b[7] as u32) << 24) | ((b[6] as u32) << 16) | ((b[5] as u32) << 8) | b[4] as u32)};

        output.push(Complex::<f32>::new(i, q));
    }

    output
}

pub fn shift_frequency(inbuf: &[Complex<f32>], samplenum: &mut u64, shift_hz: f64, samplerate: u32) -> Vec<Complex<f32>> {
    let mut output = Vec::<Complex<f32>>::with_capacity(inbuf.len());

    for sample in inbuf {
        let mut corrector = Complex::<f32>::new(0.0, -2. * PI * (shift_hz / samplerate as f64 * (*samplenum) as f64) as f32);
        unsafe { ccexpf(mem::transmute(&mut corrector))};
        output.push(sample * corrector);

        if (shift_hz / samplerate as f64 * *samplenum as f64).fract() == 0.0 {
            *samplenum = 1;
        }
        else {
            *samplenum += 1;
        }
    }

    output
}

#[test]
fn test_bench_shift_frequency() {
    // use as:
    // cargo test test_bench_shift_frequency -- release

    let mut samplenr: u64 = 0;
    let shift_hz: f64 = 815000.0;
    let samplerate: u32 = 2400000;

    let input: [u8; 1_000_000] = [0xAA; 1_000_000];
    let complex_input = convert_iqf32_to_complex(&input);

    let mut iterator = 0;
    loop {
        shift_frequency(&complex_input, &mut samplenr, shift_hz, samplerate);

        iterator += 1;
        if iterator > 300 {
            break;
        }
    }
}
<|endoftext|>


<commit_before>mod messages;

use std::io;
use std::io::prelude::*;
use crc16::*;
pub use self::messages::{ReqClockSet, ResInitialize, ResInfo,
                         ResCalibration, ResPowerBuffer, ResPowerUse,
                         ResClockInfo, DateTime, Pulses};
use self::messages::{Message, MessageId, ReqHeader, ReqSwitch, ReqPowerBuffer};

const HEADER: [u8; 4] = [5, 5, 3, 3];
const FOOTER: [u8; 2] = [13, 10];
const EOM: u8 = 10;
const CRC_SIZE: usize = 4;

/// Plugwise communication snooper setting.
pub enum ProtocolSnoop<'a> {
    /// Log nothing (default).
    Nothing,
    /// Log developer readable data of the Plugwise communication.
    Debug(&'a mut Write),
    /// Log the relevant raw serial communication of the Plugwise communication.
    Raw(&'a mut Write),
    /// Log all raw serial communication of the Plugwise communication (very verbose, which
    /// actually doesn't make much sense, unless you're a developer of Plugwise devices).
    All(&'a mut Write)
}

pub struct Protocol<'a, R> {
    reader: io::BufReader<R>,
    snoop: ProtocolSnoop<'a>
}

impl<'a, R: Read + Write> Protocol<'a, R> {
    /// Wrap IO entity for Plugwise protocol handling
    pub fn new(port: R) -> Protocol<'a, R> {
        Protocol {
            reader: io::BufReader::with_capacity(1000, port),
            snoop: ProtocolSnoop::Nothing
        }
    }

    pub fn set_snoop(&mut self, snoop: ProtocolSnoop<'a>) {
        self.snoop = snoop;
    }

    /// Send payload
    fn send_message_raw(&mut self, payload: &[u8]) -> io::Result<()> {
        let crc = format!("{:04X}", State::<XMODEM>::calculate(payload)).into_bytes();

        try!(self.reader.get_mut().write(&HEADER));
        try!(self.reader.get_mut().write(payload));
        try!(self.reader.get_mut().write(&crc));
        try!(self.reader.get_mut().write(&FOOTER));

        match self.snoop {
            ProtocolSnoop::Raw(ref mut writer) |
            ProtocolSnoop::All(ref mut writer) => {
                try!(writer.write_fmt(format_args!("> ")));
                try!(writer.write(payload));
                try!(writer.write(&crc));
                try!(writer.write(&[b'\n']));
            },
            _ => {}
        }

        Ok(())
    }

    /// Wait until a Plugwise message has been received (and skip debugging stuff)
    fn receive_plugwise_message_raw(&mut self) -> io::Result<(Vec<u8>, usize, usize)> {
        loop {
            let mut buf = vec![];

            let _ = try!(self.reader.read_until(EOM, &mut buf));

            let header_pos = buf.windows(HEADER.len()).position(|x| *x==HEADER);

            if header_pos.is_some() {
                let header_pos = header_pos.unwrap(); // that would be a surprise when this panics

                let footer_pos = match buf.windows(FOOTER.len()).rposition(|x| *x==FOOTER){
                    None => return Err(io::Error::new(io::ErrorKind::Other,
                                                      "unable to locate footer in received message")),
                                                      Some(v) => v
                };

                match self.snoop {
                    ProtocolSnoop::Raw(ref mut writer) |
                    ProtocolSnoop::All(ref mut writer) => {
                        let (_, part) = buf.split_at(header_pos + HEADER.len());
                        let (part, _) = part.split_at(footer_pos - (header_pos + HEADER.len()));
                        try!(writer.write_fmt(format_args!("< ")));
                        try!(writer.write(part));
                        try!(writer.write(&[b'\n']));
                    },
                    _ => {}
                }

                return Ok((buf, header_pos, footer_pos))
            }
            else
            {
                match self.snoop {
                    ProtocolSnoop::All(ref mut writer) => {
                        let footer_pos = buf.windows(FOOTER.len()).rposition(|x| *x==FOOTER);

                        if let Some(pos) = footer_pos {
                            let (part, _) = buf.split_at(pos);
                            try!(writer.write_fmt(format_args!("< ")));
                            try!(writer.write(part));
                            try!(writer.write(&[b'\n']));
                        }
                    }
                    _ => {}
                }
            }
        }
    }

    /// Wait until a complete and valid message has been received
    fn receive_message_raw(&mut self) -> io::Result<Vec<u8>> {
        let (buf, header_pos, footer_pos) = try!(self.receive_plugwise_message_raw());

        // chop off header, footer and CRC
        let payload = buf.iter().take(footer_pos - CRC_SIZE).skip(header_pos + HEADER.len());
        let crc = buf.iter().skip(footer_pos - CRC_SIZE).take(CRC_SIZE);
        let crc = crc.take(4).fold(0, |acc, &item| {
            acc << 4 | match item {
                b'0' => 0, b'1' => 1, b'2' => 2,  b'3' => 3,  b'4' => 4,  b'5' => 5,  b'6' => 6,  b'7' => 7,
                b'8' => 8, b'9' => 9, b'A' => 10, b'B' => 11, b'C' => 12, b'D' => 13, b'E' => 14, b'F' => 15,
                _ => 0
            }
        });

        // CRC check
        let mut state = State::<XMODEM>::new();
        for byte in payload {
            state.update(&[*byte]);
        }

        if crc != state.get() {
            return Err(io::Error::new(io::ErrorKind::Other, "CRC error"));
        }

        let payload = buf.iter().take(footer_pos - CRC_SIZE).skip(header_pos + HEADER.len());

        Ok(payload.cloned().collect())
    }

    /// Keep receiving messages until the given message identifier has been received
    fn expect_message(&mut self, expected_message_id: MessageId) -> io::Result<Message> {
        loop {
            let msg = try!(self.receive_message_raw());
            let msg = try!(Message::from_payload(&msg));

            match self.snoop {
                ProtocolSnoop::Debug(ref mut writer) => {
                    try!(writer.write_fmt(format_args!("< {:?}\n", msg)));
                },
                _ => {}
            }

            if msg.to_message_id() == expected_message_id {
                return Ok(msg)
            }
        }
    }

    fn wait_for_mac_ack(&mut self, expected_mac: u64) -> io::Result<()> {
        loop {
            let ack = try!(self.expect_message(MessageId::Ack));
            if let Message::Ack(_, ack) = ack {
                if let Some(ack_mac) = ack.mac {
                    if ack_mac == expected_mac {
                        break;
                    }
                }
            }
        }

        Ok(())
    }

    /// Send message
    fn send_message(&mut self, message: Message) -> io::Result<()> {
        match self.snoop {
            ProtocolSnoop::Debug(ref mut writer) => {
                try!(writer.write_fmt(format_args!("> {:?}\n", message)));
            },
            _ => {}
        }
        let msg = try!(message.to_payload());
        try!(self.send_message_raw(&msg));
        Ok(())
    }

    /// Initialize the Plugwise USB stick
    pub fn initialize(&mut self) -> io::Result<ResInitialize> {
        try!(self.send_message(Message::ReqInitialize));

        let msg = try!(self.expect_message(MessageId::ResInitialize));

        match msg {
            Message::ResInitialize(_, res) => Ok(res),
            _ => Err(io::Error::new(io::ErrorKind::Other, "unexpected initialization response"))
        }
    }

    /// Get info from a circle
    pub fn get_info(&mut self, mac: u64) -> io::Result<ResInfo> {
        try!(self.send_message(Message::ReqInfo(ReqHeader{mac: mac})));

        let msg = try!(self.expect_message(MessageId::ResInfo));

        match msg {
            Message::ResInfo(_, res) => Ok(res),
            _ => Err(io::Error::new(io::ErrorKind::Other, "unexpected information response"))
        }
    }

    /// Switch a circle
    pub fn switch(&mut self, mac: u64, on: bool) -> io::Result<()> {
        try!(self.send_message(Message::ReqSwitch(ReqHeader{mac: mac}, ReqSwitch{on: on})));

        try!(self.wait_for_mac_ack(mac));

        Ok(())
    }

    /// Calibrate a circle
    pub fn calibrate(&mut self, mac: u64) -> io::Result<ResCalibration> {
        try!(self.send_message(Message::ReqCalibration(ReqHeader{mac: mac})));

        let msg = try!(self.expect_message(MessageId::ResCalibration));

        match msg {
            Message::ResCalibration(_, res) => Ok(res),
            _ => Err(io::Error::new(io::ErrorKind::Other, "unexpected information response"))
        }
    }

    /// Retrieve power buffer
    pub fn get_power_buffer(&mut self, mac: u64, addr: u32) -> io::Result<ResPowerBuffer> {
        try!(self.send_message( Message::ReqPowerBuffer(ReqHeader{mac: mac},
                                                        ReqPowerBuffer{logaddr: addr})));

        let msg = try!(self.expect_message(MessageId::ResPowerBuffer));

        match msg {
            Message::ResPowerBuffer(_, res) => Ok(res),
            _ => Err(io::Error::new(io::ErrorKind::Other, "unexpected information response"))
        }
    }

    /// Retrieve actual power usage
    pub fn get_power_usage(&mut self, mac: u64) -> io::Result<ResPowerUse> {
        try!(self.send_message(Message::ReqPowerUse(ReqHeader{mac: mac})));

        let msg = try!(self.expect_message(MessageId::ResPowerUse));

        match msg {
            Message::ResPowerUse(_, res) => Ok(res),
            _ => Err(io::Error::new(io::ErrorKind::Other, "unexpected information response"))
        }
    }

    /// Retrieve actual power usage
    pub fn get_clock_info(&mut self, mac: u64) -> io::Result<ResClockInfo> {
        try!(self.send_message(Message::ReqClockInfo(ReqHeader{mac: mac})));

        let msg = try!(self.expect_message(MessageId::ResClockInfo));

        match msg {
            Message::ResClockInfo(_, res) => Ok(res),
            _ => Err(io::Error::new(io::ErrorKind::Other, "unexpected information response"))
        }
    }

    /// Set clock
    pub fn set_clock(&mut self, mac: u64, clock_set: ReqClockSet) -> io::Result<()> {
        try!(self.send_message(Message::ReqClockSet(ReqHeader{mac: mac}, clock_set)));

        try!(self.wait_for_mac_ack(mac));

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    // Note that these test relies on that the implemention and stub yields
    // errors and panics when something strange happens.

    use super::super::stub;
    use super::*;
    use time;

    #[test]
    fn stub_initialize() {
        let port = stub::Stub::new();
        let mut protocol = Protocol::new(port);

        assert_eq!(true, protocol.initialize().unwrap().is_online);
    }

    #[test]
    fn stub_switch_and_info() {
        let mac1 = 0x0123456789abcdef;
        let mac2 = 0xfedcba9876543210;
        let port = stub::Stub::new();
        let mut protocol = Protocol::new(port);

        let info1 = protocol.get_info(mac1).unwrap();
        let info2 = protocol.get_info(mac2).unwrap();

        assert_eq!(false, info1.relay_state);
        assert_eq!(false, info2.relay_state);

        protocol.switch(mac1, true).unwrap();
        protocol.switch(mac2, false).unwrap();

        let info1 = protocol.get_info(mac1).unwrap();
        let info2 = protocol.get_info(mac2).unwrap();

        assert_eq!(true, info1.relay_state);
        assert_eq!(false, info2.relay_state);

        protocol.switch(mac2, true).unwrap();

        let info1 = protocol.get_info(mac1).unwrap();
        let info2 = protocol.get_info(mac2).unwrap();

        assert_eq!(true, info1.relay_state);
        assert_eq!(true, info2.relay_state);

        protocol.switch(mac1, false).unwrap();

        let info1 = protocol.get_info(mac1).unwrap();
        let info2 = protocol.get_info(mac2).unwrap();

        assert_eq!(false, info1.relay_state);
        assert_eq!(true, info2.relay_state);
    }

    #[test]
    fn stub_set_clock() {
        let mac = 0x0123456789abcdef;
        let port = stub::Stub::new();
        let mut protocol = Protocol::new(port);

        protocol.set_clock(mac, ReqClockSet::new_from_tm(time::now())).unwrap();
    }

    #[test]
    fn stub_calibrate() {
        let mac = 0x0123456789abcdef;
        let port = stub::Stub::new();
        let mut protocol = Protocol::new(port);

        let _ = protocol.calibrate(mac).unwrap();
    }

    #[test]
    fn stub_get_power_buffer() {
        let mac = 0x0123456789abcdef;
        let port = stub::Stub::new();
        let mut protocol = Protocol::new(port);

        let _ = protocol.get_power_buffer(mac, 0).unwrap();
    }

    #[test]
    fn stub_get_power_usage() {
        let mac = 0x0123456789abcdef;
        let port = stub::Stub::new();
        let mut protocol = Protocol::new(port);

        let _ = protocol.get_power_usage(mac).unwrap();
    }

    #[test]
    fn stub_get_clock_info() {
        let mac = 0x0123456789abcdef;
        let port = stub::Stub::new();
        let mut protocol = Protocol::new(port);

        let _ = protocol.get_clock_info(mac).unwrap();
    }
}
<commit_msg>Made send and expect/ack common<commit_after>mod messages;

use std::io;
use std::io::prelude::*;
use crc16::*;
pub use self::messages::{ReqClockSet, ResInitialize, ResInfo,
                         ResCalibration, ResPowerBuffer, ResPowerUse,
                         ResClockInfo, DateTime, Pulses};
use self::messages::{Message, MessageId, ReqHeader, ReqSwitch, ReqPowerBuffer};

const HEADER: [u8; 4] = [5, 5, 3, 3];
const FOOTER: [u8; 2] = [13, 10];
const EOM: u8 = 10;
const CRC_SIZE: usize = 4;

/// Plugwise communication snooper setting.
pub enum ProtocolSnoop<'a> {
    /// Log nothing (default).
    Nothing,
    /// Log developer readable data of the Plugwise communication.
    Debug(&'a mut Write),
    /// Log the relevant raw serial communication of the Plugwise communication.
    Raw(&'a mut Write),
    /// Log all raw serial communication of the Plugwise communication (very verbose, which
    /// actually doesn't make much sense, unless you're a developer of Plugwise devices).
    All(&'a mut Write)
}

pub struct Protocol<'a, R> {
    reader: io::BufReader<R>,
    snoop: ProtocolSnoop<'a>
}

impl<'a, R: Read + Write> Protocol<'a, R> {
    /// Wrap IO entity for Plugwise protocol handling
    pub fn new(port: R) -> Protocol<'a, R> {
        Protocol {
            reader: io::BufReader::with_capacity(1000, port),
            snoop: ProtocolSnoop::Nothing
        }
    }

    pub fn set_snoop(&mut self, snoop: ProtocolSnoop<'a>) {
        self.snoop = snoop;
    }

    /// Send payload
    fn send_message_raw(&mut self, payload: &[u8]) -> io::Result<()> {
        let crc = format!("{:04X}", State::<XMODEM>::calculate(payload)).into_bytes();

        try!(self.reader.get_mut().write(&HEADER));
        try!(self.reader.get_mut().write(payload));
        try!(self.reader.get_mut().write(&crc));
        try!(self.reader.get_mut().write(&FOOTER));

        match self.snoop {
            ProtocolSnoop::Raw(ref mut writer) |
            ProtocolSnoop::All(ref mut writer) => {
                try!(writer.write_fmt(format_args!("> ")));
                try!(writer.write(payload));
                try!(writer.write(&crc));
                try!(writer.write(&[b'\n']));
            },
            _ => {}
        }

        Ok(())
    }

    /// Wait until a Plugwise message has been received (and skip debugging stuff)
    fn receive_plugwise_message_raw(&mut self) -> io::Result<(Vec<u8>, usize, usize)> {
        loop {
            let mut buf = vec![];

            let _ = try!(self.reader.read_until(EOM, &mut buf));

            let header_pos = buf.windows(HEADER.len()).position(|x| *x==HEADER);

            if header_pos.is_some() {
                let header_pos = header_pos.unwrap(); // that would be a surprise when this panics

                let footer_pos = match buf.windows(FOOTER.len()).rposition(|x| *x==FOOTER){
                    None => return Err(io::Error::new(io::ErrorKind::Other,
                                                      "unable to locate footer in received message")),
                                                      Some(v) => v
                };

                match self.snoop {
                    ProtocolSnoop::Raw(ref mut writer) |
                    ProtocolSnoop::All(ref mut writer) => {
                        let (_, part) = buf.split_at(header_pos + HEADER.len());
                        let (part, _) = part.split_at(footer_pos - (header_pos + HEADER.len()));
                        try!(writer.write_fmt(format_args!("< ")));
                        try!(writer.write(part));
                        try!(writer.write(&[b'\n']));
                    },
                    _ => {}
                }

                return Ok((buf, header_pos, footer_pos))
            }
            else
            {
                match self.snoop {
                    ProtocolSnoop::All(ref mut writer) => {
                        let footer_pos = buf.windows(FOOTER.len()).rposition(|x| *x==FOOTER);

                        if let Some(pos) = footer_pos {
                            let (part, _) = buf.split_at(pos);
                            try!(writer.write_fmt(format_args!("< ")));
                            try!(writer.write(part));
                            try!(writer.write(&[b'\n']));
                        }
                    }
                    _ => {}
                }
            }
        }
    }

    /// Wait until a complete and valid message has been received
    fn receive_message_raw(&mut self) -> io::Result<Vec<u8>> {
        let (buf, header_pos, footer_pos) = try!(self.receive_plugwise_message_raw());

        // chop off header, footer and CRC
        let payload = buf.iter().take(footer_pos - CRC_SIZE).skip(header_pos + HEADER.len());
        let crc = buf.iter().skip(footer_pos - CRC_SIZE).take(CRC_SIZE);
        let crc = crc.take(4).fold(0, |acc, &item| {
            acc << 4 | match item {
                b'0' => 0, b'1' => 1, b'2' => 2,  b'3' => 3,  b'4' => 4,  b'5' => 5,  b'6' => 6,  b'7' => 7,
                b'8' => 8, b'9' => 9, b'A' => 10, b'B' => 11, b'C' => 12, b'D' => 13, b'E' => 14, b'F' => 15,
                _ => 0
            }
        });

        // CRC check
        let mut state = State::<XMODEM>::new();
        for byte in payload {
            state.update(&[*byte]);
        }

        if crc != state.get() {
            return Err(io::Error::new(io::ErrorKind::Other, "CRC error"));
        }

        let payload = buf.iter().take(footer_pos - CRC_SIZE).skip(header_pos + HEADER.len());

        Ok(payload.cloned().collect())
    }

    /// Keep receiving messages until the given message identifier has been received
    fn expect_message(&mut self, expected_message_id: MessageId) -> io::Result<Message> {
        loop {
            let msg = try!(self.receive_message_raw());
            let msg = try!(Message::from_payload(&msg));

            match self.snoop {
                ProtocolSnoop::Debug(ref mut writer) => {
                    try!(writer.write_fmt(format_args!("< {:?}\n", msg)));
                },
                _ => {}
            }

            if msg.to_message_id() == expected_message_id {
                return Ok(msg)
            }
        }
    }

    fn wait_for_mac_ack(&mut self, expected_mac: u64) -> io::Result<()> {
        loop {
            let ack = try!(self.expect_message(MessageId::Ack));
            if let Message::Ack(_, ack) = ack {
                if let Some(ack_mac) = ack.mac {
                    if ack_mac == expected_mac {
                        break;
                    }
                }
            }
        }

        Ok(())
    }

    /// Send message
    fn send_message(&mut self, message: Message) -> io::Result<()> {
        match self.snoop {
            ProtocolSnoop::Debug(ref mut writer) => {
                try!(writer.write_fmt(format_args!("> {:?}\n", message)));
            },
            _ => {}
        }
        let msg = try!(message.to_payload());
        try!(self.send_message_raw(&msg));
        Ok(())
    }

    /// Send a message and wait for response
    fn send_and_expect(&mut self, message: Message, expected: MessageId) -> io::Result<Message> {
        try!(self.send_message(message));
        Ok(try!(self.expect_message(expected)))
    }

    /// Send a message and wait for acknowledge with a mac
    fn send_and_expect_ack(&mut self, message: Message, mac: u64) -> io::Result<()> {
        try!(self.send_message(message));
        try!(self.wait_for_mac_ack(mac));
        Ok(())
    }

    /// Initialize the Plugwise USB stick
    pub fn initialize(&mut self) -> io::Result<ResInitialize> {
        let msg = try!(self.send_and_expect(Message::ReqInitialize,
                                            MessageId::ResInitialize));

        match msg {
            Message::ResInitialize(_, res) => Ok(res),
            _ => Err(io::Error::new(io::ErrorKind::Other, "unexpected initialization response"))
        }
    }

    /// Get info from a circle
    pub fn get_info(&mut self, mac: u64) -> io::Result<ResInfo> {
        let msg = try!(self.send_and_expect(Message::ReqInfo(ReqHeader{mac: mac}),
                                            MessageId::ResInfo));

        match msg {
            Message::ResInfo(_, res) => Ok(res),
            _ => Err(io::Error::new(io::ErrorKind::Other, "unexpected information response"))
        }
    }

    /// Switch a circle
    pub fn switch(&mut self, mac: u64, on: bool) -> io::Result<()> {
        try!(self.send_and_expect_ack(Message::ReqSwitch(ReqHeader{mac: mac},
                                                         ReqSwitch{on: on}),
                                      mac));
        Ok(())
    }

    /// Calibrate a circle
    pub fn calibrate(&mut self, mac: u64) -> io::Result<ResCalibration> {
        let msg = try!(self.send_and_expect(Message::ReqCalibration(ReqHeader{mac: mac}),
                                            MessageId::ResCalibration));

        match msg {
            Message::ResCalibration(_, res) => Ok(res),
            _ => Err(io::Error::new(io::ErrorKind::Other, "unexpected information response"))
        }
    }

    /// Retrieve power buffer
    pub fn get_power_buffer(&mut self, mac: u64, addr: u32) -> io::Result<ResPowerBuffer> {
        let msg = try!(self.send_and_expect(Message::ReqPowerBuffer(ReqHeader{mac: mac},
                                                                    ReqPowerBuffer{logaddr: addr}),
                                            MessageId::ResPowerBuffer));

        match msg {
            Message::ResPowerBuffer(_, res) => Ok(res),
            _ => Err(io::Error::new(io::ErrorKind::Other, "unexpected information response"))
        }
    }

    /// Retrieve actual power usage
    pub fn get_power_usage(&mut self, mac: u64) -> io::Result<ResPowerUse> {
        let msg = try!(self.send_and_expect(Message::ReqPowerUse(ReqHeader{mac: mac}),
                                            MessageId::ResPowerUse));

        match msg {
            Message::ResPowerUse(_, res) => Ok(res),
            _ => Err(io::Error::new(io::ErrorKind::Other, "unexpected information response"))
        }
    }

    /// Retrieve actual power usage
    pub fn get_clock_info(&mut self, mac: u64) -> io::Result<ResClockInfo> {
        let msg = try!(self.send_and_expect(Message::ReqClockInfo(ReqHeader{mac: mac}),
                                            MessageId::ResClockInfo));

        match msg {
            Message::ResClockInfo(_, res) => Ok(res),
            _ => Err(io::Error::new(io::ErrorKind::Other, "unexpected information response"))
        }
    }

    /// Set clock
    pub fn set_clock(&mut self, mac: u64, clock_set: ReqClockSet) -> io::Result<()> {
        try!(self.send_and_expect_ack(Message::ReqClockSet(ReqHeader{mac: mac},
                                                           clock_set),
                                      mac));
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    // Note that these test relies on that the implemention and stub yields
    // errors and panics when something strange happens.

    use super::super::stub;
    use super::*;
    use time;

    #[test]
    fn stub_initialize() {
        let port = stub::Stub::new();
        let mut protocol = Protocol::new(port);

        assert_eq!(true, protocol.initialize().unwrap().is_online);
    }

    #[test]
    fn stub_switch_and_info() {
        let mac1 = 0x0123456789abcdef;
        let mac2 = 0xfedcba9876543210;
        let port = stub::Stub::new();
        let mut protocol = Protocol::new(port);

        let info1 = protocol.get_info(mac1).unwrap();
        let info2 = protocol.get_info(mac2).unwrap();

        assert_eq!(false, info1.relay_state);
        assert_eq!(false, info2.relay_state);

        protocol.switch(mac1, true).unwrap();
        protocol.switch(mac2, false).unwrap();

        let info1 = protocol.get_info(mac1).unwrap();
        let info2 = protocol.get_info(mac2).unwrap();

        assert_eq!(true, info1.relay_state);
        assert_eq!(false, info2.relay_state);

        protocol.switch(mac2, true).unwrap();

        let info1 = protocol.get_info(mac1).unwrap();
        let info2 = protocol.get_info(mac2).unwrap();

        assert_eq!(true, info1.relay_state);
        assert_eq!(true, info2.relay_state);

        protocol.switch(mac1, false).unwrap();

        let info1 = protocol.get_info(mac1).unwrap();
        let info2 = protocol.get_info(mac2).unwrap();

        assert_eq!(false, info1.relay_state);
        assert_eq!(true, info2.relay_state);
    }

    #[test]
    fn stub_set_clock() {
        let mac = 0x0123456789abcdef;
        let port = stub::Stub::new();
        let mut protocol = Protocol::new(port);

        protocol.set_clock(mac, ReqClockSet::new_from_tm(time::now())).unwrap();
    }

    #[test]
    fn stub_calibrate() {
        let mac = 0x0123456789abcdef;
        let port = stub::Stub::new();
        let mut protocol = Protocol::new(port);

        let _ = protocol.calibrate(mac).unwrap();
    }

    #[test]
    fn stub_get_power_buffer() {
        let mac = 0x0123456789abcdef;
        let port = stub::Stub::new();
        let mut protocol = Protocol::new(port);

        let _ = protocol.get_power_buffer(mac, 0).unwrap();
    }

    #[test]
    fn stub_get_power_usage() {
        let mac = 0x0123456789abcdef;
        let port = stub::Stub::new();
        let mut protocol = Protocol::new(port);

        let _ = protocol.get_power_usage(mac).unwrap();
    }

    #[test]
    fn stub_get_clock_info() {
        let mac = 0x0123456789abcdef;
        let port = stub::Stub::new();
        let mut protocol = Protocol::new(port);

        let _ = protocol.get_clock_info(mac).unwrap();
    }
}
<|endoftext|>


<commit_before>use crate::error::ErrorImpl;
use std::fmt::{self, Debug};

impl ErrorImpl<()> {
    pub(crate) fn display(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.error())?;

        if f.alternate() {
            for cause in self.chain().skip(1) {
                write!(f, ": {}", cause)?;
            }
        }

        Ok(())
    }

    pub(crate) fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if f.alternate() {
            return Debug::fmt(self.error(), f);
        }

        writeln!(f, "{}", self.error())?;

        let mut chain = self.chain().skip(1).enumerate().peekable();
        if let Some((n, error)) = chain.next() {
            write!(f, "\nCaused by:\n    ")?;
            if chain.peek().is_some() {
                write!(f, "{}: ", n)?;
            }
            writeln!(f, "{}", error)?;
            for (n, error) in chain {
                writeln!(f, "    {}: {}", n, error)?;
            }
        }

        #[cfg(backtrace)]
        {
            use std::backtrace::BacktraceStatus;

            let backtrace = self.backtrace();
            if let BacktraceStatus::Captured = backtrace.status() {
                let mut backtrace = backtrace.to_string();
                if backtrace.starts_with("stack backtrace:") {
                    // Capitalize to match "Caused by:"
                    backtrace.replace_range(0..1, "S");
                }
                write!(f, "\n{}", backtrace)?;
            }
        }

        Ok(())
    }
}
<commit_msg>Simplify cause chain printing<commit_after>use crate::error::ErrorImpl;
use crate::Chain;
use std::fmt::{self, Debug};

impl ErrorImpl<()> {
    pub(crate) fn display(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.error())?;

        if f.alternate() {
            for cause in self.chain().skip(1) {
                write!(f, ": {}", cause)?;
            }
        }

        Ok(())
    }

    pub(crate) fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let error = self.error();

        if f.alternate() {
            return Debug::fmt(error, f);
        }

        writeln!(f, "{}", error)?;

        if let Some(cause) = error.source() {
            write!(f, "\nCaused by:\n")?;
            let multiple = cause.source().is_some();
            for (n, error) in Chain::new(cause).enumerate() {
                write!(f, "    ")?;
                if multiple {
                    write!(f, "{}: ", n)?;
                }
                writeln!(f, "{}", error)?;
            }
        }

        #[cfg(backtrace)]
        {
            use std::backtrace::BacktraceStatus;

            let backtrace = self.backtrace();
            if let BacktraceStatus::Captured = backtrace.status() {
                let mut backtrace = backtrace.to_string();
                if backtrace.starts_with("stack backtrace:") {
                    // Capitalize to match "Caused by:"
                    backtrace.replace_range(0..1, "S");
                }
                write!(f, "\n{}", backtrace)?;
            }
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::fs;
use std::path::Path;

use serde::Serialize;

use crate::error::Result;

#[derive(Serialize)]
struct Rustfmt {
    normalize_doc_attributes: bool,
}

pub fn write_rustfmt_config(outdir: impl AsRef<Path>) -> Result<()> {
    let config = Rustfmt {
        normalize_doc_attributes: true,
    };

    let toml_string = toml::to_string(&config)?;

    let rustfmt_config_path = outdir.as_ref().join("rustfmt.toml");
    fs::write(rustfmt_config_path, toml_string)?;

    Ok(())
}
<commit_msg>Disable reorderings in rustfmt<commit_after>use std::fs;
use std::path::Path;

use serde::Serialize;

use crate::error::Result;

#[derive(Serialize)]
struct Rustfmt {
    normalize_doc_attributes: bool,
    reorder_imports: bool,
    reorder_modules: bool,
}

pub fn write_rustfmt_config(outdir: impl AsRef<Path>) -> Result<()> {
    let config = Rustfmt {
        normalize_doc_attributes: true,
        reorder_imports: false,
        reorder_modules: false,
    };

    let toml_string = toml::to_string(&config)?;

    let rustfmt_config_path = outdir.as_ref().join("rustfmt.toml");
    fs::write(rustfmt_config_path, toml_string)?;

    Ok(())
}
<|endoftext|>