<commit_before>use crate::decoder::Metadata;
use crate::schema::*;

use rustc::hir::def_id::{DefId, DefIndex};
use rustc_serialize::{Encodable, opaque::Encoder};
use std::convert::TryInto;
use std::marker::PhantomData;
use std::num::NonZeroUsize;
use log::debug;

/// Helper trait, for encoding to, and decoding from, a fixed number of bytes.
/// Used mainly for Lazy positions and lengths.
/// Unchecked invariant: `Self::default()` should encode as `[0; BYTE_LEN]`,
/// but this has no impact on safety.
crate trait FixedSizeEncoding: Default {
    const BYTE_LEN: usize;

    // FIXME(eddyb) convert to and from `[u8; Self::BYTE_LEN]` instead,
    // once that starts being allowed by the compiler (i.e. lazy normalization).
    fn from_bytes(b: &[u8]) -> Self;
    fn write_to_bytes(self, b: &mut [u8]);

    // FIXME(eddyb) make these generic functions, or at least defaults here.
    // (same problem as above, needs `[u8; Self::BYTE_LEN]`)
    // For now, a macro (`fixed_size_encoding_byte_len_and_defaults`) is used.
    fn maybe_read_from_bytes_at(b: &[u8], i: usize) -> Option<Self>;
    fn write_to_bytes_at(self, b: &mut [u8], i: usize);
}

// HACK(eddyb) this shouldn't be needed (see comments on the methods above).
macro_rules! fixed_size_encoding_byte_len_and_defaults {
    ($byte_len:expr) => {
        const BYTE_LEN: usize = $byte_len;
        fn maybe_read_from_bytes_at(b: &[u8], i: usize) -> Option<Self> {
            const BYTE_LEN: usize = $byte_len;
            // HACK(eddyb) ideally this would be done with fully safe code,
            // but slicing `[u8]` with `i * N..` is optimized worse, due to the
            // possibility of `i * N` overflowing, than indexing `[[u8; N]]`.
            let b = unsafe {
                std::slice::from_raw_parts(
                    b.as_ptr() as *const [u8; BYTE_LEN],
                    b.len() / BYTE_LEN,
                )
            };
            b.get(i).map(|b| FixedSizeEncoding::from_bytes(b))
        }
        fn write_to_bytes_at(self, b: &mut [u8], i: usize) {
            const BYTE_LEN: usize = $byte_len;
            // HACK(eddyb) ideally this would be done with fully safe code,
            // see similar comment in `read_from_bytes_at` for why it can't yet.
            let b = unsafe {
                std::slice::from_raw_parts_mut(
                    b.as_mut_ptr() as *mut [u8; BYTE_LEN],
                    b.len() / BYTE_LEN,
                )
            };
            self.write_to_bytes(&mut b[i]);
        }
    }
}

impl FixedSizeEncoding for u32 {
    fixed_size_encoding_byte_len_and_defaults!(4);

    fn from_bytes(b: &[u8]) -> Self {
        let mut bytes = [0; Self::BYTE_LEN];
        bytes.copy_from_slice(&b[..Self::BYTE_LEN]);
        Self::from_le_bytes(bytes)
    }

    fn write_to_bytes(self, b: &mut [u8]) {
        b[..Self::BYTE_LEN].copy_from_slice(&self.to_le_bytes());
    }
}

// NOTE(eddyb) there could be an impl for `usize`, which would enable a more
// generic `Lazy<T>` impl, but in the general case we might not need / want to
// fit every `usize` in `u32`.
impl<T: Encodable> FixedSizeEncoding for Option<Lazy<T>> {
    fixed_size_encoding_byte_len_and_defaults!(u32::BYTE_LEN);

    fn from_bytes(b: &[u8]) -> Self {
        Some(Lazy::from_position(NonZeroUsize::new(u32::from_bytes(b) as usize)?))
    }

    fn write_to_bytes(self, b: &mut [u8]) {
        let position = self.map_or(0, |lazy| lazy.position.get());
        let position: u32 = position.try_into().unwrap();

        position.write_to_bytes(b)
    }
}

impl<T: Encodable> FixedSizeEncoding for Option<Lazy<[T]>> {
    fixed_size_encoding_byte_len_and_defaults!(u32::BYTE_LEN * 2);

    fn from_bytes(b: &[u8]) -> Self {
        Some(Lazy::from_position_and_meta(
            <Option<Lazy<T>>>::from_bytes(b)?.position,
            u32::from_bytes(&b[u32::BYTE_LEN..]) as usize,
        ))
    }

    fn write_to_bytes(self, b: &mut [u8]) {
        self.map(|lazy| Lazy::<T>::from_position(lazy.position))
            .write_to_bytes(b);

        let len = self.map_or(0, |lazy| lazy.meta);
        let len: u32 = len.try_into().unwrap();

        len.write_to_bytes(&mut b[u32::BYTE_LEN..]);
    }
}

/// Random-access table, similar to `Vec<Option<T>>`, but without requiring
/// encoding or decoding all the values eagerly and in-order.
// FIXME(eddyb) replace `Vec` with `[_]` here, such that `Box<Table<T>>` would be used
// when building it, and `Lazy<Table<T>>` or `&Table<T>` when reading it.
// (not sure if that is possible given that the `Vec` is being resized now)
crate struct Table<T> where Option<T>: FixedSizeEncoding {
    // FIXME(eddyb) store `[u8; <Option<T>>::BYTE_LEN]` instead of `u8` in `Vec`,
    // once that starts being allowed by the compiler (i.e. lazy normalization).
    bytes: Vec<u8>,
    _marker: PhantomData<T>,
}

impl<T> Default for Table<T> where Option<T>: FixedSizeEncoding {
    fn default() -> Self {
        Table {
            bytes: vec![],
            _marker: PhantomData,
        }
    }
}

impl<T> Table<T> where Option<T>: FixedSizeEncoding {
    crate fn set(&mut self, i: usize, value: T) {
        let needed = (i + 1) * <Option<T>>::BYTE_LEN;
        if self.bytes.len() < needed {
            self.bytes.resize(needed, 0);
        }
        Some(value).write_to_bytes_at(&mut self.bytes, i);
    }

    crate fn encode(&self, buf: &mut Encoder) -> Lazy<Self> {
        let pos = buf.position();
        buf.emit_raw_bytes(&self.bytes);
        Lazy::from_position_and_meta(
            NonZeroUsize::new(pos as usize).unwrap(),
            self.bytes.len(),
        )
    }
}

impl<T> LazyMeta for Table<T> where Option<T>: FixedSizeEncoding {
    type Meta = usize;

    fn min_size(len: usize) -> usize {
        len
    }
}

impl<T> Lazy<Table<T>> where Option<T>: FixedSizeEncoding {
    /// Given the metadata, extract out the value at a particular index (if any).
    #[inline(never)]
    crate fn get<'a, 'tcx, M: Metadata<'a, 'tcx>>(
        &self,
        metadata: M,
        i: usize,
    ) -> Option<T> {
        debug!("Table::lookup: index={:?} len={:?}", i, self.meta);

        let bytes = &metadata.raw_bytes()[self.position.get()..][..self.meta];
        <Option<T>>::maybe_read_from_bytes_at(bytes, i)?
    }
}

/// Per-definition table, similar to `Table` but keyed on `DefIndex`.
// FIXME(eddyb) replace by making `Table` behave like `IndexVec`,
// and by using `newtype_index!` to define `DefIndex`.
crate struct PerDefTable<T>(Table<T>) where Option<T>: FixedSizeEncoding;

impl<T> Default for PerDefTable<T> where Option<T>: FixedSizeEncoding {
    fn default() -> Self {
        PerDefTable(Table::default())
    }
}

impl<T> PerDefTable<T> where Option<T>: FixedSizeEncoding {
    crate fn set(&mut self, def_id: DefId, value: T) {
        assert!(def_id.is_local());
        self.0.set(def_id.index.index(), value);
    }

    crate fn encode(&self, buf: &mut Encoder) -> Lazy<Self> {
        let lazy = self.0.encode(buf);
        Lazy::from_position_and_meta(lazy.position, lazy.meta)
    }
}

impl<T> LazyMeta for PerDefTable<T> where Option<T>: FixedSizeEncoding {
    type Meta = <Table<T> as LazyMeta>::Meta;

    fn min_size(meta: Self::Meta) -> usize {
        Table::<T>::min_size(meta)
    }
}

impl<T> Lazy<PerDefTable<T>> where Option<T>: FixedSizeEncoding {
    fn as_table(&self) -> Lazy<Table<T>> {
        Lazy::from_position_and_meta(self.position, self.meta)
    }

    /// Given the metadata, extract out the value at a particular DefIndex (if any).
    #[inline(never)]
    crate fn get<'a, 'tcx, M: Metadata<'a, 'tcx>>(
        &self,
        metadata: M,
        def_index: DefIndex,
    ) -> Option<T> {
        self.as_table().get(metadata, def_index.index())
    }
}
<commit_msg>rustc_metadata: address some review comments.<commit_after>use crate::decoder::Metadata;
use crate::schema::*;

use rustc::hir::def_id::{DefId, DefIndex};
use rustc_serialize::{Encodable, opaque::Encoder};
use std::convert::TryInto;
use std::marker::PhantomData;
use std::num::NonZeroUsize;
use log::debug;

