<commit_before>//! # Working with `Log`
//!
//! ```
//! let config = pagecache::ConfigBuilder::new()
//!     .temporary(true)
//!     .segment_mode(pagecache::SegmentMode::Linear)
//!     .build();
//! let log = pagecache::Log::start_raw_log(config).unwrap();
//! let (first_lsn, _first_offset) = log.write(b"1").unwrap();
//! log.write(b"22").unwrap();
//! log.write(b"333").unwrap();
//!
//! // stick an abort in the middle, which should not be returned
//! let res = log.reserve(b"never_gonna_hit_disk").unwrap();
//! res.abort().unwrap();
//!
//! log.write(b"4444");
//! let (last_lsn, _last_offset) = log.write(b"55555").unwrap();
//! log.make_stable(last_lsn).unwrap();
//! let mut iter = log.iter_from(first_lsn);
//! assert_eq!(iter.next().unwrap().2, b"1");
//! assert_eq!(iter.next().unwrap().2, b"22");
//! assert_eq!(iter.next().unwrap().2, b"333");
//! assert_eq!(iter.next().unwrap().2, b"4444");
//! assert_eq!(iter.next().unwrap().2, b"55555");
//! assert_eq!(iter.next(), None);
//! ```
use super::*;

/// A sequential store which allows users to create
/// reservations placed at known log offsets, used
/// for writing persistent data structures that need
/// to know where to find persisted bits in the future.
pub struct Log {
    /// iobufs is the underlying lock-free IO write buffer.
    pub(super) iobufs: IoBufs,
    config: Config,
    /// Periodically flushes `iobufs`.
    _flusher: Option<flusher::Flusher>,
}

unsafe impl Send for Log {}

impl Log {
    /// Start the log, open or create the configured file,
    /// and optionally start the periodic buffer flush thread.
    pub fn start(
        config: Config,
        snapshot: Snapshot,
    ) -> Result<Log, ()> {
        let iobufs = IoBufs::start(config.clone(), snapshot)?;

        let iobufs_flusher = iobufs.clone();
        let flusher = config.flush_every_ms.map(move |fem| {
            flusher::Flusher::new(
                "log flusher".to_owned(),
                iobufs_flusher,
                fem,
            )
        });

        Ok(Log {
            iobufs,
            config,
            _flusher: flusher,
        })
    }

    /// Starts a log for use without a materializer.
    pub fn start_raw_log(config: Config) -> Result<Log, ()> {
        assert_eq!(config.segment_mode, SegmentMode::Linear);
        let log_iter = raw_segment_iter_from(0, &config)?;

        let snapshot = advance_snapshot::<NullMaterializer, ()>(
            log_iter,
            Snapshot::default(),
            &config,
        )?;

        Log::start(config, snapshot)
    }

    /// Flushes any pending IO buffers to disk to ensure durability.
    pub fn flush(&self) -> Result<(), ()> {
        self.iobufs.flush()
    }

    /// Reserve space in the log for a pending linearized operation.
    pub fn reserve(&self, buf: &[u8]) -> Result<Reservation<'_>, ()> {
        self.iobufs.reserve(buf)
    }

    /// Reserve a replacement buffer for a previously written
    /// blob write. This ensures the message header has the
    /// proper blob flag set.
    pub(super) fn reserve_blob(
        &self,
        blob_ptr: BlobPointer,
    ) -> Result<Reservation<'_>, ()> {
        self.iobufs.reserve_blob(blob_ptr)
    }

    /// Write a buffer into the log. Returns the log sequence
    /// number and the file offset of the write.
    pub fn write<B>(&self, buf: B) -> Result<(Lsn, DiskPtr), ()>
    where
        B: AsRef<[u8]>,
    {
        self.reserve(buf.as_ref()).and_then(|res| res.complete())
    }

    /// Return an iterator over the log, starting with
    /// a specified offset.
    pub fn iter_from(&self, lsn: Lsn) -> LogIter {
        self.iobufs.iter_from(lsn)
    }

    /// read a buffer from the disk
    pub fn read(
        &self,
        lsn: Lsn,
        ptr: DiskPtr,
    ) -> Result<LogRead, ()> {
        trace!("reading log lsn {} ptr {}", lsn, ptr);

        self.make_stable(lsn)?;

        if ptr.is_inline() {
            let lid = ptr.inline();
            let f = &self.config.file;

            let read = f.read_message(lid, &self.config);

            read.and_then(|log_read| match log_read {
                LogRead::Inline(read_lsn, _, _)
                | LogRead::Blob(read_lsn, _, _) => {
                    if lsn != read_lsn {
                        debug!(
                            "lsn of disk read is {} but we expected it to be {}",
                            read_lsn,
                            lsn,
                        );
                        Err(Error::Corruption { at: ptr })
                    } else {
                        Ok(log_read)
                    }
                }
                _ => Ok(log_read),
            })
        } else {
            let (_lid, blob_ptr) = ptr.blob();
            read_blob(blob_ptr, &self.config)
                .map(|buf| LogRead::Blob(lsn, buf, blob_ptr))
        }
    }

    /// returns the current stable offset written to disk
    pub fn stable_offset(&self) -> Lsn {
        self.iobufs.stable()
    }

    /// blocks until the specified log sequence number has
    /// been made stable on disk
    pub fn make_stable(&self, lsn: Lsn) -> Result<(), ()> {
        self.iobufs.make_stable(lsn)
    }

    // SegmentAccountant access for coordination with the `PageCache`
    pub(crate) fn with_sa<B, F>(&self, f: F) -> B
    where
        F: FnOnce(&mut SegmentAccountant) -> B,
    {
        self.iobufs.with_sa(f)
    }
}

/// Represents the kind of message written to the log
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) enum MessageKind {
    Inline,
    Blob,
    Failed,
    Pad,
    Corrupted,
}

/// All log messages are prepended with this header
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) struct MessageHeader {
    pub(crate) kind: MessageKind,
    pub(crate) lsn: Lsn,
    pub(crate) len: usize,
    pub(crate) crc32: u32,
}

/// A segment's header contains the new base LSN and a reference
/// to the previous log segment.
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) struct SegmentHeader {
    pub(crate) lsn: Lsn,
    pub(crate) ok: bool,
}

/// A segment's trailer contains the base Lsn for the segment.
/// It is written after the rest of the segment has been fsync'd,
/// and helps us indicate if a segment has been torn.
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) struct SegmentTrailer {
    pub(crate) lsn: Lsn,
    pub(crate) ok: bool,
}

#[doc(hidden)]
#[derive(Debug)]
pub enum LogRead {
    Inline(Lsn, Vec<u8>, usize),
    Blob(Lsn, Vec<u8>, BlobPointer),
    Failed(Lsn, usize),
    Pad(Lsn),
    Corrupted(usize),
    DanglingBlob(Lsn, BlobPointer),
}

impl LogRead {
    /// Optionally return successfully read inline bytes, or
    /// None if the data was corrupt or this log entry was aborted.
    pub fn inline(self) -> Option<(Lsn, Vec<u8>, usize)> {
        match self {
            LogRead::Inline(lsn, bytes, len) => {
                Some((lsn, bytes, len))
            }
            _ => None,
        }
    }

    /// Return true if this is an Inline value..
    pub fn is_inline(&self) -> bool {
        match *self {
            LogRead::Inline(_, _, _) => true,
            _ => false,
        }
    }

    /// Optionally return a successfully read pointer to an
    /// blob value, or None if the data was corrupt or
    /// this log entry was aborted.
    pub fn blob(self) -> Option<(Lsn, Vec<u8>, BlobPointer)> {
        match self {
            LogRead::Blob(lsn, buf, ptr) => Some((lsn, buf, ptr)),
            _ => None,
        }
    }

    /// Return true if we read a completed blob write successfully.
    pub fn is_blob(&self) -> bool {
        match self {
            LogRead::Blob(..) => true,
            _ => false,
        }
    }

    /// Return true if we read an aborted flush.
    pub fn is_failed(&self) -> bool {
        match *self {
            LogRead::Failed(_, _) => true,
            _ => false,
        }
    }

    /// Return true if we read a successful Inline or Blob value.
    pub fn is_successful(&self) -> bool {
        match *self {
            LogRead::Inline(_, _, _) | LogRead::Blob(_, _, _) => true,
            _ => false,
        }
    }

    /// Return true if we read a segment pad.
    pub fn is_pad(&self) -> bool {
        match *self {
            LogRead::Pad(_) => true,
            _ => false,
        }
    }

    /// Return true if we read a corrupted log entry.
    pub fn is_corrupt(&self) -> bool {
        match *self {
            LogRead::Corrupted(_) => true,
            _ => false,
        }
    }

    /// Return the underlying data read from a log read, if successful.
    pub fn into_data(self) -> Option<Vec<u8>> {
        match self {
            LogRead::Blob(_, buf, _) | LogRead::Inline(_, buf, _) => {
                Some(buf)
            }
            _ => None,
        }
    }
}

// NB we use a lot of xors below to differentiate between zeroed out
// data on disk and an lsn or crc32 of 0

impl From<[u8; MSG_HEADER_LEN]> for MessageHeader {
    fn from(buf: [u8; MSG_HEADER_LEN]) -> MessageHeader {
        let kind = match buf[0] {
            INLINE_FLUSH => MessageKind::Inline,
            BLOB_FLUSH => MessageKind::Blob,
            FAILED_FLUSH => MessageKind::Failed,
            SEGMENT_PAD => MessageKind::Pad,
            _ => MessageKind::Corrupted,
        };

        unsafe {
            let lsn = arr_to_u64(buf.get_unchecked(1..9)) as Lsn;
            let len = arr_to_u32(buf.get_unchecked(9..13));
            let crc32 =
                arr_to_u32(buf.get_unchecked(13..)) ^ 0xFFFF_FFFF;

            MessageHeader {
                kind,
                lsn,
                len: len as usize,
                crc32,
            }
        }
    }
}

impl Into<[u8; MSG_HEADER_LEN]> for MessageHeader {
    fn into(self) -> [u8; MSG_HEADER_LEN] {
        let mut buf = [0u8; MSG_HEADER_LEN];
        buf[0] = match self.kind {
            MessageKind::Inline => INLINE_FLUSH,
            MessageKind::Blob => BLOB_FLUSH,
            MessageKind::Failed => FAILED_FLUSH,
            MessageKind::Pad => SEGMENT_PAD,
            MessageKind::Corrupted => EVIL_BYTE,
        };

        assert!(self.len <= std::u32::MAX as usize);
        let lsn_arr = u64_to_arr(self.lsn as u64);
        let len_arr = u32_to_arr(self.len as u32);
        let crc32_arr = u32_to_arr(self.crc32 ^ 0xFFFF_FFFF);

        unsafe {
            std::ptr::copy_nonoverlapping(
                lsn_arr.as_ptr(),
                buf.as_mut_ptr().offset(1),
                std::mem::size_of::<u64>(),
            );
            std::ptr::copy_nonoverlapping(
                len_arr.as_ptr(),
                buf.as_mut_ptr().offset(9),
                std::mem::size_of::<u32>(),
            );
            std::ptr::copy_nonoverlapping(
                crc32_arr.as_ptr(),
                buf.as_mut_ptr().offset(13),
                std::mem::size_of::<u32>(),
            );
        }

        buf
    }
}

impl From<[u8; SEG_HEADER_LEN]> for SegmentHeader {
    fn from(buf: [u8; SEG_HEADER_LEN]) -> SegmentHeader {
        unsafe {
            let crc32_header =
                arr_to_u32(buf.get_unchecked(0..4)) ^ 0xFFFF_FFFF;

            let xor_lsn = arr_to_u64(buf.get_unchecked(4..12)) as Lsn;
            let lsn = xor_lsn ^ 0xFFFF_FFFF_FFFF_FFF;

            let crc32_tested = crc32(&buf[4..12]);

            SegmentHeader {
                lsn,
                ok: crc32_tested == crc32_header,
            }
        }
    }
}

