<commit_before>use std::io::{IoResult, standard_error, EndOfFile, OtherIoError, IoError};
use std::c_str::CString;
use libc::{c_int, c_char, c_uint};

enum GzFile {}

#[link(name="z")]
extern {
    fn gzopen(filename: *const c_char, mode: *const c_char) -> *const GzFile;
    fn gzread(file: *const GzFile, buf: *mut u8, len: c_uint) -> c_int;
    fn gzclose(file: *const GzFile) -> c_int;
    fn gzerror(file: *const GzFile, errnum: *mut c_int) -> *const c_char;
}

pub struct GzipReader {
    file: *const GzFile,
    path: Path,
    closed: bool,
}

impl GzipReader {
    pub fn open(path: &Path) -> IoResult<GzipReader> {
        let file = unsafe {
            gzopen(path.to_c_str().unwrap(), "rb".to_c_str().unwrap())
        };
        if !file.is_null() {
            Ok(GzipReader{file: file, path: path.clone(), closed: false})
        } else {
            Err(IoError::last_error())
        }
    }

    fn get_error(&mut self) -> Option<IoError> {
        let mut errnum: c_int = 0;
        let msg = unsafe {CString::new(gzerror(self.file, &mut errnum), true)};
        if errnum < 0 {
            let detail = self.path.as_str().unwrap().into_string()
                         + msg.as_str().unwrap();
            Some(IoError{kind: OtherIoError,
                         desc: "GZip reader error",
                         detail: Some(detail)
                        })
        } else {
            None
        }
    }

    fn close(&mut self) -> Option<IoError> {
        if !self.closed {
            self.closed = true;
            match unsafe {gzclose(self.file)} {
                0 => None,
                _ => self.get_error()
            }
        } else {
            None
        }
    }
}

impl Drop for GzipReader {
    fn drop(&mut self) {
        self.close();
    }
}

impl Reader for GzipReader {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        match unsafe {
                    gzread(self.file, buf.as_mut_ptr(), buf.len() as c_uint)
                } {
            size if size > 0 => Ok(size as uint),
            0 => {
                match self.close() {
                    None => Err(standard_error(EndOfFile)),
                    Some(err) => Err(err)
                }
            }
            _ => {
                let err = self.get_error().unwrap();
                self.close();
                Err(err)
            }
        }
    }
}
<commit_msg>Replace deprecated CString.unwrap() with as_ptr()<commit_after>use std::io::{IoResult, standard_error, EndOfFile, OtherIoError, IoError};
use std::c_str::CString;
use libc::{c_int, c_char, c_uint};

enum GzFile {}

#[link(name="z")]
extern {
    fn gzopen(filename: *const c_char, mode: *const c_char) -> *const GzFile;
    fn gzread(file: *const GzFile, buf: *mut u8, len: c_uint) -> c_int;
    fn gzclose(file: *const GzFile) -> c_int;
    fn gzerror(file: *const GzFile, errnum: *mut c_int) -> *const c_char;
}

pub struct GzipReader {
    file: *const GzFile,
    path: Path,
    closed: bool,
}

impl GzipReader {
    pub fn open(path: &Path) -> IoResult<GzipReader> {
        let file = unsafe {
            gzopen(path.to_c_str().as_ptr(), "rb".to_c_str().as_ptr())
        };
        if !file.is_null() {
            Ok(GzipReader{file: file, path: path.clone(), closed: false})
        } else {
            Err(IoError::last_error())
        }
    }

    fn get_error(&mut self) -> Option<IoError> {
        let mut errnum: c_int = 0;
        let msg = unsafe {CString::new(gzerror(self.file, &mut errnum), true)};
        if errnum < 0 {
            let detail = self.path.as_str().unwrap().into_string()
                         + msg.as_str().unwrap();
            Some(IoError{kind: OtherIoError,
                         desc: "GZip reader error",
                         detail: Some(detail)
                        })
        } else {
            None
        }
    }

    fn close(&mut self) -> Option<IoError> {
        if !self.closed {
            self.closed = true;
            match unsafe {gzclose(self.file)} {
                0 => None,
                _ => self.get_error()
            }
        } else {
            None
        }
    }
}

impl Drop for GzipReader {
    fn drop(&mut self) {
        self.close();
    }
}

impl Reader for GzipReader {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        match unsafe {
                    gzread(self.file, buf.as_mut_ptr(), buf.len() as c_uint)
                } {
            size if size > 0 => Ok(size as uint),
            0 => {
                match self.close() {
                    None => Err(standard_error(EndOfFile)),
                    Some(err) => Err(err)
                }
            }
            _ => {
                let err = self.get_error().unwrap();
                self.close();
                Err(err)
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate time;
extern crate http_event_store as hes;
extern crate uuid;

use hes::event::Event;
use hes::error::HesError::*;
use hes::error::UserErrorKind::*;
use hes::expected_version::ExpectedVersion;

#[macro_use]
mod support;

#[test]
fn should_retrun_stream_not_found_error_attempting_to_read_nonexistent_stream() {
    let client = hes::client::Client::default();
    let nonexistent_stream_name = "some-nonexistent";
    let result = client.read_stream_events_forward(&nonexistent_stream_name, 0, 1, true);

    assert_error!(UserError, StreamNotFound, result.unwrap_err());
}

#[test]
fn should_retrun_stream_deleted_error_attempting_to_read_deleted_stream() {
    let events: Vec<Event> = vec![Event { event_id: Some(uuid::Uuid::new_v4()),
                                          event_type: "created".to_string(),
                                          data: Some("{a:1}".to_string()) }];

    let client = hes::client::Client::default();
    let stream_name = format!("stream-{}", uuid::Uuid::new_v4().simple());
    client.append_to_stream(&stream_name, ExpectedVersion::NoStream, events).unwrap();

    assert!(client.read_stream_events_forward(&stream_name, 0, 1, true).is_ok());
    assert!(client.hard_delete_stream(&stream_name, ExpectedVersion::Any).is_ok());

    let result = client.read_stream_events_forward(&stream_name, 0, 1, true);
    assert_error!(UserError, StreamDeleted, result.unwrap_err());
}
<commit_msg>Fix a typo<commit_after>extern crate time;
extern crate http_event_store as hes;
extern crate uuid;

use hes::event::Event;
use hes::error::HesError::*;
use hes::error::UserErrorKind::*;
use hes::expected_version::ExpectedVersion;

#[macro_use]
mod support;

#[test]
fn should_return_stream_not_found_error_attempting_to_read_nonexistent_stream() {
    let client = hes::client::Client::default();
    let nonexistent_stream_name = "some-nonexistent";
    let result = client.read_stream_events_forward(&nonexistent_stream_name, 0, 1, true);

    assert_error!(UserError, StreamNotFound, result.unwrap_err());
}

#[test]
fn should_return_stream_deleted_error_attempting_to_read_deleted_stream() {
    let events: Vec<Event> = vec![Event { event_id: Some(uuid::Uuid::new_v4()),
                                          event_type: "created".to_string(),
                                          data: Some("{a:1}".to_string()) }];

    let client = hes::client::Client::default();
    let stream_name = format!("stream-{}", uuid::Uuid::new_v4().simple());
    client.append_to_stream(&stream_name, ExpectedVersion::NoStream, events).unwrap();

    assert!(client.read_stream_events_forward(&stream_name, 0, 1, true).is_ok());
    assert!(client.hard_delete_stream(&stream_name, ExpectedVersion::Any).is_ok());

    let result = client.read_stream_events_forward(&stream_name, 0, 1, true);
    assert_error!(UserError, StreamDeleted, result.unwrap_err());
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader, Cursor, Read};
use std::path::Path;

use {Error, Result};
use project::ImageData;

pub struct Image {
    pub version: u8,
    pub width: usize,
    pub height: usize,
    pub data: Vec<Vec<f32>>,
}

impl Image {
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Image> {
        let mut buf = Vec::new();
        try!(File::open(path).and_then(|mut f| f.read_to_end(&mut buf)));
        // We need to read everything in, then convert w/ utf8 lossy, because the degree sign
        // doesn't seem to be valid utf8.
        let mut iter = BufReader::new(Cursor::new(String::from_utf8_lossy(&buf[..]).into_owned()))
            .lines();
        let mut header_map: HashMap<String, String> = HashMap::new();
        {
            let mut next_line = || {
                iter.next()
                    .map(|r| r.map_err(Error::from))
                    .unwrap_or(Err(Error::ReadInfratec("Unexpected end of file".to_string())))
            };
            let header = try!(next_line());
            if header != "[Settings]" {
                return Err(Error::ReadInfratec(format!("Invalid header: {}", header)));
            }
            loop {
                let line = try!(next_line());
                if line == "[Data]" {
                    break;
                } else if line.is_empty() {
                    continue;
                } else {
                    let errmsg = format!("Invalid header line: {}", line);
                    let mut iter = line.split('=');
                    {
                        let mut next_word = || {
                            iter.next()
                                .map(|s| s.to_string())
                                .ok_or(Error::ReadInfratec(errmsg.clone()))
                        };
                        header_map.insert(try!(next_word()), try!(next_word()));
                    }
                    if iter.next().is_some() {
                        return Err(Error::ReadInfratec(errmsg));
                    }
                }
            }
        }
        let data: Vec<Vec<f32>> = try!(iter.map(|r| {
                r.map_err(Error::from)
                    .and_then(|s| {
                        s.split(';').map(|s| s.parse::<f32>().map_err(Error::from)).collect()
                    })
            })
            .collect());
        let get_from_header_map = |name| {
            header_map.get(name).ok_or(Error::ReadInfratec(format!("Missing header key: {}", name)))
        };
        Ok(Image {
            version: try!(get_from_header_map("Version")
                .and_then(|s| s.parse::<u8>().map_err(Error::from))),
            width: try!(get_from_header_map("ImageWidth")
                .and_then(|s| s.parse::<usize>().map_err(Error::from))),
            height: try!(get_from_header_map("ImageHeight")
                .and_then(|s| s.parse::<usize>().map_err(Error::from))),
            data: data,
        })
    }
}

impl ImageData for Image {
    fn get(&self, u: f64, v: f64) -> Option<f64> {
        unimplemented!()
    }
}

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

    #[test]
    fn image_from_path() {
        let image =
            Image::from_path("data/project.RiSCAN/SCANS/SP01/SCANPOSIMAGES/SP01 - Image001.csv")
                .unwrap();
        assert_eq!(3, image.version);
        assert_eq!(768, image.height);
        assert_eq!(1024, image.width);
        assert_eq!(768, image.data.len());
        assert!(image.data.iter().all(|v| v.len() == 1024));
        assert_eq!(-38.64, image.data[0][0]);
        assert_eq!(23.84, *image.data.last().unwrap().last().unwrap());
    }
}
<commit_msg>Cleaner infratec reading<commit_after>use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader, Cursor, Read};
use std::path::Path;

use {Error, Result};
use project::ImageData;

pub struct Image {
    header: Header,
    data: Vec<Vec<f64>>,
}

impl Image {
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Image> {
        let mut buf = Vec::new();
        try!(File::open(path).and_then(|mut f| f.read_to_end(&mut buf)));
        // We need to read everything in, then convert w/ utf8 lossy, because the degree sign
        // doesn't seem to be valid utf8.
        let mut reader = BufReader::new(Cursor::new(String::from_utf8_lossy(&buf[..])
            .into_owned()));
        let header = try!(Header::new(&mut reader));
        let data: Vec<Vec<f64>> = try!(reader.lines()
            .map(|r| {
                r.map_err(Error::from)
                    .and_then(|s| {
                        s.split(';').map(|s| s.parse::<f64>().map_err(Error::from)).collect()
                    })
            })
            .collect());
        Ok(Image {
            header: header,
            data: data,
        })
    }