/// Helper trait, for encoding to, and decoding from, a fixed number of bytes.
/// Used mainly for Lazy positions and lengths.
/// Unchecked invariant: `Self::default()` should encode as `[0; BYTE_LEN]`,
/// but this has no impact on safety.
crate trait FixedSizeEncoding: Default {
    const BYTE_LEN: usize;

    // FIXME(eddyb) convert to and from `[u8; Self::BYTE_LEN]` instead,
    // once that starts being allowed by the compiler (i.e. lazy normalization).
    fn from_bytes(b: &[u8]) -> Self;
    fn write_to_bytes(self, b: &mut [u8]);

    // FIXME(eddyb) make these generic functions, or at least defaults here.
    // (same problem as above, needs `[u8; Self::BYTE_LEN]`)
    // For now, a macro (`fixed_size_encoding_byte_len_and_defaults`) is used.

    /// Read a `Self` value (encoded as `Self::BYTE_LEN` bytes),
    /// from `&b[i * Self::BYTE_LEN..]`, returning `None` if `i`
    /// is not in bounds, or `Some(Self::from_bytes(...))` otherwise.
    fn maybe_read_from_bytes_at(b: &[u8], i: usize) -> Option<Self>;
    /// Write a `Self` value (encoded as `Self::BYTE_LEN` bytes),
    /// at `&mut b[i * Self::BYTE_LEN..]`, using `Self::write_to_bytes`.
    fn write_to_bytes_at(self, b: &mut [u8], i: usize);
}

// HACK(eddyb) this shouldn't be needed (see comments on the methods above).
macro_rules! fixed_size_encoding_byte_len_and_defaults {
    ($byte_len:expr) => {
        const BYTE_LEN: usize = $byte_len;
        fn maybe_read_from_bytes_at(b: &[u8], i: usize) -> Option<Self> {
            const BYTE_LEN: usize = $byte_len;
            // HACK(eddyb) ideally this would be done with fully safe code,
            // but slicing `[u8]` with `i * N..` is optimized worse, due to the
            // possibility of `i * N` overflowing, than indexing `[[u8; N]]`.
            let b = unsafe {
                std::slice::from_raw_parts(
                    b.as_ptr() as *const [u8; BYTE_LEN],
                    b.len() / BYTE_LEN,
                )
            };
            b.get(i).map(|b| FixedSizeEncoding::from_bytes(b))
        }
        fn write_to_bytes_at(self, b: &mut [u8], i: usize) {
            const BYTE_LEN: usize = $byte_len;
            // HACK(eddyb) ideally this would be done with fully safe code,
            // see similar comment in `read_from_bytes_at` for why it can't yet.
            let b = unsafe {
                std::slice::from_raw_parts_mut(
                    b.as_mut_ptr() as *mut [u8; BYTE_LEN],
                    b.len() / BYTE_LEN,
                )
            };
            self.write_to_bytes(&mut b[i]);
        }
    }
}

impl FixedSizeEncoding for u32 {
    fixed_size_encoding_byte_len_and_defaults!(4);

    fn from_bytes(b: &[u8]) -> Self {
        let mut bytes = [0; Self::BYTE_LEN];
        bytes.copy_from_slice(&b[..Self::BYTE_LEN]);
        Self::from_le_bytes(bytes)
    }

    fn write_to_bytes(self, b: &mut [u8]) {
        b[..Self::BYTE_LEN].copy_from_slice(&self.to_le_bytes());
    }
}

// NOTE(eddyb) there could be an impl for `usize`, which would enable a more
// generic `Lazy<T>` impl, but in the general case we might not need / want to
// fit every `usize` in `u32`.
impl<T: Encodable> FixedSizeEncoding for Option<Lazy<T>> {
    fixed_size_encoding_byte_len_and_defaults!(u32::BYTE_LEN);

    fn from_bytes(b: &[u8]) -> Self {
        Some(Lazy::from_position(NonZeroUsize::new(u32::from_bytes(b) as usize)?))
    }

    fn write_to_bytes(self, b: &mut [u8]) {
        let position = self.map_or(0, |lazy| lazy.position.get());
        let position: u32 = position.try_into().unwrap();

        position.write_to_bytes(b)
    }
}

impl<T: Encodable> FixedSizeEncoding for Option<Lazy<[T]>> {
    fixed_size_encoding_byte_len_and_defaults!(u32::BYTE_LEN * 2);

    fn from_bytes(b: &[u8]) -> Self {
        Some(Lazy::from_position_and_meta(
            <Option<Lazy<T>>>::from_bytes(b)?.position,
            u32::from_bytes(&b[u32::BYTE_LEN..]) as usize,
        ))
    }

    fn write_to_bytes(self, b: &mut [u8]) {
        self.map(|lazy| Lazy::<T>::from_position(lazy.position))
            .write_to_bytes(b);

        let len = self.map_or(0, |lazy| lazy.meta);
        let len: u32 = len.try_into().unwrap();

        len.write_to_bytes(&mut b[u32::BYTE_LEN..]);
    }
}

/// Random-access table (i.e. offeringconstant-time `get`/`set`), similar to
/// `Vec<Option<T>>`, but without requiring encoding or decoding all the values
/// eagerly and in-order.
/// A total of `(max_idx + 1) * <Option<T> as FixedSizeEncoding>::BYTE_LEN` bytes
/// are used for a table, where `max_idx` is the largest index passed to `set`.
// FIXME(eddyb) replace `Vec` with `[_]` here, such that `Box<Table<T>>` would be used
// when building it, and `Lazy<Table<T>>` or `&Table<T>` when reading it.
// (not sure if that is possible given that the `Vec` is being resized now)
crate struct Table<T> where Option<T>: FixedSizeEncoding {
    // FIXME(eddyb) store `[u8; <Option<T>>::BYTE_LEN]` instead of `u8` in `Vec`,
    // once that starts being allowed by the compiler (i.e. lazy normalization).
    bytes: Vec<u8>,
    _marker: PhantomData<T>,
}

impl<T> Default for Table<T> where Option<T>: FixedSizeEncoding {
    fn default() -> Self {
        Table {
            bytes: vec![],
            _marker: PhantomData,
        }
    }
}

impl<T> Table<T> where Option<T>: FixedSizeEncoding {
    crate fn set(&mut self, i: usize, value: T) {
        // FIXME(eddyb) investigate more compact encodings for sparse tables.
        // On the PR @michaelwoerister mentioned:
        // > Space requirements could perhaps be optimized by using the HAMT `popcnt`
        // > trick (i.e. divide things into buckets of 32 or 64 items and then
        // > store bit-masks of which item in each bucket is actually serialized).
        let needed = (i + 1) * <Option<T>>::BYTE_LEN;
        if self.bytes.len() < needed {
            self.bytes.resize(needed, 0);
        }

        Some(value).write_to_bytes_at(&mut self.bytes, i);
    }

    crate fn encode(&self, buf: &mut Encoder) -> Lazy<Self> {
        let pos = buf.position();
        buf.emit_raw_bytes(&self.bytes);
        Lazy::from_position_and_meta(
            NonZeroUsize::new(pos as usize).unwrap(),
            self.bytes.len(),
        )
    }
}

impl<T> LazyMeta for Table<T> where Option<T>: FixedSizeEncoding {
    type Meta = usize;

    fn min_size(len: usize) -> usize {
        len
    }
}

impl<T> Lazy<Table<T>> where Option<T>: FixedSizeEncoding {
    /// Given the metadata, extract out the value at a particular index (if any).
    #[inline(never)]
    crate fn get<'a, 'tcx, M: Metadata<'a, 'tcx>>(
        &self,
        metadata: M,
        i: usize,
    ) -> Option<T> {
        debug!("Table::lookup: index={:?} len={:?}", i, self.meta);

        let start = self.position.get();
        let bytes = &metadata.raw_bytes()[start..start + self.meta];
        <Option<T>>::maybe_read_from_bytes_at(bytes, i)?
    }
}

/// Like a `Table` but using `DefIndex` instead of `usize` as keys.
// FIXME(eddyb) replace by making `Table` behave like `IndexVec`,
// and by using `newtype_index!` to define `DefIndex`.
crate struct PerDefTable<T>(Table<T>) where Option<T>: FixedSizeEncoding;

impl<T> Default for PerDefTable<T> where Option<T>: FixedSizeEncoding {
    fn default() -> Self {
        PerDefTable(Table::default())
    }
}

impl<T> PerDefTable<T> where Option<T>: FixedSizeEncoding {
    crate fn set(&mut self, def_id: DefId, value: T) {
        assert!(def_id.is_local());
        self.0.set(def_id.index.index(), value);
    }

    crate fn encode(&self, buf: &mut Encoder) -> Lazy<Self> {
        let lazy = self.0.encode(buf);
        Lazy::from_position_and_meta(lazy.position, lazy.meta)
    }
}

impl<T> LazyMeta for PerDefTable<T> where Option<T>: FixedSizeEncoding {
    type Meta = <Table<T> as LazyMeta>::Meta;

    fn min_size(meta: Self::Meta) -> usize {
        Table::<T>::min_size(meta)
    }
}

impl<T> Lazy<PerDefTable<T>> where Option<T>: FixedSizeEncoding {
    fn as_table(&self) -> Lazy<Table<T>> {
        Lazy::from_position_and_meta(self.position, self.meta)
    }

    /// Given the metadata, extract out the value at a particular DefIndex (if any).
    #[inline(never)]
    crate fn get<'a, 'tcx, M: Metadata<'a, 'tcx>>(
        &self,
        metadata: M,
        def_index: DefIndex,
    ) -> Option<T> {
        self.as_table().get(metadata, def_index.index())
    }
}
<|endoftext|>


<commit_before>// This file is released under the same terms as Rust itself.

mod auth;

use crossbeam;
use db::{Db, PendingEntry};
use db::sqlite::SqliteDb;
use horrorshow::prelude::*;
use hyper::buffer::BufReader;
use hyper::header::{ContentType, Headers};
use hyper::net::{HttpListener, HttpStream, NetworkListener, NetworkStream};
use hyper::server::{Request, Response};
use hyper::status::StatusCode;
use hyper::uri::RequestUri;
use pipeline::PipelineId;
use spmc;
use std::collections::HashMap;
use std::convert::AsRef;
use std::error::Error;
use std::io::{BufWriter, Write};
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use ui::Pr;
use view::auth::AuthManager;

pub use view::auth::{Auth, AuthRef};

const WORKER_COUNT: usize = 3;

