<commit_before>use std::mem;
use std::iter;
use std::fmt;
use std::ops;

use super::util;

pub struct Segment {
    vecs: Vec<Vec<u8>>,
    length: usize,
}

#[derive(Copy, Clone)]
struct Index {
    outer: usize,
    inner: usize,
}

struct Indexes<'a> {
    seg: &'a Segment,
    index: Index,
}

pub struct Items<'a> {
    seg: &'a Segment,
    index: Index,
    num_elem: Option<usize>,
}

pub struct MutItems<'a> {
    seg: &'a mut Segment,
    index: Index,
    num_elem: Option<usize>,
}

pub struct Slices<'a> {
    seg: &'a Segment,
    outer: usize,
}

static min_block_size: usize = 1024 * 1024;
static max_block_size: usize = 4 * 1024 * 1024;

impl Segment {
    pub fn _internal_debug(&self) -> Vec<usize> {
        self.vecs.iter().map(|v| v.len()).collect::<Vec<usize>>()
    }
    pub fn new() -> Segment {
        Segment {
            vecs: Vec::new(),
            length: 0,
        }
    }
    pub fn from_vec(values: Vec<u8>) -> Segment {
        let len = values.len();
        Segment {
            vecs: vec!(values),
            length: len,
        }
    }

    pub fn from_slice(values: &[u8]) -> Segment {
        Segment {
            vecs: vec!(values.into()),
            length: values.len(),
        }
    }

    pub fn len(&self) -> usize {
        self.length
    }

    fn calc_len(&mut self) {
        self.length = 0;
        for len in self.vecs.iter().map(|v| v.len()) {
            self.length += len
        }
    }

    fn pos_to_index(&self, pos: usize, for_insert: bool) -> Index {
        if pos == 0 {
            return Index { outer: 0, inner: 0 };
        }

        let mut cur_pos = pos;
        for (i, vec) in self.vecs.iter().enumerate() {
            if cur_pos < vec.len() || (for_insert && cur_pos == vec.len()) {
                return Index {
                    outer: i,
                    inner: cur_pos,
                }
            }
            cur_pos -= vec.len();
        }

        panic!("Position {} is out of bounds", pos);
    }

    pub fn iter_range<'a>(&'a self, from: usize, to: usize) -> Items<'a> {
        if to < from {
            panic!("to ({}) is smaller than from ({})!", to, from);
        }

        let idx = self.pos_to_index(from, false);
        Items {
            seg: self,
            index: idx,
            num_elem: Some(to - from),
        }
    }

    pub fn mut_iter_range<'a>(&'a mut self, from: usize, to: usize) -> MutItems<'a> {
        if to < from {
            panic!("to ({}) is smaller than from ({})!", to, from);
        }

        let idx = self.pos_to_index(from, false);
        MutItems {
            seg: self,
            index: idx,
            num_elem: Some(to - from),
        }
    }

    fn iter_index<'a>(&'a self, from: usize) -> Indexes<'a> {
        let index = self.pos_to_index(from, false);
        Indexes {
            seg: self,
            index: index,
        }
    }

    pub fn iter_slices<'a>(&'a self) -> Slices<'a> {
        Slices {
            seg: self,
            outer: 0,
        }
    }

    fn prepare_insert(&mut self, index: Index) -> Index {
        // TODO: Get self.vecs.get(index.outer) into a local variable without ruining lifetimes?
        if index.outer >= self.vecs.len() {
            self.vecs.push(Vec::new());
        }

        if self.vecs[index.outer].len() < max_block_size {
            return index;
        }

        let page_start_idx = (index.inner / min_block_size) * min_block_size;
        if page_start_idx == 0 {
            if self.vecs[index.outer].len() > max_block_size {
                let insert_vec: Vec < _ >= self.vecs[index.outer][min_block_size..].into(); self
                                           .vecs.insert(index.outer + 1, insert_vec); self
                                           .vecs[index.outer].truncate(min_block_size);
            }

            return index;
        } else {
            let insert_vec: Vec<_> = self.vecs[index.outer][page_start_idx..].into();
            self.vecs.insert(index.outer + 1, insert_vec);
            self.vecs[index.outer].truncate(page_start_idx);
            return self.prepare_insert(Index {
                outer: index.outer + 1,
                inner: index.inner - page_start_idx
            })
        }
    }

    pub fn insert(&mut self, offset: usize, values: &[u8]) {
        let mut index = self.pos_to_index(offset, true);
        index = self.prepare_insert(index);

        // This is needed for the mut borrow vec
        {
            let vec = &mut self.vecs[index.outer];
            // TODO: There has to be a better way for this range
            for val in values.into_iter().rev() {
                vec.insert(index.inner, *val);
            }
        }

        self.calc_len();
    }

    pub fn move_out_slice(&mut self, start_offset: usize, end_offset: usize) -> Vec<u8> {
        assert!(start_offset <= end_offset);
        let mut res = Vec::new();
        let mut index = self.pos_to_index(start_offset, false);
        let num_elem = end_offset - start_offset;

        for _ in 0..num_elem {
            let c = self.vecs[index.outer].remove(index.inner);
            res.push(c);

            if index.inner >= self.vecs[index.outer].len() {
                if self.vecs[index.outer].len() == 0 {
                    self.vecs.remove(index.outer);
                } else {
                    index.inner = 0;
                    index.outer += 1;
                }
            }
        }

        self.calc_len();

        res
    }

    pub fn find_slice(&self, needle: &[u8]) {
        self.find_slice_from(0, needle);
    }

    pub fn find_slice_from(&self, from: usize, needle: &[u8]) -> Option<usize> {
        let len = self.len();

        for i in from..self.len() {
            if util::iter_equals(self.iter_range(i, len), needle.iter()) {
                return Some(i);
            }
        }
        None
    }
}

impl ops::Index<usize> for Segment {
    type Output = u8;
    fn index<'a>(&'a self, _index: usize) -> &'a u8 {
        let idx = self.pos_to_index(_index, false);
        &self.vecs[idx.outer][idx.inner]
    }
}

impl ops::IndexMut<usize> for Segment {
    fn index_mut<'a>(&'a mut self, _index: usize) -> &'a mut u8 {
        let idx = self.pos_to_index(_index, false);
        &mut self.vecs[idx.outer][idx.inner]
    }
}

impl fmt::Debug for Segment {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.vecs.fmt(f)
    }
}

impl<'a> Iterator for Indexes<'a> {
    type Item = Index;
    fn next(&mut self) -> Option<Index> {
        if self.index.outer >= self.seg.vecs.len() {
            return None;
        }

        let res = self.index;

        self.index.inner += 1;
        if self.index.inner >= self.seg.vecs[self.index.outer].len() {
            self.index.inner = 0;
            self.index.outer += 1;
        }
        Some(res)
    }
}

impl<'a> Iterator for Items<'a> {
    type Item = &'a u8;
    fn next(&mut self) -> Option<&'a u8> {
        if self.index.outer >= self.seg.vecs.len() {
            return None;
        }

        let elem = {
            let vv = &self.seg.vecs[self.index.outer];
            &vv[self.index.inner]
        };

        self.index.inner += 1;
        if self.index.inner >= self.seg.vecs[self.index.outer].len() {
            self.index.inner = 0;
            self.index.outer += 1;
        }

        Some(elem)
    }
}

impl<'a> Iterator for MutItems<'a> {
    type Item = &'a mut u8;
    fn next(&mut self) -> Option<&'a mut u8> {
        if self.index.outer >= self.seg.vecs.len() {
            return None;
        }

        let elem_raw: *mut u8 = {
            let vv = &mut self.seg.vecs[self.index.outer];
            &mut vv[self.index.inner]
        };

        self.index.inner += 1;
        if self.index.inner >= self.seg.vecs[self.index.outer].len() {
            self.index.inner = 0;
            self.index.outer += 1;
        }

        Some(unsafe { &mut *elem_raw })
    }
}

impl<'a> Iterator for Slices<'a> {
    type Item = &'a [u8];
    fn next(&mut self) -> Option<&'a [u8]> {
        if self.outer >= self.seg.vecs.len() {
            None
        } else {
            let i = self.outer;
            self.outer += 1;
            Some(&self.seg.vecs[i])
        }
    }
}

#[test]
fn test_segment() {
    let mut s = Segment::from_slice(&[1, 2, 3, 4]);
    s.insert_slice(0, &[7, 7, 7, 7, 7]);
}
<commit_msg>Change const to upper case<commit_after>use std::mem;
use std::iter;
use std::fmt;
use std::ops;

use super::util;

pub struct Segment {
    vecs: Vec<Vec<u8>>,
    length: usize,
}

#[derive(Copy, Clone)]
struct Index {
    outer: usize,
    inner: usize,
}

struct Indexes<'a> {
    seg: &'a Segment,
    index: Index,
}

pub struct Items<'a> {
    seg: &'a Segment,
    index: Index,
    num_elem: Option<usize>,
}

pub struct MutItems<'a> {
    seg: &'a mut Segment,
    index: Index,
    num_elem: Option<usize>,
}

pub struct Slices<'a> {
    seg: &'a Segment,
    outer: usize,
}

static MIN_BLOCK_SIZE: usize = 1024 * 1024;
static MAX_BLOCK_SIZE: usize = 4 * 1024 * 1024;

impl Segment {
    pub fn _internal_debug(&self) -> Vec<usize> {
        self.vecs.iter().map(|v| v.len()).collect::<Vec<usize>>()
    }
    pub fn new() -> Segment {
        Segment {
            vecs: Vec::new(),
            length: 0,
        }
    }
    pub fn from_vec(values: Vec<u8>) -> Segment {
        let len = values.len();
        Segment {
            vecs: vec!(values),
            length: len,
        }
    }

    pub fn from_slice(values: &[u8]) -> Segment {
        Segment {
            vecs: vec!(values.into()),
            length: values.len(),
        }
    }

    pub fn len(&self) -> usize {
        self.length
    }

    fn calc_len(&mut self) {
        self.length = 0;
        for len in self.vecs.iter().map(|v| v.len()) {
            self.length += len
        }
    }

    fn pos_to_index(&self, pos: usize, for_insert: bool) -> Index {
        if pos == 0 {
            return Index { outer: 0, inner: 0 };
        }

        let mut cur_pos = pos;
        for (i, vec) in self.vecs.iter().enumerate() {
            if cur_pos < vec.len() || (for_insert && cur_pos == vec.len()) {
                return Index {
                    outer: i,
                    inner: cur_pos,
                }
            }
            cur_pos -= vec.len();
        }

        panic!("Position {} is out of bounds", pos);
    }

