<commit_before>// Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved.
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
//
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-Google file.

//! Traits and structs to control Linux in-kernel vhost drivers.
//!
//! The initial vhost implementation is a part of the Linux kernel and uses ioctl interface to
//! communicate with userspace applications. This sub module provides ioctl based interfaces to
//! control the in-kernel net, scsi, vsock vhost drivers.

use std::os::unix::io::{AsRawFd, RawFd};

use vm_memory::{Address, GuestAddress, GuestAddressSpace, GuestMemory, GuestUsize};
use vmm_sys_util::eventfd::EventFd;
use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref};

use super::{
    Error, Result, VhostBackend, VhostUserDirtyLogRegion, VhostUserMemoryRegionInfo,
    VringConfigData, VHOST_MAX_MEMORY_REGIONS,
};

pub mod vhost_binding;
use self::vhost_binding::*;

#[cfg(feature = "vhost-vsock")]
pub mod vsock;

#[inline]
fn ioctl_result<T>(rc: i32, res: T) -> Result<T> {
    if rc < 0 {
        Err(Error::IoctlError(std::io::Error::last_os_error()))
    } else {
        Ok(res)
    }
}

/// Represent an in-kernel vhost device backend.
pub trait VhostKernBackend: AsRawFd {
    /// Associated type to access guest memory.
    type AS: GuestAddressSpace;

    /// Get the object to access the guest's memory.
    fn mem(&self) -> &Self::AS;

    /// Check whether the ring configuration is valid.
    fn is_valid(&self, config_data: &VringConfigData) -> bool {
        let queue_size = config_data.queue_size;
        if queue_size > config_data.queue_max_size
            || queue_size == 0
            || (queue_size & (queue_size - 1)) != 0
        {
            return false;
        }

        let m = self.mem().memory();
        let desc_table_size = 16 * u64::from(queue_size) as GuestUsize;
        let avail_ring_size = 6 + 2 * u64::from(queue_size) as GuestUsize;
        let used_ring_size = 6 + 8 * u64::from(queue_size) as GuestUsize;
        if GuestAddress(config_data.desc_table_addr)
            .checked_add(desc_table_size)
            .map_or(true, |v| !m.address_in_range(v))
        {
            return false;
        }
        if GuestAddress(config_data.avail_ring_addr)
            .checked_add(avail_ring_size)
            .map_or(true, |v| !m.address_in_range(v))
        {
            return false;
        }
        if GuestAddress(config_data.used_ring_addr)
            .checked_add(used_ring_size)
            .map_or(true, |v| !m.address_in_range(v))
        {
            return false;
        }

        config_data.is_log_addr_valid()
    }
}

impl<T: VhostKernBackend> VhostBackend for T {
    /// Get a bitmask of supported virtio/vhost features.
    fn get_features(&self) -> Result<u64> {
        let mut avail_features: u64 = 0;
        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl_with_mut_ref(self, VHOST_GET_FEATURES(), &mut avail_features) };
        ioctl_result(ret, avail_features)
    }

    /// Inform the vhost subsystem which features to enable. This should be a subset of
    /// supported features from VHOST_GET_FEATURES.
    ///
    /// # Arguments
    /// * `features` - Bitmask of features to set.
    fn set_features(&self, features: u64) -> Result<()> {
        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl_with_ref(self, VHOST_SET_FEATURES(), &features) };
        ioctl_result(ret, ())
    }

    /// Set the current process as the owner of this file descriptor.
    /// This must be run before any other vhost ioctls.
    fn set_owner(&self) -> Result<()> {
        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl(self, VHOST_SET_OWNER()) };
        ioctl_result(ret, ())
    }

    fn reset_owner(&self) -> Result<()> {
        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl(self, VHOST_RESET_OWNER()) };
        ioctl_result(ret, ())
    }

    /// Set the guest memory mappings for vhost to use.
    fn set_mem_table(&self, regions: &[VhostUserMemoryRegionInfo]) -> Result<()> {
        if regions.is_empty() || regions.len() > VHOST_MAX_MEMORY_REGIONS {
            return Err(Error::InvalidGuestMemory);
        }

        let mut vhost_memory = VhostMemory::new(regions.len() as u16);
        for (index, region) in regions.iter().enumerate() {
            vhost_memory.set_region(
                index as u32,
                &vhost_memory_region {
                    guest_phys_addr: region.guest_phys_addr,
                    memory_size: region.memory_size,
                    userspace_addr: region.userspace_addr,
                    flags_padding: 0u64,
                },
            )?;
        }

        // This ioctl is called with a pointer that is valid for the lifetime
        // of this function. The kernel will make its own copy of the memory
        // tables. As always, check the return value.
        let ret = unsafe { ioctl_with_ptr(self, VHOST_SET_MEM_TABLE(), vhost_memory.as_ptr()) };
        ioctl_result(ret, ())
    }

    /// Set base address for page modification logging.
    ///
    /// # Arguments
    /// * `base` - Base address for page modification logging.
    fn set_log_base(&self, base: u64, region: Option<VhostUserDirtyLogRegion>) -> Result<()> {
        if region.is_some() {
            return Err(Error::LogAddress);
        }

        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl_with_ref(self, VHOST_SET_LOG_BASE(), &base) };
        ioctl_result(ret, ())
    }

    /// Specify an eventfd file descriptor to signal on log write.
    fn set_log_fd(&self, fd: RawFd) -> Result<()> {
        // This ioctl is called on a valid vhost fd and has its return value checked.
        let val: i32 = fd;
        let ret = unsafe { ioctl_with_ref(self, VHOST_SET_LOG_FD(), &val) };
        ioctl_result(ret, ())
    }

    /// Set the number of descriptors in the vring.
    ///
    /// # Arguments
    /// * `queue_index` - Index of the queue to set descriptor count for.
    /// * `num` - Number of descriptors in the queue.
    fn set_vring_num(&self, queue_index: usize, num: u16) -> Result<()> {
        let vring_state = vhost_vring_state {
            index: queue_index as u32,
            num: u32::from(num),
        };

        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_NUM(), &vring_state) };
        ioctl_result(ret, ())
    }

    /// Set the addresses for a given vring.
    ///
    /// # Arguments
    /// * `queue_index` - Index of the queue to set addresses for.
    /// * `config_data` - Vring config data.
    fn set_vring_addr(&self, queue_index: usize, config_data: &VringConfigData) -> Result<()> {
        if !self.is_valid(config_data) {
            return Err(Error::InvalidQueue);
        }

        let vring_addr = vhost_vring_addr {
            index: queue_index as u32,
            flags: config_data.flags,
            desc_user_addr: config_data.desc_table_addr,
            used_user_addr: config_data.used_ring_addr,
            avail_user_addr: config_data.avail_ring_addr,
            log_guest_addr: config_data.get_log_addr(),
        };

        // This ioctl is called on a valid vhost fd and has its
        // return value checked.
        let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_ADDR(), &vring_addr) };
        ioctl_result(ret, ())
    }

    /// Set the first index to look for available descriptors.
    ///
    /// # Arguments
    /// * `queue_index` - Index of the queue to modify.
    /// * `num` - Index where available descriptors start.
    fn set_vring_base(&self, queue_index: usize, base: u16) -> Result<()> {
        let vring_state = vhost_vring_state {
            index: queue_index as u32,
            num: u32::from(base),
        };

        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_BASE(), &vring_state) };
        ioctl_result(ret, ())
    }

    /// Get a bitmask of supported virtio/vhost features.
    fn get_vring_base(&self, queue_index: usize) -> Result<u32> {
        let vring_state = vhost_vring_state {
            index: queue_index as u32,
            num: 0,
        };
        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl_with_ref(self, VHOST_GET_VRING_BASE(), &vring_state) };
        ioctl_result(ret, vring_state.num)
    }

    /// Set the eventfd to trigger when buffers have been used by the host.
    ///
    /// # Arguments
    /// * `queue_index` - Index of the queue to modify.
    /// * `fd` - EventFd to trigger.
    fn set_vring_call(&self, queue_index: usize, fd: &EventFd) -> Result<()> {
        let vring_file = vhost_vring_file {
            index: queue_index as u32,
            fd: fd.as_raw_fd(),
        };

        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_CALL(), &vring_file) };
        ioctl_result(ret, ())
    }

    /// Set the eventfd that will be signaled by the guest when buffers are
    /// available for the host to process.
    ///
    /// # Arguments
    /// * `queue_index` - Index of the queue to modify.
    /// * `fd` - EventFd that will be signaled from guest.
    fn set_vring_kick(&self, queue_index: usize, fd: &EventFd) -> Result<()> {
        let vring_file = vhost_vring_file {
            index: queue_index as u32,
            fd: fd.as_raw_fd(),
        };

        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_KICK(), &vring_file) };
        ioctl_result(ret, ())
    }

    /// Set the eventfd to signal an error from the vhost backend.
    ///
    /// # Arguments
    /// * `queue_index` - Index of the queue to modify.
    /// * `fd` - EventFd that will be signaled from the backend.
    fn set_vring_err(&self, queue_index: usize, fd: &EventFd) -> Result<()> {
        let vring_file = vhost_vring_file {
            index: queue_index as u32,
            fd: fd.as_raw_fd(),
        };

        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_ERR(), &vring_file) };
        ioctl_result(ret, ())
    }
}
<commit_msg>vhost_kern: add VhostKernFeatures trait<commit_after>// Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved.
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
//
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-Google file.

//! Traits and structs to control Linux in-kernel vhost drivers.
//!
//! The initial vhost implementation is a part of the Linux kernel and uses ioctl interface to
//! communicate with userspace applications. This sub module provides ioctl based interfaces to
//! control the in-kernel net, scsi, vsock vhost drivers.

use std::os::unix::io::{AsRawFd, RawFd};

use vm_memory::{Address, GuestAddress, GuestAddressSpace, GuestMemory, GuestUsize};
use vmm_sys_util::eventfd::EventFd;
use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref};

use super::{
    Error, Result, VhostBackend, VhostUserDirtyLogRegion, VhostUserMemoryRegionInfo,
    VringConfigData, VHOST_MAX_MEMORY_REGIONS,
};

pub mod vhost_binding;
use self::vhost_binding::*;

#[cfg(feature = "vhost-vsock")]
pub mod vsock;

#[inline]
fn ioctl_result<T>(rc: i32, res: T) -> Result<T> {
    if rc < 0 {
        Err(Error::IoctlError(std::io::Error::last_os_error()))
    } else {
        Ok(res)
    }
}

/// Represent an in-kernel vhost device backend.
pub trait VhostKernBackend: AsRawFd {
    /// Associated type to access guest memory.
    type AS: GuestAddressSpace;

    /// Get the object to access the guest's memory.
    fn mem(&self) -> &Self::AS;

    /// Check whether the ring configuration is valid.
    fn is_valid(&self, config_data: &VringConfigData) -> bool {
        let queue_size = config_data.queue_size;
        if queue_size > config_data.queue_max_size
            || queue_size == 0
            || (queue_size & (queue_size - 1)) != 0
        {
            return false;
        }

        let m = self.mem().memory();
        let desc_table_size = 16 * u64::from(queue_size) as GuestUsize;
        let avail_ring_size = 6 + 2 * u64::from(queue_size) as GuestUsize;
        let used_ring_size = 6 + 8 * u64::from(queue_size) as GuestUsize;
        if GuestAddress(config_data.desc_table_addr)
            .checked_add(desc_table_size)
            .map_or(true, |v| !m.address_in_range(v))
        {
            return false;
        }
        if GuestAddress(config_data.avail_ring_addr)
            .checked_add(avail_ring_size)
            .map_or(true, |v| !m.address_in_range(v))
        {
            return false;
        }
        if GuestAddress(config_data.used_ring_addr)
            .checked_add(used_ring_size)
            .map_or(true, |v| !m.address_in_range(v))
        {
            return false;
        }

        config_data.is_log_addr_valid()
    }
}