pub fn run_sqlite<'a, P: Pr, A: Into<AuthRef<'a>>>(
    listen: String,
    path: PathBuf,
    pipelines: HashMap<String, PipelineId>,
    secret: String,
    auth: A,
)
    where
::Err: Error,

::Err: Error,
{
    let path: &Path = path.as_ref();
    let listen: &str = listen.as_ref();
    let secret: &str = secret.as_ref();
    let auth = auth.into();
    let pipelines = &pipelines;
    crossbeam::scope(|scope| {
        let mut workers = Vec::with_capacity(WORKER_COUNT);
        for _ in 0..WORKER_COUNT {
            let (send, recv) = spmc::channel();
            scope.spawn(move || {
                let mut worker = Worker {
                    db: SqliteDb::
::open(path)
                        .expect("opening sqlite to succeed"),
                    pipelines: pipelines,
                    auth_manager: AuthManager{
                        auth: auth,
                        secret: secret,
                    },
                    _pr: PhantomData::
,
                };
                worker.run(recv)
            });
            workers.push(send);
        }
        let mut listener = HttpListener::new(listen).expect("a TCP socket");
        let mut i = 0;
        while let Ok(stream) = listener.accept() {
            workers[i].send(stream).unwrap();
            i += 1;
            i %= WORKER_COUNT;
        }
    });
}

struct Worker<'a, P, D>
    where P: Pr, D: Db

{
    db: D,
    pipelines: &'a HashMap<String, PipelineId>,
    auth_manager: AuthManager<'a>,
    _pr: PhantomData
,
}