    pub fn iter_range<'a>(&'a self, from: usize, to: usize) -> Items<'a> {
        if to < from {
            panic!("to ({}) is smaller than from ({})!", to, from);
        }

        let idx = self.pos_to_index(from, false);
        Items {
            seg: self,
            index: idx,
            num_elem: Some(to - from),
        }
    }

    pub fn mut_iter_range<'a>(&'a mut self, from: usize, to: usize) -> MutItems<'a> {
        if to < from {
            panic!("to ({}) is smaller than from ({})!", to, from);
        }

        let idx = self.pos_to_index(from, false);
        MutItems {
            seg: self,
            index: idx,
            num_elem: Some(to - from),
        }
    }

    fn iter_index<'a>(&'a self, from: usize) -> Indexes<'a> {
        let index = self.pos_to_index(from, false);
        Indexes {
            seg: self,
            index: index,
        }
    }

    pub fn iter_slices<'a>(&'a self) -> Slices<'a> {
        Slices {
            seg: self,
            outer: 0,
        }
    }

    fn prepare_insert(&mut self, index: Index) -> Index {
        // TODO: Get self.vecs.get(index.outer) into a local variable without ruining lifetimes?
        if index.outer >= self.vecs.len() {
            self.vecs.push(Vec::new());
        }

        if self.vecs[index.outer].len() < MAX_BLOCK_SIZE {
            return index;
        }

        let page_start_idx = (index.inner / MIN_BLOCK_SIZE) * MIN_BLOCK_SIZE;
        if page_start_idx == 0 {
            if self.vecs[index.outer].len() > MAX_BLOCK_SIZE {
                let insert_vec: Vec < _ >= self.vecs[index.outer][MIN_BLOCK_SIZE..].into(); self
                                           .vecs.insert(index.outer + 1, insert_vec); self
                                           .vecs[index.outer].truncate(MIN_BLOCK_SIZE);
            }

            return index;
        } else {
            let insert_vec: Vec<_> = self.vecs[index.outer][page_start_idx..].into();
            self.vecs.insert(index.outer + 1, insert_vec);
            self.vecs[index.outer].truncate(page_start_idx);
            return self.prepare_insert(Index {
                outer: index.outer + 1,
                inner: index.inner - page_start_idx
            })
        }
    }

    pub fn insert(&mut self, offset: usize, values: &[u8]) {
        let mut index = self.pos_to_index(offset, true);
        index = self.prepare_insert(index);

        // This is needed for the mut borrow vec
        {
            let vec = &mut self.vecs[index.outer];
            // TODO: There has to be a better way for this range
            for val in values.into_iter().rev() {
                vec.insert(index.inner, *val);
            }
        }

        self.calc_len();
    }

    pub fn move_out_slice(&mut self, start_offset: usize, end_offset: usize) -> Vec<u8> {
        assert!(start_offset <= end_offset);
        let mut res = Vec::new();
        let mut index = self.pos_to_index(start_offset, false);
        let num_elem = end_offset - start_offset;

        for _ in 0..num_elem {
            let c = self.vecs[index.outer].remove(index.inner);
            res.push(c);

            if index.inner >= self.vecs[index.outer].len() {
                if self.vecs[index.outer].len() == 0 {
                    self.vecs.remove(index.outer);
                } else {
                    index.inner = 0;
                    index.outer += 1;
                }
            }
        }

        self.calc_len();

        res
    }

    pub fn find_slice(&self, needle: &[u8]) {
        self.find_slice_from(0, needle);
    }

    pub fn find_slice_from(&self, from: usize, needle: &[u8]) -> Option<usize> {
        let len = self.len();

        for i in from..self.len() {
            if util::iter_equals(self.iter_range(i, len), needle.iter()) {
                return Some(i);
            }
        }
        None
    }
}

impl ops::Index<usize> for Segment {
    type Output = u8;
    fn index<'a>(&'a self, _index: usize) -> &'a u8 {
        let idx = self.pos_to_index(_index, false);
        &self.vecs[idx.outer][idx.inner]
    }
}

impl ops::IndexMut<usize> for Segment {
    fn index_mut<'a>(&'a mut self, _index: usize) -> &'a mut u8 {
        let idx = self.pos_to_index(_index, false);
        &mut self.vecs[idx.outer][idx.inner]
    }
}

impl fmt::Debug for Segment {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.vecs.fmt(f)
    }
}

impl<'a> Iterator for Indexes<'a> {
    type Item = Index;
    fn next(&mut self) -> Option<Index> {
        if self.index.outer >= self.seg.vecs.len() {
            return None;
        }

        let res = self.index;

        self.index.inner += 1;
        if self.index.inner >= self.seg.vecs[self.index.outer].len() {
            self.index.inner = 0;
            self.index.outer += 1;
        }
        Some(res)
    }
}

impl<'a> Iterator for Items<'a> {
    type Item = &'a u8;
    fn next(&mut self) -> Option<&'a u8> {
        if self.index.outer >= self.seg.vecs.len() {
            return None;
        }

        let elem = {
            let vv = &self.seg.vecs[self.index.outer];
            &vv[self.index.inner]
        };

        self.index.inner += 1;
        if self.index.inner >= self.seg.vecs[self.index.outer].len() {
            self.index.inner = 0;
            self.index.outer += 1;
        }

        Some(elem)
    }
}

impl<'a> Iterator for MutItems<'a> {
    type Item = &'a mut u8;
    fn next(&mut self) -> Option<&'a mut u8> {
        if self.index.outer >= self.seg.vecs.len() {
            return None;
        }

        let elem_raw: *mut u8 = {
            let vv = &mut self.seg.vecs[self.index.outer];
            &mut vv[self.index.inner]
        };

        self.index.inner += 1;
        if self.index.inner >= self.seg.vecs[self.index.outer].len() {
            self.index.inner = 0;
            self.index.outer += 1;
        }

        Some(unsafe { &mut *elem_raw })
    }
}

impl<'a> Iterator for Slices<'a> {
    type Item = &'a [u8];
    fn next(&mut self) -> Option<&'a [u8]> {
        if self.outer >= self.seg.vecs.len() {
            None
        } else {
            let i = self.outer;
            self.outer += 1;
            Some(&self.seg.vecs[i])
        }
    }
}

#[test]
fn test_segment() {
    let mut s = Segment::from_slice(&[1, 2, 3, 4]);
    s.insert_slice(0, &[7, 7, 7, 7, 7]);
}
<|endoftext|>


<commit_before>// Filebuffer -- Fast and simple file reading
// Copyright 2016 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.

//! This mod contains the platform-specific implementations for Windows based on the winapi crate.

use std::fs;
use std::io;
use std::os;
use std::os::windows::io::AsRawHandle;
use std::ptr;

extern crate winapi;

#[derive(Debug)]
pub struct PlatformData {
    // On Windows, the file must be kept open for the lifetime of the mapping.
    #[allow(dead_code)] // The field is not dead, the destructor is effectful.
    file: fs::File,
    mapping_handle: winapi::um::winnt::HANDLE,
}

impl Drop for PlatformData {
    fn drop (&mut self) {
        if self.mapping_handle != ptr::null_mut() {
            let success = unsafe { winapi::um::handleapi::CloseHandle(self.mapping_handle) };
            assert!(success != 0);
        }
    }
}

pub fn map_file(file: fs::File) -> io::Result<(*const u8, usize, PlatformData)> {
    let file_handle = file.as_raw_handle();
    let length = try!(file.metadata()).len();

    if length > usize::max_value() as u64 {
        return Err(io::Error::new(io::ErrorKind::Other, "file is larger than address space"));
    }

    let mut platform_data = PlatformData {
        file: file,
        mapping_handle: ptr::null_mut(),
    };

    // Don't try to map anything if the file is empty.
    if length == 0 {
        return Ok((ptr::null(), 0, platform_data));
    }

    // Memory-mapping a file on Windows is a two-step process: first we create a file mapping
    // object, and then we create a view of that mapping in the virtual address space.
    platform_data.mapping_handle = unsafe {
        winapi::um::memoryapi::CreateFileMappingW(
            file_handle,
            ptr::null_mut(),                  // Use default security policy.
            winapi::um::winnt::PAGE_READONLY, // The memory will be read-only.
            0, 0,                             // The mapping size is the size of the file.
            ptr::null_mut()                   // The mapping does not have a name.
        )
    };

    if platform_data.mapping_handle == ptr::null_mut() {
        return Err(io::Error::last_os_error());
    }

    let result = unsafe {
        winapi::um::memoryapi::MapViewOfFile(
            platform_data.mapping_handle,
            winapi::um::memoryapi::FILE_MAP_READ,     // The memory mapping will be read-only.
            0, 0,                                     // Start offset of the mapping is 0.
            length as winapi::shared::basetsd::SIZE_T // Map the entire file.
        )
    };

    if result == ptr::null_mut() {
        Err(io::Error::last_os_error())
    } else {
        Ok((result as *const u8, length as usize, platform_data))
    }
}

pub fn unmap_file(buffer: *const u8, _length: usize) {
    let success = unsafe {
        winapi::um::memoryapi::UnmapViewOfFile(buffer as *mut os::raw::c_void)
    };
    assert!(success != 0);
}

/// See also `unix::get_resident`.
pub fn get_resident(_buffer: *const u8, _length: usize, residency: &mut [bool]) {
    // As far as I am aware, Windows does not expose a way to query whether pages are resident.
    // There is no equivalent of `mincore()`. The closest thing is `VirtualQuery()`, but the
    // `state` value in the `MEMORY_BASIC_INFORMATION` struct that it fills does not indicate
    // whether the page is resident.

    // Lie and pretend everything is resident.
    for x in residency {
        *x = true;
    }
}

/// See also `unix::prefetch`.
pub fn prefetch(buffer: *const u8, length: usize) {
    let mut entry = winapi::um::memoryapi::WIN32_MEMORY_RANGE_ENTRY {
        VirtualAddress: buffer as *mut os::raw::c_void,
        NumberOfBytes: length as winapi::shared::basetsd::SIZE_T,
    };

    unsafe {
        let current_process_handle = winapi::um::processthreadsapi::GetCurrentProcess();
        winapi::um::memoryapi::PrefetchVirtualMemory(
            current_process_handle, // Prefetch for the current process.
            1, &mut entry,          // An array of length 1 that contains `entry`.
            0                       // Reserved flag that must be 0.
        );
    }

    // The return value of `PrefetchVirtualMemory` is ignored. MSDN says the function may fail if
    // the system is under memory pressure. (It is not entirely clear whether "fail" means
    // "returns a nonzero value", but I assume it does.)
}