impl Into<[u8; SEG_HEADER_LEN]> for SegmentHeader {
    fn into(self) -> [u8; SEG_HEADER_LEN] {
        let mut buf = [0u8; SEG_HEADER_LEN];

        let xor_lsn = self.lsn ^ 0xFFFF_FFFF_FFFF_FFF;
        let lsn_arr = u64_to_arr(xor_lsn as u64);
        let crc32 = u32_to_arr(crc32(&lsn_arr) ^ 0xFFFF_FFFF);

        unsafe {
            std::ptr::copy_nonoverlapping(
                crc32.as_ptr(),
                buf.as_mut_ptr(),
                std::mem::size_of::<u64>(),
            );
            std::ptr::copy_nonoverlapping(
                lsn_arr.as_ptr(),
                buf.as_mut_ptr().offset(4),
                std::mem::size_of::<u64>(),
            );
        }

        buf
    }
}

impl From<[u8; SEG_TRAILER_LEN]> for SegmentTrailer {
    fn from(buf: [u8; SEG_TRAILER_LEN]) -> SegmentTrailer {
        unsafe {
            let crc32_header =
                arr_to_u32(buf.get_unchecked(0..4)) ^ 0xFFFF_FFFF;

            let xor_lsn = arr_to_u64(buf.get_unchecked(4..12)) as Lsn;
            let lsn = xor_lsn ^ 0xFFFF_FFFF_FFFF_FFF;

            let crc32_tested = crc32(&buf[4..12]);

            SegmentTrailer {
                lsn,
                ok: crc32_tested == crc32_header,
            }
        }
    }
}

impl Into<[u8; SEG_TRAILER_LEN]> for SegmentTrailer {
    fn into(self) -> [u8; SEG_TRAILER_LEN] {
        let mut buf = [0u8; SEG_TRAILER_LEN];

        let xor_lsn = self.lsn ^ 0xFFFF_FFFF_FFFF_FFF;
        let lsn_arr = u64_to_arr(xor_lsn as u64);
        let crc32 = u32_to_arr(crc32(&lsn_arr) ^ 0xFFFF_FFFF);

        unsafe {
            std::ptr::copy_nonoverlapping(
                crc32.as_ptr(),
                buf.as_mut_ptr(),
                std::mem::size_of::<u32>(),
            );
            std::ptr::copy_nonoverlapping(
                lsn_arr.as_ptr(),
                buf.as_mut_ptr().offset(4),
                std::mem::size_of::<u64>(),
            );
        }

        buf
    }
}
<commit_msg>Fix buffer overrun caught by ASAN<commit_after>//! # Working with `Log`
//!
//! ```
//! let config = pagecache::ConfigBuilder::new()
//!     .temporary(true)
//!     .segment_mode(pagecache::SegmentMode::Linear)
//!     .build();
//! let log = pagecache::Log::start_raw_log(config).unwrap();
//! let (first_lsn, _first_offset) = log.write(b"1").unwrap();
//! log.write(b"22").unwrap();
//! log.write(b"333").unwrap();
//!
//! // stick an abort in the middle, which should not be returned
//! let res = log.reserve(b"never_gonna_hit_disk").unwrap();
//! res.abort().unwrap();
//!
//! log.write(b"4444");
//! let (last_lsn, _last_offset) = log.write(b"55555").unwrap();
//! log.make_stable(last_lsn).unwrap();
//! let mut iter = log.iter_from(first_lsn);
//! assert_eq!(iter.next().unwrap().2, b"1");
//! assert_eq!(iter.next().unwrap().2, b"22");
//! assert_eq!(iter.next().unwrap().2, b"333");
//! assert_eq!(iter.next().unwrap().2, b"4444");
//! assert_eq!(iter.next().unwrap().2, b"55555");
//! assert_eq!(iter.next(), None);
//! ```
use super::*;

/// A sequential store which allows users to create
/// reservations placed at known log offsets, used
/// for writing persistent data structures that need
/// to know where to find persisted bits in the future.
pub struct Log {
    /// iobufs is the underlying lock-free IO write buffer.
    pub(super) iobufs: IoBufs,
    config: Config,
    /// Periodically flushes `iobufs`.
    _flusher: Option<flusher::Flusher>,
}

unsafe impl Send for Log {}

impl Log {
    /// Start the log, open or create the configured file,
    /// and optionally start the periodic buffer flush thread.
    pub fn start(
        config: Config,
        snapshot: Snapshot,
    ) -> Result<Log, ()> {
        let iobufs = IoBufs::start(config.clone(), snapshot)?;

        let iobufs_flusher = iobufs.clone();
        let flusher = config.flush_every_ms.map(move |fem| {
            flusher::Flusher::new(
                "log flusher".to_owned(),
                iobufs_flusher,
                fem,
            )
        });

        Ok(Log {
            iobufs,
            config,
            _flusher: flusher,
        })
    }

    /// Starts a log for use without a materializer.
    pub fn start_raw_log(config: Config) -> Result<Log, ()> {
        assert_eq!(config.segment_mode, SegmentMode::Linear);
        let log_iter = raw_segment_iter_from(0, &config)?;

        let snapshot = advance_snapshot::<NullMaterializer, ()>(
            log_iter,
            Snapshot::default(),
            &config,
        )?;

        Log::start(config, snapshot)
    }

    /// Flushes any pending IO buffers to disk to ensure durability.
    pub fn flush(&self) -> Result<(), ()> {
        self.iobufs.flush()
    }

    /// Reserve space in the log for a pending linearized operation.
    pub fn reserve(&self, buf: &[u8]) -> Result<Reservation<'_>, ()> {
        self.iobufs.reserve(buf)
    }

    /// Reserve a replacement buffer for a previously written
    /// blob write. This ensures the message header has the
    /// proper blob flag set.
    pub(super) fn reserve_blob(
        &self,
        blob_ptr: BlobPointer,
    ) -> Result<Reservation<'_>, ()> {
        self.iobufs.reserve_blob(blob_ptr)
    }

    /// Write a buffer into the log. Returns the log sequence
    /// number and the file offset of the write.
    pub fn write<B>(&self, buf: B) -> Result<(Lsn, DiskPtr), ()>
    where
        B: AsRef<[u8]>,
    {
        self.reserve(buf.as_ref()).and_then(|res| res.complete())
    }

    /// Return an iterator over the log, starting with
    /// a specified offset.
    pub fn iter_from(&self, lsn: Lsn) -> LogIter {
        self.iobufs.iter_from(lsn)
    }

    /// read a buffer from the disk
    pub fn read(
        &self,
        lsn: Lsn,
        ptr: DiskPtr,
    ) -> Result<LogRead, ()> {
        trace!("reading log lsn {} ptr {}", lsn, ptr);

        self.make_stable(lsn)?;

        if ptr.is_inline() {
            let lid = ptr.inline();
            let f = &self.config.file;

            let read = f.read_message(lid, &self.config);

            read.and_then(|log_read| match log_read {
                LogRead::Inline(read_lsn, _, _)
                | LogRead::Blob(read_lsn, _, _) => {
                    if lsn != read_lsn {
                        debug!(
                            "lsn of disk read is {} but we expected it to be {}",
                            read_lsn,
                            lsn,
                        );
                        Err(Error::Corruption { at: ptr })
                    } else {
                        Ok(log_read)
                    }
                }
                _ => Ok(log_read),
            })
        } else {
            let (_lid, blob_ptr) = ptr.blob();
            read_blob(blob_ptr, &self.config)
                .map(|buf| LogRead::Blob(lsn, buf, blob_ptr))
        }
    }

    /// returns the current stable offset written to disk
    pub fn stable_offset(&self) -> Lsn {
        self.iobufs.stable()
    }

    /// blocks until the specified log sequence number has
    /// been made stable on disk
    pub fn make_stable(&self, lsn: Lsn) -> Result<(), ()> {
        self.iobufs.make_stable(lsn)
    }

    // SegmentAccountant access for coordination with the `PageCache`
    pub(crate) fn with_sa<B, F>(&self, f: F) -> B
    where
        F: FnOnce(&mut SegmentAccountant) -> B,
    {
        self.iobufs.with_sa(f)
    }
}

/// Represents the kind of message written to the log
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) enum MessageKind {
    Inline,
    Blob,
    Failed,
    Pad,
    Corrupted,
}

/// All log messages are prepended with this header
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) struct MessageHeader {
    pub(crate) kind: MessageKind,
    pub(crate) lsn: Lsn,
    pub(crate) len: usize,
    pub(crate) crc32: u32,
}

/// A segment's header contains the new base LSN and a reference
/// to the previous log segment.
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) struct SegmentHeader {
    pub(crate) lsn: Lsn,
    pub(crate) ok: bool,
}

/// A segment's trailer contains the base Lsn for the segment.
/// It is written after the rest of the segment has been fsync'd,
/// and helps us indicate if a segment has been torn.
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) struct SegmentTrailer {
    pub(crate) lsn: Lsn,
    pub(crate) ok: bool,
}

#[doc(hidden)]
#[derive(Debug)]
pub enum LogRead {
    Inline(Lsn, Vec<u8>, usize),
    Blob(Lsn, Vec<u8>, BlobPointer),
    Failed(Lsn, usize),
    Pad(Lsn),
    Corrupted(usize),
    DanglingBlob(Lsn, BlobPointer),
}

impl LogRead {
    /// Optionally return successfully read inline bytes, or
    /// None if the data was corrupt or this log entry was aborted.
    pub fn inline(self) -> Option<(Lsn, Vec<u8>, usize)> {
        match self {
            LogRead::Inline(lsn, bytes, len) => {
                Some((lsn, bytes, len))
            }
            _ => None,
        }
    }

    /// Return true if this is an Inline value..
    pub fn is_inline(&self) -> bool {
        match *self {
            LogRead::Inline(_, _, _) => true,
            _ => false,
        }
    }

    /// Optionally return a successfully read pointer to an
    /// blob value, or None if the data was corrupt or
    /// this log entry was aborted.
    pub fn blob(self) -> Option<(Lsn, Vec<u8>, BlobPointer)> {
        match self {
            LogRead::Blob(lsn, buf, ptr) => Some((lsn, buf, ptr)),
            _ => None,
        }
    }

    /// Return true if we read a completed blob write successfully.
    pub fn is_blob(&self) -> bool {
        match self {
            LogRead::Blob(..) => true,
            _ => false,
        }
    }

    /// Return true if we read an aborted flush.
    pub fn is_failed(&self) -> bool {
        match *self {
            LogRead::Failed(_, _) => true,
            _ => false,
        }
    }

    /// Return true if we read a successful Inline or Blob value.
    pub fn is_successful(&self) -> bool {
        match *self {
            LogRead::Inline(_, _, _) | LogRead::Blob(_, _, _) => true,
            _ => false,
        }
    }

    /// Return true if we read a segment pad.
    pub fn is_pad(&self) -> bool {
        match *self {
            LogRead::Pad(_) => true,
            _ => false,
        }
    }

    /// Return true if we read a corrupted log entry.
    pub fn is_corrupt(&self) -> bool {
        match *self {
            LogRead::Corrupted(_) => true,
            _ => false,
        }
    }

    /// Return the underlying data read from a log read, if successful.
    pub fn into_data(self) -> Option<Vec<u8>> {
        match self {
            LogRead::Blob(_, buf, _) | LogRead::Inline(_, buf, _) => {
                Some(buf)
            }
            _ => None,
        }
    }
}

// NB we use a lot of xors below to differentiate between zeroed out
// data on disk and an lsn or crc32 of 0

impl From<[u8; MSG_HEADER_LEN]> for MessageHeader {
    fn from(buf: [u8; MSG_HEADER_LEN]) -> MessageHeader {
        let kind = match buf[0] {
            INLINE_FLUSH => MessageKind::Inline,
            BLOB_FLUSH => MessageKind::Blob,
            FAILED_FLUSH => MessageKind::Failed,
            SEGMENT_PAD => MessageKind::Pad,
            _ => MessageKind::Corrupted,
        };

        unsafe {
            let lsn = arr_to_u64(buf.get_unchecked(1..9)) as Lsn;
            let len = arr_to_u32(buf.get_unchecked(9..13));
            let crc32 =
                arr_to_u32(buf.get_unchecked(13..)) ^ 0xFFFF_FFFF;

            MessageHeader {
                kind,
                lsn,
                len: len as usize,
                crc32,
            }
        }
    }
}