    pub fn version(&self) -> u8 {
        self.header.version
    }

    pub fn width(&self) -> usize {
        self.header.width
    }

    pub fn height(&self) -> usize {
        self.header.height
    }

    pub fn data(&self) -> &Vec<Vec<f64>> {
        &self.data
    }
}

impl ImageData for Image {
    fn get(&self, u: f64, v: f64) -> Option<f64> {
        unimplemented!()
    }
}

#[derive(Debug, Default)]
struct Header {
    version: u8,
    width: usize,
    height: usize,
}

impl Header {
    fn new<R: BufRead>(reader: &mut R) -> Result<Header> {
        let mut lines = reader.lines();
        let mut next_line = || {
            lines.next()
                .map(|r| r.map_err(Error::from))
                .unwrap_or(Err(Error::ReadInfratec("Unexpected EOF".to_string())))
        };
        let first_line = try!(next_line());
        if first_line != "[Settings]" {
            return Err(Error::ReadInfratec(format!("Invalid first line: {}", first_line)));
        }
        let mut header = Default::default();
        loop {
            let line = try!(next_line());
            if line == "[Data]" {
                return Ok(header);
            } else if line.is_empty() {
                continue;
            }
            let words = line.split('=').collect::<Vec<_>>();
            if words.len() != 2 {
                return Err(Error::ReadInfratec(format!("Invalid header line: {}", line)));
            }
            match words[0] {
                "ImageHeight" => header.height = try!(words[1].parse()),
                "ImageWidth" => header.width = try!(words[1].parse()),
                "Version" => header.version = try!(words[1].parse()),
                _ => {}
            };
        }
    }
}

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

    #[test]
    fn image_from_path() {
        let image =
            Image::from_path("data/project.RiSCAN/SCANS/SP01/SCANPOSIMAGES/SP01 - Image001.csv")
                .unwrap();
        assert_eq!(3, image.version());
        assert_eq!(768, image.height());
        assert_eq!(1024, image.width());
        assert_eq!(768, image.data().len());
        assert!(image.data.iter().all(|v| v.len() == 1024));
        assert_eq!(-38.64, image.data()[0][0]);
        assert_eq!(23.84, *image.data().last().unwrap().last().unwrap());
    }
}
<|endoftext|>


<commit_before>use std::io::net::ip::{SocketAddr, IpAddr};

use http::server::{Server, Config};
use http::server;

use super::ingot::Ingot;
use super::furnace::Furnace;
use super::response::Response;
use super::request::Request;

use super::response::ironresponse::IronResponse;
use super::request::ironrequest::IronRequest;
use super::furnace::ironfurnace::IronFurnace;

pub type ServerT =
    Iron<IronRequest, IronResponse<'static, 'static>,
         IronFurnace<'static, 'static, IronRequest, IronResponse<'static, 'static>>>;

/// The primary entrance point to `Iron`, a `struct` to instantiate a new server.
///
/// The server can be made with a specific `Furnace` (using `from_furnace`)
/// or with a new `Furnace` (using `new`). `Iron` is used to manage the server
/// processes:
/// `Iron.smelt` is used to add new `Ingot`s, and
/// `Iron.listen` is used to kick off a server process.
///
/// `Iron` contains the `Furnace` which holds the `Ingot`s necessary to run a server.
/// `Iron` is the main interface to adding `Ingot`s, and has `Furnace` as a
/// public field (for the sake of extensibility).
pub struct Iron<Rq, Rs, F> {
    /// The exposed internal field for storing `Furnace`.
    ///
    /// This is exposed for the sake of extensibility. It can be used to set
    /// furnace to implement your server's middleware stack with custom behavior.
    /// Most users will not need to touch `furnace`. This should only be used if you
    /// need custom handling of `Ingot`s. Normally, the default `IronFurnace` is
    /// sufficient.
    pub furnace: F,
    ip: Option<IpAddr>,
    port: Option<u16>
}

impl<Rq, Rs, F: Clone> Clone for Iron<Rq, Rs, F> {
    fn clone(&self) -> Iron<Rq, Rs, F> {
        Iron {
            furnace: self.furnace.clone(),
            ip: self.ip.clone(),
            port: self.port
        }
    }
}

impl<'a, 'b, Rq: Request, Rs: Response<'a, 'b>, F: Furnace<'a, 'b, Rq, Rs>>
        Iron<Rq, Rs, F> {
    /// `smelt` a new `Ingot`.
    ///
    /// This adds an `Ingot` to the `Iron`'s `furnace`, so that any requests
    /// are passed through those `Ingot`s.
    ///
    /// `Iron.smelt` delegates to `iron.furnace.smelt`, so that any `Ingot`
    /// added is added to the `Iron` instance's `furnace`.
    pub fn smelt<'a, 'b, I: Ingot<'a, 'b, Rq, Rs>>(&mut self, ingot: I) {
        self.furnace.smelt(ingot);
    }

    /// Kick off the server process.
    ///
    /// Call this once to begin listening for requests on the server.
    /// This is a blocking operation, and is the final op that should be called
    /// on the `Iron` instance. Once `listen` is called, requests will be
    /// handled as defined through the `Iron`'s `furnace`'s `Ingot`s.
    pub fn listen(mut self, ip: IpAddr, port: u16) {
        self.ip = Some(ip);
        self.port = Some(port);
        self.serve_forever();
    }

    /// Instantiate a new instance of `Iron`.
    ///
    /// This will create a new `Iron`, the base unit of the server.
    /// This creates an `Iron` with a default `furnace`, the `IronFurnace`.
    ///
    /// Custom furnaces can be used with `from_furnace`, instead of `new`.
    #[inline]
    pub fn new<Rq, Rs>() -> Iron<Rq, Rs, F> {
        let furnace = Furnace::new();
        Iron {
            furnace: furnace,
            ip: None,
            port: None
        }
    }

    /// Instantiate a new instance of `Iron` from an existing `Furnace`.
    ///
    /// This will create a new `Iron` from a give `Furnace`.
    ///
    /// This `Furnace` *may already have `Ingot`s in it*. An empty default
    /// `Furnace` can be created more easily using `new`.
    ///
    /// The `Furnace` can also be configured to handle `Ingot`s differently than
    /// `IronFurnace`. For example, this can be used to implement a `Furnace`
    /// that logs debug messages as it serves requests.
    ///
    /// Most users will not need to touch `from_furnace`. This should only be
    /// used if you need custom handling of `Ingot`s. Normally, the default
    /// `IronFurnace` is sufficient.
    pub fn from_furnace<Rq, Rs, F>(furnace: F) -> Iron<Rq, Rs, F> {
        Iron {
            furnace: furnace,
            ip: None,
            port: None
        }
    }
}

/// This is unused but required for internal functionality.
///
/// This `impl` allows `Iron` to be used as a `Server` by
/// [rust-http]('https://github.com/chris-morgan/rust-http').
/// This is not used by users of this library.
impl<'a, 'b,
     Rq: Request,
     Rs: Response<'a, 'b>,
     F: Furnace<'a, 'b, Rq, Rs>>
        Server for Iron<Rq, Rs, F> {
    fn get_config(&self) -> Config {
        Config { bind_address: SocketAddr {
            ip: self.ip.unwrap(),
            port: self.port.unwrap()
        } }
    }

    fn handle_request(&self, req: &server::Request, res: &mut server::ResponseWriter) {
        let mut furnace = self.furnace.clone();
        handler::<'a, 'b, Rq, Rs, F>(&mut furnace, req, res);
    }
}