pub fn get_page_size() -> usize {
    // Fill the `SYSTEM_INFO` struct with zeroes. It will be filled by
    // `GetSystemInfo` later but Rust requires it to be initialized.
    let mut sysinfo = winapi::um::sysinfoapi::SYSTEM_INFO {
        wProcessorArchitecture: 0,
        wReserved: 0,
        dwPageSize: 0,
        lpMinimumApplicationAddress: ptr::null_mut(),
        lpMaximumApplicationAddress: ptr::null_mut(),
        dwActiveProcessorMask: 0,
        dwNumberOfProcessors: 0,
        dwProcessorType: 0,
        dwAllocationGranularity: 0,
        wProcessorLevel: 0,
        wProcessorRevision: 0
    };
    unsafe { winapi::um::sysinfoapi::GetSystemInfo(&mut sysinfo); }
    sysinfo.dwPageSize as usize
}
<commit_msg>Fix more build errors on Windows<commit_after>// Filebuffer -- Fast and simple file reading
// Copyright 2016 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.

//! This mod contains the platform-specific implementations for Windows based on the winapi crate.

use std::fs;
use std::io;
use std::mem;
use std::os;
use std::os::windows::io::AsRawHandle;
use std::ptr;

extern crate winapi;

#[derive(Debug)]
pub struct PlatformData {
    // On Windows, the file must be kept open for the lifetime of the mapping.
    #[allow(dead_code)] // The field is not dead, the destructor is effectful.
    file: fs::File,
    mapping_handle: winapi::um::winnt::HANDLE,
}

impl Drop for PlatformData {
    fn drop (&mut self) {
        if self.mapping_handle != ptr::null_mut() {
            let success = unsafe { winapi::um::handleapi::CloseHandle(self.mapping_handle) };
            assert!(success != 0);
        }
    }
}

pub fn map_file(file: fs::File) -> io::Result<(*const u8, usize, PlatformData)> {
    let file_handle = file.as_raw_handle();
    let length = try!(file.metadata()).len();

    if length > usize::max_value() as u64 {
        return Err(io::Error::new(io::ErrorKind::Other, "file is larger than address space"));
    }

    let mut platform_data = PlatformData {
        file: file,
        mapping_handle: ptr::null_mut(),
    };

    // Don't try to map anything if the file is empty.
    if length == 0 {
        return Ok((ptr::null(), 0, platform_data));
    }

    // Memory-mapping a file on Windows is a two-step process: first we create a file mapping
    // object, and then we create a view of that mapping in the virtual address space.
    platform_data.mapping_handle = unsafe {
        winapi::um::memoryapi::CreateFileMappingW(
            file_handle as *mut winapi::ctypes::c_void,
            ptr::null_mut(),                  // Use default security policy.
            winapi::um::winnt::PAGE_READONLY, // The memory will be read-only.
            0, 0,                             // The mapping size is the size of the file.
            ptr::null_mut()                   // The mapping does not have a name.
        )
    };

    if platform_data.mapping_handle == ptr::null_mut() {
        return Err(io::Error::last_os_error());
    }

    let result = unsafe {
        winapi::um::memoryapi::MapViewOfFile(
            platform_data.mapping_handle,
            winapi::um::memoryapi::FILE_MAP_READ,     // The memory mapping will be read-only.
            0, 0,                                     // Start offset of the mapping is 0.
            length as winapi::shared::basetsd::SIZE_T // Map the entire file.
        )
    };

    if result == ptr::null_mut() {
        Err(io::Error::last_os_error())
    } else {
        Ok((result as *const u8, length as usize, platform_data))
    }
}

pub fn unmap_file(buffer: *const u8, _length: usize) {
    let success = unsafe {
        winapi::um::memoryapi::UnmapViewOfFile(buffer as *mut winapi::ctypes::c_void)
    };
    assert!(success != 0);
}

/// See also `unix::get_resident`.
pub fn get_resident(_buffer: *const u8, _length: usize, residency: &mut [bool]) {
    // As far as I am aware, Windows does not expose a way to query whether pages are resident.
    // There is no equivalent of `mincore()`. The closest thing is `VirtualQuery()`, but the
    // `state` value in the `MEMORY_BASIC_INFORMATION` struct that it fills does not indicate
    // whether the page is resident.

    // Lie and pretend everything is resident.
    for x in residency {
        *x = true;
    }
}

/// See also `unix::prefetch`.
pub fn prefetch(buffer: *const u8, length: usize) {
    let mut entry = winapi::um::memoryapi::WIN32_MEMORY_RANGE_ENTRY {
        VirtualAddress: buffer as *mut winapi::ctypes::c_void,
        NumberOfBytes: length as winapi::shared::basetsd::SIZE_T,
    };

    unsafe {
        let current_process_handle = winapi::um::processthreadsapi::GetCurrentProcess();
        winapi::um::memoryapi::PrefetchVirtualMemory(
            current_process_handle, // Prefetch for the current process.
            1, &mut entry,          // An array of length 1 that contains `entry`.
            0                       // Reserved flag that must be 0.
        );
    }

    // The return value of `PrefetchVirtualMemory` is ignored. MSDN says the function may fail if
    // the system is under memory pressure. (It is not entirely clear whether "fail" means
    // "returns a nonzero value", but I assume it does.)
}

pub fn get_page_size() -> usize {
    // Fill the `SYSTEM_INFO` struct with zeroes. It will be filled by
    // `GetSystemInfo` later but Rust requires it to be initialized.
    let mut sysinfo: winapi::um::sysinfoapi::SYSTEM_INFO = unsafe { mem::zeroed() };
    unsafe { winapi::um::sysinfoapi::GetSystemInfo(&mut sysinfo); }
    sysinfo.dwPageSize as usize
}
<|endoftext|>


<commit_before>use jsonpath_lib::select;
use lazy_static::lazy_static;
use regex::{Regex, RegexBuilder};
use serde_json::Value;
use std::{env, fmt, fs};

mod cache;
mod config;
mod error;

use cache::Cache;
use config::parse_config;
use error::CkError;

fn main() -> Result<(), String> {
    let config = parse_config(env::args().collect());

    let mut failed = Vec::new();
    let mut cache = Cache::new(&config.doc_dir);
    let commands = get_commands(&config.template)
        .map_err(|_| format!("Jsondocck failed for {}", &config.template))?;

    for command in commands {
        if let Err(e) = check_command(command, &mut cache) {
            failed.push(e);
        }
    }

    if failed.is_empty() {
        Ok(())
    } else {
        for i in failed {
            eprintln!("{}", i);
        }
        Err(format!("Jsondocck failed for {}", &config.template))
    }
}

#[derive(Debug)]
pub struct Command {
    negated: bool,
    kind: CommandKind,
    args: Vec<String>,
    lineno: usize,
}

#[derive(Debug)]
pub enum CommandKind {
    Has,
    Count,
}

impl CommandKind {
    fn validate(&self, args: &[String], command_num: usize, lineno: usize) -> bool {
        let count = match self {
            CommandKind::Has => (1..=3).contains(&args.len()),
            CommandKind::Count => 3 == args.len(),
        };

        if !count {
            print_err(&format!("Incorrect number of arguments to `@{}`", self), lineno);
            return false;
        }

        if args[0] == "-" && command_num == 0 {
            print_err(&format!("Tried to use the previous path in the first command"), lineno);
            return false;
        }

        if let CommandKind::Count = self {
            if args[2].parse::<usize>().is_err() {
                print_err(&format!("Third argument to @count must be a valid usize"), lineno);
                return false;
            }
        }

        true
    }
}

impl fmt::Display for CommandKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let text = match self {
            CommandKind::Has => "has",
            CommandKind::Count => "count",
        };
        write!(f, "{}", text)
    }
}

lazy_static! {
    static ref LINE_PATTERN: Regex = RegexBuilder::new(
        r#"
        \s(?P<invalid>!?)@(?P<negated>!?)
        (?P<cmd>[A-Za-z]+(?:-[A-Za-z]+)*)
        (?P<args>.*)$
    "#
    )
    .ignore_whitespace(true)
    .unicode(true)
    .build()
    .unwrap();
}

fn print_err(msg: &str, lineno: usize) {
    eprintln!("Invalid command: {} on line {}", msg, lineno)
}

/// Get a list of commands from a file. Does the work of ensuring the commands
/// are syntactically valid.
fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
    let mut commands = Vec::new();
    let mut errors = false;
    let file = fs::read_to_string(template).unwrap();

    for (lineno, line) in file.split('\n').enumerate() {
        let lineno = lineno + 1;

        let cap = match LINE_PATTERN.captures(line) {
            Some(c) => c,
            None => continue,
        };

        let negated = cap.name("negated").unwrap().as_str() == "!";
        let cmd = cap.name("cmd").unwrap().as_str();

        let cmd = match cmd {
            "has" => CommandKind::Has,
            "count" => CommandKind::Count,
            _ => {
                print_err(&format!("Unrecognized command name `@{}`", cmd), lineno);
                errors = true;
                continue;
            }
        };

        if let Some(m) = cap.name("invalid") {
            if m.as_str() == "!" {
                print_err(
                    &format!(
                        "`!@{0}{1}`, (help: try with `@!{1}`)",
                        if negated { "!" } else { "" },
                        cmd,
                    ),
                    lineno,
                );
                errors = true;
                continue;
            }
        }

        let args = cap.name("args").map_or(vec![], |m| shlex::split(m.as_str()).unwrap());

        if !cmd.validate(&args, commands.len(), lineno) {
            errors = true;
            continue;
        }

        commands.push(Command { negated, kind: cmd, args, lineno })
    }

    if !errors { Ok(commands) } else { Err(()) }
}

/// Performs the actual work of ensuring a command passes. Generally assumes the command
/// is syntactically valid.
fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
    let result = match command.kind {
        CommandKind::Has => {
            match command.args.len() {
                // @has <path> = file existence
                1 => cache.get_file(&command.args[0]).is_ok(),
                // @has <path> <jsonpath> = check path exists
                2 => {
                    let val = cache.get_value(&command.args[0])?;

                    match select(&val, &command.args[1]) {
                        Ok(results) => !results.is_empty(),
                        Err(_) => false,
                    }
                }
                // @has <path> <jsonpath> <value> = check *any* item matched by path equals value
                3 => {
                    let val = cache.get_value(&command.args[0])?;
                    match select(&val, &command.args[1]) {
                        Ok(results) => {
                            let pat: Value = serde_json::from_str(&command.args[2]).unwrap();

                            !results.is_empty() && results.into_iter().any(|val| *val == pat)
                        }
                        Err(_) => false,
                    }
                }
                _ => unreachable!(),
            }
        }
        CommandKind::Count => {
            // @count <path> <jsonpath> <count> = Check that the jsonpath matches exactly [count] times
            assert_eq!(command.args.len(), 3);
            let expected: usize = command.args[2].parse().unwrap();

            let val = cache.get_value(&command.args[0])?;
            match select(&val, &command.args[1]) {
                Ok(results) => results.len() == expected,
                Err(_) => false,
            }
        }
    };

    if result == command.negated {
        if command.negated {
            Err(CkError::FailedCheck(
                format!(
                    "`@!{} {}` matched when it shouldn't",
                    command.kind,
                    command.args.join(" ")
                ),
                command,
            ))
        } else {
            // FIXME: In the future, try 'peeling back' each step, and see at what level the match failed
            Err(CkError::FailedCheck(
                format!(
                    "`@{} {}` didn't match when it should",
                    command.kind,
                    command.args.join(" ")
                ),
                command,
            ))
        }
    } else {
        Ok(())
    }
}
<commit_msg>Rollup merge of #81320 - CraftSpider:jsondoc-errors, r=jyn514<commit_after>use jsonpath_lib::select;
use lazy_static::lazy_static;
use regex::{Regex, RegexBuilder};
use serde_json::Value;
use std::{env, fmt, fs};