impl Into<[u8; MSG_HEADER_LEN]> for MessageHeader {
    fn into(self) -> [u8; MSG_HEADER_LEN] {
        let mut buf = [0u8; MSG_HEADER_LEN];
        buf[0] = match self.kind {
            MessageKind::Inline => INLINE_FLUSH,
            MessageKind::Blob => BLOB_FLUSH,
            MessageKind::Failed => FAILED_FLUSH,
            MessageKind::Pad => SEGMENT_PAD,
            MessageKind::Corrupted => EVIL_BYTE,
        };

        assert!(self.len <= std::u32::MAX as usize);
        let lsn_arr = u64_to_arr(self.lsn as u64);
        let len_arr = u32_to_arr(self.len as u32);
        let crc32_arr = u32_to_arr(self.crc32 ^ 0xFFFF_FFFF);

        unsafe {
            std::ptr::copy_nonoverlapping(
                lsn_arr.as_ptr(),
                buf.as_mut_ptr().offset(1),
                std::mem::size_of::<u64>(),
            );
            std::ptr::copy_nonoverlapping(
                len_arr.as_ptr(),
                buf.as_mut_ptr().offset(9),
                std::mem::size_of::<u32>(),
            );
            std::ptr::copy_nonoverlapping(
                crc32_arr.as_ptr(),
                buf.as_mut_ptr().offset(13),
                std::mem::size_of::<u32>(),
            );
        }

        buf
    }
}

impl From<[u8; SEG_HEADER_LEN]> for SegmentHeader {
    fn from(buf: [u8; SEG_HEADER_LEN]) -> SegmentHeader {
        unsafe {
            let crc32_header =
                arr_to_u32(buf.get_unchecked(0..4)) ^ 0xFFFF_FFFF;

            let xor_lsn = arr_to_u64(buf.get_unchecked(4..12)) as Lsn;
            let lsn = xor_lsn ^ 0xFFFF_FFFF_FFFF_FFF;

            let crc32_tested = crc32(&buf[4..12]);

            SegmentHeader {
                lsn,
                ok: crc32_tested == crc32_header,
            }
        }
    }
}

impl Into<[u8; SEG_HEADER_LEN]> for SegmentHeader {
    fn into(self) -> [u8; SEG_HEADER_LEN] {
        let mut buf = [0u8; SEG_HEADER_LEN];

        let xor_lsn = self.lsn ^ 0xFFFF_FFFF_FFFF_FFF;
        let lsn_arr = u64_to_arr(xor_lsn as u64);
        let crc32 = u32_to_arr(crc32(&lsn_arr) ^ 0xFFFF_FFFF);

        unsafe {
            std::ptr::copy_nonoverlapping(
                crc32.as_ptr(),
                buf.as_mut_ptr(),
                std::mem::size_of::<u32>(),
            );
            std::ptr::copy_nonoverlapping(
                lsn_arr.as_ptr(),
                buf.as_mut_ptr().offset(4),
                std::mem::size_of::<u64>(),
            );
        }

        buf
    }
}

impl From<[u8; SEG_TRAILER_LEN]> for SegmentTrailer {
    fn from(buf: [u8; SEG_TRAILER_LEN]) -> SegmentTrailer {
        unsafe {
            let crc32_header =
                arr_to_u32(buf.get_unchecked(0..4)) ^ 0xFFFF_FFFF;

            let xor_lsn = arr_to_u64(buf.get_unchecked(4..12)) as Lsn;
            let lsn = xor_lsn ^ 0xFFFF_FFFF_FFFF_FFF;

            let crc32_tested = crc32(&buf[4..12]);

            SegmentTrailer {
                lsn,
                ok: crc32_tested == crc32_header,
            }
        }
    }
}

impl Into<[u8; SEG_TRAILER_LEN]> for SegmentTrailer {
    fn into(self) -> [u8; SEG_TRAILER_LEN] {
        let mut buf = [0u8; SEG_TRAILER_LEN];

        let xor_lsn = self.lsn ^ 0xFFFF_FFFF_FFFF_FFF;
        let lsn_arr = u64_to_arr(xor_lsn as u64);
        let crc32 = u32_to_arr(crc32(&lsn_arr) ^ 0xFFFF_FFFF);

        unsafe {
            std::ptr::copy_nonoverlapping(
                crc32.as_ptr(),
                buf.as_mut_ptr(),
                std::mem::size_of::<u32>(),
            );
            std::ptr::copy_nonoverlapping(
                lsn_arr.as_ptr(),
                buf.as_mut_ptr().offset(4),
                std::mem::size_of::<u64>(),
            );
        }

        buf
    }
}
<|endoftext|>


<commit_before>//! This module contains a platform specific implementation of the window shell.

use std::{cell::RefCell, char, collections::HashMap, rc::Rc, sync::Mutex, time::Duration};

use minifb;

use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};

use crate::{prelude::*, render::*, utils::*};

pub fn initialize() {}

fn key_event_helper(key: &mut KeyHelper, adapter: &mut A, window: &minifb::Window)
where
    A: WindowAdapter,
{
    if !key.0 && window.is_key_pressed(key.1, minifb::KeyRepeat::Yes) {
        adapter.key_event(KeyEvent {
            key: key.2,
            state: ButtonState::Down,
            text: String::new(),
        });

        key.0 = true;
    } else {
        key.0 = false;
    }
}

fn unicode_to_key_event(uni_char: u32) -> Option<KeyEvent> {
    let mut text = String::new();

    let key = if let Some(character) = char::from_u32(uni_char) {
        text = character.to_string();
        Key::from(character)
    } else {
        Key::Unknown
    };

    if key == Key::Up
        || key == Key::Down
        || key == Key::Left
        || key == Key::Right
        || key == Key::Backspace
    {
        return None;
    }

    Some(KeyEvent {
        key,
        state: ButtonState::Down,
        text,
    })
}

struct KeyInputCallBack {
    key_events: Rc<RefCell<Vec<KeyEvent>>>,
}

impl minifb::InputCallback for KeyInputCallBack {
    fn add_char(&mut self, uni_char: u32) {
        if let Some(key_event) = unicode_to_key_event(uni_char) {
            self.key_events.borrow_mut().push(key_event);
        }
    }
}

struct KeyHelper(bool, minifb::Key, Key);

/// Concrete implementation of the window shell.
pub struct WindowShell
where
    A: WindowAdapter,
{
    window: minifb::Window,
    render_context_2_d: RenderContext2D,
    adapter: A,
    mouse_pos: (f32, f32),
    button_down: (bool, bool, bool),
    window_size: (usize, usize),
    key_events: Rc<RefCell<Vec<KeyEvent>>>,
    // todo: temp solution
    key_backspace: KeyHelper,
    key_delete: KeyHelper,
    key_left: KeyHelper,
    key_right: KeyHelper,
    key_up: KeyHelper,
    key_down: KeyHelper,
    key_enter: KeyHelper,
    key_control: KeyHelper,
    key_control_right: KeyHelper,
    key_shift_l: KeyHelper,
    key_shift_r: KeyHelper,
    key_alt: KeyHelper,
    key_alt_r: KeyHelper,
    key_escape: KeyHelper,
    key_home: KeyHelper,
    update: bool,
    running: bool,
    active: bool,
}

unsafe impl HasRawWindowHandle for WindowShell where A: WindowAdapter {
    fn raw_window_handle(&self) -> RawWindowHandle {
        self.window.raw_window_handle()
    }
}

impl WindowShell
where
    A: WindowAdapter,
{
    /// Creates a new window shell with an adapter.
    pub fn new(
        window: minifb::Window,
        adapter: A,
        key_events: Rc<RefCell<Vec<KeyEvent>>>,
    ) -> WindowShell {
        let size = window.get_size();
        let render_context_2_d = RenderContext2D::new(size.0 as f64, size.1 as f64);

        WindowShell {
            window,
            // window_size: window_size,
            // mouse_buttons: (false, false, false),
            // mouse_position: Point::default(),
            render_context_2_d,
            adapter,
            mouse_pos: (0.0, 0.0),
            window_size: size,
            button_down: (false, false, false),
            key_events,
            key_backspace: KeyHelper(false, minifb::Key::Backspace, Key::Backspace),
            key_left: KeyHelper(false, minifb::Key::Left, Key::Left),
            key_right: KeyHelper(false, minifb::Key::Right, Key::Right),
            key_up: KeyHelper(false, minifb::Key::Up, Key::Up),
            key_down: KeyHelper(false, minifb::Key::Down, Key::Down),
            key_delete: KeyHelper(false, minifb::Key::Delete, Key::Delete),
            key_enter: KeyHelper(false, minifb::Key::Enter, Key::Enter),
            key_control: KeyHelper(false, minifb::Key::LeftCtrl, Key::Control),
            key_control_right: KeyHelper(false, minifb::Key::RightCtrl, Key::Control),
            key_shift_l: KeyHelper(false, minifb::Key::LeftShift, Key::ShiftL),
            key_shift_r: KeyHelper(false, minifb::Key::RightShift, Key::ShiftR),
            key_alt: KeyHelper(false, minifb::Key::LeftAlt, Key::Alt),
            key_alt_r: KeyHelper(false, minifb::Key::RightAlt, Key::Alt),
            key_escape: KeyHelper(false, minifb::Key::Escape, Key::Escape),
            key_home: KeyHelper(false, minifb::Key::Home, Key::Home),
            running: true,
            update: true,
            active: false,
        }
    }

    /// Gets if the shell is running.
    pub fn running(&self) -> bool {
        self.running
    }

    /// Sets the background color of the window.
    pub fn set_background_color(&mut self, red: u8, green: u8, blue: u8) {
        self.window
            .set_background_color(red as usize, green as usize, blue as usize);
    }

    /// Sets running.
    pub fn set_running(&mut self, running: bool) {
        self.running = running;
    }

    /// Get if the shell should be updated.
    pub fn update(&self) -> bool {
        self.update
    }

    /// Sets update.
    pub fn set_update(&mut self, update: bool) {
        self.update = update;
    }

    /// Gets the shell adapter.
    pub fn adapter(&mut self) -> &mut A {
        &mut self.adapter
    }

    /// Gets the render ctx 2D.
    pub fn render_context_2_d(&mut self) -> &mut RenderContext2D {
        &mut self.render_context_2_d
    }

    fn drain_events(&mut self) {
        // mouse move
        if let Some(pos) = self.window.get_mouse_pos(minifb::MouseMode::Discard) {
            if (pos.0.floor(), pos.1.floor()) != self.mouse_pos {
                self.adapter.mouse(pos.0 as f64, pos.1 as f64);
                self.mouse_pos = (pos.0.floor(), pos.1.floor());
            }
        }

        // mouse
        let left_button_down = self.window.get_mouse_down(minifb::MouseButton::Left);
        let middle_button_down = self.window.get_mouse_down(minifb::MouseButton::Middle);
        let right_button_down = self.window.get_mouse_down(minifb::MouseButton::Right);

        if self.active != self.window.is_active() {
            self.adapter.active(self.window.is_active());
            self.active = self.window.is_active();
        }

        if left_button_down != self.button_down.0 {
            if left_button_down {
                self.push_mouse_event(true, MouseButton::Left);
            } else {
                self.push_mouse_event(false, MouseButton::Left);
            }
            self.button_down.0 = left_button_down;
        }

        if middle_button_down != self.button_down.1 {
            if middle_button_down {
                self.push_mouse_event(true, MouseButton::Middle);
            } else {
                self.push_mouse_event(false, MouseButton::Middle);
            }
            self.button_down.1 = middle_button_down;
        }

        if right_button_down != self.button_down.2 {
            if right_button_down {
                self.push_mouse_event(true, MouseButton::Right);
            } else {
                self.push_mouse_event(false, MouseButton::Right);
            }
            self.button_down.2 = right_button_down;
        }

        // scroll
        if let Some(delta) = self.window.get_scroll_wheel() {
            self.adapter.scroll(delta.0 as f64, delta.1 as f64);
        }

        // key
        while let Some(event) = self.key_events.borrow_mut().pop() {
            self.adapter.key_event(event);
        }

        key_event_helper(&mut self.key_backspace, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_delete, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_left, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_right, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_up, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_down, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_enter, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_control, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_control_right, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_shift_l, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_shift_r, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_alt, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_alt_r, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_escape, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_home, &mut self.adapter, &self.window);

        // resize
        if self.window_size != self.window.get_size() {
            self.window_size = self.window.get_size();
            self.render_context_2_d
                .resize(self.window_size.0 as f64, self.window_size.1 as f64);
            self.adapter
                .resize(self.window_size.0 as f64, self.window_size.1 as f64);
        }
    }

    fn push_mouse_event(&mut self, pressed: bool, button: MouseButton) {
        let state = if pressed {
            ButtonState::Down
        } else {
            ButtonState::Up
        };

        self.adapter.mouse_event(MouseEvent {
            x: self.mouse_pos.0 as f64,
            y: self.mouse_pos.1 as f64,
            button,
            state,
        });
    }

    pub fn flip(&mut self) -> bool {
        if let Some(data) = self.render_context_2_d.data() {
            let _ = self
                .window
                .update_with_buffer(data, self.window_size.0, self.window_size.1);
            CONSOLE.time_end("render");
            return true;
        }

        false
    }
}