fn handler<'a, 'b,
            Rq: Request,
            Rs: Response<'a, 'b>,
            F: Furnace<'a, 'b, Rq, Rs>>
        (furnace: &mut F, req: &server::Request, res: &mut server::ResponseWriter) {
    let mut request: Rq = Request::from_http(req);
    let mut response: Rs = Response::from_http(res);
    furnace.forge(&mut request, &mut response, None);
}
<commit_msg>(refactor) Required new trait for Server impls.<commit_after>use std::io::net::ip::{SocketAddr, IpAddr};

use http::server::{Server, Config};
use http::server;

use super::ingot::Ingot;
use super::furnace::Furnace;
use super::response::{Response, HttpResponse};
use super::request::Request;

use super::response::ironresponse::IronResponse;
use super::request::ironrequest::IronRequest;
use super::furnace::ironfurnace::IronFurnace;

pub type ServerT =
    Iron<IronRequest, IronResponse<'static, 'static>,
         IronFurnace<IronRequest, IronResponse<'static, 'static>>>;

/// The primary entrance point to `Iron`, a `struct` to instantiate a new server.
///
/// The server can be made with a specific `Furnace` (using `from_furnace`)
/// or with a new `Furnace` (using `new`). `Iron` is used to manage the server
/// processes:
/// `Iron.smelt` is used to add new `Ingot`s, and
/// `Iron.listen` is used to kick off a server process.
///
/// `Iron` contains the `Furnace` which holds the `Ingot`s necessary to run a server.
/// `Iron` is the main interface to adding `Ingot`s, and has `Furnace` as a
/// public field (for the sake of extensibility).
pub struct Iron<Rq, Rs, F> {
    /// The exposed internal field for storing `Furnace`.
    ///
    /// This is exposed for the sake of extensibility. It can be used to set
    /// furnace to implement your server's middleware stack with custom behavior.
    /// Most users will not need to touch `furnace`. This should only be used if you
    /// need custom handling of `Ingot`s. Normally, the default `IronFurnace` is
    /// sufficient.
    pub furnace: F,
    ip: Option<IpAddr>,
    port: Option<u16>
}

impl<Rq, Rs, F: Clone> Clone for Iron<Rq, Rs, F> {
    fn clone(&self) -> Iron<Rq, Rs, F> {
        Iron {
            furnace: self.furnace.clone(),
            ip: self.ip.clone(),
            port: self.port
        }
    }
}

impl<'a, 'b, Rq: Request, Rs: Response + HttpResponse<'a, 'b>, F: Furnace<Rq, Rs>>
        Iron<Rq, Rs, F> {
    /// `smelt` a new `Ingot`.
    ///
    /// This adds an `Ingot` to the `Iron`'s `furnace`, so that any requests
    /// are passed through those `Ingot`s.
    ///
    /// `Iron.smelt` delegates to `iron.furnace.smelt`, so that any `Ingot`
    /// added is added to the `Iron` instance's `furnace`.
    pub fn smelt<I: Ingot<Rq, Rs>>(&mut self, ingot: I) {
        self.furnace.smelt(ingot);
    }

    /// Kick off the server process.
    ///
    /// Call this once to begin listening for requests on the server.
    /// This is a blocking operation, and is the final op that should be called
    /// on the `Iron` instance. Once `listen` is called, requests will be
    /// handled as defined through the `Iron`'s `furnace`'s `Ingot`s.
    pub fn listen(mut self, ip: IpAddr, port: u16) {
        self.ip = Some(ip);
        self.port = Some(port);
        self.serve_forever();
    }

    /// Instantiate a new instance of `Iron`.
    ///
    /// This will create a new `Iron`, the base unit of the server.
    /// This creates an `Iron` with a default `furnace`, the `IronFurnace`.
    ///
    /// Custom furnaces can be used with `from_furnace`, instead of `new`.
    #[inline]
    pub fn new<Rq, Rs>() -> Iron<Rq, Rs, F> {
        let furnace = Furnace::new();
        Iron {
            furnace: furnace,
            ip: None,
            port: None
        }
    }

    /// Instantiate a new instance of `Iron` from an existing `Furnace`.
    ///
    /// This will create a new `Iron` from a give `Furnace`.
    ///
    /// This `Furnace` *may already have `Ingot`s in it*. An empty default
    /// `Furnace` can be created more easily using `new`.
    ///
    /// The `Furnace` can also be configured to handle `Ingot`s differently than
    /// `IronFurnace`. For example, this can be used to implement a `Furnace`
    /// that logs debug messages as it serves requests.
    ///
    /// Most users will not need to touch `from_furnace`. This should only be
    /// used if you need custom handling of `Ingot`s. Normally, the default
    /// `IronFurnace` is sufficient.
    pub fn from_furnace<Rq, Rs, F>(furnace: F) -> Iron<Rq, Rs, F> {
        Iron {
            furnace: furnace,
            ip: None,
            port: None
        }
    }
}

/// This is unused but required for internal functionality.
///
/// This `impl` allows `Iron` to be used as a `Server` by
/// [rust-http]('https://github.com/chris-morgan/rust-http').
/// This is not used by users of this library.
impl<'a, 'b,
     Rq: Request,
     Rs: Response + HttpResponse<'a, 'b>,
     F: Furnace<Rq, Rs>>
        Server for Iron<Rq, Rs, F> {
    fn get_config(&self) -> Config {
        Config { bind_address: SocketAddr {
            ip: self.ip.unwrap(),
            port: self.port.unwrap()
        } }
    }

    fn handle_request(&self, req: &server::Request, res: &mut server::ResponseWriter) {
        let mut furnace = self.furnace.clone();
        handler::<'a, 'b, Rq, Rs, F>(&mut furnace, req, res);
    }
}

fn handler<'a, 'b,
            Rq: Request,
            Rs: Response + HttpResponse<'a, 'b>,
            F: Furnace<Rq, Rs>>
        (furnace: &mut F, req: &server::Request, res: &mut server::ResponseWriter) {
    let mut request: Rq = Request::from_http(req);
    let mut response: Rs = HttpResponse::from_http(res);
    furnace.forge(&mut request, &mut response, None);
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;

use ffi;
use LuaContext;

use AsLua;
use AsMutLua;
use Push;
use PushGuard;
use LuaRead;

/// Represents a table stored in the Lua context.
///
/// Loading this type mutably borrows the Lua context.
pub struct LuaTable<L> {
    table: L,
}

unsafe impl<L> AsLua for LuaTable<L> where L: AsLua {
    fn as_lua(&self) -> LuaContext {
        self.table.as_lua()
    }
}

unsafe impl<L> AsMutLua for LuaTable<L> where L: AsMutLua {
    fn as_mut_lua(&mut self) -> LuaContext {
        self.table.as_mut_lua()
    }
}

impl<L> LuaRead<L> for LuaTable<L> where L: AsMutLua {
    fn lua_read_at_position(mut lua: L, index: i32) -> Result<LuaTable<L>, L> {
        assert!(index == -1);   // FIXME: not sure if it's working
        if unsafe { ffi::lua_istable(lua.as_mut_lua().0, index) } {
            Ok(LuaTable { table: lua })
        } else {
            Err(lua)
        }
    }
}

/// Iterator that enumerates the content of a Lua table.
// while the LuaTableIterator is active, the current key is constantly pushed over the table
pub struct LuaTableIterator<'t, L: 't, K, V> where L: AsMutLua {
    table: &'t mut LuaTable<L>,
    finished: bool,     // if true, the key is not on the stack anymore
    marker: PhantomData<(K, V)>,
}

unsafe impl<'t, L, K, V> AsLua for LuaTableIterator<'t, L, K, V> where L: AsMutLua {
    fn as_lua(&self) -> LuaContext {
        self.table.as_lua()
    }
}

unsafe impl<'t, L, K, V> AsMutLua for LuaTableIterator<'t, L, K, V> where L: AsMutLua {
    fn as_mut_lua(&mut self) -> LuaContext {
        self.table.as_mut_lua()
    }
}

impl<L> LuaTable<L> where L: AsMutLua {
    /// Destroys the LuaTable and returns its inner Lua context. Useful when it takes Lua by value.
    pub fn into_inner(self) -> L {
        self.table
    }

    /// Iterates over the elements inside the table.
    pub fn iter<K, V>(&mut self) -> LuaTableIterator<L, K, V> {
        unsafe { ffi::lua_pushnil(self.table.as_mut_lua().0) };

        LuaTableIterator {
            table: self,
            finished: false,
            marker: PhantomData,
        }
    }

    /// Loads a value in the table given its index.
    pub fn get<'a, R, I>(&'a mut self, index: I) -> Option<R>
                         where R: LuaRead<PushGuard<&'a mut LuaTable<L>>>,
                               I: for<'b> Push<&'b mut &'a mut LuaTable<L>>
    {
        let mut me = self;
        index.push_to_lua(&mut me).forget();
        unsafe { ffi::lua_gettable(me.as_mut_lua().0, -2); }
        if unsafe { ffi::lua_isnil(me.as_lua().0, -1) } {
            let _guard = PushGuard { lua: me, size: 1 };
            return None;
        }
        let guard = PushGuard { lua: me, size: 1 };
        LuaRead::lua_read(guard).ok()
    }

    /// Loads a value in the table, with the result capturing the table by value.
    pub fn into_get<'a, R, I>(self, index: I) -> Result<R, PushGuard<Self>>
        where R: LuaRead<PushGuard<LuaTable<L>>>,
              I: for<'b> Push<&'b mut LuaTable<L>>
    {
        let mut me = self;
        index.push_to_lua(&mut me).forget();
        unsafe { ffi::lua_gettable(me.as_mut_lua().0, -2); }
        let is_nil = unsafe { ffi::lua_isnil(me.as_mut_lua().0, -1) };
        let guard = PushGuard { lua: me, size: 1 };
        if is_nil {
            Err(guard)
        } else {
            LuaRead::lua_read(guard)
        }
    }

    /// Inserts or modifies an elements of the table.
    pub fn set<'s, I, V>(&'s mut self, index: I, value: V)
                         where I: for<'a> Push<&'a mut &'s mut LuaTable<L>>,
                               V: for<'a> Push<&'a mut &'s mut LuaTable<L>>
    {
        let mut me = self;
        index.push_to_lua(&mut me).forget();
        value.push_to_lua(&mut me).forget();
        unsafe { ffi::lua_settable(me.as_mut_lua().0, -3); }
    }

    /// Inserts an empty array, then loads it.
    pub fn empty_array<'s, I>(&'s mut self, index: I) -> LuaTable<PushGuard<&'s mut LuaTable<L>>>
                              where I: for<'a> Push<&'a mut &'s mut LuaTable<L>> + Clone
    {
        // TODO: cleaner implementation
        let mut me = self;
        index.clone().push_to_lua(&mut me).forget();
        Vec::<u8>::with_capacity(0).push_to_lua(&mut me).forget();
        unsafe { ffi::lua_settable(me.as_mut_lua().0, -3); }

        me.get(index).unwrap()
    }

    /// Obtains or create the metatable of the table.
    pub fn get_or_create_metatable(mut self) -> LuaTable<PushGuard<L>> {
        let result = unsafe { ffi::lua_getmetatable(self.table.as_mut_lua().0, -1) };

        if result == 0 {
            unsafe {
                ffi::lua_newtable(self.table.as_mut_lua().0);
                ffi::lua_setmetatable(self.table.as_mut_lua().0, -2);
                let r = ffi::lua_getmetatable(self.table.as_mut_lua().0, -1);
                assert!(r != 0);
            }
        }

        LuaTable {
            table: PushGuard { lua: self.table, size: 1 }
        }
    }
}

