<commit_before>use std::{collections::BTreeMap, io};

use rayon::prelude::*;

use self::reader::LogReader;

use super::*;

pub struct LogIter {
    pub config: Config,
    pub segment_iter: Box<dyn Iterator<Item = (Lsn, LogId)>>,
    pub segment_base: Option<LogId>,
    pub max_lsn: Lsn,
    pub cur_lsn: Lsn,
}

impl Iterator for LogIter {
    type Item = (LogKind, PageId, Lsn, DiskPtr, usize);

    fn next(&mut self) -> Option<Self::Item> {
        // If segment is None, get next on segment_iter, panic
        // if we can't read something we expect to be able to,
        // return None if there are no more remaining segments.
        loop {
            let remaining_seg_too_small_for_msg = !valid_entry_offset(
                self.cur_lsn as LogId,
                self.config.io_buf_size,
            );

            if self.segment_base.is_none() || remaining_seg_too_small_for_msg {
                if let Some((next_lsn, next_lid)) = self.segment_iter.next() {
                    assert!(
                        next_lsn + (self.config.io_buf_size as Lsn)
                            >= self.cur_lsn,
                        "caller is responsible for providing segments \
                         that contain the initial cur_lsn value or higher"
                    );

                    #[cfg(target_os = "linux")]
                    self.fadvise_willneed(next_lid);

                    if let Err(e) = self.read_segment(next_lsn, next_lid) {
                        debug!(
                            "hit snap while reading segments in \
                             iterator: {:?}",
                            e
                        );
                        return None;
                    }
                } else {
                    trace!("no segments remaining to iterate over");
                    return None;
                }
            }

            if self.cur_lsn > self.max_lsn {
                // all done
                trace!("hit max_lsn in iterator, stopping");
                return None;
            }

            let lid = self.segment_base.unwrap()
                + (self.cur_lsn % self.config.io_buf_size as Lsn) as LogId;

            let f = &self.config.file;

            match f.read_message(lid, self.cur_lsn, &self.config) {
                Ok(LogRead::Blob(header, _buf, blob_ptr)) => {
                    trace!("read blob flush in LogIter::next");
                    let sz = MSG_HEADER_LEN + BLOB_INLINE_LEN;
                    self.cur_lsn += sz as Lsn;

                    return Some((
                        LogKind::from(header.kind),
                        header.pid,
                        header.lsn,
                        DiskPtr::Blob(lid, blob_ptr),
                        sz,
                    ));
                }
                Ok(LogRead::Inline(header, _buf, on_disk_len)) => {
                    trace!("read inline flush in LogIter::next");
                    let sz = MSG_HEADER_LEN + on_disk_len as usize;
                    self.cur_lsn += sz as Lsn;

                    return Some((
                        LogKind::from(header.kind),
                        header.pid,
                        header.lsn,
                        DiskPtr::Inline(lid),
                        sz,
                    ));
                }
                Ok(LogRead::BatchManifest(last_lsn_in_batch)) => {
                    if last_lsn_in_batch > self.max_lsn {
                        return None;
                    } else {
                        self.cur_lsn +=
                            (MSG_HEADER_LEN + BATCH_MANIFEST_INLINE_LEN) as Lsn;
                        continue;
                    }
                }
                Ok(LogRead::Failed(_, on_disk_len)) => {
                    trace!("read zeroed in LogIter::next");
                    self.cur_lsn +=
                        Lsn::from(MSG_HEADER_LEN as u32 + on_disk_len);
                }
                Ok(LogRead::Corrupted(_len)) => {
                    trace!(
                        "read corrupted msg in LogIter::next as lid {} lsn {}",
                        lid,
                        self.cur_lsn
                    );
                    return None;
                }
                Ok(LogRead::Pad(_lsn)) => {
                    self.segment_base.take();

                    continue;
                }
                Ok(LogRead::DanglingBlob(header, blob_ptr)) => {
                    debug!(
                        "encountered dangling blob \
                         pointer at lsn {} ptr {}",
                        header.lsn, blob_ptr
                    );
                    self.cur_lsn += (MSG_HEADER_LEN + BLOB_INLINE_LEN) as Lsn;
                    continue;
                }
                Err(e) => {
                    debug!(
                        "failed to read log message at lid {} \
                         with expected lsn {} during iteration: {}",
                        lid, self.cur_lsn, e
                    );
                    return None;
                }
            }
        }
    }
}

impl LogIter {
    /// read a segment of log messages. Only call after
    /// pausing segment rewriting on the segment accountant!
    fn read_segment(&mut self, lsn: Lsn, offset: LogId) -> Result<()> {
        trace!(
            "LogIter::read_segment lsn: {:?} cur_lsn: {:?}",
            lsn,
            self.cur_lsn
        );
        // we add segment_len to this check because we may be getting the
        // initial segment that is a bit behind where we left off before.
        assert!(lsn + self.config.io_buf_size as Lsn >= self.cur_lsn);
        let f = &self.config.file;
        let segment_header = f.read_segment_header(offset)?;
        if offset % self.config.io_buf_size as LogId != 0 {
            debug!("segment offset not divisible by segment length");
            return Err(Error::Corruption {
                at: DiskPtr::Inline(offset),
            });
        }
        if segment_header.lsn % self.config.io_buf_size as Lsn != 0 {
            debug!(
                "expected a segment header lsn that is divisible \
                 by the io_buf_size ({}) instead it was {}",
                self.config.io_buf_size, segment_header.lsn
            );
            return Err(Error::Corruption {
                at: DiskPtr::Inline(offset),
            });
        }

        if segment_header.lsn != lsn {
            // this page was torn, nothing to read
            debug!(
                "segment header lsn ({}) != expected lsn ({})",
                segment_header.lsn, lsn
            );
            return Err(io::Error::new(
                io::ErrorKind::Other,
                "encountered torn segment",
            )
            .into());
        }

        trace!("read segment header {:?}", segment_header);

        self.cur_lsn = segment_header.lsn + SEG_HEADER_LEN as Lsn;
        self.segment_base = Some(offset);

        Ok(())
    }

    #[cfg(target_os = "linux")]
    fn fadvise_willneed(&self, lid: LogId) {
        use std::os::unix::io::AsRawFd;

        let f = &self.config.file;
        let ret = unsafe {
            libc::posix_fadvise(
                f.as_raw_fd(),
                libc::off_t::try_from(lid).unwrap(),
                libc::off_t::try_from(self.config.io_buf_size).unwrap(),
                libc::POSIX_FADV_WILLNEED,
            )
        };
        if ret != 0 {
            panic!(
                "failed to call fadvise: {}",
                std::io::Error::from_raw_os_error(ret)
            );
        }
    }
}

fn valid_entry_offset(lid: LogId, segment_len: usize) -> bool {
    let seg_start = lid / segment_len as LogId * segment_len as LogId;

    let max_lid = seg_start + segment_len as LogId - MSG_HEADER_LEN as LogId;

    let min_lid = seg_start + SEG_HEADER_LEN as LogId;

    lid >= min_lid && lid <= max_lid
}

// Scan the log file if we don't know of any Lsn offsets yet,
// and recover the order of segments, and the highest Lsn.
fn scan_segment_lsns(
    min: Lsn,
    config: &Config,
) -> Result<(BTreeMap<Lsn, LogId>, Lsn)> {
    let segment_len = config.io_buf_size as LogId;

    let f = &config.file;
    let file_len = f.metadata()?.len();
    let segments = LogId::from(file_len) / segment_len;
    let headers_res: Result<Vec<(LogId, SegmentHeader)>> = (0..segments)
        .into_par_iter()
        .map(|idx| {
            let base_lid = idx * segment_len;
            let segment = LogReader::read_segment_header(&**f, base_lid)?;
            trace!(
                "SA scanned header at lid {} during startup: {:?}",
                base_lid,
                segment
            );
            assert_ne!(segment.lsn, Lsn::max_value());
            Ok((base_lid, segment))
        })
        .collect();

    let mut headers = headers_res?;

    headers.retain(|(_, segment)| segment.lsn >= min);

    let mut ordering = BTreeMap::new();
    let mut max_header_stable_lsn = 0;

    for (lid, header) in headers {
        max_header_stable_lsn =
            std::cmp::max(header.max_stable_lsn, max_header_stable_lsn);

        let old = ordering.insert(header.lsn, lid);
        assert_eq!(
            old, None,
            "duplicate segment LSN {} detected at both {} and {}, \
             one should have been zeroed out during recovery",
            header.lsn, ordering[&header.lsn], lid
        );
    }

    debug!("ordering before clearing tears: {:?}", ordering);

    // Check that the segments above max_header_stable_lsn
    // properly link their previous segment pointers.
    let ordering =
        clean_tail_tears(max_header_stable_lsn, ordering, config, &f)?;

    Ok((ordering, max_header_stable_lsn))
}

// This ensures that the last <# io buffers> segments on
// disk connect via their previous segment pointers in
// the header. This is important because we expect that
// the last <# io buffers> segments will join up, and we
// never reuse buffers within this safety range.
fn clean_tail_tears(
    max_header_stable_lsn: Lsn,
    mut ordering: BTreeMap<Lsn, LogId>,
    config: &Config,
    f: &std::fs::File,
) -> Result<BTreeMap<Lsn, LogId>> {
    let io_buf_size = config.io_buf_size as Lsn;
    let lowest_lsn_in_tail: Lsn =
        ((max_header_stable_lsn / io_buf_size) - 1) * io_buf_size;

    let logical_tail = ordering
        .range(lowest_lsn_in_tail..)
        .map(|(lsn, lid)| (*lsn, *lid))
        .collect::<Vec<_>>();

    let iter = LogIter {
        config: config.clone(),
        segment_iter: Box::new(logical_tail.into_iter()),
        segment_base: None,
        max_lsn: Lsn::max_value(),
        cur_lsn: 0,
    };

    let tip: (Lsn, LogId) = iter
        .max_by_key(|(_kind, _pid, lsn, _ptr, _sz)| *lsn)
        .map(|(_, _, lsn, ptr, _)| (lsn, ptr.lid()))
        .unwrap_or_else(|| {
            if max_header_stable_lsn > 0 {
                (max_header_stable_lsn, ordering[&max_header_stable_lsn])
            } else {
                (0, 0)
            }
        });

    debug!(
        "filtering out segments after detected tear at lsn {} lid {}",
        tip.0, tip.1
    );
    for (lsn, lid) in ordering
        .range((std::ops::Bound::Excluded(tip.0), std::ops::Bound::Unbounded))
    {
        error!("filtering out segment with lsn {} at lid {}", lsn, lid);

        f.pwrite_all(
            &*vec![MessageKind::Corrupted.into(); SEG_HEADER_LEN],
            *lid,
        )?;
        f.sync_all()?;
    }

    ordering = ordering
        .into_iter()
        .filter(|&(lsn, _lid)| lsn <= tip.0)
        .collect();

    Ok(ordering)
}