mod cache;
mod config;
mod error;

use cache::Cache;
use config::parse_config;
use error::CkError;

fn main() -> Result<(), String> {
    let config = parse_config(env::args().collect());

    let mut failed = Vec::new();
    let mut cache = Cache::new(&config.doc_dir);
    let commands = get_commands(&config.template)
        .map_err(|_| format!("Jsondocck failed for {}", &config.template))?;

    for command in commands {
        if let Err(e) = check_command(command, &mut cache) {
            failed.push(e);
        }
    }

    if failed.is_empty() {
        Ok(())
    } else {
        for i in failed {
            eprintln!("{}", i);
        }
        Err(format!("Jsondocck failed for {}", &config.template))
    }
}

#[derive(Debug)]
pub struct Command {
    negated: bool,
    kind: CommandKind,
    args: Vec<String>,
    lineno: usize,
}

#[derive(Debug)]
pub enum CommandKind {
    Has,
    Count,
}

impl CommandKind {
    fn validate(&self, args: &[String], command_num: usize, lineno: usize) -> bool {
        let count = match self {
            CommandKind::Has => (1..=3).contains(&args.len()),
            CommandKind::Count => 3 == args.len(),
        };

        if !count {
            print_err(&format!("Incorrect number of arguments to `@{}`", self), lineno);
            return false;
        }

        if args[0] == "-" && command_num == 0 {
            print_err(&format!("Tried to use the previous path in the first command"), lineno);
            return false;
        }

        if let CommandKind::Count = self {
            if args[2].parse::<usize>().is_err() {
                print_err(&format!("Third argument to @count must be a valid usize"), lineno);
                return false;
            }
        }

        true
    }
}

impl fmt::Display for CommandKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let text = match self {
            CommandKind::Has => "has",
            CommandKind::Count => "count",
        };
        write!(f, "{}", text)
    }
}

lazy_static! {
    static ref LINE_PATTERN: Regex = RegexBuilder::new(
        r#"
        \s(?P<invalid>!?)@(?P<negated>!?)
        (?P<cmd>[A-Za-z]+(?:-[A-Za-z]+)*)
        (?P<args>.*)$
    "#
    )
    .ignore_whitespace(true)
    .unicode(true)
    .build()
    .unwrap();
}

fn print_err(msg: &str, lineno: usize) {
    eprintln!("Invalid command: {} on line {}", msg, lineno)
}

/// Get a list of commands from a file. Does the work of ensuring the commands
/// are syntactically valid.
fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
    let mut commands = Vec::new();
    let mut errors = false;
    let file = fs::read_to_string(template).unwrap();

    for (lineno, line) in file.split('\n').enumerate() {
        let lineno = lineno + 1;

        let cap = match LINE_PATTERN.captures(line) {
            Some(c) => c,
            None => continue,
        };

        let negated = cap.name("negated").unwrap().as_str() == "!";
        let cmd = cap.name("cmd").unwrap().as_str();

        let cmd = match cmd {
            "has" => CommandKind::Has,
            "count" => CommandKind::Count,
            _ => {
                print_err(&format!("Unrecognized command name `@{}`", cmd), lineno);
                errors = true;
                continue;
            }
        };

        if let Some(m) = cap.name("invalid") {
            if m.as_str() == "!" {
                print_err(
                    &format!(
                        "`!@{0}{1}`, (help: try with `@!{1}`)",
                        if negated { "!" } else { "" },
                        cmd,
                    ),
                    lineno,
                );
                errors = true;
                continue;
            }
        }

        let args = cap.name("args").map_or(Some(vec![]), |m| shlex::split(m.as_str()));

        let args = match args {
            Some(args) => args,
            None => {
                print_err(
                    &format!(
                        "Invalid arguments to shlex::split: `{}`",
                        cap.name("args").unwrap().as_str()
                    ),
                    lineno,
                );
                errors = true;
                continue;
            }
        };

        if !cmd.validate(&args, commands.len(), lineno) {
            errors = true;
            continue;
        }

        commands.push(Command { negated, kind: cmd, args, lineno })
    }

    if !errors { Ok(commands) } else { Err(()) }
}

/// Performs the actual work of ensuring a command passes. Generally assumes the command
/// is syntactically valid.
fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
    let result = match command.kind {
        CommandKind::Has => {
            match command.args.len() {
                // @has <path> = file existence
                1 => cache.get_file(&command.args[0]).is_ok(),
                // @has <path> <jsonpath> = check path exists
                2 => {
                    let val = cache.get_value(&command.args[0])?;

                    match select(&val, &command.args[1]) {
                        Ok(results) => !results.is_empty(),
                        Err(_) => false,
                    }
                }
                // @has <path> <jsonpath> <value> = check *any* item matched by path equals value
                3 => {
                    let val = cache.get_value(&command.args[0])?;
                    match select(&val, &command.args[1]) {
                        Ok(results) => {
                            let pat: Value = serde_json::from_str(&command.args[2]).unwrap();

                            !results.is_empty() && results.into_iter().any(|val| *val == pat)
                        }
                        Err(_) => false,
                    }
                }
                _ => unreachable!(),
            }
        }
        CommandKind::Count => {
            // @count <path> <jsonpath> <count> = Check that the jsonpath matches exactly [count] times
            assert_eq!(command.args.len(), 3);
            let expected: usize = command.args[2].parse().unwrap();

            let val = cache.get_value(&command.args[0])?;
            match select(&val, &command.args[1]) {
                Ok(results) => results.len() == expected,
                Err(_) => false,
            }
        }
    };

    if result == command.negated {
        if command.negated {
            Err(CkError::FailedCheck(
                format!(
                    "`@!{} {}` matched when it shouldn't",
                    command.kind,
                    command.args.join(" ")
                ),
                command,
            ))
        } else {
            // FIXME: In the future, try 'peeling back' each step, and see at what level the match failed
            Err(CkError::FailedCheck(
                format!(
                    "`@{} {}` didn't match when it should",
                    command.kind,
                    command.args.join(" ")
                ),
                command,
            ))
        }
    } else {
        Ok(())
    }
}
<|endoftext|>


<commit_before>//! ESMTP features

use crate::transport::smtp::{
    authentication::Mechanism, error::Error, response::Response, util::XText,
};
use std::{
    collections::HashSet,
    fmt::{self, Display, Formatter},
    net::{Ipv4Addr, Ipv6Addr},
    result::Result,
};

/// Default client id
const DEFAULT_DOMAIN_CLIENT_ID: &str = "localhost";

/// Client identifier, the parameter to `EHLO`
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ClientId {
    /// A fully-qualified domain name
    Domain(String),
    /// An IPv4 address
    Ipv4(Ipv4Addr),
    /// An IPv6 address
    Ipv6(Ipv6Addr),
}

impl Display for ClientId {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            ClientId::Domain(ref value) => f.write_str(value),
            ClientId::Ipv4(ref value) => write!(f, "{}", value),
            ClientId::Ipv6(ref value) => write!(f, "{}", value),
        }
    }
}

impl ClientId {
    /// Creates a new `ClientId` from a fully qualified domain name
    pub fn new(domain: String) -> ClientId {
        ClientId::Domain(domain)
    }

    /// Defines a `ClientId` with the current hostname, of `localhost` if hostname could not be
    /// found
    #[cfg(feature = "hostname")]
    pub fn hostname() -> ClientId {
        ClientId::Domain(
            hostname::get()
                .map_err(|_| ())
                .and_then(|s| s.into_string().map_err(|_| ()))
                .unwrap_or_else(|_| DEFAULT_DOMAIN_CLIENT_ID.to_string()),
        )
    }
    #[cfg(not(feature = "hostname"))]
    pub fn hostname() -> ClientId {
        ClientId::Domain(DEFAULT_DOMAIN_CLIENT_ID.to_string())
    }
}

/// Supported ESMTP keywords
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Extension {
    /// 8BITMIME keyword
    ///
    /// RFC 6152: https://tools.ietf.org/html/rfc6152
    EightBitMime,
    /// SMTPUTF8 keyword
    ///
    /// RFC 6531: https://tools.ietf.org/html/rfc6531
    SmtpUtfEight,
    /// STARTTLS keyword
    ///
    /// RFC 2487: https://tools.ietf.org/html/rfc2487
    StartTls,
    /// AUTH mechanism
    Authentication(Mechanism),
}

impl Display for Extension {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            Extension::EightBitMime => f.write_str("8BITMIME"),
            Extension::SmtpUtfEight => f.write_str("SMTPUTF8"),
            Extension::StartTls => f.write_str("STARTTLS"),
            Extension::Authentication(ref mechanism) => write!(f, "AUTH {}", mechanism),
        }
    }
}

/// Contains information about an SMTP server
#[derive(Clone, Debug, Eq, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ServerInfo {
    /// Server name
    ///
    /// The name given in the server banner
    pub name: String,
    /// ESMTP features supported by the server
    ///
    /// It contains the features supported by the server and known by the `Extension` module.
    pub features: HashSet<Extension>,
}

impl Display for ServerInfo {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let features = if self.features.is_empty() {
            "no supported features".to_string()
        } else {
            format!("{:?}", self.features)
        };
        write!(f, "{} with {}", self.name, features)
    }
}