impl<'a, P: Pr, D: Db
> Worker<'a, P, D> {
    fn run(&mut self, recv: spmc::Receiver<HttpStream>) {
        while let Ok(mut stream) = recv.recv() {
            let addr = stream.peer_addr()
                .expect("view client address");
            let mut stream_clone = stream.clone();
            let mut buf_read = BufReader::new(
                &mut stream_clone as &mut NetworkStream
            );
            let mut buf_write = BufWriter::new(&mut stream);
            let req = match Request::new(&mut buf_read, addr) {
                Ok(req) => req,
                Err(e) => {
                    warn!("Invalid view HTTP: {:?}", e);
                    continue;
                }
            };
            let mut head = Headers::new();
            let res = Response::new(&mut buf_write, &mut head);
            match self.handle_req(req, res) {
                Ok(()) => (),
                Err(e) => {
                    warn!("Failed to handle view request: {:?}", e);
                }
            }
        }
    }
    fn handle_req(
        &mut self,
        req: Request,
        res: Response,
    ) -> Result<(), Box<Error>> {
        let (req, mut res) = match self.auth_manager.check(req, res) {
            auth::CheckResult::Authenticated(req, res) => (req, res),
            auth::CheckResult::Err(e) => return Err(Box::new(e)),
            auth::CheckResult::NotAuthenticated => return Ok(()),
        };
        let pipeline = if let RequestUri::AbsolutePath(ref path) = req.uri {
            let mut path = &path[..];
            if path == "/" {
                None
            } else {
                if path.as_bytes()[0] == b'/' {
                    path = &path[1..];
                }
                match self.pipelines.get(path) {
                    Some(pipeline_id) => {
                        *res.status_mut() = StatusCode::Ok;
                        Some((path.to_owned(), *pipeline_id))
                    }
                    None => {
                        *res.status_mut() = StatusCode::NotFound;
                        return Ok(());
                    }
                }
            }
        } else {
            *res.status_mut() = StatusCode::BadRequest;
            return Ok(());
        };
        res.headers_mut().set(ContentType::html());
        let mut res = try!(res.start());
        try!(res.write_all(br##"<!DOCTYPE html>"##));
        if let Some((name, pipeline_id)) = pipeline {
            self.handle_pipeline_req(&name, pipeline_id, req, res)
        } else {
            self.handle_home_req(req, res)
        }
    }
    fn handle_pipeline_req(
        &mut self,
        name: &str,
        pipeline_id: PipelineId,
        _req: Request,
        mut res: Response<::hyper::net::Streaming>,
    ) -> Result<(), Box<Error>> {
        let pending_entries = self.db.list_pending(pipeline_id);
        let is_empty = pending_entries.is_empty();
        let queued_entries = self.db.list_queue(pipeline_id);
        let running_entry = self.db.peek_running(pipeline_id);
        let mut running = None;
        let mut queued = Vec::new();
        let pending: Vec<_> = pending_entries.into_iter().filter_map(|entry| {
            if Some(&entry.pr) == running_entry.as_ref().map(|x| &x.pr) {
                running = Some(entry);
            } else if queued_entries.iter()
                    .filter(|q| q.pr == entry.pr)
                    .next().is_some() {
                queued.push(entry);
            } else {
                return Some(entry);
            }
            None
        }).collect();
        let html = html!{
            html {
                head {
                    title { : name }
                    link(rel="stylesheet", href="https://cdnjs.cloudflare.com/ajax/libs/normalize/4.1.1/normalize.min.css");
                    style { : raw!(include_str!("style.css")) }
                }
                body {
                    h1 { : name }
                    table {
                        thead {
                            th { : "Status" }
                            th { : "PR#" }
                            th { : "Title" }
                        }
                        tbody {
                            |t| {
                                for entry in running {
                                    render_entry(State::Running, entry, t);
                                }
                                for entry in queued {
                                    render_entry(State::Queued, entry, t);
                                }
                                for entry in pending {
                                    render_entry(State::Pending, entry, t);
                                }
                                if is_empty {
                                    t << html!{
                                        td(colspan=3) {
                                            : "No opened pull requests"
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        };
        try!(html.write_to_io(&mut res));
        try!(res.end());
        Ok(())
    }
    fn handle_home_req(
        &mut self,
        _req: Request,
        mut res: Response<::hyper::net::Streaming>,
    ) -> Result<(), Box<Error>> {
        let html = html!{
            html {
                head {
                    title { : "Aelita" }
                    link(rel="stylesheet", href="https://cdnjs.cloudflare.com/ajax/libs/normalize/4.1.1/normalize.min.css");
                    style { : raw!(include_str!("style.css")) }
                }
                body {
                    h1 { : "Pipelines" }
                    table {
                        thead {
                            th { : "Name" }
                            th { : "Running" }
                            th { : "In queue" }
                            th { : "In review" }
                            th { : "Opened" }
                        }
                        tbody {
                            @ for (n, &pid) in self.pipelines { |t| {
                                let opened = self.db.list_pending(pid).len();
                                let queue = self.db.list_queue(pid).len();
                                let running = self.db.peek_running(pid).is_some();
                                let running = if running { 1 } else { 0 };
                                let review = opened - queue - running;
                                t << html!{
                                    tr {
                                        td(class="fill-link") {
                                            a(href=n) : { n }
                                        }
                                        td { : running }
                                        td { : queue }
                                        td { : review }
                                        td { : opened }
                                    }
                                }
                            }}
                            @ if self.pipelines.is_empty() {
                                td(colspan=5) {
                                    : "No configured repositories"
                                }
                            }
                        }
                    }
                    h2 { : "Github cheat sheet" }
                    p { : "To use the robot, say a command to it." }
                    dl {
                        dt { : raw!("r+") }
                        dd { : "Add the pull request to the merge queue." }
                        dt { : raw!("r=@username") }
                        dd { : "Add the pull request on behalf of \"username.\"" }
                        dt { : raw!("r-") }
                        dd { : "Cancel the pull request." }
                        dt { : raw!("try+") }
                        dd { : "Test the pull request without mergeing it." }
                        dt { : raw!("try-") }
                        dd { : "Cancel the pull request." }
                    }
                }
            }
        };
        try!(html.write_to_io(&mut res));
        try!(res.end());
        Ok(())
    }
}

enum State {
    Running,
    Queued,
    Pending,
}

fn render_entry
(
    state: State,
    entry: PendingEntry
,
    t: &mut TemplateBuffer,
) {
    t < "Running",
                    State::Queued => "In queue",
                    State::Pending => "In review",
                }
            }
            td(class="fill-link") {
                a(href=entry.url.to_string()) { : entry.pr.to_string() }
            }
            td { : &entry.title }
        }
    };
}<commit_msg>Fix a typo in the view page<commit_after>// This file is released under the same terms as Rust itself.

mod auth;

use crossbeam;
use db::{Db, PendingEntry};
use db::sqlite::SqliteDb;
use horrorshow::prelude::*;
use hyper::buffer::BufReader;
use hyper::header::{ContentType, Headers};
use hyper::net::{HttpListener, HttpStream, NetworkListener, NetworkStream};
use hyper::server::{Request, Response};
use hyper::status::StatusCode;
use hyper::uri::RequestUri;
use pipeline::PipelineId;
use spmc;
use std::collections::HashMap;
use std::convert::AsRef;
use std::error::Error;
use std::io::{BufWriter, Write};
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use ui::Pr;
use view::auth::AuthManager;

pub use view::auth::{Auth, AuthRef};

const WORKER_COUNT: usize = 3;

pub fn run_sqlite<'a, P: Pr, A: Into<AuthRef<'a>>>(
    listen: String,
    path: PathBuf,
    pipelines: HashMap<String, PipelineId>,
    secret: String,
    auth: A,
)
    where
::Err: Error,

::Err: Error,
{
    let path: &Path = path.as_ref();
    let listen: &str = listen.as_ref();
    let secret: &str = secret.as_ref();
    let auth = auth.into();
    let pipelines = &pipelines;
    crossbeam::scope(|scope| {
        let mut workers = Vec::with_capacity(WORKER_COUNT);
        for _ in 0..WORKER_COUNT {
            let (send, recv) = spmc::channel();
            scope.spawn(move || {
                let mut worker = Worker {
                    db: SqliteDb::
::open(path)
                        .expect("opening sqlite to succeed"),
                    pipelines: pipelines,
                    auth_manager: AuthManager{
                        auth: auth,
                        secret: secret,
                    },
                    _pr: PhantomData::
,
                };
                worker.run(recv)
            });
            workers.push(send);
        }
        let mut listener = HttpListener::new(listen).expect("a TCP socket");
        let mut i = 0;
        while let Ok(stream) = listener.accept() {
            workers[i].send(stream).unwrap();
            i += 1;
            i %= WORKER_COUNT;
        }
    });
}

struct Worker<'a, P, D>
    where P: Pr, D: Db

{
    db: D,
    pipelines: &'a HashMap<String, PipelineId>,
    auth_manager: AuthManager<'a>,
    _pr: PhantomData
,
}

impl<'a, P: Pr, D: Db
> Worker<'a, P, D> {
    fn run(&mut self, recv: spmc::Receiver<HttpStream>) {
        while let Ok(mut stream) = recv.recv() {
            let addr = stream.peer_addr()
                .expect("view client address");
            let mut stream_clone = stream.clone();
            let mut buf_read = BufReader::new(
                &mut stream_clone as &mut NetworkStream
            );
            let mut buf_write = BufWriter::new(&mut stream);
            let req = match Request::new(&mut buf_read, addr) {
                Ok(req) => req,
                Err(e) => {
                    warn!("Invalid view HTTP: {:?}", e);
                    continue;
                }
            };
            let mut head = Headers::new();
            let res = Response::new(&mut buf_write, &mut head);
            match self.handle_req(req, res) {
                Ok(()) => (),
                Err(e) => {
                    warn!("Failed to handle view request: {:?}", e);
                }
            }
        }
    }
    fn handle_req(
        &mut self,
        req: Request,
        res: Response,
    ) -> Result<(), Box<Error>> {
        let (req, mut res) = match self.auth_manager.check(req, res) {
            auth::CheckResult::Authenticated(req, res) => (req, res),
            auth::CheckResult::Err(e) => return Err(Box::new(e)),
            auth::CheckResult::NotAuthenticated => return Ok(()),
        };
        let pipeline = if let RequestUri::AbsolutePath(ref path) = req.uri {
            let mut path = &path[..];
            if path == "/" {
                None
            } else {
                if path.as_bytes()[0] == b'/' {
                    path = &path[1..];
                }
                match self.pipelines.get(path) {
                    Some(pipeline_id) => {
                        *res.status_mut() = StatusCode::Ok;
                        Some((path.to_owned(), *pipeline_id))
                    }
                    None => {
                        *res.status_mut() = StatusCode::NotFound;
                        return Ok(());
                    }
                }
            }
        } else {
            *res.status_mut() = StatusCode::BadRequest;
            return Ok(());
        };
        res.headers_mut().set(ContentType::html());
        let mut res = try!(res.start());
        try!(res.write_all(br##"<!DOCTYPE html>"##));
        if let Some((name, pipeline_id)) = pipeline {
            self.handle_pipeline_req(&name, pipeline_id, req, res)
        } else {
            self.handle_home_req(req, res)
        }
    }
    fn handle_pipeline_req(
        &mut self,
        name: &str,
        pipeline_id: PipelineId,
        _req: Request,
        mut res: Response<::hyper::net::Streaming>,
    ) -> Result<(), Box<Error>> {
        let pending_entries = self.db.list_pending(pipeline_id);
        let is_empty = pending_entries.is_empty();
        let queued_entries = self.db.list_queue(pipeline_id);
        let running_entry = self.db.peek_running(pipeline_id);
        let mut running = None;
        let mut queued = Vec::new();
        let pending: Vec<_> = pending_entries.into_iter().filter_map(|entry| {
            if Some(&entry.pr) == running_entry.as_ref().map(|x| &x.pr) {
                running = Some(entry);
            } else if queued_entries.iter()
                    .filter(|q| q.pr == entry.pr)
                    .next().is_some() {
                queued.push(entry);
            } else {
                return Some(entry);
            }
            None
        }).collect();
        let html = html!{
            html {
                head {
                    title { : name }
                    link(rel="stylesheet", href="https://cdnjs.cloudflare.com/ajax/libs/normalize/4.1.1/normalize.min.css");
                    style { : raw!(include_str!("style.css")) }
                }
                body {
                    h1 { : name }
                    table {
                        thead {
                            th { : "Status" }
                            th { : "PR#" }
                            th { : "Title" }
                        }
                        tbody {
                            |t| {
                                for entry in running {
                                    render_entry(State::Running, entry, t);
                                }
                                for entry in queued {
                                    render_entry(State::Queued, entry, t);
                                }
                                for entry in pending {
                                    render_entry(State::Pending, entry, t);
                                }
                                if is_empty {
                                    t << html!{
                                        td(colspan=3) {
                                            : "No opened pull requests"
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        };
        try!(html.write_to_io(&mut res));
        try!(res.end());
        Ok(())
    }
    fn handle_home_req(
        &mut self,
        _req: Request,
        mut res: Response<::hyper::net::Streaming>,
    ) -> Result<(), Box<Error>> {
        let html = html!{
            html {
                head {
                    title { : "Aelita" }
                    link(rel="stylesheet", href="https://cdnjs.cloudflare.com/ajax/libs/normalize/4.1.1/normalize.min.css");
                    style { : raw!(include_str!("style.css")) }
                }
                body {
                    h1 { : "Pipelines" }
                    table {
                        thead {
                            th { : "Name" }
                            th { : "Running" }
                            th { : "In queue" }
                            th { : "In review" }
                            th { : "Opened" }
                        }
                        tbody {
                            @ for (n, &pid) in self.pipelines { |t| {
                                let opened = self.db.list_pending(pid).len();
                                let queue = self.db.list_queue(pid).len();
                                let running = self.db.peek_running(pid).is_some();
                                let running = if running { 1 } else { 0 };
                                let review = opened - queue - running;
                                t << html!{
                                    tr {
                                        td(class="fill-link") {
                                            a(href=n) : { n }
                                        }
                                        td { : running }
                                        td { : queue }
                                        td { : review }
                                        td { : opened }
                                    }
                                }
                            }}
                            @ if self.pipelines.is_empty() {
                                td(colspan=5) {
                                    : "No configured repositories"
                                }
                            }
                        }
                    }
                    h2 { : "Github cheat sheet" }
                    p { : "To use the robot, say a command to it." }
                    dl {
                        dt { : raw!("r+") }
                        dd { : "Add the pull request to the merge queue." }
                        dt { : raw!("r=@username") }
                        dd { : "Add the pull request on behalf of \"username.\"" }
                        dt { : raw!("r-") }
                        dd { : "Cancel the pull request." }
                        dt { : raw!("try+") }
                        dd { : "Test the pull request without merging it." }
                        dt { : raw!("try-") }
                        dd { : "Cancel the pull request." }
                    }
                }
            }
        };
        try!(html.write_to_io(&mut res));
        try!(res.end());
        Ok(())
    }
}

enum State {
    Running,
    Queued,
    Pending,
}

fn render_entry
(
    state: State,
    entry: PendingEntry
,
    t: &mut TemplateBuffer,
) {
    t < "Running",
                    State::Queued => "In queue",
                    State::Pending => "In review",
                }
            }
            td(class="fill-link") {
                a(href=entry.url.to_string()) { : entry.pr.to_string() }
            }
            td { : &entry.title }
        }
    };
}
<|endoftext|>


<commit_before>//! A simple wait-free, grow-only pagetable, assumes a dense keyspace.

use std::{
    alloc::{alloc_zeroed, Layout},
    mem::{align_of, size_of},
    sync::atomic::Ordering::Relaxed,
};

use super::*;

const FANFACTOR: u64 = 18;
const FANOUT: u64 = 1 << FANFACTOR;
const FAN_MASK: u64 = FANOUT - 1;

#[doc(hidden)]
pub const PAGETABLE_NODE_SZ: usize = size_of::<Node1<()>>();

pub type PageId = u64;

#[inline(always)]
fn split_fanout(i: u64) -> (u64, u64) {
    // right shift 32 on 32-bit pointer systems panics
    #[cfg(target_pointer_width = "64")]
    assert!(
        i <= 1 << (FANFACTOR * 2),
        "trying to access key of {}, which is \
         higher than 2 ^ {}",
        i,
        (FANFACTOR * 2)
    );

    let left = i >> FANFACTOR;
    let right = i & FAN_MASK;
    (left, right)
}

struct Node1<T: Send + 'static> {
    children: [Atomic<Node2<T>>; FANOUT as usize],
}

struct Node2<T: Send + 'static> {
    children: [Atomic<T>; FANOUT as usize],
}

impl<T: Send + 'static> Node1<T> {
    fn new() -> Box<Node1<T>> {
        let size = size_of::<Node1<T>>();
        let align = align_of::<Node1<T>>();

        unsafe {
            let layout = Layout::from_size_align_unchecked(size, align);

            #[allow(clippy::cast_ptr_alignment)]
            let ptr = alloc_zeroed(layout) as *mut Node1<T>;

            Box::from_raw(ptr)
        }
    }
}

impl<T: Send + 'static> Node2<T> {
    fn new() -> Owned<Node2<T>> {
        let size = size_of::<Node2<T>>();
        let align = align_of::<Node2<T>>();

        unsafe {
            let layout = Layout::from_size_align_unchecked(size, align);

            #[allow(clippy::cast_ptr_alignment)]
            let ptr = alloc_zeroed(layout) as *mut Node2<T>;

            Owned::from_raw(ptr)
        }
    }
}

impl<T: Send + 'static> Drop for Node1<T> {
    fn drop(&mut self) {
        unsafe {
            let children = self
                .children
                .iter()
                .map(|c| c.load(Relaxed, &unprotected()).as_raw())
                .filter(|c| !c.is_null());

            for child in children {
                drop(Box::from_raw(child as *mut Node2<T>));
            }
        }
    }
}

impl<T: Send + 'static> Drop for Node2<T> {
    fn drop(&mut self) {
        unsafe {
            let children = self
                .children
                .iter()
                .map(|c| c.load(Relaxed, &unprotected()).as_raw())
                .filter(|c| !c.is_null());

            for child in children {
                drop(Box::from_raw(child as *mut T));
            }
        }
    }
}

/// A simple lock-free radix tree.
pub struct PageTable<T>
where
    T: 'static + Send + Sync,
{
    head: Atomic<Node1<T>>,
}

impl<T> Default for PageTable<T>
where
    T: 'static + Send + Sync,
{
    fn default() -> PageTable<T> {
        let head = Node1::new();
        PageTable {
            head: Atomic::from(head),
        }
    }
}

impl<T> PageTable<T>
where
    T: 'static + Send + Sync,
{
    /// Atomically swap the previous value in a tree with a new one.
    pub fn swap<'g>(
        &self,
        pid: PageId,
        new: Shared<'g, T>,
        guard: &'g Guard,
    ) -> Shared<'g, T> {
        let tip = traverse(self.head.load(Acquire, guard), pid, guard);
        debug_delay();
        tip.swap(new, SeqCst, guard)
    }

    /// Compare and swap an old value to a new one.
    pub fn cas<'g>(
        &self,
        pid: PageId,
        old: Shared<'g, T>,
        new: Shared<'g, T>,
        guard: &'g Guard,
    ) -> std::result::Result<Shared<'g, T>, Shared<'g, T>> {
        debug_delay();
        let tip = traverse(self.head.load(Acquire, guard), pid, guard);

        debug_delay();

        let _ = tip
            .compare_and_set(old, new, Release, guard)
            .map_err(|e| e.current)?;

        if !old.is_null() {
            unsafe {
                guard.defer_destroy(old);
            }
        }
        Ok(new)
    }

    /// Try to get a value from the tree.
    pub fn get<'g>(
        &self,
        pid: PageId,
        guard: &'g Guard,
    ) -> Option<Shared<'g, T>> {
        debug_delay();
        let tip = traverse(self.head.load(Acquire, guard), pid, guard);

        let res = tip.load(Acquire, guard);
        if res.is_null() {
            None
        } else {
            Some(res)
        }
    }

    /// Delete a value from the tree, returning the old value if it was set.
    pub fn del<'g>(
        &self,
        pid: PageId,
        guard: &'g Guard,
    ) -> Option<Shared<'g, T>> {
        debug_delay();
        let old = self.swap(pid, Shared::null(), guard);
        if !old.is_null() {
            unsafe {
                guard.defer_destroy(old);
            }
            Some(old)
        } else {
            None
        }
    }
}