impl Drop for WindowShell
where
    A: WindowAdapter,
{
    fn drop(&mut self) {}
}

/// Implementation of the OrbClient based shell runner.
pub struct ShellRunner
where
    A: WindowAdapter,
{
    pub window_shell: Rc<RefCell<WindowShell>>,
    pub updater: Box<dyn Updater>,
}

impl ShellRunner
where
    A: WindowAdapter,
{
    pub fn run(&mut self) {
        loop {
            if !self.window_shell.borrow().running() || !self.window_shell.borrow().window.is_open()
            {
                break;
            }

            // CONSOLE.time("complete run");

            self.updater.update();

            if self.window_shell.borrow_mut().update() {
                self.window_shell.borrow_mut().set_update(false);
            }

            if !self.window_shell.borrow_mut().flip() {
                self.window_shell.borrow_mut().window.update();
            }

            self.window_shell.borrow_mut().drain_events();

            // CONSOLE.time_end("complete run");
        }
    }
}

/// Constructs the window shell
pub struct WindowBuilder
where
    A: WindowAdapter,
{
    title: String,

    resizeable: bool,

    bounds: Rectangle,

    adapter: A,
}

impl WindowBuilder
where
    A: WindowAdapter,
{
    /// Create a new window builder with the given adapter.
    pub fn new(adapter: A) -> Self {
        WindowBuilder {
            adapter,
            title: String::default(),
            resizeable: false,
            bounds: Rectangle::default(),
        }
    }

    /// Sets the title.
    pub fn title(mut self, title: impl Into<String>) -> Self {
        self.title = title.into();
        self
    }

    /// Sets resizeable.
    pub fn resizeable(mut self, resizeable: bool) -> Self {
        self.resizeable = resizeable;
        self
    }

    /// Sets the bounds.
    pub fn bounds(mut self, bounds: impl Into<Rectangle>) -> Self {
        self.bounds = bounds.into();
        self
    }

    /// Builds the window shell.
    pub fn build(self) -> WindowShell {
        let window_options = minifb::WindowOptions {
            resize: self.resizeable,
            scale_mode: minifb::ScaleMode::UpperLeft,
            ..Default::default()
        };

        let mut window = minifb::Window::new(
            self.title.as_str(),
            self.bounds.width as usize,
            self.bounds.height as usize,
            window_options,
        )
        .unwrap_or_else(|e| {
            panic!("{}", e);
        });

         // Limit to max ~60 fps update rate
        window.limit_update_rate(Some(Duration::from_micros(16600)));

        let key_events = Rc::new(RefCell::new(vec![]));

        window.set_input_callback(Box::new(KeyInputCallBack {
            key_events: key_events.clone(),
        }));

        window.set_position(self.bounds.x as isize, self.bounds.y as isize);

        WindowShell::new(window, self.adapter, key_events)
    }
}

use std::time::Instant;

lazy_static! {
    pub static ref CONSOLE: Console = Console {
        instants: Mutex::new(HashMap::new())
    };
}

pub struct Console {
    instants: Mutex<HashMap<String, Instant>>,
}

impl Console {
    pub fn time(&self, name: impl Into<String>) {
        self.instants
            .lock()
            .unwrap()
            .insert(name.into(), Instant::now());
    }

    pub fn time_end(&self, name: impl Into<String>) {
        if let Some((_k, _v)) = self.instants.lock().unwrap().remove_entry(&name.into()) {
            // println!("{} {}ms - timer ended", k, v.elapsed().as_millis());
        }
    }

    pub fn log(&self, message: impl Into<String>) {
        println!("{}", message.into());
    }
}
<commit_msg>Next redox fix.<commit_after>//! This module contains a platform specific implementation of the window shell.

use std::{cell::RefCell, char, collections::HashMap, rc::Rc, sync::Mutex, time::Duration};

use minifb;

use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};

use crate::{prelude::*, render::*, utils::*};

pub fn initialize() {}

fn key_event_helper(key: &mut KeyHelper, adapter: &mut A, window: &minifb::Window)
where
    A: WindowAdapter,
{
    if !key.0 && window.is_key_pressed(key.1, minifb::KeyRepeat::Yes) {
        adapter.key_event(KeyEvent {
            key: key.2,
            state: ButtonState::Down,
            text: String::new(),
        });

        key.0 = true;
    } else {
        key.0 = false;
    }
}

fn unicode_to_key_event(uni_char: u32) -> Option<KeyEvent> {
    let mut text = String::new();

    let key = if let Some(character) = char::from_u32(uni_char) {
        text = character.to_string();
        Key::from(character)
    } else {
        Key::Unknown
    };

    if key == Key::Up
        || key == Key::Down
        || key == Key::Left
        || key == Key::Right
        || key == Key::Backspace
    {
        return None;
    }

    Some(KeyEvent {
        key,
        state: ButtonState::Down,
        text,
    })
}

struct KeyInputCallBack {
    key_events: Rc<RefCell<Vec<KeyEvent>>>,
}

impl minifb::InputCallback for KeyInputCallBack {
    fn add_char(&mut self, uni_char: u32) {
        if let Some(key_event) = unicode_to_key_event(uni_char) {
            self.key_events.borrow_mut().push(key_event);
        }
    }
}

struct KeyHelper(bool, minifb::Key, Key);

/// Concrete implementation of the window shell.
pub struct WindowShell
where
    A: WindowAdapter,
{
    window: minifb::Window,
    render_context_2_d: RenderContext2D,
    adapter: A,
    mouse_pos: (f32, f32),
    button_down: (bool, bool, bool),
    window_size: (usize, usize),
    key_events: Rc<RefCell<Vec<KeyEvent>>>,
    // todo: temp solution
    key_backspace: KeyHelper,
    key_delete: KeyHelper,
    key_left: KeyHelper,
    key_right: KeyHelper,
    key_up: KeyHelper,
    key_down: KeyHelper,
    key_enter: KeyHelper,
    key_control: KeyHelper,
    key_control_right: KeyHelper,
    key_shift_l: KeyHelper,
    key_shift_r: KeyHelper,
    key_alt: KeyHelper,
    key_alt_r: KeyHelper,
    key_escape: KeyHelper,
    key_home: KeyHelper,
    update: bool,
    running: bool,
    active: bool,
}

#[cfg(not(target_os = "redox"))]
unsafe impl HasRawWindowHandle for WindowShell where A: WindowAdapter {
    fn raw_window_handle(&self) -> RawWindowHandle {
        self.window.raw_window_handle()
    }
}

impl WindowShell
where
    A: WindowAdapter,
{
    /// Creates a new window shell with an adapter.
    pub fn new(
        window: minifb::Window,
        adapter: A,
        key_events: Rc<RefCell<Vec<KeyEvent>>>,
    ) -> WindowShell {
        let size = window.get_size();
        let render_context_2_d = RenderContext2D::new(size.0 as f64, size.1 as f64);

        WindowShell {
            window,
            // window_size: window_size,
            // mouse_buttons: (false, false, false),
            // mouse_position: Point::default(),
            render_context_2_d,
            adapter,
            mouse_pos: (0.0, 0.0),
            window_size: size,
            button_down: (false, false, false),
            key_events,
            key_backspace: KeyHelper(false, minifb::Key::Backspace, Key::Backspace),
            key_left: KeyHelper(false, minifb::Key::Left, Key::Left),
            key_right: KeyHelper(false, minifb::Key::Right, Key::Right),
            key_up: KeyHelper(false, minifb::Key::Up, Key::Up),
            key_down: KeyHelper(false, minifb::Key::Down, Key::Down),
            key_delete: KeyHelper(false, minifb::Key::Delete, Key::Delete),
            key_enter: KeyHelper(false, minifb::Key::Enter, Key::Enter),
            key_control: KeyHelper(false, minifb::Key::LeftCtrl, Key::Control),
            key_control_right: KeyHelper(false, minifb::Key::RightCtrl, Key::Control),
            key_shift_l: KeyHelper(false, minifb::Key::LeftShift, Key::ShiftL),
            key_shift_r: KeyHelper(false, minifb::Key::RightShift, Key::ShiftR),
            key_alt: KeyHelper(false, minifb::Key::LeftAlt, Key::Alt),
            key_alt_r: KeyHelper(false, minifb::Key::RightAlt, Key::Alt),
            key_escape: KeyHelper(false, minifb::Key::Escape, Key::Escape),
            key_home: KeyHelper(false, minifb::Key::Home, Key::Home),
            running: true,
            update: true,
            active: false,
        }
    }

    /// Gets if the shell is running.
    pub fn running(&self) -> bool {
        self.running
    }

    /// Sets the background color of the window.
    pub fn set_background_color(&mut self, red: u8, green: u8, blue: u8) {
        self.window
            .set_background_color(red as usize, green as usize, blue as usize);
    }

    /// Sets running.
    pub fn set_running(&mut self, running: bool) {
        self.running = running;
    }

    /// Get if the shell should be updated.
    pub fn update(&self) -> bool {
        self.update
    }

    /// Sets update.
    pub fn set_update(&mut self, update: bool) {
        self.update = update;
    }

    /// Gets the shell adapter.
    pub fn adapter(&mut self) -> &mut A {
        &mut self.adapter
    }

    /// Gets the render ctx 2D.
    pub fn render_context_2_d(&mut self) -> &mut RenderContext2D {
        &mut self.render_context_2_d
    }

    fn drain_events(&mut self) {
        // mouse move
        if let Some(pos) = self.window.get_mouse_pos(minifb::MouseMode::Discard) {
            if (pos.0.floor(), pos.1.floor()) != self.mouse_pos {
                self.adapter.mouse(pos.0 as f64, pos.1 as f64);
                self.mouse_pos = (pos.0.floor(), pos.1.floor());
            }
        }

        // mouse
        let left_button_down = self.window.get_mouse_down(minifb::MouseButton::Left);
        let middle_button_down = self.window.get_mouse_down(minifb::MouseButton::Middle);
        let right_button_down = self.window.get_mouse_down(minifb::MouseButton::Right);

        if self.active != self.window.is_active() {
            self.adapter.active(self.window.is_active());
            self.active = self.window.is_active();
        }

        if left_button_down != self.button_down.0 {
            if left_button_down {
                self.push_mouse_event(true, MouseButton::Left);
            } else {
                self.push_mouse_event(false, MouseButton::Left);
            }
            self.button_down.0 = left_button_down;
        }

        if middle_button_down != self.button_down.1 {
            if middle_button_down {
                self.push_mouse_event(true, MouseButton::Middle);
            } else {
                self.push_mouse_event(false, MouseButton::Middle);
            }
            self.button_down.1 = middle_button_down;
        }

        if right_button_down != self.button_down.2 {
            if right_button_down {
                self.push_mouse_event(true, MouseButton::Right);
            } else {
                self.push_mouse_event(false, MouseButton::Right);
            }
            self.button_down.2 = right_button_down;
        }

        // scroll
        if let Some(delta) = self.window.get_scroll_wheel() {
            self.adapter.scroll(delta.0 as f64, delta.1 as f64);
        }

        // key
        while let Some(event) = self.key_events.borrow_mut().pop() {
            self.adapter.key_event(event);
        }

        key_event_helper(&mut self.key_backspace, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_delete, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_left, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_right, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_up, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_down, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_enter, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_control, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_control_right, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_shift_l, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_shift_r, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_alt, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_alt_r, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_escape, &mut self.adapter, &self.window);
        key_event_helper(&mut self.key_home, &mut self.adapter, &self.window);

        // resize
        if self.window_size != self.window.get_size() {
            self.window_size = self.window.get_size();
            self.render_context_2_d
                .resize(self.window_size.0 as f64, self.window_size.1 as f64);
            self.adapter
                .resize(self.window_size.0 as f64, self.window_size.1 as f64);
        }
    }

    fn push_mouse_event(&mut self, pressed: bool, button: MouseButton) {
        let state = if pressed {
            ButtonState::Down
        } else {
            ButtonState::Up
        };

        self.adapter.mouse_event(MouseEvent {
            x: self.mouse_pos.0 as f64,
            y: self.mouse_pos.1 as f64,
            button,
            state,
        });
    }

    pub fn flip(&mut self) -> bool {
        if let Some(data) = self.render_context_2_d.data() {
            let _ = self
                .window
                .update_with_buffer(data, self.window_size.0, self.window_size.1);
            CONSOLE.time_end("render");
            return true;
        }

        false
    }
}

