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

use super::{Drawable, BoundingBox, MeasureMode};
use ::elements::{Element};
use ::paint::{Canvas, Point, Rect};
use ::platform::Context;
use ::props::{Color};

pub type StrokeWidthReader<T> = fn(&T) -> f32;
pub type ColorReader<T> = fn(&T) -> &Color;

pub enum LineParam {
    Fixed { start: Point, end: Point },
    Vertical { x: f32 },
    Horizontal { y: f32 },
}

pub struct Line<'a, T: Element + 'a> {
    param: LineParam,

    element: &'a T,
    stroke_width_reader: StrokeWidthReader<T>,
    color_reader: ColorReader<T>,

    bounding_box: BoundingBox,
    start: Point,
    end: Point,
}

impl<'a, T: Element + 'a> Drawable for Line<'a, T> {
    fn draw(&self, canvas: &Canvas, pen_pos: &Point) {
        canvas.draw_line(
            &self.start,
            &self.end,
            (self.color_reader)(self.element),
            (self.stroke_width_reader)(self.element),
        )
    }

    fn calculate(&mut self, context: &Context, width_mode: &MeasureMode, height_mode: &MeasureMode) {
        let (start, end) = match self.param {
            LineParam::Fixed { ref start, ref end } => {
                (start.clone(), end.clone())
            },
            LineParam::Vertical { x } => {
                (
                    Point::new(x, 0.),
                    Point::new(x, if let MeasureMode::UpTo(height) = *height_mode { height } else { 0. })
                )
            },
            LineParam::Horizontal { y } => {
                (
                    Point::new(0., y),
                    Point::new(if let MeasureMode::UpTo(width) = *width_mode { width } else { 0. }, y)
                )
            }
        };

        let stroke_width = (self.stroke_width_reader)(self.element);

        let slope = (start.y()-end.y()).abs() / (start.x()-end.x()).abs();
        let angle = slope.atan();
        let x_diff = angle.sin()*stroke_width/2.;
        let y_diff = angle.cos()*stroke_width/2.;

        let width = (start.x()-end.x()).abs()+(x_diff*2.);
        let height = (start.y()-end.y()).abs()+(y_diff*2.);

        self.start = &start+&Point::new(x_diff, y_diff);
        self.end = &end+&Point::new(x_diff, y_diff);

        self.bounding_box = BoundingBox {
            rect: Rect::new(width, height),
            baseline: height/2.,
            axis: height/2.,
        }
    }

    fn bounding_box(&self) -> &BoundingBox {
        &self.bounding_box
    }
}