pub(super) fn raw_segment_iter_from(
    lsn: Lsn,
    config: &Config,
) -> Result<(LogIter, Lsn)> {
    let segment_len = config.io_buf_size as Lsn;
    let normalized_lsn = lsn / segment_len * segment_len;

    let (ordering, max_header_stable_lsn) = scan_segment_lsns(0, &config)?;

    trace!(
        "generated iterator over segments {:?} with lsn >= {}",
        ordering,
        normalized_lsn
    );

    let segment_iter = Box::new(
        ordering
            .into_iter()
            .filter(move |&(l, _)| l >= normalized_lsn),
    );

    Ok((
        LogIter {
            config: config.clone(),
            max_lsn: Lsn::max_value(),
            cur_lsn: SEG_HEADER_LEN as Lsn,
            segment_base: None,
            segment_iter,
        },
        max_header_stable_lsn,
    ))
}
<commit_msg>adjust segment header collection<commit_after>use std::{collections::BTreeMap, io};

use rayon::prelude::*;

use self::reader::LogReader;

use super::*;

pub struct LogIter {
    pub config: Config,
    pub segment_iter: Box<dyn Iterator<Item = (Lsn, LogId)>>,
    pub segment_base: Option<LogId>,
    pub max_lsn: Lsn,
    pub cur_lsn: Lsn,
}

impl Iterator for LogIter {
    type Item = (LogKind, PageId, Lsn, DiskPtr, usize);

    fn next(&mut self) -> Option<Self::Item> {
        // If segment is None, get next on segment_iter, panic
        // if we can't read something we expect to be able to,
        // return None if there are no more remaining segments.
        loop {
            let remaining_seg_too_small_for_msg = !valid_entry_offset(
                self.cur_lsn as LogId,
                self.config.io_buf_size,
            );

            if self.segment_base.is_none() || remaining_seg_too_small_for_msg {
                if let Some((next_lsn, next_lid)) = self.segment_iter.next() {
                    assert!(
                        next_lsn + (self.config.io_buf_size as Lsn)
                            >= self.cur_lsn,
                        "caller is responsible for providing segments \
                         that contain the initial cur_lsn value or higher"
                    );

                    #[cfg(target_os = "linux")]
                    self.fadvise_willneed(next_lid);

                    if let Err(e) = self.read_segment(next_lsn, next_lid) {
                        debug!(
                            "hit snap while reading segments in \
                             iterator: {:?}",
                            e
                        );
                        return None;
                    }
                } else {
                    trace!("no segments remaining to iterate over");
                    return None;
                }
            }

            if self.cur_lsn > self.max_lsn {
                // all done
                trace!("hit max_lsn in iterator, stopping");
                return None;
            }

            let lid = self.segment_base.unwrap()
                + (self.cur_lsn % self.config.io_buf_size as Lsn) as LogId;

            let f = &self.config.file;

            match f.read_message(lid, self.cur_lsn, &self.config) {
                Ok(LogRead::Blob(header, _buf, blob_ptr)) => {
                    trace!("read blob flush in LogIter::next");
                    let sz = MSG_HEADER_LEN + BLOB_INLINE_LEN;
                    self.cur_lsn += sz as Lsn;

                    return Some((
                        LogKind::from(header.kind),
                        header.pid,
                        header.lsn,
                        DiskPtr::Blob(lid, blob_ptr),
                        sz,
                    ));
                }
                Ok(LogRead::Inline(header, _buf, on_disk_len)) => {
                    trace!("read inline flush in LogIter::next");
                    let sz = MSG_HEADER_LEN + on_disk_len as usize;
                    self.cur_lsn += sz as Lsn;

                    return Some((
                        LogKind::from(header.kind),
                        header.pid,
                        header.lsn,
                        DiskPtr::Inline(lid),
                        sz,
                    ));
                }
                Ok(LogRead::BatchManifest(last_lsn_in_batch)) => {
                    if last_lsn_in_batch > self.max_lsn {
                        return None;
                    } else {
                        self.cur_lsn +=
                            (MSG_HEADER_LEN + BATCH_MANIFEST_INLINE_LEN) as Lsn;
                        continue;
                    }
                }
                Ok(LogRead::Failed(_, on_disk_len)) => {
                    trace!("read zeroed in LogIter::next");
                    self.cur_lsn +=
                        Lsn::from(MSG_HEADER_LEN as u32 + on_disk_len);
                }
                Ok(LogRead::Corrupted(_len)) => {
                    trace!(
                        "read corrupted msg in LogIter::next as lid {} lsn {}",
                        lid,
                        self.cur_lsn
                    );
                    return None;
                }
                Ok(LogRead::Pad(_lsn)) => {
                    self.segment_base.take();

                    continue;
                }
                Ok(LogRead::DanglingBlob(header, blob_ptr)) => {
                    debug!(
                        "encountered dangling blob \
                         pointer at lsn {} ptr {}",
                        header.lsn, blob_ptr
                    );
                    self.cur_lsn += (MSG_HEADER_LEN + BLOB_INLINE_LEN) as Lsn;
                    continue;
                }
                Err(e) => {
                    debug!(
                        "failed to read log message at lid {} \
                         with expected lsn {} during iteration: {}",
                        lid, self.cur_lsn, e
                    );
                    return None;
                }
            }
        }
    }
}

impl LogIter {
    /// read a segment of log messages. Only call after
    /// pausing segment rewriting on the segment accountant!
    fn read_segment(&mut self, lsn: Lsn, offset: LogId) -> Result<()> {
        trace!(
            "LogIter::read_segment lsn: {:?} cur_lsn: {:?}",
            lsn,
            self.cur_lsn
        );
        // we add segment_len to this check because we may be getting the
        // initial segment that is a bit behind where we left off before.
        assert!(lsn + self.config.io_buf_size as Lsn >= self.cur_lsn);
        let f = &self.config.file;
        let segment_header = f.read_segment_header(offset)?;
        if offset % self.config.io_buf_size as LogId != 0 {
            debug!("segment offset not divisible by segment length");
            return Err(Error::Corruption {
                at: DiskPtr::Inline(offset),
            });
        }
        if segment_header.lsn % self.config.io_buf_size as Lsn != 0 {
            debug!(
                "expected a segment header lsn that is divisible \
                 by the io_buf_size ({}) instead it was {}",
                self.config.io_buf_size, segment_header.lsn
            );
            return Err(Error::Corruption {
                at: DiskPtr::Inline(offset),
            });
        }

        if segment_header.lsn != lsn {
            // this page was torn, nothing to read
            debug!(
                "segment header lsn ({}) != expected lsn ({})",
                segment_header.lsn, lsn
            );
            return Err(io::Error::new(
                io::ErrorKind::Other,
                "encountered torn segment",
            )
            .into());
        }

        trace!("read segment header {:?}", segment_header);

        self.cur_lsn = segment_header.lsn + SEG_HEADER_LEN as Lsn;
        self.segment_base = Some(offset);

        Ok(())
    }

    #[cfg(target_os = "linux")]
    fn fadvise_willneed(&self, lid: LogId) {
        use std::os::unix::io::AsRawFd;

        let f = &self.config.file;
        let ret = unsafe {
            libc::posix_fadvise(
                f.as_raw_fd(),
                libc::off_t::try_from(lid).unwrap(),
                libc::off_t::try_from(self.config.io_buf_size).unwrap(),
                libc::POSIX_FADV_WILLNEED,
            )
        };
        if ret != 0 {
            panic!(
                "failed to call fadvise: {}",
                std::io::Error::from_raw_os_error(ret)
            );
        }
    }
}

fn valid_entry_offset(lid: LogId, segment_len: usize) -> bool {
    let seg_start = lid / segment_len as LogId * segment_len as LogId;

    let max_lid = seg_start + segment_len as LogId - MSG_HEADER_LEN as LogId;

    let min_lid = seg_start + SEG_HEADER_LEN as LogId;

    lid >= min_lid && lid <= max_lid
}

// Scan the log file if we don't know of any Lsn offsets yet,
// and recover the order of segments, and the highest Lsn.
fn scan_segment_lsns(
    min: Lsn,
    config: &Config,
) -> Result<(BTreeMap<Lsn, LogId>, Lsn)> {
    let segment_len = config.io_buf_size as LogId;

    let f = &config.file;
    let file_len = f.metadata()?.len();
    let segments = LogId::from(file_len) / segment_len;
    let headers: Vec<(LogId, SegmentHeader)> = (0..segments)
        .into_par_iter()
        .filter_map(|idx| {
            let base_lid = idx * segment_len;
            let segment = f.read_segment_header(base_lid).ok()?;
            trace!(
                "SA scanned header at lid {} during startup: {:?}",
                base_lid,
                segment
            );
            if segment.ok && segment.lsn >= min {
                assert_ne!(segment.lsn, Lsn::max_value());
                Some((base_lid, segment))
            } else {
                None
            }
        })
        .collect();

    let mut ordering = BTreeMap::new();
    let mut max_header_stable_lsn = 0;

    for (lid, header) in headers {
        max_header_stable_lsn =
            std::cmp::max(header.max_stable_lsn, max_header_stable_lsn);

        let old = ordering.insert(header.lsn, lid);
        assert_eq!(
            old, None,
            "duplicate segment LSN {} detected at both {} and {}, \
             one should have been zeroed out during recovery",
            header.lsn, ordering[&header.lsn], lid
        );
    }

    debug!("ordering before clearing tears: {:?}", ordering);

    // Check that the segments above max_header_stable_lsn
    // properly link their previous segment pointers.
    let ordering =
        clean_tail_tears(max_header_stable_lsn, ordering, config, &f)?;

    Ok((ordering, max_header_stable_lsn))
}