fn traverse<'g, T: 'static + Send>(
    head: Shared<'g, Node1<T>>,
    k: PageId,
    guard: &'g Guard,
) -> &'g Atomic<T> {
    let (l1k, l2k) = split_fanout(k);

    debug_delay();
    let l1 = unsafe { &head.deref().children };

    debug_delay();
    let mut l2_ptr = l1[usize::try_from(l1k).unwrap()].load(Acquire, guard);

    if l2_ptr.is_null() {
        let next_child = Node2::new().into_shared(guard);

        debug_delay();
        let ret = l1[usize::try_from(l1k).unwrap()]
            .compare_and_set(l2_ptr, next_child, Release, guard);

        match ret {
            Ok(_) => {
                // CAS worked
                l2_ptr = next_child;
            }
            Err(e) => {
                // another thread beat us, drop unused created
                // child and use what is already set
                l2_ptr = e.current;
            }
        }
    }

    debug_delay();
    let l2 = unsafe { &l2_ptr.deref().children };

    &l2[usize::try_from(l2k).unwrap()]
}

impl<T> Drop for PageTable<T>
where
    T: 'static + Send + Sync,
{
    fn drop(&mut self) {
        unsafe {
            let head = self.head.load(Relaxed, &unprotected()).into_owned();
            drop(head);
        }
    }
}

#[test]
fn test_split_fanout() {
    assert_eq!(
        split_fanout(0b11_1111_1111_1111_1111),
        (0, 0b11_1111_1111_1111_1111)
    );
    assert_eq!(
        split_fanout(0b111_1111_1111_1111_1111),
        (0b1, 0b11_1111_1111_1111_1111)
    );
}

#[test]
fn basic_functionality() {
    unsafe {
        let guard = pin();
        let rt = PageTable::default();
        let v1 = Owned::new(5).into_shared(&guard);
        rt.cas(0, Shared::null(), v1, &guard).unwrap();
        let ptr = rt.get(0, &guard).unwrap();
        assert_eq!(ptr.deref(), &5);
        rt.cas(0, ptr, Owned::new(6).into_shared(&guard), &guard)
            .unwrap();
        assert_eq!(rt.get(0, &guard).unwrap().deref(), &6);
        rt.del(0, &guard);
        assert!(rt.get(0, &guard).is_none());

        let k2 = 321 << FANFACTOR;
        let k3 = k2 + 1;

        let v2 = Owned::new(2).into_shared(&guard);
        rt.cas(k2, Shared::null(), v2, &guard).unwrap();
        assert_eq!(rt.get(k2, &guard).unwrap().deref(), &2);
        assert!(rt.get(k3, &guard).is_none());
        let v3 = Owned::new(3).into_shared(&guard);
        rt.cas(k3, Shared::null(), v3, &guard).unwrap();
        assert_eq!(rt.get(k3, &guard).unwrap().deref(), &3);
        assert_eq!(rt.get(k2, &guard).unwrap().deref(), &2);
    }
}

#[test]
#[ignore]
fn test_model() {
    use self::Shared as EpochShared;
    use model::{model, prop_oneof};

    model! {
        Model => let mut m = std::collections::HashMap::new(),
        Implementation => let i = PageTable::default(),
        Insert((u64, u64))((k, new) in (0u64..4, 0u64..4)) => {
            if !m.contains_key(&k) {
                m.insert(k, new);

                let guard = pin();
                let v = Owned::new(new).into_shared(&guard);
                i.cas(k, EpochShared::null(), v, &guard ).expect("should be able to insert a value");
            }
        },
        Get(u64)(k in 0u64..4) => {
            let guard = pin();
            let expected = m.get(&k);
            let actual = i.get(k, &guard).map(|s| unsafe { s.deref() });
            assert_eq!(expected, actual);
        },
        Cas((u64, u64, u64))((k, old, new) in (0u64..4, 0u64..4, 0u64..4)) => {
            let guard = pin();
            let expected_current = m.get(&k).cloned();
            let actual_current = i.get(k, &guard);
            assert_eq!(expected_current, actual_current.map(|s| unsafe { *s.deref() }));
            if expected_current.is_none() {
                continue;
            }

            let new_v = Owned::new(new).into_shared(&guard);

            if expected_current == Some(old) {
                m.insert(k, new);
                let cas_res = i.cas(k, actual_current.unwrap(), new_v, &guard);
                assert!(cas_res.is_ok());
            };
        }
    }
}

#[test]
#[ignore]
fn test_linearizability() {
    use self::Shared as EpochShared;
    use model::{linearizable, prop_oneof, Shared};

    linearizable! {
        Implementation => let i = Shared::new(PageTable::default()),
        Get(u64)(k in 0u64..4) -> Option<u64> {
            let guard = pin();
            unsafe {
                i.get(k, &guard).map(|s| *s.deref())
            }
        },
        Insert((u64, u64))((k, new) in (0u64..4, 0u64..4)) -> bool {
            let guard = pin();
            let v = Owned::new(new).into_shared(&guard);
            i.cas(k, EpochShared::null(), v, &guard).is_err()
        },
        Cas((u64, u64, u64))((k, old, new)
            in (0u64..4, 0u64..4, 0u64..4))
            -> std::result::Result<(), u64> {
            let guard = pin();
            i.cas(k, Owned::new(old).into_shared(&guard), Owned::new(new).into_shared(&guard), &guard)
                .map(|_| ())
                .map_err(|s| if s.is_null() {
                    0
                } else {
                    unsafe { *s.deref() }
                })
        }
    }
}
<commit_msg>use more easily optimizable pagetable node drop<commit_after>//! A simple wait-free, grow-only pagetable, assumes a dense keyspace.

use std::{
    alloc::{alloc_zeroed, Layout},
    mem::{align_of, size_of},
    sync::atomic::Ordering::Relaxed,
};

use super::*;

const FANFACTOR: u64 = 18;
const FANOUT: u64 = 1 << FANFACTOR;
const FAN_MASK: u64 = FANOUT - 1;

#[doc(hidden)]
pub const PAGETABLE_NODE_SZ: usize = size_of::<Node1<()>>();

pub type PageId = u64;

#[inline(always)]
fn split_fanout(i: u64) -> (u64, u64) {
    // right shift 32 on 32-bit pointer systems panics
    #[cfg(target_pointer_width = "64")]
    assert!(
        i <= 1 << (FANFACTOR * 2),
        "trying to access key of {}, which is \
         higher than 2 ^ {}",
        i,
        (FANFACTOR * 2)
    );

    let left = i >> FANFACTOR;
    let right = i & FAN_MASK;
    (left, right)
}

struct Node1<T: Send + 'static> {
    children: [Atomic<Node2<T>>; FANOUT as usize],
}

struct Node2<T: Send + 'static> {
    children: [Atomic<T>; FANOUT as usize],
}

impl<T: Send + 'static> Node1<T> {
    fn new() -> Box<Node1<T>> {
        let size = size_of::<Node1<T>>();
        let align = align_of::<Node1<T>>();

        unsafe {
            let layout = Layout::from_size_align_unchecked(size, align);

            #[allow(clippy::cast_ptr_alignment)]
            let ptr = alloc_zeroed(layout) as *mut Node1<T>;

            Box::from_raw(ptr)
        }
    }
}