impl Drop for WindowShell
where
    A: WindowAdapter,
{
    fn drop(&mut self) {}
}

/// Implementation of the OrbClient based shell runner.
pub struct ShellRunner
where
    A: WindowAdapter,
{
    pub window_shell: Rc<RefCell<WindowShell>>,
    pub updater: Box<dyn Updater>,
}

impl ShellRunner
where
    A: WindowAdapter,
{
    pub fn run(&mut self) {
        loop {
            if !self.window_shell.borrow().running() || !self.window_shell.borrow().window.is_open()
            {
                break;
            }

            // CONSOLE.time("complete run");

            self.updater.update();

            if self.window_shell.borrow_mut().update() {
                self.window_shell.borrow_mut().set_update(false);
            }

            if !self.window_shell.borrow_mut().flip() {
                self.window_shell.borrow_mut().window.update();
            }

            self.window_shell.borrow_mut().drain_events();

            // CONSOLE.time_end("complete run");
        }
    }
}

/// Constructs the window shell
pub struct WindowBuilder
where
    A: WindowAdapter,
{
    title: String,

    resizeable: bool,

    bounds: Rectangle,

    adapter: A,
}

impl WindowBuilder
where
    A: WindowAdapter,
{
    /// Create a new window builder with the given adapter.
    pub fn new(adapter: A) -> Self {
        WindowBuilder {
            adapter,
            title: String::default(),
            resizeable: false,
            bounds: Rectangle::default(),
        }
    }

    /// Sets the title.
    pub fn title(mut self, title: impl Into<String>) -> Self {
        self.title = title.into();
        self
    }

    /// Sets resizeable.
    pub fn resizeable(mut self, resizeable: bool) -> Self {
        self.resizeable = resizeable;
        self
    }

    /// Sets the bounds.
    pub fn bounds(mut self, bounds: impl Into<Rectangle>) -> Self {
        self.bounds = bounds.into();
        self
    }

    /// Builds the window shell.
    pub fn build(self) -> WindowShell {
        let window_options = minifb::WindowOptions {
            resize: self.resizeable,
            scale_mode: minifb::ScaleMode::UpperLeft,
            ..Default::default()
        };

        let mut window = minifb::Window::new(
            self.title.as_str(),
            self.bounds.width as usize,
            self.bounds.height as usize,
            window_options,
        )
        .unwrap_or_else(|e| {
            panic!("{}", e);
        });

         // Limit to max ~60 fps update rate
        window.limit_update_rate(Some(Duration::from_micros(16600)));

        let key_events = Rc::new(RefCell::new(vec![]));

        window.set_input_callback(Box::new(KeyInputCallBack {
            key_events: key_events.clone(),
        }));

        window.set_position(self.bounds.x as isize, self.bounds.y as isize);

        WindowShell::new(window, self.adapter, key_events)
    }
}

use std::time::Instant;

lazy_static! {
    pub static ref CONSOLE: Console = Console {
        instants: Mutex::new(HashMap::new())
    };
}

pub struct Console {
    instants: Mutex<HashMap<String, Instant>>,
}

impl Console {
    pub fn time(&self, name: impl Into<String>) {
        self.instants
            .lock()
            .unwrap()
            .insert(name.into(), Instant::now());
    }

    pub fn time_end(&self, name: impl Into<String>) {
        if let Some((_k, _v)) = self.instants.lock().unwrap().remove_entry(&name.into()) {
            // println!("{} {}ms - timer ended", k, v.elapsed().as_millis());
        }
    }

    pub fn log(&self, message: impl Into<String>) {
        println!("{}", message.into());
    }
}
<|endoftext|>


<commit_before>use std::io::Write;
use std::fs;
use std::str;
use std::cmp::min;
use std::io::Read;
use log;
use serde_json;
use nom::{HexDisplay,IResult,Offset};
use yxorp::messages::Command;
use yxorp::network::ProxyOrder;
use yxorp::network::buffer::Buffer;

use super::{CommandServer,FrontToken,Listener,ListenerConfiguration,StoredListener,parse};
use super::data::{ConfigCommand,ConfigMessage};

impl CommandServer {
  pub fn handle_message(&mut self, token: FrontToken, message: &ConfigMessage) {
    let config_command = message.data.clone();
    match config_command {
      ConfigCommand::SaveState(path) => {
        if let Ok(mut f) = fs::File::create(&path) {
          let stored_listeners: Vec<StoredListener> = self.listeners.values()
            .map(|listener_list| StoredListener::from_listener(listener_list.first().unwrap())).collect();

          let mut counter = 0usize;
          for listener in stored_listeners {
            for command in listener.state.generate_commands() {
              let message = ConfigMessage {
                id:       format!("SAVE-{}", counter),
                listener: Some(listener.tag.to_string()),
                data:     ConfigCommand::ProxyConfiguration(command)
              };
              f.write_all(&serde_json::to_string(&message).map(|s| s.into_bytes()).unwrap_or(vec!()));
              f.write_all(&b"\n\0"[..]);
              counter += 1;
            }
            f.sync_all();
          }
          // FIXME: should send back a DONE message here
        } else {
          // FIXME: should send back error here
          log!(log::LogLevel::Error, "could not open file: {}", &path);
          self.conns[token].back_buf.write(b"could not open file\0");
        }
      },
      ConfigCommand::DumpState => {
        //FIXME:
        //let v: &Vec<Listener> = self.listeners.values().next().unwrap();
        let stored_listeners = self.listeners.values()
          .map(|listener_list| StoredListener::from_listener(listener_list.first().unwrap())).collect();
        let conf = ListenerConfiguration {
          id:        message.id.clone(),
          listeners: stored_listeners,
        };
        let encoded = serde_json::to_string(&conf).map(|s| s.into_bytes()).unwrap_or(vec!());
        if self.conns[token].back_buf.grow(min(encoded.len() + 10, self.max_buffer_size)) {
          log!(log::LogLevel::Info, "write buffer was not large enough, growing to {} bytes", encoded.len());
        }
        info!("dumping state to {:?}:\n{}", token, str::from_utf8(&encoded).unwrap());
        self.conns[token].back_buf.write(&encoded);
        self.conns[token].back_buf.write(&b"\0"[..]);
      },
      ConfigCommand::LoadState(path) => {
        self.load_state(&message.id, &path);
        self.conns[token].back_buf.write(b"loaded the configuration\0");
      },
      ConfigCommand::ProxyConfiguration(command) => {
        if let Some(ref tag) = message.listener {
          if let &Command::AddTlsFront(ref data) = &command {
            log!(log::LogLevel::Info, "received AddTlsFront(TlsFront {{ app_id: {}, hostname: {}, path_begin: {} }}) with tag {:?}",
            data.app_id, data.hostname, data.path_begin, tag);
          } else {
            log!(log::LogLevel::Info, "received {:?} with tag {:?}", command, tag);
          }
          if let Some(ref mut listener_vec) = self.listeners.get_mut (tag) {
            for listener in listener_vec.iter_mut() {
              let cl = command.clone();
              self.conns[token].add_message_id(message.id.clone());
              listener.state.handle_command(&cl);
              listener.sender.send(ProxyOrder::Command(message.id.clone(), cl));
            }
          } else {
            // FIXME: should send back error here
            log!(log::LogLevel::Error, "no listener found for tag: {}", tag);
          }
        } else {
          // FIXME: should send back error here
          log!(log::LogLevel::Error, "expecting listener tag");
        }
      }
    }
  }

  pub fn load_state(&mut self, message_id: &str, path: &str) {
    match fs::File::open(&path) {
      Err(e)   => error!("cannot open file at path '{}': {:?}", path, e),
      Ok(mut file) => {
        //let mut data = vec!();
        loop {
        let mut buffer = Buffer::with_capacity(12000);
        //FIXME: we should read in streaming here
          if let Ok(sz) = file.read(buffer.space()) {
            buffer.fill(sz);
          } else {
            error!("error reading state file");
            break;
          }

          let mut offset = 0;
          match parse(buffer.data()) {
            IResult::Done(i, o) => {
              if i.len() > 0 {
                info!("could not parse {} bytes", i.len());
              }
              offset = buffer.data().offset(i);

              for message in o {
                if let ConfigCommand::ProxyConfiguration(command) = message.data {
                  if let Some(ref tag) = message.listener {
                    if let &Command::AddTlsFront(ref data) = &command {
                      log!(log::LogLevel::Info, "received AddTlsFront(TlsFront {{ app_id: {}, hostname: {}, path_begin: {} }}) with tag {:?}",
                      data.app_id, data.hostname, data.path_begin, tag);
                    } else {
                      log!(log::LogLevel::Info, "received {:?} with tag {:?}", command, tag);
                    }
                    if let Some(ref mut listener_vec) = self.listeners.get_mut(tag) {
                      for listener in listener_vec.iter_mut() {
                        let cl = command.clone();
                        listener.state.handle_command(&cl);
                        listener.sender.send(ProxyOrder::Command(message.id.clone(), cl));
                      }
                    } else {
                      // FIXME: should send back error here
                      log!(log::LogLevel::Error, "no listener found for tag: {}", tag);
                    }
                  } else {
                    // FIXME: should send back error here
                    log!(log::LogLevel::Error, "expecting listener tag");
                  }
                }
              }
            },
            IResult::Incomplete(_) => {
              if buffer.available_data() == buffer.capacity() {
                error!("message too big, stopping parsing:\n{}", buffer.data().to_hex(16));
                break;
              }
            }
            IResult::Error(e) => {
              error!("saved state parse error: {:?}", e);
              break;
            },
          }
          buffer.consume(offset);
        }
      }
    }
  }
}
<commit_msg>do not create the buffer in a loop<commit_after>use std::io::Write;
use std::fs;
use std::str;
use std::cmp::min;
use std::io::Read;
use log;
use serde_json;
use nom::{HexDisplay,IResult,Offset};
use yxorp::messages::Command;
use yxorp::network::ProxyOrder;
use yxorp::network::buffer::Buffer;

use super::{CommandServer,FrontToken,Listener,ListenerConfiguration,StoredListener,parse};
use super::data::{ConfigCommand,ConfigMessage};