// This ensures that the last <# io buffers> segments on
// disk connect via their previous segment pointers in
// the header. This is important because we expect that
// the last <# io buffers> segments will join up, and we
// never reuse buffers within this safety range.
fn clean_tail_tears(
    max_header_stable_lsn: Lsn,
    mut ordering: BTreeMap<Lsn, LogId>,
    config: &Config,
    f: &std::fs::File,
) -> Result<BTreeMap<Lsn, LogId>> {
    let io_buf_size = config.io_buf_size as Lsn;
    let lowest_lsn_in_tail: Lsn =
        ((max_header_stable_lsn / io_buf_size) - 1) * io_buf_size;

    let logical_tail = ordering
        .range(lowest_lsn_in_tail..)
        .map(|(lsn, lid)| (*lsn, *lid))
        .collect::<Vec<_>>();

    let iter = LogIter {
        config: config.clone(),
        segment_iter: Box::new(logical_tail.into_iter()),
        segment_base: None,
        max_lsn: Lsn::max_value(),
        cur_lsn: 0,
    };

    let tip: (Lsn, LogId) = iter
        .max_by_key(|(_kind, _pid, lsn, _ptr, _sz)| *lsn)
        .map(|(_, _, lsn, ptr, _)| (lsn, ptr.lid()))
        .unwrap_or_else(|| {
            if max_header_stable_lsn > 0 {
                (max_header_stable_lsn, ordering[&max_header_stable_lsn])
            } else {
                (0, 0)
            }
        });

    debug!(
        "filtering out segments after detected tear at lsn {} lid {}",
        tip.0, tip.1
    );
    for (lsn, lid) in ordering
        .range((std::ops::Bound::Excluded(tip.0), std::ops::Bound::Unbounded))
    {
        error!("filtering out segment with lsn {} at lid {}", lsn, lid);

        f.pwrite_all(
            &*vec![MessageKind::Corrupted.into(); SEG_HEADER_LEN],
            *lid,
        )?;
        f.sync_all()?;
    }

    ordering = ordering
        .into_iter()
        .filter(|&(lsn, _lid)| lsn <= tip.0)
        .collect();

    Ok(ordering)
}

pub(super) fn raw_segment_iter_from(
    lsn: Lsn,
    config: &Config,
) -> Result<(LogIter, Lsn)> {
    let segment_len = config.io_buf_size as Lsn;
    let normalized_lsn = lsn / segment_len * segment_len;

    let (ordering, max_header_stable_lsn) = scan_segment_lsns(0, &config)?;

    trace!(
        "generated iterator over segments {:?} with lsn >= {}",
        ordering,
        normalized_lsn
    );

    let segment_iter = Box::new(
        ordering
            .into_iter()
            .filter(move |&(l, _)| l >= normalized_lsn),
    );

    Ok((
        LogIter {
            config: config.clone(),
            max_lsn: Lsn::max_value(),
            cur_lsn: SEG_HEADER_LEN as Lsn,
            segment_base: None,
            segment_iter,
        },
        max_header_stable_lsn,
    ))
}
<|endoftext|>


<commit_before>//! Advertizes the capabilities of the LSP Server.

use lsp_types::{
    CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
    DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability,
    ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions,
    SelectionRangeProviderCapability, ServerCapabilities, SignatureHelpOptions,
    TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
    TypeDefinitionProviderCapability, WorkDoneProgressOptions,
};

pub fn server_capabilities() -> ServerCapabilities {
    ServerCapabilities {
        text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
            open_close: Some(true),
            change: Some(TextDocumentSyncKind::Full),
            will_save: None,
            will_save_wait_until: None,
            save: Some(SaveOptions::default()),
        })),
        hover_provider: Some(true),
        completion_provider: Some(CompletionOptions {
            resolve_provider: None,
            trigger_characters: Some(vec![":".to_string(), ".".to_string()]),
            work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
        }),
        signature_help_provider: Some(SignatureHelpOptions {
            trigger_characters: Some(vec!["(".to_string(), ",".to_string()]),
            retrigger_characters: None,
            work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
        }),
        declaration_provider: None,
        definition_provider: Some(true),
        type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
        implementation_provider: Some(ImplementationProviderCapability::Simple(true)),
        references_provider: Some(true),
        document_highlight_provider: Some(true),
        document_symbol_provider: Some(true),
        workspace_symbol_provider: Some(true),
        code_action_provider: Some(CodeActionProviderCapability::Simple(true)),
        code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
        document_formatting_provider: Some(true),
        document_range_formatting_provider: None,
        document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
            first_trigger_character: "=".to_string(),
            more_trigger_character: Some(vec![".".to_string(), ">".to_string()]),
        }),
        selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
        semantic_highlighting: None,
        folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
        rename_provider: Some(RenameProviderCapability::Options(RenameOptions {
            prepare_provider: Some(true),
            work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
        })),
        document_link_provider: None,
        color_provider: None,
        execute_command_provider: None,
        workspace: None,
        experimental: Default::default(),
    }
}
<commit_msg>Fix back compat<commit_after>//! Advertizes the capabilities of the LSP Server.

use lsp_types::{
    CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
    DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability,
    ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions,
    SelectionRangeProviderCapability, ServerCapabilities, SignatureHelpOptions,
    TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
    TypeDefinitionProviderCapability, WorkDoneProgressOptions,
};

pub fn server_capabilities() -> ServerCapabilities {
    ServerCapabilities {
        text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
            open_close: Some(true),
            change: Some(TextDocumentSyncKind::Full),
            will_save: None,
            will_save_wait_until: None,
            save: Some(SaveOptions::default()),
        })),
        hover_provider: Some(true),
        completion_provider: Some(CompletionOptions {
            resolve_provider: None,
            trigger_characters: Some(vec![":".to_string(), ".".to_string()]),
            work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
        }),
        signature_help_provider: Some(SignatureHelpOptions {
            trigger_characters: Some(vec!["(".to_string(), ",".to_string()]),
            retrigger_characters: None,
            work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
        }),
        declaration_provider: None,
        definition_provider: Some(true),
        type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
        implementation_provider: Some(ImplementationProviderCapability::Simple(true)),
        references_provider: Some(true),
        document_highlight_provider: Some(true),
        document_symbol_provider: Some(true),
        workspace_symbol_provider: Some(true),
        code_action_provider: Some(CodeActionProviderCapability::Simple(true)),
        code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
        document_formatting_provider: Some(true),
        document_range_formatting_provider: None,
        document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
            first_trigger_character: "=".to_string(),
            more_trigger_character: Some(vec![".".to_string(), ">".to_string()]),
        }),
        selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
        semantic_highlighting: None,
        folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
        rename_provider: Some(RenameProviderCapability::Options(RenameOptions {
            prepare_provider: Some(true),
            work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
        })),
        document_link_provider: None,
        color_provider: None,
        execute_command_provider: None,
        workspace: None,
        call_hierarchy_provider: None,
        experimental: Default::default(),
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use fnv::FnvHashMap;
use std::collections::hash_map::Entry;
use std::cmp;
use std::marker::PhantomData;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::Weak;
use std::thread;

use command_buffer::pool::CommandPool;
use command_buffer::pool::CommandPoolAlloc;
use command_buffer::pool::CommandPoolBuilderAlloc;
use command_buffer::pool::UnsafeCommandPool;
use command_buffer::pool::UnsafeCommandPoolAlloc;
use instance::QueueFamily;

use OomError;
use VulkanObject;
use device::Device;
use device::DeviceOwned;

/// Standard implementation of a command pool.
///
/// Will use one Vulkan pool per thread in order to avoid locking. Will try to reuse command
/// buffers. Command buffers can't be moved between threads during the building process, but
/// finished command buffers can.
pub struct StandardCommandPool {
    // The device.
    device: Arc<Device>,

    // Identifier of the queue family.
    queue_family: u32,

    // For each thread, we store thread-specific info.
    per_thread: Mutex<FnvHashMap<thread::ThreadId, Weak<Mutex<StandardCommandPoolPerThread>>>>,
}

unsafe impl Send for StandardCommandPool {
}
unsafe impl Sync for StandardCommandPool {
}

struct StandardCommandPoolPerThread {
    // The Vulkan pool of this thread.
    pool: UnsafeCommandPool,
    // List of existing primary command buffers that are available for reuse.
    available_primary_command_buffers: Vec<UnsafeCommandPoolAlloc>,
    // List of existing secondary command buffers that are available for reuse.
    available_secondary_command_buffers: Vec<UnsafeCommandPoolAlloc>,
}

impl StandardCommandPool {
    /// Builds a new pool.
    ///
    /// # Panic
    ///
    /// - Panics if the device and the queue family don't belong to the same physical device.
    ///
    pub fn new(device: Arc<Device>, queue_family: QueueFamily) -> StandardCommandPool {
        assert_eq!(device.physical_device().internal_object(),
                   queue_family.physical_device().internal_object());

        StandardCommandPool {
            device: device,
            queue_family: queue_family.id(),
            per_thread: Mutex::new(Default::default()),
        }
    }
}

unsafe impl CommandPool for Arc<StandardCommandPool> {
    type Iter = Box<Iterator<Item = StandardCommandPoolBuilder>>; // TODO: meh for Box
    type Builder = StandardCommandPoolBuilder;
    type Alloc = StandardCommandPoolAlloc;

    fn alloc(&self, secondary: bool, count: u32) -> Result<Self::Iter, OomError> {
        // Find the correct `StandardCommandPoolPerThread` structure.
        let mut hashmap = self.per_thread.lock().unwrap();
        // TODO: meh for iterating everything every time
        hashmap.retain(|_, w| w.upgrade().is_some());

        // Get an appropriate `Arc<Mutex<StandardCommandPoolPerThread>>`.
        let per_thread = match hashmap.entry(thread::current().id()) {
            Entry::Occupied(mut entry) => {
                if let Some(entry) = entry.get().upgrade() {
                    entry
                } else {
                    let new_pool =
                        UnsafeCommandPool::new(self.device.clone(), self.queue_family(), false, true)?;
                    let pt = Arc::new(Mutex::new(StandardCommandPoolPerThread {
                                                    pool: new_pool,
                                                    available_primary_command_buffers: Vec::new(),
                                                    available_secondary_command_buffers: Vec::new(),
                                                }));

                    entry.insert(Arc::downgrade(&pt));
                    pt
                }
            },
            Entry::Vacant(entry) => {
                let new_pool =
                    UnsafeCommandPool::new(self.device.clone(), self.queue_family(), false, true)?;
                let pt = Arc::new(Mutex::new(StandardCommandPoolPerThread {
                                                 pool: new_pool,
                                                 available_primary_command_buffers: Vec::new(),
                                                 available_secondary_command_buffers: Vec::new(),
                                             }));

                entry.insert(Arc::downgrade(&pt));
                pt
            },
        };

        let mut pt_lock = per_thread.lock().unwrap();

        // Build an iterator to pick from already-existing command buffers.
        let (num_from_existing, from_existing) = {
            // Which list of already-existing command buffers we are going to pick CBs from.
            let mut existing = if secondary {
                &mut pt_lock.available_secondary_command_buffers
            } else {
                &mut pt_lock.available_primary_command_buffers
            };
            let num_from_existing = cmp::min(count as usize, existing.len());
            let from_existing = existing
                .drain(0 .. num_from_existing)
                .collect::<Vec<_>>()
                .into_iter();
            (num_from_existing, from_existing)
        };

        // Build an iterator to construct the missing command buffers from the Vulkan pool.
        let num_new = count as usize - num_from_existing;
        debug_assert!(num_new <= count as usize); // Check overflows.
        let newly_allocated = pt_lock.pool.alloc_command_buffers(secondary, num_new)?;

        // Returning them as a chain.
        let device = self.device.clone();
        let per_thread = per_thread.clone();
        let final_iter = from_existing
            .chain(newly_allocated)
            .map(move |cmd| {
                StandardCommandPoolBuilder {
                    inner: StandardCommandPoolAlloc {
                        cmd: Some(cmd),
                        pool: per_thread.clone(),
                        secondary: secondary,
                        device: device.clone(),
                    },
                    dummy_avoid_send_sync: PhantomData,
                }
            })
            .collect::<Vec<_>>();

        Ok(Box::new(final_iter.into_iter()))
    }