impl<'t, L, K, V> Iterator for LuaTableIterator<'t, L, K, V>
                  where L: AsMutLua + 't,
                        K: for<'i, 'j> LuaRead<&'i mut &'j mut LuaTableIterator<'t, L, K, V>> + 'static,
                        V: for<'i, 'j> LuaRead<&'i mut &'j mut LuaTableIterator<'t, L, K, V>> + 'static
{
    type Item = Option<(K, V)>;

    fn next(&mut self) -> Option<Option<(K,V)>> {
        if self.finished {
            return None;
        }

        // this call pushes the next key and value on the stack
        if unsafe { ffi::lua_next(self.table.as_mut_lua().0, -2) } == 0 {
            self.finished = true;
            return None;
        }

        let mut me = self;
        let key = LuaRead::lua_read_at_position(&mut me, -2).ok();
        let value = LuaRead::lua_read_at_position(&mut me, -1).ok();

        // removing the value, leaving only the key on the top of the stack
        unsafe { ffi::lua_pop(me.table.as_mut_lua().0, 1) };

        //
        if key.is_none() || value.is_none() {
            Some(None)
        } else {
            Some(Some((key.unwrap(), value.unwrap())))
        }
    }
}

impl<'t, L, K, V> Drop for LuaTableIterator<'t, L, K, V> where L: AsMutLua + 't {
    fn drop(&mut self) {
        if !self.finished {
            unsafe { ffi::lua_pop(self.table.table.as_mut_lua().0, 1) }
        }
    }
}
<commit_msg>Tables know their index on the stack, so table FFI now works even if the table is not the last argument to a rust function<commit_after>use std::marker::PhantomData;

use ffi;
use LuaContext;

use AsLua;
use AsMutLua;
use Push;
use PushGuard;
use LuaRead;

/// Represents a table stored in the Lua context.
///
/// Loading this type mutably borrows the Lua context.
pub struct LuaTable<L> {
    table: L,
    index: i32
}

unsafe impl<L> AsLua for LuaTable<L> where L: AsLua {
    fn as_lua(&self) -> LuaContext {
        self.table.as_lua()
    }
}

unsafe impl<L> AsMutLua for LuaTable<L> where L: AsMutLua {
    fn as_mut_lua(&mut self) -> LuaContext {
        self.table.as_mut_lua()
    }
}

impl<L> LuaRead<L> for LuaTable<L> where L: AsMutLua {
    fn lua_read_at_position(mut lua: L, index: i32) -> Result<LuaTable<L>, L> {
        if unsafe { ffi::lua_istable(lua.as_mut_lua().0, index) } {
            Ok(LuaTable { table: lua, index: index })
        } else {
            Err(lua)
        }
    }
}

/// Iterator that enumerates the content of a Lua table.
// while the LuaTableIterator is active, the current key is constantly pushed over the table
pub struct LuaTableIterator<'t, L: 't, K, V> where L: AsMutLua {
    table: &'t mut LuaTable<L>,
    finished: bool,     // if true, the key is not on the stack anymore
    marker: PhantomData<(K, V)>,
}

unsafe impl<'t, L, K, V> AsLua for LuaTableIterator<'t, L, K, V> where L: AsMutLua {
    fn as_lua(&self) -> LuaContext {
        self.table.as_lua()
    }
}

unsafe impl<'t, L, K, V> AsMutLua for LuaTableIterator<'t, L, K, V> where L: AsMutLua {
    fn as_mut_lua(&mut self) -> LuaContext {
        self.table.as_mut_lua()
    }
}

impl<L> LuaTable<L> where L: AsMutLua {
    /// Destroys the LuaTable and returns its inner Lua context. Useful when it takes Lua by value.
    pub fn into_inner(self) -> L {
        self.table
    }

    /// Iterates over the elements inside the table.
    pub fn iter<K, V>(&mut self) -> LuaTableIterator<L, K, V> {
        unsafe { ffi::lua_pushnil(self.table.as_mut_lua().0) };

        LuaTableIterator {
            table: self,
            finished: false,
            marker: PhantomData,
        }
    }

    /// Loads a value in the table given its index.
    pub fn get<'a, R, I>(&'a mut self, index: I) -> Option<R>
                         where R: LuaRead<PushGuard<&'a mut LuaTable<L>>>,
                               I: for<'b> Push<&'b mut &'a mut LuaTable<L>>
    {
        let mut me = self;
        index.push_to_lua(&mut me).forget();
        unsafe { ffi::lua_gettable(me.as_mut_lua().0, -1 + me.index); }
        if unsafe { ffi::lua_isnil(me.as_lua().0, -1) } {
            let _guard = PushGuard { lua: me, size: 1 };
            return None;
        }
        let guard = PushGuard { lua: me, size: 1 };
        LuaRead::lua_read(guard).ok()
    }

    /// Loads a value in the table, with the result capturing the table by value.
    pub fn into_get<'a, R, I>(self, index: I) -> Result<R, PushGuard<Self>>
        where R: LuaRead<PushGuard<LuaTable<L>>>,
              I: for<'b> Push<&'b mut LuaTable<L>>
    {
        let mut me = self;
        index.push_to_lua(&mut me).forget();
        unsafe { ffi::lua_gettable(me.as_mut_lua().0, -1 + me.index); }
        let is_nil = unsafe { ffi::lua_isnil(me.as_mut_lua().0, -1) };
        let guard = PushGuard { lua: me, size: 1 };
        if is_nil {
            Err(guard)
        } else {
            LuaRead::lua_read(guard)
        }
    }

    /// Inserts or modifies an elements of the table.
    pub fn set<'s, I, V>(&'s mut self, index: I, value: V)
                         where I: for<'a> Push<&'a mut &'s mut LuaTable<L>>,
                               V: for<'a> Push<&'a mut &'s mut LuaTable<L>>
    {
        let mut me = self;
        index.push_to_lua(&mut me).forget();
        value.push_to_lua(&mut me).forget();
        unsafe { ffi::lua_settable(me.as_mut_lua().0, -2 + me.index); }
    }

    /// Inserts an empty array, then loads it.
    pub fn empty_array<'s, I>(&'s mut self, index: I) -> LuaTable<PushGuard<&'s mut LuaTable<L>>>
                              where I: for<'a> Push<&'a mut &'s mut LuaTable<L>> + Clone
    {
        // TODO: cleaner implementation
        let mut me = self;
        index.clone().push_to_lua(&mut me).forget();
        Vec::<u8>::with_capacity(0).push_to_lua(&mut me).forget();
        unsafe { ffi::lua_settable(me.as_mut_lua().0, -2 + me.index); }

        me.get(index).unwrap()
    }

    /// Obtains or create the metatable of the table.
    pub fn get_or_create_metatable(mut self) -> LuaTable<PushGuard<L>> {
        let result = unsafe { ffi::lua_getmetatable(self.table.as_mut_lua().0, self.index) };

        if result == 0 {
            unsafe {
                ffi::lua_newtable(self.table.as_mut_lua().0);
                ffi::lua_setmetatable(self.table.as_mut_lua().0, -1 + self.index);
                let r = ffi::lua_getmetatable(self.table.as_mut_lua().0, self.index);
                assert!(r != 0);
            }
        }

        LuaTable {
            table: PushGuard { lua: self.table, size: 1 },
            index: -1 // After creating the metatable, it will be on top of the stack.
        }
    }
}