impl CommandServer {
  pub fn handle_message(&mut self, token: FrontToken, message: &ConfigMessage) {
    let config_command = message.data.clone();
    match config_command {
      ConfigCommand::SaveState(path) => {
        if let Ok(mut f) = fs::File::create(&path) {
          let stored_listeners: Vec<StoredListener> = self.listeners.values()
            .map(|listener_list| StoredListener::from_listener(listener_list.first().unwrap())).collect();

          let mut counter = 0usize;
          for listener in stored_listeners {
            for command in listener.state.generate_commands() {
              let message = ConfigMessage {
                id:       format!("SAVE-{}", counter),
                listener: Some(listener.tag.to_string()),
                data:     ConfigCommand::ProxyConfiguration(command)
              };
              f.write_all(&serde_json::to_string(&message).map(|s| s.into_bytes()).unwrap_or(vec!()));
              f.write_all(&b"\n\0"[..]);
              counter += 1;
            }
            f.sync_all();
          }
          // FIXME: should send back a DONE message here
        } else {
          // FIXME: should send back error here
          log!(log::LogLevel::Error, "could not open file: {}", &path);
          self.conns[token].back_buf.write(b"could not open file\0");
        }
      },
      ConfigCommand::DumpState => {
        //FIXME:
        //let v: &Vec<Listener> = self.listeners.values().next().unwrap();
        let stored_listeners = self.listeners.values()
          .map(|listener_list| StoredListener::from_listener(listener_list.first().unwrap())).collect();
        let conf = ListenerConfiguration {
          id:        message.id.clone(),
          listeners: stored_listeners,
        };
        let encoded = serde_json::to_string(&conf).map(|s| s.into_bytes()).unwrap_or(vec!());
        if self.conns[token].back_buf.grow(min(encoded.len() + 10, self.max_buffer_size)) {
          log!(log::LogLevel::Info, "write buffer was not large enough, growing to {} bytes", encoded.len());
        }
        info!("dumping state to {:?}:\n{}", token, str::from_utf8(&encoded).unwrap());
        self.conns[token].back_buf.write(&encoded);
        self.conns[token].back_buf.write(&b"\0"[..]);
      },
      ConfigCommand::LoadState(path) => {
        self.load_state(&message.id, &path);
        self.conns[token].back_buf.write(b"loaded the configuration\0");
      },
      ConfigCommand::ProxyConfiguration(command) => {
        if let Some(ref tag) = message.listener {
          if let &Command::AddTlsFront(ref data) = &command {
            log!(log::LogLevel::Info, "received AddTlsFront(TlsFront {{ app_id: {}, hostname: {}, path_begin: {} }}) with tag {:?}",
            data.app_id, data.hostname, data.path_begin, tag);
          } else {
            log!(log::LogLevel::Info, "received {:?} with tag {:?}", command, tag);
          }
          if let Some(ref mut listener_vec) = self.listeners.get_mut (tag) {
            for listener in listener_vec.iter_mut() {
              let cl = command.clone();
              self.conns[token].add_message_id(message.id.clone());
              listener.state.handle_command(&cl);
              listener.sender.send(ProxyOrder::Command(message.id.clone(), cl));
            }
          } else {
            // FIXME: should send back error here
            log!(log::LogLevel::Error, "no listener found for tag: {}", tag);
          }
        } else {
          // FIXME: should send back error here
          log!(log::LogLevel::Error, "expecting listener tag");
        }
      }
    }
  }

  pub fn load_state(&mut self, message_id: &str, path: &str) {
    match fs::File::open(&path) {
      Err(e)   => error!("cannot open file at path '{}': {:?}", path, e),
      Ok(mut file) => {
        //let mut data = vec!();
        let mut buffer = Buffer::with_capacity(12000);
        loop {
        //FIXME: we should read in streaming here
          if let Ok(sz) = file.read(buffer.space()) {
            buffer.fill(sz);
          } else {
            error!("error reading state file");
            break;
          }

          let mut offset = 0;
          match parse(buffer.data()) {
            IResult::Done(i, o) => {
              if i.len() > 0 {
                info!("could not parse {} bytes", i.len());
              }
              offset = buffer.data().offset(i);

              for message in o {
                if let ConfigCommand::ProxyConfiguration(command) = message.data {
                  if let Some(ref tag) = message.listener {
                    if let &Command::AddTlsFront(ref data) = &command {
                      log!(log::LogLevel::Info, "received AddTlsFront(TlsFront {{ app_id: {}, hostname: {}, path_begin: {} }}) with tag {:?}",
                      data.app_id, data.hostname, data.path_begin, tag);
                    } else {
                      log!(log::LogLevel::Info, "received {:?} with tag {:?}", command, tag);
                    }
                    if let Some(ref mut listener_vec) = self.listeners.get_mut(tag) {
                      for listener in listener_vec.iter_mut() {
                        let cl = command.clone();
                        listener.state.handle_command(&cl);
                        listener.sender.send(ProxyOrder::Command(message.id.clone(), cl));
                      }
                    } else {
                      // FIXME: should send back error here
                      log!(log::LogLevel::Error, "no listener found for tag: {}", tag);
                    }
                  } else {
                    // FIXME: should send back error here
                    log!(log::LogLevel::Error, "expecting listener tag");
                  }
                }
              }
            },
            IResult::Incomplete(_) => {
              if buffer.available_data() == buffer.capacity() {
                error!("message too big, stopping parsing:\n{}", buffer.data().to_hex(16));
                break;
              }
            }
            IResult::Error(e) => {
              error!("saved state parse error: {:?}", e);
              break;
            },
          }
          buffer.consume(offset);
        }
      }
    }
  }
}
<|endoftext|>


<commit_before>use lucet_idl;
use std::fs::File;
use std::io::prelude::*;
use std::process::Command;
use tempfile::TempDir;

#[test]
fn compile_and_run() {
    let mut source = String::new();
    File::open("tests/example.idl")
        .expect("open example.idl")
        .read_to_string(&mut source)
        .expect("read example.idl");

    let config = lucet_idl::Config {
        backend: lucet_idl::Backend::C,
        backend_config: lucet_idl::BackendConfig::default(),
        target: lucet_idl::Target::Generic,
    };

    let tempdir = TempDir::new().expect("create tempdir");

    lucet_idl::run(
        &config,
        &source,
        File::create(tempdir.path().join("example.h")).expect("create file"),
    )
    .expect("run lucet_idl");

    let cmd_cc = Command::new("cc")
        .arg("--std=c99")
        .arg("-I")
        .arg(tempdir.path())
        .arg("tests/example_driver.c")
        .arg("-o")
        .arg(tempdir.path().join("example"))
        .status()
        .expect("run cc");
    assert!(cmd_cc.success(), "failure to compile generated code");

    /* currently assertions in the example_driver fail. re-enable this when we figure out how to
     * fix them:
    let cmd_run = Command::new(tempdir.path().join("example"))
        .status()
        .expect("run generated code");
    assert!(cmd_run.success(), "failure to run generated code");
    */
}
<commit_msg>Revert "lucet-idl: disable last part of test"<commit_after>use lucet_idl;
use std::fs::File;
use std::io::prelude::*;
use std::process::Command;
use tempfile::TempDir;

#[test]
fn compile_and_run() {
    let mut source = String::new();
    File::open("tests/example.idl")
        .expect("open example.idl")
        .read_to_string(&mut source)
        .expect("read example.idl");

    let config = lucet_idl::Config {
        backend: lucet_idl::Backend::C,
        backend_config: lucet_idl::BackendConfig::default(),
        target: lucet_idl::Target::Generic,
    };

    let tempdir = TempDir::new().expect("create tempdir");

    lucet_idl::run(
        &config,
        &source,
        File::create(tempdir.path().join("example.h")).expect("create file"),
    )
    .expect("run lucet_idl");

    let cmd_cc = Command::new("cc")
        .arg("--std=c99")
        .arg("-I")
        .arg(tempdir.path())
        .arg("tests/example_driver.c")
        .arg("-o")
        .arg(tempdir.path().join("example"))
        .status()
        .expect("run cc");
    assert!(cmd_cc.success(), "failure to compile generated code");

    let cmd_run = Command::new(tempdir.path().join("example"))
        .status()
        .expect("run generated code");
    assert!(cmd_run.success(), "failure to run generated code");
}
<|endoftext|>


<commit_before>use rosxmlrpc;
use std;

pub struct Master {
    client: rosxmlrpc::Client,
    client_id: String,
    caller_api: String,
}

impl Master {
    pub fn new(client_id: &str, caller_api: &str) -> Master {
        let master_uri = std::env::var("ROS_MASTER_URI")
            .unwrap_or("http://localhost:11311/".to_owned());
        Master {
            client: rosxmlrpc::Client::new(&master_uri),
            client_id: client_id.to_owned(),
            caller_api: caller_api.to_owned(),
        }
    }

    pub fn register_service(&self,
                            service: &str,
                            service_api: &str)
                            -> rosxmlrpc::client::ClientResult {
        self.client
            .request("registerService",
                     &[self.client_id.as_str(), service, service_api, self.caller_api.as_str()])
    }

    pub fn unregister_service(&self,
                              service: &str,
                              service_api: &str)
                              -> rosxmlrpc::client::ClientResult {
        self.client
            .request("unregisterService",
                     &[self.client_id.as_str(), service, service_api])
    }

    pub fn register_subscriber(&self,
                               topic: &str,
                               topic_type: &str)
                               -> rosxmlrpc::client::ClientResult {
        self.client
            .request("registerSubscriber",
                     &[self.client_id.as_str(), topic, topic_type, self.caller_api.as_str()])
    }

    pub fn unregister_subscriber(&self, topic: &str) -> rosxmlrpc::client::ClientResult {
        self.client
            .request("unregisterSubscriber",
                     &[self.client_id.as_str(), topic, self.caller_api.as_str()])
    }

    pub fn register_publisher(&self,
                              topic: &str,
                              topic_type: &str)
                              -> rosxmlrpc::client::ClientResult {
        self.client
            .request("registerPublisher",
                     &[self.client_id.as_str(), topic, topic_type, self.caller_api.as_str()])
    }

    pub fn unregister_publisher(&self, topic: &str) -> rosxmlrpc::client::ClientResult {
        self.client
            .request("unregisterPublisher",
                     &[self.client_id.as_str(), topic, self.caller_api.as_str()])
    }

    pub fn lookup_node(&self, node_name: &str) -> rosxmlrpc::client::ClientResult {
        self.client
            .request("lookupNode", &[self.client_id.as_str(), node_name])
    }

    pub fn get_published_topics(&self, subgraph: &str) -> rosxmlrpc::client::ClientResult {
        self.client
            .request("getPublishedTopics", &[self.client_id.as_str(), subgraph])
    }

    pub fn get_topic_types(&self) -> rosxmlrpc::client::ClientResult {
        self.client
            .request("getTopicTypes", &[self.client_id.as_str()])
    }

    pub fn get_system_state(&self) -> rosxmlrpc::client::ClientResult {
        self.client
            .request("getSystemState", &[self.client_id.as_str()])
    }

    pub fn get_uri(&self) -> rosxmlrpc::client::ClientResult {
        self.client
            .request("getUri", &[self.client_id.as_str()])
    }

    pub fn lookup_service(&self, service: &str) -> rosxmlrpc::client::ClientResult {
        self.client
            .request("lookupService", &[self.client_id.as_str(), service])
    }
}
<commit_msg>Start a return type for Master<commit_after>use rosxmlrpc;
use std;

pub struct Master {
    client: rosxmlrpc::Client,
    client_id: String,
    caller_api: String,
}

impl Master {
    pub fn new(client_id: &str, caller_api: &str) -> Master {
        let master_uri = std::env::var("ROS_MASTER_URI")
            .unwrap_or("http://localhost:11311/".to_owned());
        Master {
            client: rosxmlrpc::Client::new(&master_uri),
            client_id: client_id.to_owned(),
            caller_api: caller_api.to_owned(),
        }
    }

    pub fn register_service(&self,
                            service: &str,
                            service_api: &str)
                            -> rosxmlrpc::client::ClientResult {
        self.client
            .request("registerService",
                     &[self.client_id.as_str(), service, service_api, self.caller_api.as_str()])
    }

    pub fn unregister_service(&self,
                              service: &str,
                              service_api: &str)
                              -> rosxmlrpc::client::ClientResult {
        self.client
            .request("unregisterService",
                     &[self.client_id.as_str(), service, service_api])
    }

    pub fn register_subscriber(&self,
                               topic: &str,
                               topic_type: &str)
                               -> rosxmlrpc::client::ClientResult {
        self.client
            .request("registerSubscriber",
                     &[self.client_id.as_str(), topic, topic_type, self.caller_api.as_str()])
    }

    pub fn unregister_subscriber(&self, topic: &str) -> rosxmlrpc::client::ClientResult {
        self.client
            .request("unregisterSubscriber",
                     &[self.client_id.as_str(), topic, self.caller_api.as_str()])
    }