impl<T: Send + 'static> Node2<T> {
    fn new() -> Owned<Node2<T>> {
        let size = size_of::<Node2<T>>();
        let align = align_of::<Node2<T>>();

        unsafe {
            let layout = Layout::from_size_align_unchecked(size, align);

            #[allow(clippy::cast_ptr_alignment)]
            let ptr = alloc_zeroed(layout) as *mut Node2<T>;

            Owned::from_raw(ptr)
        }
    }
}

impl<T: Send + 'static> Drop for Node1<T> {
    fn drop(&mut self) {
        for child in self.children.iter() {
            unsafe {
                let shared_child = child.load(Relaxed, &unprotected());
                if shared_child.as_raw().is_null() {
                    continue;
                }
                drop(shared_child.into_owned());
            }
        }
    }
}

impl<T: Send + 'static> Drop for Node2<T> {
    fn drop(&mut self) {
        for child in self.children.iter() {
            unsafe {
                let shared_child = child.load(Relaxed, &unprotected());
                if shared_child.as_raw().is_null() {
                    continue;
                }
                drop(shared_child.into_owned());
            }
        }
    }
}

/// A simple lock-free radix tree.
pub struct PageTable<T>
where
    T: 'static + Send + Sync,
{
    head: Atomic<Node1<T>>,
}

impl<T> Default for PageTable<T>
where
    T: 'static + Send + Sync,
{
    fn default() -> PageTable<T> {
        let head = Node1::new();
        PageTable {
            head: Atomic::from(head),
        }
    }
}

impl<T> PageTable<T>
where
    T: 'static + Send + Sync,
{
    /// Atomically swap the previous value in a tree with a new one.
    pub fn swap<'g>(
        &self,
        pid: PageId,
        new: Shared<'g, T>,
        guard: &'g Guard,
    ) -> Shared<'g, T> {
        let tip = traverse(self.head.load(Acquire, guard), pid, guard);
        debug_delay();
        tip.swap(new, SeqCst, guard)
    }

    /// Compare and swap an old value to a new one.
    pub fn cas<'g>(
        &self,
        pid: PageId,
        old: Shared<'g, T>,
        new: Shared<'g, T>,
        guard: &'g Guard,
    ) -> std::result::Result<Shared<'g, T>, Shared<'g, T>> {
        debug_delay();
        let tip = traverse(self.head.load(Acquire, guard), pid, guard);

        debug_delay();

        let _ = tip
            .compare_and_set(old, new, Release, guard)
            .map_err(|e| e.current)?;

        if !old.is_null() {
            unsafe {
                guard.defer_destroy(old);
            }
        }
        Ok(new)
    }

    /// Try to get a value from the tree.
    pub fn get<'g>(
        &self,
        pid: PageId,
        guard: &'g Guard,
    ) -> Option<Shared<'g, T>> {
        debug_delay();
        let tip = traverse(self.head.load(Acquire, guard), pid, guard);

        let res = tip.load(Acquire, guard);
        if res.is_null() {
            None
        } else {
            Some(res)
        }
    }

    /// Delete a value from the tree, returning the old value if it was set.
    pub fn del<'g>(
        &self,
        pid: PageId,
        guard: &'g Guard,
    ) -> Option<Shared<'g, T>> {
        debug_delay();
        let old = self.swap(pid, Shared::null(), guard);
        if !old.is_null() {
            unsafe {
                guard.defer_destroy(old);
            }
            Some(old)
        } else {
            None
        }
    }
}

fn traverse<'g, T: 'static + Send>(
    head: Shared<'g, Node1<T>>,
    k: PageId,
    guard: &'g Guard,
) -> &'g Atomic<T> {
    let (l1k, l2k) = split_fanout(k);

    debug_delay();
    let l1 = unsafe { &head.deref().children };

    debug_delay();
    let mut l2_ptr = l1[usize::try_from(l1k).unwrap()].load(Acquire, guard);

    if l2_ptr.is_null() {
        let next_child = Node2::new().into_shared(guard);

        debug_delay();
        let ret = l1[usize::try_from(l1k).unwrap()]
            .compare_and_set(l2_ptr, next_child, Release, guard);

        match ret {
            Ok(_) => {
                // CAS worked
                l2_ptr = next_child;
            }
            Err(e) => {
                // another thread beat us, drop unused created
                // child and use what is already set
                l2_ptr = e.current;
            }
        }
    }

    debug_delay();
    let l2 = unsafe { &l2_ptr.deref().children };

    &l2[usize::try_from(l2k).unwrap()]
}

impl<T> Drop for PageTable<T>
where
    T: 'static + Send + Sync,
{
    fn drop(&mut self) {
        unsafe {
            let head = self.head.load(Relaxed, &unprotected()).into_owned();
            drop(head);
        }
    }
}

#[test]
fn test_split_fanout() {
    assert_eq!(
        split_fanout(0b11_1111_1111_1111_1111),
        (0, 0b11_1111_1111_1111_1111)
    );
    assert_eq!(
        split_fanout(0b111_1111_1111_1111_1111),
        (0b1, 0b11_1111_1111_1111_1111)
    );
}

#[test]
fn basic_functionality() {
    unsafe {
        let guard = pin();
        let rt = PageTable::default();
        let v1 = Owned::new(5).into_shared(&guard);
        rt.cas(0, Shared::null(), v1, &guard).unwrap();
        let ptr = rt.get(0, &guard).unwrap();
        assert_eq!(ptr.deref(), &5);
        rt.cas(0, ptr, Owned::new(6).into_shared(&guard), &guard)
            .unwrap();
        assert_eq!(rt.get(0, &guard).unwrap().deref(), &6);
        rt.del(0, &guard);
        assert!(rt.get(0, &guard).is_none());

        let k2 = 321 << FANFACTOR;
        let k3 = k2 + 1;

        let v2 = Owned::new(2).into_shared(&guard);
        rt.cas(k2, Shared::null(), v2, &guard).unwrap();
        assert_eq!(rt.get(k2, &guard).unwrap().deref(), &2);
        assert!(rt.get(k3, &guard).is_none());
        let v3 = Owned::new(3).into_shared(&guard);
        rt.cas(k3, Shared::null(), v3, &guard).unwrap();
        assert_eq!(rt.get(k3, &guard).unwrap().deref(), &3);
        assert_eq!(rt.get(k2, &guard).unwrap().deref(), &2);
    }
}

#[test]
#[ignore]
fn test_model() {
    use self::Shared as EpochShared;
    use model::{model, prop_oneof};

    model! {
        Model => let mut m = std::collections::HashMap::new(),
        Implementation => let i = PageTable::default(),
        Insert((u64, u64))((k, new) in (0u64..4, 0u64..4)) => {
            if !m.contains_key(&k) {
                m.insert(k, new);

                let guard = pin();
                let v = Owned::new(new).into_shared(&guard);
                i.cas(k, EpochShared::null(), v, &guard ).expect("should be able to insert a value");
            }
        },
        Get(u64)(k in 0u64..4) => {
            let guard = pin();
            let expected = m.get(&k);
            let actual = i.get(k, &guard).map(|s| unsafe { s.deref() });
            assert_eq!(expected, actual);
        },
        Cas((u64, u64, u64))((k, old, new) in (0u64..4, 0u64..4, 0u64..4)) => {
            let guard = pin();
            let expected_current = m.get(&k).cloned();
            let actual_current = i.get(k, &guard);
            assert_eq!(expected_current, actual_current.map(|s| unsafe { *s.deref() }));
            if expected_current.is_none() {
                continue;
            }

            let new_v = Owned::new(new).into_shared(&guard);

            if expected_current == Some(old) {
                m.insert(k, new);
                let cas_res = i.cas(k, actual_current.unwrap(), new_v, &guard);
                assert!(cas_res.is_ok());
            };
        }
    }
}

#[test]
#[ignore]
fn test_linearizability() {
    use self::Shared as EpochShared;
    use model::{linearizable, prop_oneof, Shared};

    linearizable! {
        Implementation => let i = Shared::new(PageTable::default()),
        Get(u64)(k in 0u64..4) -> Option<u64> {
            let guard = pin();
            unsafe {
                i.get(k, &guard).map(|s| *s.deref())
            }
        },
        Insert((u64, u64))((k, new) in (0u64..4, 0u64..4)) -> bool {
            let guard = pin();
            let v = Owned::new(new).into_shared(&guard);
            i.cas(k, EpochShared::null(), v, &guard).is_err()
        },
        Cas((u64, u64, u64))((k, old, new)
            in (0u64..4, 0u64..4, 0u64..4))
            -> std::result::Result<(), u64> {
            let guard = pin();
            i.cas(k, Owned::new(old).into_shared(&guard), Owned::new(new).into_shared(&guard), &guard)
                .map(|_| ())
                .map_err(|s| if s.is_null() {
                    0
                } else {
                    unsafe { *s.deref() }
                })
        }
    }
}
<|endoftext|>


<commit_before>//! This module contains an import search funcionality that is provided to the ra_assists module.
//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module.

use crate::{
    db::RootDatabase,
    references::{classify_name, NameDefinition, NameKind},
    symbol_index::{self, FileSymbol},
    Query,
};
use hir::{db::HirDatabase, ModuleDef, SourceBinder};
use ra_assists::ImportsLocator;
use ra_prof::profile;
use ra_syntax::{ast, AstNode, SyntaxKind::NAME};

pub(crate) struct ImportsLocatorIde<'a> {
    source_binder: SourceBinder<'a, RootDatabase>,
}

impl<'a> ImportsLocatorIde<'a> {
    pub(crate) fn new(db: &'a RootDatabase) -> Self {
        Self { source_binder: SourceBinder::new(db) }
    }