impl ServerInfo {
    /// Parses a EHLO response to create a `ServerInfo`
    pub fn from_response(response: &Response) -> Result<ServerInfo, Error> {
        let name = match response.first_word() {
            Some(name) => name,
            None => return Err(Error::ResponseParsing("Could not read server name")),
        };

        let mut features: HashSet<Extension> = HashSet::new();

        for line in response.message.as_slice() {
            if line.is_empty() {
                continue;
            }

            let mut split = line.split_whitespace();
            match split.next().unwrap() {
                "8BITMIME" => {
                    features.insert(Extension::EightBitMime);
                }
                "SMTPUTF8" => {
                    features.insert(Extension::SmtpUtfEight);
                }
                "STARTTLS" => {
                    features.insert(Extension::StartTls);
                }
                "AUTH" => {
                    for mechanism in split {
                        match mechanism {
                            "PLAIN" => {
                                features.insert(Extension::Authentication(Mechanism::Plain));
                            }
                            "LOGIN" => {
                                features.insert(Extension::Authentication(Mechanism::Login));
                            }
                            "XOAUTH2" => {
                                features.insert(Extension::Authentication(Mechanism::Xoauth2));
                            }
                            _ => (),
                        }
                    }
                }
                _ => (),
            };
        }

        Ok(ServerInfo {
            name: name.to_string(),
            features,
        })
    }

    /// Checks if the server supports an ESMTP feature
    pub fn supports_feature(&self, keyword: Extension) -> bool {
        self.features.contains(&keyword)
    }

    /// Checks if the server supports an ESMTP feature
    pub fn supports_auth_mechanism(&self, mechanism: Mechanism) -> bool {
        self.features
            .contains(&Extension::Authentication(mechanism))
    }

    /// Gets a compatible mechanism from list
    pub fn get_auth_mechanism(&self, mechanisms: &[Mechanism]) -> Option<Mechanism> {
        for mechanism in mechanisms {
            if self.supports_auth_mechanism(*mechanism) {
                return Some(*mechanism);
            }
        }
        None
    }
}

/// A `MAIL FROM` extension parameter
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum MailParameter {
    /// `BODY` parameter
    Body(MailBodyParameter),
    /// `SIZE` parameter
    Size(usize),
    /// `SMTPUTF8` parameter
    SmtpUtfEight,
    /// Custom parameter
    Other {
        /// Parameter keyword
        keyword: String,
        /// Parameter value
        value: Option<String>,
    },
}

impl Display for MailParameter {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            MailParameter::Body(ref value) => write!(f, "BODY={}", value),
            MailParameter::Size(size) => write!(f, "SIZE={}", size),
            MailParameter::SmtpUtfEight => f.write_str("SMTPUTF8"),
            MailParameter::Other {
                ref keyword,
                value: Some(ref value),
            } => write!(f, "{}={}", keyword, XText(value)),
            MailParameter::Other {
                ref keyword,
                value: None,
            } => f.write_str(keyword),
        }
    }
}

/// Values for the `BODY` parameter to `MAIL FROM`
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum MailBodyParameter {
    /// `7BIT`
    SevenBit,
    /// `8BITMIME`
    EightBitMime,
}

impl Display for MailBodyParameter {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            MailBodyParameter::SevenBit => f.write_str("7BIT"),
            MailBodyParameter::EightBitMime => f.write_str("8BITMIME"),
        }
    }
}

/// A `RCPT TO` extension parameter
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum RcptParameter {
    /// Custom parameter
    Other {
        /// Parameter keyword
        keyword: String,
        /// Parameter value
        value: Option<String>,
    },
}

impl Display for RcptParameter {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            RcptParameter::Other {
                ref keyword,
                value: Some(ref value),
            } => write!(f, "{}={}", keyword, XText(value)),
            RcptParameter::Other {
                ref keyword,
                value: None,
            } => f.write_str(keyword),
        }
    }
}

#[cfg(test)]
mod test {

    use super::{ClientId, Extension, ServerInfo};
    use crate::transport::smtp::{
        authentication::Mechanism,
        response::{Category, Code, Detail, Response, Severity},
    };
    use std::collections::HashSet;

    #[test]
    fn test_clientid_fmt() {
        assert_eq!(
            format!("{}", ClientId::new("test".to_string())),
            "test".to_string()
        );
    }

    #[test]
    fn test_extension_fmt() {
        assert_eq!(
            format!("{}", Extension::EightBitMime),
            "8BITMIME".to_string()
        );
        assert_eq!(
            format!("{}", Extension::Authentication(Mechanism::Plain)),
            "AUTH PLAIN".to_string()
        );
    }

    #[test]
    fn test_serverinfo_fmt() {
        let mut eightbitmime = HashSet::new();
        assert!(eightbitmime.insert(Extension::EightBitMime));

        assert_eq!(
            format!(
                "{}",
                ServerInfo {
                    name: "name".to_string(),
                    features: eightbitmime.clone(),
                }
            ),
            "name with {EightBitMime}".to_string()
        );

        let empty = HashSet::new();

        assert_eq!(
            format!(
                "{}",
                ServerInfo {
                    name: "name".to_string(),
                    features: empty,
                }
            ),
            "name with no supported features".to_string()
        );

        let mut plain = HashSet::new();
        assert!(plain.insert(Extension::Authentication(Mechanism::Plain)));

        assert_eq!(
            format!(
                "{}",
                ServerInfo {
                    name: "name".to_string(),
                    features: plain.clone(),
                }
            ),
            "name with {Authentication(Plain)}".to_string()
        );
    }

    #[test]
    fn test_serverinfo() {
        let response = Response::new(
            Code::new(
                Severity::PositiveCompletion,
                Category::Unspecified4,
                Detail::One,
            ),
            vec![
                "me".to_string(),
                "8BITMIME".to_string(),
                "SIZE 42".to_string(),
            ],
        );

        let mut features = HashSet::new();
        assert!(features.insert(Extension::EightBitMime));

        let server_info = ServerInfo {
            name: "me".to_string(),
            features,
        };

        assert_eq!(ServerInfo::from_response(&response).unwrap(), server_info);

        assert!(server_info.supports_feature(Extension::EightBitMime));
        assert!(!server_info.supports_feature(Extension::StartTls));

        let response2 = Response::new(
            Code::new(
                Severity::PositiveCompletion,
                Category::Unspecified4,
                Detail::One,
            ),
            vec![
                "me".to_string(),
                "AUTH PLAIN CRAM-MD5 XOAUTH2 OTHER".to_string(),
                "8BITMIME".to_string(),
                "SIZE 42".to_string(),
            ],
        );

        let mut features2 = HashSet::new();
        assert!(features2.insert(Extension::EightBitMime));
        assert!(features2.insert(Extension::Authentication(Mechanism::Plain),));
        assert!(features2.insert(Extension::Authentication(Mechanism::Xoauth2),));

        let server_info2 = ServerInfo {
            name: "me".to_string(),
            features: features2,
        };

        assert_eq!(ServerInfo::from_response(&response2).unwrap(), server_info2);

        assert!(server_info2.supports_feature(Extension::EightBitMime));
        assert!(server_info2.supports_auth_mechanism(Mechanism::Plain));
        assert!(!server_info2.supports_feature(Extension::StartTls));
    }
}
<commit_msg>chore: simplify ClientId::hostname<commit_after>//! ESMTP features

use crate::transport::smtp::{
    authentication::Mechanism, error::Error, response::Response, util::XText,
};
use std::{
    collections::HashSet,
    fmt::{self, Display, Formatter},
    net::{Ipv4Addr, Ipv6Addr},
    result::Result,
};

/// Default client id
const DEFAULT_DOMAIN_CLIENT_ID: &str = "localhost";

/// Client identifier, the parameter to `EHLO`
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ClientId {
    /// A fully-qualified domain name
    Domain(String),
    /// An IPv4 address
    Ipv4(Ipv4Addr),
    /// An IPv6 address
    Ipv6(Ipv6Addr),
}

impl Display for ClientId {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            ClientId::Domain(ref value) => f.write_str(value),
            ClientId::Ipv4(ref value) => write!(f, "{}", value),
            ClientId::Ipv6(ref value) => write!(f, "{}", value),
        }
    }
}

impl ClientId {
    /// Creates a new `ClientId` from a fully qualified domain name
    pub fn new(domain: String) -> ClientId {
        ClientId::Domain(domain)
    }

    /// Defines a `ClientId` with the current hostname, of `localhost` if hostname could not be
    /// found
    pub fn hostname() -> ClientId {
        #[cfg(feature = "hostname")]
        return ClientId::Domain(
            hostname::get()
                .ok()
                .and_then(|s| s.into_string().ok())
                .unwrap_or_else(|| DEFAULT_DOMAIN_CLIENT_ID.to_string()),
        );

        #[cfg(not(feature = "hostname"))]
        return ClientId::Domain(DEFAULT_DOMAIN_CLIENT_ID.to_string());
    }
}

/// Supported ESMTP keywords
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Extension {
    /// 8BITMIME keyword
    ///
    /// RFC 6152: https://tools.ietf.org/html/rfc6152
    EightBitMime,
    /// SMTPUTF8 keyword
    ///
    /// RFC 6531: https://tools.ietf.org/html/rfc6531
    SmtpUtfEight,
    /// STARTTLS keyword
    ///
    /// RFC 2487: https://tools.ietf.org/html/rfc2487
    StartTls,
    /// AUTH mechanism
    Authentication(Mechanism),
}

impl Display for Extension {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            Extension::EightBitMime => f.write_str("8BITMIME"),
            Extension::SmtpUtfEight => f.write_str("SMTPUTF8"),
            Extension::StartTls => f.write_str("STARTTLS"),
            Extension::Authentication(ref mechanism) => write!(f, "AUTH {}", mechanism),
        }
    }
}

/// Contains information about an SMTP server
#[derive(Clone, Debug, Eq, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ServerInfo {
    /// Server name
    ///
    /// The name given in the server banner
    pub name: String,
    /// ESMTP features supported by the server
    ///
    /// It contains the features supported by the server and known by the `Extension` module.
    pub features: HashSet<Extension>,
}

impl Display for ServerInfo {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let features = if self.features.is_empty() {
            "no supported features".to_string()
        } else {
            format!("{:?}", self.features)
        };
        write!(f, "{} with {}", self.name, features)
    }
}

impl ServerInfo {
    /// Parses a EHLO response to create a `ServerInfo`
    pub fn from_response(response: &Response) -> Result<ServerInfo, Error> {
        let name = match response.first_word() {
            Some(name) => name,
            None => return Err(Error::ResponseParsing("Could not read server name")),
        };

        let mut features: HashSet<Extension> = HashSet::new();

        for line in response.message.as_slice() {
            if line.is_empty() {
                continue;
            }

            let mut split = line.split_whitespace();
            match split.next().unwrap() {
                "8BITMIME" => {
                    features.insert(Extension::EightBitMime);
                }
                "SMTPUTF8" => {
                    features.insert(Extension::SmtpUtfEight);
                }
                "STARTTLS" => {
                    features.insert(Extension::StartTls);
                }
                "AUTH" => {
                    for mechanism in split {
                        match mechanism {
                            "PLAIN" => {
                                features.insert(Extension::Authentication(Mechanism::Plain));
                            }
                            "LOGIN" => {
                                features.insert(Extension::Authentication(Mechanism::Login));
                            }
                            "XOAUTH2" => {
                                features.insert(Extension::Authentication(Mechanism::Xoauth2));
                            }
                            _ => (),
                        }
                    }
                }
                _ => (),
            };
        }

        Ok(ServerInfo {
            name: name.to_string(),
            features,
        })
    }