impl<'a, T: Element + 'a> Line<'a, T> {
    pub fn new(param: LineParam, element: &'a T, stroke_width_reader: StrokeWidthReader<T>,
               color_reader: ColorReader<T>) -> Line<'a, T> {
        Line {
            param,
            element,
            stroke_width_reader,
            color_reader,
            bounding_box: BoundingBox::default(),
            start: Point::new(0., 0.),
            end: Point::new(0., 0.),
        }
    }
}

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

    struct MockElement;
    impl Element for MockElement {
        fn layout<'a>(&'a self, context: &Context) -> Box<Drawable + 'a> {
            unimplemented!()
        }
    }

    #[test]
    fn it_work_vertically() {
        let context = test_context();
        let element = MockElement {};
        let mut line = Line::new(
            LineParam::Vertical { x: 10. },
            &element,
            |_| 5.,
            |_| &Color::RGB(0, 0, 0)
        );

        line.calculate(&context, &MeasureMode::Wrap, &MeasureMode::UpTo(100.));

        assert_eq!(line.bounding_box().width(), 5.);
        assert_eq!(line.bounding_box().height(), 100.);
    }

    #[test]
    fn it_work_horizontally() {
        let context = test_context();
        let element = MockElement {};
        let mut line = Line::new(
            LineParam::Horizontal { y: 10. },
            &element,
            |_| 5.,
            |_| &Color::RGB(0, 0, 0)
        );

        line.calculate(&context, &MeasureMode::UpTo(100.), &MeasureMode::Wrap);

        assert_eq!(line.bounding_box().width(), 100.);
        assert_eq!(line.bounding_box().height(), 5.);
    }

    #[test]
    fn it_works_when_inclined() {
        let context = test_context();
        let element = MockElement {};
        let mut line = Line::new(
            LineParam::Fixed { start: Point::new(0., 0.), end: Point::new(10., 10.) },
            &element,
            |_| 5.,
            |_| &Color::RGB(0, 0, 0)
        );

        line.calculate(&context, &MeasureMode::Wrap, &MeasureMode::Wrap);

        assert_eq!(line.bounding_box().width(), 13.535534);
        assert_eq!(line.bounding_box().height(), 13.535534);
    }
}<commit_msg>Fix NAN<commit_after>/*
 * Copyright 2017 Sreejith Krishnan R
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

use super::{Drawable, BoundingBox, MeasureMode};
use ::elements::{Element};
use ::paint::{Canvas, Point, Rect};
use ::platform::Context;
use ::props::{Color};

pub type StrokeWidthReader<T> = fn(&T) -> f32;
pub type ColorReader<T> = fn(&T) -> &Color;

pub enum LineParam {
    Fixed { start: Point, end: Point },
    Vertical { x: f32 },
    Horizontal { y: f32 },
}

pub struct Line<'a, T: Element + 'a> {
    param: LineParam,

    element: &'a T,
    stroke_width_reader: StrokeWidthReader<T>,
    color_reader: ColorReader<T>,

    bounding_box: BoundingBox,
    start: Point,
    end: Point,
}

impl<'a, T: Element + 'a> Drawable for Line<'a, T> {
    fn draw(&self, canvas: &Canvas, pen_pos: &Point) {
        canvas.draw_line(
            &self.start,
            &self.end,
            (self.color_reader)(self.element),
            (self.stroke_width_reader)(self.element),
        )
    }

    fn calculate(&mut self, context: &Context, width_mode: &MeasureMode, height_mode: &MeasureMode) {
        let (start, end) = match self.param {
            LineParam::Fixed { ref start, ref end } => {
                (start.clone(), end.clone())
            },
            LineParam::Vertical { x } => {
                (
                    Point::new(x, 0.),
                    Point::new(x, if let MeasureMode::UpTo(height) = *height_mode { height } else { 0. })
                )
            },
            LineParam::Horizontal { y } => {
                (
                    Point::new(0., y),
                    Point::new(if let MeasureMode::UpTo(width) = *width_mode { width } else { 0. }, y)
                )
            }
        };

        let stroke_width = (self.stroke_width_reader)(self.element);

        let slope = (start.y()-end.y()).abs() / (start.x()-end.x()).abs();
        let angle = slope.atan();
        let x_diff = angle.sin()*stroke_width/2.;
        let y_diff = angle.cos()*stroke_width/2.;

        let mut width = (start.x()-end.x()).abs()+(x_diff*2.);
        let mut height = (start.y()-end.y()).abs()+(y_diff*2.);

        if width.is_nan() {
            width = 0.;
        }

        if height.is_nan() {
            height = 0.;
        }

        self.start = &start+&Point::new(x_diff, y_diff);
        self.end = &end+&Point::new(x_diff, y_diff);

        self.bounding_box = BoundingBox {
            rect: Rect::new(width, height),
            baseline: height/2.,
            axis: height/2.,
        }
    }

    fn bounding_box(&self) -> &BoundingBox {
        &self.bounding_box
    }
}

impl<'a, T: Element + 'a> Line<'a, T> {
    pub fn new(param: LineParam, element: &'a T, stroke_width_reader: StrokeWidthReader<T>,
               color_reader: ColorReader<T>) -> Line<'a, T> {
        Line {
            param,
            element,
            stroke_width_reader,
            color_reader,
            bounding_box: BoundingBox::default(),
            start: Point::new(0., 0.),
            end: Point::new(0., 0.),
        }
    }
}

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

    struct MockElement;
    impl Element for MockElement {
        fn layout<'a>(&'a self, context: &Context) -> Box<Drawable + 'a> {
            unimplemented!()
        }
    }

    #[test]
    fn it_work_vertically() {
        let context = test_context();
        let element = MockElement {};
        let mut line = Line::new(
            LineParam::Vertical { x: 10. },
            &element,
            |_| 5.,
            |_| &Color::RGB(0, 0, 0)
        );

        line.calculate(&context, &MeasureMode::Wrap, &MeasureMode::UpTo(100.));

        assert_eq!(line.bounding_box().width(), 5.);
        assert_eq!(line.bounding_box().height(), 100.);
    }

    #[test]
    fn it_work_horizontally() {
        let context = test_context();
        let element = MockElement {};
        let mut line = Line::new(
            LineParam::Horizontal { y: 10. },
            &element,
            |_| 5.,
            |_| &Color::RGB(0, 0, 0)
        );

        line.calculate(&context, &MeasureMode::UpTo(100.), &MeasureMode::Wrap);

        assert_eq!(line.bounding_box().width(), 100.);
        assert_eq!(line.bounding_box().height(), 5.);
    }

    #[test]
    fn it_works_when_inclined() {
        let context = test_context();
        let element = MockElement {};
        let mut line = Line::new(
            LineParam::Fixed { start: Point::new(0., 0.), end: Point::new(10., 10.) },
            &element,
            |_| 5.,
            |_| &Color::RGB(0, 0, 0)
        );

        line.calculate(&context, &MeasureMode::Wrap, &MeasureMode::Wrap);

        assert_eq!(line.bounding_box().width(), 13.535534);
        assert_eq!(line.bounding_box().height(), 13.535534);
    }
}<|endoftext|>


<commit_before>//! Endpoints for getting and synchronizing events.
<commit_msg>Add sync endpoint.<commit_after>//! Endpoints for getting and synchronizing events.

/// GET /_matrix/client/r0/sync
///
/// [Matrix spec link](http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-sync)
pub mod sync {
    use ruma_identifiers::RoomId;
    use ruma_events::collections::only;
    use std::collections::HashMap;

    /// Details about this API endpoint.
    pub struct Endpoint;

    /// Wether to set presence or not during sync
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub enum SetPresence {
        #[serde(rename="offline")]
        Offline
    }

    /// This API endpoint's query parameters.
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct QueryParams {
        pub filter: Option<String>,
        pub since: Option<String>,
        pub full_state: Option<bool>,
        pub set_presence: Option<SetPresence>,
        pub timeout: Option<u64>
    }

    /// Updates to rooms
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct Rooms {
        pub leave: HashMap<RoomId, LeftRoom>,
        pub join: HashMap<RoomId, JoinedRoom>,
        pub invite: HashMap<RoomId, InvitedRoom>
    }

    /// Historical updates to left rooms
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct LeftRoom {
        pub timeline: Timeline,
        pub state: State
    }

    /// Updates to joined rooms
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct JoinedRoom {
        pub unread_notifications: UnreadNotificationsCount,
        pub timeline: Timeline,
        pub state: State,
        pub account_data: AccountData,
        pub ephemeral: Ephemeral
    }

    /// unread notifications count
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct UnreadNotificationsCount {
        pub highlight_count: u64,
        pub notification_count: u64
    }

    /// timeline
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct Timeline {
        pub limited: bool,
        pub prev_batch: String,
        pub events: only::RoomEvent
    }

    /// state
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct State {
        pub events: only::StateEvent
    }

    /// account data
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct AccountData {
        pub events: only::Event
    }

    /// ephemeral
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct Ephemeral {
        pub events: only::Event
    }

    /// invited room updates
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct InvitedRoom {
        pub invite_state: InviteState
    }

    /// invite state
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct InviteState {
        pub events: only::StateEvent
    }

    /// presence
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct Presence {
        pub events: only::Event
    }

    /// This API endpoint's reponse.
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct Response {
        pub next_batch: String,
        pub rooms: Rooms,
        pub presence: Presence
    }

    impl ::Endpoint for Endpoint {
        type BodyParams = ();
        type PathParams = ();
        type QueryParams = QueryParams;
        type Response = Response;

        fn method() -> ::Method {
            ::Method::Get
        }

        fn request_path(_params: Self::PathParams) -> String {
            "/_matrix/client/r0/sync".to_string()
        }

        fn router_path() -> String {
            "/_matrix/client/r0/sync".to_string()
        }
    }
}
<|endoftext|>


<commit_before>use regex::Regex;

enum ReplaceMode {
    Text,
    Regex(Regex),
}

pub struct Replacer {
    mode: ReplaceMode,
}

impl Replacer {
    fn new_raw() -> Replacer {
        Replacer { mode: ReplaceMode::Text }
    }

    fn replace(&self, text: &str, from: &str, to: &str) -> String {
        text.replace(from, to)
    }
}

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

    #[test]
    fn text_replace() {
        let r = Replacer::new_raw();

        assert_eq!("eeee", r.replace("wewe", "w", "e"));
    }
}
<commit_msg>small steps...<commit_after>use regex::Regex;

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ReplaceMode {
    Text,
    Regex,
}

#[derive(Clone, Debug)]
enum ReplaceModeStorage {
    Text(String),
    Regex(Regex),
}

pub struct Replacer {
    mode: ReplaceModeStorage,
    to: String,
}

impl Replacer {
    fn new(mode: ReplaceMode, from: &str, to: &str) -> Option<Replacer> {
        match mode {
            ReplaceMode::Text => Some(Replacer {
                mode: ReplaceModeStorage::Text(from.into()),
                to: to.into(),
            }),
            ReplaceMode::Regex => {
                match Regex::new(from) {
                    Ok(r) => {
                        Some(Replacer {
                            mode: ReplaceModeStorage::Regex(r),
                            to: to.into(),
                        })
                    }
                    Err(_) => None,
                }
            }
        }
    }

    fn replace(&self, text: &str) -> String {
        match self.mode {
            ReplaceModeStorage::Text(ref from) => text.replace(from, &self.to),
            ReplaceModeStorage::Regex(ref regex) => regex.replace_all(text, &*self.to).into_owned(),
        }
    }
}

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

    #[test]
    fn text_replace() {
        let r = Replacer::new(ReplaceMode::Text, "w", "e").unwrap();
        assert_eq!("eeee", r.replace("wewe"));
    }

    #[test]
    fn regex_replace() {
        let r = Replacer::new(ReplaceMode::Regex, "(a|b)", "c").unwrap();
        assert_eq!("cd", r.replace("ad"));
        assert_eq!("cccd", r.replace("abcd"));
    }

    #[test]
    fn regex_backreference() {
        let r = Replacer::new(ReplaceMode::Regex, "(a|b)", "$1$1").unwrap();
        assert_eq!("aad", r.replace("ad"));
    }
}
<|endoftext|>


<commit_before>use std::io;
use super::{Deserialize, Serialize, Error, VarUint32, CountedList, InitExpr, CountedListWriter};

/// Entry in the element section.
#[derive(Debug, Clone)]
pub struct ElementSegment {
	index: u32,
	offset: InitExpr,
	members: Vec<u32>,
}

impl ElementSegment {
	/// New element segment.
	pub fn new(index: u32, offset: InitExpr, members: Vec<u32>) -> Self {
		ElementSegment { index: index, offset: offset, members: members }
	}

	/// Sequence of function indices.
	pub fn members(&self) -> &[u32] { &self.members }

	/// Sequence of function indices (mutable)
	pub fn members_mut(&mut self) -> &mut Vec<u32> { &mut self.members }

	/// Table index (currently valid only value of `0`)
	pub fn index(&self) -> u32 { self.index }

	/// An i32 initializer expression that computes the offset at which to place the elements.
	pub fn offset(&self) -> &InitExpr { &self.offset }

	/// An i32 initializer expression that computes the offset at which to place the elements (mutable)
	pub fn offset_mut(&mut self) -> &mut InitExpr { &mut self.offset }
}

impl Deserialize for ElementSegment {
	 type Error = Error;

	fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error> {
		let index = VarUint32::deserialize(reader)?;
		let offset = InitExpr::deserialize(reader)?;
		let funcs: Vec<u32> = CountedList::<VarUint32>::deserialize(reader)?
			.into_inner()
			.into_iter()
			.map(Into::into)
			.collect();

		Ok(ElementSegment {
			index: index.into(),
			offset: offset,
			members: funcs,
		})
	}
}

impl Serialize for ElementSegment {
	type Error = Error;

	fn serialize<W: io::Write>(self, writer: &mut W) -> Result<(), Self::Error> {
		VarUint32::from(self.index).serialize(writer)?;
		self.offset.serialize(writer)?;
		let data = self.members;
		let counted_list = CountedListWriter::<VarUint32, _>(
			data.len(),
			data.into_iter().map(Into::into),
		);
		counted_list.serialize(writer)?;
		Ok(())
	}
}

/// Data segment definition.
#[derive(Clone, Debug)]
pub struct DataSegment {
	index: u32,
	offset: InitExpr,
	value: Vec<u8>,
}

impl DataSegment {
	/// New data segments.
	pub fn new(index: u32, offset: InitExpr, value: Vec<u8>) -> Self {
		DataSegment {
			index: index,
			offset: offset,
			value: value,
		}
	}

	/// Linear memory index (currently the only valid value is `0`).
	pub fn index(&self) -> u32 { self.index }

	/// An i32 initializer expression that computes the offset at which to place the data.
	pub fn offset(&self) -> &InitExpr { &self.offset }

	/// An i32 initializer expression that computes the offset at which to place the data (mutable)
	pub fn offset_mut(&mut self) -> &mut InitExpr { &mut self.offset }

	/// Initial value of the data segment.
	pub fn value(&self) -> &[u8] { &self.value }

	/// Initial value of the data segment (mutable).
	pub fn value_mut(&mut self) -> &mut Vec<u8> { &mut self.value }
}

impl Deserialize for DataSegment {
	 type Error = Error;

	fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error> {
		let index = VarUint32::deserialize(reader)?;
		let offset = InitExpr::deserialize(reader)?;
		let value_len = VarUint32::deserialize(reader)?;

		let mut value_buf = vec![0u8; value_len.into()];
		reader.read_exact(&mut value_buf[..])?;

		Ok(DataSegment {
			index: index.into(),
			offset: offset,
			value: value_buf,
		})
	}
}

impl Serialize for DataSegment {
	type Error = Error;

	fn serialize<W: io::Write>(self, writer: &mut W) -> Result<(), Self::Error> {
		VarUint32::from(self.index).serialize(writer)?;
		self.offset.serialize(writer)?;

		let value = self.value;
		VarUint32::from(value.len()).serialize(writer)?;
		writer.write_all(&value[..])?;
		Ok(())
	}
}
<commit_msg>fix segment read untrusted<commit_after>use std::io;
use super::{Deserialize, Serialize, Error, VarUint32, CountedList, InitExpr, CountedListWriter};

/// Entry in the element section.
#[derive(Debug, Clone)]
pub struct ElementSegment {
	index: u32,
	offset: InitExpr,
	members: Vec<u32>,
}

impl ElementSegment {
	/// New element segment.
	pub fn new(index: u32, offset: InitExpr, members: Vec<u32>) -> Self {
		ElementSegment { index: index, offset: offset, members: members }
	}

	/// Sequence of function indices.
	pub fn members(&self) -> &[u32] { &self.members }

	/// Sequence of function indices (mutable)
	pub fn members_mut(&mut self) -> &mut Vec<u32> { &mut self.members }

	/// Table index (currently valid only value of `0`)
	pub fn index(&self) -> u32 { self.index }

	/// An i32 initializer expression that computes the offset at which to place the elements.
	pub fn offset(&self) -> &InitExpr { &self.offset }

	/// An i32 initializer expression that computes the offset at which to place the elements (mutable)
	pub fn offset_mut(&mut self) -> &mut InitExpr { &mut self.offset }
}

impl Deserialize for ElementSegment {
	 type Error = Error;

	fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error> {
		let index = VarUint32::deserialize(reader)?;
		let offset = InitExpr::deserialize(reader)?;
		let funcs: Vec<u32> = CountedList::<VarUint32>::deserialize(reader)?
			.into_inner()
			.into_iter()
			.map(Into::into)
			.collect();

		Ok(ElementSegment {
			index: index.into(),
			offset: offset,
			members: funcs,
		})
	}
}

impl Serialize for ElementSegment {
	type Error = Error;

	fn serialize<W: io::Write>(self, writer: &mut W) -> Result<(), Self::Error> {
		VarUint32::from(self.index).serialize(writer)?;
		self.offset.serialize(writer)?;
		let data = self.members;
		let counted_list = CountedListWriter::<VarUint32, _>(
			data.len(),
			data.into_iter().map(Into::into),
		);
		counted_list.serialize(writer)?;
		Ok(())
	}
}

/// Data segment definition.
#[derive(Clone, Debug)]
pub struct DataSegment {
	index: u32,
	offset: InitExpr,
	value: Vec<u8>,
}

impl DataSegment {
	/// New data segments.
	pub fn new(index: u32, offset: InitExpr, value: Vec<u8>) -> Self {
		DataSegment {
			index: index,
			offset: offset,
			value: value,
		}
	}

	/// Linear memory index (currently the only valid value is `0`).
	pub fn index(&self) -> u32 { self.index }

	/// An i32 initializer expression that computes the offset at which to place the data.
	pub fn offset(&self) -> &InitExpr { &self.offset }

	/// An i32 initializer expression that computes the offset at which to place the data (mutable)
	pub fn offset_mut(&mut self) -> &mut InitExpr { &mut self.offset }

	/// Initial value of the data segment.
	pub fn value(&self) -> &[u8] { &self.value }

	/// Initial value of the data segment (mutable).
	pub fn value_mut(&mut self) -> &mut Vec<u8> { &mut self.value }
}

impl Deserialize for DataSegment {
	 type Error = Error;

	fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error> {
		let index = VarUint32::deserialize(reader)?;
		let offset = InitExpr::deserialize(reader)?;
		let value_len = u32::from(VarUint32::deserialize(reader)?) as usize;

		let mut value_buf = Vec::new();
		let mut total_read = 0;
		let mut buf = [0u8; 65536];
		while total_read < value_len {
			let next_to_read = if value_len - total_read > 65536 { 65536 } else { value_len - total_read };
			reader.read_exact(&mut buf[0..next_to_read])?;
			value_buf.extend_from_slice(&buf[0..next_to_read]);
			total_read += next_to_read;
		}

		Ok(DataSegment {
			index: index.into(),
			offset: offset,
			value: value_buf,
		})
	}
}

impl Serialize for DataSegment {
	type Error = Error;

	fn serialize<W: io::Write>(self, writer: &mut W) -> Result<(), Self::Error> {
		VarUint32::from(self.index).serialize(writer)?;
		self.offset.serialize(writer)?;

		let value = self.value;
		VarUint32::from(value.len()).serialize(writer)?;
		writer.write_all(&value[..])?;
		Ok(())
	}
}
<|endoftext|>


<commit_before>//! Implements search parallelization facilities.

use std::thread;
use std::cmp::{min, max};
use std::cell::UnsafeCell;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver, RecvError};
use basetypes::*;
use chess_move::*;
use tt::*;
use position::Position;
use super::search::Search;

/// Represents a command to a search thread.
pub enum Command {
    /// Requests a new search.
    Search {
        /// A number identifying the new search.
        search_id: usize,

        /// The root position.
        position: Position,

        /// The requested search depth.
        depth: u8,

        /// The lower bound for the new search.
        lower_bound: Value,

        /// The upper bound for the new search.
        upper_bound: Value,
    },

    /// Stops the currently running search.
    Stop,

    /// Stops the currently running search and exits the search
    /// thread.
    Exit,
}

/// Represents a report from a search thread.
pub enum Report {
    /// Reports search progress.
    Progress {
        /// The ID passed with the search command.
        search_id: usize,

        /// The number of positions searched so far.
        searched_nodes: NodeCount,

        /// The search depth completed so far.
        searched_depth: u8,

        /// The evaluation of the root position so far.
        value: Option<Value>,
    },

    /// Reports that the search is finished.
    Done {
        /// The ID passed with the search command.
        search_id: usize,

        /// The total number of positions searched.
        searched_nodes: NodeCount,

        /// The search depth completed.
        searched_depth: u8,

        /// The evaluation of the root position.
        value: Option<Value>,
    },
}

/// Listens for commands, executes simple searches, sends reports
/// back.
///
/// This function will block and wait to receive commands on the
/// `commands` channel to start, stop, or exit searches. It is
/// intended to be called in a separate thread. While the search is
/// executed, regular `Report::Progress` messages will be send back to
/// the master thread via the `reports` channel. When the search is
/// done, a final `Report::Done` message will be sent via the
/// `reports` channel.
///
/// # Example:
///
/// ```rust
/// // Spawn a slave thread:
/// let tt = Arc::new(tt);
/// let (commands_tx, commands_rx) = channel();
/// let (reports_tx, reports_rx) = channel();
/// thread::spawn(move || {
///     serve_simple(tt, commands_rx, reports_tx);
/// });
///
/// // Send a command to start a new search:
/// commands_tx.send(Command::Search {
///     search_id: 0,
///     position: Position::form_fen("8/8/8/8/8/8/7P/5k1K b - - 0 99"),
///     depth: 5,
///     lower_bound: -20000,
///     upper_bound: 20000,
/// }).unwrap();
/// ```
///
/// This function executes sequential (non-parallel) search to a fixed
/// depth.
pub fn serve_simple(tt: Arc<TranspositionTable>,
                    commands: Receiver<Command>,
                    reports: Sender<Report>) {
    thread_local!(
        static MOVE_STACK: UnsafeCell<MoveStack> = UnsafeCell::new(MoveStack::new())
    );
    MOVE_STACK.with(|s| {
        let mut move_stack = unsafe { &mut *s.get() };
        let mut pending_command = None;
        loop {
            // If there is a pending command, we take it, otherwise we
            // block and wait to receive a new one.
            let command = match pending_command.take() {
                Some(cmd) => cmd,
                None => commands.recv().or::<RecvError>(Ok(Command::Exit)).unwrap(),
            };

            match command {
                Command::Search { search_id, position, depth, lower_bound, upper_bound } => {
                    let mut report = |searched_nodes| {
                        reports.send(Report::Progress {
                                   search_id: search_id,
                                   searched_nodes: searched_nodes,
                                   searched_depth: 0,
                                   value: None,
                               })
                               .ok();
                        if let Ok(cmd) = commands.try_recv() {
                            pending_command = Some(cmd);
                            true
                        } else {
                            false
                        }
                    };
                    let mut search = Search::new(position, &tt, move_stack, &mut report);
                    let value = search.run(lower_bound, upper_bound, depth).ok();
                    let searched_depth = if value.is_some() {
                        depth
                    } else {
                        0
                    };
                    reports.send(Report::Progress {
                               search_id: search_id,
                               searched_nodes: search.node_count(),
                               searched_depth: searched_depth,
                               value: value,
                           })
                           .ok();
                    reports.send(Report::Done {
                               search_id: search_id,
                               searched_nodes: search.node_count(),
                               searched_depth: searched_depth,
                               value: value,
                           })
                           .ok();
                    search.reset();
                }

                Command::Stop => continue,

                Command::Exit => break,
            }
        }
    })
}

/// Listens for commands, executes deepening searches, sends reports
/// back.
///
/// This function will block and wait to receive commands on the
/// `commands` channel to start, stop, or exit searches. It is
/// intended to be called in a separate thread. While the search is
/// executed, regular `Report::Progress` messages will be send back to
/// the master thread via the `reports` channel. When the search is
/// done, a final `Report::Done` message will be sent via the
/// `reports` channel.
///
/// # Example:
///
/// ```rust
/// // Spawn a slave thread:
/// let tt = Arc::new(tt);
/// let (commands_tx, commands_rx) = channel();
/// let (reports_tx, reports_rx) = channel();
/// thread::spawn(move || {
///     serve_deepening(tt, commands_rx, reports_tx);
/// });
///
/// // Send a command to start a new search:
/// commands_tx.send(Command::Search {
///     search_id: 0,
///     position: Position::form_fen("8/8/8/8/8/8/7P/5k1K b - - 0 99"),
///     depth: 5,
///     lower_bound: -20000,
///     upper_bound: 20000,
/// }).unwrap();
/// ```
///
/// This function executes a deepening search. It starts at depth `1`
/// and consequently increases it until the specified final depth is
/// reached.
pub fn serve_deepening(tt: Arc<TranspositionTable>,
                       commands: Receiver<Command>,
                       reports: Sender<Report>) {
    // Start a slave thread that will be commanded to run searches
    // with increasing depths (search deepening).
    let (slave_commands_tx, slave_commands_rx) = channel();
    let (slave_reports_tx, slave_reports_rx) = channel();
    let slave = thread::spawn(move || {
        serve_simple(tt, slave_commands_rx, slave_reports_tx);
    });

    let mut pending_command = None;
    loop {
        // If there is a pending command, we take it, otherwise we
        // block and wait to receive a new one.
        let command = match pending_command.take() {
            Some(cmd) => cmd,
            None => commands.recv().or::<RecvError>(Ok(Command::Exit)).unwrap(),
        };

        match command {
            Command::Search { search_id, position, depth, lower_bound, upper_bound } => {
                let mut current_searched_nodes = 0;
                let mut current_value = None;
                let mut current_depth = 1;

                'depthloop: while current_depth <= depth {
                    // First we set up the aspiration window. Aspiration windows are a way
                    // to reduce the search space in the search. We use `current_value`
                    // from the last iteration of `depth`, and calculate a window around
                    // this as the alpha-beta bounds. Because the window is narrower, more
                    // beta cutoffs are achieved, and the search takes a shorter time. The
                    // drawback is that if the true score is outside this window, then a
                    // costly re-search must be made. But then most probably the re-search
                    // will be much faster, because many positions will be remembered from
                    // the TT.
                    //
                    // Here `delta` is the initial half-width of the window, which will be
                    // increased each time the search failed. We use `isize` type to avoid
                    // overflows.
                    let mut delta = super::DELTA as isize;
                    let (mut alpha, mut beta) = if current_depth < 5 {
                        (lower_bound, upper_bound)
                    } else {
                        let v = current_value.unwrap() as isize;
                        (max(lower_bound as isize, v - delta) as Value,
                         min(v + delta, upper_bound as isize) as Value)
                    };

                    'aspiration: loop {
                        // Command the slave thread to run a search.
                        slave_commands_tx.send(Command::Search {
                                             search_id: current_depth as usize,
                                             position: position.clone(),
                                             depth: current_depth,
                                             lower_bound: alpha,
                                             upper_bound: beta,
                                         })
                                         .unwrap();

                        'report: loop {
                            // In this loop we process the reports coming from the slave
                            // thread, but we also constantly check if there is a new
                            // pending command for us, in which case we have to terminate
                            // the search.
                            match slave_reports_rx.recv().unwrap() {
                                Report::Progress { searched_nodes, .. } => {
                                    reports.send(Report::Progress {
                                               search_id: search_id,
                                               searched_nodes: current_searched_nodes +
                                                               searched_nodes,
                                               searched_depth: current_depth - 1,
                                               value: current_value,
                                           })
                                           .ok();
                                    if pending_command.is_none() {
                                        if let Ok(cmd) = commands.try_recv() {
                                            slave_commands_tx.send(Command::Stop).unwrap();
                                            pending_command = Some(cmd);
                                        }
                                    }
                                }
                                Report::Done { searched_nodes, value, .. } => {
                                    current_searched_nodes += searched_nodes;
                                    if pending_command.is_none() {
                                        current_value = value;
                                        break 'report;
                                    } else {
                                        break 'depthloop;
                                    }
                                }
                            }
                        } // end of 'report

                        // Check if the `current_value` is within the aspiration window
                        // (alpha, beta). If not so, we must consider running a re-search.
                        let v = current_value.unwrap() as isize;
                        if current_value.unwrap() <= alpha && lower_bound < alpha {
                            alpha = max(lower_bound as isize, v - delta) as Value;
                        } else if current_value.unwrap() >= beta && upper_bound > beta {
                            beta = min(v + delta, upper_bound as isize) as Value;
                        } else {
                            break 'aspiration;
                        }

                        // Increase the half-width of the aspiration window.
                        delta += 3 * delta / 8;
                        if delta > 1500 {
                            delta = 1_000_000;
                        }

                    } // end of 'aspiration

                    // Send a progress report with `current_value` for
                    // every completed depth.
                    reports.send(Report::Progress {
                               search_id: search_id,
                               searched_nodes: current_searched_nodes,
                               searched_depth: current_depth,
                               value: current_value,
                           })
                           .ok();
                    current_depth += 1;

                } // end of 'depthloop

                // The search is done -- send a final report.
                reports.send(Report::Done {
                           search_id: search_id,
                           searched_nodes: current_searched_nodes,
                           searched_depth: current_depth - 1,
                           value: current_value,
                       })
                       .ok();
            }

            Command::Stop => {
                slave_commands_tx.send(Command::Stop).unwrap();
                continue;
            }

            Command::Exit => {
                slave_commands_tx.send(Command::Exit).unwrap();
                break;
            }
        }
    }
    slave.join().unwrap();
}
<commit_msg>comments<commit_after>//! Implements search parallelization facilities.

use std::thread;
use std::cmp::{min, max};
use std::cell::UnsafeCell;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver, RecvError};
use basetypes::*;
use chess_move::*;
use tt::*;
use position::Position;
use super::search::Search;

/// Represents a command to a search thread.
pub enum Command {
    /// Requests a new search.
    Search {
        /// A number identifying the new search.
        search_id: usize,

        /// The root position.
        position: Position,

        /// The requested search depth.
        depth: u8,

        /// The lower bound for the new search.
        lower_bound: Value,

        /// The upper bound for the new search.
        upper_bound: Value,
    },

    /// Stops the currently running search.
    Stop,

    /// Stops the currently running search and exits the search
    /// thread.
    Exit,
}

/// Represents a report from a search thread.
pub enum Report {
    /// Reports search progress.
    Progress {
        /// The ID passed with the search command.
        search_id: usize,

        /// The number of positions searched so far.
        searched_nodes: NodeCount,

        /// The search depth completed so far.
        searched_depth: u8,

        /// The evaluation of the root position so far.
        value: Option<Value>,
    },

    /// Reports that the search is finished.
    Done {
        /// The ID passed with the search command.
        search_id: usize,

        /// The total number of positions searched.
        searched_nodes: NodeCount,

        /// The search depth completed.
        searched_depth: u8,

        /// The evaluation of the root position.
        value: Option<Value>,
    },
}

/// Listens for commands, executes simple searches, sends reports
/// back.
///
/// This function will block and wait to receive commands on the
/// `commands` channel to start, stop, or exit searches. It is
/// intended to be called in a separate thread. While the search is
/// executed, regular `Report::Progress` messages will be send back to
/// the master thread via the `reports` channel. When the search is
/// done, a final `Report::Done` message will be sent via the
/// `reports` channel.
///
/// # Example:
///
/// ```rust
/// // Spawn a slave thread:
/// let tt = Arc::new(tt);
/// let (commands_tx, commands_rx) = channel();
/// let (reports_tx, reports_rx) = channel();
/// thread::spawn(move || {
///     serve_simple(tt, commands_rx, reports_tx);
/// });
///
/// // Send a command to start a new search:
/// commands_tx.send(Command::Search {
///     search_id: 0,
///     position: Position::form_fen("8/8/8/8/8/8/7P/5k1K b - - 0 99"),
///     depth: 5,
///     lower_bound: -20000,
///     upper_bound: 20000,
/// }).unwrap();
/// ```
///
/// This function executes sequential (non-parallel) search to a fixed
/// depth.
pub fn serve_simple(tt: Arc<TranspositionTable>,
                    commands: Receiver<Command>,
                    reports: Sender<Report>) {
    thread_local!(
        static MOVE_STACK: UnsafeCell<MoveStack> = UnsafeCell::new(MoveStack::new())
    );
    MOVE_STACK.with(|s| {
        let mut move_stack = unsafe { &mut *s.get() };
        let mut pending_command = None;
        loop {
            // If there is a pending command, we take it, otherwise we
            // block and wait to receive a new one.
            let command = match pending_command.take() {
                Some(cmd) => cmd,
                None => commands.recv().or::<RecvError>(Ok(Command::Exit)).unwrap(),
            };

            match command {
                Command::Search { search_id, position, depth, lower_bound, upper_bound } => {
                    let mut report = |searched_nodes| {
                        reports.send(Report::Progress {
                                   search_id: search_id,
                                   searched_nodes: searched_nodes,
                                   searched_depth: 0,
                                   value: None,
                               })
                               .ok();
                        if let Ok(cmd) = commands.try_recv() {
                            pending_command = Some(cmd);
                            true
                        } else {
                            false
                        }
                    };
                    let mut search = Search::new(position, &tt, move_stack, &mut report);
                    let value = search.run(lower_bound, upper_bound, depth).ok();
                    let searched_depth = if value.is_some() {
                        depth
                    } else {
                        0
                    };
                    reports.send(Report::Progress {
                               search_id: search_id,
                               searched_nodes: search.node_count(),
                               searched_depth: searched_depth,
                               value: value,
                           })
                           .ok();
                    reports.send(Report::Done {
                               search_id: search_id,
                               searched_nodes: search.node_count(),
                               searched_depth: searched_depth,
                               value: value,
                           })
                           .ok();
                    search.reset();
                }

                Command::Stop => continue,

                Command::Exit => break,
            }
        }
    })
}

/// Listens for commands, executes deepening searches, sends reports
/// back.
///
/// This function will block and wait to receive commands on the
/// `commands` channel to start, stop, or exit searches. It is
/// intended to be called in a separate thread. While the search is
/// executed, regular `Report::Progress` messages will be send back to
/// the master thread via the `reports` channel. When the search is
/// done, a final `Report::Done` message will be sent via the
/// `reports` channel.
///
/// # Example:
///
/// ```rust
/// // Spawn a slave thread:
/// let tt = Arc::new(tt);
/// let (commands_tx, commands_rx) = channel();
/// let (reports_tx, reports_rx) = channel();
/// thread::spawn(move || {
///     serve_deepening(tt, commands_rx, reports_tx);
/// });
///
/// // Send a command to start a new search:
/// commands_tx.send(Command::Search {
///     search_id: 0,
///     position: Position::form_fen("8/8/8/8/8/8/7P/5k1K b - - 0 99"),
///     depth: 5,
///     lower_bound: -20000,
///     upper_bound: 20000,
/// }).unwrap();
/// ```
///
/// This function executes a deepening search with aspiration
/// window. It starts at depth `1` and consequently increases it until
/// the specified final depth is reached.
pub fn serve_deepening(tt: Arc<TranspositionTable>,
                       commands: Receiver<Command>,
                       reports: Sender<Report>) {
    // Start a slave thread that will be commanded to run searches
    // with increasing depths (search deepening).
    let (slave_commands_tx, slave_commands_rx) = channel();
    let (slave_reports_tx, slave_reports_rx) = channel();
    let slave = thread::spawn(move || {
        serve_simple(tt, slave_commands_rx, slave_reports_tx);
    });

    let mut pending_command = None;
    loop {
        // If there is a pending command, we take it, otherwise we
        // block and wait to receive a new one.
        let command = match pending_command.take() {
            Some(cmd) => cmd,
            None => commands.recv().or::<RecvError>(Ok(Command::Exit)).unwrap(),
        };

        match command {
            Command::Search { search_id, position, depth, lower_bound, upper_bound } => {
                let mut current_searched_nodes = 0;
                let mut current_value = None;
                let mut current_depth = 1;

                'depthloop: while current_depth <= depth {
                    // First we set up the aspiration window. Aspiration windows are a way
                    // to reduce the search space in the search. We use `current_value`
                    // from the last iteration of `depth`, and calculate a window around
                    // this as the alpha-beta bounds. Because the window is narrower, more
                    // beta cutoffs are achieved, and the search takes a shorter time. The
                    // drawback is that if the true score is outside this window, then a
                    // costly re-search must be made. But then most probably the re-search
                    // will be much faster, because many positions will be remembered from
                    // the TT.
                    //
                    // Here `delta` is the initial half-width of the window, that will be
                    // exponentially increased each time the search failed. We use `isize`
                    // type to avoid overflows.
                    let mut delta = super::DELTA as isize;
                    let (mut alpha, mut beta) = if current_depth < 5 {
                        (lower_bound, upper_bound)
                    } else {
                        let v = current_value.unwrap() as isize;
                        (max(lower_bound as isize, v - delta) as Value,
                         min(v + delta, upper_bound as isize) as Value)
                    };

                    'aspiration: loop {
                        // Command the slave thread to run a search.
                        slave_commands_tx.send(Command::Search {
                                             search_id: current_depth as usize,
                                             position: position.clone(),
                                             depth: current_depth,
                                             lower_bound: alpha,
                                             upper_bound: beta,
                                         })
                                         .unwrap();

                        'report: loop {
                            // In this loop we process the reports coming from the slave
                            // thread, but we also constantly check if there is a new
                            // pending command for us, in which case we have to terminate
                            // the search.
                            match slave_reports_rx.recv().unwrap() {
                                Report::Progress { searched_nodes, .. } => {
                                    reports.send(Report::Progress {
                                               search_id: search_id,
                                               searched_nodes: current_searched_nodes +
                                                               searched_nodes,
                                               searched_depth: current_depth - 1,
                                               value: current_value,
                                           })
                                           .ok();
                                    if pending_command.is_none() {
                                        if let Ok(cmd) = commands.try_recv() {
                                            slave_commands_tx.send(Command::Stop).unwrap();
                                            pending_command = Some(cmd);
                                        }
                                    }
                                }
                                Report::Done { searched_nodes, value, .. } => {
                                    current_searched_nodes += searched_nodes;
                                    if pending_command.is_none() {
                                        current_value = value;
                                        break 'report;
                                    } else {
                                        break 'depthloop;
                                    }
                                }
                            }
                        } // end of 'report

                        // Check if the `current_value` is within the aspiration window
                        // (alpha, beta). If not so, we must consider running a re-search.
                        let v = current_value.unwrap() as isize;
                        if current_value.unwrap() <= alpha && lower_bound < alpha {
                            alpha = max(lower_bound as isize, v - delta) as Value;
                        } else if current_value.unwrap() >= beta && upper_bound > beta {
                            beta = min(v + delta, upper_bound as isize) as Value;
                        } else {
                            break 'aspiration;
                        }

                        // Increase the half-width of the aspiration window.
                        delta += 3 * delta / 8;
                        if delta > 1500 {
                            delta = 1_000_000;
                        }

                    } // end of 'aspiration

                    // Send a progress report with `current_value` for
                    // every completed depth.
                    reports.send(Report::Progress {
                               search_id: search_id,
                               searched_nodes: current_searched_nodes,
                               searched_depth: current_depth,
                               value: current_value,
                           })
                           .ok();
                    current_depth += 1;

                } // end of 'depthloop

                // The search is done -- send a final report.
                reports.send(Report::Done {
                           search_id: search_id,
                           searched_nodes: current_searched_nodes,
                           searched_depth: current_depth - 1,
                           value: current_value,
                       })
                       .ok();
            }

            Command::Stop => {
                slave_commands_tx.send(Command::Stop).unwrap();
                continue;
            }

            Command::Exit => {
                slave_commands_tx.send(Command::Exit).unwrap();
                break;
            }
        }
    }
    slave.join().unwrap();
}
<|endoftext|>


<commit_before>#[derive(Debug)]
pub enum ExpectedVersion {
    Number(u64),
    Empty,             //  0 states that the stream should exist but should be empty.
    NotExist,          // -1 states that the stream should not exist at the time of the writing (this write will create it).
    NoConflict,        // -2 states that this write should never conflict with anything and should always succeed.
    Exist,             // -4 to say that the stream should exist with any number of events in it.

    Unexpected(String) // Any string (including -3 OR less than or equal -5).
}

impl From<ExpectedVersion> for String {
    fn from(version: ExpectedVersion) -> String {
        match version {
            ExpectedVersion::Number(n) => n.to_string(),
            ExpectedVersion::Empty => "0".to_string(),
            ExpectedVersion::NotExist => "-1".to_string(),
            ExpectedVersion::NoConflict => "-2".to_string(),
            ExpectedVersion::Exist => "-4".to_string(),
            ExpectedVersion::Unexpected(s) => s,
        }
    }
}

impl From<String> for ExpectedVersion {
    fn from(string: String) -> ExpectedVersion {
        match string.as_ref() {
            "-4" => ExpectedVersion::Exist,
            "-2" => ExpectedVersion::NoConflict,
            "-1" => ExpectedVersion::NotExist,
            "0" => ExpectedVersion::Empty,
            _ => ExpectedVersion::parse_number_or_unexpected(string)
        }
    }
}

impl ExpectedVersion {
    fn parse_number_or_unexpected(string: String) -> ExpectedVersion {
        match string.parse::<u64>() {
            Ok(n) => if n >= 0 { ExpectedVersion::Number(n) } else { ExpectedVersion::Unexpected(string) },
            Err(_) => ExpectedVersion::Unexpected(string)
        }
    }
}
<commit_msg>Remove redundant if statement<commit_after>#[derive(Debug)]
pub enum ExpectedVersion {
    Number(u64),
    Empty,             //  0 states that the stream should exist but should be empty.
    NotExist,          // -1 states that the stream should not exist at the time of the writing (this write will create it).
    NoConflict,        // -2 states that this write should never conflict with anything and should always succeed.
    Exist,             // -4 to say that the stream should exist with any number of events in it.

    Unexpected(String) // Any string (including -3 OR less than or equal -5).
}

impl From<ExpectedVersion> for String {
    fn from(version: ExpectedVersion) -> String {
        match version {
            ExpectedVersion::Number(n) => n.to_string(),
            ExpectedVersion::Empty => "0".to_string(),
            ExpectedVersion::NotExist => "-1".to_string(),
            ExpectedVersion::NoConflict => "-2".to_string(),
            ExpectedVersion::Exist => "-4".to_string(),
            ExpectedVersion::Unexpected(s) => s,
        }
    }
}

impl From<String> for ExpectedVersion {
    fn from(string: String) -> ExpectedVersion {
        match string.as_ref() {
            "-4" => ExpectedVersion::Exist,
            "-2" => ExpectedVersion::NoConflict,
            "-1" => ExpectedVersion::NotExist,
            "0" => ExpectedVersion::Empty,
            _ => ExpectedVersion::parse_number_or_unexpected(string)
        }
    }
}

impl ExpectedVersion {
    fn parse_number_or_unexpected(string: String) -> ExpectedVersion {
        match string.parse::<u64>() {
            Ok(n) =>  ExpectedVersion::Number(n),
            Err(_) => ExpectedVersion::Unexpected(string)
        }
    }
}
<|endoftext|>


<commit_before>//! Test command for testing the binary machine code emission.
//!
//! The `binemit` test command generates binary machine code for every instruction in the input
//! functions and compares the results to the expected output.

use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::Write;
use cretonne::binemit;
use cretonne::ir;
use cretonne::ir::entities::AnyEntity;
use cretonne::regalloc::RegDiversions;
use cton_reader::TestCommand;
use filetest::subtest::{SubTest, Context, Result};
use utils::{match_directive, pretty_error};

struct TestBinEmit;

pub fn subtest(parsed: &TestCommand) -> Result<Box<SubTest>> {
    assert_eq!(parsed.command, "binemit");
    if !parsed.options.is_empty() {
        Err(format!("No options allowed on {}", parsed))
    } else {
        Ok(Box::new(TestBinEmit))
    }
}

// Code sink that generates text.
struct TextSink {
    offset: binemit::CodeOffset,
    text: String,
}

impl TextSink {
    /// Create a new empty TextSink.
    pub fn new() -> Self {
        Self {
            offset: 0,
            text: String::new(),
        }
    }
}

impl binemit::CodeSink for TextSink {
    fn offset(&self) -> binemit::CodeOffset {
        self.offset
    }

    fn put1(&mut self, x: u8) {
        write!(self.text, "{:02x} ", x).unwrap();
        self.offset += 1;
    }

    fn put2(&mut self, x: u16) {
        write!(self.text, "{:04x} ", x).unwrap();
        self.offset += 2;
    }

    fn put4(&mut self, x: u32) {
        write!(self.text, "{:08x} ", x).unwrap();
        self.offset += 4;
    }

    fn put8(&mut self, x: u64) {
        write!(self.text, "{:016x} ", x).unwrap();
        self.offset += 8;
    }

    fn reloc_ebb(&mut self, reloc: binemit::Reloc, ebb_offset: binemit::CodeOffset) {
        write!(self.text, "{}({}) ", reloc, ebb_offset).unwrap();
    }

    fn reloc_external(
        &mut self,
        reloc: binemit::Reloc,
        name: &ir::ExternalName,
        addend: binemit::Addend,
    ) {
        write!(
            self.text,
            "{}({}",
            reloc,
            name,
        ).unwrap();
        if addend != 0 {
            write!(
                self.text,
                "{:+}",
                addend,
            ).unwrap();
        }
        write!(
            self.text,
            ") ",
        ).unwrap();
    }

    fn reloc_jt(&mut self, reloc: binemit::Reloc, jt: ir::JumpTable) {
        write!(self.text, "{}({}) ", reloc, jt).unwrap();
    }
}

impl SubTest for TestBinEmit {
    fn name(&self) -> Cow<str> {
        Cow::from("binemit")
    }

    fn is_mutating(&self) -> bool {
        true
    }

    fn needs_isa(&self) -> bool {
        true
    }

    fn run(&self, func: Cow<ir::Function>, context: &Context) -> Result<()> {
        let isa = context.isa.expect("binemit needs an ISA");
        let encinfo = isa.encoding_info();
        // TODO: Run a verifier pass over the code first to detect any bad encodings or missing/bad
        // value locations. The current error reporting is just crashing...
        let mut func = func.into_owned();

        // Fix the stack frame layout so we can test spill/fill encodings.
        let min_offset = func.stack_slots
            .keys()
            .map(|ss| func.stack_slots[ss].offset)
            .min();
        func.stack_slots.frame_size = min_offset.map(|off| (-off) as u32);

        let is_compressed = isa.flags().is_compressed();

        // Give an encoding to any instruction that doesn't already have one.
        let mut divert = RegDiversions::new();
        for ebb in func.layout.ebbs() {
            divert.clear();
            for inst in func.layout.ebb_insts(ebb) {
                if !func.encodings[inst].is_legal() {
                    let mut legal_encodings = isa.legal_encodings(
                        &func.dfg,
                        &func.dfg[inst],
                        func.dfg.ctrl_typevar(inst),
                    );

                    let enc = if is_compressed {
                        // Get the smallest legal encoding
                        legal_encodings
                            .filter(|e| {
                                let recipe_constraints = &encinfo.constraints[e.recipe()];
                                recipe_constraints.satisfied(inst, &divert, &func)
                            })
                            .min_by_key(|&e| encinfo.bytes(e))
                    } else {
                        // If not using compressed, just use the first encoding.
                        legal_encodings.next()
                    };

                    if let Some(enc) = enc {
                        func.encodings[inst] = enc;
                    }
                }
                divert.apply(&func.dfg[inst]);
            }
        }

        // Relax branches and compute EBB offsets based on the encodings.
        let code_size = binemit::relax_branches(&mut func, isa).map_err(|e| {
            pretty_error(&func, context.isa, e)
        })?;

        // Collect all of the 'bin:' directives on instructions.
        let mut bins = HashMap::new();
        for comment in &context.details.comments {
            if let Some(want) = match_directive(comment.text, "bin:") {
                match comment.entity {
                    AnyEntity::Inst(inst) => {
                        if let Some(prev) = bins.insert(inst, want) {
                            return Err(format!(
                                "multiple 'bin:' directives on {}: '{}' and '{}'",
                                func.dfg.display_inst(inst, isa),
                                prev,
                                want
                            ));
                        }
                    }
                    _ => {
                        return Err(format!(
                            "'bin:' directive on non-inst {}: {}",
                            comment.entity,
                            comment.text
                        ))
                    }
                }
            }
        }
        if bins.is_empty() {
            return Err("No 'bin:' directives found".to_string());
        }

        // Now emit all instructions.
        let mut sink = TextSink::new();
        for ebb in func.layout.ebbs() {
            divert.clear();
            // Correct header offsets should have been computed by `relax_branches()`.
            assert_eq!(
                sink.offset,
                func.offsets[ebb],
                "Inconsistent {} header offset",
                ebb
            );
            for inst in func.layout.ebb_insts(ebb) {
                sink.text.clear();
                let enc = func.encodings[inst];

                // Send legal encodings into the emitter.
                if enc.is_legal() {
                    let before = sink.offset;
                    isa.emit_inst(&func, inst, &mut divert, &mut sink);
                    let emitted = sink.offset - before;
                    // Verify the encoding recipe sizes against the ISAs emit_inst implementation.
                    assert_eq!(
                        emitted,
                        encinfo.bytes(enc),
                        "Inconsistent size for [{}] {}",
                        encinfo.display(enc),
                        func.dfg.display_inst(inst, isa)
                    );
                }

                // Check against bin: directives.
                if let Some(want) = bins.remove(&inst) {
                    if !enc.is_legal() {
                        return Err(format!(
                            "{} can't be encoded: {}",
                            inst,
                            func.dfg.display_inst(inst, isa)
                        ));
                    }
                    let have = sink.text.trim();
                    if have != want {
                        return Err(format!(
                            "Bad machine code for {}: {}\nWant: {}\nGot:  {}",
                            inst,
                            func.dfg.display_inst(inst, isa),
                            want,
                            have
                        ));
                    }
                }
            }
        }

        if sink.offset != code_size {
            return Err(format!(
                "Expected code size {}, got {}",
                code_size,
                sink.offset
            ));
        }

        Ok(())
    }
}
<commit_msg>Give better error messages in "test binemit".<commit_after>//! Test command for testing the binary machine code emission.
//!
//! The `binemit` test command generates binary machine code for every instruction in the input
//! functions and compares the results to the expected output.

use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::Write;
use cretonne::binemit;
use cretonne::dbg::DisplayList;
use cretonne::ir;
use cretonne::ir::entities::AnyEntity;
use cretonne::regalloc::RegDiversions;
use cton_reader::TestCommand;
use filetest::subtest::{SubTest, Context, Result};
use utils::{match_directive, pretty_error};

struct TestBinEmit;

pub fn subtest(parsed: &TestCommand) -> Result<Box<SubTest>> {
    assert_eq!(parsed.command, "binemit");
    if !parsed.options.is_empty() {
        Err(format!("No options allowed on {}", parsed))
    } else {
        Ok(Box::new(TestBinEmit))
    }
}

// Code sink that generates text.
struct TextSink {
    offset: binemit::CodeOffset,
    text: String,
}

impl TextSink {
    /// Create a new empty TextSink.
    pub fn new() -> Self {
        Self {
            offset: 0,
            text: String::new(),
        }
    }
}

impl binemit::CodeSink for TextSink {
    fn offset(&self) -> binemit::CodeOffset {
        self.offset
    }

    fn put1(&mut self, x: u8) {
        write!(self.text, "{:02x} ", x).unwrap();
        self.offset += 1;
    }

    fn put2(&mut self, x: u16) {
        write!(self.text, "{:04x} ", x).unwrap();
        self.offset += 2;
    }

    fn put4(&mut self, x: u32) {
        write!(self.text, "{:08x} ", x).unwrap();
        self.offset += 4;
    }

    fn put8(&mut self, x: u64) {
        write!(self.text, "{:016x} ", x).unwrap();
        self.offset += 8;
    }

    fn reloc_ebb(&mut self, reloc: binemit::Reloc, ebb_offset: binemit::CodeOffset) {
        write!(self.text, "{}({}) ", reloc, ebb_offset).unwrap();
    }

    fn reloc_external(
        &mut self,
        reloc: binemit::Reloc,
        name: &ir::ExternalName,
        addend: binemit::Addend,
    ) {
        write!(
            self.text,
            "{}({}",
            reloc,
            name,
        ).unwrap();
        if addend != 0 {
            write!(
                self.text,
                "{:+}",
                addend,
            ).unwrap();
        }
        write!(
            self.text,
            ") ",
        ).unwrap();
    }

    fn reloc_jt(&mut self, reloc: binemit::Reloc, jt: ir::JumpTable) {
        write!(self.text, "{}({}) ", reloc, jt).unwrap();
    }
}

impl SubTest for TestBinEmit {
    fn name(&self) -> Cow<str> {
        Cow::from("binemit")
    }

    fn is_mutating(&self) -> bool {
        true
    }

    fn needs_isa(&self) -> bool {
        true
    }

    fn run(&self, func: Cow<ir::Function>, context: &Context) -> Result<()> {
        let isa = context.isa.expect("binemit needs an ISA");
        let encinfo = isa.encoding_info();
        // TODO: Run a verifier pass over the code first to detect any bad encodings or missing/bad
        // value locations. The current error reporting is just crashing...
        let mut func = func.into_owned();

        // Fix the stack frame layout so we can test spill/fill encodings.
        let min_offset = func.stack_slots
            .keys()
            .map(|ss| func.stack_slots[ss].offset)
            .min();
        func.stack_slots.frame_size = min_offset.map(|off| (-off) as u32);

        let is_compressed = isa.flags().is_compressed();

        // Give an encoding to any instruction that doesn't already have one.
        let mut divert = RegDiversions::new();
        for ebb in func.layout.ebbs() {
            divert.clear();
            for inst in func.layout.ebb_insts(ebb) {
                if !func.encodings[inst].is_legal() {
                    // Find an encoding that satisfies both immediate field and register
                    // constraints.
                    if let Some(enc) = {
                        let mut legal_encodings = isa.legal_encodings(
                            &func.dfg,
                            &func.dfg[inst],
                            func.dfg.ctrl_typevar(inst),
                        ).filter(|e| {
                                let recipe_constraints = &encinfo.constraints[e.recipe()];
                                recipe_constraints.satisfied(inst, &divert, &func)
                            });

                        if is_compressed {
                            // Get the smallest legal encoding
                            legal_encodings.min_by_key(|&e| encinfo.bytes(e))
                        } else {
                            // If not using compressed, just use the first encoding.
                            legal_encodings.next()
                        }
                    }
                    {
                        func.encodings[inst] = enc;
                    }
                }
                divert.apply(&func.dfg[inst]);
            }
        }

        // Relax branches and compute EBB offsets based on the encodings.
        let code_size = binemit::relax_branches(&mut func, isa).map_err(|e| {
            pretty_error(&func, context.isa, e)
        })?;

        // Collect all of the 'bin:' directives on instructions.
        let mut bins = HashMap::new();
        for comment in &context.details.comments {
            if let Some(want) = match_directive(comment.text, "bin:") {
                match comment.entity {
                    AnyEntity::Inst(inst) => {
                        if let Some(prev) = bins.insert(inst, want) {
                            return Err(format!(
                                "multiple 'bin:' directives on {}: '{}' and '{}'",
                                func.dfg.display_inst(inst, isa),
                                prev,
                                want
                            ));
                        }
                    }
                    _ => {
                        return Err(format!(
                            "'bin:' directive on non-inst {}: {}",
                            comment.entity,
                            comment.text
                        ))
                    }
                }
            }
        }
        if bins.is_empty() {
            return Err("No 'bin:' directives found".to_string());
        }

        // Now emit all instructions.
        let mut sink = TextSink::new();
        for ebb in func.layout.ebbs() {
            divert.clear();
            // Correct header offsets should have been computed by `relax_branches()`.
            assert_eq!(
                sink.offset,
                func.offsets[ebb],
                "Inconsistent {} header offset",
                ebb
            );
            for inst in func.layout.ebb_insts(ebb) {
                sink.text.clear();
                let enc = func.encodings[inst];

                // Send legal encodings into the emitter.
                if enc.is_legal() {
                    // Generate a better error message if output locations are not specified.
                    if let Some(&v) = func.dfg.inst_results(inst).iter().find(|&&v| {
                        !func.locations[v].is_assigned()
                    })
                    {
                        return Err(format!(
                            "Missing register/stack slot for {} in {}",
                            v,
                            func.dfg.display_inst(inst, isa)
                        ));
                    }
                    let before = sink.offset;
                    isa.emit_inst(&func, inst, &mut divert, &mut sink);
                    let emitted = sink.offset - before;
                    // Verify the encoding recipe sizes against the ISAs emit_inst implementation.
                    assert_eq!(
                        emitted,
                        encinfo.bytes(enc),
                        "Inconsistent size for [{}] {}",
                        encinfo.display(enc),
                        func.dfg.display_inst(inst, isa)
                    );
                }

                // Check against bin: directives.
                if let Some(want) = bins.remove(&inst) {
                    if !enc.is_legal() {
                        // A possible cause of an unencoded instruction is a missing location for
                        // one of the input operands.
                        if let Some(&v) = func.dfg.inst_args(inst).iter().find(|&&v| {
                            !func.locations[v].is_assigned()
                        })
                        {
                            return Err(format!(
                                "Missing register/stack slot for {} in {}",
                                v,
                                func.dfg.display_inst(inst, isa)
                            ));
                        }

                        // Do any encodings exist?
                        let encodings = isa.legal_encodings(
                            &func.dfg,
                            &func.dfg[inst],
                            func.dfg.ctrl_typevar(inst),
                        ).map(|e| encinfo.display(e))
                            .collect::<Vec<_>>();

                        if encodings.is_empty() {
                            return Err(format!(
                                "No encodings found for: {}",
                                func.dfg.display_inst(inst, isa)
                            ));
                        } else {
                            return Err(format!(
                                "No matching encodings for {} in {}",
                                func.dfg.display_inst(inst, isa),
                                DisplayList(&encodings),
                            ));
                        }
                    }
                    let have = sink.text.trim();
                    if have != want {
                        return Err(format!(
                            "Bad machine code for {}: {}\nWant: {}\nGot:  {}",
                            inst,
                            func.dfg.display_inst(inst, isa),
                            want,
                            have
                        ));
                    }
                }
            }
        }

        if sink.offset != code_size {
            return Err(format!(
                "Expected code size {}, got {}",
                code_size,
                sink.offset
            ));
        }

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


<commit_before>
use token::{Token, TokenKind};
use error::{NcclError, ErrorKind};

// ranked worst to best
enum Indent {
    Neither,
    Tabs,
    Spaces(u8),
}

pub struct Scanner {
    source: Vec<u8>,
    tokens: Vec<Token>,
    start: usize,
    current: usize,
    line: u64,
    indent: Indent,
}

impl Scanner {
    pub fn new(source: String) -> Self {
        Scanner {
            source: source.into_bytes(),
            tokens: Vec::new(),
            start: 0,
            current: 0,
            line: 1,
            indent: Indent::Neither,
        }
    }

    pub fn scan_tokens(&mut self) -> Result<Vec<Token>, Vec<NcclError>> {
        let mut err = vec![];

        while !self.is_at_end() {
            self.start = self.current;
            let e = self.scan_token();
            if e.is_err() {
                err.push(e.err().unwrap());
            }
        }

        self.tokens.push(Token::new(TokenKind::EOF, "".into(), self.line));

        if !err.is_empty() {
            Err(err)
        } else {
            Ok(self.tokens.clone())
        }
    }

    fn scan_token(&mut self) -> Result<(), NcclError> {
        let mut error = Ok(());
        match self.advance() {
            b'#' => {
                while self.peek() != b'\n' && !self.is_at_end() {
                    self.advance();
                }
            },

            b' ' => {
                match self.indent {
                    Indent::Neither => {
                        let mut spaces = 0;
                        while self.peek() == b' ' && !self.is_at_end() {
                            self.advance();
                            spaces += 1;
                        }
                        if self.is_at_end() {
                            error = Err(NcclError::new(ErrorKind::ParseError, "Expected value, found EOF", self.line));
                        }
                        self.indent = Indent::Spaces(spaces);
                        self.add_token(TokenKind::Indent);
                    }
                    Indent::Spaces(s) => {
                        let mut spaces = 0;
                        while spaces < s  && !self.is_at_end() {
                            if self.peek() != b' ' {
                                error = Err(NcclError::new(ErrorKind::IndentationError, &format!("Incorrect number of spaces: found {}, expected {}", spaces, s), self.line));
                            }
                            self.advance();
                            spaces += 1;
                        }
                        if self.is_at_end() {
                            error = Err(NcclError::new(ErrorKind::ParseError, "Expected value, found EOF", self.line));
                        }
                        self.add_token(TokenKind::Indent);
                    },
                    Indent::Tabs => { error = Err(NcclError::new(ErrorKind::IndentationError, "Expected tabs, found spaces", self.line)); }
                }
            },

            b'\t' => {
                match self.indent {
                    Indent::Neither => {
                        self.add_token(TokenKind::Indent);
                        self.indent = Indent::Tabs;
                    },
                    Indent::Tabs => {
                        self.add_token(TokenKind::Indent);
                    },
                    Indent::Spaces(_) => { error = Err(NcclError::new(ErrorKind::IndentationError, "Expected spaces, found tabs", self.line)); }
                }
            },

            b'\n' => {
                self.add_token(TokenKind::Newline);
                self.line += 1;
            },

            b'\r' => {},

            b'"' => if let Err(e) = self.string() { error = Err(e); },

            _ => if let Err(e) = self.identifier() { error = Err(e); },
        }

        error
    }

    fn identifier(&mut self) -> Result<(), NcclError> {
        while self.peek() != b'\n' && self.peek() != b'\r' && self.peek() != b':' && !self.is_at_end() {
            self.advance();
        }

        let value = String::from_utf8(self.source[self.start..self.current].to_vec()).unwrap();
        self.add_token_string(TokenKind::Value, value);

        Ok(())
    }

    fn string(&mut self) -> Result<(), NcclError> {
        let mut value = String::new();
        while self.peek() != b'"' && !self.is_at_end() {
            if self.peek() == b'\n' {
                self.line += 1;
            }

            if self.peek() == b'\\' {
                self.advance();
                match self.peek() {
                    b'n' => {
                        value.push('\n');
                    },
                    b'r' => {
                        value.push('\r');
                    }
                    b'\\' => {
                        value.push('\\');
                    },
                    b'"' => {
                        value.push('"');
                    },
                    _ => return Err(NcclError::new(ErrorKind::ParseError, &format!("Unknown format code: {}", self.peek()), self.line))
                }
            } else {
                value.push(self.source[self.current] as char);
            }

            self.advance();
        }

        if self.is_at_end() {
            return Err(NcclError::new(ErrorKind::ParseError, "Unterminated string", self.line));
        }

        self.advance();

        self.add_token_string(TokenKind::Value, value);

        Ok(())
    }

    fn is_at_end(&mut self) -> bool {
        self.current >= self.source.len()
    }

    fn advance(&mut self) -> u8 {
        self.current += 1;
        self.source[self.current - 1]
    }

    fn add_token(&mut self, kind: TokenKind) {
        let text = String::from_utf8(self.source[self.start..self.current].to_vec()).unwrap();
        self.tokens.push(Token::new(kind, text, self.line));
    }

    fn add_token_string(&mut self, kind: TokenKind, value: String) {
        self.tokens.push(Token::new(kind, value, self.line));
    }

    fn peek(&mut self) -> u8 {
        if self.current >= self.source.len() {
            b'\0'
        } else {
            self.source[self.current]
        }
    }
}

<commit_msg>finish removing colon<commit_after>
use token::{Token, TokenKind};
use error::{NcclError, ErrorKind};

// ranked worst to best
enum Indent {
    Neither,
    Tabs,
    Spaces(u8),
}

pub struct Scanner {
    source: Vec<u8>,
    tokens: Vec<Token>,
    start: usize,
    current: usize,
    line: u64,
    indent: Indent,
}

impl Scanner {
    pub fn new(source: String) -> Self {
        Scanner {
            source: source.into_bytes(),
            tokens: Vec::new(),
            start: 0,
            current: 0,
            line: 1,
            indent: Indent::Neither,
        }
    }

    pub fn scan_tokens(&mut self) -> Result<Vec<Token>, Vec<NcclError>> {
        let mut err = vec![];

        while !self.is_at_end() {
            self.start = self.current;
            let e = self.scan_token();
            if e.is_err() {
                err.push(e.err().unwrap());
            }
        }

        self.tokens.push(Token::new(TokenKind::EOF, "".into(), self.line));

        if !err.is_empty() {
            Err(err)
        } else {
            Ok(self.tokens.clone())
        }
    }

    fn scan_token(&mut self) -> Result<(), NcclError> {
        let mut error = Ok(());
        match self.advance() {
            b'#' => {
                while self.peek() != b'\n' && !self.is_at_end() {
                    self.advance();
                }
            },

            b' ' => {
                match self.indent {
                    Indent::Neither => {
                        let mut spaces = 0;
                        while self.peek() == b' ' && !self.is_at_end() {
                            self.advance();
                            spaces += 1;
                        }
                        if self.is_at_end() {
                            error = Err(NcclError::new(ErrorKind::ParseError, "Expected value, found EOF", self.line));
                        }
                        self.indent = Indent::Spaces(spaces);
                        self.add_token(TokenKind::Indent);
                    }
                    Indent::Spaces(s) => {
                        let mut spaces = 0;
                        while spaces < s  && !self.is_at_end() {
                            if self.peek() != b' ' {
                                error = Err(NcclError::new(ErrorKind::IndentationError, &format!("Incorrect number of spaces: found {}, expected {}", spaces, s), self.line));
                            }
                            self.advance();
                            spaces += 1;
                        }
                        if self.is_at_end() {
                            error = Err(NcclError::new(ErrorKind::ParseError, "Expected value, found EOF", self.line));
                        }
                        self.add_token(TokenKind::Indent);
                    },
                    Indent::Tabs => { error = Err(NcclError::new(ErrorKind::IndentationError, "Expected tabs, found spaces", self.line)); }
                }
            },

            b'\t' => {
                match self.indent {
                    Indent::Neither => {
                        self.add_token(TokenKind::Indent);
                        self.indent = Indent::Tabs;
                    },
                    Indent::Tabs => {
                        self.add_token(TokenKind::Indent);
                    },
                    Indent::Spaces(_) => { error = Err(NcclError::new(ErrorKind::IndentationError, "Expected spaces, found tabs", self.line)); }
                }
            },

            b'\n' => {
                self.add_token(TokenKind::Newline);
                self.line += 1;
            },

            b'\r' => {},

            b'"' => if let Err(e) = self.string() { error = Err(e); },

            _ => if let Err(e) = self.identifier() { error = Err(e); },
        }

        error
    }

    fn identifier(&mut self) -> Result<(), NcclError> {
        while self.peek() != b'\n' && self.peek() != b'\r' && !self.is_at_end() {
            self.advance();
        }

        let value = String::from_utf8(self.source[self.start..self.current].to_vec()).unwrap();
        self.add_token_string(TokenKind::Value, value);

        Ok(())
    }

    fn string(&mut self) -> Result<(), NcclError> {
        let mut value = String::new();
        while self.peek() != b'"' && !self.is_at_end() {
            if self.peek() == b'\n' {
                self.line += 1;
            }

            if self.peek() == b'\\' {
                self.advance();
                match self.peek() {
                    b'n' => {
                        value.push('\n');
                    },
                    b'r' => {
                        value.push('\r');
                    }
                    b'\\' => {
                        value.push('\\');
                    },
                    b'"' => {
                        value.push('"');
                    },
                    _ => return Err(NcclError::new(ErrorKind::ParseError, &format!("Unknown format code: {}", self.peek()), self.line))
                }
            } else {
                value.push(self.source[self.current] as char);
            }

            self.advance();
        }

        if self.is_at_end() {
            return Err(NcclError::new(ErrorKind::ParseError, "Unterminated string", self.line));
        }

        self.advance();

        self.add_token_string(TokenKind::Value, value);

        Ok(())
    }

    fn is_at_end(&mut self) -> bool {
        self.current >= self.source.len()
    }

    fn advance(&mut self) -> u8 {
        self.current += 1;
        self.source[self.current - 1]
    }

    fn add_token(&mut self, kind: TokenKind) {
        let text = String::from_utf8(self.source[self.start..self.current].to_vec()).unwrap();
        self.tokens.push(Token::new(kind, text, self.line));
    }

    fn add_token_string(&mut self, kind: TokenKind, value: String) {
        self.tokens.push(Token::new(kind, value, self.line));
    }

    fn peek(&mut self) -> u8 {
        if self.current >= self.source.len() {
            b'\0'
        } else {
            self.source[self.current]
        }
    }
}

<|endoftext|>