impl<T: VhostKernBackend> VhostBackend for T {
    /// Get a bitmask of supported virtio/vhost features.
    fn get_features(&self) -> Result<u64> {
        let mut avail_features: u64 = 0;
        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl_with_mut_ref(self, VHOST_GET_FEATURES(), &mut avail_features) };
        ioctl_result(ret, avail_features)
    }

    /// Inform the vhost subsystem which features to enable. This should be a subset of
    /// supported features from VHOST_GET_FEATURES.
    ///
    /// # Arguments
    /// * `features` - Bitmask of features to set.
    fn set_features(&self, features: u64) -> Result<()> {
        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl_with_ref(self, VHOST_SET_FEATURES(), &features) };
        ioctl_result(ret, ())
    }

    /// Set the current process as the owner of this file descriptor.
    /// This must be run before any other vhost ioctls.
    fn set_owner(&self) -> Result<()> {
        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl(self, VHOST_SET_OWNER()) };
        ioctl_result(ret, ())
    }

    fn reset_owner(&self) -> Result<()> {
        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl(self, VHOST_RESET_OWNER()) };
        ioctl_result(ret, ())
    }

    /// Set the guest memory mappings for vhost to use.
    fn set_mem_table(&self, regions: &[VhostUserMemoryRegionInfo]) -> Result<()> {
        if regions.is_empty() || regions.len() > VHOST_MAX_MEMORY_REGIONS {
            return Err(Error::InvalidGuestMemory);
        }

        let mut vhost_memory = VhostMemory::new(regions.len() as u16);
        for (index, region) in regions.iter().enumerate() {
            vhost_memory.set_region(
                index as u32,
                &vhost_memory_region {
                    guest_phys_addr: region.guest_phys_addr,
                    memory_size: region.memory_size,
                    userspace_addr: region.userspace_addr,
                    flags_padding: 0u64,
                },
            )?;
        }

        // This ioctl is called with a pointer that is valid for the lifetime
        // of this function. The kernel will make its own copy of the memory
        // tables. As always, check the return value.
        let ret = unsafe { ioctl_with_ptr(self, VHOST_SET_MEM_TABLE(), vhost_memory.as_ptr()) };
        ioctl_result(ret, ())
    }

    /// Set base address for page modification logging.
    ///
    /// # Arguments
    /// * `base` - Base address for page modification logging.
    fn set_log_base(&self, base: u64, region: Option<VhostUserDirtyLogRegion>) -> Result<()> {
        if region.is_some() {
            return Err(Error::LogAddress);
        }

        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl_with_ref(self, VHOST_SET_LOG_BASE(), &base) };
        ioctl_result(ret, ())
    }

    /// Specify an eventfd file descriptor to signal on log write.
    fn set_log_fd(&self, fd: RawFd) -> Result<()> {
        // This ioctl is called on a valid vhost fd and has its return value checked.
        let val: i32 = fd;
        let ret = unsafe { ioctl_with_ref(self, VHOST_SET_LOG_FD(), &val) };
        ioctl_result(ret, ())
    }

    /// Set the number of descriptors in the vring.
    ///
    /// # Arguments
    /// * `queue_index` - Index of the queue to set descriptor count for.
    /// * `num` - Number of descriptors in the queue.
    fn set_vring_num(&self, queue_index: usize, num: u16) -> Result<()> {
        let vring_state = vhost_vring_state {
            index: queue_index as u32,
            num: u32::from(num),
        };

        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_NUM(), &vring_state) };
        ioctl_result(ret, ())
    }

    /// Set the addresses for a given vring.
    ///
    /// # Arguments
    /// * `queue_index` - Index of the queue to set addresses for.
    /// * `config_data` - Vring config data.
    fn set_vring_addr(&self, queue_index: usize, config_data: &VringConfigData) -> Result<()> {
        if !self.is_valid(config_data) {
            return Err(Error::InvalidQueue);
        }

        let vring_addr = vhost_vring_addr {
            index: queue_index as u32,
            flags: config_data.flags,
            desc_user_addr: config_data.desc_table_addr,
            used_user_addr: config_data.used_ring_addr,
            avail_user_addr: config_data.avail_ring_addr,
            log_guest_addr: config_data.get_log_addr(),
        };

        // This ioctl is called on a valid vhost fd and has its
        // return value checked.
        let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_ADDR(), &vring_addr) };
        ioctl_result(ret, ())
    }

    /// Set the first index to look for available descriptors.
    ///
    /// # Arguments
    /// * `queue_index` - Index of the queue to modify.
    /// * `num` - Index where available descriptors start.
    fn set_vring_base(&self, queue_index: usize, base: u16) -> Result<()> {
        let vring_state = vhost_vring_state {
            index: queue_index as u32,
            num: u32::from(base),
        };

        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_BASE(), &vring_state) };
        ioctl_result(ret, ())
    }

    /// Get a bitmask of supported virtio/vhost features.
    fn get_vring_base(&self, queue_index: usize) -> Result<u32> {
        let vring_state = vhost_vring_state {
            index: queue_index as u32,
            num: 0,
        };
        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl_with_ref(self, VHOST_GET_VRING_BASE(), &vring_state) };
        ioctl_result(ret, vring_state.num)
    }

    /// Set the eventfd to trigger when buffers have been used by the host.
    ///
    /// # Arguments
    /// * `queue_index` - Index of the queue to modify.
    /// * `fd` - EventFd to trigger.
    fn set_vring_call(&self, queue_index: usize, fd: &EventFd) -> Result<()> {
        let vring_file = vhost_vring_file {
            index: queue_index as u32,
            fd: fd.as_raw_fd(),
        };

        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_CALL(), &vring_file) };
        ioctl_result(ret, ())
    }

    /// Set the eventfd that will be signaled by the guest when buffers are
    /// available for the host to process.
    ///
    /// # Arguments
    /// * `queue_index` - Index of the queue to modify.
    /// * `fd` - EventFd that will be signaled from guest.
    fn set_vring_kick(&self, queue_index: usize, fd: &EventFd) -> Result<()> {
        let vring_file = vhost_vring_file {
            index: queue_index as u32,
            fd: fd.as_raw_fd(),
        };

        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_KICK(), &vring_file) };
        ioctl_result(ret, ())
    }

    /// Set the eventfd to signal an error from the vhost backend.
    ///
    /// # Arguments
    /// * `queue_index` - Index of the queue to modify.
    /// * `fd` - EventFd that will be signaled from the backend.
    fn set_vring_err(&self, queue_index: usize, fd: &EventFd) -> Result<()> {
        let vring_file = vhost_vring_file {
            index: queue_index as u32,
            fd: fd.as_raw_fd(),
        };

        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_ERR(), &vring_file) };
        ioctl_result(ret, ())
    }
}

/// Interface to handle in-kernel backend features.
pub trait VhostKernFeatures: Sized + AsRawFd {
    /// Get features acked with the vhost backend.
    fn get_backend_features_acked(&self) -> u64;

    /// Set features acked with the vhost backend.
    fn set_backend_features_acked(&mut self, features: u64);

    /// Get a bitmask of supported vhost backend features.
    fn get_backend_features(&self) -> Result<u64> {
        let mut avail_features: u64 = 0;
        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret =
            unsafe { ioctl_with_mut_ref(self, VHOST_GET_BACKEND_FEATURES(), &mut avail_features) };
        ioctl_result(ret, avail_features)
    }

    /// Inform the vhost subsystem which backend features to enable. This should
    /// be a subset of supported features from VHOST_GET_BACKEND_FEATURES.
    ///
    /// # Arguments
    /// * `features` - Bitmask of features to set.
    fn set_backend_features(&mut self, features: u64) -> Result<()> {
        // This ioctl is called on a valid vhost fd and has its return value checked.
        let ret = unsafe { ioctl_with_ref(self, VHOST_SET_BACKEND_FEATURES(), &features) };

        if ret >= 0 {
            self.set_backend_features_acked(features);
        }

        ioctl_result(ret, ())
    }
}<|endoftext|>


<commit_before>use flowgger::config::Config;
use flowgger::decoder::Decoder;
use flowgger::encoder::Encoder;
use flowgger::splitter::Splitter;
use flowgger::splitter::line_splitter::LineSplitter;
use flowgger::splitter::nul_splitter::NulSplitter;
use flowgger::splitter::syslen_splitter::SyslenSplitter;
use openssl::bn::BigNum;
use openssl::dh::DH;
use openssl::ssl::*;
use openssl::ssl::SslMethod::*;
use openssl::x509::X509FileType;
use std::io::{stderr, Write, BufReader};
use std::net::{TcpListener, TcpStream};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::sync::mpsc::SyncSender;
use std::time::Duration;
use std::thread;
use super::Input;

const DEFAULT_CERT: &'static str = "flowgger.pem";
const DEFAULT_CIPHERS: &'static str = "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA";
const DEFAULT_FRAMING: &'static str = "line";
const DEFAULT_KEY: &'static str = "flowgger.pem";
const DEFAULT_LISTEN: &'static str = "0.0.0.0:6514";
const DEFAULT_TIMEOUT: u64 = 3600;
const DEFAULT_TLS_METHOD: &'static str = "TLSv1.2";
const DEFAULT_VERIFY_PEER: bool = false;
const DEFAULT_COMPRESSION: bool = false;
const TLS_VERIFY_DEPTH: u32 = 6;

#[derive(Clone)]
struct TlsConfig {
    framing: String,
    arc_ctx: Arc<SslContext>
}

pub struct TlsInput {
    listen: String,
    timeout: Option<Duration>,
    tls_config: TlsConfig
}