    /// Checks if the server supports an ESMTP feature
    pub fn supports_feature(&self, keyword: Extension) -> bool {
        self.features.contains(&keyword)
    }

    /// Checks if the server supports an ESMTP feature
    pub fn supports_auth_mechanism(&self, mechanism: Mechanism) -> bool {
        self.features
            .contains(&Extension::Authentication(mechanism))
    }

    /// Gets a compatible mechanism from list
    pub fn get_auth_mechanism(&self, mechanisms: &[Mechanism]) -> Option<Mechanism> {
        for mechanism in mechanisms {
            if self.supports_auth_mechanism(*mechanism) {
                return Some(*mechanism);
            }
        }
        None
    }
}

/// A `MAIL FROM` extension parameter
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum MailParameter {
    /// `BODY` parameter
    Body(MailBodyParameter),
    /// `SIZE` parameter
    Size(usize),
    /// `SMTPUTF8` parameter
    SmtpUtfEight,
    /// Custom parameter
    Other {
        /// Parameter keyword
        keyword: String,
        /// Parameter value
        value: Option<String>,
    },
}

impl Display for MailParameter {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            MailParameter::Body(ref value) => write!(f, "BODY={}", value),
            MailParameter::Size(size) => write!(f, "SIZE={}", size),
            MailParameter::SmtpUtfEight => f.write_str("SMTPUTF8"),
            MailParameter::Other {
                ref keyword,
                value: Some(ref value),
            } => write!(f, "{}={}", keyword, XText(value)),
            MailParameter::Other {
                ref keyword,
                value: None,
            } => f.write_str(keyword),
        }
    }
}

/// Values for the `BODY` parameter to `MAIL FROM`
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum MailBodyParameter {
    /// `7BIT`
    SevenBit,
    /// `8BITMIME`
    EightBitMime,
}

impl Display for MailBodyParameter {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            MailBodyParameter::SevenBit => f.write_str("7BIT"),
            MailBodyParameter::EightBitMime => f.write_str("8BITMIME"),
        }
    }
}

/// A `RCPT TO` extension parameter
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum RcptParameter {
    /// Custom parameter
    Other {
        /// Parameter keyword
        keyword: String,
        /// Parameter value
        value: Option<String>,
    },
}

impl Display for RcptParameter {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            RcptParameter::Other {
                ref keyword,
                value: Some(ref value),
            } => write!(f, "{}={}", keyword, XText(value)),
            RcptParameter::Other {
                ref keyword,
                value: None,
            } => f.write_str(keyword),
        }
    }
}

#[cfg(test)]
mod test {

    use super::{ClientId, Extension, ServerInfo};
    use crate::transport::smtp::{
        authentication::Mechanism,
        response::{Category, Code, Detail, Response, Severity},
    };
    use std::collections::HashSet;

    #[test]
    fn test_clientid_fmt() {
        assert_eq!(
            format!("{}", ClientId::new("test".to_string())),
            "test".to_string()
        );
    }

    #[test]
    fn test_extension_fmt() {
        assert_eq!(
            format!("{}", Extension::EightBitMime),
            "8BITMIME".to_string()
        );
        assert_eq!(
            format!("{}", Extension::Authentication(Mechanism::Plain)),
            "AUTH PLAIN".to_string()
        );
    }

    #[test]
    fn test_serverinfo_fmt() {
        let mut eightbitmime = HashSet::new();
        assert!(eightbitmime.insert(Extension::EightBitMime));

        assert_eq!(
            format!(
                "{}",
                ServerInfo {
                    name: "name".to_string(),
                    features: eightbitmime.clone(),
                }
            ),
            "name with {EightBitMime}".to_string()
        );

        let empty = HashSet::new();

        assert_eq!(
            format!(
                "{}",
                ServerInfo {
                    name: "name".to_string(),
                    features: empty,
                }
            ),
            "name with no supported features".to_string()
        );

        let mut plain = HashSet::new();
        assert!(plain.insert(Extension::Authentication(Mechanism::Plain)));

        assert_eq!(
            format!(
                "{}",
                ServerInfo {
                    name: "name".to_string(),
                    features: plain.clone(),
                }
            ),
            "name with {Authentication(Plain)}".to_string()
        );
    }

    #[test]
    fn test_serverinfo() {
        let response = Response::new(
            Code::new(
                Severity::PositiveCompletion,
                Category::Unspecified4,
                Detail::One,
            ),
            vec![
                "me".to_string(),
                "8BITMIME".to_string(),
                "SIZE 42".to_string(),
            ],
        );

        let mut features = HashSet::new();
        assert!(features.insert(Extension::EightBitMime));

        let server_info = ServerInfo {
            name: "me".to_string(),
            features,
        };

        assert_eq!(ServerInfo::from_response(&response).unwrap(), server_info);

        assert!(server_info.supports_feature(Extension::EightBitMime));
        assert!(!server_info.supports_feature(Extension::StartTls));

        let response2 = Response::new(
            Code::new(
                Severity::PositiveCompletion,
                Category::Unspecified4,
                Detail::One,
            ),
            vec![
                "me".to_string(),
                "AUTH PLAIN CRAM-MD5 XOAUTH2 OTHER".to_string(),
                "8BITMIME".to_string(),
                "SIZE 42".to_string(),
            ],
        );

        let mut features2 = HashSet::new();
        assert!(features2.insert(Extension::EightBitMime));
        assert!(features2.insert(Extension::Authentication(Mechanism::Plain),));
        assert!(features2.insert(Extension::Authentication(Mechanism::Xoauth2),));

        let server_info2 = ServerInfo {
            name: "me".to_string(),
            features: features2,
        };

        assert_eq!(ServerInfo::from_response(&response2).unwrap(), server_info2);

        assert!(server_info2.supports_feature(Extension::EightBitMime));
        assert!(server_info2.supports_auth_mechanism(Mechanism::Plain));
        assert!(!server_info2.supports_feature(Extension::StartTls));
    }
}
<|endoftext|>


<commit_before>use value::AMQPValue;

use std::collections::BTreeMap;
use std::fmt;

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum AMQPType {
    Boolean,
    ShortShortInt,
    ShortShortUInt,
    ShortInt,
    ShortUInt,
    LongInt,
    LongUInt,
    LongLongInt,
    LongLongUInt,
    Float,
    Double,
    DecimalValue,
    LongString,
    FieldArray,
    Timestamp,
    FieldTable,
    Void,
    // TODO: byte array (x)
}

impl AMQPType {
    pub fn from_id(id: char) -> Option<AMQPType> {
        match id {
            't' => Some(AMQPType::Boolean),
            'b' => Some(AMQPType::ShortShortInt),
            'B' => Some(AMQPType::ShortShortUInt),
            's' => Some(AMQPType::ShortInt),
            'u' => Some(AMQPType::ShortUInt),
            'I' => Some(AMQPType::LongInt),
            'i' => Some(AMQPType::LongUInt),
            /* RabbitMQ treats both 'l' and 'L' as LongLongInt and ignores LongLongUInt */
            'L' => Some(AMQPType::LongLongInt),
            'l' => Some(AMQPType::LongLongInt),
            'f' => Some(AMQPType::Float),
            'd' => Some(AMQPType::Double),
            'D' => Some(AMQPType::DecimalValue),
            'S' => Some(AMQPType::LongString),
            'A' => Some(AMQPType::FieldArray),
            'T' => Some(AMQPType::Timestamp),
            'F' => Some(AMQPType::FieldTable),
            'V' => Some(AMQPType::Void),
            _   => None,
        }
    }

    pub fn get_id(&self) -> char {
        match *self {
            AMQPType::Boolean        => 't',
            AMQPType::ShortShortInt  => 'b',
            AMQPType::ShortShortUInt => 'B',
            AMQPType::ShortInt       => 's',
            AMQPType::ShortUInt      => 'u',
            AMQPType::LongInt        => 'I',
            AMQPType::LongUInt       => 'i',
            /* RabbitMQ treats both 'l' and 'L' as LongLongInt and ignores LongLongUInt */
            AMQPType::LongLongInt    => 'l',
            AMQPType::LongLongUInt   => 'l',
            AMQPType::Float          => 'f',
            AMQPType::Double         => 'd',
            AMQPType::DecimalValue   => 'D',
            AMQPType::LongString     => 'S',
            AMQPType::FieldArray     => 'A',
            AMQPType::Timestamp      => 'T',
            AMQPType::FieldTable     => 'F',
            AMQPType::Void           => 'V',
        }
    }

    pub fn to_string(&self) -> String {
        format!("{}", self)
    }
}

impl fmt::Display for AMQPType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

pub type Boolean        = bool;
pub type ShortShortInt  = i8;
pub type ShortShortUInt = u8;
pub type ShortInt       = i16;
pub type ShortUInt      = u16;
pub type LongInt        = i32;
pub type LongUInt       = u32;
pub type LongLongInt    = i64;
pub type LongLongUInt   = u64;
pub type Float          = f32;
pub type Double         = f64;
pub type LongString     = String;
pub type FieldArray     = Vec<AMQPValue>;
pub type Timestamp      = LongLongUInt;
pub type FieldTable     = BTreeMap<LongString, AMQPValue>;
pub type Void           = ();

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct DecimalValue {
    pub scale: ShortShortUInt,
    pub value: LongUInt,
}

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

    #[test]
    fn test_type_from_id() {
        assert_eq!(AMQPType::from_id('T'), Some(AMQPType::Timestamp));
        assert_eq!(AMQPType::from_id('S'), Some(AMQPType::LongString));
        assert_eq!(AMQPType::from_id('s'), Some(AMQPType::ShortInt));
        assert_eq!(AMQPType::from_id('z'), None);
    }

    #[test]
    fn test_type_to_string() {
        assert_eq!(AMQPType::Boolean.to_string(), "Boolean");
        assert_eq!(AMQPType::Void.to_string(),    "Void");
    }
}
<commit_msg>types: still handle 'U' for types<commit_after>use value::AMQPValue;

use std::collections::BTreeMap;
use std::fmt;

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum AMQPType {
    Boolean,
    ShortShortInt,
    ShortShortUInt,
    ShortInt,
    ShortUInt,
    LongInt,
    LongUInt,
    LongLongInt,
    LongLongUInt,
    Float,
    Double,
    DecimalValue,
    LongString,
    FieldArray,
    Timestamp,
    FieldTable,
    Void,
    // TODO: byte array (x)
}