impl<'t, L, K, V> Iterator for LuaTableIterator<'t, L, K, V>
                  where L: AsMutLua + 't,
                        K: for<'i, 'j> LuaRead<&'i mut &'j mut LuaTableIterator<'t, L, K, V>> + 'static,
                        V: for<'i, 'j> LuaRead<&'i mut &'j mut LuaTableIterator<'t, L, K, V>> + 'static
{
    type Item = Option<(K, V)>;

    fn next(&mut self) -> Option<Option<(K,V)>> {
        if self.finished {
            return None;
        }

        // this call pushes the next key and value on the stack
        if unsafe { ffi::lua_next(self.table.as_mut_lua().0, -1 + self.table.index) } == 0 {
            self.finished = true;
            return None;
        }

        let mut me = self;
        let key = LuaRead::lua_read_at_position(&mut me, -2).ok();
        let value = LuaRead::lua_read_at_position(&mut me, -1).ok();

        // removing the value, leaving only the key on the top of the stack
        unsafe { ffi::lua_pop(me.table.as_mut_lua().0, 1) };

        //
        if key.is_none() || value.is_none() {
            Some(None)
        } else {
            Some(Some((key.unwrap(), value.unwrap())))
        }
    }
}

impl<'t, L, K, V> Drop for LuaTableIterator<'t, L, K, V> where L: AsMutLua + 't {
    fn drop(&mut self) {
        if !self.finished {
            unsafe { ffi::lua_pop(self.table.table.as_mut_lua().0, 1) }
        }
    }
}
<|endoftext|>


<commit_before>use crate::generate::build_tables::item::TokenSet;
use crate::generate::grammars::LexicalGrammar;
use crate::generate::nfa::{CharacterSet, NfaCursor, NfaTransition};
use hashbrown::HashSet;
use std::cmp::Ordering;
use std::fmt;

#[derive(Clone, Debug, Default, PartialEq, Eq)]
struct TokenConflictStatus {
    does_overlap: bool,
    does_match_valid_continuation: bool,
    does_match_separators: bool,
    matches_same_string: bool,
}

pub(crate) struct TokenConflictMap<'a> {
    n: usize,
    status_matrix: Vec<TokenConflictStatus>,
    starting_chars_by_index: Vec<CharacterSet>,
    following_chars_by_index: Vec<CharacterSet>,
    grammar: &'a LexicalGrammar,
}

impl<'a> TokenConflictMap<'a> {
    pub fn new(grammar: &'a LexicalGrammar, following_tokens: Vec<TokenSet>) -> Self {
        let mut cursor = NfaCursor::new(&grammar.nfa, Vec::new());
        let starting_chars = get_starting_chars(&mut cursor, grammar);
        let following_chars = get_following_chars(&starting_chars, following_tokens);

        let n = grammar.variables.len();
        let mut status_matrix = vec![TokenConflictStatus::default(); n * n];
        for i in 0..grammar.variables.len() {
            for j in 0..i {
                let status = compute_conflict_status(&mut cursor, grammar, &following_chars, i, j);
                status_matrix[matrix_index(n, i, j)] = status.0;
                status_matrix[matrix_index(n, j, i)] = status.1;
            }
        }

        TokenConflictMap {
            n,
            status_matrix,
            starting_chars_by_index: starting_chars,
            following_chars_by_index: following_chars,
            grammar,
        }
    }

    pub fn has_same_conflict_status(&self, a: usize, b: usize, other: usize) -> bool {
        let left = &self.status_matrix[matrix_index(self.n, a, other)];
        let right = &self.status_matrix[matrix_index(self.n, b, other)];
        left == right
    }

    pub fn does_match_same_string(&self, i: usize, j: usize) -> bool {
        self.status_matrix[matrix_index(self.n, i, j)].matches_same_string
    }

    pub fn does_conflict(&self, i: usize, j: usize) -> bool {
        let entry = &self.status_matrix[matrix_index(self.n, i, j)];
        entry.does_match_valid_continuation
            || entry.does_match_separators
            || entry.matches_same_string
    }

    pub fn does_overlap(&self, i: usize, j: usize) -> bool {
        self.status_matrix[matrix_index(self.n, i, j)].does_overlap
    }

    pub fn prefer_token(grammar: &LexicalGrammar, left: (i32, usize), right: (i32, usize)) -> bool {
        if left.0 > right.0 {
            return true;
        } else if left.0 < right.0 {
            return false;
        }

        match grammar.variables[left.1]
            .implicit_precedence
            .cmp(&grammar.variables[right.1].implicit_precedence)
        {
            Ordering::Less => false,
            Ordering::Greater => true,
            Ordering::Equal => left.1 < right.1,
        }
    }

    pub fn prefer_transition(
        grammar: &LexicalGrammar,
        t: &NfaTransition,
        completed_id: usize,
        completed_precedence: i32,
        has_separator_transitions: bool,
    ) -> bool {
        if t.precedence < completed_precedence {
            return false;
        }
        if t.precedence == completed_precedence {
            if t.is_separator {
                return false;
            }
            if has_separator_transitions
                && grammar
                    .variable_indices_for_nfa_states(&t.states)
                    .position(|i| i == completed_id)
                    .is_none()
            {
                return false;
            }
        }
        true
    }
}

impl<'a> fmt::Debug for TokenConflictMap<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "TokenConflictMap {{\n")?;

        write!(f, "  starting_characters: {{\n")?;
        for i in 0..self.n {
            write!(f, "    {}: {:?},\n", i, self.starting_chars_by_index[i])?;
        }
        write!(f, "  }},\n")?;

        write!(f, "  following_characters: {{\n")?;
        for i in 0..self.n {
            write!(
                f,
                "    {:?}: {:?},\n",
                self.grammar.variables[i].name, self.following_chars_by_index[i]
            )?;
        }
        write!(f, "  }},\n")?;

        write!(f, "  status_matrix: {{\n")?;
        for i in 0..self.n {
            write!(f, "    {:?}: {{\n", self.grammar.variables[i].name)?;
            for j in 0..self.n {
                write!(
                    f,
                    "      {:?}: {:?},\n",
                    self.grammar.variables[j].name,
                    self.status_matrix[matrix_index(self.n, i, j)]
                )?;
            }
            write!(f, "    }},\n")?;
        }
        write!(f, "  }},")?;
        write!(f, "}}")?;
        Ok(())
    }
}

fn matrix_index(variable_count: usize, i: usize, j: usize) -> usize {
    variable_count * i + j
}

fn get_starting_chars(cursor: &mut NfaCursor, grammar: &LexicalGrammar) -> Vec<CharacterSet> {
    let mut result = Vec::with_capacity(grammar.variables.len());
    for variable in &grammar.variables {
        cursor.reset(vec![variable.start_state]);
        let mut all_chars = CharacterSet::empty();
        for (chars, _) in cursor.transition_chars() {
            all_chars = all_chars.add(chars);
        }
        result.push(all_chars);
    }
    result
}

fn get_following_chars(
    starting_chars: &Vec<CharacterSet>,
    following_tokens: Vec<TokenSet>,
) -> Vec<CharacterSet> {
    following_tokens
        .into_iter()
        .map(|following_tokens| {
            let mut chars = CharacterSet::empty();
            for token in following_tokens.iter() {
                if token.is_terminal() {
                    chars = chars.add(&starting_chars[token.index]);
                }
            }
            chars
        })
        .collect()
}

