<commit_before>//! Decoding and Encoding of TIFF Images
//!
//! TIFF (Tagged Image File Format) is a versatile image format that supports
//! lossless and lossy compression.
//!
//! # Related Links
//! * <http://partners.adobe.com/public/developer/tiff/index.html> - The TIFF specification

extern crate tiff;

use std::convert::TryFrom;
use std::io::{self, Cursor, Read, Seek, Write};
use std::marker::PhantomData;
use std::mem;

use crate::color::{ColorType, ExtendedColorType};
use crate::error::{
    DecodingError, EncodingError, ImageError, ImageResult, LimitError, LimitErrorKind,
    ParameterError, ParameterErrorKind, UnsupportedError, UnsupportedErrorKind,
};
use crate::image::{ImageDecoder, ImageEncoder, ImageFormat};
use crate::utils;

/// Decoder for TIFF images.
pub struct TiffDecoder<R>
where
    R: Read + Seek,
{
    dimensions: (u32, u32),
    color_type: ColorType,

    // We only use an Option here so we can call with_limits on the decoder without moving.
    inner: Option<tiff::decoder::Decoder<R>>,
}

impl<R> TiffDecoder<R>
where
    R: Read + Seek,
{
    /// Create a new TiffDecoder.
    pub fn new(r: R) -> Result<TiffDecoder<R>, ImageError> {
        let mut inner = tiff::decoder::Decoder::new(r).map_err(ImageError::from_tiff_decode)?;

        let dimensions = inner.dimensions().map_err(ImageError::from_tiff_decode)?;
        let color_type = inner.colortype().map_err(ImageError::from_tiff_decode)?;
        match inner.find_tag_unsigned_vec::<u16>(tiff::tags::Tag::SampleFormat) {
            Ok(Some(sample_formats)) => {
                for format in sample_formats {
                    check_sample_format(format)?;
                }
            }
            Ok(None) => { /* assume UInt format */ }
            Err(other) => return Err(ImageError::from_tiff_decode(other)),
        };

        let color_type = match color_type {
            tiff::ColorType::Gray(8) => ColorType::L8,
            tiff::ColorType::Gray(16) => ColorType::L16,
            tiff::ColorType::GrayA(8) => ColorType::La8,
            tiff::ColorType::GrayA(16) => ColorType::La16,
            tiff::ColorType::RGB(8) => ColorType::Rgb8,
            tiff::ColorType::RGB(16) => ColorType::Rgb16,
            tiff::ColorType::RGBA(8) => ColorType::Rgba8,
            tiff::ColorType::RGBA(16) => ColorType::Rgba16,

            tiff::ColorType::Palette(n) | tiff::ColorType::Gray(n) => {
                return Err(err_unknown_color_type(n))
            }
            tiff::ColorType::GrayA(n) => return Err(err_unknown_color_type(n * 2)),
            tiff::ColorType::RGB(n) => return Err(err_unknown_color_type(n * 3)),
            tiff::ColorType::RGBA(n) | tiff::ColorType::CMYK(n) => {
                return Err(err_unknown_color_type(n * 4))
            }
        };

        Ok(TiffDecoder {
            dimensions,
            color_type,
            inner: Some(inner),
        })
    }
}

fn check_sample_format(sample_format: u16) -> Result<(), ImageError> {
    match tiff::tags::SampleFormat::from_u16(sample_format) {
        Some(tiff::tags::SampleFormat::Uint) => Ok(()),
        Some(other) => Err(ImageError::Unsupported(
            UnsupportedError::from_format_and_kind(
                ImageFormat::Tiff.into(),
                UnsupportedErrorKind::GenericFeature(format!(
                    "Unhandled TIFF sample format {:?}",
                    other
                )),
            ),
        )),
        None => Err(ImageError::Decoding(DecodingError::from_format_hint(
            ImageFormat::Tiff.into(),
        ))),
    }
}

fn err_unknown_color_type(value: u8) -> ImageError {
    ImageError::Unsupported(UnsupportedError::from_format_and_kind(
        ImageFormat::Tiff.into(),
        UnsupportedErrorKind::Color(ExtendedColorType::Unknown(value)),
    ))
}

impl ImageError {
    fn from_tiff_decode(err: tiff::TiffError) -> ImageError {
        match err {
            tiff::TiffError::IoError(err) => ImageError::IoError(err),
            err @ tiff::TiffError::FormatError(_)
            | err @ tiff::TiffError::IntSizeError
            | err @ tiff::TiffError::UsageError(_) => {
                ImageError::Decoding(DecodingError::new(ImageFormat::Tiff.into(), err))
            }
            tiff::TiffError::UnsupportedError(desc) => {
                ImageError::Unsupported(UnsupportedError::from_format_and_kind(
                    ImageFormat::Tiff.into(),
                    UnsupportedErrorKind::GenericFeature(desc.to_string()),
                ))
            }
            tiff::TiffError::LimitsExceeded => {
                ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
            }
        }
    }

    fn from_tiff_encode(err: tiff::TiffError) -> ImageError {
        match err {
            tiff::TiffError::IoError(err) => ImageError::IoError(err),
            err @ tiff::TiffError::FormatError(_)
            | err @ tiff::TiffError::IntSizeError
            | err @ tiff::TiffError::UsageError(_) => {
                ImageError::Encoding(EncodingError::new(ImageFormat::Tiff.into(), err))
            }
            tiff::TiffError::UnsupportedError(desc) => {
                ImageError::Unsupported(UnsupportedError::from_format_and_kind(
                    ImageFormat::Tiff.into(),
                    UnsupportedErrorKind::GenericFeature(desc.to_string()),
                ))
            }
            tiff::TiffError::LimitsExceeded => {
                ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
            }
        }
    }
}

/// Wrapper struct around a `Cursor<Vec<u8>>`
pub struct TiffReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
impl<R> Read for TiffReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        if self.0.position() == 0 && buf.is_empty() {
            mem::swap(buf, self.0.get_mut());
            Ok(buf.len())
        } else {
            self.0.read_to_end(buf)
        }
    }
}