    pub fn register_publisher(&self,
                              topic: &str,
                              topic_type: &str)
                              -> rosxmlrpc::client::ClientResult {
        self.client
            .request("registerPublisher",
                     &[self.client_id.as_str(), topic, topic_type, self.caller_api.as_str()])
    }

    pub fn unregister_publisher(&self, topic: &str) -> rosxmlrpc::client::ClientResult {
        self.client
            .request("unregisterPublisher",
                     &[self.client_id.as_str(), topic, self.caller_api.as_str()])
    }

    pub fn lookup_node(&self, node_name: &str) -> rosxmlrpc::client::ClientResult {
        self.client
            .request("lookupNode", &[self.client_id.as_str(), node_name])
    }

    pub fn get_published_topics(&self, subgraph: &str) -> rosxmlrpc::client::ClientResult {
        self.client
            .request("getPublishedTopics", &[self.client_id.as_str(), subgraph])
    }

    pub fn get_topic_types(&self) -> rosxmlrpc::client::ClientResult {
        self.client
            .request("getTopicTypes", &[self.client_id.as_str()])
    }

    pub fn get_system_state(&self) -> rosxmlrpc::client::ClientResult {
        self.client
            .request("getSystemState", &[self.client_id.as_str()])
    }

    pub fn get_uri(&self) -> rosxmlrpc::client::ClientResult {
        self.client
            .request("getUri", &[self.client_id.as_str()])
    }

    pub fn lookup_service(&self, service: &str) -> rosxmlrpc::client::ClientResult {
        self.client
            .request("lookupService", &[self.client_id.as_str(), service])
    }
}

#[derive(Debug)]
pub enum Error {
    XmlRpc(rosxmlrpc::client::Error),
    Ros(String),
    Format,
}

type MasterResult<T> = Result<T, Error>;

impl From<rosxmlrpc::client::Error> for Error {
    fn from(err: rosxmlrpc::client::Error) -> Error {
        Error::XmlRpc(err)
    }
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match *self {
            Error::XmlRpc(ref err) => write!(f, "XML RPC error: {}", err),
            Error::Ros(ref err) => write!(f, "ROS core error: {}", err),
            Error::Format => write!(f, "Bad response format"),
        }
    }
}

impl std::error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::XmlRpc(ref err) => err.description(),
            Error::Ros(ref err) => &err,
            Error::Format => "Bad response format received, expected [int, string, *]",
        }
    }

    fn cause(&self) -> Option<&std::error::Error> {
        match *self {
            Error::XmlRpc(ref err) => Some(err),
            Error::Ros(..) => None,
            Error::Format => None,
        }
    }
}
<|endoftext|>


<commit_before>// atomic/mod.rs
// AltOSRust
//
// Created by Daniel Seitz on 11/30/16

use ::core::cell::UnsafeCell;
use ::core::ops::{Add, Sub, BitAnd, BitOr, BitXor};

macro_rules! start_critical {
  ($var:ident) => {{
    unsafe {
      #![cfg(target_arch="arm")]
      asm!(
        concat!(
          "mrs $0, PRIMASK\n",
          "cpsid i\n")
        : "=r"($var)
        : /* no inputs */
        : /* no clobbers */
        : "volatile");
    }
  }}
}

macro_rules! end_critical {
  ($var:ident) => {{
    unsafe {
      #![cfg(target_arch="arm")]
      asm!("msr PRIMASK, $0"
        : /* no outputs */
        : "r"($var)
        : /* no clobbers */
        : "volatile");
    }
  }}
}

macro_rules! atomic {
  { $( $code:expr );*; } => {{
    let primask: u32;
    start_critical!(primask);
    $(
      $code;
    )*
    end_critical!(primask);
  }};
  { $( $code:expr );*; $last:expr } => {{
    let primask: u32;
    start_critical!(primask);
    $(
      $code;
    )*
    let result = $last;
    end_critical!(primask);
    result
  }};
  { $last:expr } => {{
    let primask: u32;
    start_critical!(primask);
    let result = $last;
    end_critical!(primask);
    result
  }}
}

pub struct Atomic<T> {
  data: UnsafeCell<T>,
}

unsafe impl<T> Sync for Atomic<T> {}

impl<T: Copy> Atomic<T> {
  pub const fn new(data: T) -> Self {
    Atomic { data: UnsafeCell::new(data) }
  }

  pub fn load(&self) -> T {
    atomic! {
      unsafe {
        *self.data.get()
      }
    }
  }

  pub fn store(&self, data: T) {
    atomic! {
      unsafe {
        *self.data.get() = data;
      }
    }
  }

  pub fn swap(&self, data: T) -> T {
    atomic! {
      unsafe {
        ::core::mem::replace(&mut *self.data.get(), data)
      }
    }
  }
}

impl<T: Copy + Default> Default for Atomic<T> {
  fn default() -> Self {
    Atomic { data: UnsafeCell::new(T::default()) }
  }
}

impl<T: Copy + PartialOrd> Atomic<T> {
  pub fn compare_and_swap(&self, current: T, new: T) -> T {
    match self.compare_exchange(current, new) {
      Ok(x) => x,
      Err(x) => x,
    }
  }

  pub fn compare_exchange(&self, current: T, new: T) -> Result<T, T> {
    atomic! {
      unsafe {
        let old = self.data.get();
        if *old == current {
          Ok(::core::mem::replace(&mut *old, new))
        }
        else {
          Err(*old)
        }
      }
    }
  }
}

impl<T: Copy + Add<Output=T>> Atomic<T> {
  pub fn fetch_add(&self, data: T) -> T {
    atomic! {
      unsafe {
        let old = self.data.get();
        let new = *old + data;
        ::core::mem::replace(&mut *old, new)
      }
    }
  }
}

impl<T: Copy + Sub<Output=T>> Atomic<T> {
  pub fn fetch_sub(&self, data: T) -> T {
    atomic! {
      unsafe {
        let old = self.data.get();
        let new = *old - data;
        ::core::mem::replace(&mut *old, new)
      }
    }
  }
}

impl<T: Copy + BitAnd<Output=T>> Atomic<T> {
  pub fn fetch_and(&self, data: T) -> T {
    atomic! {
      unsafe {
        let old = self.data.get();
        let new = *old & data;
        ::core::mem::replace(&mut *old, new)
      }
    }
  }

}

impl<T: Copy + BitOr<Output=T>> Atomic<T> {
  pub fn fetch_or(&self, data: T) -> T {
    atomic! {
      unsafe {
        let old = self.data.get();
        let new = *old | data;
        ::core::mem::replace(&mut *old, new)
      }
    }
  }

}

impl<T: Copy + BitXor<Output=T>> Atomic<T> {
  pub fn fetch_xor(&self, data: T) -> T {
    atomic! {
      unsafe {
        let old = self.data.get();
        let new = *old ^ data;
        ::core::mem::replace(&mut *old, new)
      }
    }
  }
}

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

  #[test]
  fn load() {
    let atom: Atomic<usize> = Atomic::new(0);

    assert_eq!(atom.load(), 0);
  }

  #[test]
  fn store() {
    let atom: Atomic<usize> = Atomic::new(0);

    atom.store(1);
    assert_eq!(atom.load(), 1);
  }

  #[test]
  fn swap() {
    let atom: Atomic<usize> = Atomic::new(0);

    assert_eq!(atom.swap(1), 0);
    assert_eq!(atom.load(), 1);
  }

  #[test]
  fn compare_and_swap() {
    let atom: Atomic<usize> = Atomic::new(0);

    assert_eq!(atom.compare_and_swap(0, 1), 0);
    assert_eq!(atom.load(), 1);

  }

  #[test]
  fn compare_and_swap_fail() {
    let atom: Atomic<usize> = Atomic::new(0);

    assert_eq!(atom.compare_and_swap(1, 2), 0);
    assert_eq!(atom.load(), 0);
  }

  #[test]
  fn compare_exchange() {
    let atom: Atomic<usize> = Atomic::new(0);

    assert_eq!(atom.compare_exchange(0, 1), Ok(0));
    assert_eq!(atom.load(), 1);
  }

  #[test]
  fn compare_exchange_fail() {
    let atom: Atomic<usize> = Atomic::new(0);

    assert_eq!(atom.compare_exchange(1, 2), Err(0));
    assert_eq!(atom.load(), 0);
  }

  #[test]
  fn fetch_add() {
    let atom: Atomic<usize> = Atomic::new(0);

    assert_eq!(atom.fetch_add(1), 0);
    assert_eq!(atom.fetch_add(1), 1);
    assert_eq!(atom.load(), 2);
  }

  #[test]
  fn fetch_sub() {
    let atom: Atomic<usize> = Atomic::new(10);

    assert_eq!(atom.fetch_sub(1), 10);
    assert_eq!(atom.fetch_sub(4), 9);
    assert_eq!(atom.load(), 5);
  }

  #[test]
  fn fetch_and() {
    let atom: Atomic<usize> = Atomic::new(0xFF);

    assert_eq!(atom.fetch_and(0xAA), 0xFF);
    assert_eq!(atom.fetch_and(0xF), 0xAA);
    assert_eq!(atom.load(), 0xA);
  }

  #[test]
  fn fetch_or() {
    let atom: Atomic<usize> = Atomic::new(0);

    assert_eq!(atom.fetch_or(0xAA), 0x0);
    assert_eq!(atom.fetch_or(0x55), 0xAA);
    assert_eq!(atom.load(), 0xFF);
  }

  #[test]
  fn fetch_xor() {
    let atom: Atomic<usize> = Atomic::new(0xAA);

    assert_eq!(atom.fetch_xor(0xFF), 0xAA);
    assert_eq!(atom.fetch_xor(0xFF), 0x55);
    assert_eq!(atom.load(), 0xAA);
  }
}
<commit_msg>comment on atomic test<commit_after>// atomic/mod.rs
// AltOSRust
//
// Created by Daniel Seitz on 11/30/16

use ::core::cell::UnsafeCell;
use ::core::ops::{Add, Sub, BitAnd, BitOr, BitXor};

macro_rules! start_critical {
  ($var:ident) => {{
    unsafe {
      #![cfg(target_arch="arm")]
      asm!(
        concat!(
          "mrs $0, PRIMASK\n",
          "cpsid i\n")
        : "=r"($var)
        : /* no inputs */
        : /* no clobbers */
        : "volatile");
    }
  }}
}

macro_rules! end_critical {
  ($var:ident) => {{
    unsafe {
      #![cfg(target_arch="arm")]
      asm!("msr PRIMASK, $0"
        : /* no outputs */
        : "r"($var)
        : /* no clobbers */
        : "volatile");
    }
  }}
}

macro_rules! atomic {
  { $( $code:expr );*; } => {{
    let primask: u32;
    start_critical!(primask);
    $(
      $code;
    )*
    end_critical!(primask);
  }};
  { $( $code:expr );*; $last:expr } => {{
    let primask: u32;
    start_critical!(primask);
    $(
      $code;
    )*
    let result = $last;
    end_critical!(primask);
    result
  }};
  { $last:expr } => {{
    let primask: u32;
    start_critical!(primask);
    let result = $last;
    end_critical!(primask);
    result
  }}
}

pub struct Atomic<T> {
  data: UnsafeCell<T>,
}

unsafe impl<T> Sync for Atomic<T> {}

impl<T: Copy> Atomic<T> {
  pub const fn new(data: T) -> Self {
    Atomic { data: UnsafeCell::new(data) }
  }

  pub fn load(&self) -> T {
    atomic! {
      unsafe {
        *self.data.get()
      }
    }
  }

  pub fn store(&self, data: T) {
    atomic! {
      unsafe {
        *self.data.get() = data;
      }
    }
  }

  pub fn swap(&self, data: T) -> T {
    atomic! {
      unsafe {
        ::core::mem::replace(&mut *self.data.get(), data)
      }
    }
  }
}

impl<T: Copy + Default> Default for Atomic<T> {
  fn default() -> Self {
    Atomic { data: UnsafeCell::new(T::default()) }
  }
}

impl<T: Copy + PartialOrd> Atomic<T> {
  pub fn compare_and_swap(&self, current: T, new: T) -> T {
    match self.compare_exchange(current, new) {
      Ok(x) => x,
      Err(x) => x,
    }
  }