fn compute_conflict_status(
    cursor: &mut NfaCursor,
    grammar: &LexicalGrammar,
    following_chars: &Vec<CharacterSet>,
    i: usize,
    j: usize,
) -> (TokenConflictStatus, TokenConflictStatus) {
    let mut visited_state_sets = HashSet::new();
    let mut state_set_queue = vec![vec![
        grammar.variables[i].start_state,
        grammar.variables[j].start_state,
    ]];
    let mut result = (
        TokenConflictStatus::default(),
        TokenConflictStatus::default(),
    );

    while let Some(state_set) = state_set_queue.pop() {
        // Don't pursue states where there's no potential for conflict.
        if grammar.variable_indices_for_nfa_states(&state_set).count() > 1 {
            cursor.reset(state_set);
        } else {
            continue;
        }

        let mut completion = None;
        for (id, precedence) in cursor.completions() {
            if let Some((prev_id, prev_precedence)) = completion {
                if id == prev_id {
                    continue;
                }

                // Prefer tokens with higher precedence. For tokens with equal precedence,
                // prefer those listed earlier in the grammar.
                let preferred_id;
                if TokenConflictMap::prefer_token(
                    grammar,
                    (prev_precedence, prev_id),
                    (precedence, id),
                ) {
                    preferred_id = prev_id;
                } else {
                    preferred_id = id;
                    completion = Some((id, precedence));
                }

                if preferred_id == i {
                    result.0.matches_same_string = true;
                    result.0.does_overlap = true;
                } else {
                    result.1.matches_same_string = true;
                    result.1.does_overlap = true;
                }
            } else {
                completion = Some((id, precedence));
            }
        }

        let has_sep = cursor.transition_chars().any(|(_, sep)| sep);

        for transition in cursor.transitions() {
            let mut can_advance = true;
            if let Some((completed_id, completed_precedence)) = completion {
                let mut other_id = None;
                let mut successor_contains_completed_id = false;
                for variable_id in grammar.variable_indices_for_nfa_states(&transition.states) {
                    if variable_id == completed_id {
                        successor_contains_completed_id = true;
                        break;
                    } else {
                        other_id = Some(variable_id);
                    }
                }

                if let (Some(other_id), false) = (other_id, successor_contains_completed_id) {
                    let preferred_id = if TokenConflictMap::prefer_transition(
                        grammar,
                        &transition,
                        completed_id,
                        completed_precedence,
                        has_sep,
                    ) {
                        can_advance = true;
                        other_id
                    } else {
                        completed_id
                    };

                    if preferred_id == i {
                        result.0.does_overlap = true;
                        if transition.characters.does_intersect(&following_chars[j]) {
                            result.0.does_match_valid_continuation = true;
                        }
                        if transition.is_separator || has_sep {
                            result.0.does_match_separators = true;
                        }
                    } else {
                        result.1.does_overlap = true;
                        if transition.characters.does_intersect(&following_chars[i]) {
                            result.1.does_match_valid_continuation = true;
                        }
                    }
                }
            }

            if can_advance && visited_state_sets.insert(transition.states.clone()) {
                state_set_queue.push(transition.states);
            }
        }
    }
    result
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::generate::grammars::{Variable, VariableType};
    use crate::generate::prepare_grammar::{expand_tokens, ExtractedLexicalGrammar};
    use crate::generate::rules::{Rule, Symbol};

    #[test]
    fn test_starting_characters() {
        let grammar = expand_tokens(ExtractedLexicalGrammar {
            separators: Vec::new(),
            variables: vec![
                Variable {
                    name: "token_0".to_string(),
                    kind: VariableType::Named,
                    rule: Rule::pattern("[a-f]1|0x\\d"),
                },
                Variable {
                    name: "token_1".to_string(),
                    kind: VariableType::Named,
                    rule: Rule::pattern("d*ef"),
                },
            ],
        })
        .unwrap();

        let token_map = TokenConflictMap::new(&grammar, Vec::new());

        assert_eq!(
            token_map.starting_chars_by_index[0],
            CharacterSet::empty().add_range('a', 'f').add_char('0')
        );
        assert_eq!(
            token_map.starting_chars_by_index[1],
            CharacterSet::empty().add_range('d', 'e')
        );
    }

    #[test]
    fn test_token_conflicts() {
        let grammar = expand_tokens(ExtractedLexicalGrammar {
            separators: Vec::new(),
            variables: vec![
                Variable {
                    name: "in".to_string(),
                    kind: VariableType::Named,
                    rule: Rule::string("in"),
                },
                Variable {
                    name: "identifier".to_string(),
                    kind: VariableType::Named,
                    rule: Rule::pattern("\\w+"),
                },
                Variable {
                    name: "instanceof".to_string(),
                    kind: VariableType::Named,
                    rule: Rule::string("instanceof"),
                },
            ],
        })
        .unwrap();

        let var = |name| index_of_var(&grammar, name);

        let token_map = TokenConflictMap::new(
            &grammar,
            vec![
                [Symbol::terminal(var("identifier"))]
                    .iter()
                    .cloned()
                    .collect(),
                [Symbol::terminal(var("in"))].iter().cloned().collect(),
                [Symbol::terminal(var("identifier"))]
                    .iter()
                    .cloned()
                    .collect(),
            ],
        );

        // Given the string "in", the `in` token is preferred over the `identifier` token
        assert!(token_map.does_match_same_string(var("in"), var("identifier")));
        assert!(!token_map.does_match_same_string(var("identifier"), var("in")));

        // Depending on what character follows, the string "in" may be treated as part of an
        // `identifier` token.
        assert!(token_map.does_conflict(var("identifier"), var("in")));

        // Depending on what character follows, the string "instanceof" may be treated as part of
        // an `identifier` token.
        assert!(token_map.does_conflict(var("identifier"), var("instanceof")));
        assert!(token_map.does_conflict(var("instanceof"), var("in")));
    }

    fn index_of_var(grammar: &LexicalGrammar, name: &str) -> usize {
        grammar
            .variables
            .iter()
            .position(|v| v.name == name)
            .unwrap()
    }
}
<commit_msg>Fix another token conflict detection bug<commit_after>use crate::generate::build_tables::item::TokenSet;
use crate::generate::grammars::LexicalGrammar;
use crate::generate::nfa::{CharacterSet, NfaCursor, NfaTransition};
use hashbrown::HashSet;
use std::cmp::Ordering;
use std::fmt;

#[derive(Clone, Debug, Default, PartialEq, Eq)]
struct TokenConflictStatus {
    does_overlap: bool,
    does_match_valid_continuation: bool,
    does_match_separators: bool,
    matches_same_string: bool,
}

pub(crate) struct TokenConflictMap<'a> {
    n: usize,
    status_matrix: Vec<TokenConflictStatus>,
    starting_chars_by_index: Vec<CharacterSet>,
    following_chars_by_index: Vec<CharacterSet>,
    grammar: &'a LexicalGrammar,
}

impl<'a> TokenConflictMap<'a> {
    pub fn new(grammar: &'a LexicalGrammar, following_tokens: Vec<TokenSet>) -> Self {
        let mut cursor = NfaCursor::new(&grammar.nfa, Vec::new());
        let starting_chars = get_starting_chars(&mut cursor, grammar);
        let following_chars = get_following_chars(&starting_chars, following_tokens);

        let n = grammar.variables.len();
        let mut status_matrix = vec![TokenConflictStatus::default(); n * n];
        for i in 0..grammar.variables.len() {
            for j in 0..i {
                let status = compute_conflict_status(&mut cursor, grammar, &following_chars, i, j);
                status_matrix[matrix_index(n, i, j)] = status.0;
                status_matrix[matrix_index(n, j, i)] = status.1;
            }
        }

        TokenConflictMap {
            n,
            status_matrix,
            starting_chars_by_index: starting_chars,
            following_chars_by_index: following_chars,
            grammar,
        }
    }

    pub fn has_same_conflict_status(&self, a: usize, b: usize, other: usize) -> bool {
        let left = &self.status_matrix[matrix_index(self.n, a, other)];
        let right = &self.status_matrix[matrix_index(self.n, b, other)];
        left == right
    }

    pub fn does_match_same_string(&self, i: usize, j: usize) -> bool {
        self.status_matrix[matrix_index(self.n, i, j)].matches_same_string
    }

    pub fn does_conflict(&self, i: usize, j: usize) -> bool {
        let entry = &self.status_matrix[matrix_index(self.n, i, j)];
        entry.does_match_valid_continuation
            || entry.does_match_separators
            || entry.matches_same_string
    }

    pub fn does_overlap(&self, i: usize, j: usize) -> bool {
        self.status_matrix[matrix_index(self.n, i, j)].does_overlap
    }

    pub fn prefer_token(grammar: &LexicalGrammar, left: (i32, usize), right: (i32, usize)) -> bool {
        if left.0 > right.0 {
            return true;
        } else if left.0 < right.0 {
            return false;
        }

        match grammar.variables[left.1]
            .implicit_precedence
            .cmp(&grammar.variables[right.1].implicit_precedence)
        {
            Ordering::Less => false,
            Ordering::Greater => true,
            Ordering::Equal => left.1 < right.1,
        }
    }

    pub fn prefer_transition(
        grammar: &LexicalGrammar,
        t: &NfaTransition,
        completed_id: usize,
        completed_precedence: i32,
        has_separator_transitions: bool,
    ) -> bool {
        if t.precedence < completed_precedence {
            return false;
        }
        if t.precedence == completed_precedence {
            if t.is_separator {
                return false;
            }
            if has_separator_transitions
                && grammar
                    .variable_indices_for_nfa_states(&t.states)
                    .position(|i| i == completed_id)
                    .is_none()
            {
                return false;
            }
        }
        true
    }
}

impl<'a> fmt::Debug for TokenConflictMap<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "TokenConflictMap {{\n")?;

        write!(f, "  starting_characters: {{\n")?;
        for i in 0..self.n {
            write!(f, "    {}: {:?},\n", i, self.starting_chars_by_index[i])?;
        }
        write!(f, "  }},\n")?;

        write!(f, "  following_characters: {{\n")?;
        for i in 0..self.n {
            write!(
                f,
                "    {:?}: {:?},\n",
                self.grammar.variables[i].name, self.following_chars_by_index[i]
            )?;
        }
        write!(f, "  }},\n")?;

        write!(f, "  status_matrix: {{\n")?;
        for i in 0..self.n {
            write!(f, "    {:?}: {{\n", self.grammar.variables[i].name)?;
            for j in 0..self.n {
                write!(
                    f,
                    "      {:?}: {:?},\n",
                    self.grammar.variables[j].name,
                    self.status_matrix[matrix_index(self.n, i, j)]
                )?;
            }
            write!(f, "    }},\n")?;
        }
        write!(f, "  }},")?;
        write!(f, "}}")?;
        Ok(())
    }
}

fn matrix_index(variable_count: usize, i: usize, j: usize) -> usize {
    variable_count * i + j
}