    #[inline]
    fn queue_family(&self) -> QueueFamily {
        self.device
            .physical_device()
            .queue_family_by_id(self.queue_family)
            .unwrap()
    }
}

unsafe impl DeviceOwned for StandardCommandPool {
    #[inline]
    fn device(&self) -> &Arc<Device> {
        &self.device
    }
}

/// Command buffer allocated from a `StandardCommandPool` and that is currently being built.
pub struct StandardCommandPoolBuilder {
    // The only difference between a `StandardCommandPoolBuilder` and a `StandardCommandPoolAlloc`
    // is that the former must not implement `Send` and `Sync`. Therefore we just share the structs.
    inner: StandardCommandPoolAlloc,
    // Unimplemented `Send` and `Sync` from the builder.
    dummy_avoid_send_sync: PhantomData<*const u8>,
}

unsafe impl CommandPoolBuilderAlloc for StandardCommandPoolBuilder {
    type Alloc = StandardCommandPoolAlloc;

    #[inline]
    fn inner(&self) -> &UnsafeCommandPoolAlloc {
        self.inner.inner()
    }

    #[inline]
    fn into_alloc(self) -> Self::Alloc {
        self.inner
    }

    #[inline]
    fn queue_family(&self) -> QueueFamily {
        self.inner.queue_family()
    }
}

unsafe impl DeviceOwned for StandardCommandPoolBuilder {
    #[inline]
    fn device(&self) -> &Arc<Device> {
        self.inner.device()
    }
}

/// Command buffer allocated from a `StandardCommandPool`.
pub struct StandardCommandPoolAlloc {
    // The actual command buffer. Must always be `Some`. Value extracted in the destructor.
    cmd: Option<UnsafeCommandPoolAlloc>,
    // We hold a reference to the command pool for our destructor.
    pool: Arc<Mutex<StandardCommandPoolPerThread>>,
    // True if secondary command buffer.
    secondary: bool,
    // The device we belong to. Necessary because of the `DeviceOwned` trait implementation.
    device: Arc<Device>,
}

unsafe impl Send for StandardCommandPoolAlloc {
}
unsafe impl Sync for StandardCommandPoolAlloc {
}

unsafe impl CommandPoolAlloc for StandardCommandPoolAlloc {
    #[inline]
    fn inner(&self) -> &UnsafeCommandPoolAlloc {
        self.cmd.as_ref().unwrap()
    }

    #[inline]
    fn queue_family(&self) -> QueueFamily {
        let queue_family_id = self.pool.lock().unwrap().pool.queue_family().id();

        self.device
            .physical_device()
            .queue_family_by_id(queue_family_id)
            .unwrap()
    }
}

unsafe impl DeviceOwned for StandardCommandPoolAlloc {
    #[inline]
    fn device(&self) -> &Arc<Device> {
        // Note that we could grab the device from `self.pool`. Unfortunately this requires a mutex
        // lock, so it isn't compatible with the API of `DeviceOwned`.
        &self.device
    }
}

impl Drop for StandardCommandPoolAlloc {
    fn drop(&mut self) {
        let mut pool = self.pool.lock().unwrap();

        if self.secondary {
            pool.available_secondary_command_buffers
                .push(self.cmd.take().unwrap());
        } else {
            pool.available_primary_command_buffers
                .push(self.cmd.take().unwrap());
        }
    }
}
<commit_msg>Use a lockfree Vec for command buffers<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use crossbeam::sync::MsQueue;
use fnv::FnvHashMap;
use std::collections::hash_map::Entry;
use std::marker::PhantomData;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::Weak;
use std::thread;
use std::vec::IntoIter as VecIntoIter;

use command_buffer::pool::CommandPool;
use command_buffer::pool::CommandPoolAlloc;
use command_buffer::pool::CommandPoolBuilderAlloc;
use command_buffer::pool::UnsafeCommandPool;
use command_buffer::pool::UnsafeCommandPoolAlloc;
use instance::QueueFamily;

use OomError;
use VulkanObject;
use device::Device;
use device::DeviceOwned;

/// Standard implementation of a command pool.
///
/// Will use one Vulkan pool per thread in order to avoid locking. Will try to reuse command
/// buffers. Command buffers can't be moved between threads during the building process, but
/// finished command buffers can.
pub struct StandardCommandPool {
    // The device.
    device: Arc<Device>,

    // Identifier of the queue family.
    queue_family: u32,

    // For each thread, we store thread-specific info.
    per_thread: Mutex<FnvHashMap<thread::ThreadId, Weak<StandardCommandPoolPerThread>>>,
}

unsafe impl Send for StandardCommandPool {
}
unsafe impl Sync for StandardCommandPool {
}

struct StandardCommandPoolPerThread {
    // The Vulkan pool of this thread.
    pool: Mutex<UnsafeCommandPool>,
    // List of existing primary command buffers that are available for reuse.
    available_primary_command_buffers: MsQueue<UnsafeCommandPoolAlloc>,
    // List of existing secondary command buffers that are available for reuse.
    available_secondary_command_buffers: MsQueue<UnsafeCommandPoolAlloc>,
}

impl StandardCommandPool {
    /// Builds a new pool.
    ///
    /// # Panic
    ///
    /// - Panics if the device and the queue family don't belong to the same physical device.
    ///
    pub fn new(device: Arc<Device>, queue_family: QueueFamily) -> StandardCommandPool {
        assert_eq!(device.physical_device().internal_object(),
                   queue_family.physical_device().internal_object());

        StandardCommandPool {
            device: device,
            queue_family: queue_family.id(),
            per_thread: Mutex::new(Default::default()),
        }
    }
}

unsafe impl CommandPool for Arc<StandardCommandPool> {
    type Iter = VecIntoIter<StandardCommandPoolBuilder>;
    type Builder = StandardCommandPoolBuilder;
    type Alloc = StandardCommandPoolAlloc;

    fn alloc(&self, secondary: bool, count: u32) -> Result<Self::Iter, OomError> {
        // Find the correct `StandardCommandPoolPerThread` structure.
        let mut hashmap = self.per_thread.lock().unwrap();
        // TODO: meh for iterating everything every time
        hashmap.retain(|_, w| w.upgrade().is_some());

        // Get an appropriate `Arc<StandardCommandPoolPerThread>`.
        let per_thread = match hashmap.entry(thread::current().id()) {
            Entry::Occupied(mut entry) => {
                if let Some(entry) = entry.get().upgrade() {
                    entry
                } else {
                    let new_pool =
                        UnsafeCommandPool::new(self.device.clone(), self.queue_family(), false, true)?;
                    let pt = Arc::new(StandardCommandPoolPerThread {
                                          pool: Mutex::new(new_pool),
                                          available_primary_command_buffers: MsQueue::new(),
                                          available_secondary_command_buffers: MsQueue::new(),
                                      });

                    entry.insert(Arc::downgrade(&pt));
                    pt
                }
            },
            Entry::Vacant(entry) => {
                let new_pool =
                    UnsafeCommandPool::new(self.device.clone(), self.queue_family(), false, true)?;
                let pt = Arc::new(StandardCommandPoolPerThread {
                                      pool: Mutex::new(new_pool),
                                      available_primary_command_buffers: MsQueue::new(),
                                      available_secondary_command_buffers: MsQueue::new(),
                                  });

                entry.insert(Arc::downgrade(&pt));
                pt
            },
        };

        // The final output.
        let mut output = Vec::with_capacity(count as usize);

        // First, pick from already-existing command buffers.
        {
            let existing = if secondary {
                &per_thread.available_secondary_command_buffers
            } else {
                &per_thread.available_primary_command_buffers
            };

            for _ in 0 .. count as usize {
                if let Some(cmd) = existing.try_pop() {
                    output.push(StandardCommandPoolBuilder {
                        inner: StandardCommandPoolAlloc {
                            cmd: Some(cmd),
                            pool: per_thread.clone(),
                            secondary: secondary,
                            device: self.device.clone(),
                        },
                        dummy_avoid_send_sync: PhantomData,
                    });
                } else {
                    break;
                }
            }
        };

        // Then allocate the rest.
        if output.len() < count as usize {
            let pool_lock = per_thread.pool.lock().unwrap();
            let num_new = count as usize - output.len();

            for cmd in pool_lock.alloc_command_buffers(secondary, num_new)? {
                output.push(StandardCommandPoolBuilder {
                    inner: StandardCommandPoolAlloc {
                        cmd: Some(cmd),
                        pool: per_thread.clone(),
                        secondary: secondary,
                        device: self.device.clone(),
                    },
                    dummy_avoid_send_sync: PhantomData,
                });
            }
        }

        // Final output.
        Ok(output.into_iter())
    }

    #[inline]
    fn queue_family(&self) -> QueueFamily {
        self.device
            .physical_device()
            .queue_family_by_id(self.queue_family)
            .unwrap()
    }
}

unsafe impl DeviceOwned for StandardCommandPool {
    #[inline]
    fn device(&self) -> &Arc<Device> {
        &self.device
    }
}

/// Command buffer allocated from a `StandardCommandPool` and that is currently being built.
pub struct StandardCommandPoolBuilder {
    // The only difference between a `StandardCommandPoolBuilder` and a `StandardCommandPoolAlloc`
    // is that the former must not implement `Send` and `Sync`. Therefore we just share the structs.
    inner: StandardCommandPoolAlloc,
    // Unimplemented `Send` and `Sync` from the builder.
    dummy_avoid_send_sync: PhantomData<*const u8>,
}