impl AMQPType {
    pub fn from_id(id: char) -> Option<AMQPType> {
        match id {
            't' => Some(AMQPType::Boolean),
            'b' => Some(AMQPType::ShortShortInt),
            'B' => Some(AMQPType::ShortShortUInt),
            /* Specs says 'U', RabbitMQ says 's' (which means ShortString in specs) */
            's' => Some(AMQPType::ShortInt),
            'U' => Some(AMQPType::ShortInt),
            'u' => Some(AMQPType::ShortUInt),
            'I' => Some(AMQPType::LongInt),
            'i' => Some(AMQPType::LongUInt),
            /* RabbitMQ treats both 'l' and 'L' as LongLongInt and ignores LongLongUInt */
            'L' => Some(AMQPType::LongLongInt),
            'l' => Some(AMQPType::LongLongInt),
            'f' => Some(AMQPType::Float),
            'd' => Some(AMQPType::Double),
            'D' => Some(AMQPType::DecimalValue),
            'S' => Some(AMQPType::LongString),
            'A' => Some(AMQPType::FieldArray),
            'T' => Some(AMQPType::Timestamp),
            'F' => Some(AMQPType::FieldTable),
            'V' => Some(AMQPType::Void),
            _   => None,
        }
    }

    pub fn get_id(&self) -> char {
        match *self {
            AMQPType::Boolean        => 't',
            AMQPType::ShortShortInt  => 'b',
            AMQPType::ShortShortUInt => 'B',
            /* Specs says 'U', RabbitMQ says 's' (which means ShortString in specs) */
            AMQPType::ShortInt       => 's',
            AMQPType::ShortUInt      => 'u',
            AMQPType::LongInt        => 'I',
            AMQPType::LongUInt       => 'i',
            /* RabbitMQ treats both 'l' and 'L' as LongLongInt and ignores LongLongUInt */
            AMQPType::LongLongInt    => 'l',
            AMQPType::LongLongUInt   => 'l',
            AMQPType::Float          => 'f',
            AMQPType::Double         => 'd',
            AMQPType::DecimalValue   => 'D',
            AMQPType::LongString     => 'S',
            AMQPType::FieldArray     => 'A',
            AMQPType::Timestamp      => 'T',
            AMQPType::FieldTable     => 'F',
            AMQPType::Void           => 'V',
        }
    }

    pub fn to_string(&self) -> String {
        format!("{}", self)
    }
}

impl fmt::Display for AMQPType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

pub type Boolean        = bool;
pub type ShortShortInt  = i8;
pub type ShortShortUInt = u8;
pub type ShortInt       = i16;
pub type ShortUInt      = u16;
pub type LongInt        = i32;
pub type LongUInt       = u32;
pub type LongLongInt    = i64;
pub type LongLongUInt   = u64;
pub type Float          = f32;
pub type Double         = f64;
pub type LongString     = String;
pub type FieldArray     = Vec<AMQPValue>;
pub type Timestamp      = LongLongUInt;
pub type FieldTable     = BTreeMap<LongString, AMQPValue>;
pub type Void           = ();

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct DecimalValue {
    pub scale: ShortShortUInt,
    pub value: LongUInt,
}

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

    #[test]
    fn test_type_from_id() {
        assert_eq!(AMQPType::from_id('T'), Some(AMQPType::Timestamp));
        assert_eq!(AMQPType::from_id('S'), Some(AMQPType::LongString));
        assert_eq!(AMQPType::from_id('s'), Some(AMQPType::ShortInt));
        assert_eq!(AMQPType::from_id('z'), None);
    }

    #[test]
    fn test_type_to_string() {
        assert_eq!(AMQPType::Boolean.to_string(), "Boolean");
        assert_eq!(AMQPType::Void.to_string(),    "Void");
    }
}
<|endoftext|>


<commit_before>//! A module for user processes
// So far it just contains some test code

use super::Process;
use super::ready_queue;

use super::super::vga::window::{Window, Color};

use super::super::data_structures::concurrency::{StaticSemaphore};

// useful constants
const ROWS: usize = 25;
const COLS: usize = 80;

// Some test semaphores
static mut current: (usize, usize) = (0,0);
static mut s1: StaticSemaphore = StaticSemaphore::new(1);
static mut s2: StaticSemaphore = StaticSemaphore::new(1);

// Some test routines
pub fn run(this: &Process) -> usize {
    let mut w0 = Window::new(COLS, ROWS, (0, 0));
    let mut msg = Window::new(60, 4, (1,1));

    unsafe { *(0xf00000 as *mut usize) = this.pid; }

    w0.set_bg(Color::LightBlue);
    w0.paint();

    msg.set_cursor((0,0));
    msg.set_bg(Color::LightGray);
    msg.set_fg(Color::Black);

    msg.put_str("<-- If semaphores work correctly, then only this block \
                should be red when all loop_procs finish running.\n\nYay :)");

    for _ in 0..206*3 {
        ready_queue::make_ready(Process::new("loop_proc", super::user::run2));
        unsafe { s1.down(); }

        // test vm
        if unsafe { *(0xf00000 as *mut usize) } != this.pid {
            panic!("Oh no! *0xf00000 should be {} but is {}",
                   this.pid, unsafe { *(0xf00000 as *mut usize) });
        }
    }

    0
}

fn get_prev((r,c): (usize, usize)) -> (usize, usize) {
    if r == 0 && c > 0 { // top
        (0, c-1)
    } else if c == COLS-1 { // right
        (r-1, c)
    } else if r == ROWS-1 { // bottom
        (r, c+1)
    } else { // left
        (r+1, c)
    }
}

fn get_next((r,c): (usize, usize)) -> (usize, usize) {
    if r == 0 && c < COLS-1 { // top
        (0, c+1)
    } else if c == COLS-1 && r < ROWS-1 { // right
        (r+1, c)
    } else if r == ROWS-1 && c > 0 { // bottom
        (r, c-1)
    } else { // left
        (r-1, c)
    }
}

fn run2(this: &Process) -> usize {

    unsafe { *(0xf00000 as *mut usize) = this.pid; }

    // test vm
    if unsafe { *(0xf00000 as *mut usize) } != this.pid {
        panic!("Oh no! *0xf00000 should be {} but is {}",
               this.pid, unsafe { *(0xf00000 as *mut usize) });
    }

    unsafe { s2.down(); }

    let mut w = Window::new(COLS,ROWS, (0,0));

    let me = unsafe { current };
    let prev = get_prev(me);

    unsafe {
        current = get_next(me);
    }

    printf!("Erase ({},{}) ", prev.0, prev.1);
    w.set_bg(Color::LightBlue);
    w.set_cursor(prev);
    w.put_char(' ');

    printf!("Draw ({},{})\n", me.0,me.1);
    w.set_bg(Color::Red);
    w.set_cursor(me);
    w.put_char(' ');

    unsafe { s2.up(); }

    // test vm
    if unsafe { *(0xf00000 as *mut usize) } != this.pid {
        panic!("Oh no! *0xf00000 should be {} but is {}",
               this.pid, unsafe { *(0xf00000 as *mut usize) });
    }

    unsafe { s1.up(); }

    0
}
<commit_msg>Minor change<commit_after>//! A module for user processes
// So far it just contains some test code

use super::Process;
use super::ready_queue;

use super::super::vga::window::{Window, Color};

use super::super::data_structures::concurrency::{StaticSemaphore};

// useful constants
const ROWS: usize = 25;
const COLS: usize = 80;

// Some test semaphores
static mut current: (usize, usize) = (0,0);
static mut s1: StaticSemaphore = StaticSemaphore::new(1);
static mut s2: StaticSemaphore = StaticSemaphore::new(1);

// Some test routines
pub fn run(this: &Process) -> usize {
    let mut w0 = Window::new(COLS, ROWS, (0, 0));
    let mut msg = Window::new(60, 4, (1,1));

    unsafe { *(0xf00000 as *mut usize) = this.pid; }

    w0.set_bg(Color::LightBlue);
    w0.paint();

    msg.set_cursor((0,0));
    msg.set_bg(Color::LightGray);
    msg.set_fg(Color::Black);

    msg.put_str("<-- If semaphores work correctly, then only this block \
                should be red when all loop_procs finish running.");

    for _ in 0..206*3 {
        ready_queue::make_ready(Process::new("loop_proc", super::user::run2));
        unsafe { s1.down(); }

        // test vm
        if unsafe { *(0xf00000 as *mut usize) } != this.pid {
            panic!("Oh no! *0xf00000 should be {} but is {}",
                   this.pid, unsafe { *(0xf00000 as *mut usize) });
        }
    }

    msg.put_str("\n\nYay :)");

    0
}

fn get_prev((r,c): (usize, usize)) -> (usize, usize) {
    if r == 0 && c > 0 { // top
        (0, c-1)
    } else if c == COLS-1 { // right
        (r-1, c)
    } else if r == ROWS-1 { // bottom
        (r, c+1)
    } else { // left
        (r+1, c)
    }
}

fn get_next((r,c): (usize, usize)) -> (usize, usize) {
    if r == 0 && c < COLS-1 { // top
        (0, c+1)
    } else if c == COLS-1 && r < ROWS-1 { // right
        (r+1, c)
    } else if r == ROWS-1 && c > 0 { // bottom
        (r, c-1)
    } else { // left
        (r-1, c)
    }
}

fn run2(this: &Process) -> usize {

    unsafe { *(0xf00000 as *mut usize) = this.pid; }

    // test vm
    if unsafe { *(0xf00000 as *mut usize) } != this.pid {
        panic!("Oh no! *0xf00000 should be {} but is {}",
               this.pid, unsafe { *(0xf00000 as *mut usize) });
    }

    unsafe { s2.down(); }

    let mut w = Window::new(COLS,ROWS, (0,0));

    let me = unsafe { current };
    let prev = get_prev(me);

    unsafe {
        current = get_next(me);
    }

    printf!("Erase ({},{}) ", prev.0, prev.1);
    w.set_bg(Color::LightBlue);
    w.set_cursor(prev);
    w.put_char(' ');

    printf!("Draw ({},{})\n", me.0,me.1);
    w.set_bg(Color::Red);
    w.set_cursor(me);
    w.put_char(' ');

    unsafe { s2.up(); }

    // test vm
    if unsafe { *(0xf00000 as *mut usize) } != this.pid {
        panic!("Oh no! *0xf00000 should be {} but is {}",
               this.pid, unsafe { *(0xf00000 as *mut usize) });
    }

    unsafe { s1.up(); }

    0
}
<|endoftext|>