fn get_starting_chars(cursor: &mut NfaCursor, grammar: &LexicalGrammar) -> Vec<CharacterSet> {
    let mut result = Vec::with_capacity(grammar.variables.len());
    for variable in &grammar.variables {
        cursor.reset(vec![variable.start_state]);
        let mut all_chars = CharacterSet::empty();
        for (chars, _) in cursor.transition_chars() {
            all_chars = all_chars.add(chars);
        }
        result.push(all_chars);
    }
    result
}

fn get_following_chars(
    starting_chars: &Vec<CharacterSet>,
    following_tokens: Vec<TokenSet>,
) -> Vec<CharacterSet> {
    following_tokens
        .into_iter()
        .map(|following_tokens| {
            let mut chars = CharacterSet::empty();
            for token in following_tokens.iter() {
                if token.is_terminal() {
                    chars = chars.add(&starting_chars[token.index]);
                }
            }
            chars
        })
        .collect()
}

fn compute_conflict_status(
    cursor: &mut NfaCursor,
    grammar: &LexicalGrammar,
    following_chars: &Vec<CharacterSet>,
    i: usize,
    j: usize,
) -> (TokenConflictStatus, TokenConflictStatus) {
    let mut visited_state_sets = HashSet::new();
    let mut state_set_queue = vec![vec![
        grammar.variables[i].start_state,
        grammar.variables[j].start_state,
    ]];
    let mut result = (
        TokenConflictStatus::default(),
        TokenConflictStatus::default(),
    );

    while let Some(state_set) = state_set_queue.pop() {
        // Don't pursue states where there's no potential for conflict.
        if grammar.variable_indices_for_nfa_states(&state_set).count() > 1 {
            cursor.reset(state_set);
        } else {
            continue;
        }

        let mut completion = None;
        for (id, precedence) in cursor.completions() {
            if let Some((prev_id, prev_precedence)) = completion {
                if id == prev_id {
                    continue;
                }

                // Prefer tokens with higher precedence. For tokens with equal precedence,
                // prefer those listed earlier in the grammar.
                let preferred_id;
                if TokenConflictMap::prefer_token(
                    grammar,
                    (prev_precedence, prev_id),
                    (precedence, id),
                ) {
                    preferred_id = prev_id;
                } else {
                    preferred_id = id;
                    completion = Some((id, precedence));
                }

                if preferred_id == i {
                    result.0.matches_same_string = true;
                    result.0.does_overlap = true;
                } else {
                    result.1.matches_same_string = true;
                    result.1.does_overlap = true;
                }
            } else {
                completion = Some((id, precedence));
            }
        }

        let has_sep = cursor.transition_chars().any(|(_, sep)| sep);

        for transition in cursor.transitions() {
            let mut can_advance = true;
            if let Some((completed_id, completed_precedence)) = completion {
                let mut other_id = None;
                let mut successor_contains_completed_id = false;
                for variable_id in grammar.variable_indices_for_nfa_states(&transition.states) {
                    if variable_id == completed_id {
                        successor_contains_completed_id = true;
                        break;
                    } else {
                        other_id = Some(variable_id);
                    }
                }

                if let (Some(other_id), false) = (other_id, successor_contains_completed_id) {
                    let preferred_id = if TokenConflictMap::prefer_transition(
                        grammar,
                        &transition,
                        completed_id,
                        completed_precedence,
                        has_sep,
                    ) {
                        can_advance = true;
                        other_id
                    } else {
                        completed_id
                    };

                    if preferred_id == i {
                        result.0.does_overlap = true;
                        if transition.characters.does_intersect(&following_chars[j]) {
                            result.0.does_match_valid_continuation = true;
                        }
                        if transition.is_separator || has_sep {
                            result.0.does_match_separators = true;
                        }
                    } else {
                        result.1.does_overlap = true;
                        if transition.characters.does_intersect(&following_chars[i]) {
                            result.1.does_match_valid_continuation = true;
                        }
                        if transition.is_separator || has_sep {
                            result.1.does_match_separators = true;
                        }
                    }
                }
            }

            if can_advance && visited_state_sets.insert(transition.states.clone()) {
                state_set_queue.push(transition.states);
            }
        }
    }
    result
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::generate::grammars::{Variable, VariableType};
    use crate::generate::prepare_grammar::{expand_tokens, ExtractedLexicalGrammar};
    use crate::generate::rules::{Rule, Symbol};

    #[test]
    fn test_starting_characters() {
        let grammar = expand_tokens(ExtractedLexicalGrammar {
            separators: Vec::new(),
            variables: vec![
                Variable {
                    name: "token_0".to_string(),
                    kind: VariableType::Named,
                    rule: Rule::pattern("[a-f]1|0x\\d"),
                },
                Variable {
                    name: "token_1".to_string(),
                    kind: VariableType::Named,
                    rule: Rule::pattern("d*ef"),
                },
            ],
        })
        .unwrap();

        let token_map = TokenConflictMap::new(&grammar, Vec::new());

        assert_eq!(
            token_map.starting_chars_by_index[0],
            CharacterSet::empty().add_range('a', 'f').add_char('0')
        );
        assert_eq!(
            token_map.starting_chars_by_index[1],
            CharacterSet::empty().add_range('d', 'e')
        );
    }

    #[test]
    fn test_token_conflicts() {
        let grammar = expand_tokens(ExtractedLexicalGrammar {
            separators: Vec::new(),
            variables: vec![
                Variable {
                    name: "in".to_string(),
                    kind: VariableType::Named,
                    rule: Rule::string("in"),
                },
                Variable {
                    name: "identifier".to_string(),
                    kind: VariableType::Named,
                    rule: Rule::pattern("\\w+"),
                },
                Variable {
                    name: "instanceof".to_string(),
                    kind: VariableType::Named,
                    rule: Rule::string("instanceof"),
                },
            ],
        })
        .unwrap();

        let var = |name| index_of_var(&grammar, name);

        let token_map = TokenConflictMap::new(
            &grammar,
            vec![
                [Symbol::terminal(var("identifier"))]
                    .iter()
                    .cloned()
                    .collect(),
                [Symbol::terminal(var("in"))].iter().cloned().collect(),
                [Symbol::terminal(var("identifier"))]
                    .iter()
                    .cloned()
                    .collect(),
            ],
        );

        // Given the string "in", the `in` token is preferred over the `identifier` token
        assert!(token_map.does_match_same_string(var("in"), var("identifier")));
        assert!(!token_map.does_match_same_string(var("identifier"), var("in")));

        // Depending on what character follows, the string "in" may be treated as part of an
        // `identifier` token.
        assert!(token_map.does_conflict(var("identifier"), var("in")));

        // Depending on what character follows, the string "instanceof" may be treated as part of
        // an `identifier` token.
        assert!(token_map.does_conflict(var("identifier"), var("instanceof")));
        assert!(token_map.does_conflict(var("instanceof"), var("in")));
    }

    fn index_of_var(grammar: &LexicalGrammar, name: &str) -> usize {
        grammar
            .variables
            .iter()
            .position(|v| v.name == name)
            .unwrap()
    }
}
<|endoftext|>


<commit_before>use std::mem;

// Borrowing encoding of paramaterized types from
// https://github.com/rust-lang/rfcs/blob/master/text/0195-associated-items.md#encoding-higher-kinded-types

pub trait RefType<'a> {
    type Ref;
}

pub type Ref<'a,T> where T: RefType<'a> = T::Ref;

#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)]
pub enum PushResult<T> {
    WillMatch,
    MightMatch,
    WontMatch,
    MatchedAll,
    MatchedSome(T),
}

pub trait Listener<T> where T: for<'a> RefType<'a> {
    fn push<'a,'b>(&'a mut self, value: Ref<'b,T>) -> PushResult<Ref<'b,T>>;
    fn done(&mut self);
}

pub struct StrRef;

impl<'a> RefType<'a> for StrRef {
    type Ref = &'a str;
}

enum WhitespaceParserState {
    Beginning,
    Middle(String),
    End,
}

struct WhitespaceParser<T> {
    downstream: T,
    state: WhitespaceParserState,
}

impl<T> From<T> for WhitespaceParser<T> {
    fn from(downstream: T) -> WhitespaceParser<T> {
        WhitespaceParser{ downstream: downstream, state: WhitespaceParserState::Beginning }
    }
}

impl<T> Listener<StrRef> for WhitespaceParser<T> where T: Listener<StrRef> {
    fn push<'a,'b>(&'a mut self, string: &'b str) -> PushResult<&'b str> {
        match mem::replace(&mut self.state, WhitespaceParserState::End) {
            WhitespaceParserState::Beginning => {
                match string.chars().next() {
                    None => {
                        self.state = WhitespaceParserState::Beginning;
                        PushResult::MightMatch
                    },
                    Some(ch) if ch.is_whitespace() => {
                        let len = ch.len_utf8();
                        match string[len..].find(|ch: char| !ch.is_whitespace()) {
                            None => {
                                self.state = WhitespaceParserState::Middle(String::from(string));
                                PushResult::WillMatch
                            },
                            Some(index) => {
                                self.downstream.push(&string[0..index+len]);
                                if string.len() == index+len {
                                    PushResult::MatchedAll
                                } else {
                                    PushResult::MatchedSome(&string[index+len..])
                                }
                            }
                        }
                    },
                    Some(_) => PushResult::WontMatch,
                }
            },
            WhitespaceParserState::Middle(mut buffer) => {
                match string.find(|ch: char| !ch.is_whitespace()) {
                    None => {
                        buffer.push_str(string);
                        self.state = WhitespaceParserState::Middle(buffer);
                        PushResult::WillMatch
                    },
                    Some(index) => {
                        buffer.push_str(&string[0..index]);
                        self.downstream.push(&*buffer);
                        if string.len() == index {
                            PushResult::MatchedAll
                        } else {
                            PushResult::MatchedSome(&string[index..])
                        }
                    }
                }
            },
            WhitespaceParserState::End => PushResult::MatchedSome(string),
        }
    }
    fn done(&mut self) {
        match mem::replace(&mut self.state, WhitespaceParserState::End) {
            WhitespaceParserState::Middle(buffer) => {
                self.downstream.push(&*buffer);
            },
            _              => (),
        }
        self.downstream.done();
    }
}