    fn get_name_definition(
        &mut self,
        db: &impl HirDatabase,
        import_candidate: &FileSymbol,
    ) -> Option<NameDefinition> {
        let _p = profile("get_name_definition");
        let file_id = import_candidate.file_id.into();
        let candidate_node = import_candidate.ptr.to_node(&db.parse_or_expand(file_id)?);
        let candidate_name_node = if candidate_node.kind() != NAME {
            candidate_node.children().find(|it| it.kind() == NAME)?
        } else {
            candidate_node
        };
        classify_name(
            &mut self.source_binder,
            hir::InFile { file_id, value: &ast::Name::cast(candidate_name_node)? },
        )
    }
}

impl ImportsLocator for ImportsLocatorIde<'_> {
    fn find_imports(&mut self, name_to_import: &str) -> Vec<ModuleDef> {
        let _p = profile("search_for_imports");
        let db = self.source_binder.db;

        let project_results = {
            let mut query = Query::new(name_to_import.to_string());
            query.exact();
            query.limit(40);
            symbol_index::world_symbols(db, query)
        };
        let lib_results = {
            let mut query = Query::new(name_to_import.to_string());
            query.libs();
            query.exact();
            query.limit(40);
            symbol_index::world_symbols(db, query)
        };

        project_results
            .into_iter()
            .chain(lib_results.into_iter())
            .filter_map(|import_candidate| self.get_name_definition(db, &import_candidate))
            .filter_map(|name_definition_to_import| {
                if let NameKind::Def(module_def) = name_definition_to_import.kind {
                    Some(module_def)
                } else {
                    None
                }
            })
            .collect()
    }
}
<commit_msg>minor, if let else -> match<commit_after>//! This module contains an import search funcionality that is provided to the ra_assists module.
//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module.

use crate::{
    db::RootDatabase,
    references::{classify_name, NameDefinition, NameKind},
    symbol_index::{self, FileSymbol},
    Query,
};
use hir::{db::HirDatabase, ModuleDef, SourceBinder};
use ra_assists::ImportsLocator;
use ra_prof::profile;
use ra_syntax::{ast, AstNode, SyntaxKind::NAME};

pub(crate) struct ImportsLocatorIde<'a> {
    source_binder: SourceBinder<'a, RootDatabase>,
}

impl<'a> ImportsLocatorIde<'a> {
    pub(crate) fn new(db: &'a RootDatabase) -> Self {
        Self { source_binder: SourceBinder::new(db) }
    }

    fn get_name_definition(
        &mut self,
        db: &impl HirDatabase,
        import_candidate: &FileSymbol,
    ) -> Option<NameDefinition> {
        let _p = profile("get_name_definition");
        let file_id = import_candidate.file_id.into();
        let candidate_node = import_candidate.ptr.to_node(&db.parse_or_expand(file_id)?);
        let candidate_name_node = if candidate_node.kind() != NAME {
            candidate_node.children().find(|it| it.kind() == NAME)?
        } else {
            candidate_node
        };
        classify_name(
            &mut self.source_binder,
            hir::InFile { file_id, value: &ast::Name::cast(candidate_name_node)? },
        )
    }
}

impl ImportsLocator for ImportsLocatorIde<'_> {
    fn find_imports(&mut self, name_to_import: &str) -> Vec<ModuleDef> {
        let _p = profile("search_for_imports");
        let db = self.source_binder.db;

        let project_results = {
            let mut query = Query::new(name_to_import.to_string());
            query.exact();
            query.limit(40);
            symbol_index::world_symbols(db, query)
        };
        let lib_results = {
            let mut query = Query::new(name_to_import.to_string());
            query.libs();
            query.exact();
            query.limit(40);
            symbol_index::world_symbols(db, query)
        };

        project_results
            .into_iter()
            .chain(lib_results.into_iter())
            .filter_map(|import_candidate| self.get_name_definition(db, &import_candidate))
            .filter_map(|name_definition_to_import| match name_definition_to_import.kind {
                NameKind::Def(module_def) => Some(module_def),
                _ => None,
            })
            .collect()
    }
}
<|endoftext|>


<commit_before>//! Module that defines `SyntaxError`.

use std::fmt;

use crate::{TextRange, TextUnit};

/// Represents the result of unsuccessful tokenization, parsing
/// or tree validation.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SyntaxError(String, TextRange);

// FIXME: there was an unused SyntaxErrorKind previously (before this enum was removed)
// It was introduced in this PR: https://github.com/rust-analyzer/rust-analyzer/pull/846/files#diff-827da9b03b8f9faa1bade5cdd44d5dafR95
// but it was not removed by a mistake.
//
// So, we need to find a place where to stick validation for attributes in match clauses.
// Code before refactor:
// InvalidMatchInnerAttr => {
//    write!(f, "Inner attributes are only allowed directly after the opening brace of the match expression")
// }

impl SyntaxError {
    pub fn new(message: impl Into<String>, range: TextRange) -> Self {
        Self(message.into(), range)
    }
    pub fn new_at_offset(message: impl Into<String>, offset: TextUnit) -> Self {
        Self(message.into(), TextRange::offset_len(offset, 0.into()))
    }

    pub fn message(&self) -> &str {
        &self.0
    }
    pub fn range(&self) -> TextRange {
        self.1
    }

    pub fn with_range(mut self, range: TextRange) -> Self {
        self.1 = range;
        self
    }
}

impl fmt::Display for SyntaxError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}
<commit_msg>ra_syntax: change module-level documentation for syntax_error to "see X" as per matklad<commit_after>//! See docs for `SyntaxError`.

use std::fmt;

use crate::{TextRange, TextUnit};

/// Represents the result of unsuccessful tokenization, parsing
/// or tree validation.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SyntaxError(String, TextRange);

// FIXME: there was an unused SyntaxErrorKind previously (before this enum was removed)
// It was introduced in this PR: https://github.com/rust-analyzer/rust-analyzer/pull/846/files#diff-827da9b03b8f9faa1bade5cdd44d5dafR95
// but it was not removed by a mistake.
//
// So, we need to find a place where to stick validation for attributes in match clauses.
// Code before refactor:
// InvalidMatchInnerAttr => {
//    write!(f, "Inner attributes are only allowed directly after the opening brace of the match expression")
// }

impl SyntaxError {
    pub fn new(message: impl Into<String>, range: TextRange) -> Self {
        Self(message.into(), range)
    }
    pub fn new_at_offset(message: impl Into<String>, offset: TextUnit) -> Self {
        Self(message.into(), TextRange::offset_len(offset, 0.into()))
    }

    pub fn message(&self) -> &str {
        &self.0
    }
    pub fn range(&self) -> TextRange {
        self.1
    }

    pub fn with_range(mut self, range: TextRange) -> Self {
        self.1 = range;
        self
    }
}

impl fmt::Display for SyntaxError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}
<|endoftext|>


<commit_before>pub mod boolean_query;

use kite::Query;

use RocksDBIndexReader;
use search::boolean_retrieval::BooleanQueryOp;
use search::scoring::{CombinatorScorer, ScoreFunctionOp};

#[derive(Debug)]
pub struct SearchPlan {
    pub boolean_query: Vec<BooleanQueryOp>,
    pub boolean_query_is_negated: bool,
    pub score_function: Vec<ScoreFunctionOp>,
}

impl SearchPlan {
    pub fn new() -> SearchPlan {
        SearchPlan {
            boolean_query: Vec::new(),
            boolean_query_is_negated: false,
            score_function: Vec::new(),
        }
    }
}

fn plan_query_combinator(index_reader: &RocksDBIndexReader, mut plan: &mut SearchPlan, queries: &Vec<Query>, join_op: BooleanQueryOp, score: bool, scorer: CombinatorScorer) {
    match queries.len() {
        0 => {
            plan.boolean_query.push(BooleanQueryOp::PushEmpty);
            plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
        }
        1 =>  plan_query(index_reader, &mut plan, &queries[0], score),
        _ => {
            let mut query_iter = queries.iter();
            plan_query(index_reader, &mut plan, query_iter.next().unwrap(), score);

            for query in query_iter {
                plan_query(index_reader, &mut plan, query, score);
                plan.boolean_query.push(join_op.clone());
            }
        }
    }

    plan.score_function.push(ScoreFunctionOp::CombinatorScorer(queries.len() as u32, scorer));
}