unsafe impl CommandPoolBuilderAlloc for StandardCommandPoolBuilder {
    type Alloc = StandardCommandPoolAlloc;

    #[inline]
    fn inner(&self) -> &UnsafeCommandPoolAlloc {
        self.inner.inner()
    }

    #[inline]
    fn into_alloc(self) -> Self::Alloc {
        self.inner
    }

    #[inline]
    fn queue_family(&self) -> QueueFamily {
        self.inner.queue_family()
    }
}

unsafe impl DeviceOwned for StandardCommandPoolBuilder {
    #[inline]
    fn device(&self) -> &Arc<Device> {
        self.inner.device()
    }
}

/// Command buffer allocated from a `StandardCommandPool`.
pub struct StandardCommandPoolAlloc {
    // The actual command buffer. Must always be `Some`. Value extracted in the destructor.
    cmd: Option<UnsafeCommandPoolAlloc>,
    // We hold a reference to the command pool for our destructor.
    pool: Arc<StandardCommandPoolPerThread>,
    // True if secondary command buffer.
    secondary: bool,
    // The device we belong to. Necessary because of the `DeviceOwned` trait implementation.
    device: Arc<Device>,
}

unsafe impl Send for StandardCommandPoolAlloc {
}
unsafe impl Sync for StandardCommandPoolAlloc {
}

unsafe impl CommandPoolAlloc for StandardCommandPoolAlloc {
    #[inline]
    fn inner(&self) -> &UnsafeCommandPoolAlloc {
        self.cmd.as_ref().unwrap()
    }

    #[inline]
    fn queue_family(&self) -> QueueFamily {
        let queue_family_id = self.pool.pool.lock().unwrap().queue_family().id();

        self.device
            .physical_device()
            .queue_family_by_id(queue_family_id)
            .unwrap()
    }
}

unsafe impl DeviceOwned for StandardCommandPoolAlloc {
    #[inline]
    fn device(&self) -> &Arc<Device> {
        // Note that we could grab the device from `self.pool`. Unfortunately this requires a mutex
        // lock, so it isn't compatible with the API of `DeviceOwned`.
        &self.device
    }
}

impl Drop for StandardCommandPoolAlloc {
    fn drop(&mut self) {
        let cmd = self.cmd.take().unwrap();

        if self.secondary {
            self.pool.available_secondary_command_buffers.push(cmd);
        } else {
            self.pool.available_primary_command_buffers.push(cmd);
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>rustfmt<commit_after><|endoftext|>


<commit_before>use crate::client;
use graphql_client::{GraphQLQuery, Response};

#[derive(GraphQLQuery)]
#[graphql(
    schema_path = "graphql/schema.json",
    query_path = "graphql/events.graphql",
    response_derives = "Debug"
)]
struct EventsQuery;

pub fn events(submission_id: String) {
    let body = EventsQuery::build_query(events_query::Variables { submission_id });
    let response: Response<events_query::ResponseData> = client::authenticated_request(&body);

    if let Some(errors) = response.errors {
        for error in errors {
            eprintln!("Error getting user informations: {}", error.message);
        }
        panic!("Error submitting query");
    }

    for event in response.data.unwrap().events {
        println!("{}", event.event_json);
    }
}
<commit_msg>rust: fix GraphQL schema path (merge conflict)<commit_after>use crate::client;
use graphql_client::{GraphQLQuery, Response};

#[derive(GraphQLQuery)]
#[graphql(
    schema_path = "__generated__/graphql-schema.json",
    query_path = "graphql/events.graphql",
    response_derives = "Debug"
)]
struct EventsQuery;

pub fn events(submission_id: String) {
    let body = EventsQuery::build_query(events_query::Variables { submission_id });
    let response: Response<events_query::ResponseData> = client::authenticated_request(&body);

    if let Some(errors) = response.errors {
        for error in errors {
            eprintln!("Error getting user informations: {}", error.message);
        }
        panic!("Error submitting query");
    }

    for event in response.data.unwrap().events {
        println!("{}", event.event_json);
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code, unused_imports, unused_variables)]

#[macro_use]
extern crate criterion;
extern crate regex_automata;

use criterion::{Bencher, Benchmark, Criterion, Throughput};
use regex_automata::{dense, RegexBuilder, DFA};

use inputs::*;

mod inputs;

fn is_match(c: &mut Criterion) {
    let corpus = SHERLOCK_HUGE;
    define(c, "is-match", "sherlock-huge", corpus, move |b| {
        let re = RegexBuilder::new()
            .anchored(false)
            .minimize(true)
            .premultiply(true)
            .byte_classes(false)
            .build(r"\p{Greek}")
            .unwrap();
        // let re = re.forward().to_sparse().unwrap();
        b.iter(|| {
            assert!(!re.is_match(corpus));
        });
    });

    // let corpus = OPEN_ZH_SMALL;
    let corpus = SHERLOCK_SMALL;
    define(c, "is-match", "sherlock-small", corpus, move |b| {
        let re = RegexBuilder::new()
            .anchored(false)
            .minimize(true)
            .premultiply(true)
            .byte_classes(false)
            .build(r"\p{Greek}")
            .unwrap();
        // let re = re.forward().to_sparse().unwrap();
        b.iter(|| {
            assert!(!re.is_match(corpus));
        });
    });

    let corpus = SHERLOCK_TINY;
    define(c, "is-match", "sherlock-tiny", corpus, move |b| {
        let re = RegexBuilder::new()
            .anchored(false)
            .minimize(true)
            .premultiply(true)
            .byte_classes(false)
            .build(r"\p{Greek}")
            .unwrap();
        b.iter(|| {
            assert!(!re.is_match(corpus));
        });
    });

    let corpus = EMPTY;
    define(c, "is-match", "empty", corpus, move |b| {
        let re = RegexBuilder::new()
            .anchored(false)
            .minimize(true)
            .premultiply(true)
            .byte_classes(false)
            .build(r"\p{Greek}")
            .unwrap();
        b.iter(|| {
            assert!(!re.is_match(corpus));
        });
    });
}

// \p{Other_Math} has 1,362 codepoints
fn compile_unicode_other_math(c: &mut Criterion) {
    define_compile(c, "unicode-other-math", r"\p{Other_Math}");
}

// \p{Other_Uppercase} has 120 codepoints
fn compile_unicode_other_uppercase(c: &mut Criterion) {
    define_compile(
        c,
        "unicode-other-uppercase",
        r"\p{any}*?\p{Other_Uppercase}",
    );
}

fn compile_muammar(c: &mut Criterion) {
    define_compile(
        c,
        "muammar",
        r"\p{any}*?M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]",
    );
}

fn define_compile(c: &mut Criterion, group_name: &str, pattern: &'static str) {
    let group = format!("compile/{}", group_name);
    define(c, &group, "unminimized-noclasses", &[], move |b| {
        b.iter(|| {
            let result = dense::Builder::new()
                .anchored(true)
                .minimize(false)
                .premultiply(false)
                .byte_classes(false)
                .build(pattern);
            assert!(result.is_ok());
        });
    });
    define(c, &group, "unminimized-classes", &[], move |b| {
        b.iter(|| {
            let result = dense::Builder::new()
                .anchored(true)
                .minimize(false)
                .premultiply(false)
                .byte_classes(true)
                .build(pattern);
            assert!(result.is_ok());
        });
    });
    // TODO: Minimization is too slow to benchmark for now...
    define(c, &group, "minimized-noclasses", &[], move |b| {
        let mut dfa = dense::Builder::new()
            .anchored(true)
            .minimize(false)
            .premultiply(false)
            .byte_classes(false)
            .build(pattern)
            .unwrap();
        let old = dfa.memory_usage();
        b.iter(|| {
            dfa.minimize();
            assert!(dfa.memory_usage() <= old);
        });
    });
    define(c, &group, "minimized-classes", &[], move |b| {
        let mut dfa = dense::Builder::new()
            .anchored(true)
            .minimize(false)
            .premultiply(false)
            .byte_classes(true)
            .build(pattern)
            .unwrap();
        let old = dfa.memory_usage();
        b.iter(|| {
            dfa.minimize();
            assert!(dfa.memory_usage() <= old);
        });
    });
}

fn define(
    c: &mut Criterion,
    group_name: &str,
    bench_name: &str,
    corpus: &[u8],
    bench: impl FnMut(&mut Bencher) + 'static,
) {
    let tput = Throughput::Bytes(corpus.len() as u64);
    let benchmark = Benchmark::new(bench_name, bench).throughput(tput);
    c.bench(group_name, benchmark);
}

criterion_group!(g1, is_match);
criterion_group!(g2, compile_unicode_other_math);
criterion_group!(g3, compile_unicode_other_uppercase);
criterion_group!(g4, compile_muammar);
criterion_main!(g1, g2, g3, g4);
<commit_msg>bench: add reverse compilation benchmark<commit_after>#[macro_use]
extern crate criterion;
extern crate regex_automata;

use std::time::Duration;

use criterion::{Bencher, Benchmark, Criterion, Throughput};
use regex_automata::{dense, RegexBuilder, DFA};

use inputs::*;

mod inputs;

fn is_match(c: &mut Criterion) {
    let corpus = SHERLOCK_HUGE;
    define(c, "is-match", "sherlock-huge", corpus, move |b| {
        let re = RegexBuilder::new()
            .anchored(false)
            .minimize(true)
            .premultiply(true)
            .byte_classes(false)
            .build(r"\p{Greek}")
            .unwrap();
        // let re = re.forward().to_sparse().unwrap();
        b.iter(|| {
            assert!(!re.is_match(corpus));
        });
    });

    // let corpus = OPEN_ZH_SMALL;
    let corpus = SHERLOCK_SMALL;
    define(c, "is-match", "sherlock-small", corpus, move |b| {
        let re = RegexBuilder::new()
            .anchored(false)
            .minimize(true)
            .premultiply(true)
            .byte_classes(false)
            .build(r"\p{Greek}")
            .unwrap();
        // let re = re.forward().to_sparse().unwrap();
        b.iter(|| {
            assert!(!re.is_match(corpus));
        });
    });

    let corpus = SHERLOCK_TINY;
    define(c, "is-match", "sherlock-tiny", corpus, move |b| {
        let re = RegexBuilder::new()
            .anchored(false)
            .minimize(true)
            .premultiply(true)
            .byte_classes(false)
            .build(r"\p{Greek}")
            .unwrap();
        b.iter(|| {
            assert!(!re.is_match(corpus));
        });
    });

    let corpus = EMPTY;
    define(c, "is-match", "empty", corpus, move |b| {
        let re = RegexBuilder::new()
            .anchored(false)
            .minimize(true)
            .premultiply(true)
            .byte_classes(false)
            .build(r"\p{Greek}")
            .unwrap();
        b.iter(|| {
            assert!(!re.is_match(corpus));
        });
    });
}