impl Listener<StrRef> for String {
    fn push<'a,'b>(&'a mut self, string: &'b str) -> PushResult<&'b str> {
        self.push_str(string);
        PushResult::MatchedAll
    }
    fn done(&mut self) {
    }
}

#[test]
fn test_whitespace() {
    let buffer = String::from("");
    let mut parser = WhitespaceParser::from(buffer);
    assert_eq!(parser.push(" \r\n\t"), PushResult::WillMatch);
    assert_eq!(parser.downstream, "");
    assert_eq!(parser.push(" stuff"), PushResult::MatchedSome("stuff"));
    assert_eq!(parser.downstream, " \r\n\t ");
}
<commit_msg>Renamed Listener to Matcher.<commit_after>use std::mem;

// ----------- Types for streaming -------------

// Borrowing encoding of paramaterized types from
// https://github.com/rust-lang/rfcs/blob/master/text/0195-associated-items.md#encoding-higher-kinded-types

pub trait RefType<'a> {
    type Ref;
}

pub type Ref<'a,T> where T: RefType<'a> = T::Ref;

#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)]
pub enum MatchResult<T> {
    WillMatch,
    MightMatch,
    WontMatch,
    MatchedAll,
    MatchedSome(T),
}

pub trait Matcher<T> where T: for<'a> RefType<'a> {
    fn push<'a,'b>(&'a mut self, value: Ref<'b,T>) -> MatchResult<Ref<'b,T>>;
    fn done(&mut self);
}

// ----------- Types for streaming -------------

pub struct StrRef;

impl<'a> RefType<'a> for StrRef {
    type Ref = &'a str;
}

enum WhitespaceParserState {
    Beginning,
    Middle(String),
    End,
}

struct WhitespaceParser<T> {
    downstream: T,
    state: WhitespaceParserState,
}

impl<T> From<T> for WhitespaceParser<T> {
    fn from(downstream: T) -> WhitespaceParser<T> {
        WhitespaceParser{ downstream: downstream, state: WhitespaceParserState::Beginning }
    }
}

impl<T> Matcher<StrRef> for WhitespaceParser<T> where T: Matcher<StrRef> {
    fn push<'a,'b>(&'a mut self, string: &'b str) -> MatchResult<&'b str> {
        match mem::replace(&mut self.state, WhitespaceParserState::End) {
            WhitespaceParserState::Beginning => {
                match string.chars().next() {
                    None => {
                        self.state = WhitespaceParserState::Beginning;
                        MatchResult::MightMatch
                    },
                    Some(ch) if ch.is_whitespace() => {
                        let len = ch.len_utf8();
                        match string[len..].find(|ch: char| !ch.is_whitespace()) {
                            None => {
                                self.state = WhitespaceParserState::Middle(String::from(string));
                                MatchResult::WillMatch
                            },
                            Some(index) => {
                                self.downstream.push(&string[0..index+len]);
                                if string.len() == index+len {
                                    MatchResult::MatchedAll
                                } else {
                                    MatchResult::MatchedSome(&string[index+len..])
                                }
                            }
                        }
                    },
                    Some(_) => MatchResult::WontMatch,
                }
            },
            WhitespaceParserState::Middle(mut buffer) => {
                match string.find(|ch: char| !ch.is_whitespace()) {
                    None => {
                        buffer.push_str(string);
                        self.state = WhitespaceParserState::Middle(buffer);
                        MatchResult::WillMatch
                    },
                    Some(index) => {
                        buffer.push_str(&string[0..index]);
                        self.downstream.push(&*buffer);
                        if string.len() == index {
                            MatchResult::MatchedAll
                        } else {
                            MatchResult::MatchedSome(&string[index..])
                        }
                    }
                }
            },
            WhitespaceParserState::End => MatchResult::MatchedSome(string),
        }
    }
    fn done(&mut self) {
        match mem::replace(&mut self.state, WhitespaceParserState::End) {
            WhitespaceParserState::Middle(buffer) => {
                self.downstream.push(&*buffer);
            },
            _              => (),
        }
        self.downstream.done();
    }
}

impl Matcher<StrRef> for String {
    fn push<'a,'b>(&'a mut self, string: &'b str) -> MatchResult<&'b str> {
        self.push_str(string);
        MatchResult::MatchedAll
    }
    fn done(&mut self) {
    }
}

#[test]
fn test_whitespace() {
    let buffer = String::from("");
    let mut parser = WhitespaceParser::from(buffer);
    assert_eq!(parser.push(" \r\n\t"), MatchResult::WillMatch);
    assert_eq!(parser.downstream, "");
    assert_eq!(parser.push(" stuff"), MatchResult::MatchedSome("stuff"));
    assert_eq!(parser.downstream, " \r\n\t ");
}
<|endoftext|>


<commit_before>use crate::stable_hasher::{HashStable, StableHasher};
use crate::sync::{MappedReadGuard, ReadGuard, RwLock};

/// The `Steal` struct is intended to used as the value for a query.
/// Specifically, we sometimes have queries (*cough* MIR *cough*)
/// where we create a large, complex value that we want to iteratively
/// update (e.g., optimize). We could clone the value for each
/// optimization, but that'd be expensive. And yet we don't just want
/// to mutate it in place, because that would spoil the idea that
/// queries are these pure functions that produce an immutable value
/// (since if you did the query twice, you could observe the mutations).
/// So instead we have the query produce a `&'tcx Steal<mir::Body<'tcx>>`
/// (to be very specific). Now we can read from this
/// as much as we want (using `borrow()`), but you can also
/// `steal()`. Once you steal, any further attempt to read will panic.
/// Therefore, we know that -- assuming no ICE -- nobody is observing
/// the fact that the MIR was updated.
///
/// Obviously, whenever you have a query that yields a `Steal` value,
/// you must treat it with caution, and make sure that you know that
/// -- once the value is stolen -- it will never be read from again.
//
// FIXME(#41710): what is the best way to model linear queries?
pub struct Steal<T> {
    value: RwLock<Option<T>>,
}

impl<T> Steal<T> {
    pub fn new(value: T) -> Self {
        Steal { value: RwLock::new(Some(value)) }
    }

    pub fn borrow(&self) -> MappedReadGuard<'_, T> {
        ReadGuard::map(self.value.borrow(), |opt| match *opt {
            None => panic!("attempted to read from stolen value"),
            Some(ref v) => v,
        })
    }

    pub fn steal(&self) -> T {
        let value_ref = &mut *self.value.try_write().expect("stealing value which is locked");
        let value = value_ref.take();
        value.expect("attempt to read from stolen value")
    }
}

impl<CTX, T: HashStable<CTX>> HashStable<CTX> for Steal<T> {
    fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
        self.borrow().hash_stable(hcx, hasher);
    }
}
<commit_msg>Add track_caller to .steal()<commit_after>use crate::stable_hasher::{HashStable, StableHasher};
use crate::sync::{MappedReadGuard, ReadGuard, RwLock};

/// The `Steal` struct is intended to used as the value for a query.
/// Specifically, we sometimes have queries (*cough* MIR *cough*)
/// where we create a large, complex value that we want to iteratively
/// update (e.g., optimize). We could clone the value for each
/// optimization, but that'd be expensive. And yet we don't just want
/// to mutate it in place, because that would spoil the idea that
/// queries are these pure functions that produce an immutable value
/// (since if you did the query twice, you could observe the mutations).
/// So instead we have the query produce a `&'tcx Steal<mir::Body<'tcx>>`
/// (to be very specific). Now we can read from this
/// as much as we want (using `borrow()`), but you can also
/// `steal()`. Once you steal, any further attempt to read will panic.
/// Therefore, we know that -- assuming no ICE -- nobody is observing
/// the fact that the MIR was updated.
///
/// Obviously, whenever you have a query that yields a `Steal` value,
/// you must treat it with caution, and make sure that you know that
/// -- once the value is stolen -- it will never be read from again.
//
// FIXME(#41710): what is the best way to model linear queries?
pub struct Steal<T> {
    value: RwLock<Option<T>>,
}

impl<T> Steal<T> {
    pub fn new(value: T) -> Self {
        Steal { value: RwLock::new(Some(value)) }
    }

    #[track_caller]
    pub fn borrow(&self) -> MappedReadGuard<'_, T> {
        ReadGuard::map(self.value.borrow(), |opt| match *opt {
            None => panic!("attempted to read from stolen value"),
            Some(ref v) => v,
        })
    }

    #[track_caller]
    pub fn steal(&self) -> T {
        let value_ref = &mut *self.value.try_write().expect("stealing value which is locked");
        let value = value_ref.take();
        value.expect("attempt to steal from stolen value")
    }
}

impl<CTX, T: HashStable<CTX>> HashStable<CTX> for Steal<T> {
    fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
        self.borrow().hash_stable(hcx, hasher);
    }
}
<|endoftext|>