impl<'a, R: 'a + Read + Seek> ImageDecoder<'a> for TiffDecoder<R> {
    type Reader = TiffReader<R>;

    fn dimensions(&self) -> (u32, u32) {
        self.dimensions
    }

    fn color_type(&self) -> ColorType {
        self.color_type
    }

    fn set_limits(&mut self, limits: crate::io::Limits) -> ImageResult<()> {
        limits.check_support(&crate::io::LimitSupport::default())?;

        let (width, height) = self.dimensions();
        limits.check_dimensions(width, height)?;

        let max_alloc = limits.max_alloc.unwrap_or(u64::MAX);
        let max_intermediate_alloc = max_alloc.saturating_sub(self.total_bytes());

        let mut tiff_limits: tiff::decoder::Limits = Default::default();
        tiff_limits.decoding_buffer_size =
            usize::try_from(max_alloc - max_intermediate_alloc).unwrap_or(usize::MAX);
        tiff_limits.intermediate_buffer_size =
            usize::try_from(max_intermediate_alloc).unwrap_or(usize::MAX);
        tiff_limits.ifd_value_size = tiff_limits.intermediate_buffer_size;
        self.inner = Some(self.inner.take().unwrap().with_limits(tiff_limits));

        Ok(())
    }

    fn into_reader(self) -> ImageResult<Self::Reader> {
        let buf = match self
            .inner
            .unwrap()
            .read_image()
            .map_err(ImageError::from_tiff_decode)?
        {
            tiff::decoder::DecodingResult::U8(v) => v,
            tiff::decoder::DecodingResult::U16(v) => utils::vec_copy_to_u8(&v),
            tiff::decoder::DecodingResult::U32(v) => utils::vec_copy_to_u8(&v),
            tiff::decoder::DecodingResult::U64(v) => utils::vec_copy_to_u8(&v),
            tiff::decoder::DecodingResult::I8(v) => utils::vec_copy_to_u8(&v),
            tiff::decoder::DecodingResult::I16(v) => utils::vec_copy_to_u8(&v),
            tiff::decoder::DecodingResult::I32(v) => utils::vec_copy_to_u8(&v),
            tiff::decoder::DecodingResult::I64(v) => utils::vec_copy_to_u8(&v),
            tiff::decoder::DecodingResult::F32(v) => utils::vec_copy_to_u8(&v),
            tiff::decoder::DecodingResult::F64(v) => utils::vec_copy_to_u8(&v),
        };

        Ok(TiffReader(Cursor::new(buf), PhantomData))
    }

    fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
        match self
            .inner
            .unwrap()
            .read_image()
            .map_err(ImageError::from_tiff_decode)?
        {
            tiff::decoder::DecodingResult::U8(v) => {
                buf.copy_from_slice(&v);
            }
            tiff::decoder::DecodingResult::U16(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::U32(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::U64(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::I8(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::I16(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::I32(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::I64(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::F32(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::F64(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
        }
        Ok(())
    }
}

/// Encoder for tiff images
pub struct TiffEncoder<W> {
    w: W,
}

// Utility to simplify and deduplicate error handling during 16-bit encoding.
fn u8_slice_as_u16(buf: &[u8]) -> ImageResult<&[u16]> {
    bytemuck::try_cast_slice(buf).map_err(|err| {
        // If the buffer is not aligned or the correct length for a u16 slice, err.
        //
        // `bytemuck::PodCastError` of bytemuck-1.2.0 does not implement
        // `Error` and `Display` trait.
        // See <https://github.com/Lokathor/bytemuck/issues/22>.
        ImageError::Parameter(ParameterError::from_kind(ParameterErrorKind::Generic(
            format!("{:?}", err),
        )))
    })
}

impl<W: Write + Seek> TiffEncoder<W> {
    /// Create a new encoder that writes its output to `w`
    pub fn new(w: W) -> TiffEncoder<W> {
        TiffEncoder { w }
    }

    /// Encodes the image `image` that has dimensions `width` and `height` and `ColorType` `c`.
    ///
    /// 16-bit types assume the buffer is native endian.
    pub fn encode(self, data: &[u8], width: u32, height: u32, color: ColorType) -> ImageResult<()> {
        let mut encoder =
            tiff::encoder::TiffEncoder::new(self.w).map_err(ImageError::from_tiff_encode)?;
        match color {
            ColorType::L8 => {
                encoder.write_image::<tiff::encoder::colortype::Gray8>(width, height, data)
            }
            ColorType::Rgb8 => {
                encoder.write_image::<tiff::encoder::colortype::RGB8>(width, height, data)
            }
            ColorType::Rgba8 => {
                encoder.write_image::<tiff::encoder::colortype::RGBA8>(width, height, data)
            }
            ColorType::L16 => encoder.write_image::<tiff::encoder::colortype::Gray16>(
                width,
                height,
                u8_slice_as_u16(data)?,
            ),
            ColorType::Rgb16 => encoder.write_image::<tiff::encoder::colortype::RGB16>(
                width,
                height,
                u8_slice_as_u16(data)?,
            ),
            ColorType::Rgba16 => encoder.write_image::<tiff::encoder::colortype::RGBA16>(
                width,
                height,
                u8_slice_as_u16(data)?,
            ),
            _ => {
                return Err(ImageError::Unsupported(
                    UnsupportedError::from_format_and_kind(
                        ImageFormat::Tiff.into(),
                        UnsupportedErrorKind::Color(color.into()),
                    ),
                ))
            }
        }
        .map_err(ImageError::from_tiff_encode)?;

        Ok(())
    }
}

impl<W: Write + Seek> ImageEncoder for TiffEncoder<W> {
    fn write_image(
        self,
        buf: &[u8],
        width: u32,
        height: u32,
        color_type: ColorType,
    ) -> ImageResult<()> {
        self.encode(buf, width, height, color_type)
    }
}
<commit_msg>Fix arithmetic overflow in TIFF error reporting<commit_after>//! Decoding and Encoding of TIFF Images
//!
//! TIFF (Tagged Image File Format) is a versatile image format that supports
//! lossless and lossy compression.
//!
//! # Related Links
//! * <http://partners.adobe.com/public/developer/tiff/index.html> - The TIFF specification

extern crate tiff;

use std::convert::TryFrom;
use std::io::{self, Cursor, Read, Seek, Write};
use std::marker::PhantomData;
use std::mem;

use crate::color::{ColorType, ExtendedColorType};
use crate::error::{
    DecodingError, EncodingError, ImageError, ImageResult, LimitError, LimitErrorKind,
    ParameterError, ParameterErrorKind, UnsupportedError, UnsupportedErrorKind,
};
use crate::image::{ImageDecoder, ImageEncoder, ImageFormat};
use crate::utils;

/// Decoder for TIFF images.
pub struct TiffDecoder<R>
where
    R: Read + Seek,
{
    dimensions: (u32, u32),
    color_type: ColorType,

    // We only use an Option here so we can call with_limits on the decoder without moving.
    inner: Option<tiff::decoder::Decoder<R>>,
}

impl<R> TiffDecoder<R>
where
    R: Read + Seek,
{
    /// Create a new TiffDecoder.
    pub fn new(r: R) -> Result<TiffDecoder<R>, ImageError> {
        let mut inner = tiff::decoder::Decoder::new(r).map_err(ImageError::from_tiff_decode)?;

        let dimensions = inner.dimensions().map_err(ImageError::from_tiff_decode)?;
        let color_type = inner.colortype().map_err(ImageError::from_tiff_decode)?;
        match inner.find_tag_unsigned_vec::<u16>(tiff::tags::Tag::SampleFormat) {
            Ok(Some(sample_formats)) => {
                for format in sample_formats {
                    check_sample_format(format)?;
                }
            }
            Ok(None) => { /* assume UInt format */ }
            Err(other) => return Err(ImageError::from_tiff_decode(other)),
        };

        let color_type = match color_type {
            tiff::ColorType::Gray(8) => ColorType::L8,
            tiff::ColorType::Gray(16) => ColorType::L16,
            tiff::ColorType::GrayA(8) => ColorType::La8,
            tiff::ColorType::GrayA(16) => ColorType::La16,
            tiff::ColorType::RGB(8) => ColorType::Rgb8,
            tiff::ColorType::RGB(16) => ColorType::Rgb16,
            tiff::ColorType::RGBA(8) => ColorType::Rgba8,
            tiff::ColorType::RGBA(16) => ColorType::Rgba16,

            tiff::ColorType::Palette(n) | tiff::ColorType::Gray(n) => {
                return Err(err_unknown_color_type(n))
            }
            tiff::ColorType::GrayA(n) => return Err(err_unknown_color_type(n.saturating_mul(2))),
            tiff::ColorType::RGB(n) => return Err(err_unknown_color_type(n.saturating_mul(3))),
            tiff::ColorType::RGBA(n) | tiff::ColorType::CMYK(n) => {
                return Err(err_unknown_color_type(n.saturating_mul(4)))
            }
        };

        Ok(TiffDecoder {
            dimensions,
            color_type,
            inner: Some(inner),
        })
    }
}

fn check_sample_format(sample_format: u16) -> Result<(), ImageError> {
    match tiff::tags::SampleFormat::from_u16(sample_format) {
        Some(tiff::tags::SampleFormat::Uint) => Ok(()),
        Some(other) => Err(ImageError::Unsupported(
            UnsupportedError::from_format_and_kind(
                ImageFormat::Tiff.into(),
                UnsupportedErrorKind::GenericFeature(format!(
                    "Unhandled TIFF sample format {:?}",
                    other
                )),
            ),
        )),
        None => Err(ImageError::Decoding(DecodingError::from_format_hint(
            ImageFormat::Tiff.into(),
        ))),
    }
}

fn err_unknown_color_type(value: u8) -> ImageError {
    ImageError::Unsupported(UnsupportedError::from_format_and_kind(
        ImageFormat::Tiff.into(),
        UnsupportedErrorKind::Color(ExtendedColorType::Unknown(value)),
    ))
}

impl ImageError {
    fn from_tiff_decode(err: tiff::TiffError) -> ImageError {
        match err {
            tiff::TiffError::IoError(err) => ImageError::IoError(err),
            err @ tiff::TiffError::FormatError(_)
            | err @ tiff::TiffError::IntSizeError
            | err @ tiff::TiffError::UsageError(_) => {
                ImageError::Decoding(DecodingError::new(ImageFormat::Tiff.into(), err))
            }
            tiff::TiffError::UnsupportedError(desc) => {
                ImageError::Unsupported(UnsupportedError::from_format_and_kind(
                    ImageFormat::Tiff.into(),
                    UnsupportedErrorKind::GenericFeature(desc.to_string()),
                ))
            }
            tiff::TiffError::LimitsExceeded => {
                ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
            }
        }
    }

    fn from_tiff_encode(err: tiff::TiffError) -> ImageError {
        match err {
            tiff::TiffError::IoError(err) => ImageError::IoError(err),
            err @ tiff::TiffError::FormatError(_)
            | err @ tiff::TiffError::IntSizeError
            | err @ tiff::TiffError::UsageError(_) => {
                ImageError::Encoding(EncodingError::new(ImageFormat::Tiff.into(), err))
            }
            tiff::TiffError::UnsupportedError(desc) => {
                ImageError::Unsupported(UnsupportedError::from_format_and_kind(
                    ImageFormat::Tiff.into(),
                    UnsupportedErrorKind::GenericFeature(desc.to_string()),
                ))
            }
            tiff::TiffError::LimitsExceeded => {
                ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
            }
        }
    }
}

/// Wrapper struct around a `Cursor<Vec<u8>>`
pub struct TiffReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
impl<R> Read for TiffReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        if self.0.position() == 0 && buf.is_empty() {
            mem::swap(buf, self.0.get_mut());
            Ok(buf.len())
        } else {
            self.0.read_to_end(buf)
        }
    }
}

impl<'a, R: 'a + Read + Seek> ImageDecoder<'a> for TiffDecoder<R> {
    type Reader = TiffReader<R>;

    fn dimensions(&self) -> (u32, u32) {
        self.dimensions
    }

    fn color_type(&self) -> ColorType {
        self.color_type
    }

    fn set_limits(&mut self, limits: crate::io::Limits) -> ImageResult<()> {
        limits.check_support(&crate::io::LimitSupport::default())?;

        let (width, height) = self.dimensions();
        limits.check_dimensions(width, height)?;

        let max_alloc = limits.max_alloc.unwrap_or(u64::MAX);
        let max_intermediate_alloc = max_alloc.saturating_sub(self.total_bytes());

        let mut tiff_limits: tiff::decoder::Limits = Default::default();
        tiff_limits.decoding_buffer_size =
            usize::try_from(max_alloc - max_intermediate_alloc).unwrap_or(usize::MAX);
        tiff_limits.intermediate_buffer_size =
            usize::try_from(max_intermediate_alloc).unwrap_or(usize::MAX);
        tiff_limits.ifd_value_size = tiff_limits.intermediate_buffer_size;
        self.inner = Some(self.inner.take().unwrap().with_limits(tiff_limits));

        Ok(())
    }

    fn into_reader(self) -> ImageResult<Self::Reader> {
        let buf = match self
            .inner
            .unwrap()
            .read_image()
            .map_err(ImageError::from_tiff_decode)?
        {
            tiff::decoder::DecodingResult::U8(v) => v,
            tiff::decoder::DecodingResult::U16(v) => utils::vec_copy_to_u8(&v),
            tiff::decoder::DecodingResult::U32(v) => utils::vec_copy_to_u8(&v),
            tiff::decoder::DecodingResult::U64(v) => utils::vec_copy_to_u8(&v),
            tiff::decoder::DecodingResult::I8(v) => utils::vec_copy_to_u8(&v),
            tiff::decoder::DecodingResult::I16(v) => utils::vec_copy_to_u8(&v),
            tiff::decoder::DecodingResult::I32(v) => utils::vec_copy_to_u8(&v),
            tiff::decoder::DecodingResult::I64(v) => utils::vec_copy_to_u8(&v),
            tiff::decoder::DecodingResult::F32(v) => utils::vec_copy_to_u8(&v),
            tiff::decoder::DecodingResult::F64(v) => utils::vec_copy_to_u8(&v),
        };

        Ok(TiffReader(Cursor::new(buf), PhantomData))
    }

    fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
        match self
            .inner
            .unwrap()
            .read_image()
            .map_err(ImageError::from_tiff_decode)?
        {
            tiff::decoder::DecodingResult::U8(v) => {
                buf.copy_from_slice(&v);
            }
            tiff::decoder::DecodingResult::U16(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::U32(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::U64(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::I8(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::I16(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::I32(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::I64(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::F32(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::F64(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
        }
        Ok(())
    }
}

/// Encoder for tiff images
pub struct TiffEncoder<W> {
    w: W,
}

// Utility to simplify and deduplicate error handling during 16-bit encoding.
fn u8_slice_as_u16(buf: &[u8]) -> ImageResult<&[u16]> {
    bytemuck::try_cast_slice(buf).map_err(|err| {
        // If the buffer is not aligned or the correct length for a u16 slice, err.
        //
        // `bytemuck::PodCastError` of bytemuck-1.2.0 does not implement
        // `Error` and `Display` trait.
        // See <https://github.com/Lokathor/bytemuck/issues/22>.
        ImageError::Parameter(ParameterError::from_kind(ParameterErrorKind::Generic(
            format!("{:?}", err),
        )))
    })
}

impl<W: Write + Seek> TiffEncoder<W> {
    /// Create a new encoder that writes its output to `w`
    pub fn new(w: W) -> TiffEncoder<W> {
        TiffEncoder { w }
    }

    /// Encodes the image `image` that has dimensions `width` and `height` and `ColorType` `c`.
    ///
    /// 16-bit types assume the buffer is native endian.
    pub fn encode(self, data: &[u8], width: u32, height: u32, color: ColorType) -> ImageResult<()> {
        let mut encoder =
            tiff::encoder::TiffEncoder::new(self.w).map_err(ImageError::from_tiff_encode)?;
        match color {
            ColorType::L8 => {
                encoder.write_image::<tiff::encoder::colortype::Gray8>(width, height, data)
            }
            ColorType::Rgb8 => {
                encoder.write_image::<tiff::encoder::colortype::RGB8>(width, height, data)
            }
            ColorType::Rgba8 => {
                encoder.write_image::<tiff::encoder::colortype::RGBA8>(width, height, data)
            }
            ColorType::L16 => encoder.write_image::<tiff::encoder::colortype::Gray16>(
                width,
                height,
                u8_slice_as_u16(data)?,
            ),
            ColorType::Rgb16 => encoder.write_image::<tiff::encoder::colortype::RGB16>(
                width,
                height,
                u8_slice_as_u16(data)?,
            ),
            ColorType::Rgba16 => encoder.write_image::<tiff::encoder::colortype::RGBA16>(
                width,
                height,
                u8_slice_as_u16(data)?,
            ),
            _ => {
                return Err(ImageError::Unsupported(
                    UnsupportedError::from_format_and_kind(
                        ImageFormat::Tiff.into(),
                        UnsupportedErrorKind::Color(color.into()),
                    ),
                ))
            }
        }
        .map_err(ImageError::from_tiff_encode)?;

        Ok(())
    }
}

impl<W: Write + Seek> ImageEncoder for TiffEncoder<W> {
    fn write_image(
        self,
        buf: &[u8],
        width: u32,
        height: u32,
        color_type: ColorType,
    ) -> ImageResult<()> {
        self.encode(buf, width, height, color_type)
    }
}
<|endoftext|>


<commit_before>//! The `lang` library bundles core functions that integrate deeply into the language runtime and
//! form an essential part of the language.
//!
//! Examples:
//! * `assert`
//! * `error`
//! * `setmetatable`
//! * `tonumber`
//! * `tostring`
//! * `type`
//! * `pcall`

#![feature(slice_patterns)] // required by the abomination macros

#[macro_use]
extern crate lea_vm as vm;
extern crate lea_compiler as compiler;

use vm::VM;
use vm::mem::{GcStrategy, TracedRef};
use vm::{Str, Value};
use vm::function::{Function, FunctionProto, Upval};

use compiler::{CompileConfig, compile_str};

lea_libfn! {
    fn assert(vm) {
        (val: *, msg: *, rest: ...) -> (vals: ...) => {
            if !val.is_truthy() { return Err(msg.into()) }
            return [val, msg, rest]
        }
        (val: *) -> (val: *) => {
            if !val.is_truthy() { return Err("assertion failed".to_owned().into()) }
            return [val]
        }
    }

    fn error(vm) {
        () -> () => {
            return Err("explicit error".to_owned().into())
        }
        (msg: *) -> () => {
            return Err(msg.into())
        }
        (msg: *, _level: number) -> () => {
            // TODO add error position info according to `level`
            return Err(msg.into())
        }
    }

    fn tostring(vm) {
        (val: string) -> (s: string) => {
            return [Value::String(val)]
        }
        (val: number) -> (s: string) => {
            return [format!("{}", val)]
        }
    }

    fn tonumber(vm) {
        (num: number) -> (num: number) => {
            return [num]
        }
        (s: string) -> (num: number) => {
            let result = unsafe { vm.gc.get_ref(s) }.parse::<::vm::number::LeaFloat>();
            let num = match result {
                Ok(num) => num,
                Err(e) => return Err(format!("{}", e).into()),
            };

            return [num]
        }
    }

    fn type_name(vm) {
        (of: *) -> (typename: string) => {
            return [of.get_type_name()]
        }
    }

    fn print(vm) {
        (values: ...) -> () => {
            for (i, val) in values.iter().enumerate() {
                if i != 0 {
                    print!("\t");
                }

                unsafe { val.fmt(::std::io::stdout(), &vm.gc) }.unwrap();
            }
            println!("");
        }
    }

    fn load(vm) {
        (code: string, source_name: string, _mode: string, env: *) -> (compiled: fn, err: string) => {
            let res = match super::load_impl(vm, code, Some(source_name), Some(env)) {
                Ok(res) => res,
                Err(e) => return Err(e.into()),
            };

            return [res]
        }
        (code: string, source_name: string, _mode: string) -> (compiled: fn, err: string) => {
            let res = match super::load_impl(vm, code, Some(source_name), None) {
                Ok(res) => res,
                Err(e) => return Err(e.into()),
            };

            return [res]
        }
        (code: string, source_name: string) -> (compiled: fn, err: string) => {
            let res = match super::load_impl(vm, code, Some(source_name), None) {
                Ok(res) => res,
                Err(e) => return Err(e.into()),
            };

            return [res]
        }
        (code: string) -> (compiled: fn, err: string) => {
            let res = match super::load_impl(vm, code, None, None) {
                Ok(res) => res,
                Err(e) => return Err(e.into()),
            };

            return [res]
        }
    }
}

fn load_impl(vm: &mut VM,
             code: TracedRef<Str>,
             source_name: Option<TracedRef<Str>>,
             env: Option<Value>)
             -> Result<Value, String> {
    let compile_result = {
        let cfg = CompileConfig::default();
        let code: &str = unsafe { &vm.gc.get_ref(code) };
        let source_name: &str = match source_name {
            Some(s) => unsafe { &vm.gc.get_ref(s) },
            None => "<load>",
        };

        // compile and dispose of the unneeded data. since the AST is borrowed, this wouldn't work.
        compile_str(code, source_name, &cfg).map(|result| result.mainproto)
    };

    match compile_result {
        Ok(res) => {
            let proto = FunctionProto::from_fndata(res, &mut vm.gc);
            let main = vm.main_ref();
            let main_env: Value = match unsafe { vm.gc.get_ref(main).upvalues[0].get() } {
                Upval::Closed(env) => env,
                Upval::Open(_) => unreachable!(),
            };

            let f = Function::with_env(&vm.gc, proto, env.unwrap_or(main_env));

            Ok(Value::Closure(vm.gc.register_obj(f)))
        }
        Err(e) => {
            Err(format!("{:?}", e)) // FIXME impl Display for CompileError
        }
    }
}

lea_lib! {
    _VERSION = str option_env!("CARGO_PKG_VERSION").unwrap_or("<unknown version>"),
    assert = fn assert,
    error = fn error,
    tostring = fn tostring,
    tonumber = fn tonumber,
    type = fn type_name,
    print = fn print,
    load = fn load,
}
<commit_msg>Add partial implementation of dofile<commit_after>//! The `lang` library bundles core functions that integrate deeply into the language runtime and
//! form an essential part of the language.
//!
//! Examples:
//! * `assert`
//! * `error`
//! * `setmetatable`
//! * `tonumber`
//! * `tostring`
//! * `type`
//! * `pcall`

#![feature(slice_patterns)] // required by the abomination macros

#[macro_use]
extern crate lea_vm as vm;
extern crate lea_compiler as compiler;

use vm::VM;
use vm::mem::{GcStrategy, TracedRef};
use vm::{Str, Value};
use vm::function::{Function, FunctionProto, Upval};

use compiler::{CompileConfig, compile_str};

lea_libfn! {
    fn assert(vm) {
        (val: *, msg: *, rest: ...) -> (vals: ...) => {
            if !val.is_truthy() { return Err(msg.into()) }
            return [val, msg, rest]
        }
        (val: *) -> (val: *) => {
            if !val.is_truthy() { return Err("assertion failed".to_owned().into()) }
            return [val]
        }
    }

    fn error(vm) {
        () -> () => {
            return Err("explicit error".to_owned().into())
        }
        (msg: *) -> () => {
            return Err(msg.into())
        }
        (msg: *, _level: number) -> () => {
            // TODO add error position info according to `level`
            return Err(msg.into())
        }
    }

    fn tostring(vm) {
        (val: string) -> (s: string) => {
            return [Value::String(val)]
        }
        (val: number) -> (s: string) => {
            return [format!("{}", val)]
        }
    }

    fn tonumber(vm) {
        (num: number) -> (num: number) => {
            return [num]
        }
        (s: string) -> (num: number) => {
            let result = unsafe { vm.gc.get_ref(s) }.parse::<::vm::number::LeaFloat>();
            let num = match result {
                Ok(num) => num,
                Err(e) => return Err(format!("{}", e).into()),
            };

            return [num]
        }
    }

    fn type_name(vm) {
        (of: *) -> (typename: string) => {
            return [of.get_type_name()]
        }
    }

    fn print(vm) {
        (values: ...) -> () => {
            for (i, val) in values.iter().enumerate() {
                if i != 0 {
                    print!("\t");
                }

                unsafe { val.fmt(::std::io::stdout(), &vm.gc) }.unwrap();
            }
            println!("");
        }
    }

    fn load(vm) {
        (code: string, source_name: string, _mode: string, env: *) -> (compiled: fn, err: string) => {
            let res = match super::load_impl(vm, code, Some(source_name), Some(env)) {
                Ok(res) => res,
                Err(e) => return Err(e.into()),
            };

            return [res]
        }
        (code: string, source_name: string, _mode: string) -> (compiled: fn, err: string) => {
            let res = match super::load_impl(vm, code, Some(source_name), None) {
                Ok(res) => res,
                Err(e) => return Err(e.into()),
            };

            return [res]
        }
        (code: string, source_name: string) -> (compiled: fn, err: string) => {
            let res = match super::load_impl(vm, code, Some(source_name), None) {
                Ok(res) => res,
                Err(e) => return Err(e.into()),
            };

            return [res]
        }
        (code: string) -> (compiled: fn, err: string) => {
            let res = match super::load_impl(vm, code, None, None) {
                Ok(res) => res,
                Err(e) => return Err(e.into()),
            };

            return [res]
        }
    }

    fn dofile(vm) {
        () -> (ret: ...) => {
            use std::io::{Read, stdin};

            let mut buf = String::new();
            let _res: &[Value] = match stdin().read_to_string(&mut buf) {
                Ok(_) => {
                    let code = vm.gc.intern_str(buf);

                    match super::load_impl(vm, code, None, None) {
                        Ok(_func) => unimplemented!(),    // TODO call this
                        Err(e) => return Err(e.into()),
                    }
                }
                Err(e) => return Err(format!("couldn't read from stdin: {}", e).into())
            };

            return [_res]
        }
        (filename: string) -> (ret: ...) => {
            use std::io::Read;
            use std::fs::File;

            let mut file = match File::open(unsafe { &vm.gc.get_ref(filename) as &str }) {
                Ok(f) => f,
                Err(e) => return Err(format!(
                    "couldn't open file '{}': {}",
                    unsafe { &vm.gc.get_ref(filename) }, e).into()),
            };

            let mut buf = String::new();
            let _res: &[Value] = match file.read_to_string(&mut buf) {
                Ok(_) => {
                    let code = vm.gc.intern_str(buf);

                    match super::load_impl(vm, code, None, None) {
                        Ok(_func) => unimplemented!(),    // TODO call this
                        Err(e) => return Err(e.into()),
                    }
                }
                Err(e) => return Err(format!(
                    "couldn't read file '{}': {}",
                    unsafe { &vm.gc.get_ref(filename) }, e).into())
            };

            return [_res]
        }
    }
}

fn load_impl(vm: &mut VM,
             code: TracedRef<Str>,
             source_name: Option<TracedRef<Str>>,
             env: Option<Value>)
             -> Result<Value, String> {
    let compile_result = {
        let cfg = CompileConfig::default();
        let code: &str = unsafe { &vm.gc.get_ref(code) };
        let source_name: &str = match source_name {
            Some(s) => unsafe { &vm.gc.get_ref(s) },
            None => "<load>",
        };

        // compile and dispose of the unneeded data. since the AST is borrowed, this wouldn't work.
        compile_str(code, source_name, &cfg).map(|result| result.mainproto)
    };

    match compile_result {
        Ok(res) => {
            let proto = FunctionProto::from_fndata(res, &mut vm.gc);
            let main = vm.main_ref();
            let main_env: Value = match unsafe { vm.gc.get_ref(main).upvalues[0].get() } {
                Upval::Closed(env) => env,
                Upval::Open(_) => unreachable!(),
            };

            let f = Function::with_env(&vm.gc, proto, env.unwrap_or(main_env));

            Ok(Value::Closure(vm.gc.register_obj(f)))
        }
        Err(e) => {
            Err(format!("{:?}", e)) // FIXME impl Display for CompileError
        }
    }
}

lea_lib! {
    _VERSION = str option_env!("CARGO_PKG_VERSION").unwrap_or("<unknown version>"),
    assert = fn assert,
    error = fn error,
    tostring = fn tostring,
    tonumber = fn tonumber,
    type = fn type_name,
    print = fn print,
    load = fn load,
    dofile = fn dofile,
}
<|endoftext|>


<commit_before>use raft::{
    SyncServiceClient, RaftMsg, LogEntry, ClientQryResponse,
    ClientCmdResponse};
use raft::state_machine::OpType;
use raft::state_machine::master::{ExecResult, ExecError};
use raft::state_machine::callback::client::SubscriptionService;
use raft::state_machine::configs::CONFIG_SM_ID;
use raft::state_machine::configs::commands::{subscribe as conf_subscribe};
use std::collections::{HashMap, BTreeMap, HashSet};
use std::iter::FromIterator;
use parking_lot::{RwLock, RwLockWriteGuard};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::cmp::max;
use bifrost_hasher::{hash_str, hash_bytes};
use rand;
use rpc;
use backtrace::Backtrace;

const ORDERING: Ordering = Ordering::Relaxed;
pub type Client = Arc<SyncServiceClient>;

#[derive(Debug)]
pub enum ClientError {
    LeaderIdValid,
    ServerUnreachable,
}

#[derive(Debug)]
pub enum SubscriptionError {
    RemoteError,
    SubServiceNotSet,
}

struct QryMeta {
    pos: AtomicU64
}

struct Members {
    clients: BTreeMap<u64, Client>,
    id_map: HashMap<u64, String>,
}

pub struct RaftClient {
    qry_meta: QryMeta,
    members: RwLock<Members>,
    callback: RwLock<Option<Arc<SubscriptionService>>>,
    leader_id: AtomicU64,
    last_log_id: AtomicU64,
    last_log_term: AtomicU64,
    service_id: u64
}

impl RaftClient {
    pub fn new(servers: &Vec<String>, service_id: u64) -> Result<Arc<RaftClient>, ClientError> {
        let client = RaftClient {
            qry_meta: QryMeta {
                pos: AtomicU64::new(rand::random::<u64>())
            },
            members: RwLock::new(Members {
                clients: BTreeMap::new(),
                id_map: HashMap::new()
            }),
            leader_id: AtomicU64::new(0),
            last_log_id: AtomicU64::new(0),
            last_log_term: AtomicU64::new(0),
            service_id: service_id,
            callback: RwLock::new(None)
        };
        let init = {
            let mut members = client.members.write();
            client.update_info(
                &mut members,
                &HashSet::from_iter(servers.iter().cloned())
            )
        };
        match init {
            Ok(_) => Ok(Arc::new(client)),
            Err(e) => Err(e)
        }
    }
    pub fn prepare_subscription(&self, server: &Arc<rpc::Server>) {
        let mut callback = self.callback.write();
        if callback.is_none() {
            let sub_service = SubscriptionService::initialize(&server);
            *callback = Some(sub_service.clone());
        }
    }

   fn update_info(&self, members: &mut RwLockWriteGuard<Members>, servers: &HashSet<String>) -> Result<(), ClientError> {
        let mut cluster_info = None;
        for server_addr in servers {
            let id = hash_str(&server_addr);
            if !members.clients.contains_key(&id) {
                match rpc::DEFAULT_CLIENT_POOL.get(&server_addr) {
                    Ok(client) => {
                        members.clients.insert(id, SyncServiceClient::new(self.service_id, &client));
                    },
                    Err(_) => {continue;}
                }
            }
            let client = members.clients.get(&id).unwrap();
            if let Ok(Ok(info)) = client.c_server_cluster_info() {
                if info.leader_id != 0 {
                    cluster_info = Some(info);
                    break;
                }
            }
        }
        match cluster_info {
            Some(info) => {
                let remote_members = info.members;
                let mut remote_ids = HashSet::with_capacity(remote_members.len());
                members.id_map.clear();
                for (id, addr) in remote_members {
                    members.id_map.insert(id, addr);
                    remote_ids.insert(id);
                }
                let mut connected_ids = HashSet::with_capacity(members.clients.len());
                for id in members.clients.keys() {connected_ids.insert(*id);}
                let ids_to_remove = connected_ids.difference(&remote_ids);
                for id in ids_to_remove {members.clients.remove(id);}
                for id in remote_ids.difference(&connected_ids) {
                    let addr = members.id_map.get(id).unwrap().clone();
                    if !members.clients.contains_key(id) {
                        if let Ok(client) = rpc::DEFAULT_CLIENT_POOL.get(&addr) {
                            members.clients.insert(*id, SyncServiceClient::new(self.service_id, &client));
                        }
                    }
                }
                self.leader_id.store(info.leader_id, ORDERING);
                Ok(())
            },
            None => Err(ClientError::ServerUnreachable),
        }
    }

    pub fn execute<R>(&self, sm_id: u64, msg: &RaftMsg<R>) -> Result<R, ExecError> {
        let (fn_id, op, req_data) = msg.encode();
        let response = match op {
            OpType::QUERY => {
                self.query(sm_id, fn_id, &req_data, 0)
            },
            OpType::COMMAND | OpType::SUBSCRIBE => {
                self.command(sm_id, fn_id, &req_data, 0)
            },
        };
        match response {
            Ok(data) => {
                match data {
                    Ok(data) => Ok(msg.decode_return(&data)),
                    Err(e) => Err(e)
                }
            },
            Err(e) => Err(e)
        }
    }

    pub fn subscribe
    <M, R, F>
    (&self, sm_id: u64, msg: M, f: F) -> Result<Result<u64, SubscriptionError>, ExecError>
    where M: RaftMsg<R> + 'static,
          F: Fn(R) + 'static + Send + Sync
    {
        let callback = self.callback.read();
        if callback.is_none() {
            println!("Subscription service not set: {:?}", Backtrace::new());
            return Ok(Err(SubscriptionError::SubServiceNotSet))
        }
        let callback = callback.clone().unwrap();
        let raft_sid = self.service_id;
        let (fn_id, pattern_id) = {
            let (fn_id, _, pattern_data) = msg.encode();
            (fn_id, hash_bytes(pattern_data.as_slice()))
        };
        let wrapper_fn = move |data: Vec<u8>| {
            f(msg.decode_return(&data))
        };
        let key = (raft_sid, sm_id, fn_id, pattern_id);
        let mut subs_map = callback.subs.write();
        let mut subs_lst = subs_map.entry(key).or_insert_with(|| Vec::new());
        subs_lst.push(Box::new(wrapper_fn));
        let cluster_subs = self.execute(
            CONFIG_SM_ID,
            &conf_subscribe::new(&key, &callback.server_address, &callback.session_id)
        );
        match cluster_subs {
            Ok(sub_result) => match sub_result {
                Ok(sub_id) => Ok(Ok(sub_id)),
                Err(_) => Ok(Err(SubscriptionError::RemoteError))
            },
            Err(e) => Err(e)
        }
    }

    fn query(&self, sm_id: u64, fn_id: u64, data: &Vec<u8>, depth: usize) -> Result<ExecResult, ExecError> {
        let pos = self.qry_meta.pos.fetch_add(1, ORDERING);
        let mut num_members = 0;
        let res = {
            let members = self.members.read();
            let client = {
                let members_count = members.clients.len();
                if members_count < 1 {
                    return Err(ExecError::ServersUnreachable)
                } else {
                    members.clients.values().nth(pos as usize % members_count).unwrap()
                }
            };
            num_members = members.clients.len();
            client.c_query(&self.gen_log_entry(sm_id, fn_id, data))
        };
        match res {
            Ok(Ok(res)) => {
                match res {
                    ClientQryResponse::LeftBehind => {
                        if depth >= num_members {
                            Err(ExecError::TooManyRetry)
                        } else {
                            self.query(sm_id, fn_id, data, depth + 1)
                        }
                    },
                    ClientQryResponse::Success{
                        data, last_log_term, last_log_id
                    } => {
                        swap_when_greater(&self.last_log_id, last_log_id);
                        swap_when_greater(&self.last_log_term, last_log_term);
                        Ok(data)
                    },
                }
            },
            _ => Err(ExecError::Unknown)
        }
    }

    fn command(&self, sm_id: u64, fn_id: u64, data: &Vec<u8>, depth: usize) -> Result<ExecResult, ExecError> {
        enum FailureAction {
            SwitchLeader,
            NotCommitted,
            UpdateInfo,
            NotLeader,
            Retry,
        }
        let failure = {
            if depth > 0 {
                let members = self.members.read();
                let num_members = members.clients.len();
                if depth >= max(num_members, 5) {
                    return Err(ExecError::TooManyRetry)
                };
            }
            match self.current_leader_client() {
                Some((leader_id, client)) => {
                    match client.c_command(&self.gen_log_entry(sm_id, fn_id, data)) {
                        Ok(Ok(ClientCmdResponse::Success {
                                  data, last_log_term, last_log_id
                              })) => {
                            swap_when_greater(&self.last_log_id, last_log_id);
                            swap_when_greater(&self.last_log_term, last_log_term);
                            return Ok(data);
                        },
                        Ok(Ok(ClientCmdResponse::NotLeader(leader_id))) => {
                            self.leader_id.store(leader_id, ORDERING);
                            FailureAction::NotLeader
                        },
                        Ok(Ok(ClientCmdResponse::NotCommitted)) => {
                            FailureAction::NotCommitted
                        },
                        Err(e) => {
                            println!("CLIENT: E1 - {} - {:?}", leader_id, e);
                            FailureAction::SwitchLeader // need switch server for leader
                        }
                        Ok(Err(e)) => {
                            println!("CLIENT: E2 - {} - {:?}", leader_id, e);
                            FailureAction::SwitchLeader // need switch server for leader
                        }
                    }
                },
                None => FailureAction::UpdateInfo // need update members
            }
        }; //
        match failure {
            FailureAction::SwitchLeader => {
                let members = self.members.read();
                let num_members = members.clients.len();
                let pos = self.qry_meta.pos.load(ORDERING);
                let leader_id = self.leader_id.load(ORDERING);
                let index = members.clients.keys()
                    .nth(pos as usize % num_members)
                    .unwrap();
                self.leader_id.compare_and_swap(leader_id, *index, ORDERING);
                println!("CLIENT: Switch leader");
            },
            _ => {}
        }
        self.command(sm_id, fn_id, data, depth + 1)
    }

    fn gen_log_entry(&self, sm_id: u64, fn_id: u64, data: &Vec<u8>) -> LogEntry {
        LogEntry {
            id: self.last_log_id.load(ORDERING),
            term: self.last_log_term.load(ORDERING),
            sm_id: sm_id,
            fn_id: fn_id,
            data: data.clone()
        }
    }
    pub fn leader_id(&self) -> u64 {self.leader_id.load(ORDERING)}
    pub fn leader_client(&self) -> Option<(u64, Client)> {
        let members = self.members.read();
        let leader_id = self.leader_id();
        if let Some(client) = members.clients.get(&leader_id) {
            Some((leader_id,  client.clone()))
        } else {
            None
        }
    }
    pub fn current_leader_client(&self) -> Option<(u64, Client)> {
        {
            let leader_client = self.leader_client();
            if leader_client.is_some() {
                return leader_client
            }
        }
        {
            let mut members = self.members.write();
            let mut members_addrs = HashSet::new();
            for address in members.id_map.values() {
                members_addrs.insert(address.clone());

            }
            self.update_info(&mut members, &members_addrs);
            let leader_id = self.leader_id.load(ORDERING);
            if let Some(client) = members.clients.get(&leader_id) {
                Some((leader_id,  client.clone()))
            } else {
                None
            }
        }
    }
    pub fn current_leader_rpc_client(&self) -> Option<Arc<rpc::RPCClient>> {
        match self.current_leader_client() {
            Some((_, client)) => Some(client.client.clone()),
            None => None
        }
    }
}

fn swap_when_greater(atomic: &AtomicU64, value: u64) {
    let mut orig_num = atomic.load(ORDERING);
    loop {
        if orig_num >= value {
            return;
        }
        let actual = atomic.compare_and_swap(orig_num, value, ORDERING);
        if actual == orig_num {
            return;
        } else {
            orig_num = actual;
        }
    }
}<commit_msg>return subscription result<commit_after>use raft::{
    SyncServiceClient, RaftMsg, LogEntry, ClientQryResponse,
    ClientCmdResponse};
use raft::state_machine::OpType;
use raft::state_machine::master::{ExecResult, ExecError};
use raft::state_machine::callback::client::SubscriptionService;
use raft::state_machine::configs::CONFIG_SM_ID;
use raft::state_machine::configs::commands::{subscribe as conf_subscribe};
use std::collections::{HashMap, BTreeMap, HashSet};
use std::iter::FromIterator;
use parking_lot::{RwLock, RwLockWriteGuard};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::cmp::max;
use bifrost_hasher::{hash_str, hash_bytes};
use rand;
use rpc;
use backtrace::Backtrace;

const ORDERING: Ordering = Ordering::Relaxed;
pub type Client = Arc<SyncServiceClient>;

#[derive(Debug)]
pub enum ClientError {
    LeaderIdValid,
    ServerUnreachable,
}

#[derive(Debug)]
pub enum SubscriptionError {
    RemoteError,
    SubServiceNotSet,
}

struct QryMeta {
    pos: AtomicU64
}

struct Members {
    clients: BTreeMap<u64, Client>,
    id_map: HashMap<u64, String>,
}

pub struct RaftClient {
    qry_meta: QryMeta,
    members: RwLock<Members>,
    callback: RwLock<Option<Arc<SubscriptionService>>>,
    leader_id: AtomicU64,
    last_log_id: AtomicU64,
    last_log_term: AtomicU64,
    service_id: u64
}

impl RaftClient {
    pub fn new(servers: &Vec<String>, service_id: u64) -> Result<Arc<RaftClient>, ClientError> {
        let client = RaftClient {
            qry_meta: QryMeta {
                pos: AtomicU64::new(rand::random::<u64>())
            },
            members: RwLock::new(Members {
                clients: BTreeMap::new(),
                id_map: HashMap::new()
            }),
            leader_id: AtomicU64::new(0),
            last_log_id: AtomicU64::new(0),
            last_log_term: AtomicU64::new(0),
            service_id: service_id,
            callback: RwLock::new(None)
        };
        let init = {
            let mut members = client.members.write();
            client.update_info(
                &mut members,
                &HashSet::from_iter(servers.iter().cloned())
            )
        };
        match init {
            Ok(_) => Ok(Arc::new(client)),
            Err(e) => Err(e)
        }
    }
    pub fn prepare_subscription(&self, server: &Arc<rpc::Server>) -> Option<()> {
        let mut callback = self.callback.write();
        if callback.is_none() {
            let sub_service = SubscriptionService::initialize(&server);
            *callback = Some(sub_service.clone());
            return Some(())
        } else {
            return None
        }
    }

   fn update_info(&self, members: &mut RwLockWriteGuard<Members>, servers: &HashSet<String>) -> Result<(), ClientError> {
        let mut cluster_info = None;
        for server_addr in servers {
            let id = hash_str(&server_addr);
            if !members.clients.contains_key(&id) {
                match rpc::DEFAULT_CLIENT_POOL.get(&server_addr) {
                    Ok(client) => {
                        members.clients.insert(id, SyncServiceClient::new(self.service_id, &client));
                    },
                    Err(_) => {continue;}
                }
            }
            let client = members.clients.get(&id).unwrap();
            if let Ok(Ok(info)) = client.c_server_cluster_info() {
                if info.leader_id != 0 {
                    cluster_info = Some(info);
                    break;
                }
            }
        }
        match cluster_info {
            Some(info) => {
                let remote_members = info.members;
                let mut remote_ids = HashSet::with_capacity(remote_members.len());
                members.id_map.clear();
                for (id, addr) in remote_members {
                    members.id_map.insert(id, addr);
                    remote_ids.insert(id);
                }
                let mut connected_ids = HashSet::with_capacity(members.clients.len());
                for id in members.clients.keys() {connected_ids.insert(*id);}
                let ids_to_remove = connected_ids.difference(&remote_ids);
                for id in ids_to_remove {members.clients.remove(id);}
                for id in remote_ids.difference(&connected_ids) {
                    let addr = members.id_map.get(id).unwrap().clone();
                    if !members.clients.contains_key(id) {
                        if let Ok(client) = rpc::DEFAULT_CLIENT_POOL.get(&addr) {
                            members.clients.insert(*id, SyncServiceClient::new(self.service_id, &client));
                        }
                    }
                }
                self.leader_id.store(info.leader_id, ORDERING);
                Ok(())
            },
            None => Err(ClientError::ServerUnreachable),
        }
    }

    pub fn execute<R>(&self, sm_id: u64, msg: &RaftMsg<R>) -> Result<R, ExecError> {
        let (fn_id, op, req_data) = msg.encode();
        let response = match op {
            OpType::QUERY => {
                self.query(sm_id, fn_id, &req_data, 0)
            },
            OpType::COMMAND | OpType::SUBSCRIBE => {
                self.command(sm_id, fn_id, &req_data, 0)
            },
        };
        match response {
            Ok(data) => {
                match data {
                    Ok(data) => Ok(msg.decode_return(&data)),
                    Err(e) => Err(e)
                }
            },
            Err(e) => Err(e)
        }
    }

    pub fn subscribe
    <M, R, F>
    (&self, sm_id: u64, msg: M, f: F) -> Result<Result<u64, SubscriptionError>, ExecError>
    where M: RaftMsg<R> + 'static,
          F: Fn(R) + 'static + Send + Sync
    {
        let callback = self.callback.read();
        if callback.is_none() {
            println!("Subscription service not set: {:?}", Backtrace::new());
            return Ok(Err(SubscriptionError::SubServiceNotSet))
        }
        let callback = callback.clone().unwrap();
        let raft_sid = self.service_id;
        let (fn_id, pattern_id) = {
            let (fn_id, _, pattern_data) = msg.encode();
            (fn_id, hash_bytes(pattern_data.as_slice()))
        };
        let wrapper_fn = move |data: Vec<u8>| {
            f(msg.decode_return(&data))
        };
        let key = (raft_sid, sm_id, fn_id, pattern_id);
        let mut subs_map = callback.subs.write();
        let mut subs_lst = subs_map.entry(key).or_insert_with(|| Vec::new());
        subs_lst.push(Box::new(wrapper_fn));
        let cluster_subs = self.execute(
            CONFIG_SM_ID,
            &conf_subscribe::new(&key, &callback.server_address, &callback.session_id)
        );
        match cluster_subs {
            Ok(sub_result) => match sub_result {
                Ok(sub_id) => Ok(Ok(sub_id)),
                Err(_) => Ok(Err(SubscriptionError::RemoteError))
            },
            Err(e) => Err(e)
        }
    }

    fn query(&self, sm_id: u64, fn_id: u64, data: &Vec<u8>, depth: usize) -> Result<ExecResult, ExecError> {
        let pos = self.qry_meta.pos.fetch_add(1, ORDERING);
        let mut num_members = 0;
        let res = {
            let members = self.members.read();
            let client = {
                let members_count = members.clients.len();
                if members_count < 1 {
                    return Err(ExecError::ServersUnreachable)
                } else {
                    members.clients.values().nth(pos as usize % members_count).unwrap()
                }
            };
            num_members = members.clients.len();
            client.c_query(&self.gen_log_entry(sm_id, fn_id, data))
        };
        match res {
            Ok(Ok(res)) => {
                match res {
                    ClientQryResponse::LeftBehind => {
                        if depth >= num_members {
                            Err(ExecError::TooManyRetry)
                        } else {
                            self.query(sm_id, fn_id, data, depth + 1)
                        }
                    },
                    ClientQryResponse::Success{
                        data, last_log_term, last_log_id
                    } => {
                        swap_when_greater(&self.last_log_id, last_log_id);
                        swap_when_greater(&self.last_log_term, last_log_term);
                        Ok(data)
                    },
                }
            },
            _ => Err(ExecError::Unknown)
        }
    }

    fn command(&self, sm_id: u64, fn_id: u64, data: &Vec<u8>, depth: usize) -> Result<ExecResult, ExecError> {
        enum FailureAction {
            SwitchLeader,
            NotCommitted,
            UpdateInfo,
            NotLeader,
            Retry,
        }
        let failure = {
            if depth > 0 {
                let members = self.members.read();
                let num_members = members.clients.len();
                if depth >= max(num_members, 5) {
                    return Err(ExecError::TooManyRetry)
                };
            }
            match self.current_leader_client() {
                Some((leader_id, client)) => {
                    match client.c_command(&self.gen_log_entry(sm_id, fn_id, data)) {
                        Ok(Ok(ClientCmdResponse::Success {
                                  data, last_log_term, last_log_id
                              })) => {
                            swap_when_greater(&self.last_log_id, last_log_id);
                            swap_when_greater(&self.last_log_term, last_log_term);
                            return Ok(data);
                        },
                        Ok(Ok(ClientCmdResponse::NotLeader(leader_id))) => {
                            self.leader_id.store(leader_id, ORDERING);
                            FailureAction::NotLeader
                        },
                        Ok(Ok(ClientCmdResponse::NotCommitted)) => {
                            FailureAction::NotCommitted
                        },
                        Err(e) => {
                            println!("CLIENT: E1 - {} - {:?}", leader_id, e);
                            FailureAction::SwitchLeader // need switch server for leader
                        }
                        Ok(Err(e)) => {
                            println!("CLIENT: E2 - {} - {:?}", leader_id, e);
                            FailureAction::SwitchLeader // need switch server for leader
                        }
                    }
                },
                None => FailureAction::UpdateInfo // need update members
            }
        }; //
        match failure {
            FailureAction::SwitchLeader => {
                let members = self.members.read();
                let num_members = members.clients.len();
                let pos = self.qry_meta.pos.load(ORDERING);
                let leader_id = self.leader_id.load(ORDERING);
                let index = members.clients.keys()
                    .nth(pos as usize % num_members)
                    .unwrap();
                self.leader_id.compare_and_swap(leader_id, *index, ORDERING);
                println!("CLIENT: Switch leader");
            },
            _ => {}
        }
        self.command(sm_id, fn_id, data, depth + 1)
    }

    fn gen_log_entry(&self, sm_id: u64, fn_id: u64, data: &Vec<u8>) -> LogEntry {
        LogEntry {
            id: self.last_log_id.load(ORDERING),
            term: self.last_log_term.load(ORDERING),
            sm_id: sm_id,
            fn_id: fn_id,
            data: data.clone()
        }
    }
    pub fn leader_id(&self) -> u64 {self.leader_id.load(ORDERING)}
    pub fn leader_client(&self) -> Option<(u64, Client)> {
        let members = self.members.read();
        let leader_id = self.leader_id();
        if let Some(client) = members.clients.get(&leader_id) {
            Some((leader_id,  client.clone()))
        } else {
            None
        }
    }
    pub fn current_leader_client(&self) -> Option<(u64, Client)> {
        {
            let leader_client = self.leader_client();
            if leader_client.is_some() {
                return leader_client
            }
        }
        {
            let mut members = self.members.write();
            let mut members_addrs = HashSet::new();
            for address in members.id_map.values() {
                members_addrs.insert(address.clone());

            }
            self.update_info(&mut members, &members_addrs);
            let leader_id = self.leader_id.load(ORDERING);
            if let Some(client) = members.clients.get(&leader_id) {
                Some((leader_id,  client.clone()))
            } else {
                None
            }
        }
    }
    pub fn current_leader_rpc_client(&self) -> Option<Arc<rpc::RPCClient>> {
        match self.current_leader_client() {
            Some((_, client)) => Some(client.client.clone()),
            None => None
        }
    }
}

fn swap_when_greater(atomic: &AtomicU64, value: u64) {
    let mut orig_num = atomic.load(ORDERING);
    loop {
        if orig_num >= value {
            return;
        }
        let actual = atomic.compare_and_swap(orig_num, value, ORDERING);
        if actual == orig_num {
            return;
        } else {
            orig_num = actual;
        }
    }
}<|endoftext|>


<commit_before>use agent::Crowd;
use std::io::fs::File;
use std::io::{Write, Truncate};
mod agent;

// Type Observer
pub trait Observer {
    fn see(&self, time: f64, crowd: &Crowd);
    fn request_at(&self, time: f64, dt: f64) -> bool;
    // fn request_at(&self, time: f64, dt: f64) -> bool {
    //     let inter_size = self.tmax / (self.freq as f64);
    //     let int_num=(time/inter_size) as uint;
    //     (time-inter_size*(int_num as f64)).abs() < dt
    // }
}

//
// * Barre de progression
//
pub struct ProgressMeter {
    tmax: f64,
    freq: uint
}

impl ProgressMeter {
    pub fn new(tmax: f64, freq: uint) -> ProgressMeter {
        ProgressMeter {tmax: tmax, freq: freq}
    }
}

impl Observer for ProgressMeter {
    fn see(&self, time: f64, crowd: &Crowd) {
        println!("{:f} : {:u} cellules", time, crowd.size());
    }

    fn request_at(&self, time: f64, dt: f64) -> bool {
        let inter_size = self.tmax / (self.freq as f64);
        let int_num=(time/inter_size) as uint;
        (time-inter_size*(int_num as f64)).abs() < dt
    }
}
//
// * Affichage à l'écran
//
pub struct ScreenPrinter {
    tmax: f64,
    freq: uint
}

impl ScreenPrinter {
    pub fn new(tmax: f64, freq: uint) -> ScreenPrinter {
        ScreenPrinter { tmax: tmax, freq: freq}
    }
}

impl Observer for ScreenPrinter {
    fn see(&self, time: f64, crowd: &Crowd) {
        println!("{:f}\n", time);
        println(crowd.to_str());
    }

    fn request_at(&self, time: f64, dt: f64) -> bool {
        let inter_size = self.tmax / (self.freq as f64);
        let int_num=(time/inter_size) as uint;
        (time-inter_size*(int_num as f64)).abs() < dt
    }
}

//
// * Sortie sur le disque
//
pub struct DiskWriter {
    tmax: f64,
    freq: uint,
    fname: ~str,
}

impl DiskWriter {
    pub fn new(tmax: f64, freq:uint, name: ~str) -> DiskWriter {
        DiskWriter {tmax: tmax, freq: freq, fname: name}
    }

    pub fn new_filename(&self, time: f64) -> ~str {
        let inter_size = self.tmax / (self.freq as f64);
        let int_num=(time/inter_size) as uint;
        format!("./{:s}-{:03u}.vtk", self.fname, int_num)
    }
}

impl Observer for DiskWriter {
    fn see(&self, time: f64, crowd: &Crowd) {
        let outfile=self.new_filename(time);
        let file_path=Path::new(outfile);

        match File::open_mode(&file_path, Truncate , Write)  {
            None => fail!(),

            Some(ref mut f) => {
                let writer= f as &mut Writer;
                // Ecriture de l'entête
                write!(writer, "{:c} vtk DataFile Version 3.0\n", '#');
                write!(writer, "Rusty_Cells\n");
                write!(writer, "ASCII\n");
                write!(writer, "DATASET UNSTRUCTURED_GRID\n");
                write!(writer, "POINTS {:u} float\n", crowd.size());

                // Coordonnées des cellules
                for cell in crowd.cells.iter() {
                    write!(writer, "{:f} {:f} {:f}\n", cell.x(), cell.y(), cell.z() );
                }

                // Données sur les nœuds
                // * Age
                 write!(writer,"POINT_DATA {:u}\nSCALARS {:s} float\nLOOKUP_TABLE default\n", crowd.size(), "age");
                for cell in crowd.cells.iter() {
                    write!(writer,"{:f}\n", cell.age);
                }
                // * Vitesse
                write!(writer,"VECTORS {:s} float\n",  "vitesse");
                for cell in crowd.cells.iter() {
                    let speed=cell.velocity*1e3f64;
                    write!(writer,"{:f} {:f} {:f}\n", speed.x, speed.y, speed.z)
                }
            }
        }
    }

    fn request_at(&self, time: f64, dt: f64) -> bool {
        let inter_size = self.tmax / (self.freq as f64);
        let int_num=(time/inter_size) as uint;
        (time-inter_size*(int_num as f64)).abs() < dt
    }
}
<commit_msg>Replace println<commit_after>use agent::Crowd;
use std::io::fs::File;
use std::io::{Write, Truncate};
mod agent;

// Type Observer
pub trait Observer {
    fn see(&self, time: f64, crowd: &Crowd);
    fn request_at(&self, time: f64, dt: f64) -> bool;
    // fn request_at(&self, time: f64, dt: f64) -> bool {
    //     let inter_size = self.tmax / (self.freq as f64);
    //     let int_num=(time/inter_size) as uint;
    //     (time-inter_size*(int_num as f64)).abs() < dt
    // }
}

//
// * Barre de progression
//
pub struct ProgressMeter {
    tmax: f64,
    freq: uint
}

impl ProgressMeter {
    pub fn new(tmax: f64, freq: uint) -> ProgressMeter {
        ProgressMeter {tmax: tmax, freq: freq}
    }
}

impl Observer for ProgressMeter {
    fn see(&self, time: f64, crowd: &Crowd) {
        println!("{:f} : {:u} cellules", time, crowd.size());
    }

    fn request_at(&self, time: f64, dt: f64) -> bool {
        let inter_size = self.tmax / (self.freq as f64);
        let int_num=(time/inter_size) as uint;
        (time-inter_size*(int_num as f64)).abs() < dt
    }
}
//
// * Affichage à l'écran
//
pub struct ScreenPrinter {
    tmax: f64,
    freq: uint
}

impl ScreenPrinter {
    pub fn new(tmax: f64, freq: uint) -> ScreenPrinter {
        ScreenPrinter { tmax: tmax, freq: freq}
    }
}

impl Observer for ScreenPrinter {
    fn see(&self, time: f64, crowd: &Crowd) {
        println!("{:f}\n", time);
        println!("{:s}",crowd.to_str());
    }

    fn request_at(&self, time: f64, dt: f64) -> bool {
        let inter_size = self.tmax / (self.freq as f64);
        let int_num=(time/inter_size) as uint;
        (time-inter_size*(int_num as f64)).abs() < dt
    }
}

//
// * Sortie sur le disque
//
pub struct DiskWriter {
    tmax: f64,
    freq: uint,
    fname: ~str,
}

impl DiskWriter {
    pub fn new(tmax: f64, freq:uint, name: ~str) -> DiskWriter {
        DiskWriter {tmax: tmax, freq: freq, fname: name}
    }

    pub fn new_filename(&self, time: f64) -> ~str {
        let inter_size = self.tmax / (self.freq as f64);
        let int_num=(time/inter_size) as uint;
        format!("./{:s}-{:03u}.vtk", self.fname, int_num)
    }
}

impl Observer for DiskWriter {
    fn see(&self, time: f64, crowd: &Crowd) {
        let outfile=self.new_filename(time);
        let file_path=Path::new(outfile);

        match File::open_mode(&file_path, Truncate , Write)  {
            None => fail!(),

            Some(ref mut f) => {
                let writer= f as &mut Writer;
                // Ecriture de l'entête
                write!(writer, "{:c} vtk DataFile Version 3.0\n", '#');
                write!(writer, "Rusty_Cells\n");
                write!(writer, "ASCII\n");
                write!(writer, "DATASET UNSTRUCTURED_GRID\n");
                write!(writer, "POINTS {:u} float\n", crowd.size());

                // Coordonnées des cellules
                for cell in crowd.cells.iter() {
                    write!(writer, "{:f} {:f} {:f}\n", cell.x(), cell.y(), cell.z() );
                }

                // Données sur les nœuds
                // * Age
                 write!(writer,"POINT_DATA {:u}\nSCALARS {:s} float\nLOOKUP_TABLE default\n", crowd.size(), "age");
                for cell in crowd.cells.iter() {
                    write!(writer,"{:f}\n", cell.age);
                }
                // * Vitesse
                write!(writer,"VECTORS {:s} float\n",  "vitesse");
                for cell in crowd.cells.iter() {
                    let speed=cell.velocity*1e3f64;
                    write!(writer,"{:f} {:f} {:f}\n", speed.x, speed.y, speed.z)
                }
            }
        }
    }

    fn request_at(&self, time: f64, dt: f64) -> bool {
        let inter_size = self.tmax / (self.freq as f64);
        let int_num=(time/inter_size) as uint;
        (time-inter_size*(int_num as f64)).abs() < dt
    }
}
<|endoftext|>


<commit_before>//! [Standard terms](https://en.wikipedia.org/wiki/Lambda_calculus#Standard_terms) and
//! [combinators](https://en.wikipedia.org/wiki/Combinatory_logic#Combinatory_calculi)
//!
//! * [SKI](https://en.wikipedia.org/wiki/SKI_combinator_calculus)
//! * [Iota](https://en.wikipedia.org/wiki/Iota_and_Jot)
//! * [BCKW](https://en.wikipedia.org/wiki/B,_C,_K,_W_system)
// //! * the recursion combinator U - needs more research
//! * the looping combinator ω
//! * the divergent combinator Ω
//! * [the fixed-point combinator Y](https://en.wikipedia.org/wiki/Fixed-point_combinator)
//! * the thrush (reverse application) combinator T

use term::*;
use term::Term::*;
use reduction::{EVALUATION_ORDER, Order};

/// I - the identity combinator.
///
/// I := λx.x = λ 1
///
/// # Example
/// ```
/// use lambda_calculus::combinators::i;
/// use lambda_calculus::arithmetic::zero;
/// use lambda_calculus::reduction::beta_full;
///
/// assert_eq!(beta_full(i().app(zero())), zero());
/// ```
pub fn i() -> Term { abs(Var(1)) }

/// K - the constant / discarding combinator.
///
/// K := λxy.x = λ λ 2 = TRUE
///
/// # Example
/// ```
/// use lambda_calculus::combinators::k;
/// use lambda_calculus::arithmetic::{zero, one};
/// use lambda_calculus::reduction::beta_full;
///
/// assert_eq!(beta_full(k().app(zero()).app(one())), zero());
/// ```
pub fn k() -> Term { abs(abs(Var(2))) }

/// S - the substitution combinator.
///
/// S := λxyz.x z (y z) = λ λ λ 3 1 (2 1)
///
/// # Example
/// ```
/// use lambda_calculus::term::Term;
/// use lambda_calculus::combinators::s;
/// use lambda_calculus::reduction::beta_full;
///
/// assert_eq!(beta_full(s().app(0.into()).app(1.into()).app(2.into())),
///            beta_full(Term::from(0).app(2.into()).app(Term::from(1).app(2.into()))));
/// ```
pub fn s() -> Term {
    abs(abs(abs(
        Var(3)
        .app(Var(1))
        .app(Var(2).app(Var(1)))
    )))
}

/// Iota - the universal combinator.
///
/// ι := λx.x S K = λ 1 S K
///
/// # Example
/// ```
/// use lambda_calculus::combinators::{iota, i, k, s};
/// use lambda_calculus::reduction::beta_full;
///
/// assert_eq!(beta_full(iota().app(iota())), i());
/// assert_eq!(beta_full(iota().app(iota().app(iota().app(iota())))), k());
/// assert_eq!(beta_full(iota().app(iota().app(iota().app(iota().app(iota()))))), s());
/// ```
pub fn iota() -> Term { abs(Var(1).app(s()).app(k())) }

/// B - the composition combinator.
///
/// B := λxyz.x (y z) = λ λ λ 3 (2 1)
///
/// # Example
/// ```
/// use lambda_calculus::term::Term;
/// use lambda_calculus::combinators::b;
/// use lambda_calculus::reduction::beta_full;
///
/// assert_eq!(beta_full(b().app(0.into()).app(1.into()).app(2.into())),
///            beta_full(Term::from(0).app(Term::from(1).app(2.into()))));
/// ```
pub fn b() -> Term {
    abs(abs(abs(
        Var(3)
        .app(Var(2).app(Var(1)))
    )))
}

/// C - the swapping combinator.
///
/// C := λxyz.x z y = λ λ λ 3 1 2
///
/// # Example
/// ```
/// use lambda_calculus::term::Term;
/// use lambda_calculus::combinators::c;
/// use lambda_calculus::reduction::beta_full;
///
/// assert_eq!(beta_full(c().app(0.into()).app(1.into()).app(2.into())),
///            beta_full(Term::from(0).app(2.into()).app(1.into())));
/// ```
pub fn c() -> Term {
    abs(abs(abs(
        Var(3)
        .app(Var(1))
        .app(Var(2))
    )))
}

/// W - the duplicating combinator.
///
/// W := λxy.x y y = λ λ 2 1 1
///
/// # Example
/// ```
/// use lambda_calculus::combinators::w;
/// use lambda_calculus::arithmetic::{zero, one};
/// use lambda_calculus::reduction::beta_full;
///
/// assert_eq!(beta_full(w().app(zero()).app(one())),
///            beta_full(zero().app(one()).app(one())));
/// ```
pub fn w() -> Term {
    abs(abs(
        Var(2)
        .app(Var(1))
        .app(Var(1))
    ))
}
/*
/// U - the recursion combinator.
///
/// U := λxy.y (x x y) = λ λ 1 (2 2 1)
pub fn u() -> Term { abs(abs(Var(1).app(Var(2).app(Var(2)).app(Var(1))))) }
*/
/// ω - the looping combinator.
///
/// ω := λx.x x = λ 1 1
///
/// # Example
/// ```
/// use lambda_calculus::combinators::om;
/// use lambda_calculus::arithmetic::zero;
/// use lambda_calculus::reduction::beta_full;
///
/// assert_eq!(beta_full(om().app(zero())), beta_full(zero().app(zero())));
/// ```
pub fn om() -> Term { abs(Var(1).app(Var(1))) }

/// Ω - the divergent combinator.
///
/// Ω := ω ω
///
/// # Example
/// ```
/// use lambda_calculus::combinators::omm;
///
/// let mut doesnt_reduce = omm();
///
/// doesnt_reduce.beta_once();
///
/// assert_eq!(doesnt_reduce, omm());
/// ```
pub fn omm() -> Term { om().app(om()) }

/// Y - the fixed-point combinator.
///
/// Y := λf.(λx.f (x x)) (λx.f (x x)) = λ (λ 2 (1 1)) (λ 2 (1 1))
///
/// # Example
/// ```
/// use lambda_calculus::combinators::y;
/// use lambda_calculus::arithmetic::zero;
/// use lambda_calculus::reduction::beta_full;
///
/// assert_eq!(beta_full(y().app(zero())), beta_full(zero().app(y().app(zero()))));
/// ```
pub fn y() -> Term {
    match EVALUATION_ORDER {
        Order::Normal => {
            abs(app(
                abs(Var(2).app(Var(1).app(Var(1)))),
                abs(Var(2).app(Var(1).app(Var(1))))
            ))
        }
        Order::ApplicativeLeft | Order::ApplicativeRight => { /* should any variant work? */
            panic!("Y combinator works only with normal evaluation order")
            //parse(&"(λλ212)(λλ2(121))").unwrap()
            //parse(&"(λλ(1(λ3321)))(λλ(1(λ3321)))").unwrap()
            //parse(&"(λ11)(λ(λ(3(22)1)))").unwrap()
            //parse(&"λ(λ(2(λ(221))))(λ(2(λ(221))))").unwrap()
            //parse(&"λ(λ11)(λ(2(λ((22)1))))").unwrap()
        }
    }
}

/// T - the thrush combinator
///
/// T := λxf. f x = λ λ 1 2
///
/// # Example
/// ```
/// use lambda_calculus::combinators::t;
/// use lambda_calculus::arithmetic::{zero, one};
///
/// assert_eq!(t().apply(&zero()).and_then(|t| t.apply(&one())), Ok(one().app(zero())));
/// ```
pub fn t() -> Term {
    abs(abs(
        app(Var(1), Var(2))
    ))
}
<commit_msg>rewrite combinators with app!()<commit_after>//! [Standard terms](https://en.wikipedia.org/wiki/Lambda_calculus#Standard_terms) and
//! [combinators](https://en.wikipedia.org/wiki/Combinatory_logic#Combinatory_calculi)
//!
//! * [SKI](https://en.wikipedia.org/wiki/SKI_combinator_calculus)
//! * [Iota](https://en.wikipedia.org/wiki/Iota_and_Jot)
//! * [BCKW](https://en.wikipedia.org/wiki/B,_C,_K,_W_system)
// //! * the recursion combinator U - needs more research
//! * the looping combinator ω
//! * the divergent combinator Ω
//! * [the fixed-point combinator Y](https://en.wikipedia.org/wiki/Fixed-point_combinator)
//! * the thrush (reverse application) combinator T

use term::*;
use term::Term::*;
use reduction::{EVALUATION_ORDER, Order};

/// I - the identity combinator.
///
/// I := λx.x = λ 1
///
/// # Example
/// ```
/// use lambda_calculus::combinators::i;
/// use lambda_calculus::arithmetic::zero;
/// use lambda_calculus::reduction::beta_full;
///
/// assert_eq!(beta_full(i().app(zero())), zero());
/// ```
pub fn i() -> Term { abs(Var(1)) }

/// K - the constant / discarding combinator.
///
/// K := λxy.x = λ λ 2 = TRUE
///
/// # Example
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::combinators::k;
/// use lambda_calculus::arithmetic::{zero, one};
/// use lambda_calculus::reduction::beta_full;
///
/// assert_eq!(beta_full(app!(k(), zero(), one())), zero());
/// # }
/// ```
pub fn k() -> Term { abs(abs(Var(2))) }

/// S - the substitution combinator.
///
/// S := λxyz.x z (y z) = λ λ λ 3 1 (2 1)
///
/// # Example
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::term::Term;
/// use lambda_calculus::combinators::s;
/// use lambda_calculus::reduction::beta_full;
///
/// assert_eq!(beta_full(app!(s(), 0.into(), 1.into(), 2.into())),
///            beta_full(app!(Term::from(0), 2.into(), app!(Term::from(1), 2.into()))));
/// # }
/// ```
pub fn s() -> Term {
    abs(abs(abs(
        Var(3)
        .app(Var(1))
        .app(Var(2).app(Var(1)))
    )))
}

/// Iota - the universal combinator.
///
/// ι := λx.x S K = λ 1 S K
///
/// # Example
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::combinators::{iota, i, k, s};
/// use lambda_calculus::reduction::beta_full;
///
/// assert_eq!(beta_full(app!(iota(), iota())), i());
/// assert_eq!(beta_full(app!(iota(), app!(iota(), app!(iota(), iota())))), k());
/// assert_eq!(beta_full(app!(iota(), app!(iota(), app!(iota(), app!(iota(), iota()))))), s());
/// # }
/// ```
pub fn iota() -> Term { abs(app!(Var(1), s(), k())) }

/// B - the composition combinator.
///
/// B := λxyz.x (y z) = λ λ λ 3 (2 1)
///
/// # Example
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::term::Term;
/// use lambda_calculus::combinators::b;
/// use lambda_calculus::reduction::beta_full;
///
/// assert_eq!(beta_full(app!(b(), 0.into(), 1.into(), 2.into())),
///            beta_full(app!(Term::from(0), app!(Term::from(1), 2.into()))));
/// # }
/// ```
pub fn b() -> Term {
    abs(abs(abs(
        app!(Var(3), app!(Var(2), Var(1)))
    )))
}

/// C - the swapping combinator.
///
/// C := λxyz.x z y = λ λ λ 3 1 2
///
/// # Example
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::term::Term;
/// use lambda_calculus::combinators::c;
/// use lambda_calculus::reduction::beta_full;
///
/// assert_eq!(beta_full(app!(c(), 0.into(), 1.into(), 2.into())),
///            beta_full(app!(Term::from(0), 2.into(), 1.into())));
/// # }
/// ```
pub fn c() -> Term {
    abs(abs(abs(
        app!(Var(3), Var(1), Var(2))
    )))
}

/// W - the duplicating combinator.
///
/// W := λxy.x y y = λ λ 2 1 1
///
/// # Example
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::combinators::w;
/// use lambda_calculus::arithmetic::{zero, one};
/// use lambda_calculus::reduction::beta_full;
///
/// assert_eq!(beta_full(app!(w(), zero(), one())),
///            beta_full(app!(zero(), one(), one())));
/// # }
/// ```
pub fn w() -> Term {
    abs(abs(
        app!(Var(2), Var(1), Var(1))
    ))
}
/*
/// U - the recursion combinator.
///
/// U := λxy.y (x x y) = λ λ 1 (2 2 1)
pub fn u() -> Term { abs(abs(Var(1).app(Var(2).app(Var(2)).app(Var(1))))) }
*/
/// ω - the looping combinator.
///
/// ω := λx.x x = λ 1 1
///
/// # Example
/// ```
/// use lambda_calculus::combinators::om;
/// use lambda_calculus::arithmetic::zero;
/// use lambda_calculus::reduction::beta_full;
///
/// assert_eq!(beta_full(om().app(zero())), beta_full(zero().app(zero())));
/// ```
pub fn om() -> Term { abs(Var(1).app(Var(1))) }

/// Ω - the divergent combinator.
///
/// Ω := ω ω
///
/// # Example
/// ```
/// use lambda_calculus::combinators::omm;
///
/// let mut doesnt_reduce = omm();
///
/// doesnt_reduce.beta_once();
///
/// assert_eq!(doesnt_reduce, omm());
/// ```
pub fn omm() -> Term { om().app(om()) }

/// Y - the fixed-point combinator.
///
/// Y := λf.(λx.f (x x)) (λx.f (x x)) = λ (λ 2 (1 1)) (λ 2 (1 1))
///
/// # Example
/// ```
/// use lambda_calculus::combinators::y;
/// use lambda_calculus::arithmetic::zero;
/// use lambda_calculus::reduction::beta_full;
///
/// assert_eq!(beta_full(y().app(zero())), beta_full(zero().app(y().app(zero()))));
/// ```
pub fn y() -> Term {
    match EVALUATION_ORDER {
        Order::Normal => {
            abs(app(
                abs(app!(Var(2), app!(Var(1), Var(1)))),
                abs(app!(Var(2), app!(Var(1), Var(1))))
            ))
        }
        Order::ApplicativeLeft | Order::ApplicativeRight => { /* should any variant work? */
            panic!("Y combinator works only with normal evaluation order")
            //parse(&"(λλ212)(λλ2(121))").unwrap()
            //parse(&"(λλ(1(λ3321)))(λλ(1(λ3321)))").unwrap()
            //parse(&"(λ11)(λ(λ(3(22)1)))").unwrap()
            //parse(&"λ(λ(2(λ(221))))(λ(2(λ(221))))").unwrap()
            //parse(&"λ(λ11)(λ(2(λ((22)1))))").unwrap()
        }
    }
}

/// T - the thrush combinator
///
/// T := λxf. f x = λ λ 1 2
///
/// # Example
/// ```
/// use lambda_calculus::combinators::t;
/// use lambda_calculus::arithmetic::{zero, one};
///
/// assert_eq!(t().apply(&zero()).and_then(|t| t.apply(&one())), Ok(one().app(zero())));
/// ```
pub fn t() -> Term {
    abs(abs(
        app(Var(1), Var(2))
    ))
}
<|endoftext|>


<commit_before>// Copyright 2015-2016 Intecture Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

//! FFI interface for Command

use ffi_helpers::Leaky;
use host::Host;
use libc::{c_char, uint8_t};
use std::convert;
use std::ffi::CString;
use std::panic::catch_unwind;
use super::{Command, CommandResult};

#[repr(C)]
pub struct Ffi__CommandResult {
    pub exit_code: i32,
    pub stdout: *mut c_char,
    pub stderr: *mut c_char,
}

impl convert::From<CommandResult> for Ffi__CommandResult {
    fn from(result: CommandResult) -> Ffi__CommandResult {
        Ffi__CommandResult {
            exit_code: result.exit_code,
            stdout: CString::new(result.stdout).unwrap().into_raw(),
            stderr: CString::new(result.stderr).unwrap().into_raw(),
        }
    }
}

#[no_mangle]
pub extern "C" fn command_new(cmd_ptr: *const c_char) -> *mut Command {
    let cmd_string = trynull!(ptrtostr!(cmd_ptr, "command string"));
    Box::into_raw(Box::new(Command::new(cmd_string)))
}

#[no_mangle]
pub extern "C" fn command_exec(cmd_ptr: *mut Command, host_ptr: *mut Host) -> *mut Ffi__CommandResult {
    let cmd = Leaky::new(trynull!(readptr!(cmd_ptr, "Command pointer")));
    let mut host = Leaky::new(trynull!(readptr!(host_ptr, "Host pointer")));

    let result = trynull!(cmd.exec(&mut host));
    let ffi_result: Ffi__CommandResult = trynull!(catch_unwind(|| result.into()));

    Box::into_raw(Box::new(ffi_result))
}

#[no_mangle]
pub extern "C" fn command_free(cmd_ptr: *mut Command) -> uint8_t {
    tryrc!(boxptr!(cmd_ptr, "Command pointer"));
    0
}

#[no_mangle]
pub extern "C" fn command_result_free(result_ptr: *mut Ffi__CommandResult) -> uint8_t {
    let result = tryrc!(boxptr!(result_ptr, "CommandResult pointer"));
    if !result.stdout.is_null() {
        unsafe { CString::from_raw(result.stdout) };
    }
    if !result.stderr.is_null() {
        unsafe { CString::from_raw(result.stderr) };
    }
    0
}

#[cfg(test)]
mod tests {
    use command::CommandResult;
    #[cfg(feature = "remote-run")]
    use czmq::{ZMsg, ZSys};
    use error::ERRMSG;
    #[cfg(feature = "remote-run")]
    use host::ffi::host_close;
    use host::Host;
    #[cfg(feature = "remote-run")]
    use std::{str, thread};
    use std::ffi::CStr;
    use std::ffi::CString;
    use std::ptr;
    use super::*;

    #[test]
    fn test_convert_command_result() {
        let result = CommandResult {
            exit_code: 0,
            stdout: "moo".to_string(),
            stderr: "cow".to_string(),
        };
        Ffi__CommandResult::from(result);
    }

    #[test]
    fn test_new() {
        let cmd = readptr!(command_new(CString::new("moo").unwrap().into_raw()), "Command pointer").unwrap();
        assert_eq!(cmd.cmd, "moo");

        assert!(command_new(ptr::null()).is_null());
        assert_eq!(unsafe { CStr::from_ptr(ERRMSG).to_str().unwrap() }, "Received null when we expected a command string pointer");
    }

    #[cfg(feature = "local-run")]
    #[test]
    fn test_exec() {
        let path: Option<String> = None;
        let mut host = Box::into_raw(Box::new(Host::local(path).unwrap()));

        let whoami = CString::new("whoami").unwrap();
        let cmd = command_new(whoami.into_raw());
        assert!(!cmd.is_null());

        let result = readptr!(command_exec(cmd, host), "CommandResult pointer").unwrap();
        assert_eq!(result.exit_code, 0);

        assert_eq!(command_free(cmd), 0);
        unsafe { Box::from_raw(host) };
    }

    #[cfg(feature = "remote-run")]
    #[test]
    fn test_exec() {
        ZSys::init();

        let (client, mut server) = ZSys::create_pipe().unwrap();
        client.set_sndtimeo(Some(500));
        server.set_rcvtimeo(Some(500));

        let agent_mock = thread::spawn(move || {
            let req = ZMsg::recv(&mut server).unwrap();
            assert_eq!("command::exec", req.popstr().unwrap().unwrap());
            assert_eq!("moo", req.popstr().unwrap().unwrap());

            let rep = ZMsg::new();
            rep.addstr("Ok").unwrap();
            rep.addstr("0").unwrap();
            rep.addstr("cow").unwrap();
            rep.addstr("err").unwrap();
            rep.send(&mut server).unwrap();
        });

        let host = Box::into_raw(Box::new(Host::test_new(None, Some(client), None, None)));
        let whoami = CString::new("moo").unwrap();
        let command = command_new(whoami.into_raw());

        let result = readptr!(command_exec(command, host), "CommandResult pointer").unwrap();
        assert_eq!(result.exit_code, 0);
        assert_eq!(ptrtostr!(result.stdout, "stdout").unwrap(), "cow");
        assert_eq!(ptrtostr!(result.stderr, "stderr").unwrap(), "err");

        assert_eq!(command_free(command), 0);
        assert_eq!(host_close(host), 0);
        agent_mock.join().unwrap();
    }
}
<commit_msg>Fix warning in local-run test<commit_after>// Copyright 2015-2016 Intecture Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

//! FFI interface for Command

use ffi_helpers::Leaky;
use host::Host;
use libc::{c_char, uint8_t};
use std::convert;
use std::ffi::CString;
use std::panic::catch_unwind;
use super::{Command, CommandResult};

#[repr(C)]
pub struct Ffi__CommandResult {
    pub exit_code: i32,
    pub stdout: *mut c_char,
    pub stderr: *mut c_char,
}

impl convert::From<CommandResult> for Ffi__CommandResult {
    fn from(result: CommandResult) -> Ffi__CommandResult {
        Ffi__CommandResult {
            exit_code: result.exit_code,
            stdout: CString::new(result.stdout).unwrap().into_raw(),
            stderr: CString::new(result.stderr).unwrap().into_raw(),
        }
    }
}

#[no_mangle]
pub extern "C" fn command_new(cmd_ptr: *const c_char) -> *mut Command {
    let cmd_string = trynull!(ptrtostr!(cmd_ptr, "command string"));
    Box::into_raw(Box::new(Command::new(cmd_string)))
}

#[no_mangle]
pub extern "C" fn command_exec(cmd_ptr: *mut Command, host_ptr: *mut Host) -> *mut Ffi__CommandResult {
    let cmd = Leaky::new(trynull!(readptr!(cmd_ptr, "Command pointer")));
    let mut host = Leaky::new(trynull!(readptr!(host_ptr, "Host pointer")));

    let result = trynull!(cmd.exec(&mut host));
    let ffi_result: Ffi__CommandResult = trynull!(catch_unwind(|| result.into()));

    Box::into_raw(Box::new(ffi_result))
}

#[no_mangle]
pub extern "C" fn command_free(cmd_ptr: *mut Command) -> uint8_t {
    tryrc!(boxptr!(cmd_ptr, "Command pointer"));
    0
}

#[no_mangle]
pub extern "C" fn command_result_free(result_ptr: *mut Ffi__CommandResult) -> uint8_t {
    let result = tryrc!(boxptr!(result_ptr, "CommandResult pointer"));
    if !result.stdout.is_null() {
        unsafe { CString::from_raw(result.stdout) };
    }
    if !result.stderr.is_null() {
        unsafe { CString::from_raw(result.stderr) };
    }
    0
}

#[cfg(test)]
mod tests {
    use command::CommandResult;
    #[cfg(feature = "remote-run")]
    use czmq::{ZMsg, ZSys};
    use error::ERRMSG;
    #[cfg(feature = "remote-run")]
    use host::ffi::host_close;
    use host::Host;
    #[cfg(feature = "remote-run")]
    use std::{str, thread};
    use std::ffi::CStr;
    use std::ffi::CString;
    use std::ptr;
    use super::*;

    #[test]
    fn test_convert_command_result() {
        let result = CommandResult {
            exit_code: 0,
            stdout: "moo".to_string(),
            stderr: "cow".to_string(),
        };
        Ffi__CommandResult::from(result);
    }

    #[test]
    fn test_new() {
        let cmd = readptr!(command_new(CString::new("moo").unwrap().into_raw()), "Command pointer").unwrap();
        assert_eq!(cmd.cmd, "moo");

        assert!(command_new(ptr::null()).is_null());
        assert_eq!(unsafe { CStr::from_ptr(ERRMSG).to_str().unwrap() }, "Received null when we expected a command string pointer");
    }

    #[cfg(feature = "local-run")]
    #[test]
    fn test_exec() {
        let path: Option<String> = None;
        let host = Box::into_raw(Box::new(Host::local(path).unwrap()));

        let whoami = CString::new("whoami").unwrap();
        let cmd = command_new(whoami.into_raw());
        assert!(!cmd.is_null());

        let result = readptr!(command_exec(cmd, host), "CommandResult pointer").unwrap();
        assert_eq!(result.exit_code, 0);

        assert_eq!(command_free(cmd), 0);
        unsafe { Box::from_raw(host) };
    }

    #[cfg(feature = "remote-run")]
    #[test]
    fn test_exec() {
        ZSys::init();

        let (client, mut server) = ZSys::create_pipe().unwrap();
        client.set_sndtimeo(Some(500));
        server.set_rcvtimeo(Some(500));

        let agent_mock = thread::spawn(move || {
            let req = ZMsg::recv(&mut server).unwrap();
            assert_eq!("command::exec", req.popstr().unwrap().unwrap());
            assert_eq!("moo", req.popstr().unwrap().unwrap());

            let rep = ZMsg::new();
            rep.addstr("Ok").unwrap();
            rep.addstr("0").unwrap();
            rep.addstr("cow").unwrap();
            rep.addstr("err").unwrap();
            rep.send(&mut server).unwrap();
        });

        let host = Box::into_raw(Box::new(Host::test_new(None, Some(client), None, None)));
        let whoami = CString::new("moo").unwrap();
        let command = command_new(whoami.into_raw());

        let result = readptr!(command_exec(command, host), "CommandResult pointer").unwrap();
        assert_eq!(result.exit_code, 0);
        assert_eq!(ptrtostr!(result.stdout, "stdout").unwrap(), "cow");
        assert_eq!(ptrtostr!(result.stderr, "stderr").unwrap(), "err");

        assert_eq!(command_free(command), 0);
        assert_eq!(host_close(host), 0);
        agent_mock.join().unwrap();
    }
}
<|endoftext|>