<commit_before>use opengles;   // FIXME: Should only be for tests.
import glut::{check_loop, create_window, destroy_window, init, init_display_mode, swap_buffers};
import opengles::gl2::{ARRAY_BUFFER, COLOR_BUFFER_BIT, COMPILE_STATUS};
import opengles::gl2::{FRAGMENT_SHADER, LINK_STATUS, NO_ERROR, STATIC_DRAW};
import opengles::gl2::{TRIANGLE_STRIP, VERTEX_SHADER, GLclampf, GLenum};
import opengles::gl2::{GLsizei, GLuint, attach_shader, bind_buffer};
import opengles::gl2::{buffer_data, create_program, clear, clear_color};
import opengles::gl2::{compile_shader, create_shader, draw_arrays};
import opengles::gl2::{enable_vertex_attrib_array, gen_buffers};
import opengles::gl2::{get_attrib_location, get_error, get_program_iv};
import opengles::gl2::{get_shader_info_log, get_shader_iv};
import opengles::gl2::{get_uniform_location, link_program, shader_source};
import opengles::gl2::{use_program, vertex_attrib_pointer_f32};

import comm::{chan, peek, port, recv, send, Chan, Port};
import io::println;
import ptr::{addr_of, null};
import str::bytes;
import task::TaskBuilder;
import vec::unsafe::to_ptr;

fn fragment_shader_source() -> ~str {
    ~"
    #ifdef GLES2
        precision mediump float;
    #endif

        void main(void) {
            gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
        }
    "
}

fn vertex_shader_source() -> ~str {
    ~"
        attribute vec3 aVertexPosition;

        /*uniform mat4 uMVMatrix;
        uniform mat4 uPMatrix;*/

        void main(void) {
            gl_Position = /*uPMatrix * uMVMatrix **/
                vec4(aVertexPosition, 1.0);
        }
    "
}

fn load_shader(source_str: ~str, shader_type: GLenum) -> GLuint {
    let shader_id = create_shader(shader_type);
    shader_source(shader_id, ~[bytes(source_str)]);
    compile_shader(shader_id);

    if get_error() != NO_ERROR {
        println(#fmt("error: %d", get_error() as int));
        fail ~"failed to compile shader with error";
    }

    if get_shader_iv(shader_id, COMPILE_STATUS) == (0 as GLint) {
        println(get_shader_info_log(shader_id));
        fail ~"failed to compile shader";
    }
    return shader_id;
}

struct shader_program {
    let program: GLuint;
    let aVertexPosition: c_int;
    /*let uPMatrix: c_int;
    let uMVMatrix: c_int;*/

    new(program: GLuint) {
        self.program = program;
        self.aVertexPosition = get_attrib_location(program, ~"aVertexPosition");
        /*self.uPMatrix = get_uniform_location(program, "uPMatrix");
        self.uMVMatrix = get_uniform_location(program, "uMVMatrix");*/

        enable_vertex_attrib_array(self.aVertexPosition as GLuint);
    }
}

fn init_shaders() -> shader_program {
    let vertex_shader = load_shader(vertex_shader_source(), VERTEX_SHADER);
    let fragment_shader = load_shader(fragment_shader_source(),
                                      FRAGMENT_SHADER);

    let program = create_program();
    attach_shader(program, vertex_shader);
    attach_shader(program, fragment_shader);
    link_program(program);

    if get_program_iv(program, LINK_STATUS) == (0 as GLint) {
        fail ~"failed to initialize program";
    }

    use_program(program);

    return shader_program(program);
}

fn init_buffers() -> GLuint {
    let triangle_vertex_buffer = gen_buffers(1 as GLsizei)[0];
    bind_buffer(ARRAY_BUFFER, triangle_vertex_buffer);
    let vertices = ~[
        0.0f32, 1.0f32, 0.0f32,
        1.0f32, 0.0f32, 0.0f32,
        0.0f32, 0.0f32, 0.0f32
    ];
    buffer_data(ARRAY_BUFFER, vertices, STATIC_DRAW);
    return triangle_vertex_buffer;
}

fn draw_scene(shader_program: shader_program, vertex_buffer: GLuint) {
    clear_color(0.0f32, 0.0f32, 1.0f32, 1.0f32);
    clear(COLOR_BUFFER_BIT);

    bind_buffer(ARRAY_BUFFER, vertex_buffer);
    vertex_attrib_pointer_f32(shader_program.aVertexPosition as GLuint,
                              3 as GLint, false, 0 as GLsizei, 0 as GLuint);
    draw_arrays(TRIANGLE_STRIP, 0 as GLint, 3 as GLint);
}

fn display_callback() {
    let program = init_shaders();
    let vertex_buffer = init_buffers();
    draw_scene(program, vertex_buffer);

    swap_buffers();
}

#[test]
fn test_triangle_and_square() unsafe {
    let builder = task::task().sched_mode(task::PlatformThread);

    let po: Port<()> = port();
    let ch = chan(po);
    let _result_ch: Chan<()> = builder.spawn_listener(|_port| {
        init();
        init_display_mode(0 as c_uint);
        let window = create_window(~"Rust GLUT");
        display_func(display_callback);

        let wakeup = port();
        let wakeup_chan = chan(wakeup);
        timer_func(1000, || send(wakeup_chan, ()));

        loop {
            check_loop();

            if peek(wakeup) {
                recv(wakeup);
                send(ch, ());
                destroy_window(window);
                break;
            }
        }
    });

    recv(po);
}

<commit_msg>Fix tests<commit_after>use opengles;   // FIXME: Should only be for tests.
import glut::{check_loop, create_window, destroy_window, init, init_display_mode, swap_buffers};
import opengles::gl2::{ARRAY_BUFFER, COLOR_BUFFER_BIT, COMPILE_STATUS};
import opengles::gl2::{FRAGMENT_SHADER, LINK_STATUS, NO_ERROR, STATIC_DRAW};
import opengles::gl2::{TRIANGLE_STRIP, VERTEX_SHADER, GLclampf, GLenum};
import opengles::gl2::{GLsizei, GLuint, attach_shader, bind_buffer};
import opengles::gl2::{buffer_data, create_program, clear, clear_color};
import opengles::gl2::{compile_shader, create_shader, draw_arrays};
import opengles::gl2::{enable_vertex_attrib_array, gen_buffers};
import opengles::gl2::{get_attrib_location, get_error, get_program_iv};
import opengles::gl2::{get_shader_info_log, get_shader_iv};
import opengles::gl2::{get_uniform_location, link_program, shader_source};
import opengles::gl2::{use_program, vertex_attrib_pointer_f32};

import comm::{Chan, peek, Port, recv, send};
import io::println;
import ptr::{addr_of, null};
import str::to_bytes;
import task::TaskBuilder;
import vec::unsafe::to_ptr;

fn fragment_shader_source() -> ~str {
    ~"
    #ifdef GLES2
        precision mediump float;
    #endif

        void main(void) {
            gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
        }
    "
}

fn vertex_shader_source() -> ~str {
    ~"
        attribute vec3 aVertexPosition;

        /*uniform mat4 uMVMatrix;
        uniform mat4 uPMatrix;*/

        void main(void) {
            gl_Position = /*uPMatrix * uMVMatrix **/
                vec4(aVertexPosition, 1.0);
        }
    "
}

fn load_shader(source_str: ~str, shader_type: GLenum) -> GLuint {
    let shader_id = create_shader(shader_type);
    shader_source(shader_id, ~[to_bytes(source_str)]);
    compile_shader(shader_id);

    if get_error() != NO_ERROR {
        println(#fmt("error: %d", get_error() as int));
        fail ~"failed to compile shader with error";
    }

    if get_shader_iv(shader_id, COMPILE_STATUS) == (0 as GLint) {
        println(get_shader_info_log(shader_id));
        fail ~"failed to compile shader";
    }
    return shader_id;
}

struct shader_program {
    let program: GLuint;
    let aVertexPosition: c_int;
    /*let uPMatrix: c_int;
    let uMVMatrix: c_int;*/

    new(program: GLuint) {
        self.program = program;
        self.aVertexPosition = get_attrib_location(program, ~"aVertexPosition");
        /*self.uPMatrix = get_uniform_location(program, "uPMatrix");
        self.uMVMatrix = get_uniform_location(program, "uMVMatrix");*/

        enable_vertex_attrib_array(self.aVertexPosition as GLuint);
    }
}

fn init_shaders() -> shader_program {
    let vertex_shader = load_shader(vertex_shader_source(), VERTEX_SHADER);
    let fragment_shader = load_shader(fragment_shader_source(),
                                      FRAGMENT_SHADER);

    let program = create_program();
    attach_shader(program, vertex_shader);
    attach_shader(program, fragment_shader);
    link_program(program);

    if get_program_iv(program, LINK_STATUS) == (0 as GLint) {
        fail ~"failed to initialize program";
    }

    use_program(program);

    return shader_program(program);
}

fn init_buffers() -> GLuint {
    let triangle_vertex_buffer = gen_buffers(1 as GLsizei)[0];
    bind_buffer(ARRAY_BUFFER, triangle_vertex_buffer);
    let vertices = ~[
        0.0f32, 1.0f32, 0.0f32,
        1.0f32, 0.0f32, 0.0f32,
        0.0f32, 0.0f32, 0.0f32
    ];
    buffer_data(ARRAY_BUFFER, vertices, STATIC_DRAW);
    return triangle_vertex_buffer;
}

fn draw_scene(shader_program: shader_program, vertex_buffer: GLuint) {
    clear_color(0.0f32, 0.0f32, 1.0f32, 1.0f32);
    clear(COLOR_BUFFER_BIT);

    bind_buffer(ARRAY_BUFFER, vertex_buffer);
    vertex_attrib_pointer_f32(shader_program.aVertexPosition as GLuint,
                              3 as GLint, false, 0 as GLsizei, 0 as GLuint);
    draw_arrays(TRIANGLE_STRIP, 0 as GLint, 3 as GLint);
}

fn display_callback() {
    let program = init_shaders();
    let vertex_buffer = init_buffers();
    draw_scene(program, vertex_buffer);

    swap_buffers();
}

#[test]
fn test_triangle_and_square() unsafe {
    let builder = task::task().sched_mode(task::PlatformThread);

    let po: Port<()> = Port();
    let ch = Chan(po);
    let _result_ch: Chan<()> = builder.spawn_listener(|_port| {
        init();
        init_display_mode(0 as c_uint);
        let window = create_window(~"Rust GLUT");
        display_func(display_callback);

        let wakeup = Port();
        let wakeup_chan = Chan(wakeup);
        timer_func(1000, || send(wakeup_chan, ()));

        loop {
            check_loop();

            if peek(wakeup) {
                recv(wakeup);
                send(ch, ());
                destroy_window(window);
                break;
            }
        }
    });

    recv(po);
}

<|endoftext|>