pub fn plan_query(index_reader: &RocksDBIndexReader,mut plan: &mut SearchPlan, query: &Query, score: bool) {
    match *query {
        Query::MatchAll{ref score} => {
            plan.boolean_query.push(BooleanQueryOp::PushFull);
            plan.score_function.push(ScoreFunctionOp::Literal(*score));
        }
        Query::MatchNone => {
            plan.boolean_query.push(BooleanQueryOp::PushEmpty);
            plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
        }
        Query::MatchTerm{ref field, ref term, ref scorer} => {
            // Get field
            let field_ref = match index_reader.schema().get_field_by_name(field) {
                Some(field_ref) => field_ref,
                None => {
                    // Field doesn't exist, so will never match
                    plan.boolean_query.push(BooleanQueryOp::PushEmpty);
                    plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
                    return
                }
            };

            // Get term
            let term_bytes = term.to_bytes();
            let term_ref = match index_reader.store.term_dictionary.get(&term_bytes) {
                Some(term_ref) => term_ref,
                None => {
                    // Term doesn't exist, so will never match
                    plan.boolean_query.push(BooleanQueryOp::PushEmpty);
                    plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
                    return
                }
            };

            plan.boolean_query.push(BooleanQueryOp::PushTermDirectory(field_ref, term_ref));
            plan.score_function.push(ScoreFunctionOp::TermScorer(field_ref, term_ref, scorer.clone()));
        }
        Query::MatchMultiTerm{ref field, ref term_selector, ref scorer} => {
            // Get field
            let field_ref = match index_reader.schema().get_field_by_name(field) {
                Some(field_ref) => field_ref,
                None => {
                    // Field doesn't exist, so will never match
                    plan.boolean_query.push(BooleanQueryOp::PushEmpty);
                    plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
                    return
                }
            };

            // Get terms
            plan.boolean_query.push(BooleanQueryOp::PushEmpty);
            let mut total_terms = 0;
            for term_ref in index_reader.store.term_dictionary.select(term_selector) {
                plan.boolean_query.push(BooleanQueryOp::PushTermDirectory(field_ref, term_ref));
                plan.boolean_query.push(BooleanQueryOp::Or);
                plan.score_function.push(ScoreFunctionOp::TermScorer(field_ref, term_ref, scorer.clone()));
                total_terms += 1;
            }

            // This query must push only one score value onto the stack.
            // If we haven't pushed any score operations, Push a literal 0.0
            // If we have pushed more than one score operations, which will lead to more
            // than one score value being pushed to the stack, combine the score values
            // with a combinator operation.
            match total_terms {
                0 => plan.score_function.push(ScoreFunctionOp::Literal(0.0f64)),
                1 => {},
                _ => plan.score_function.push(ScoreFunctionOp::CombinatorScorer(total_terms, CombinatorScorer::Avg)),
            }
        }
        Query::Conjunction{ref queries} => {
            plan_query_combinator(index_reader, &mut plan, queries, BooleanQueryOp::And, score, CombinatorScorer::Avg);
        }
        Query::Disjunction{ref queries} => {
            plan_query_combinator(index_reader, &mut plan, queries, BooleanQueryOp::Or, score, CombinatorScorer::Avg);
        }
        Query::DisjunctionMax{ref queries} => {
            plan_query_combinator(index_reader, &mut plan, queries, BooleanQueryOp::Or, score, CombinatorScorer::Max);
        }
        Query::Filter{ref query, ref filter} => {
            plan_query(index_reader, &mut plan, query, score);
            plan_query(index_reader, &mut plan, filter, false);
            plan.boolean_query.push(BooleanQueryOp::And);
        }
        Query::Exclude{ref query, ref exclude} => {
            plan_query(index_reader, &mut plan, query, score);
            plan_query(index_reader, &mut plan, exclude, false);
            plan.boolean_query.push(BooleanQueryOp::AndNot);
        }
    }
}
<commit_msg>Split plan_query into two separate functions<commit_after>pub mod boolean_query;

use kite::Query;

use RocksDBIndexReader;
use search::boolean_retrieval::BooleanQueryOp;
use search::scoring::{CombinatorScorer, ScoreFunctionOp};

#[derive(Debug)]
pub struct SearchPlan {
    pub boolean_query: Vec<BooleanQueryOp>,
    pub boolean_query_is_negated: bool,
    pub score_function: Vec<ScoreFunctionOp>,
}

impl SearchPlan {
    pub fn new() -> SearchPlan {
        SearchPlan {
            boolean_query: Vec::new(),
            boolean_query_is_negated: false,
            score_function: Vec::new(),
        }
    }
}

fn plan_boolean_query_combinator(index_reader: &RocksDBIndexReader, mut plan: &mut SearchPlan, queries: &Vec<Query>, join_op: BooleanQueryOp) {
    match queries.len() {
        0 => {
            plan.boolean_query.push(BooleanQueryOp::PushEmpty);
        }
        1 =>  plan_boolean_query(index_reader, &mut plan, &queries[0]),
        _ => {
            let mut query_iter = queries.iter();
            plan_boolean_query(index_reader, &mut plan, query_iter.next().unwrap());

            for query in query_iter {
                plan_boolean_query(index_reader, &mut plan, query);
                plan.boolean_query.push(join_op.clone());
            }
        }
    }
}

fn plan_boolean_query(index_reader: &RocksDBIndexReader, mut plan: &mut SearchPlan, query: &Query) {
    match *query {
        Query::MatchAll{ref score} => {
            plan.boolean_query.push(BooleanQueryOp::PushFull);
        }
        Query::MatchNone => {
            plan.boolean_query.push(BooleanQueryOp::PushEmpty);
        }
        Query::MatchTerm{ref field, ref term, ref scorer} => {
            // Get field
            let field_ref = match index_reader.schema().get_field_by_name(field) {
                Some(field_ref) => field_ref,
                None => {
                    // Field doesn't exist, so will never match
                    plan.boolean_query.push(BooleanQueryOp::PushEmpty);
                    return
                }
            };

            // Get term
            let term_bytes = term.to_bytes();
            let term_ref = match index_reader.store.term_dictionary.get(&term_bytes) {
                Some(term_ref) => term_ref,
                None => {
                    // Term doesn't exist, so will never match
                    plan.boolean_query.push(BooleanQueryOp::PushEmpty);
                    return
                }
            };

            plan.boolean_query.push(BooleanQueryOp::PushTermDirectory(field_ref, term_ref));
        }
        Query::MatchMultiTerm{ref field, ref term_selector, ref scorer} => {
            // Get field
            let field_ref = match index_reader.schema().get_field_by_name(field) {
                Some(field_ref) => field_ref,
                None => {
                    // Field doesn't exist, so will never match
                    plan.boolean_query.push(BooleanQueryOp::PushEmpty);
                    return
                }
            };

            // Get terms
            plan.boolean_query.push(BooleanQueryOp::PushEmpty);
            for term_ref in index_reader.store.term_dictionary.select(term_selector) {
                plan.boolean_query.push(BooleanQueryOp::PushTermDirectory(field_ref, term_ref));
                plan.boolean_query.push(BooleanQueryOp::Or);
            }
        }
        Query::Conjunction{ref queries} => {
            plan_boolean_query_combinator(index_reader, &mut plan, queries, BooleanQueryOp::And);
        }
        Query::Disjunction{ref queries} => {
            plan_boolean_query_combinator(index_reader, &mut plan, queries, BooleanQueryOp::Or);
        }
        Query::DisjunctionMax{ref queries} => {
            plan_boolean_query_combinator(index_reader, &mut plan, queries, BooleanQueryOp::Or);
        }
        Query::Filter{ref query, ref filter} => {
            plan_boolean_query(index_reader, &mut plan, query);
            plan_boolean_query(index_reader, &mut plan, filter);
            plan.boolean_query.push(BooleanQueryOp::And);
        }
        Query::Exclude{ref query, ref exclude} => {
            plan_boolean_query(index_reader, &mut plan, query);
            plan_boolean_query(index_reader, &mut plan, exclude);
            plan.boolean_query.push(BooleanQueryOp::AndNot);
        }
    }
}

fn plan_score_function_combinator(index_reader: &RocksDBIndexReader, mut plan: &mut SearchPlan, queries: &Vec<Query>, scorer: CombinatorScorer) {
    match queries.len() {
        0 => {
            plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
        }
        1 =>  plan_score_function(index_reader, &mut plan, &queries[0]),
        _ => {
            let mut query_iter = queries.iter();
            plan_score_function(index_reader, &mut plan, query_iter.next().unwrap());

            for query in query_iter {
                plan_score_function(index_reader, &mut plan, query);
            }
        }
    }

    plan.score_function.push(ScoreFunctionOp::CombinatorScorer(queries.len() as u32, scorer));
}

fn plan_score_function(index_reader: &RocksDBIndexReader, mut plan: &mut SearchPlan, query: &Query) {
    match *query {
        Query::MatchAll{ref score} => {
            plan.score_function.push(ScoreFunctionOp::Literal(*score));
        }
        Query::MatchNone => {
            plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
        }
        Query::MatchTerm{ref field, ref term, ref scorer} => {
            // Get field
            let field_ref = match index_reader.schema().get_field_by_name(field) {
                Some(field_ref) => field_ref,
                None => {
                    // Field doesn't exist, so will never match
                    plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
                    return
                }
            };

            // Get term
            let term_bytes = term.to_bytes();
            let term_ref = match index_reader.store.term_dictionary.get(&term_bytes) {
                Some(term_ref) => term_ref,
                None => {
                    // Term doesn't exist, so will never match
                    plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
                    return
                }
            };

            plan.score_function.push(ScoreFunctionOp::TermScorer(field_ref, term_ref, scorer.clone()));
        }
        Query::MatchMultiTerm{ref field, ref term_selector, ref scorer} => {
            // Get field
            let field_ref = match index_reader.schema().get_field_by_name(field) {
                Some(field_ref) => field_ref,
                None => {
                    // Field doesn't exist, so will never match
                    plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
                    return
                }
            };

            // Get terms
            let mut total_terms = 0;
            for term_ref in index_reader.store.term_dictionary.select(term_selector) {
                plan.score_function.push(ScoreFunctionOp::TermScorer(field_ref, term_ref, scorer.clone()));
                total_terms += 1;
            }

            // This query must push only one score value onto the stack.
            // If we haven't pushed any score operations, Push a literal 0.0
            // If we have pushed more than one score operations, which will lead to more
            // than one score value being pushed to the stack, combine the score values
            // with a combinator operation.
            match total_terms {
                0 => plan.score_function.push(ScoreFunctionOp::Literal(0.0f64)),
                1 => {},
                _ => plan.score_function.push(ScoreFunctionOp::CombinatorScorer(total_terms, CombinatorScorer::Avg)),
            }
        }
        Query::Conjunction{ref queries} => {
            plan_score_function_combinator(index_reader, &mut plan, queries, CombinatorScorer::Avg);
        }
        Query::Disjunction{ref queries} => {
            plan_score_function_combinator(index_reader, &mut plan, queries, CombinatorScorer::Avg);
        }
        Query::DisjunctionMax{ref queries} => {
            plan_score_function_combinator(index_reader, &mut plan, queries, CombinatorScorer::Max);
        }
        Query::Filter{ref query, ref filter} => {
            plan_score_function(index_reader, &mut plan, query);
        }
        Query::Exclude{ref query, ref exclude} => {
            plan_score_function(index_reader, &mut plan, query);
        }
    }
}

pub fn plan_query(index_reader: &RocksDBIndexReader, mut plan: &mut SearchPlan, query: &Query, score: bool) {
    plan_boolean_query(index_reader, plan, query);

    if score {
        plan_score_function(index_reader, plan, query);
    } else {
        plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
    }
}
<|endoftext|>