// \w has 128,640 codepoints.
fn compile_unicode_word(c: &mut Criterion) {
    define_compile(c, "unicode-word", r"\w");
    define_compile_reverse(c, "unicode-word", r"\w");
}

// \p{Other_Math} has 1,362 codepoints
fn compile_unicode_other_math(c: &mut Criterion) {
    define_compile(c, "unicode-other-math", r"\p{Other_Math}");
}

// \p{Other_Uppercase} has 120 codepoints
fn compile_unicode_other_uppercase(c: &mut Criterion) {
    define_compile(
        c,
        "unicode-other-uppercase",
        r"\p{any}*?\p{Other_Uppercase}",
    );
}

fn compile_muammar(c: &mut Criterion) {
    define_compile(
        c,
        "muammar",
        r"\p{any}*?M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]",
    );
}

fn define_compile(c: &mut Criterion, group_name: &str, pattern: &'static str) {
    let group = format!("fwd-compile/{}", group_name);
    define(c, &group, "unminimized-noclasses", &[], move |b| {
        b.iter(|| {
            let result = dense::Builder::new()
                .anchored(true)
                .minimize(false)
                .premultiply(false)
                .byte_classes(false)
                .build(pattern);
            assert!(result.is_ok());
        });
    });
    define(c, &group, "unminimized-classes", &[], move |b| {
        b.iter(|| {
            let result = dense::Builder::new()
                .anchored(true)
                .minimize(false)
                .premultiply(false)
                .byte_classes(true)
                .build(pattern);
            assert!(result.is_ok());
        });
    });
    define(c, &group, "minimized-noclasses", &[], move |b| {
        let mut dfa = dense::Builder::new()
            .anchored(true)
            .minimize(false)
            .premultiply(false)
            .byte_classes(false)
            .build(pattern)
            .unwrap();
        let old = dfa.memory_usage();
        b.iter(|| {
            dfa.minimize();
            assert!(dfa.memory_usage() <= old);
        });
    });
    define(c, &group, "minimized-classes", &[], move |b| {
        let mut dfa = dense::Builder::new()
            .anchored(true)
            .minimize(false)
            .premultiply(false)
            .byte_classes(true)
            .build(pattern)
            .unwrap();
        let old = dfa.memory_usage();
        b.iter(|| {
            dfa.minimize();
            assert!(dfa.memory_usage() <= old);
        });
    });
}

fn define_compile_reverse(
    c: &mut Criterion,
    group_name: &str,
    pattern: &'static str,
) {
    let group = format!("rev-compile/{}", group_name);
    define(c, &group, "unminimized-noclasses", &[], move |b| {
        b.iter(|| {
            let result = dense::Builder::new()
                .reverse(true)
                .anchored(true)
                .minimize(false)
                .premultiply(false)
                .byte_classes(false)
                .build(pattern);
            assert!(result.is_ok());
        });
    });
    define(c, &group, "unminimized-classes", &[], move |b| {
        b.iter(|| {
            let result = dense::Builder::new()
                .reverse(true)
                .anchored(true)
                .minimize(false)
                .premultiply(false)
                .byte_classes(true)
                .build(pattern);
            assert!(result.is_ok());
        });
    });
    define(c, &group, "minimized-noclasses", &[], move |b| {
        let mut dfa = dense::Builder::new()
            .reverse(true)
            .anchored(true)
            .minimize(false)
            .premultiply(false)
            .byte_classes(false)
            .build(pattern)
            .unwrap();
        let old = dfa.memory_usage();
        b.iter(|| {
            dfa.minimize();
            assert!(dfa.memory_usage() <= old);
        });
    });
    define(c, &group, "minimized-classes", &[], move |b| {
        let mut dfa = dense::Builder::new()
            .reverse(true)
            .anchored(true)
            .minimize(false)
            .premultiply(false)
            .byte_classes(true)
            .build(pattern)
            .unwrap();
        let old = dfa.memory_usage();
        b.iter(|| {
            dfa.minimize();
            assert!(dfa.memory_usage() <= old);
        });
    });
}

fn define(
    c: &mut Criterion,
    group_name: &str,
    bench_name: &str,
    corpus: &[u8],
    bench: impl FnMut(&mut Bencher) + 'static,
) {
    let tput = Throughput::Bytes(corpus.len() as u64);
    let benchmark = Benchmark::new(bench_name, bench)
        .throughput(tput)
        .sample_size(25)
        .warm_up_time(Duration::from_millis(500))
        .measurement_time(Duration::from_secs(3));
    c.bench(group_name, benchmark);
}

criterion_group!(g1, is_match);
criterion_group!(g2, compile_unicode_other_math);
criterion_group!(g3, compile_unicode_other_uppercase);
criterion_group!(g4, compile_muammar);
criterion_group!(g5, compile_unicode_word);
criterion_main!(g1, g2, g3, g4, g5);
<|endoftext|>


<commit_before>//! An asynchronous fibonacci sequence generator.

#[macro_use]
extern crate crossbeam_channel as channel;

use std::thread;
use crossbeam_channel::{bounded, Receiver, Sender};

fn fibonacci(fib: Sender<u64>, quit: Receiver<()>) {
    let (mut x, mut y) = (0, 1);
    loop {
        select! {
            send(fib, x) -> _ => {
                let tmp = x;
                x = y;
                y = tmp + y;
            }
            recv(quit) -> _ => {
                println!("quit");
                return;
            }
        }
    }
}

fn main() {
    let (fib_s, fib_r) = bounded(0);
    let (quit_s, quit_r) = bounded(0);

    thread::spawn(move || {
        for _ in 0..10 {
            println!("{}", fib_r.recv().unwrap());
        }
        quit_s.send(()).unwrap();
    });

    fibonacci(fib_s, quit_r);
}
<commit_msg>Remove the quit channel<commit_after>//! An asynchronous fibonacci sequence generator.

#[macro_use]
extern crate crossbeam_channel;

use std::thread;
use crossbeam_channel::{bounded, Sender};

fn fibonacci(sender: Sender<u64>) {
    let (mut x, mut y) = (0, 1);
    loop {
        select! {
            send(sender, x) -> res => match res {
                Err(_) => return,
                Ok(()) => {
                    let tmp = x;
                    x = y;
                    y = tmp + y;
                }
            }
        }
    }
}

fn main() {
    let (s, r) = bounded(0);

    thread::spawn(move || {
        for num in r.iter().take(20) {
            println!("{}", num);
        }
        drop(r);
    });

    fibonacci(s);
}
<|endoftext|>


<commit_before><commit_msg>Beutify the rust code<commit_after><|endoftext|>


<commit_before>
use mouros_rust_bindings::CVoid;
use mouros_rust_bindings::tasks;
use mouros_rust_bindings::tasks::CriticalLock;
use mouros_rust_bindings::pool_alloc::Pool;

use mouros_rust_bindings::mailbox;
use mouros_rust_bindings::mailbox::Mailbox;
use mouros_rust_bindings::mailbox::{TxChannelSpsc, RxChannelSpsc};

use bindings::message_dispatcher as md;

use bsp::i2c;

use core::ptr;

use core::mem;

use core::slice;
use core::str;

use core::convert::TryFrom;

mod bmp085;
mod tsl2651;

union MessagePayload {
    channel: u32
}

const GET_TEMPERATURE_MSG_ID: u32 = 0;
const GET_HUMIDITY_MSG_ID: u32 = 1;

static mut MESSAGE_ARR: Option<[md::message; 20]> = None;
static mut MESSAGE_POOL: Option<CriticalLock<Pool<md::message>>> = None;

static mut MESSAGE_PAYLOAD_ARR: Option<[MessagePayload; 20]> = None;
static mut MESSAGE_PAYLOAD_POOL: Option<CriticalLock<Pool<MessagePayload>>> = None;

unsafe extern "C" fn meteo_alloc(msg_type_id: u32) -> *mut md::message {
    let msg_pool = MESSAGE_POOL.as_ref().expect("message pool not initialized");
    let payload_pool = MESSAGE_PAYLOAD_POOL.as_ref().expect(
        "message payload pool not initialized",
    );

    match msg_type_id {
        GET_TEMPERATURE_MSG_ID |
        GET_HUMIDITY_MSG_ID => {
            let msg = msg_pool.lock().take().and_then(|m| if let Some(payload) =
                payload_pool.lock().take()
            {
                m.data = payload as *mut MessagePayload as *mut CVoid;
                Some(m)
            } else {
                meteo_free(m);
                None
            });

            msg.unwrap_or(&mut *ptr::null_mut())
        }
        _ => ptr::null_mut(),
    }
}

unsafe extern "C" fn meteo_free(msg: *mut md::message) {
    if msg.is_null() {
        return;
    }

    let msg_pool = MESSAGE_POOL.as_ref().expect("message pool not initialized");
    let payload_pool = MESSAGE_PAYLOAD_POOL.as_ref().expect(
        "message payload pool not initialized",
    );

    match (*msg).msg_type {
        GET_TEMPERATURE_MSG_ID |
        GET_HUMIDITY_MSG_ID => {
            if !(*msg).data.is_null() {
                payload_pool.lock().give(
                    &mut *((*msg).data as
                        *mut MessagePayload),
                );
            }
            msg_pool.lock().give(&mut *msg);
        }
        _ => {}
    }
}

unsafe fn cstr_ptr_to_str_slice<'a>(cstr_ptr: *mut u8) -> Result<&'a mut str, str::Utf8Error> {
    let mut len = 0;
    while *cstr_ptr.offset(len) != b'\0' {
        len += 1;
    }

    str::from_utf8_mut(slice::from_raw_parts_mut(cstr_ptr, len as usize))
}

unsafe extern "C" fn get_temperature_parse(msg_ptr: *mut md::message, save_ptr: *mut u8) -> bool {
    if msg_ptr.is_null() || (*msg_ptr).data.is_null() || save_ptr.is_null() {
        return false;
    }

    let payload = unwrap_or_ret_false!(cstr_ptr_to_str_slice(save_ptr));
    let ch = unwrap_or_ret_false!(payload.parse::<u32>());

    let msg_data = (*msg_ptr).data as *mut MessagePayload;
    (*msg_data).channel = ch;

    true
}