impl TlsInput {
    pub fn new(config: &Config) -> TlsInput {
        let listen = config.lookup("input.listen").map_or(DEFAULT_LISTEN, |x| x.as_str().
            expect("input.listen must be an ip:port string")).to_owned();
        let cert = config.lookup("input.tls_cert").map_or(DEFAULT_CERT, |x| x.as_str().
            expect("input.tls_cert must be a path to a .pem file")).to_owned();
        let key = config.lookup("input.tls_key").map_or(DEFAULT_KEY, |x| x.as_str().
            expect("input.tls_key must be a path to a .pem file")).to_owned();
        let ciphers = config.lookup("input.tls_ciphers").map_or(DEFAULT_CIPHERS, |x| x.as_str().
            expect("input.tls_ciphers must be a string with a cipher suite")).to_owned();
        let tls_method = match config.lookup("input.tls_method").map_or(DEFAULT_TLS_METHOD, |x| x.as_str().
            expect("input.tls_method must be a string with the TLS method")).to_lowercase().as_ref() {
                "sslv23" => SslMethod::Sslv23,
                "tlsv1" | "tlsv1.0" => SslMethod::Tlsv1,
                "tlsv1.1" => SslMethod::Tlsv1_1,
                "tlsv1.2" => SslMethod::Tlsv1_2,
                _ => panic!(r#"TLS method must be "SSLv23", "TLSv1.0", "TLSv1.1" or "TLSv1.2""#)
        };
        let verify_peer = config.lookup("input.tls_verify_peer").map_or(DEFAULT_VERIFY_PEER, |x| x.as_bool().
            expect("input.tls_verify_peer must be a boolean"));
        let ca_file: Option<PathBuf> = config.lookup("input.tls_ca_file").map_or(None, |x|
            Some(PathBuf::from(x.as_str().expect("input.tls_ca_file must be a path to a file"))));
        let compression = config.lookup("input.tls_compression").map_or(DEFAULT_COMPRESSION, |x| x.as_bool().
            expect("input.tls_compression must be a boolean"));
        let timeout = config.lookup("input.timeout").map_or(DEFAULT_TIMEOUT, |x| x.as_integer().
            expect("input.timeout must be an integer") as u64);
        let framing = if config.lookup("input.framed").map_or(false, |x| x.as_bool().
            expect("input.framed must be a boolean")) {
            "syslen"
        } else {
            DEFAULT_FRAMING
        };
        let framing = config.lookup("input.framing").map_or(framing, |x| x.as_str().
            expect(r#"input.framing must be a string set to "line", "nul" or "syslen""#)).to_owned();
        let mut ctx = SslContext::new(tls_method).unwrap();
        if verify_peer == false {
            ctx.set_verify(SSL_VERIFY_NONE, None);
        } else {
            ctx.set_verify_depth(TLS_VERIFY_DEPTH);
            ctx.set_verify(SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, None);
            if let Some(ca_file) = ca_file {
                if ctx.set_CA_file(&ca_file).is_err() {
                    panic!("Unable to read the trusted CA file");
                }
            }
        }
        let mut opts = SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
        if compression == false {
            opts = opts | SSL_OP_NO_COMPRESSION;
        }
        ctx.set_options(opts);
        set_fs(&mut ctx);
        ctx.set_certificate_file(&Path::new(&cert), X509FileType::PEM).unwrap();
        ctx.set_private_key_file(&Path::new(&key), X509FileType::PEM).unwrap();
        ctx.set_cipher_list(&ciphers).unwrap();
        let arc_ctx = Arc::new(ctx);
        let tls_config = TlsConfig {
            framing: framing,
            arc_ctx: arc_ctx
        };
        TlsInput {
            listen: listen,
            tls_config: tls_config,
            timeout: Some(Duration::from_secs(timeout))
        }
    }
}

impl Input for TlsInput {
    fn accept(&self, tx: SyncSender<Vec<u8>>, decoder: Box<Decoder + Send>, encoder: Box<Encoder + Send>) {
        let listener = TcpListener::bind(&self.listen as &str).unwrap();
        for client in listener.incoming() {
            match client {
                Ok(client) => {
                    let _ = client.set_read_timeout(self.timeout);
                    let tx = tx.clone();
                    let (decoder, encoder) = (decoder.clone_boxed(), encoder.clone_boxed());
                    let tls_config = self.tls_config.clone();
                    thread::spawn(move|| {
                        handle_client(client, tx, decoder, encoder, tls_config);
                    });
                }
                Err(_) => { }
            }
        }
    }
}

#[cfg(feature = "ecdh")]
fn set_ecdh(ctx: &mut SslContext) {
    ctx.set_ecdh_auto(true).unwrap();
}

#[cfg(not(feature = "ecdh"))]
fn set_ecdh(ctx: &mut SslContext) {
    let _ = ctx;
}

fn set_fs(ctx: &mut SslContext) {
    let p = BigNum::from_hex_str("87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597").unwrap();
    let g = BigNum::from_hex_str("3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6184B523D1DB246C32F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659").unwrap();
    let q = BigNum::from_hex_str("8CF83642A709A097B447997640129DA299B1A47D1EB3750BA308B0FE64F5FBD3").unwrap();
    let dh = DH::from_params(p, g, q).unwrap();
    ctx.set_tmp_dh(dh).unwrap();
    set_ecdh(ctx);
}

fn handle_client(client: TcpStream, tx: SyncSender<Vec<u8>>, decoder: Box<Decoder>, encoder: Box<Encoder>, tls_config: TlsConfig) {
    if let Ok(peer_addr) = client.peer_addr() {
        println!("Connection over TLS from [{}]", peer_addr);
    }
    let sslclient = match SslStream::accept(&*tls_config.arc_ctx, client) {
        Err(_) => {
            let _ = writeln!(stderr(), "SSL handshake aborted by the client");
            return
        }
        Ok(sslclient) => sslclient
    };
    let reader = BufReader::new(sslclient);
    let splitter = match &tls_config.framing as &str {
        "line" => Box::new(LineSplitter) as Box<Splitter<_>>,
        "syslen" => Box::new(SyslenSplitter) as Box<Splitter<_>>,
        "nul" => Box::new(NulSplitter) as Box<Splitter<_>>,
        _ => panic!("Unsupported framing scheme")
    };
    splitter.run(reader, tx, decoder, encoder);
}
<commit_msg>Favor AES128-GCM over ChaCha20-Poly1305. This is sad, but we can reasonably expect most servers running Flowgger to have a CPU that supports AES-NI.<commit_after>use flowgger::config::Config;
use flowgger::decoder::Decoder;
use flowgger::encoder::Encoder;
use flowgger::splitter::Splitter;
use flowgger::splitter::line_splitter::LineSplitter;
use flowgger::splitter::nul_splitter::NulSplitter;
use flowgger::splitter::syslen_splitter::SyslenSplitter;
use openssl::bn::BigNum;
use openssl::dh::DH;
use openssl::ssl::*;
use openssl::ssl::SslMethod::*;
use openssl::x509::X509FileType;
use std::io::{stderr, Write, BufReader};
use std::net::{TcpListener, TcpStream};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::sync::mpsc::SyncSender;
use std::time::Duration;
use std::thread;
use super::Input;

const DEFAULT_CERT: &'static str = "flowgger.pem";
const DEFAULT_CIPHERS: &'static str = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA";
const DEFAULT_FRAMING: &'static str = "line";
const DEFAULT_KEY: &'static str = "flowgger.pem";
const DEFAULT_LISTEN: &'static str = "0.0.0.0:6514";
const DEFAULT_TIMEOUT: u64 = 3600;
const DEFAULT_TLS_METHOD: &'static str = "TLSv1.2";
const DEFAULT_VERIFY_PEER: bool = false;
const DEFAULT_COMPRESSION: bool = false;
const TLS_VERIFY_DEPTH: u32 = 6;

#[derive(Clone)]
struct TlsConfig {
    framing: String,
    arc_ctx: Arc<SslContext>
}

pub struct TlsInput {
    listen: String,
    timeout: Option<Duration>,
    tls_config: TlsConfig
}

impl TlsInput {
    pub fn new(config: &Config) -> TlsInput {
        let listen = config.lookup("input.listen").map_or(DEFAULT_LISTEN, |x| x.as_str().
            expect("input.listen must be an ip:port string")).to_owned();
        let cert = config.lookup("input.tls_cert").map_or(DEFAULT_CERT, |x| x.as_str().
            expect("input.tls_cert must be a path to a .pem file")).to_owned();
        let key = config.lookup("input.tls_key").map_or(DEFAULT_KEY, |x| x.as_str().
            expect("input.tls_key must be a path to a .pem file")).to_owned();
        let ciphers = config.lookup("input.tls_ciphers").map_or(DEFAULT_CIPHERS, |x| x.as_str().
            expect("input.tls_ciphers must be a string with a cipher suite")).to_owned();
        let tls_method = match config.lookup("input.tls_method").map_or(DEFAULT_TLS_METHOD, |x| x.as_str().
            expect("input.tls_method must be a string with the TLS method")).to_lowercase().as_ref() {
                "sslv23" => SslMethod::Sslv23,
                "tlsv1" | "tlsv1.0" => SslMethod::Tlsv1,
                "tlsv1.1" => SslMethod::Tlsv1_1,
                "tlsv1.2" => SslMethod::Tlsv1_2,
                _ => panic!(r#"TLS method must be "SSLv23", "TLSv1.0", "TLSv1.1" or "TLSv1.2""#)
        };
        let verify_peer = config.lookup("input.tls_verify_peer").map_or(DEFAULT_VERIFY_PEER, |x| x.as_bool().
            expect("input.tls_verify_peer must be a boolean"));
        let ca_file: Option<PathBuf> = config.lookup("input.tls_ca_file").map_or(None, |x|
            Some(PathBuf::from(x.as_str().expect("input.tls_ca_file must be a path to a file"))));
        let compression = config.lookup("input.tls_compression").map_or(DEFAULT_COMPRESSION, |x| x.as_bool().
            expect("input.tls_compression must be a boolean"));
        let timeout = config.lookup("input.timeout").map_or(DEFAULT_TIMEOUT, |x| x.as_integer().
            expect("input.timeout must be an integer") as u64);
        let framing = if config.lookup("input.framed").map_or(false, |x| x.as_bool().
            expect("input.framed must be a boolean")) {
            "syslen"
        } else {
            DEFAULT_FRAMING
        };
        let framing = config.lookup("input.framing").map_or(framing, |x| x.as_str().
            expect(r#"input.framing must be a string set to "line", "nul" or "syslen""#)).to_owned();
        let mut ctx = SslContext::new(tls_method).unwrap();
        if verify_peer == false {
            ctx.set_verify(SSL_VERIFY_NONE, None);
        } else {
            ctx.set_verify_depth(TLS_VERIFY_DEPTH);
            ctx.set_verify(SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, None);
            if let Some(ca_file) = ca_file {
                if ctx.set_CA_file(&ca_file).is_err() {
                    panic!("Unable to read the trusted CA file");
                }
            }
        }
        let mut opts = SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
        if compression == false {
            opts = opts | SSL_OP_NO_COMPRESSION;
        }
        ctx.set_options(opts);
        set_fs(&mut ctx);
        ctx.set_certificate_file(&Path::new(&cert), X509FileType::PEM).unwrap();
        ctx.set_private_key_file(&Path::new(&key), X509FileType::PEM).unwrap();
        ctx.set_cipher_list(&ciphers).unwrap();
        let arc_ctx = Arc::new(ctx);
        let tls_config = TlsConfig {
            framing: framing,
            arc_ctx: arc_ctx
        };
        TlsInput {
            listen: listen,
            tls_config: tls_config,
            timeout: Some(Duration::from_secs(timeout))
        }
    }
}

impl Input for TlsInput {
    fn accept(&self, tx: SyncSender<Vec<u8>>, decoder: Box<Decoder + Send>, encoder: Box<Encoder + Send>) {
        let listener = TcpListener::bind(&self.listen as &str).unwrap();
        for client in listener.incoming() {
            match client {
                Ok(client) => {
                    let _ = client.set_read_timeout(self.timeout);
                    let tx = tx.clone();
                    let (decoder, encoder) = (decoder.clone_boxed(), encoder.clone_boxed());
                    let tls_config = self.tls_config.clone();
                    thread::spawn(move|| {
                        handle_client(client, tx, decoder, encoder, tls_config);
                    });
                }
                Err(_) => { }
            }
        }
    }
}

#[cfg(feature = "ecdh")]
fn set_ecdh(ctx: &mut SslContext) {
    ctx.set_ecdh_auto(true).unwrap();
}

#[cfg(not(feature = "ecdh"))]
fn set_ecdh(ctx: &mut SslContext) {
    let _ = ctx;
}

fn set_fs(ctx: &mut SslContext) {
    let p = BigNum::from_hex_str("87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597").unwrap();
    let g = BigNum::from_hex_str("3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6184B523D1DB246C32F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659").unwrap();
    let q = BigNum::from_hex_str("8CF83642A709A097B447997640129DA299B1A47D1EB3750BA308B0FE64F5FBD3").unwrap();
    let dh = DH::from_params(p, g, q).unwrap();
    ctx.set_tmp_dh(dh).unwrap();
    set_ecdh(ctx);
}

fn handle_client(client: TcpStream, tx: SyncSender<Vec<u8>>, decoder: Box<Decoder>, encoder: Box<Encoder>, tls_config: TlsConfig) {
    if let Ok(peer_addr) = client.peer_addr() {
        println!("Connection over TLS from [{}]", peer_addr);
    }
    let sslclient = match SslStream::accept(&*tls_config.arc_ctx, client) {
        Err(_) => {
            let _ = writeln!(stderr(), "SSL handshake aborted by the client");
            return
        }
        Ok(sslclient) => sslclient
    };
    let reader = BufReader::new(sslclient);
    let splitter = match &tls_config.framing as &str {
        "line" => Box::new(LineSplitter) as Box<Splitter<_>>,
        "syslen" => Box::new(SyslenSplitter) as Box<Splitter<_>>,
        "nul" => Box::new(NulSplitter) as Box<Splitter<_>>,
        _ => panic!("Unsupported framing scheme")
    };
    splitter.run(reader, tx, decoder, encoder);
}
<|endoftext|>


<commit_before>// Copyright 2013-2015, The Rust-GNOME Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! GdkScreen — Object representing a physical screen

use glib::translate::{FromGlibPtr};
use ffi;
use libc::c_int;

#[repr(C)]
pub struct Screen {
    pointer: *mut ffi::C_GdkScreen
}

impl Screen {
    pub fn get_default() -> Option<Screen> {
        let tmp = unsafe { ffi::gdk_screen_get_default() };

        if tmp.is_null() {
            None
        } else {
            Some(Screen {
                pointer: tmp
            })
        }
    }

    pub fn get_system_visual(&self) -> Option<::Visual> {
        let tmp = unsafe { ffi::gdk_screen_get_system_visual(self.pointer) };

        if tmp.is_null() {
            None
        } else {
            Some(::Visual::wrap_pointer(tmp))
        }
    }

    pub fn get_rgba_visual(&self) -> Option<::Visual> {
        let tmp = unsafe { ffi::gdk_screen_get_rgba_visual(self.pointer) };

        if tmp.is_null() {
            None
        } else {
            Some(::Visual::wrap_pointer(tmp))
        }
    }

    pub fn is_composited(&self) -> bool {
        unsafe { ::glib::to_bool(ffi::gdk_screen_is_composited(self.pointer)) }
    }

    pub fn get_root_window(&self) -> Option<::Window> {
        let tmp = unsafe { ffi::gdk_screen_get_root_window(self.pointer) };

        if tmp.is_null() {
            None
        } else {
            Some(::Window::wrap_pointer(tmp))
        }
    }

    pub fn get_display(&self) -> Option<::Display> {
        let tmp = unsafe { ffi::gdk_screen_get_display(self.pointer) };

        if tmp.is_null() {
            None
        } else {
            Some(::Display::wrap_pointer(tmp))
        }
    }

    pub fn get_number(&self) -> i32 {
        unsafe { ffi::gdk_screen_get_number(self.pointer) }
    }

    pub fn get_width(&self) -> i32 {
        unsafe { ffi::gdk_screen_get_width(self.pointer) }
    }

    pub fn get_height(&self) -> i32 {
        unsafe { ffi::gdk_screen_get_height(self.pointer) }
    }

    pub fn get_width_mm(&self) -> i32 {
        unsafe { ffi::gdk_screen_get_width_mm(self.pointer) }
    }

    pub fn get_height_mm(&self) -> i32 {
        unsafe { ffi::gdk_screen_get_height_mm(self.pointer) }
    }

    pub fn make_display_name(&self) -> Option<String> {
        unsafe {
            FromGlibPtr::take(
                ffi::gdk_screen_make_display_name(self.pointer))
        }
    }

    pub fn get_n_monitors(&self) -> i32 {
        unsafe { ffi::gdk_screen_get_n_monitors(self.pointer) }
    }

    pub fn get_primary_monitor(&self) -> i32 {
        unsafe { ffi::gdk_screen_get_primary_monitor(self.pointer) }
    }

    pub fn get_monitor_geometry(&self, monitor_num: i32, dest: &mut ffi::C_GdkRectangle) {
        unsafe { ffi::gdk_screen_get_monitor_geometry(self.pointer, monitor_num as c_int, dest) }
    }

    pub fn get_monitor_workarea(&self, monitor_num: i32, dest: &mut ffi::C_GdkRectangle) {
        unsafe { ffi::gdk_screen_get_monitor_workarea(self.pointer, monitor_num as c_int, dest) }
    }

    pub fn get_monitor_at_point(&self, x: i32, y: i32) -> i32 {
        unsafe { ffi::gdk_screen_get_monitor_at_point(self.pointer, x as c_int, y as c_int) }
    }

    pub fn get_monitor_at_window(&self, window: &mut ::Window) -> i32 {
        unsafe { ffi::gdk_screen_get_monitor_at_window(self.pointer, window.unwrap_pointer()) }
    }

    pub fn get_monitor_width_mm(&self, monitor_num: i32) -> i32 {
        unsafe { ffi::gdk_screen_get_monitor_width_mm(self.pointer, monitor_num as c_int) }
    }

    pub fn get_monitor_height_mm(&self, monitor_num: i32) -> i32 {
        unsafe { ffi::gdk_screen_get_monitor_height_mm(self.pointer, monitor_num as c_int) }
    }

    pub fn get_monitor_plug_name(&self, monitor_num: i32) -> Option<String> {
        unsafe {
            FromGlibPtr::take(
                ffi::gdk_screen_get_monitor_plug_name(self.pointer,
                                                      monitor_num as c_int))
        }
    }

    #[cfg(feature = "gdk_3_10")]
    pub fn get_monitor_scale_factor(&self, monitor_num: i32) -> i32 {
        unsafe { ffi::gdk_screen_get_monitor_scale_factor(self.pointer, monitor_num as c_int) }
    }

    pub fn get_resolution(&self) -> f64 {
        unsafe { ffi::gdk_screen_get_resolution(self.pointer) }
    }

    pub fn set_resolution(&self, dpi: f64) {
        unsafe { ffi::gdk_screen_set_resolution(self.pointer, dpi) }
    }

    pub fn get_active_window(&self) -> Option<::Window> {
        let tmp = unsafe { ffi::gdk_screen_get_active_window(self.pointer) };

        if tmp.is_null() {
            None
        } else {
            Some(::Window::wrap_pointer(tmp))
        }
    }

    pub unsafe fn wrap(ptr: *mut ffi::C_GdkScreen) -> Screen {
        ::glib_ffi::g_object_ref(ptr as *mut _);
        Screen { pointer: ptr }
    }
}

impl_GObjectFunctions!(Screen, C_GdkScreen);
<commit_msg>Revert "Support wrapping a screen pointer"<commit_after>// Copyright 2013-2015, The Rust-GNOME Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! GdkScreen — Object representing a physical screen

use glib::translate::{FromGlibPtr};
use ffi;
use libc::c_int;

#[repr(C)]
pub struct Screen {
    pointer: *mut ffi::C_GdkScreen
}

impl Screen {
    pub fn get_default() -> Option<Screen> {
        let tmp = unsafe { ffi::gdk_screen_get_default() };

        if tmp.is_null() {
            None
        } else {
            Some(Screen {
                pointer: tmp
            })
        }
    }

    pub fn get_system_visual(&self) -> Option<::Visual> {
        let tmp = unsafe { ffi::gdk_screen_get_system_visual(self.pointer) };

        if tmp.is_null() {
            None
        } else {
            Some(::Visual::wrap_pointer(tmp))
        }
    }

    pub fn get_rgba_visual(&self) -> Option<::Visual> {
        let tmp = unsafe { ffi::gdk_screen_get_rgba_visual(self.pointer) };

        if tmp.is_null() {
            None
        } else {
            Some(::Visual::wrap_pointer(tmp))
        }
    }

    pub fn is_composited(&self) -> bool {
        unsafe { ::glib::to_bool(ffi::gdk_screen_is_composited(self.pointer)) }
    }

    pub fn get_root_window(&self) -> Option<::Window> {
        let tmp = unsafe { ffi::gdk_screen_get_root_window(self.pointer) };

        if tmp.is_null() {
            None
        } else {
            Some(::Window::wrap_pointer(tmp))
        }
    }

    pub fn get_display(&self) -> Option<::Display> {
        let tmp = unsafe { ffi::gdk_screen_get_display(self.pointer) };

        if tmp.is_null() {
            None
        } else {
            Some(::Display::wrap_pointer(tmp))
        }
    }

    pub fn get_number(&self) -> i32 {
        unsafe { ffi::gdk_screen_get_number(self.pointer) }
    }

    pub fn get_width(&self) -> i32 {
        unsafe { ffi::gdk_screen_get_width(self.pointer) }
    }

    pub fn get_height(&self) -> i32 {
        unsafe { ffi::gdk_screen_get_height(self.pointer) }
    }

    pub fn get_width_mm(&self) -> i32 {
        unsafe { ffi::gdk_screen_get_width_mm(self.pointer) }
    }

    pub fn get_height_mm(&self) -> i32 {
        unsafe { ffi::gdk_screen_get_height_mm(self.pointer) }
    }

    pub fn make_display_name(&self) -> Option<String> {
        unsafe {
            FromGlibPtr::take(
                ffi::gdk_screen_make_display_name(self.pointer))
        }
    }

    pub fn get_n_monitors(&self) -> i32 {
        unsafe { ffi::gdk_screen_get_n_monitors(self.pointer) }
    }

    pub fn get_primary_monitor(&self) -> i32 {
        unsafe { ffi::gdk_screen_get_primary_monitor(self.pointer) }
    }

    pub fn get_monitor_geometry(&self, monitor_num: i32, dest: &mut ffi::C_GdkRectangle) {
        unsafe { ffi::gdk_screen_get_monitor_geometry(self.pointer, monitor_num as c_int, dest) }
    }

    pub fn get_monitor_workarea(&self, monitor_num: i32, dest: &mut ffi::C_GdkRectangle) {
        unsafe { ffi::gdk_screen_get_monitor_workarea(self.pointer, monitor_num as c_int, dest) }
    }

    pub fn get_monitor_at_point(&self, x: i32, y: i32) -> i32 {
        unsafe { ffi::gdk_screen_get_monitor_at_point(self.pointer, x as c_int, y as c_int) }
    }

    pub fn get_monitor_at_window(&self, window: &mut ::Window) -> i32 {
        unsafe { ffi::gdk_screen_get_monitor_at_window(self.pointer, window.unwrap_pointer()) }
    }

    pub fn get_monitor_width_mm(&self, monitor_num: i32) -> i32 {
        unsafe { ffi::gdk_screen_get_monitor_width_mm(self.pointer, monitor_num as c_int) }
    }

    pub fn get_monitor_height_mm(&self, monitor_num: i32) -> i32 {
        unsafe { ffi::gdk_screen_get_monitor_height_mm(self.pointer, monitor_num as c_int) }
    }

    pub fn get_monitor_plug_name(&self, monitor_num: i32) -> Option<String> {
        unsafe {
            FromGlibPtr::take(
                ffi::gdk_screen_get_monitor_plug_name(self.pointer,
                                                      monitor_num as c_int))
        }
    }

    #[cfg(feature = "gdk_3_10")]
    pub fn get_monitor_scale_factor(&self, monitor_num: i32) -> i32 {
        unsafe { ffi::gdk_screen_get_monitor_scale_factor(self.pointer, monitor_num as c_int) }
    }

    pub fn get_resolution(&self) -> f64 {
        unsafe { ffi::gdk_screen_get_resolution(self.pointer) }
    }

    pub fn set_resolution(&self, dpi: f64) {
        unsafe { ffi::gdk_screen_set_resolution(self.pointer, dpi) }
    }

    pub fn get_active_window(&self) -> Option<::Window> {
        let tmp = unsafe { ffi::gdk_screen_get_active_window(self.pointer) };

        if tmp.is_null() {
            None
        } else {
            Some(::Window::wrap_pointer(tmp))
        }
    }
}

impl_GObjectFunctions!(Screen, C_GdkScreen);
<|endoftext|>


<commit_before>use std::vec::Vec;

use chunk::{Chunk, Param, TupleMode};
use codegen::translate_from_glib::TranslateFromGlib;
use codegen::translate_to_glib::TranslateToGlib;
use env::Env;
use super::primitives::*;

pub trait ToCode {
    fn to_code(&self, env: &Env) -> Vec<String>;
}

impl ToCode for Chunk {
    fn to_code(&self, env: &Env) -> Vec<String> {
        use chunk::Chunk::*;
        match *self {
            Comment(ref chs) => comment_block(&chs.to_code(env)),
            Chunks(ref chs) => chs.to_code(env),
            BlockHalf(ref chs) => format_block("", "}", &chs.to_code(env)),
            UnsafeSmart(ref chs) => {
                format_block_smart("unsafe {", "}", &chs.to_code(env), " ", " ")
            }
            Unsafe(ref chs) => format_block("unsafe {", "}", &chs.to_code(env)),
            FfiCallTODO(ref name) => vec![format!("TODO: call ffi::{}()", name)],
            FfiCall {
                ref name,
                ref params,
            } => {
                let prefix = if name.find("::").is_some() {
                    format!("{}(", name)
                } else {
                    format!("ffi::{}(", name)
                };
                //TODO: change to format_block or format_block_smart
                let s = format_block_one_line(&prefix, ")", &params.to_code(env), "", ", ");
                vec![s]
            }
            FfiCallParameter {
                ref transformation_type,
            } => {
                let s = transformation_type.translate_to_glib();
                vec![s]
            }
            FfiCallOutParameter { ref par } => {
                let s = if par.caller_allocates {
                    format!("{}.to_glib_none_mut().0", par.name)
                } else {
                    format!("&mut {}", par.name)
                };
                vec![s]
            }
            FfiCallConversion {
                ref ret,
                ref array_length_name,
                ref call,
            } => {
                let call_strings = call.to_code(env);
                let (prefix, suffix) =
                    ret.translate_from_glib_as_function(env, array_length_name.as_ref());
                let s = format_block_one_line(&prefix, &suffix, &call_strings, "", "");
                vec![s]
            }
            Let {
                ref name,
                is_mut,
                ref value,
                ref type_,
            } => {
                let modif = if is_mut { "mut " } else { "" };
                let type_string = if let Some(ref type_) = *type_ {
                    let type_strings = type_.to_code(env);
                    format_block_one_line(": ", "", &type_strings, "", "")
                } else {
                    "".to_owned()
                };
                let value_strings = value.to_code(env);
                let prefix = format!("let {}{}{} = ", modif, name, type_string);
                let s = format_block_one_line(&prefix, ";", &value_strings, "", "");
                vec![s]
            }
            Uninitialized => vec!["mem::uninitialized()".into()],
            UninitializedNamed { ref name } => {
                let s = format!("{}::uninitialized()", name);
                vec![s]
            }
            NullPtr => vec!["ptr::null()".into()],
            NullMutPtr => vec!["ptr::null_mut()".into()],
            Custom(ref string) => vec![string.clone()],
            Tuple(ref chs, mode) => {
                #[allow(deprecated)]
                let with_bracket = match mode {
                    TupleMode::Auto => chs.len() > 1,
                    TupleMode::WithUnit => chs.len() != 1,
                    TupleMode::Simple => true,
                };
                let (prefix, suffix) = if with_bracket { ("(", ")") } else { ("", "") };
                let s = format_block_one_line(prefix, suffix, &chs.to_code(env), "", ", ");
                vec![s]
            }
            FromGlibConversion {
                ref mode,
                ref array_length_name,
                ref value,
            } => {
                let value_strings = value.to_code(env);
                let (prefix, suffix) =
                    mode.translate_from_glib_as_function(env, array_length_name.as_ref());
                let s = format_block_one_line(&prefix, &suffix, &value_strings, "", "");
                vec![s]
            }
            OptionalReturn {
                ref condition,
                ref value,
            } => {
                let value_strings = value.to_code(env);
                let prefix = format!("if {} {{ Some(", condition);
                let suffix = ") } else { None }";
                let s = format_block_one_line(&prefix, suffix, &value_strings, "", "");
                vec![s]
            }
            ErrorResultReturn { ref value } => {
                let value_strings = value.to_code(env);
                let prefix = "if error.is_null() { Ok(";
                let suffix = ") } else { Err(from_glib_full(error)) }";
                let s = format_block_one_line(prefix, suffix, &value_strings, "", "");
                vec![s]
            }
            AssertInitializedAndInMainThread => {
                vec!["assert_initialized_main_thread!();".to_string()]
            }
            AssertSkipInitialized => vec!["skip_assert_initialized!();".to_string()],
            Connect {
                ref signal,
                ref trampoline,
                in_trait,
            } => {
                let s1 = format!("connect_raw(self.as_ptr() as *mut _, b\"{}\\0\".as_ptr() as *const _,", signal);
                let self_str = if in_trait { "Self, " } else { "" };
                let s2 = format!(
                    "\ttransmute({}::<{}F> as usize), Box_::into_raw(f) as *mut _)",
                    trampoline,
                    self_str
                );
                vec![s1, s2]
            }
            Name(ref name) => vec![name.clone()],
            ExternCFunc { ref name, ref parameters, ref body, ref return_value, ref bounds } => {
                let prefix = format!(r#"unsafe extern "C" fn {}{}("#, name, bounds);
                let suffix = ")".to_string();
                let params: Vec<_> = parameters.iter()
                    .flat_map(|param| param.to_code(env))
                    .collect();
                let mut s = format_block_one_line(&prefix, &suffix, &params, "", ", ");
                if let Some(ref return_value) = return_value {
                    s.push_str(&format!(" -> {}", return_value));
                }
                s.push_str(" {");
                let mut code = format_block("", "}", &body.to_code(env));
                code.insert(0, s);
                code
            }
            Cast { ref name, ref type_ } => vec![format!("{} as {}", name, type_)],
            Call { ref func_name, ref arguments } => {
                let args: Vec<_> = arguments.iter()
                    .flat_map(|arg| arg.to_code(env))
                    .collect();
                let s = format_block_one_line("(", ")", &args, "", ",");
                vec![format!("{}{};", func_name, s)]
            }
        }
    }
}

impl ToCode for Param {
    fn to_code(&self, _env: &Env) -> Vec<String> {
        vec![format!("{}: {}", self.name, self.typ)]
    }
}

impl ToCode for [Chunk] {
    fn to_code(&self, env: &Env) -> Vec<String> {
        let mut v = Vec::new();
        for ch in self {
            let strs = ch.to_code(env);
            v.extend_from_slice(&strs);
        }
        v
    }
}
<commit_msg>Correctly generate signal connector function arguments<commit_after>use std::vec::Vec;

use chunk::{Chunk, Param, TupleMode};
use codegen::translate_from_glib::TranslateFromGlib;
use codegen::translate_to_glib::TranslateToGlib;
use env::Env;
use super::primitives::*;

pub trait ToCode {
    fn to_code(&self, env: &Env) -> Vec<String>;
}

impl ToCode for Chunk {
    fn to_code(&self, env: &Env) -> Vec<String> {
        use chunk::Chunk::*;
        match *self {
            Comment(ref chs) => comment_block(&chs.to_code(env)),
            Chunks(ref chs) => chs.to_code(env),
            BlockHalf(ref chs) => format_block("", "}", &chs.to_code(env)),
            UnsafeSmart(ref chs) => {
                format_block_smart("unsafe {", "}", &chs.to_code(env), " ", " ")
            }
            Unsafe(ref chs) => format_block("unsafe {", "}", &chs.to_code(env)),
            FfiCallTODO(ref name) => vec![format!("TODO: call ffi::{}()", name)],
            FfiCall {
                ref name,
                ref params,
            } => {
                let prefix = if name.find("::").is_some() {
                    format!("{}(", name)
                } else {
                    format!("ffi::{}(", name)
                };
                //TODO: change to format_block or format_block_smart
                let s = format_block_one_line(&prefix, ")", &params.to_code(env), "", ", ");
                vec![s]
            }
            FfiCallParameter {
                ref transformation_type,
            } => {
                let s = transformation_type.translate_to_glib();
                vec![s]
            }
            FfiCallOutParameter { ref par } => {
                let s = if par.caller_allocates {
                    format!("{}.to_glib_none_mut().0", par.name)
                } else {
                    format!("&mut {}", par.name)
                };
                vec![s]
            }
            FfiCallConversion {
                ref ret,
                ref array_length_name,
                ref call,
            } => {
                let call_strings = call.to_code(env);
                let (prefix, suffix) =
                    ret.translate_from_glib_as_function(env, array_length_name.as_ref());
                let s = format_block_one_line(&prefix, &suffix, &call_strings, "", "");
                vec![s]
            }
            Let {
                ref name,
                is_mut,
                ref value,
                ref type_,
            } => {
                let modif = if is_mut { "mut " } else { "" };
                let type_string = if let Some(ref type_) = *type_ {
                    let type_strings = type_.to_code(env);
                    format_block_one_line(": ", "", &type_strings, "", "")
                } else {
                    "".to_owned()
                };
                let value_strings = value.to_code(env);
                let prefix = format!("let {}{}{} = ", modif, name, type_string);
                let s = format_block_one_line(&prefix, ";", &value_strings, "", "");
                vec![s]
            }
            Uninitialized => vec!["mem::uninitialized()".into()],
            UninitializedNamed { ref name } => {
                let s = format!("{}::uninitialized()", name);
                vec![s]
            }
            NullPtr => vec!["ptr::null()".into()],
            NullMutPtr => vec!["ptr::null_mut()".into()],
            Custom(ref string) => vec![string.clone()],
            Tuple(ref chs, mode) => {
                #[allow(deprecated)]
                let with_bracket = match mode {
                    TupleMode::Auto => chs.len() > 1,
                    TupleMode::WithUnit => chs.len() != 1,
                    TupleMode::Simple => true,
                };
                let (prefix, suffix) = if with_bracket { ("(", ")") } else { ("", "") };
                let s = format_block_one_line(prefix, suffix, &chs.to_code(env), "", ", ");
                vec![s]
            }
            FromGlibConversion {
                ref mode,
                ref array_length_name,
                ref value,
            } => {
                let value_strings = value.to_code(env);
                let (prefix, suffix) =
                    mode.translate_from_glib_as_function(env, array_length_name.as_ref());
                let s = format_block_one_line(&prefix, &suffix, &value_strings, "", "");
                vec![s]
            }
            OptionalReturn {
                ref condition,
                ref value,
            } => {
                let value_strings = value.to_code(env);
                let prefix = format!("if {} {{ Some(", condition);
                let suffix = ") } else { None }";
                let s = format_block_one_line(&prefix, suffix, &value_strings, "", "");
                vec![s]
            }
            ErrorResultReturn { ref value } => {
                let value_strings = value.to_code(env);
                let prefix = "if error.is_null() { Ok(";
                let suffix = ") } else { Err(from_glib_full(error)) }";
                let s = format_block_one_line(prefix, suffix, &value_strings, "", "");
                vec![s]
            }
            AssertInitializedAndInMainThread => {
                vec!["assert_initialized_main_thread!();".to_string()]
            }
            AssertSkipInitialized => vec!["skip_assert_initialized!();".to_string()],
            Connect {
                ref signal,
                ref trampoline,
                in_trait,
            } => {
                let s1 = format!("connect_raw(self.as_ptr() as *mut _, b\"{}\\0\".as_ptr() as *const _,", signal);
                let self_str = if in_trait { "Self, " } else { "" };
                let s2 = format!(
                    "\tSome(transmute({}::<{}F> as usize)), Box_::into_raw(f))",
                    trampoline,
                    self_str
                );
                vec![s1, s2]
            }
            Name(ref name) => vec![name.clone()],
            ExternCFunc { ref name, ref parameters, ref body, ref return_value, ref bounds } => {
                let prefix = format!(r#"unsafe extern "C" fn {}{}("#, name, bounds);
                let suffix = ")".to_string();
                let params: Vec<_> = parameters.iter()
                    .flat_map(|param| param.to_code(env))
                    .collect();
                let mut s = format_block_one_line(&prefix, &suffix, &params, "", ", ");
                if let Some(ref return_value) = return_value {
                    s.push_str(&format!(" -> {}", return_value));
                }
                s.push_str(" {");
                let mut code = format_block("", "}", &body.to_code(env));
                code.insert(0, s);
                code
            }
            Cast { ref name, ref type_ } => vec![format!("{} as {}", name, type_)],
            Call { ref func_name, ref arguments } => {
                let args: Vec<_> = arguments.iter()
                    .flat_map(|arg| arg.to_code(env))
                    .collect();
                let s = format_block_one_line("(", ")", &args, "", ",");
                vec![format!("{}{};", func_name, s)]
            }
        }
    }
}

impl ToCode for Param {
    fn to_code(&self, _env: &Env) -> Vec<String> {
        vec![format!("{}: {}", self.name, self.typ)]
    }
}

impl ToCode for [Chunk] {
    fn to_code(&self, env: &Env) -> Vec<String> {
        let mut v = Vec::new();
        for ch in self {
            let strs = ch.to_code(env);
            v.extend_from_slice(&strs);
        }
        v
    }
}
<|endoftext|>


<commit_before>use file_conflict::{OrigFileName, Details};
use parser::{Parser, ParseError};

// parses a wuala file conflict enoded in the file name in the form:
//
//    '<base_name> (conflicting version <version> from <host>).<extension>'
//
// e.g:
//
//    'x_original (conflicting version 5 from blub).txt'
//
pub fn parse(file_name: &str) -> Result<(OrigFileName, Details), ParseError>
{
   let mut parser = Parser::new(file_name);
   let mut base_name = try!(parser.take_while(|c| c != '('));

   // drops the whitespace before the '('
   base_name.pop();

   try!(parser.skip("(conflicting version "));
   let version = try!(parser.take_uint());
   try!(parser.skip(" from "));
   let host = try!(parser.take_while(|c| c != ')'));
   try!(parser.skip(")"));

   if ! parser.eof() {
      let extension = try!(parser.take_till_eof());
      base_name.push_str(extension.as_slice());
   }

   let details = format!("Version {} from {}", version, host);
   Ok((base_name, details))
}
<commit_msg>wuala_conflict: added tests<commit_after>use file_conflict::{OrigFileName, Details};
use parser::{Parser, ParseError};

// parses a wuala file conflict encoded in the file name in the form:
//
//    '<base_name> (conflicting version <version> from <host>).<extension>'
//
// e.g:
//
//    'x_original (conflicting version 5 from blub).txt'
//
pub fn parse(file_name: &str) -> Result<(OrigFileName, Details), ParseError>
{
   let mut parser = Parser::new(file_name);
   let mut base_name = try!(parser.take_while(|c| c != '('));

   // drops the whitespace before the '('
   base_name.pop();

   try!(parser.skip("(conflicting version "));
   let version = try!(parser.take_uint());

   try!(parser.skip(" from ")
              .or(parser.skip(" from"))
              .or(Ok(true)));

   let host = try!(parser.take_while(|c| c != ')'));
   try!(parser.skip(")"));

   if ! parser.eof() {
      let extension = try!(parser.take_till_eof());
      base_name.push_str(extension.as_slice());
   }

   let details = format!("Version {} from {}", version, host);
   Ok((base_name, details))
}

#[test]
fn tests()
{
   test_str("original (conflicting version 1 from machine)", "original", "Version 1 from machine");
   test_str("x_original (conflicting version 5 from blub).txt", "x_original.txt", "Version 5 from blub");
   test_str("x_original (conflicting version 5 from ).txt", "x_original.txt", "Version 5 from ");
   test_str("x_original (conflicting version 5 from).txt", "x_original.txt", "Version 5 from ");
   test_str("x_original (conflicting version 5).txt", "x_original.txt", "Version 5 from ");
}

fn test_str(file_name: &str, orig_name: &str, details: &str)
{
   match parse(file_name) {
      Ok((name, det)) => {
         assert_eq!(name, orig_name.to_string());
         assert_eq!(det , details.to_string());
      }

      Err(err) => assert!(false, err)
   }
}
<|endoftext|>


<commit_before>#[cfg(test)]
mod tests {
    use crate::*;

    fn check_raw_str(
        s: &str,
        expected: UnvalidatedRawStr,
        validated: Result<ValidatedRawStr, LexRawStrError>,
    ) {
        let s = &format!("r{}", s);
        let mut cursor = Cursor::new(s);
        cursor.bump();
        let tok = cursor.raw_double_quoted_string(0);
        assert_eq!(tok, expected);
        assert_eq!(tok.validate(), validated);
    }

    #[test]
    fn test_naked_raw_str() {
        check_raw_str(
            r#""abc""#,
            UnvalidatedRawStr {
                n_start_hashes: 0,
                n_end_hashes: 0,
                valid_start: true,
                valid_end: true,
                possible_terminator_offset: None,
            },
            Ok(ValidatedRawStr { n_hashes: 0 }),
        );
    }

    #[test]
    fn test_raw_no_start() {
        check_raw_str(
            r##""abc"#"##,
            UnvalidatedRawStr {
                n_start_hashes: 0,
                n_end_hashes: 0,
                valid_start: true,
                valid_end: true,
                possible_terminator_offset: None,
            },
            Ok(ValidatedRawStr { n_hashes: 0 }),
        );
    }

    #[test]
    fn test_too_many_terminators() {
        // this error is handled in the parser later
        check_raw_str(
            r###"#"abc"##"###,
            UnvalidatedRawStr {
                n_start_hashes: 1,
                n_end_hashes: 1,
                valid_end: true,
                valid_start: true,
                possible_terminator_offset: None,
            },
            Ok(ValidatedRawStr { n_hashes: 1 }),
        );
    }

    #[test]
    fn test_unterminated() {
        check_raw_str(
            r#"#"abc"#,
            UnvalidatedRawStr {
                n_start_hashes: 1,
                n_end_hashes: 0,
                valid_end: false,
                valid_start: true,
                possible_terminator_offset: None,
            },
            Err(LexRawStrError::NoTerminator {
                expected: 1,
                found: 0,
                possible_terminator_offset: None,
            }),
        );
        check_raw_str(
            r###"##"abc"#"###,
            UnvalidatedRawStr {
                n_start_hashes: 2,
                n_end_hashes: 1,
                valid_start: true,
                valid_end: false,
                possible_terminator_offset: Some(7),
            },
            Err(LexRawStrError::NoTerminator {
                expected: 2,
                found: 1,
                possible_terminator_offset: Some(7),
            }),
        );
        // We're looking for "# not just any #
        check_raw_str(
            r###"##"abc#"###,
            UnvalidatedRawStr {
                n_start_hashes: 2,
                n_end_hashes: 0,
                valid_start: true,
                valid_end: false,
                possible_terminator_offset: None,
            },
            Err(LexRawStrError::NoTerminator {
                expected: 2,
                found: 0,
                possible_terminator_offset: None,
            }),
        )
    }

    #[test]
    fn test_invalid_start() {
        check_raw_str(
            r##"#~"abc"#"##,
            UnvalidatedRawStr {
                n_start_hashes: 1,
                n_end_hashes: 0,
                valid_start: false,
                valid_end: false,
                possible_terminator_offset: None,
            },
            Err(LexRawStrError::InvalidStarter),
        );
    }

    #[test]
    fn test_unterminated_no_pound() {
        // https://github.com/rust-lang/rust/issues/70677
        check_raw_str(
            r#"""#,
            UnvalidatedRawStr {
                n_start_hashes: 0,
                n_end_hashes: 0,
                valid_start: true,
                valid_end: false,
                possible_terminator_offset: None,
            },
            Err(LexRawStrError::NoTerminator {
                expected: 0,
                found: 0,
                possible_terminator_offset: None,
            }),
        );
    }

    #[test]
    fn test_valid_shebang() {
        // https://github.com/rust-lang/rust/issues/70528
        let input = "#!/usr/bin/rustrun";
        let actual = strip_shebang(input);
        let expected: Option<usize> = Some(18);
        assert_eq!(expected, actual);
    }

    #[test]
    fn test_invalid_shebang_valid_rust_syntax() {
        // https://github.com/rust-lang/rust/issues/70528
        let input = "#!    [bad_attribute]";
        let actual = strip_shebang(input);
        let expected: Option<usize> = None;
        assert_eq!(expected, actual);
    }

}
<commit_msg>Fix formatting issue<commit_after>#[cfg(test)]
mod tests {
    use crate::*;

    fn check_raw_str(
        s: &str,
        expected: UnvalidatedRawStr,
        validated: Result<ValidatedRawStr, LexRawStrError>,
    ) {
        let s = &format!("r{}", s);
        let mut cursor = Cursor::new(s);
        cursor.bump();
        let tok = cursor.raw_double_quoted_string(0);
        assert_eq!(tok, expected);
        assert_eq!(tok.validate(), validated);
    }

    #[test]
    fn test_naked_raw_str() {
        check_raw_str(
            r#""abc""#,
            UnvalidatedRawStr {
                n_start_hashes: 0,
                n_end_hashes: 0,
                valid_start: true,
                valid_end: true,
                possible_terminator_offset: None,
            },
            Ok(ValidatedRawStr { n_hashes: 0 }),
        );
    }

    #[test]
    fn test_raw_no_start() {
        check_raw_str(
            r##""abc"#"##,
            UnvalidatedRawStr {
                n_start_hashes: 0,
                n_end_hashes: 0,
                valid_start: true,
                valid_end: true,
                possible_terminator_offset: None,
            },
            Ok(ValidatedRawStr { n_hashes: 0 }),
        );
    }

    #[test]
    fn test_too_many_terminators() {
        // this error is handled in the parser later
        check_raw_str(
            r###"#"abc"##"###,
            UnvalidatedRawStr {
                n_start_hashes: 1,
                n_end_hashes: 1,
                valid_end: true,
                valid_start: true,
                possible_terminator_offset: None,
            },
            Ok(ValidatedRawStr { n_hashes: 1 }),
        );
    }

    #[test]
    fn test_unterminated() {
        check_raw_str(
            r#"#"abc"#,
            UnvalidatedRawStr {
                n_start_hashes: 1,
                n_end_hashes: 0,
                valid_end: false,
                valid_start: true,
                possible_terminator_offset: None,
            },
            Err(LexRawStrError::NoTerminator {
                expected: 1,
                found: 0,
                possible_terminator_offset: None,
            }),
        );
        check_raw_str(
            r###"##"abc"#"###,
            UnvalidatedRawStr {
                n_start_hashes: 2,
                n_end_hashes: 1,
                valid_start: true,
                valid_end: false,
                possible_terminator_offset: Some(7),
            },
            Err(LexRawStrError::NoTerminator {
                expected: 2,
                found: 1,
                possible_terminator_offset: Some(7),
            }),
        );
        // We're looking for "# not just any #
        check_raw_str(
            r###"##"abc#"###,
            UnvalidatedRawStr {
                n_start_hashes: 2,
                n_end_hashes: 0,
                valid_start: true,
                valid_end: false,
                possible_terminator_offset: None,
            },
            Err(LexRawStrError::NoTerminator {
                expected: 2,
                found: 0,
                possible_terminator_offset: None,
            }),
        )
    }

    #[test]
    fn test_invalid_start() {
        check_raw_str(
            r##"#~"abc"#"##,
            UnvalidatedRawStr {
                n_start_hashes: 1,
                n_end_hashes: 0,
                valid_start: false,
                valid_end: false,
                possible_terminator_offset: None,
            },
            Err(LexRawStrError::InvalidStarter),
        );
    }

    #[test]
    fn test_unterminated_no_pound() {
        // https://github.com/rust-lang/rust/issues/70677
        check_raw_str(
            r#"""#,
            UnvalidatedRawStr {
                n_start_hashes: 0,
                n_end_hashes: 0,
                valid_start: true,
                valid_end: false,
                possible_terminator_offset: None,
            },
            Err(LexRawStrError::NoTerminator {
                expected: 0,
                found: 0,
                possible_terminator_offset: None,
            }),
        );
    }

    #[test]
    fn test_valid_shebang() {
        // https://github.com/rust-lang/rust/issues/70528
        let input = "#!/usr/bin/rustrun";
        let actual = strip_shebang(input);
        let expected: Option<usize> = Some(18);
        assert_eq!(expected, actual);
    }

    #[test]
    fn test_invalid_shebang_valid_rust_syntax() {
        // https://github.com/rust-lang/rust/issues/70528
        let input = "#!    [bad_attribute]";
        let actual = strip_shebang(input);
        let expected: Option<usize> = None;
        assert_eq!(expected, actual);
    }
}
<|endoftext|>


<commit_before>use jpeg;
use png;
use std::cmp;
use std::fs::File;
use std::path::Path;

use super::common;

#[test]
fn reftest() {
    let files = common::test_files(&Path::new("tests").join("reftest").join("images"));

    for path in &files {
        reftest_file(path);
    }
}

#[test]
fn reftest_scaled() {
    let base = &Path::new("tests").join("reftest").join("images");
    reftest_scaled_file(&base.join("rgb.jpg"), 500, 333, &base.join("rgb.png"));
    reftest_scaled_file(&base.join("rgb.jpg"), 250, 167, &base.join("rgb_250x167.png"));
    reftest_scaled_file(&base.join("rgb.jpg"), 125, 84,  &base.join("rgb_125x84.png"));
    reftest_scaled_file(&base.join("rgb.jpg"), 63,  42,  &base.join("rgb_63x42.png"));
}

fn reftest_file(path: &Path) {
    let file = File::open(path).unwrap();
    let decoder = jpeg::Decoder::new(file);
    reftest_decoder(decoder, path, &path.with_extension("png"));
}

fn reftest_scaled_file(path: &Path, width: u16, height: u16, ref_path: &Path) {
    let file = File::open(path).unwrap();
    let mut decoder = jpeg::Decoder::new(file);
    decoder.read_info().unwrap();
    decoder.scale(width, height).unwrap();
    reftest_decoder(decoder, path, &ref_path);
}

fn reftest_decoder<T: std::io::Read>(mut decoder: jpeg::Decoder<T>, path: &Path, ref_path: &Path) {
    let mut data = decoder.decode().expect(&format!("failed to decode file: {:?}", path));
    let info = decoder.info().unwrap();
    let mut pixel_format = info.pixel_format;

    if pixel_format == jpeg::PixelFormat::CMYK32 {
        data = cmyk_to_rgb(&data);
        pixel_format = jpeg::PixelFormat::RGB24;
    }

    let ref_file = File::open(ref_path).unwrap();
    let mut decoder = png::Decoder::new(ref_file);

    if pixel_format == jpeg::PixelFormat::L16 {
        // disable the default 8bit output of png v0.16.8 (fixed in master branch of png)
        decoder.set_transformations(png::Transformations::EXPAND);
    }

    let (ref_info, mut ref_reader) = decoder.read_info().expect("png failed to read info");

    assert_eq!(ref_info.width, info.width as u32);
    assert_eq!(ref_info.height, info.height as u32);

    let mut ref_data = vec![0; ref_info.buffer_size()];
    ref_reader.next_frame(&mut ref_data).expect("png decode failed");
    let mut ref_pixel_format = ref_info.color_type;

    if ref_pixel_format == png::ColorType::RGBA {
        ref_data = rgba_to_rgb(&ref_data);
        ref_pixel_format = png::ColorType::RGB;
    }

    let (refdata_16, data_u16) : (Vec<u16>, Vec<u16>) = match pixel_format {
        jpeg::PixelFormat::L8 => {
            assert_eq!(ref_pixel_format, png::ColorType::Grayscale);
            assert_eq!(ref_info.bit_depth, png::BitDepth::Eight);
            (ref_data.iter().map(|x| *x as u16).collect(), data.iter().map(|x| *x as u16).collect())
        },
        jpeg::PixelFormat::L16 => {
            assert_eq!(ref_pixel_format, png::ColorType::Grayscale);
            assert_eq!(ref_info.bit_depth, png::BitDepth::Sixteen);
            (ref_data.chunks_exact(2).map(|a| u16::from_be_bytes([a[0],a[1]])).collect(),
            data.chunks_exact(2).map(|a| u16::from_be_bytes([a[0],a[1]])).collect())
        },
        jpeg::PixelFormat::RGB24 => {
            assert_eq!(ref_pixel_format, png::ColorType::RGB);
            assert_eq!(ref_info.bit_depth, png::BitDepth::Eight);
            (ref_data.iter().map(|x| *x as u16).collect(), data.iter().map(|x| *x as u16).collect())
        },
        _ => panic!(),
    };

    assert_eq!(data_u16.len(), refdata_16.len());
    let mut max_diff = 0;
    let pixels: Vec<u8> = data_u16.iter().zip(refdata_16.iter()).map(|(&a, &b)| {
        let diff = (a as isize - b as isize).abs();
        max_diff = cmp::max(diff, max_diff);

        // FIXME: Only a diff of 1 should be allowed?
        if (info.coding_process != jpeg::CodingProcess::Lossless && diff <= 2) || diff == 0  {
            // White for correct
            0xFF
        } else {
            // "1100" in the RGBA channel with an error for an incorrect value
            // This results in some number of C0 and FFs, which is much more
            // readable (and distinguishable) than the previous difference-wise
            // scaling but does not require reconstructing the actual RGBA pixel.
            0xC0
        }
    }).collect();

    if pixels.iter().any(|&a| a < 255) {
        let output_path = path.with_file_name(format!("{}-diff.png", path.file_stem().unwrap().to_str().unwrap()));
        let output = File::create(&output_path).unwrap();
        let mut encoder = png::Encoder::new(output, info.width as u32, info.height as u32);
        encoder.set_depth(png::BitDepth::Eight);
        encoder.set_color(ref_pixel_format);
        encoder.write_header().expect("png failed to write header").write_image_data(&pixels).expect("png failed to write data");

        panic!("decoding difference: {:?}, maximum difference was {}", output_path, max_diff);
    }
}

fn rgba_to_rgb(input: &[u8]) -> Vec<u8> {
    let size = input.len() - input.len() / 4;
    let mut output = Vec::with_capacity(size);

    for pixel in input.chunks(4) {
        assert_eq!(pixel[3], 255);

        output.push(pixel[0]);
        output.push(pixel[1]);
        output.push(pixel[2]);
    }

    output
}

fn cmyk_to_rgb(input: &[u8]) -> Vec<u8> {
    let size = input.len() - input.len() / 4;
    let mut output = Vec::with_capacity(size);

    for pixel in input.chunks(4) {
        let c = pixel[0] as f32 / 255.0;
        let m = pixel[1] as f32 / 255.0;
        let y = pixel[2] as f32 / 255.0;
        let k = pixel[3] as f32 / 255.0;

        // CMYK -> CMY
        let c = c * (1.0 - k) + k;
        let m = m * (1.0 - k) + k;
        let y = y * (1.0 - k) + k;

        // CMY -> RGB
        let r = (1.0 - c) * 255.0;
        let g = (1.0 - m) * 255.0;
        let b = (1.0 - y) * 255.0;

        output.push(r as u8);
        output.push(g as u8);
        output.push(b as u8);
    }

    output
}
<commit_msg>also change to native endian in tests<commit_after>use jpeg;
use png;
use std::cmp;
use std::fs::File;
use std::path::Path;

use super::common;

#[test]
fn reftest() {
    let files = common::test_files(&Path::new("tests").join("reftest").join("images"));

    for path in &files {
        reftest_file(path);
    }
}

#[test]
fn reftest_scaled() {
    let base = &Path::new("tests").join("reftest").join("images");
    reftest_scaled_file(&base.join("rgb.jpg"), 500, 333, &base.join("rgb.png"));
    reftest_scaled_file(&base.join("rgb.jpg"), 250, 167, &base.join("rgb_250x167.png"));
    reftest_scaled_file(&base.join("rgb.jpg"), 125, 84,  &base.join("rgb_125x84.png"));
    reftest_scaled_file(&base.join("rgb.jpg"), 63,  42,  &base.join("rgb_63x42.png"));
}

fn reftest_file(path: &Path) {
    let file = File::open(path).unwrap();
    let decoder = jpeg::Decoder::new(file);
    reftest_decoder(decoder, path, &path.with_extension("png"));
}

fn reftest_scaled_file(path: &Path, width: u16, height: u16, ref_path: &Path) {
    let file = File::open(path).unwrap();
    let mut decoder = jpeg::Decoder::new(file);
    decoder.read_info().unwrap();
    decoder.scale(width, height).unwrap();
    reftest_decoder(decoder, path, &ref_path);
}

fn reftest_decoder<T: std::io::Read>(mut decoder: jpeg::Decoder<T>, path: &Path, ref_path: &Path) {
    let mut data = decoder.decode().expect(&format!("failed to decode file: {:?}", path));
    let info = decoder.info().unwrap();
    let mut pixel_format = info.pixel_format;

    if pixel_format == jpeg::PixelFormat::CMYK32 {
        data = cmyk_to_rgb(&data);
        pixel_format = jpeg::PixelFormat::RGB24;
    }

    let ref_file = File::open(ref_path).unwrap();
    let mut decoder = png::Decoder::new(ref_file);

    if pixel_format == jpeg::PixelFormat::L16 {
        // disable the default 8bit output of png v0.16.8 (fixed in master branch of png)
        decoder.set_transformations(png::Transformations::EXPAND);
    }

    let (ref_info, mut ref_reader) = decoder.read_info().expect("png failed to read info");

    assert_eq!(ref_info.width, info.width as u32);
    assert_eq!(ref_info.height, info.height as u32);

    let mut ref_data = vec![0; ref_info.buffer_size()];
    ref_reader.next_frame(&mut ref_data).expect("png decode failed");
    let mut ref_pixel_format = ref_info.color_type;

    if ref_pixel_format == png::ColorType::RGBA {
        ref_data = rgba_to_rgb(&ref_data);
        ref_pixel_format = png::ColorType::RGB;
    }

    let (refdata_16, data_u16) : (Vec<u16>, Vec<u16>) = match pixel_format {
        jpeg::PixelFormat::L8 => {
            assert_eq!(ref_pixel_format, png::ColorType::Grayscale);
            assert_eq!(ref_info.bit_depth, png::BitDepth::Eight);
            (ref_data.iter().map(|x| *x as u16).collect(), data.iter().map(|x| *x as u16).collect())
        },
        jpeg::PixelFormat::L16 => {
            assert_eq!(ref_pixel_format, png::ColorType::Grayscale);
            assert_eq!(ref_info.bit_depth, png::BitDepth::Sixteen);
            (ref_data.chunks_exact(2).map(|a| u16::from_be_bytes([a[0],a[1]])).collect(),
            data.chunks_exact(2).map(|a| u16::from_ne_bytes([a[0],a[1]])).collect())
        },
        jpeg::PixelFormat::RGB24 => {
            assert_eq!(ref_pixel_format, png::ColorType::RGB);
            assert_eq!(ref_info.bit_depth, png::BitDepth::Eight);
            (ref_data.iter().map(|x| *x as u16).collect(), data.iter().map(|x| *x as u16).collect())
        },
        _ => panic!(),
    };

    assert_eq!(data_u16.len(), refdata_16.len());
    let mut max_diff = 0;
    let pixels: Vec<u8> = data_u16.iter().zip(refdata_16.iter()).map(|(&a, &b)| {
        let diff = (a as isize - b as isize).abs();
        max_diff = cmp::max(diff, max_diff);

        // FIXME: Only a diff of 1 should be allowed?
        if (info.coding_process != jpeg::CodingProcess::Lossless && diff <= 2) || diff == 0  {
            // White for correct
            0xFF
        } else {
            // "1100" in the RGBA channel with an error for an incorrect value
            // This results in some number of C0 and FFs, which is much more
            // readable (and distinguishable) than the previous difference-wise
            // scaling but does not require reconstructing the actual RGBA pixel.
            0xC0
        }
    }).collect();

    if pixels.iter().any(|&a| a < 255) {
        let output_path = path.with_file_name(format!("{}-diff.png", path.file_stem().unwrap().to_str().unwrap()));
        let output = File::create(&output_path).unwrap();
        let mut encoder = png::Encoder::new(output, info.width as u32, info.height as u32);
        encoder.set_depth(png::BitDepth::Eight);
        encoder.set_color(ref_pixel_format);
        encoder.write_header().expect("png failed to write header").write_image_data(&pixels).expect("png failed to write data");

        panic!("decoding difference: {:?}, maximum difference was {}", output_path, max_diff);
    }
}

fn rgba_to_rgb(input: &[u8]) -> Vec<u8> {
    let size = input.len() - input.len() / 4;
    let mut output = Vec::with_capacity(size);

    for pixel in input.chunks(4) {
        assert_eq!(pixel[3], 255);

        output.push(pixel[0]);
        output.push(pixel[1]);
        output.push(pixel[2]);
    }

    output
}

fn cmyk_to_rgb(input: &[u8]) -> Vec<u8> {
    let size = input.len() - input.len() / 4;
    let mut output = Vec::with_capacity(size);

    for pixel in input.chunks(4) {
        let c = pixel[0] as f32 / 255.0;
        let m = pixel[1] as f32 / 255.0;
        let y = pixel[2] as f32 / 255.0;
        let k = pixel[3] as f32 / 255.0;

        // CMYK -> CMY
        let c = c * (1.0 - k) + k;
        let m = m * (1.0 - k) + k;
        let y = y * (1.0 - k) + k;

        // CMY -> RGB
        let r = (1.0 - c) * 255.0;
        let g = (1.0 - m) * 255.0;
        let b = (1.0 - y) * 255.0;

        output.push(r as u8);
        output.push(g as u8);
        output.push(b as u8);
    }

    output
}
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#![crate_name="thrift-client"]
#![crate_type="bin"]

// TODO: move to TProtocol
#[allow(dead_code)]
#[allow(non_camel_case_types)]
enum MessageType {
    T_CALL = 1,
    T_REPLY = 2,
    T_EXCEPTION = 3,
    T_ONEWAY = 4,
}

// TODO: move to Transport
#[allow(dead_code)]
struct ThriftBufferedTransport {
    x: i32,
}

impl ThriftBufferedTransport {

    fn new() -> ThriftBufferedTransport {
        ThriftBufferedTransport { x: 0 }
    }

    fn open(&self) {
        // TODO
    }

    fn write_end(&self) {
        // TODO
    }

    fn flush(&self) {
        // TODO
    }
}

// TODO: move to Protocol
#[allow(dead_code)]
struct ThriftBinaryProtocol<'a> {
    transport: &'a ThriftBufferedTransport, // TODO: replace with Transport
}

impl<'a> ThriftBinaryProtocol<'a> {

    fn new(t: &'a ThriftBufferedTransport) -> ThriftBinaryProtocol<'a> {
        ThriftBinaryProtocol { transport: t }
    }

    fn get_transport(&self) -> &ThriftBufferedTransport {
        self.transport
    }

    fn write_message_begin(&self, name: &str, message_type: MessageType, seq_id: i32) -> i32 {
        println!("Protocol: message begin");

        // TODO: strict write
        let mut wsize: i32 = 0;
        wsize += self.write_string(name);
        wsize += self.write_byte(message_type as i8);
        wsize += self.write_i32(seq_id);
        wsize
    }

    fn write_string(&self, s: &str) -> i32 {
        println!("Protocol: {}", s);
        s.len() as i32
    }

    fn write_byte(&self, b: i8) -> i32 {
        println!("Protocol: {}", b);
        1
    }

    fn write_i32(&self, i: i32) -> i32 {
        println!("Protocol: {}", i);
        4
    }

    fn write_message_end(&self) -> i32 {
        println!("Protocol: end");
        0
    }
}

// TODO: shall be generated
#[allow(dead_code)]
struct CalculatorPingArgs;

impl CalculatorPingArgs {

    #[allow(unused_variables)]
    fn write(&self, oprot: &ThriftBinaryProtocol) {
        println!("CalculatorPingArgs::write");
    }
}

#[allow(dead_code)]
struct CalculatorClient<'a> {
    oprotocol: &'a ThriftBinaryProtocol<'a>,
    iprotocol: &'a ThriftBinaryProtocol<'a>,
}

impl<'a> CalculatorClient<'a> {

    fn new(protocol: &'a ThriftBinaryProtocol) -> CalculatorClient<'a> {
        CalculatorClient { oprotocol: protocol, iprotocol: protocol }
    }

    fn ping(&self) {
        self.send_ping();
        self.receive_ping();
    }

    fn send_ping(&self) {
        let cseqid: i32 = 0;
        self.oprotocol.write_message_begin("ping", MessageType::T_CALL, cseqid);

        let args = CalculatorPingArgs;
        args.write(self.oprotocol);

        self.oprotocol.write_message_end();

        self.oprotocol.get_transport().write_end();
        self.oprotocol.get_transport().flush();
    }

    fn receive_ping(&self) {
        // TODO
    }
}

pub fn main() {
    //let socket = ThriftSocket::new("localhost", 9090); // TODO
    let transport = ThriftBufferedTransport::new( /*socket*/ );
    let protocol = ThriftBinaryProtocol::new(&transport);
    let client = CalculatorClient::new(&protocol);

    transport.open();

    client.ping();

    println!("PASS")
}
<commit_msg>Added TSocket stub<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#![crate_name="thrift-client"]
#![crate_type="bin"]

// TODO: move to TProtocol
// Decided to leave the same naming as in the C++ code; to be discussed
#[allow(dead_code)]
#[allow(non_camel_case_types)]
enum MessageType {
    T_CALL = 1,
    T_REPLY = 2,
    T_EXCEPTION = 3,
    T_ONEWAY = 4,
}

// TODO: move to Socket
#[allow(dead_code)]
struct TSocket<'a> {
    host: &'a str,
    port: i32,
}

impl<'a> TSocket<'a> {

    fn new(h: &'a str, p: i32) -> TSocket {
        TSocket { host: h, port: p }
    }
}

// TODO: move to Transport
#[allow(dead_code)]
struct ThriftBufferedTransport<'a> {
    socket: &'a TSocket<'a>,
}

impl<'a> ThriftBufferedTransport<'a> {

    fn new(s: &'a TSocket) -> ThriftBufferedTransport<'a> {
        ThriftBufferedTransport { socket: s }
    }

    fn open(&self) {
        // TODO
    }

    fn write_end(&self) {
        // TODO
    }

    fn flush(&self) {
        // TODO
    }
}

// TODO: move to Protocol
#[allow(dead_code)]
struct ThriftBinaryProtocol<'a> {
    transport: &'a ThriftBufferedTransport<'a>, // TODO: replace with Transport
}

impl<'a> ThriftBinaryProtocol<'a> {

    fn new(t: &'a ThriftBufferedTransport) -> ThriftBinaryProtocol<'a> {
        ThriftBinaryProtocol { transport: t }
    }

    fn get_transport(&self) -> &ThriftBufferedTransport {
        self.transport
    }

    fn write_message_begin(&self, name: &str, message_type: MessageType, seq_id: i32) -> i32 {
        println!("Protocol: message begin");

        // TODO: strict write
        let mut wsize: i32 = 0;
        wsize += self.write_string(name);
        wsize += self.write_byte(message_type as i8);
        wsize += self.write_i32(seq_id);
        wsize
    }

    fn write_string(&self, s: &str) -> i32 {
        println!("Protocol: {}", s);
        s.len() as i32
    }

    fn write_byte(&self, b: i8) -> i32 {
        println!("Protocol: {}", b);
        1
    }

    fn write_i32(&self, i: i32) -> i32 {
        println!("Protocol: {}", i);
        4
    }

    fn write_message_end(&self) -> i32 {
        println!("Protocol: end");
        0
    }
}

// TODO: shall be generated
#[allow(dead_code)]
struct CalculatorPingArgs;

impl CalculatorPingArgs {

    #[allow(unused_variables)]
    fn write(&self, oprot: &ThriftBinaryProtocol) {
        println!("CalculatorPingArgs::write");
    }
}

#[allow(dead_code)]
struct CalculatorClient<'a> {
    oprotocol: &'a ThriftBinaryProtocol<'a>,
    iprotocol: &'a ThriftBinaryProtocol<'a>,
}

impl<'a> CalculatorClient<'a> {

    fn new(protocol: &'a ThriftBinaryProtocol) -> CalculatorClient<'a> {
        CalculatorClient { oprotocol: protocol, iprotocol: protocol }
    }

    fn ping(&self) {
        self.send_ping();
        self.receive_ping();
    }

    fn send_ping(&self) {
        let cseqid: i32 = 0;
        self.oprotocol.write_message_begin("ping", MessageType::T_CALL, cseqid);

        let args = CalculatorPingArgs;
        args.write(self.oprotocol);

        self.oprotocol.write_message_end();

        self.oprotocol.get_transport().write_end();
        self.oprotocol.get_transport().flush();
    }

    fn receive_ping(&self) {
        // TODO
    }
}

pub fn main() {
    let socket = TSocket::new("localhost", 9090);
    let transport = ThriftBufferedTransport::new(&socket);
    let protocol = ThriftBinaryProtocol::new(&transport);
    let client = CalculatorClient::new(&protocol);

    transport.open();

    client.ping();

    println!("PASS")
}
<|endoftext|>