  pub fn compare_exchange(&self, current: T, new: T) -> Result<T, T> {
    atomic! {
      unsafe {
        let old = self.data.get();
        if *old == current {
          Ok(::core::mem::replace(&mut *old, new))
        }
        else {
          Err(*old)
        }
      }
    }
  }
}

impl<T: Copy + Add<Output=T>> Atomic<T> {
  pub fn fetch_add(&self, data: T) -> T {
    atomic! {
      unsafe {
        let old = self.data.get();
        let new = *old + data;
        ::core::mem::replace(&mut *old, new)
      }
    }
  }
}

impl<T: Copy + Sub<Output=T>> Atomic<T> {
  pub fn fetch_sub(&self, data: T) -> T {
    atomic! {
      unsafe {
        let old = self.data.get();
        let new = *old - data;
        ::core::mem::replace(&mut *old, new)
      }
    }
  }
}

impl<T: Copy + BitAnd<Output=T>> Atomic<T> {
  pub fn fetch_and(&self, data: T) -> T {
    atomic! {
      unsafe {
        let old = self.data.get();
        let new = *old & data;
        ::core::mem::replace(&mut *old, new)
      }
    }
  }

}

impl<T: Copy + BitOr<Output=T>> Atomic<T> {
  pub fn fetch_or(&self, data: T) -> T {
    atomic! {
      unsafe {
        let old = self.data.get();
        let new = *old | data;
        ::core::mem::replace(&mut *old, new)
      }
    }
  }

}

impl<T: Copy + BitXor<Output=T>> Atomic<T> {
  pub fn fetch_xor(&self, data: T) -> T {
    atomic! {
      unsafe {
        let old = self.data.get();
        let new = *old ^ data;
        ::core::mem::replace(&mut *old, new)
      }
    }
  }
}

#[cfg(test)]
mod tests {
  // As a side note, these operations are not actually atomic when compiled
  // for anything other than ARM
  use super::Atomic;

  #[test]
  fn load() {
    let atom: Atomic<usize> = Atomic::new(0);

    assert_eq!(atom.load(), 0);
  }

  #[test]
  fn store() {
    let atom: Atomic<usize> = Atomic::new(0);

    atom.store(1);
    assert_eq!(atom.load(), 1);
  }

  #[test]
  fn swap() {
    let atom: Atomic<usize> = Atomic::new(0);

    assert_eq!(atom.swap(1), 0);
    assert_eq!(atom.load(), 1);
  }

  #[test]
  fn compare_and_swap() {
    let atom: Atomic<usize> = Atomic::new(0);

    assert_eq!(atom.compare_and_swap(0, 1), 0);
    assert_eq!(atom.load(), 1);

  }

  #[test]
  fn compare_and_swap_fail() {
    let atom: Atomic<usize> = Atomic::new(0);

    assert_eq!(atom.compare_and_swap(1, 2), 0);
    assert_eq!(atom.load(), 0);
  }

  #[test]
  fn compare_exchange() {
    let atom: Atomic<usize> = Atomic::new(0);

    assert_eq!(atom.compare_exchange(0, 1), Ok(0));
    assert_eq!(atom.load(), 1);
  }

  #[test]
  fn compare_exchange_fail() {
    let atom: Atomic<usize> = Atomic::new(0);

    assert_eq!(atom.compare_exchange(1, 2), Err(0));
    assert_eq!(atom.load(), 0);
  }

  #[test]
  fn fetch_add() {
    let atom: Atomic<usize> = Atomic::new(0);

    assert_eq!(atom.fetch_add(1), 0);
    assert_eq!(atom.fetch_add(1), 1);
    assert_eq!(atom.load(), 2);
  }

  #[test]
  fn fetch_sub() {
    let atom: Atomic<usize> = Atomic::new(10);

    assert_eq!(atom.fetch_sub(1), 10);
    assert_eq!(atom.fetch_sub(4), 9);
    assert_eq!(atom.load(), 5);
  }

  #[test]
  fn fetch_and() {
    let atom: Atomic<usize> = Atomic::new(0xFF);

    assert_eq!(atom.fetch_and(0xAA), 0xFF);
    assert_eq!(atom.fetch_and(0xF), 0xAA);
    assert_eq!(atom.load(), 0xA);
  }

  #[test]
  fn fetch_or() {
    let atom: Atomic<usize> = Atomic::new(0);

    assert_eq!(atom.fetch_or(0xAA), 0x0);
    assert_eq!(atom.fetch_or(0x55), 0xAA);
    assert_eq!(atom.load(), 0xFF);
  }

  #[test]
  fn fetch_xor() {
    let atom: Atomic<usize> = Atomic::new(0xAA);

    assert_eq!(atom.fetch_xor(0xFF), 0xAA);
    assert_eq!(atom.fetch_xor(0xFF), 0x55);
    assert_eq!(atom.load(), 0xAA);
  }
}
<|endoftext|>


<commit_before>use crate::SQLPOINTER;

/// Governs behaviour of EnvironmentAttribute
#[repr(i32)]
#[allow(non_camel_case_types)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum EnvironmentAttribute {
    SQL_ATTR_ODBC_VERSION = 200,
    SQL_ATTR_CONNECTION_POOLING = 201,
    SQL_ATTR_CP_MATCH = 202,
    // This attribute was commented out because there is no mention of it in the ODBC
    // specification nor does this attribute exist in unixODBC or iODBC implementations.
    // This attribute exists in Microsoft implementation only and it's usage is unclear.
    // For private driver manager
    //SQL_ATTR_APPLICATION_KEY = 203,
    SQL_ATTR_OUTPUT_NTS = 10001,
}
pub use EnvironmentAttribute::*;

/// ODBC verions
///
/// Possible values for `SQL_ATTR_ODBC_VERSION` attribute set with `SQLSetEnvAttr` to
/// declare ODBC version
#[allow(non_camel_case_types)]
#[repr(i32)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum OdbcVersion {
    SQL_OV_ODBC2 = 2,
    SQL_OV_ODBC3 = 3,
    #[cfg(feature = "odbc_version_3_80")]
    SQL_OV_ODBC3_80 = 380,
    #[cfg(feature = "odbc_version_4")]
    SQL_OV_ODBC4 = 400,
}
pub use OdbcVersion::*;

impl From<OdbcVersion> for SQLPOINTER {
    fn from(source: OdbcVersion) -> SQLPOINTER {
        source as i32 as SQLPOINTER
    }
}

/// Connection pool configuration
///
/// Possible values for `SQL_ATTR_CONNECTION_POOLING` attribute set with `SQLSetEnvAttr` to define
/// which pooling scheme will be used
#[allow(non_camel_case_types)]
#[repr(u32)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ConnPoolConfig {
    SQL_CP_OFF = 0,
    SQL_CP_ONE_PER_DRIVER = 1,
    SQL_CP_ONE_PER_HENV = 2,
    SQL_CP_DRIVER_AWARE = 3,
}
pub use ConnPoolConfig::*;

/// Connection pool default configuration
pub const SQL_CP_DEFAULT: ConnPoolConfig = ConnPoolConfig::SQL_CP_OFF;

impl From<ConnPoolConfig> for SQLPOINTER {
    fn from(source: ConnPoolConfig) -> SQLPOINTER {
        source as u32 as SQLPOINTER
    }
}

/// Matching of pooled connections
///
/// Possible values for `SQL_ATTR_CP_MATCH` attribute set with `SQLSetEnvAttr` to define
/// which connection attributes must match for a connection returned from the pool
#[allow(non_camel_case_types)]
#[repr(u32)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ConnPoolMatch {
    SQL_CP_STRICT_MATCH = 0,
    SQL_CP_RELAXED_MATCH = 1,
}
pub use ConnPoolMatch::*;

/// Default matching for connections returned from the pool
pub const SQL_CP_MATCH_DEFAULT: ConnPoolMatch = ConnPoolMatch::SQL_CP_STRICT_MATCH;

impl From<ConnPoolMatch> for SQLPOINTER {
    fn from(source: ConnPoolMatch) -> SQLPOINTER {
        source as u32 as SQLPOINTER
    }
}

/// Enable/disable null-terminated strings
///
/// Possible values for `SQL_ATTR_OUTPUT_NTS` attribute set with `SQLSetEnvAttr` to
/// enable/disable null-terminated strings
#[allow(non_camel_case_types)]
#[repr(i32)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum OutputNTS {
    SQL_FALSE = 0,
    SQL_TRUE = 1,
}
pub use OutputNTS::*;

impl From<OutputNTS> for SQLPOINTER {
    fn from(source: OutputNTS) -> SQLPOINTER {
        source as i32 as SQLPOINTER
    }
}
<commit_msg>Renamed ValuePtr enums to reflect with which attribute they are meant to be used<commit_after>use crate::SQLPOINTER;

/// Governs behaviour of EnvironmentAttribute
#[repr(i32)]
#[allow(non_camel_case_types)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum EnvironmentAttribute {
    SQL_ATTR_ODBC_VERSION = 200,
    SQL_ATTR_CONNECTION_POOLING = 201,
    SQL_ATTR_CP_MATCH = 202,
    // This attribute was commented out because there is no mention of it in the ODBC
    // specification nor does this attribute exist in unixODBC or iODBC implementations.
    // This attribute exists in Microsoft implementation only and it's usage is unclear.
    // For private driver manager
    //SQL_ATTR_APPLICATION_KEY = 203,
    SQL_ATTR_OUTPUT_NTS = 10001,
}
pub use EnvironmentAttribute::*;

/// ODBC verions
///
/// Possible values for `SQL_ATTR_ODBC_VERSION` attribute set with `SQLSetEnvAttr` to
/// declare ODBC version
#[allow(non_camel_case_types)]
#[repr(i32)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SQL_ATTR_ODBC_VERSION {
    SQL_OV_ODBC2 = 2,
    SQL_OV_ODBC3 = 3,
    #[cfg(feature = "odbc_version_3_80")]
    SQL_OV_ODBC3_80 = 380,
    #[cfg(feature = "odbc_version_4")]
    SQL_OV_ODBC4 = 400,
}
pub use SQL_ATTR_ODBC_VERSION::*;

impl From<SQL_ATTR_ODBC_VERSION> for SQLPOINTER {
    fn from(source: SQL_ATTR_ODBC_VERSION) -> SQLPOINTER {
        source as i32 as SQLPOINTER
    }
}

/// Connection pool configuration
///
/// Possible values for `SQL_ATTR_CONNECTION_POOLING` attribute set with `SQLSetEnvAttr` to define
/// which pooling scheme will be used
#[allow(non_camel_case_types)]
#[repr(u32)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SQL_ATTR_CONNECTION_POOLING {
    SQL_CP_OFF = 0,
    SQL_CP_ONE_PER_DRIVER = 1,
    SQL_CP_ONE_PER_HENV = 2,
    SQL_CP_DRIVER_AWARE = 3,
}
pub use SQL_ATTR_CONNECTION_POOLING::*;

/// Connection pool default configuration
pub const SQL_CP_DEFAULT: SQL_ATTR_CONNECTION_POOLING = SQL_ATTR_CONNECTION_POOLING::SQL_CP_OFF;

impl From<SQL_ATTR_CONNECTION_POOLING> for SQLPOINTER {
    fn from(source: SQL_ATTR_CONNECTION_POOLING) -> SQLPOINTER {
        source as u32 as SQLPOINTER
    }
}

/// Matching of pooled connections
///
/// Possible values for `SQL_ATTR_CP_MATCH` attribute set with `SQLSetEnvAttr` to define
/// which connection attributes must match for a connection returned from the pool
#[allow(non_camel_case_types)]
#[repr(u32)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SQL_ATTR_CP_MATCH {
    SQL_CP_STRICT_MATCH = 0,
    SQL_CP_RELAXED_MATCH = 1,
}
pub use SQL_ATTR_CP_MATCH::*;

/// Default matching for connections returned from the pool
pub const SQL_CP_MATCH_DEFAULT: SQL_ATTR_CP_MATCH = SQL_ATTR_CP_MATCH::SQL_CP_STRICT_MATCH;

impl From<SQL_ATTR_CP_MATCH> for SQLPOINTER {
    fn from(source: SQL_ATTR_CP_MATCH) -> SQLPOINTER {
        source as u32 as SQLPOINTER
    }
}
<|endoftext|>