unsafe extern "C" fn get_humidity_parse(msg_ptr: *mut md::message, save_ptr: *mut u8) -> bool {
    if msg_ptr.is_null() || (*msg_ptr).data.is_null() || save_ptr.is_null() {
        return false;
    }

    let payload = unwrap_or_ret_false!(cstr_ptr_to_str_slice(save_ptr));
    let ch = unwrap_or_ret_false!(payload.parse::<u32>());

    let msg_data = (*msg_ptr).data as *mut MessagePayload;
    (*msg_data).channel = ch;

    true
}

enum IncomingMsg<'payload> {
    GetTemperature(&'payload u32),
    GetHumidity(&'payload u32),
}

impl<'payload> md::MemManagement for IncomingMsg<'payload> {
    fn alloc(msg_type: u32) -> *mut md::message {
        unsafe { meteo_alloc(msg_type) }
    }

    fn free(msg_ptr: *mut md::message) {
        unsafe {
            meteo_free(msg_ptr);
        }
    }
}

impl<'payload> TryFrom<*mut md::message> for IncomingMsg<'payload> {
    type Error = ();

    fn try_from(raw_msg_ptr: *mut md::message) -> Result<Self, Self::Error> {
        unsafe {
            if raw_msg_ptr.is_null() || (*raw_msg_ptr).data.is_null() {
                return Err(());
            }

            match (*raw_msg_ptr).msg_type {
                GET_TEMPERATURE_MSG_ID => Ok(IncomingMsg::GetTemperature(
                    &*((*raw_msg_ptr).data as *const u32),
                )),
                GET_HUMIDITY_MSG_ID => Ok(IncomingMsg::GetHumidity(
                    &*((*raw_msg_ptr).data as *const u32),
                )),
                _ => Err(()),
            }
        }
    }
}

static mut RX_QUEUE_ARR: Option<[*mut md::message; 20]> = None;
static mut RX_QUEUE: Option<Mailbox<*mut md::message>> = None;

static mut TX_QUEUE_ARR: Option<[*mut md::message; 20]> = None;
static mut TX_QUEUE: Option<Mailbox<*mut md::message>> = None;

static mut ERR_QUEUE_ARR: Option<[i32; 20]> = None;
static mut ERR_QUEUE: Option<Mailbox<i32>> = None;

static mut MSG_HANDLERS: Option<[md::message_handler; 2]> = None;
static mut MSG_CONF: Option<md::subsystem_message_conf> = None;

struct MeteoTaskCtx<'mb, 'mem: 'mb> {
    rx_msg_queue: RxChannelSpsc<'mb, 'mem, *mut md::message>,
    tx_msg_queue: TxChannelSpsc<'mb, 'mem, *mut md::message>,
    err_msg_queue: TxChannelSpsc<'mb, 'mem, i32>,
    hum_sensor: bmp085::Bmp085,
    light_sensor: tsl2651::Tsl2561,
}

static mut METEO_CTX: Option<MeteoTaskCtx> = None;

#[no_mangle]
pub unsafe extern "C" fn rust_meteo_init() -> *const CVoid {
    if METEO_CTX.is_some() {
        panic!("Meteo module already initialized.");
    }

    RX_QUEUE_ARR = Some(mem::zeroed());
    RX_QUEUE = Some(Mailbox::new(RX_QUEUE_ARR.as_mut().unwrap().as_mut()));

    TX_QUEUE_ARR = Some(mem::zeroed());
    TX_QUEUE = Some(Mailbox::new(TX_QUEUE_ARR.as_mut().unwrap().as_mut()));

    ERR_QUEUE_ARR = Some(mem::zeroed());
    ERR_QUEUE = Some(Mailbox::new(ERR_QUEUE_ARR.as_mut().unwrap().as_mut()));

    MESSAGE_ARR = Some(mem::zeroed());
    MESSAGE_POOL = Some(CriticalLock::new(
        Pool::new(MESSAGE_ARR.as_mut().unwrap().as_mut()),
    ));

    MESSAGE_PAYLOAD_ARR = Some(mem::zeroed());
    MESSAGE_PAYLOAD_POOL = Some(CriticalLock::new(
        Pool::new(MESSAGE_PAYLOAD_ARR.as_mut().unwrap().as_mut()),
    ));

    let (rx_msg_queue, _) = mailbox::channel_spsc(RX_QUEUE.as_mut().unwrap());
    let (_, tx_msg_queue) = mailbox::channel_spsc(TX_QUEUE.as_mut().unwrap());
    let (_, err_msg_queue) = mailbox::channel_spsc(ERR_QUEUE.as_mut().unwrap());

    MSG_HANDLERS = Some(
        [
            md::message_handler {
                message_name: b"GET_TEMPERATURE\0" as *const u8,
                parsing_func: Some(get_temperature_parse),
                serialization_func: None,
            },
            md::message_handler {
                message_name: b"GET_HUMIDITY\0" as *const u8,
                parsing_func: Some(get_humidity_parse),
                serialization_func: None,
            },
        ],
    );

    MSG_CONF = Some(md::subsystem_message_conf {
        subsystem_name: b"METEO\0" as *const u8,
        message_handlers: MSG_HANDLERS.as_ref().unwrap() as *const md::message_handler,
        num_message_types: 2,
        incoming_msg_queue: rx_msg_queue.get_raw_mailbox(),
        outgoing_msg_queue: tx_msg_queue.get_raw_mailbox(),
        outgoing_err_queue: err_msg_queue.get_raw_mailbox(),
        alloc_message: Some(meteo_alloc),
        free_message: Some(meteo_free),
    });

    md::dispatcher_register_subsystem(MSG_CONF.as_mut().unwrap());

    let mut hum_sensor = bmp085::Bmp085::new(i2c::Peripheral::I2C3);
    hum_sensor.set_precision(bmp085::Precision::UltraHigh);

    let light_sensor = tsl2651::Tsl2561::new(i2c::Peripheral::I2C3);

    METEO_CTX = Some(MeteoTaskCtx {
        rx_msg_queue,
        tx_msg_queue,
        err_msg_queue,
        hum_sensor,
        light_sensor,
    });

    METEO_CTX.as_ref().unwrap() as *const MeteoTaskCtx as *const CVoid
}

use bindings::bsp as bb;

#[no_mangle]
pub extern "C" fn rust_meteo_comm_loop(ctx_raw: *mut CVoid) {

    let ctx = unsafe { &mut *(ctx_raw as *mut MeteoTaskCtx) };

    if let Some(msg_ptr) = ctx.rx_msg_queue.read() {
        if let Ok(msg) = md::MessageWrapper::try_from(msg_ptr) {
            match *msg {
                IncomingMsg::GetTemperature(ch) => bb::led_toggle(bb::BoardLed::LED1),
                IncomingMsg::GetHumidity(ch) => bb::led_toggle(bb::BoardLed::LED2),
            }
        }
    }

    tasks::sleep(50);
}
<commit_msg>Implemented temp, humidity, and pressure messages<commit_after>
use mouros_rust_bindings::CVoid;
use mouros_rust_bindings::tasks;
use mouros_rust_bindings::tasks::CriticalLock;
use mouros_rust_bindings::pool_alloc::Pool;

use mouros_rust_bindings::mailbox;
use mouros_rust_bindings::mailbox::Mailbox;
use mouros_rust_bindings::mailbox::{TxChannelSpsc, RxChannelSpsc};
use mouros_rust_bindings::mailbox::{SendError, RecvError};

use bindings::message_dispatcher as md;
use bindings::message_dispatcher::MemManagement;

use bsp::i2c;

use core::ptr;

use core::mem;

use core::slice;
use core::str;

use core::convert::TryFrom;

use core::fmt::Write;

mod bmp085;
mod tsl2651;

union MessagePayload {
    channel: u32,
    value: f32
}

const GET_TEMPERATURE_MSG_ID: u32 = 0;
const GET_PRESSURE_MSG_ID: u32 = 1;
const GET_HUMIDITY_MSG_ID: u32 = 2;

const TEMPERATURE_REPLY_MSG_ID: u32 = 3;
const PRESSURE_REPLY_MSG_ID: u32 = 4;
const HUMIDITY_REPLY_MSG_ID: u32 = 5;

static mut MESSAGE_ARR: Option<[md::message; 20]> = None;
static mut MESSAGE_POOL: Option<CriticalLock<Pool<md::message>>> = None;

static mut MESSAGE_PAYLOAD_ARR: Option<[MessagePayload; 20]> = None;
static mut MESSAGE_PAYLOAD_POOL: Option<CriticalLock<Pool<MessagePayload>>> = None;

unsafe extern "C" fn meteo_alloc(msg_type_id: u32) -> *mut md::message {
    let msg_pool = MESSAGE_POOL.as_ref().expect("message pool not initialized");
    let payload_pool = MESSAGE_PAYLOAD_POOL.as_ref().expect(
        "message payload pool not initialized",
    );

    match msg_type_id {
        GET_TEMPERATURE_MSG_ID |
        GET_PRESSURE_MSG_ID |
        GET_HUMIDITY_MSG_ID |
        TEMPERATURE_REPLY_MSG_ID |
        PRESSURE_REPLY_MSG_ID |
        HUMIDITY_REPLY_MSG_ID => {
            msg_pool
                .lock()
                .take()
                .and_then(|mut m| if let Some(mut payload) =
                    payload_pool.lock().take()
                {
                    m = mem::zeroed();
                    payload = mem::zeroed();

                    m.msg_type = msg_type_id;
                    m.data = payload as *mut MessagePayload as *mut CVoid;

                    Some(m)
                } else {
                    meteo_free(m);
                    None
                })
                .unwrap_or(&mut *ptr::null_mut())
        }
        _ => ptr::null_mut(),
    }
}

unsafe extern "C" fn meteo_free(msg: *mut md::message) {
    if msg.is_null() {
        return;
    }

    let msg_pool = MESSAGE_POOL.as_ref().expect("message pool not initialized");
    let payload_pool = MESSAGE_PAYLOAD_POOL.as_ref().expect(
        "message payload pool not initialized",
    );

    match (*msg).msg_type {
        GET_TEMPERATURE_MSG_ID |
        GET_PRESSURE_MSG_ID |
        GET_HUMIDITY_MSG_ID |
        TEMPERATURE_REPLY_MSG_ID |
        PRESSURE_REPLY_MSG_ID |
        HUMIDITY_REPLY_MSG_ID => {
            if !(*msg).data.is_null() {
                payload_pool.lock().give(
                    &mut *((*msg).data as
                        *mut MessagePayload),
                );
            }
            msg_pool.lock().give(&mut *msg);
        }
        _ => {}
    }
}

unsafe fn cstr_ptr_to_str_slice<'a>(cstr_ptr: *mut u8) -> Result<&'a mut str, str::Utf8Error> {
    let mut len = 0;
    while *cstr_ptr.offset(len) != b'\0' {
        len += 1;
    }

    str::from_utf8_mut(slice::from_raw_parts_mut(cstr_ptr, len as usize))
}

unsafe extern "C" fn single_channel_num_parse(
    msg_ptr: *mut md::message,
    save_ptr: *mut u8,
) -> bool {
    if msg_ptr.is_null() || (*msg_ptr).data.is_null() || save_ptr.is_null() {
        return false;
    }

    let payload = unwrap_or_ret_false!(cstr_ptr_to_str_slice(save_ptr));
    let ch = unwrap_or_ret_false!(payload.parse::<u32>());

    let msg_data = (*msg_ptr).data as *mut MessagePayload;
    (*msg_data).channel = ch;

    true
}

unsafe extern "C" fn single_floating_value_serialize(
    msg_ptr: *const md::message,
    output_str: *mut u8,
    output_str_max_len: u32,
) -> isize {
    if msg_ptr.is_null() || (*msg_ptr).data.is_null() {
        return -1;
    }

    let buf = slice::from_raw_parts_mut(output_str, output_str_max_len as usize);
    let mut w = md::CStrWriter::new(buf);

    if write!(w, "{}", *((*msg_ptr).data as *const f32)).is_ok() {
        w.len() as isize
    } else {
        -1
    }
}

enum IncomingMsg<'payload> {
    GetTemperature(&'payload u32),
    GetPressure(&'payload u32),
    GetHumidity(&'payload u32),
}

derive_message_wrapper!(IncomingMsg<'payload>, [
    GET_TEMPERATURE_MSG_ID => (|payload_ptr| {
        Ok(IncomingMsg::GetTemperature(&*(payload_ptr as *const u32)))
    }),
    GET_PRESSURE_MSG_ID => (|payload_ptr| {
        Ok(IncomingMsg::GetPressure(&*(payload_ptr as *const u32)))
    }),
    GET_HUMIDITY_MSG_ID => (|payload_ptr| {
        Ok(IncomingMsg::GetHumidity(&*(payload_ptr as *const u32)))
    })],
    meteo_alloc,
    meteo_free);

struct TemperatureReply<'payload>(&'payload mut f32);

derive_message_wrapper!(TemperatureReply<'payload>,
    TEMPERATURE_REPLY_MSG_ID => (|payload_ptr| Ok(TemperatureReply(&mut *(payload_ptr as *mut f32)))),
    meteo_alloc,
    meteo_free);

struct PressureReply<'payload>(&'payload mut f32);

derive_message_wrapper!(PressureReply<'payload>,
    PRESSURE_REPLY_MSG_ID => (|payload_ptr| Ok(PressureReply(&mut *(payload_ptr as *mut f32)))),
    meteo_alloc,
    meteo_free);

struct HumidityReply<'payload>(&'payload mut f32);

derive_message_wrapper!(HumidityReply<'payload>,
    HUMIDITY_REPLY_MSG_ID => (|payload_ptr| Ok(HumidityReply(&mut *(payload_ptr as *mut f32)))),
    meteo_alloc,
    meteo_free);

static mut RX_QUEUE_ARR: Option<[*mut md::message; 20]> = None;
static mut RX_QUEUE: Option<Mailbox<*mut md::message>> = None;

static mut TX_QUEUE_ARR: Option<[*mut md::message; 20]> = None;
static mut TX_QUEUE: Option<Mailbox<*mut md::message>> = None;

static mut ERR_QUEUE_ARR: Option<[i32; 20]> = None;
static mut ERR_QUEUE: Option<Mailbox<i32>> = None;

static mut MSG_HANDLERS: Option<[md::message_handler; 6]> = None;
static mut MSG_CONF: Option<md::subsystem_message_conf> = None;

struct MeteoTaskCtx<'mb, 'mem: 'mb> {
    rx_msg_queue: RxChannelSpsc<'mb, 'mem, *mut md::message>,
    tx_msg_queue: TxChannelSpsc<'mb, 'mem, *mut md::message>,
    err_msg_queue: TxChannelSpsc<'mb, 'mem, i32>,
    press_sensor: bmp085::Bmp085,
    light_sensor: tsl2651::Tsl2561,
}

impl<'mb, 'mem> MeteoTaskCtx<'mb, 'mem> {
    fn send_msg<T>(&self, msg: md::MessageWrapper<T>) -> Result<(), ()>
    where
        T: md::Wrappable,
    {
        self.tx_msg_queue.try_send(msg.into()).or_else(|err| {
            T::free(err.0);
            self.send_err(-2);
            Err(())
        })
    }

    fn send_err(&self, err_code: i32) {
        let _ = self.err_msg_queue.try_send(err_code);
    }
}

static mut METEO_CTX: Option<MeteoTaskCtx> = None;

#[no_mangle]
pub unsafe extern "C" fn rust_meteo_init() -> *const CVoid {
    if METEO_CTX.is_some() {
        panic!("Meteo module already initialized.");
    }

    RX_QUEUE_ARR = Some(mem::zeroed());
    RX_QUEUE = Some(Mailbox::new(RX_QUEUE_ARR.as_mut().unwrap().as_mut()));

    TX_QUEUE_ARR = Some(mem::zeroed());
    TX_QUEUE = Some(Mailbox::new(TX_QUEUE_ARR.as_mut().unwrap().as_mut()));

    ERR_QUEUE_ARR = Some(mem::zeroed());
    ERR_QUEUE = Some(Mailbox::new(ERR_QUEUE_ARR.as_mut().unwrap().as_mut()));

    MESSAGE_ARR = Some(mem::zeroed());
    MESSAGE_POOL = Some(CriticalLock::new(
        Pool::new(MESSAGE_ARR.as_mut().unwrap().as_mut()),
    ));

    MESSAGE_PAYLOAD_ARR = Some(mem::zeroed());
    MESSAGE_PAYLOAD_POOL = Some(CriticalLock::new(
        Pool::new(MESSAGE_PAYLOAD_ARR.as_mut().unwrap().as_mut()),
    ));

    let (rx_msg_queue, _) = mailbox::channel_spsc(RX_QUEUE.as_mut().unwrap());
    let (_, tx_msg_queue) = mailbox::channel_spsc(TX_QUEUE.as_mut().unwrap());
    let (_, err_msg_queue) = mailbox::channel_spsc(ERR_QUEUE.as_mut().unwrap());

    MSG_HANDLERS = Some(
        [
            md::message_handler {
                message_name: b"GET_TEMPERATURE\0" as *const u8,
                parsing_func: Some(single_channel_num_parse),
                serialization_func: None,
            },
            md::message_handler {
                message_name: b"GET_PRESSURE\0" as *const u8,
                parsing_func: Some(single_channel_num_parse),
                serialization_func: None,
            },
            md::message_handler {
                message_name: b"GET_HUMIDITY\0" as *const u8,
                parsing_func: Some(single_channel_num_parse),
                serialization_func: None,
            },
            md::message_handler {
                message_name: b"TEMPERATURE_REPLY\0" as *const u8,
                parsing_func: None,
                serialization_func: Some(single_floating_value_serialize),
            },
            md::message_handler {
                message_name: b"PRESSURE_REPLY\0" as *const u8,
                parsing_func: None,
                serialization_func: Some(single_floating_value_serialize),
            },
            md::message_handler {
                message_name: b"HUMIDITY_REPLY\0" as *const u8,
                parsing_func: None,
                serialization_func: Some(single_floating_value_serialize),
            },
        ],
    );

    MSG_CONF = Some(md::subsystem_message_conf {
        subsystem_name: b"METEO\0" as *const u8,
        message_handlers: MSG_HANDLERS.as_ref().unwrap() as *const md::message_handler,
        num_message_types: MSG_HANDLERS.as_ref().unwrap().len() as u32,
        incoming_msg_queue: rx_msg_queue.get_raw_mailbox(),
        outgoing_msg_queue: tx_msg_queue.get_raw_mailbox(),
        outgoing_err_queue: err_msg_queue.get_raw_mailbox(),
        alloc_message: Some(meteo_alloc),
        free_message: Some(meteo_free),
    });

    md::dispatcher_register_subsystem(MSG_CONF.as_mut().unwrap());

    let mut press_sensor = bmp085::Bmp085::new(i2c::Peripheral::I2C3);
    press_sensor.set_precision(bmp085::Precision::UltraHigh);

    let light_sensor = tsl2651::Tsl2561::new(i2c::Peripheral::I2C3);

    METEO_CTX = Some(MeteoTaskCtx {
        rx_msg_queue,
        tx_msg_queue,
        err_msg_queue,
        press_sensor,
        light_sensor,
    });

    METEO_CTX.as_ref().unwrap() as *const MeteoTaskCtx as *const CVoid
}

fn process_msg(ctx: &mut MeteoTaskCtx, msg: md::MessageWrapper<IncomingMsg>) {
    match *msg {
        IncomingMsg::GetTemperature(_ch) => {
            ctx.press_sensor.measure().and_then(|(t, _)| {

                md::MessageWrapper::new(msg.get_transaction_id(), TEMPERATURE_REPLY_MSG_ID)
                    .and_then(|mut reply: md::MessageWrapper<TemperatureReply>| {
                        *reply.0 = t;

                        ctx.send_msg(reply)
                    })
            });
        }
        IncomingMsg::GetPressure(_ch) => {
            ctx.press_sensor.measure().and_then(|(_, p)| {

                md::MessageWrapper::new(msg.get_transaction_id(), PRESSURE_REPLY_MSG_ID)
                    .and_then(|mut reply: md::MessageWrapper<PressureReply>| {
                        *reply.0 = p;

                        ctx.send_msg(reply)
                    })
            });
        }
        IncomingMsg::GetHumidity(ch) => {
            md::MessageWrapper::new(msg.get_transaction_id(), HUMIDITY_REPLY_MSG_ID)
                .and_then(|mut reply: md::MessageWrapper<HumidityReply>| {
                    *reply.0 = 0.0;

                    ctx.send_msg(reply)
                });
        }
    }
}

#[no_mangle]
pub extern "C" fn rust_meteo_comm_loop(ctx_raw: *mut CVoid) {

    let ctx = unsafe { &mut *(ctx_raw as *mut MeteoTaskCtx) };

    if let Ok(msg_ptr) = ctx.rx_msg_queue.try_recv() {
        if let Ok(msg) = md::MessageWrapper::try_from(msg_ptr) {
            process_msg(ctx, msg);
        } else {
            ctx.send_err(-1);
        }
    }

    tasks::sleep(50);
}
<|endoftext|>