<commit_before>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use std::fs::OpenOptions;
use std::io;
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::ptr;

use libc;

use crate::gpio::{Error, Result};
use crate::system::DeviceInfo;

// The BCM2835 has 41 32-bit registers related to the GPIO (datasheet @ 6.1).
const GPIO_MEM_SIZE: usize = 164;

#[derive(Debug)]
pub struct GpioMem {
    mapped: bool,
    mem_ptr: *mut u32,
}

impl GpioMem {
    pub fn new() -> GpioMem {
        GpioMem {
            mapped: false,
            mem_ptr: ptr::null_mut(),
        }
    }

    pub fn open(&mut self) -> Result<()> {
        if self.mapped {
            return Ok(());
        }

        // Try /dev/gpiomem first. If that fails, try /dev/mem instead. If neither works,
        // report back the error that's the most relevant.
        self.mem_ptr = match self.map_devgpiomem() {
            Ok(ptr) => ptr,
            Err(gpiomem_err) => match self.map_devmem() {
                Ok(ptr) => ptr,
                Err(Error::Io(ref e)) if e.kind() == io::ErrorKind::PermissionDenied => {
                    return Err(Error::PermissionDenied)
                }
                Err(Error::UnknownSoC) => return Err(Error::UnknownSoC),
                _ => return Err(gpiomem_err),
            },
        };

        self.mapped = true;

        Ok(())
    }

    fn map_devgpiomem(&self) -> Result<*mut u32> {
        // Open /dev/gpiomem with read/write/sync flags. This might fail if
        // /dev/gpiomem doesn't exist (< Raspbian Jessie), or /dev/gpiomem
        // doesn't have the appropriate permissions, or the current user is
        // not a member of the gpio group.
        let gpiomem_file = match OpenOptions::new()
            .read(true)
            .write(true)
            .custom_flags(libc::O_SYNC)
            .open("/dev/gpiomem")
        {
            Ok(file) => file,
            Err(e) => return Err(Error::Io(e)),
        };

        // Memory-map /dev/gpiomem at offset 0
        let gpiomem_ptr = unsafe {
            libc::mmap(
                ptr::null_mut(),
                GPIO_MEM_SIZE,
                libc::PROT_READ | libc::PROT_WRITE,
                libc::MAP_SHARED,
                gpiomem_file.as_raw_fd(),
                0,
            )
        };

        if gpiomem_ptr == libc::MAP_FAILED {
            return Err(Error::Io(io::Error::last_os_error()));
        }

        Ok(gpiomem_ptr as *mut u32)
    }

    fn map_devmem(&self) -> Result<*mut u32> {
        // Identify which SoC we're using, so we know what offset to start at
        let device_info = match DeviceInfo::new() {
            Ok(s) => s,
            Err(_) => return Err(Error::UnknownSoC),
        };

        let mem_file = match OpenOptions::new()
            .read(true)
            .write(true)
            .custom_flags(libc::O_SYNC)
            .open("/dev/mem")
        {
            Ok(file) => file,
            Err(e) => return Err(Error::Io(e)),
        };

        // Memory-map /dev/mem at the appropriate offset for our SoC
        let mem_ptr = unsafe {
            libc::mmap(
                ptr::null_mut(),
                GPIO_MEM_SIZE,
                libc::PROT_READ | libc::PROT_WRITE,
                libc::MAP_SHARED,
                mem_file.as_raw_fd(),
                (device_info.peripheral_base() + device_info.gpio_offset()) as libc::off_t,
            )
        };

        if mem_ptr == libc::MAP_FAILED {
            return Err(Error::Io(io::Error::last_os_error()));
        }

        Ok(mem_ptr as *mut u32)
    }

    pub fn close(&mut self) {
        if !self.mapped {
            return;
        }

        unsafe {
            libc::munmap(
                self.mem_ptr as *mut libc::c_void,
                GPIO_MEM_SIZE as libc::size_t,
            );
        }

        self.mapped = false;
    }

    pub fn read(&self, offset: usize) -> u32 {
        if !self.mapped || offset >= GPIO_MEM_SIZE {
            return 0;
        }

        unsafe { ptr::read_volatile(self.mem_ptr.add(offset)) }
    }

    pub fn write(&self, offset: usize, value: u32) {
        if !self.mapped || offset >= GPIO_MEM_SIZE {
            return;
        }

        unsafe {
            ptr::write_volatile(self.mem_ptr.add(offset), value);
        }
    }
}

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

// Required because of the raw pointer to our memory-mapped file
unsafe impl Send for GpioMem {}
<commit_msg>Use `?` operator.<commit_after>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use std::fs::OpenOptions;
use std::io;
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::ptr;

use libc;

use crate::gpio::{Error, Result};
use crate::system::DeviceInfo;

// The BCM2835 has 41 32-bit registers related to the GPIO (datasheet @ 6.1).
const GPIO_MEM_SIZE: usize = 164;

#[derive(Debug)]
pub struct GpioMem {
    mapped: bool,
    mem_ptr: *mut u32,
}

impl GpioMem {
    pub fn new() -> GpioMem {
        GpioMem {
            mapped: false,
            mem_ptr: ptr::null_mut(),
        }
    }

    pub fn open(&mut self) -> Result<()> {
        if self.mapped {
            return Ok(());
        }

        // Try /dev/gpiomem first. If that fails, try /dev/mem instead. If neither works,
        // report back the error that's the most relevant.
        self.mem_ptr = match self.map_devgpiomem() {
            Ok(ptr) => ptr,
            Err(gpiomem_err) => match self.map_devmem() {
                Ok(ptr) => ptr,
                Err(Error::Io(ref e)) if e.kind() == io::ErrorKind::PermissionDenied => {
                    return Err(Error::PermissionDenied)
                }
                Err(Error::UnknownSoC) => return Err(Error::UnknownSoC),
                _ => return Err(gpiomem_err),
            },
        };

        self.mapped = true;

        Ok(())
    }

    fn map_devgpiomem(&self) -> Result<*mut u32> {
        // Open /dev/gpiomem with read/write/sync flags. This might fail if
        // /dev/gpiomem doesn't exist (< Raspbian Jessie), or /dev/gpiomem
        // doesn't have the appropriate permissions, or the current user is
        // not a member of the gpio group.
        let gpiomem_file = OpenOptions::new()
            .read(true)
            .write(true)
            .custom_flags(libc::O_SYNC)
            .open("/dev/gpiomem")?;

        // Memory-map /dev/gpiomem at offset 0
        let gpiomem_ptr = unsafe {
            libc::mmap(
                ptr::null_mut(),
                GPIO_MEM_SIZE,
                libc::PROT_READ | libc::PROT_WRITE,
                libc::MAP_SHARED,
                gpiomem_file.as_raw_fd(),
                0,
            )
        };

        if gpiomem_ptr == libc::MAP_FAILED {
            return Err(Error::Io(io::Error::last_os_error()));
        }

        Ok(gpiomem_ptr as *mut u32)
    }

    fn map_devmem(&self) -> Result<*mut u32> {
        // Identify which SoC we're using, so we know what offset to start at
        let device_info = DeviceInfo::new().map_err(|_| Error::UnknownSoC)?;

        let mem_file = OpenOptions::new()
            .read(true)
            .write(true)
            .custom_flags(libc::O_SYNC)
            .open("/dev/mem")?;

        // Memory-map /dev/mem at the appropriate offset for our SoC
        let mem_ptr = unsafe {
            libc::mmap(
                ptr::null_mut(),
                GPIO_MEM_SIZE,
                libc::PROT_READ | libc::PROT_WRITE,
                libc::MAP_SHARED,
                mem_file.as_raw_fd(),
                (device_info.peripheral_base() + device_info.gpio_offset()) as libc::off_t,
            )
        };

        if mem_ptr == libc::MAP_FAILED {
            return Err(Error::Io(io::Error::last_os_error()));
        }

        Ok(mem_ptr as *mut u32)
    }

    pub fn close(&mut self) {
        if !self.mapped {
            return;
        }

        unsafe {
            libc::munmap(
                self.mem_ptr as *mut libc::c_void,
                GPIO_MEM_SIZE as libc::size_t,
            );
        }

        self.mapped = false;
    }

    pub fn read(&self, offset: usize) -> u32 {
        if !self.mapped || offset >= GPIO_MEM_SIZE {
            return 0;
        }

        unsafe { ptr::read_volatile(self.mem_ptr.add(offset)) }
    }

    pub fn write(&self, offset: usize, value: u32) {
        if !self.mapped || offset >= GPIO_MEM_SIZE {
            return;
        }

        unsafe {
            ptr::write_volatile(self.mem_ptr.add(offset), value);
        }
    }
}

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

// Required because of the raw pointer to our memory-mapped file
unsafe impl Send for GpioMem {}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2019 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use std::fmt;
use std::fs::OpenOptions;
use std::io;
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use std::time::Duration;
use libc::{self, c_void, MAP_FAILED, MAP_SHARED, O_SYNC, off_t, PROT_READ, PROT_WRITE, size_t};
use crate::gpio::{Error, Level, Mode, PullUpDown, Result};
use crate::system::{DeviceInfo, SoC};

const PATH_DEV_GPIOMEM: &str = "/dev/gpiomem";
const PATH_DEV_MEM: &str = "/dev/mem";
// The BCM2835 has 41 32-bit registers related to the GPIO (datasheet @ 6.1).
// The BCM2711 (RPi4) has 58 32-bit registers related to the GPIO.
const GPIO_MEM_REGISTERS: usize = 58;
const GPIO_MEM_SIZE: usize = GPIO_MEM_REGISTERS * std::mem::size_of::<u32>();
const GPFSEL0: usize = 0x00;
const GPSET0: usize = 0x1c / std::mem::size_of::<u32>();
const GPCLR0: usize = 0x28 / std::mem::size_of::<u32>();
const GPLEV0: usize = 0x34 / std::mem::size_of::<u32>();
const GPPUD: usize = 0x94 / std::mem::size_of::<u32>();
const GPPUDCLK0: usize = 0x98 / std::mem::size_of::<u32>();
// Only available in BCM2711 (RPi4).
const GPPUD_CNTRL_REG0: usize = 0xe4 / std::mem::size_of::<u32>();

pub struct GpioMem {
    mem_ptr: *mut u32,
    locks: [AtomicBool; GPIO_MEM_REGISTERS],
    soc: SoC,
}

impl fmt::Debug for GpioMem {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("GpioMem")
         .field("mem_ptr", &self.mem_ptr)
         .field("locks", &format_args!("{{ .. }}"))
         .field("soc", &self.soc)
         .finish()
    }
}

impl GpioMem {
    pub fn open() -> Result<GpioMem> {
        // Try /dev/gpiomem first. If that fails, try /dev/mem instead. If neither works,
        // report back the error that's the most relevant.
        let mem_ptr = match Self::map_devgpiomem() {
            Ok(ptr) => ptr,
            Err(gpiomem_err) => match Self::map_devmem() {
                Ok(ptr) => ptr,
                Err(Error::Io(ref e)) if e.kind() == io::ErrorKind::PermissionDenied => {
                    // Did /dev/gpiomem also give us a Permission Denied error? If so, return
                    // that path instead of /dev/mem. Solving /dev/gpiomem issues should be
                    // preferred (add user to gpio group) over /dev/mem (use sudo),
                    match gpiomem_err {
                        Error::Io(ref e) if e.kind() == io::ErrorKind::PermissionDenied => {
                            return Err(Error::PermissionDenied(String::from(PATH_DEV_GPIOMEM)));
                        }
                        _ => return Err(Error::PermissionDenied(String::from(PATH_DEV_MEM))),
                    }
                }
                Err(Error::UnknownModel) => return Err(Error::UnknownModel),
                _ => return Err(gpiomem_err),
            },
        };
        let locks = init_array!(AtomicBool::new(false), GPIO_MEM_REGISTERS);
        // Identify which SoC we're using.
        let soc = DeviceInfo::new().map_err(|_| Error::UnknownModel)?.soc();
        Ok(GpioMem { mem_ptr, locks, soc })
    }
    fn map_devgpiomem() -> Result<*mut u32> {
        // Open /dev/gpiomem with read/write/sync flags. This might fail if
        // /dev/gpiomem doesn't exist (< Raspbian Jessie), or /dev/gpiomem
        // doesn't have the appropriate permissions, or the current user is
        // not a member of the gpio group.
        let gpiomem_file = OpenOptions::new()
            .read(true)
            .write(true)
            .custom_flags(O_SYNC)
            .open(PATH_DEV_GPIOMEM)?;
        // Memory-map /dev/gpiomem at offset 0
        let gpiomem_ptr = unsafe {
            libc::mmap(
                ptr::null_mut(),
                GPIO_MEM_SIZE,
                PROT_READ | PROT_WRITE,
                MAP_SHARED,
                gpiomem_file.as_raw_fd(),
                0,
            )
        };
        if gpiomem_ptr == MAP_FAILED {
            return Err(Error::Io(io::Error::last_os_error()));
        }
        Ok(gpiomem_ptr as *mut u32)
    }
    fn map_devmem() -> Result<*mut u32> {
        // Identify which SoC we're using, so we know what offset to start at
        let device_info = DeviceInfo::new().map_err(|_| Error::UnknownModel)?;
        let mem_file = OpenOptions::new()
            .read(true)
            .write(true)
            .custom_flags(O_SYNC)
            .open(PATH_DEV_MEM)?;
        // Memory-map /dev/mem at the appropriate offset for our SoC
        let mem_ptr = unsafe {
            libc::mmap(
                ptr::null_mut(),
                GPIO_MEM_SIZE,
                PROT_READ | PROT_WRITE,
                MAP_SHARED,
                mem_file.as_raw_fd(),
                (device_info.peripheral_base() + device_info.gpio_offset()) as off_t,
            )
        };
        if mem_ptr == MAP_FAILED {
            return Err(Error::Io(io::Error::last_os_error()));
        }
        Ok(mem_ptr as *mut u32)
    }
    #[inline(always)]
    fn read(&self, offset: usize) -> u32 {
        unsafe { ptr::read_volatile(self.mem_ptr.add(offset)) }
    }
    #[inline(always)]
    fn write(&self, offset: usize, value: u32) {
        unsafe {
            ptr::write_volatile(self.mem_ptr.add(offset), value);
        }
    }
    #[inline(always)]
    pub(crate) fn set_high(&self, pin: u8) {
        let offset = GPSET0 + pin as usize / 32;
        let shift = pin % 32;
        self.write(offset, 1 << shift);
    }
    #[inline(always)]
    pub(crate) fn set_low(&self, pin: u8) {
        let offset = GPCLR0 + pin as usize / 32;
        let shift = pin % 32;
        self.write(offset, 1 << shift);
    }
    #[inline(always)]
    pub(crate) fn level(&self, pin: u8) -> Level {
        let offset = GPLEV0 + pin as usize / 32;
        let shift = pin % 32;
        let reg_value = self.read(offset);
        unsafe { std::mem::transmute((reg_value >> shift) as u8 & 0b1) }
    }
    pub(crate) fn mode(&self, pin: u8) -> Mode {
        let offset = GPFSEL0 + pin as usize / 10;
        let shift = (pin % 10) * 3;
        let reg_value = self.read(offset);
        unsafe { std::mem::transmute((reg_value >> shift) as u8 & 0b111) }
    }
    pub(crate) fn set_mode(&self, pin: u8, mode: Mode) {
        let offset = GPFSEL0 + pin as usize / 10;
        let shift = (pin % 10) * 3;
        loop {
            if !self.locks[offset].compare_and_swap(false, true, Ordering::SeqCst) {
                break;
            }
        }
        let reg_value = self.read(offset);
        self.write(
            offset,
            (reg_value & !(0b111 << shift)) | ((mode as u32) << shift),
        );
        self.locks[offset].store(false, Ordering::SeqCst);
    }
    pub(crate) fn set_pullupdown(&self, pin: u8, pud: PullUpDown) {
        let lock: usize;
        let offset: usize;
        let shift: u8;
        if self.soc == SoC::Bcm2711 {
            lock = GPPUD_CNTRL_REG0 + pin as usize / 32;
            offset = GPPUD_CNTRL_REG0 + pin as usize / 16;
            shift = (pin & 0xf) << 1;
            loop {
                if !self.locks[lock].compare_and_swap(false, true, Ordering::SeqCst) {
                    break;
                }
            }
            let reg_value = self.read(offset);
            self.write(
                offset,
                (reg_value & !(0b11 << shift)) | ((pud as u32) << shift),
            );
            self.locks[lock].store(false, Ordering::SeqCst);
        } else {
            lock = GPPUDCLK0 + pin as usize / 32;
            offset = lock;
            shift = pin % 32;
            loop {
                if !self.locks[GPPUD].compare_and_swap(false, true, Ordering::SeqCst) {
                    if !self.locks[lock].compare_and_swap(false, true, Ordering::SeqCst) {
                        break;
                    } else {
                        self.locks[GPPUD].store(false, Ordering::SeqCst);
                    }
                }
            }
            // Set the control signal in GPPUD.
            let reg_value = self.read(GPPUD);
            self.write(GPPUD, (reg_value & !0b11) | ((pud as u32) & 0b11));
            // The datasheet mentions waiting at least 150 cycles for set-up and hold, but
            // doesn't state which clock is used. This is likely the VPU clock (see
            // https://www.raspberrypi.org/forums/viewtopic.php?f=72&t=163352). At either
            // 250MHz or 400MHz, a 5µs delay + overhead is more than adequate.
            // Set-up time for the control signal.
            thread::sleep(Duration::new(0, 5000)); // >= 5µs
            // Clock the control signal into the selected pin.
            self.write(offset, 1 << shift);
            // Hold time for the control signal.
            thread::sleep(Duration::new(0, 5000)); // >= 5µs
            // Remove the control signal and clock.
            self.write(GPPUD, reg_value & !0b11);
            self.write(offset, 0);
            self.locks[lock].store(false, Ordering::SeqCst);
            self.locks[GPPUD].store(false, Ordering::SeqCst);
        }
    }
}

impl Drop for GpioMem {
    fn drop(&mut self) {
        unsafe {
            libc::munmap(self.mem_ptr as *mut c_void, GPIO_MEM_SIZE as size_t);
        }
    }
}

// Required because of the raw pointer to our memory-mapped file
unsafe impl Send for GpioMem {}

unsafe impl Sync for GpioMem {}
<commit_msg>Update / refactoring of Rpi4 set_pullupdown() code.<commit_after>// Copyright (c) 2017-2019 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use crate::gpio::{Error, Level, Mode, PullUpDown, Result};
use crate::system::{DeviceInfo, SoC};
use libc::{self, c_void, off_t, size_t, MAP_FAILED, MAP_SHARED, O_SYNC, PROT_READ, PROT_WRITE};
use std::fmt;
use std::fs::OpenOptions;
use std::io;
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use std::time::Duration;

const PATH_DEV_GPIOMEM: &str = "/dev/gpiomem";
const PATH_DEV_MEM: &str = "/dev/mem";
// The BCM2835 has 41 32-bit registers related to the GPIO (datasheet @ 6.1).
// The BCM2711 (RPi4) has 58 32-bit registers related to the GPIO.
const GPIO_MEM_REGISTERS: usize = 58;
const GPIO_MEM_SIZE: usize = GPIO_MEM_REGISTERS * std::mem::size_of::<u32>();
const GPFSEL0: usize = 0x00;
const GPSET0: usize = 0x1c / std::mem::size_of::<u32>();
const GPCLR0: usize = 0x28 / std::mem::size_of::<u32>();
const GPLEV0: usize = 0x34 / std::mem::size_of::<u32>();
const GPPUD: usize = 0x94 / std::mem::size_of::<u32>();
const GPPUDCLK0: usize = 0x98 / std::mem::size_of::<u32>();
// Only available in BCM2711 (RPi4).
const GPPUD_CNTRL_REG0: usize = 0xe4 / std::mem::size_of::<u32>();

pub struct GpioMem {
    mem_ptr: *mut u32,
    locks: [AtomicBool; GPIO_MEM_REGISTERS],
    soc: SoC,
}

impl fmt::Debug for GpioMem {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("GpioMem")
            .field("mem_ptr", &self.mem_ptr)
            .field("locks", &format_args!("{{ .. }}"))
            .field("soc", &self.soc)
            .finish()
    }
}

impl GpioMem {
    pub fn open() -> Result<GpioMem> {
        // Try /dev/gpiomem first. If that fails, try /dev/mem instead. If neither works,
        // report back the error that's the most relevant.
        let mem_ptr = match Self::map_devgpiomem() {
            Ok(ptr) => ptr,
            Err(gpiomem_err) => match Self::map_devmem() {
                Ok(ptr) => ptr,
                Err(Error::Io(ref e)) if e.kind() == io::ErrorKind::PermissionDenied => {
                    // Did /dev/gpiomem also give us a Permission Denied error? If so, return
                    // that path instead of /dev/mem. Solving /dev/gpiomem issues should be
                    // preferred (add user to gpio group) over /dev/mem (use sudo),
                    match gpiomem_err {
                        Error::Io(ref e) if e.kind() == io::ErrorKind::PermissionDenied => {
                            return Err(Error::PermissionDenied(String::from(PATH_DEV_GPIOMEM)));
                        }
                        _ => return Err(Error::PermissionDenied(String::from(PATH_DEV_MEM))),
                    }
                }
                Err(Error::UnknownModel) => return Err(Error::UnknownModel),
                _ => return Err(gpiomem_err),
            },
        };
        let locks = init_array!(AtomicBool::new(false), GPIO_MEM_REGISTERS);
        // Identify which SoC we're using.
        let soc = DeviceInfo::new().map_err(|_| Error::UnknownModel)?.soc();
        Ok(GpioMem {
            mem_ptr,
            locks,
            soc,
        })
    }
    fn map_devgpiomem() -> Result<*mut u32> {
        // Open /dev/gpiomem with read/write/sync flags. This might fail if
        // /dev/gpiomem doesn't exist (< Raspbian Jessie), or /dev/gpiomem
        // doesn't have the appropriate permissions, or the current user is
        // not a member of the gpio group.
        let gpiomem_file = OpenOptions::new()
            .read(true)
            .write(true)
            .custom_flags(O_SYNC)
            .open(PATH_DEV_GPIOMEM)?;
        // Memory-map /dev/gpiomem at offset 0
        let gpiomem_ptr = unsafe {
            libc::mmap(
                ptr::null_mut(),
                GPIO_MEM_SIZE,
                PROT_READ | PROT_WRITE,
                MAP_SHARED,
                gpiomem_file.as_raw_fd(),
                0,
            )
        };
        if gpiomem_ptr == MAP_FAILED {
            return Err(Error::Io(io::Error::last_os_error()));
        }
        Ok(gpiomem_ptr as *mut u32)
    }
    fn map_devmem() -> Result<*mut u32> {
        // Identify which SoC we're using, so we know what offset to start at
        let device_info = DeviceInfo::new().map_err(|_| Error::UnknownModel)?;
        let mem_file = OpenOptions::new()
            .read(true)
            .write(true)
            .custom_flags(O_SYNC)
            .open(PATH_DEV_MEM)?;
        // Memory-map /dev/mem at the appropriate offset for our SoC
        let mem_ptr = unsafe {
            libc::mmap(
                ptr::null_mut(),
                GPIO_MEM_SIZE,
                PROT_READ | PROT_WRITE,
                MAP_SHARED,
                mem_file.as_raw_fd(),
                (device_info.peripheral_base() + device_info.gpio_offset()) as off_t,
            )
        };
        if mem_ptr == MAP_FAILED {
            return Err(Error::Io(io::Error::last_os_error()));
        }
        Ok(mem_ptr as *mut u32)
    }
    #[inline(always)]
    fn read(&self, offset: usize) -> u32 {
        unsafe { ptr::read_volatile(self.mem_ptr.add(offset)) }
    }
    #[inline(always)]
    fn write(&self, offset: usize, value: u32) {
        unsafe {
            ptr::write_volatile(self.mem_ptr.add(offset), value);
        }
    }
    #[inline(always)]
    pub(crate) fn set_high(&self, pin: u8) {
        let offset = GPSET0 + pin as usize / 32;
        let shift = pin % 32;
        self.write(offset, 1 << shift);
    }
    #[inline(always)]
    pub(crate) fn set_low(&self, pin: u8) {
        let offset = GPCLR0 + pin as usize / 32;
        let shift = pin % 32;
        self.write(offset, 1 << shift);
    }
    #[inline(always)]
    pub(crate) fn level(&self, pin: u8) -> Level {
        let offset = GPLEV0 + pin as usize / 32;
        let shift = pin % 32;
        let reg_value = self.read(offset);
        unsafe { std::mem::transmute((reg_value >> shift) as u8 & 0b1) }
    }
    pub(crate) fn mode(&self, pin: u8) -> Mode {
        let offset = GPFSEL0 + pin as usize / 10;
        let shift = (pin % 10) * 3;
        let reg_value = self.read(offset);
        unsafe { std::mem::transmute((reg_value >> shift) as u8 & 0b111) }
    }
    pub(crate) fn set_mode(&self, pin: u8, mode: Mode) {
        let offset = GPFSEL0 + pin as usize / 10;
        let shift = (pin % 10) * 3;
        loop {
            if !self.locks[offset].compare_and_swap(false, true, Ordering::SeqCst) {
                break;
            }
        }
        let reg_value = self.read(offset);
        self.write(
            offset,
            (reg_value & !(0b111 << shift)) | ((mode as u32) << shift),
        );
        self.locks[offset].store(false, Ordering::SeqCst);
    }
    pub(crate) fn set_pullupdown(&self, pin: u8, pud: PullUpDown) {
        // Offset for register.
        let offset: usize;
        // Bit shift for pin position within register value.
        let shift: u8;
        // Only BCM2711 (RPi4) needs special handling for now.
        if self.soc == SoC::Bcm2711 {
            offset = GPPUD_CNTRL_REG0 + pin as usize / 16;
            shift = pin % 16 * 2;
            // Index for lock is different than register.
            let lock = GPPUD_CNTRL_REG0 + pin as usize / 32;
            // Pull up vs pull down has a reverse bit pattern on BCM2711 vs others.
            let pud = match pud {
                PullUpDown::Off => 0b00u32,
                PullUpDown::PullDown => 0b10,
                PullUpDown::PullUp => 0b01,
            };
            loop {
                if !self.locks[lock].compare_and_swap(false, true, Ordering::SeqCst) {
                    break;
                }
            }
            let reg_value = self.read(offset);
            self.write(offset, (reg_value & !(0b11 << shift)) | (pud << shift));
            self.locks[lock].store(false, Ordering::SeqCst);
        } else {
            offset = GPPUDCLK0 + pin as usize / 32;
            shift = pin % 32;
            loop {
                if !self.locks[GPPUD].compare_and_swap(false, true, Ordering::SeqCst) {
                    if !self.locks[offset].compare_and_swap(false, true, Ordering::SeqCst) {
                        break;
                    } else {
                        self.locks[GPPUD].store(false, Ordering::SeqCst);
                    }
                }
            }
            // Set the control signal in GPPUD.
            let reg_value = self.read(GPPUD);
            self.write(GPPUD, (reg_value & !0b11) | ((pud as u32) & 0b11));
            // The datasheet mentions waiting at least 150 cycles for set-up and hold, but
            // doesn't state which clock is used. This is likely the VPU clock (see
            // https://www.raspberrypi.org/forums/viewtopic.php?f=72&t=163352). At either
            // 250MHz or 400MHz, a 5µs delay + overhead is more than adequate.
            // Set-up time for the control signal.
            thread::sleep(Duration::new(0, 5000)); // >= 5µs
                                                   // Clock the control signal into the selected pin.
            self.write(offset, 1 << shift);
            // Hold time for the control signal.
            thread::sleep(Duration::new(0, 5000)); // >= 5µs
                                                   // Remove the control signal and clock.
            self.write(GPPUD, reg_value & !0b11);
            self.write(offset, 0);
            self.locks[offset].store(false, Ordering::SeqCst);
            self.locks[GPPUD].store(false, Ordering::SeqCst);
        }
    }
}

impl Drop for GpioMem {
    fn drop(&mut self) {
        unsafe {
            libc::munmap(self.mem_ptr as *mut c_void, GPIO_MEM_SIZE as size_t);
        }
    }
}

// Required because of the raw pointer to our memory-mapped file
unsafe impl Send for GpioMem {}

unsafe impl Sync for GpioMem {}
<|endoftext|>


<commit_before>use crate::i18n::LocalText;
use crate::*;

use colored::Colorize;
use regex::Regex;
use std::io::prelude::*;
use std::{ffi::OsString, io};
use subprocess::{Exec, ExitStatus, Redirection};

// FTL: help-subcommand-make
/// Build specified target(s)
pub fn run(target: Vec<String>) -> Result<()> {
    setup::is_setup()?;
    show_header("make-header");
    let mut makeflags: Vec<OsString> = Vec::new();
    let cpus = num_cpus::get();
    makeflags.push(OsString::from(format!("--jobs={}", cpus)));
    let mut makefiles: Vec<OsString> = Vec::new();
    let rules = status::get_rules()?;
    makefiles.push(OsString::from("-f"));
    makefiles.push(OsString::from(format!(
        "{}{}",
        CONFIGURE_DATADIR, "/rules/casile.mk"
    )));
    for rule in rules {
        makefiles.push(OsString::from("-f"));
        let p = rule.into_os_string();
        makefiles.push(p);
    }
    makefiles.push(OsString::from("-f"));
    makefiles.push(OsString::from(format!(
        "{}{}",
        CONFIGURE_DATADIR, "/rules/rules.mk"
    )));
    let mut targets: Vec<_> = target.into_iter().collect();
    if status::is_gha()? {
        targets.push(String::from("_gha"));
        if targets.len() == 1 {
            targets.push(String::from("default"));
        }
        targets.push(String::from("install-dist"));
    }
    if status::is_glc()? {
        targets.push(String::from("_glc"));
        if targets.len() == 1 {
            targets.push(String::from("default"));
        }
        targets.push(String::from("install-dist"));
    }
    let mut process = Exec::cmd("make")
        .args(&makeflags)
        .args(&makefiles)
        .args(&targets);
    let gitname = status::get_gitname()?;
    let git_version = status::get_git_version();
    process = process
        .env("CASILE_CLI", "true")
        .env("CASILEDIR", CONFIGURE_DATADIR)
        .env("CONTAINERIZED", status::is_container().to_string())
        .env("LANGUAGE", lang_to_language(CONF.get_string("language")?))
        .env("PROJECT", &gitname)
        .env("PROJECTDIR", CONF.get_string("path")?)
        .env("PROJECTVERSION", git_version);
    if CONF.get_bool("debug")? {
        process = process.env("DEBUG", "true");
    };
    if CONF.get_bool("quiet")? {
        process = process.env("QUIET", "true");
    };
    if CONF.get_bool("verbose")? || targets.contains(&"debug".into()) {
        process = process.env("VERBOSE", "true");
    };
    let repo = get_repo()?;
    let workdir = repo.workdir().unwrap();
    process = process.cwd(workdir);
    let process = process.stderr(Redirection::Merge).stdout(Redirection::Pipe);
    let mut popen = process.popen()?;
    let buf = io::BufReader::new(popen.stdout.as_mut().unwrap());
    let mut backlog: Vec<String> = Vec::new();
    let seps = Regex::new(r"").unwrap();
    let mut ret: u32 = 0;
    for line in buf.lines() {
        let text: &str = &line.unwrap();
        let fields: Vec<&str> = seps.splitn(text, 4).collect();
        match fields[0] {
            "CASILE" => match fields[1] {
                "PRE" => report_start(fields[2]),
                "STDOUT" => {
                    if status::is_gha()? {
                        println!("{}", fields[3]);
                    } else if CONF.get_bool("verbose")? {
                        report_line(fields[3]);
                    } else {
                        backlog.push(String::from(fields[3]));
                    }
                }
                "STDERR" => {
                    if CONF.get_bool("verbose")? {
                        report_line(fields[3]);
                    } else {
                        backlog.push(String::from(fields[3]));
                    }
                }
                "POST" => match fields[2] {
                    "0" => {
                        report_end(fields[3]);
                    }
                    val => {
                        report_fail(fields[3]);
                        ret = val.parse().unwrap_or(1);
                    }
                },
                _ => {
                    let errmsg = LocalText::new("make-error-unknown-code").fmt();
                    panic!("{}", errmsg)
                }
            },
            _ => backlog.push(String::from(fields[0])),
        }
    }
    let status = popen.wait();
    match status {
        Ok(ExitStatus::Exited(int)) => {
            let code = int + ret;
            match code {
                0 => {
                    if CONF.get_bool("debug")?
                        || targets.contains(&"debug".into())
                        || targets.contains(&"-p".into())
                    {
                        dump_backlog(&backlog)
                    };
                    Ok(())
                }
                1 => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-unfinished").fmt(),
                    )))
                }
                2 => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-build").fmt(),
                    )))
                }
                3 => {
                    if !CONF.get_bool("verbose")? {
                        dump_backlog(&backlog);
                    }
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-target").fmt(),
                    )))
                }
                _ => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-unknown").fmt(),
                    )))
                }
            }
        }
        _ => Err(Box::new(io::Error::new(
            io::ErrorKind::InvalidInput,
            LocalText::new("make-error").fmt(),
        ))),
    }
}

fn dump_backlog(backlog: &[String]) {
    let start = LocalText::new("make-backlog-start").fmt();
    eprintln!("{} {}", "┖┄".cyan(), start);
    for line in backlog.iter() {
        eprintln!("{}", line);
    }
    let end = LocalText::new("make-backlog-end").fmt();
    eprintln!("{} {}", "┎┄".cyan(), end);
}

fn report_line(line: &str) {
    eprintln!("{} {}", "┠╎".cyan(), line.dimmed());
}

fn report_start(target: &str) {
    let text = LocalText::new("make-report-start")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.yellow());
}

fn report_end(target: &str) {
    let text = LocalText::new("make-report-end")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.green());
}

fn report_fail(target: &str) {
    let text = LocalText::new("make-report-fail")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.red());
}
<commit_msg>fix(cli): Don't force install-dist target if debug is first target<commit_after>use crate::i18n::LocalText;
use crate::*;

use colored::Colorize;
use regex::Regex;
use std::io::prelude::*;
use std::{ffi::OsString, io};
use subprocess::{Exec, ExitStatus, Redirection};

// FTL: help-subcommand-make
/// Build specified target(s)
pub fn run(target: Vec<String>) -> Result<()> {
    setup::is_setup()?;
    show_header("make-header");
    let mut makeflags: Vec<OsString> = Vec::new();
    let cpus = num_cpus::get();
    makeflags.push(OsString::from(format!("--jobs={}", cpus)));
    let mut makefiles: Vec<OsString> = Vec::new();
    let rules = status::get_rules()?;
    makefiles.push(OsString::from("-f"));
    makefiles.push(OsString::from(format!(
        "{}{}",
        CONFIGURE_DATADIR, "/rules/casile.mk"
    )));
    for rule in rules {
        makefiles.push(OsString::from("-f"));
        let p = rule.into_os_string();
        makefiles.push(p);
    }
    makefiles.push(OsString::from("-f"));
    makefiles.push(OsString::from(format!(
        "{}{}",
        CONFIGURE_DATADIR, "/rules/rules.mk"
    )));
    let mut targets: Vec<_> = target.into_iter().collect();
    if targets.is_empty() {
        targets.push(String::from("default"));
    }
    if status::is_gha()? {
        targets.push(String::from("_gha"));
    }
    if status::is_glc()? {
        targets.push(String::from("_glc"));
    }
    if (status::is_gha()? || status::is_glc()?) && targets.first().unwrap() != "debug" {
        targets.push(String::from("install-dist"));
    }
    let mut process = Exec::cmd("make")
        .args(&makeflags)
        .args(&makefiles)
        .args(&targets);
    let gitname = status::get_gitname()?;
    let git_version = status::get_git_version();
    process = process
        .env("CASILE_CLI", "true")
        .env("CASILEDIR", CONFIGURE_DATADIR)
        .env("CONTAINERIZED", status::is_container().to_string())
        .env("LANGUAGE", lang_to_language(CONF.get_string("language")?))
        .env("PROJECT", &gitname)
        .env("PROJECTDIR", CONF.get_string("path")?)
        .env("PROJECTVERSION", git_version);
    if CONF.get_bool("debug")? {
        process = process.env("DEBUG", "true");
    };
    if CONF.get_bool("quiet")? {
        process = process.env("QUIET", "true");
    };
    if CONF.get_bool("verbose")? || targets.contains(&"debug".into()) {
        process = process.env("VERBOSE", "true");
    };
    let repo = get_repo()?;
    let workdir = repo.workdir().unwrap();
    process = process.cwd(workdir);
    let process = process.stderr(Redirection::Merge).stdout(Redirection::Pipe);
    let mut popen = process.popen()?;
    let buf = io::BufReader::new(popen.stdout.as_mut().unwrap());
    let mut backlog: Vec<String> = Vec::new();
    let seps = Regex::new(r"").unwrap();
    let mut ret: u32 = 0;
    for line in buf.lines() {
        let text: &str = &line.unwrap();
        let fields: Vec<&str> = seps.splitn(text, 4).collect();
        match fields[0] {
            "CASILE" => match fields[1] {
                "PRE" => report_start(fields[2]),
                "STDOUT" => {
                    if status::is_gha()? {
                        println!("{}", fields[3]);
                    } else if CONF.get_bool("verbose")? {
                        report_line(fields[3]);
                    } else {
                        backlog.push(String::from(fields[3]));
                    }
                }
                "STDERR" => {
                    if CONF.get_bool("verbose")? {
                        report_line(fields[3]);
                    } else {
                        backlog.push(String::from(fields[3]));
                    }
                }
                "POST" => match fields[2] {
                    "0" => {
                        report_end(fields[3]);
                    }
                    val => {
                        report_fail(fields[3]);
                        ret = val.parse().unwrap_or(1);
                    }
                },
                _ => {
                    let errmsg = LocalText::new("make-error-unknown-code").fmt();
                    panic!("{}", errmsg)
                }
            },
            _ => backlog.push(String::from(fields[0])),
        }
    }
    let status = popen.wait();
    match status {
        Ok(ExitStatus::Exited(int)) => {
            let code = int + ret;
            match code {
                0 => {
                    if CONF.get_bool("debug")?
                        || targets.contains(&"debug".into())
                        || targets.contains(&"-p".into())
                    {
                        dump_backlog(&backlog)
                    };
                    Ok(())
                }
                1 => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-unfinished").fmt(),
                    )))
                }
                2 => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-build").fmt(),
                    )))
                }
                3 => {
                    if !CONF.get_bool("verbose")? {
                        dump_backlog(&backlog);
                    }
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-target").fmt(),
                    )))
                }
                _ => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-unknown").fmt(),
                    )))
                }
            }
        }
        _ => Err(Box::new(io::Error::new(
            io::ErrorKind::InvalidInput,
            LocalText::new("make-error").fmt(),
        ))),
    }
}

fn dump_backlog(backlog: &[String]) {
    let start = LocalText::new("make-backlog-start").fmt();
    eprintln!("{} {}", "┖┄".cyan(), start);
    for line in backlog.iter() {
        eprintln!("{}", line);
    }
    let end = LocalText::new("make-backlog-end").fmt();
    eprintln!("{} {}", "┎┄".cyan(), end);
}

fn report_line(line: &str) {
    eprintln!("{} {}", "┠╎".cyan(), line.dimmed());
}

fn report_start(target: &str) {
    let text = LocalText::new("make-report-start")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.yellow());
}

fn report_end(target: &str) {
    let text = LocalText::new("make-report-end")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.green());
}

fn report_fail(target: &str) {
    let text = LocalText::new("make-report-fail")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.red());
}
<|endoftext|>


<commit_before>//! Page management.
//!
//! Pages are virtual data units of size 4088 bytes. They're represented on disk somewhat
//! non-obviously, since clusters can hold more than one page at once (compression). Every cluster
//! will maximize the number of pages held and when it's filled up, a new cluster will be fetched.

/// The size (in bytes) of the metacluster header.
const METACLUSTER_HEADER: usize = 8;
/// The size (in bytes) of the metacluster's non-checksum section.
const METACLUSTER_SIZE: usize = disk::SECTOR - METACLUSTER_HEADER;

quick_error! {
    /// A page management error.
    enum Error {
        /// No clusters left in the freelist.
        ///
        /// This is the equivalent to OOM, but with disk space.
        OutOfClusters {
            description("Out of free clusters.")
        }
        /// The checksum of the data and the provided checksum does not match.
        ///
        /// This indicates some form of data corruption.
        ChecksumMismatch {
            cluster: cluster::Pointer,
            /// The checksum of the data.
            expected: u64,
            /// The expected/stored value of the checksum.
            found: u64,
        } {
            display("Mismatching checksums in cluster {} - expected {:x}, found {:x}.", cluster, expected, found)
            description("Mismatching checksum.")
        }
        /// The compressed data is invalid and cannot be decompressed.
        ///
        /// Multiple reasons exists for this to happen:
        ///
        /// 1. The compression configuration option has been changed without recompressing clusters.
        /// 2. Silent data corruption occured, and did the unlikely thing to has the right checksum.
        /// 3. There is a bug in compression or decompression.
        InvalidCompression {
            cluster: cluster::Pointer,
        } {
            display("Unable to decompress data from cluster {}.", cluster)
            description("Unable to decompress data.")
        }
        /// A disk error.
        Disk(err: disk::Error) {
            from()
            description("Disk I/O error")
            display("Disk I/O error: {}", err)
        }
    }
}

/// A state of a page manager.
struct State {
    /// The state block.
    ///
    /// The state block stores the state of the file system including allocation state,
    /// configuration, and more.
    state_block: state_block::StateBlock,
    /// The first chunk of the freelist.
    ///
    /// This list is used as the allocation primitive of TFS. It is a simple freelist-based extent
    /// allocation system, but there is one twist: To optimize the data locality, the list is
    /// unrolled.
    ///
    /// The first element (if any) points to _another_ freelist chunk (a "metacluster"), which can
    /// be used to traverse to the next metacluster when needed.
    freelist: Vec<cluster::Pointer>,
}

/// The page manager.
///
/// This is the center point of the I/O stack, providing allocation, deallocation, compression,
/// etc. It manages the clusters (with the page abstraction) and caches the disks.
struct Manager<D> {
    /// The cached disk header.
    ///
    /// The disk header contains various very basic information about the disk and how to interact
    /// with it.
    ///
    /// In reality, we could fetch this from the `disk` field as-we-go, but that hurts performance,
    /// so we cache it in memory.
    header: header::DiskHeader,
    /// The inner disk.
    disk: Cache<D>,
    /// The state of the manager.
    state: State,
    /// The state of the manager on the time of last cache commit.
    ///
    /// This contains the state of the page manager upon the last cache commit (pipeline flush). It
    /// is used to roll back the page manager when an error occurs.
    committed_state: State,
}

impl<D: Disk> Manager<D> {
    /// Commit the transactions in the pipeline to the cache.
    ///
    /// This runs over the transactions in the pipeline and applies them to the cache. In a sense,
    /// it can be seen as a form of checkpoint as you can revert to the last commit through
    /// `.revert()`, as it stores the old state.
    fn commit(&mut self) {
        // Update the stored committed state to the current state, which we will commit.
        self.committed_state = self.state.clone();
        // Commit the cache pipeline.
        self.disk.commit();
    }

    /// Revert to the last commit.
    ///
    /// This will reset the state to after the previous cache commit.
    fn revert(&mut self) {
        // Revert the state to when it was committed last time.
        self.state = self.committed_state.clone();
        // Revert the cache pipeline.
        self.disk.revert();
    }

    /// Calculate the checksum of some buffer, based on the user configuration.
    fn checksum(&self, buf: &[u8]) -> u64 {
        self.state.state_block.checksum_algorithm.hash(buf)
    }

    /// Compress some data based on the compression configuration option.
    ///
    /// This compresses `source` into `target` based on the chosen configuration method, defined in
    /// the state block.
    fn compress(&self, source: &[u8], target: &mut Vec<u8>) {
        match self.state.state_block.compression_algorithm {
            // Memcpy as a compression algorithm!!!11!
            CompressionAlgorithm::Identity => target.extend_from_slice(source),
            // Compress via LZ4.
            CompressionAlgorithm::Lz4 => lz4_compress::compress_into(source, target),
        }
    }

    /// Decompress some data based on the compression configuration option.
    ///
    /// This decompresses `source` into `target` based on the chosen configuration method, defined
    /// in the state block.
    fn decompress(&self, source: &[u8], target: &mut Vec<u8>) -> Result<(), Error> {
        match self.state.state_block.compression_algorithm {
            // Memcpy as a compression algorithm!!!11!
            CompressionAlgorithm::Identity => target.extend_from_slice(source),
            // Decompress from LZ4.
            CompressionAlgorithm::Lz4 => lz4_compress::decompress_from(source, target)?,
        }

        Ok(())
    }

    /// Queue a state block flush.
    ///
    /// This queues a new transaction flushing the state block.
    fn queue_state_block_flush(&mut self) {
        self.disk.queue(self.header.state_block_address, self.state.state_block.into());
    }

    /// Queue a freelist head flush.
    ///
    /// This queues a new transaction flushing the freelist head.
    fn queue_freelist_head_flush(&mut self) {
        // Start with an all-null cluster buffer.
        let mut buf = Box::new([0; disk::SECTOR_SIZE]);

        // Write every pointer of the freelist into the buffer.
        for (n, i) in self.free.iter().enumerate() {
            LittleEndian::write(&mut buf[cluster::POINTER_SIZE * i + METACLUSTER_HEADER..], i);
        }

        // Checksum the non-checksum part of the buffer, and write it at the start of the buffer.
        LittleEndian::write(&mut buf, self.checksum(&buf[2..]));

        // Queue the write of the updated buffer.
        self.disk.queue(self.state.state_block.freelist_head, buf);
    }

    /// Queue a pop from the freelist.
    ///
    /// This adds a new transaction to the cache pipeline, which will pop from the top of the
    /// freelist and return the result.
    fn queue_freelist_pop(&mut self) -> Result<cluster::Pointer, Error> {
        // Pop from the metacluster.
        if let Some(cluster) = self.state.freelist.pop() {
            if self.freelist.head.free.is_empty() {
                // The head metacluster is exhausted, so we load the next metacluster (specified to be
                // the last pointer in the metacluster), i.e. `cluster`. The old metacluster is then
                // used as the popped cluster.
                mem::swap(&mut self.state.state_block.cluster, &mut cluster);
                self.state.load_freelist(self.disk.read(self.state_block.freelist_head)?);

                // We've updated the state block, so we queue a flush to the disk.
                self.queue_state_block_flush();
            } else {
                // Since the freelist head was changed after the pop, we queue a flush.
                self.queue_freelist_head_flush();
            }

            Ok(cluster)
        } else {
            // We ran out of clusters :(.
            Err(Error::OutOfClusters)
        }
    }

    /// Queue a push to the freelist.
    ///
    /// This adds a new transaction to the cache pipeline, which will push some free cluster to the
    /// top of the freelist.
    fn queue_freelist_push(&mut self, cluster: cluster::Pointer) -> Result<(), Error> {
        if self.state.freelist.len() == METACLUSTER_SIZE / cluster::POINTER_SIZE {
            // The freelist head is full, and therefore we use following algorithm:
            //
            // 1. Create a new metacluster at `cluster`.
            // 2. Link said metacluster to the old metacluster.
            // 3. Queue a flush.

            // Clear the in-memory freelist head mirror.
            self.state.freelist.clear();
            // Put the link to the old freelist head into the new metacluster.
            self.state.freelist.push(state_block.freelist_head);

            // Update the freelist head pointer to point to the new metacluster.
            self.state.state_block.freelist_head = cluster;
            // Queue a flush of the new freelist head. This won't leave the system in an
            // inconsistent state as it merely creates a new metacluster, which is first linked
            // later. If the state block flush fails, the metacluster will merely be an orphan
            // cluster, and therefore simply leaked space.
            self.queue_freelist_head_flush();
            // Queue a flush of the state block (or, in particular, the freelist head pointer).
            // This is completely consistent as the freelist head must flush before, thus rendering
            // the pointed cluster a valid metacluster.
            self.queue_state_block_flush();
        } else {
            // There is space for more clusters in the head metacluster.

            // Push the cluster pointer to the freelist head.
            self.state.freelist.push(cluster);
            // Queue a flush of the new freelist head.
            self.queue_freelist_head_flush();

            // lulz @ these comments. like shit, ticki, they add basically nothing you fuking dumb
            // monkey. seriously stop it
        }
    }
}
<commit_msg>Add page allocation.<commit_after>//! Page management.
//!
//! Pages are virtual data units of size 4088 bytes. They're represented on disk somewhat
//! non-obviously, since clusters can hold more than one page at once (compression). Every cluster
//! will maximize the number of pages held and when it's filled up, a new cluster will be fetched.

/// The size (in bytes) of the metacluster header.
const METACLUSTER_HEADER: usize = 8;
/// The size (in bytes) of the metacluster's non-header section.
const METACLUSTER_SIZE: usize = disk::SECTOR - METACLUSTER_HEADER;
/// The size (in bytes) of the data cluster header.
const DATA_CLUSTER_HEADER: usize = 2;
/// The size (in bytes) of the data cluster's non-header section.
const DATA_CLUSTER_SIZE: usize = disk::SECTOR - DATA_CLUSTER_HEADER;

quick_error! {
    /// A page management error.
    enum Error {
        /// No clusters left in the freelist.
        ///
        /// This is the equivalent to OOM, but with disk space.
        OutOfClusters {
            description("Out of free clusters.")
        }
        /// The checksum of the data and the provided checksum does not match.
        ///
        /// This indicates some form of data corruption.
        ChecksumMismatch {
            cluster: cluster::Pointer,
            /// The checksum of the data.
            expected: u64,
            /// The expected/stored value of the checksum.
            found: u64,
        } {
            display("Mismatching checksums in cluster {} - expected {:x}, found {:x}.", cluster, expected, found)
            description("Mismatching checksum.")
        }
        /// The compressed data is invalid and cannot be decompressed.
        ///
        /// Multiple reasons exists for this to happen:
        ///
        /// 1. The compression configuration option has been changed without recompressing clusters.
        /// 2. Silent data corruption occured, and did the unlikely thing to has the right checksum.
        /// 3. There is a bug in compression or decompression.
        InvalidCompression {
            cluster: cluster::Pointer,
        } {
            display("Unable to decompress data from cluster {}.", cluster)
            description("Unable to decompress data.")
        }
        /// A disk error.
        Disk(err: disk::Error) {
            from()
            description("Disk I/O error")
            display("Disk I/O error: {}", err)
        }
    }
}

/// A state of a page manager.
struct State {
    /// The state block.
    ///
    /// The state block stores the state of the file system including allocation state,
    /// configuration, and more.
    state_block: state_block::StateBlock,
    /// The first chunk of the freelist.
    ///
    /// This list is used as the allocation primitive of TFS. It is a simple freelist-based extent
    /// allocation system, but there is one twist: To optimize the data locality, the list is
    /// unrolled.
    ///
    /// The first element (if any) points to _another_ freelist chunk (a "metacluster"), which can
    /// be used to traverse to the next metacluster when needed.
    freelist: Vec<cluster::Pointer>,
    /// The last allocated cluster.
    last_cluster: cluster::Pointer,
    /// The last allocated cluster's data decompressed.
    ///
    /// This is used for packing pages into the cluster, by appending the new page to this vector
    /// and then compressing it to see if it fits into the cluster. If it fails to fit, the vector
    /// is reset and a new cluster is allocated.
    last_cluster_data: Vec<u8>,
}

/// The page manager.
///
/// This is the center point of the I/O stack, providing allocation, deallocation, compression,
/// etc. It manages the clusters (with the page abstraction) and caches the disks.
struct Manager<D> {
    /// The cached disk header.
    ///
    /// The disk header contains various very basic information about the disk and how to interact
    /// with it.
    ///
    /// In reality, we could fetch this from the `disk` field as-we-go, but that hurts performance,
    /// so we cache it in memory.
    header: header::DiskHeader,
    /// The inner disk.
    disk: Cache<D>,
    /// The state of the manager.
    state: State,
    /// The state of the manager on the time of last cache commit.
    ///
    /// This contains the state of the page manager upon the last cache commit (pipeline flush). It
    /// is used to roll back the page manager when an error occurs.
    committed_state: State,
}

impl<D: Disk> Manager<D> {
    /// Commit the transactions in the pipeline to the cache.
    ///
    /// This runs over the transactions in the pipeline and applies them to the cache. In a sense,
    /// it can be seen as a form of checkpoint as you can revert to the last commit through
    /// `.revert()`, as it stores the old state.
    fn commit(&mut self) {
        // Update the stored committed state to the current state, which we will commit.
        self.committed_state = self.state.clone();
        // Commit the cache pipeline.
        self.disk.commit();
    }

    /// Revert to the last commit.
    ///
    /// This will reset the state to after the previous cache commit.
    fn revert(&mut self) {
        // Revert the state to when it was committed last time.
        self.state = self.committed_state.clone();
        // Revert the cache pipeline.
        self.disk.revert();
    }

    /// Allocate a page.
    ///
    /// This adds a transaction to the cache pipeline to allocate a page. It can be committed
    /// through `.commit()`.
    fn alloc(&mut self, buf: &[u8]) -> Result<Pointer, Error> {
        // Allocate a buffer for constructing the cluster.
        let mut cluster = vec![0; DATA_CLUSTER_HEADER];
        // Extend the last allocated cluster with the new page.
        self.state.last_cluster_data.extend_from_slice(buf);
        // Compress the last allocated cluster.
        self.compress(self.state.last_cluster_data, &mut cluster);

        if cluster.len() <= disk::SECTOR_SIZE {
            // The pages could fit in the cluster.

            // Pad with zeros until the sector is full.
            while cluster.len() != disk::SECTOR_SIZE {
                cluster.push(0);
            }

            // Calculate and write the checksum.
            LittleEndian::write(&mut cluster, self.checksum(cluster[DATA_CLUSTER_HEADER..]) as u16);
            // Set the compression flag in the checksum field.
            cluster[1] <<= 1;
            cluster[1] |= 1;

            // Queue the write of the recompress cluster.
            self.state.queue(self.state.last_cluster, cluster.into_boxed_slice());
        } else {
            // Unable to fit the pages into the cluster.

            // Truncate the unusable compressed buffer.
            cluster.truncate(DATA_CLUSTER_HEADER);
            // Extend the cluster with the buffer to allocate.
            cluster.extend_from_slice(&buf);

            // Calculate and write the checksum.
            LittleEndian::write(&mut cluster, self.checksum(cluster[DATA_CLUSTER_HEADER..]) as u16);
            // Set the compression flag in the checksum field to zero (i.e. uncompressed).
            cluster[1] <<= 1;

            // We cannot fit more into the last allocated cluster, so we clear it.
            self.state.last_cluster_data.clear();
            // Update it with the new given data.
            self.state.last_cluster_data.extend_from_slice(&buf);

            // Pop from the freelist and set this as the new last allocated cluster.
            self.state.last_cluster = self.queue_freelist_pop()?;

            // Queue a write to the new cluster.
            self.disk.queue(self.state.last_cluster, cluster);
        }
    }

    /// Calculate the checksum of some buffer, based on the user configuration.
    fn checksum(&self, buf: &[u8]) -> u64 {
        self.state.state_block.checksum_algorithm.hash(buf)
    }

    /// Compress some data based on the compression configuration option.
    ///
    /// This compresses `source` into `target` based on the chosen configuration method, defined in
    /// the state block.
    fn compress(&self, source: &[u8], target: &mut Vec<u8>) {
        match self.state.state_block.compression_algorithm {
            // Memcpy as a compression algorithm!!!11!
            CompressionAlgorithm::Identity => target.extend_from_slice(source),
            // Compress via LZ4.
            CompressionAlgorithm::Lz4 => lz4_compress::compress_into(source, target),
        }
    }

    /// Decompress some data based on the compression configuration option.
    ///
    /// This decompresses `source` into `target` based on the chosen configuration method, defined
    /// in the state block.
    fn decompress(&self, source: &[u8], target: &mut Vec<u8>) -> Result<(), Error> {
        match self.state.state_block.compression_algorithm {
            // Memcpy as a compression algorithm!!!11!
            CompressionAlgorithm::Identity => target.extend_from_slice(source),
            // Decompress from LZ4.
            CompressionAlgorithm::Lz4 => lz4_compress::decompress_from(source, target)?,
        }

        Ok(())
    }

    /// Queue a state block flush.
    ///
    /// This queues a new transaction flushing the state block.
    fn queue_state_block_flush(&mut self) {
        self.disk.queue(self.header.state_block_address, self.state.state_block.into());
    }

    /// Queue a freelist head flush.
    ///
    /// This queues a new transaction flushing the freelist head.
    fn queue_freelist_head_flush(&mut self) {
        // Start with an all-null cluster buffer.
        let mut buf = Box::new([0; disk::SECTOR_SIZE]);

        // Write every pointer of the freelist into the buffer.
        for (n, i) in self.free.iter().enumerate() {
            LittleEndian::write(&mut buf[cluster::POINTER_SIZE * i + METACLUSTER_HEADER..], i);
        }

        // Checksum the non-checksum part of the buffer, and write it at the start of the buffer.
        LittleEndian::write(&mut buf, self.checksum(&buf[2..]));

        // Queue the write of the updated buffer.
        self.disk.queue(self.state.state_block.freelist_head, buf);
    }

    /// Queue a pop from the freelist.
    ///
    /// This adds a new transaction to the cache pipeline, which will pop from the top of the
    /// freelist and return the result.
    fn queue_freelist_pop(&mut self) -> Result<cluster::Pointer, Error> {
        // Pop from the metacluster.
        if let Some(cluster) = self.state.freelist.pop() {
            if self.freelist.head.free.is_empty() {
                // The head metacluster is exhausted, so we load the next metacluster (specified to be
                // the last pointer in the metacluster), i.e. `cluster`. The old metacluster is then
                // used as the popped cluster.
                mem::swap(&mut self.state.state_block.cluster, &mut cluster);
                self.state.load_freelist(self.disk.read(self.state_block.freelist_head)?);

                // We've updated the state block, so we queue a flush to the disk.
                self.queue_state_block_flush();
            } else {
                // Since the freelist head was changed after the pop, we queue a flush.
                self.queue_freelist_head_flush();
            }

            Ok(cluster)
        } else {
            // We ran out of clusters :(.
            Err(Error::OutOfClusters)
        }
    }

    /// Queue a push to the freelist.
    ///
    /// This adds a new transaction to the cache pipeline, which will push some free cluster to the
    /// top of the freelist.
    fn queue_freelist_push(&mut self, cluster: cluster::Pointer) -> Result<(), Error> {
        if self.state.freelist.len() == METACLUSTER_SIZE / cluster::POINTER_SIZE {
            // The freelist head is full, and therefore we use following algorithm:
            //
            // 1. Create a new metacluster at `cluster`.
            // 2. Link said metacluster to the old metacluster.
            // 3. Queue a flush.

            // Clear the in-memory freelist head mirror.
            self.state.freelist.clear();
            // Put the link to the old freelist head into the new metacluster.
            self.state.freelist.push(state_block.freelist_head);

            // Update the freelist head pointer to point to the new metacluster.
            self.state.state_block.freelist_head = cluster;
            // Queue a flush of the new freelist head. This won't leave the system in an
            // inconsistent state as it merely creates a new metacluster, which is first linked
            // later. If the state block flush fails, the metacluster will merely be an orphan
            // cluster, and therefore simply leaked space.
            self.queue_freelist_head_flush();
            // Queue a flush of the state block (or, in particular, the freelist head pointer).
            // This is completely consistent as the freelist head must flush before, thus rendering
            // the pointed cluster a valid metacluster.
            self.queue_state_block_flush();
        } else {
            // There is space for more clusters in the head metacluster.

            // Push the cluster pointer to the freelist head.
            self.state.freelist.push(cluster);
            // Queue a flush of the new freelist head.
            self.queue_freelist_head_flush();

            // lulz @ these comments. like shit, ticki, they add basically nothing you fuking dumb
            // monkey. seriously stop it
        }
    }
}
<|endoftext|>


<commit_before>extern crate ructe;
extern crate core;
fn parse_linkmark(text:&[u8])->(&[u8], Vec<u8>){

    let mut contents:Option<(usize, usize)> = None;
    let mut link :(Option<usize>, Option<usize>) = (None, None);
    for (i,ch) in text.iter().enumerate(){
        if let None = contents{
            if *ch == b']'{
                contents = Some((1,i));
            }
        }
        else{
            if *ch == b'('{
                link.0 = Some(i + 1);
            }
            if let Some( _ ) = link.0{
                if *ch == b')'{
                    link.1 = Some(i);
                    break;
                }
            }
        }
    }
    let r = match (link.0, link.1, contents){
        (Some(l1), Some(l2), Some(c))=>(l1,l2,c.0, c.1),
        _=>{
            return (&text[1..],vec![b'[']);
        }
    };
    let mut link = Vec::<u8>::new();
    let mut contents = Vec::<u8>::new();
    for ch in &text[r.0..r.1]{
        if *ch == b'"'{
            link.extend(b"%22");
        }
        else{
            link.push(*ch);
        }
    }
    for ch in &text[r.2 .. r.3]{
        match *ch{
            b'<'=>{
                contents.extend(b"<");
            },
            b'>'=>{
                contents.extend(b">");
            }
            _=>{
                contents.push(*ch);
            }
        }
    }
    let mut msg = Vec::<u8>::new();
    msg.extend(b"");
    msg.append(&mut contents);
    msg.extend(b"");
    return (&text[(r.1)+1..], msg);
}
fn parse_nomark(text:&[u8])->(&[u8], Vec<u8>){
    let mut res = Vec::<u8>::new();
    let mut t = &text[2..];

    while t.len() >= 2{
        if &t[..2] == b"}}"{
            return (&t[2..], res); //(&t[2..], format!("{}",link, res));
        }
        let s = match &t[0..1]{
            b"<"=>b"<",
            b">"=>b">",
            c@_=>c
        } ;
        res.extend(s);
        t = &t[1..];
    }

    return (&text[1..],vec![b'{']);
}
fn parse_strikethrough<'a>(text:&'a [u8], tag:&'a [u8])->(&'a[u8], Vec<u8>){
    let mut res = Vec::<u8>::new();
    let mut t = &text[2..];

    while t.len() > 0{
        if t.len() >= 2{
            if &t[..2] == tag{
                let mut msg = Vec::<u8>::new();
                msg.extend(b"");
                msg.append(&mut res);
                msg.extend(b"");
                return (&t[2..], msg); //(&t[2..], format!("{}",link, res));
            }
            if &t[..2] == b"__"{
                let mut r = parse_bold(t, &t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
            if &t[..2] == b"**"{
                let mut r = parse_bold(t, &t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
        }
        if &t[..1] == b"["{
            let mut r = parse_linkmark(t);
            res.append(&mut r.1);
            t = r.0;
            continue;
        }

        res.push(t[0]);
        t = &t[1..];
    }

    return (&text[1..],Vec::from(&tag[..1]));
}
fn parse_bold<'a>(text:&'a [u8], tag:&'a [u8])->(&'a[u8], Vec<u8>){
    let mut res = Vec::<u8>::new();
    let mut t = &text[2..];

    while t.len() > 0{
        if t.len() >= 2{
            if &t[..2] == tag{
                let mut msg = Vec::<u8>::new();
                msg.extend(b"");
                msg.append(&mut res);
                msg.extend(b"");
                return (&t[2..], msg); //(&t[2..], format!("{}",link, res));
            }
            if &t[..2] == b"~~"{
                let mut r = parse_strikethrough(t,&t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
            if &t[..2] == b"--"{
                let mut r = parse_strikethrough(t, &t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
        }
        if &t[..1] == b"["{
            let mut r = parse_linkmark(t);
            res.append(&mut r.1);
            t = r.0;
            continue;
        }

        res.push(t[0]);
        t = &t[1..];
    }

    return (&text[1..],Vec::from(&tag[..1]));
}
fn parse_blackqoute(text:&[u8])->(&[u8], Vec<u8>){
    let mut t = text;
    let mut html = Vec::<u8>::new();
    return (t, html);
}
fn parse(text:&str)->Vec<u8>{
    let mut res = Vec::<u8>::new();
    let mut t:&[u8] = text.as_bytes();
    res.extend(b"
");
    while t.len() != 0{
        if t.len() > 2{
            if (t.len() == text.as_bytes().len() && &t[..1] == b">") ||
            &t[..2] == b"\n>"{

            }
            if &t[..2] == b"\n\n"{
                res.extend(b"

");
                t = &t[2..];
                continue;
            }
            if &t[..2] == b"  "{
                res.extend(b"
");
                t = &t[2..];
                continue;
            }
            if &t[..2] == b"~~"{
                let mut r = parse_strikethrough(t, &t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
            if &t[..2] == b"--"{
                let mut r = parse_strikethrough(t, &t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
            if &t[..2] == b"{{"{
                let mut r = parse_nomark(t);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
            if &t[..2] == b"__"{
                let mut r = parse_bold(t,&t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
            if &t[..2] == b"**"{
                let mut r = parse_bold(t,&t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
        }
        if &t[..1] == b"["{
            let mut r = parse_linkmark(t);
            res.append(&mut r.1);
            t = r.0;
            continue;
        }

        let s = match &t[0..1]{
            b"<"=>b"<",
            b">"=>b">",
            c@_=>c
        } ;
        res.extend(s);
        t = &t[1..];
    }
    res.extend(b"
");
    return res;
    //return String::from_utf8(res).unwrap();
}

pub struct Markdown{
    obj:*const str
}
use std::io::{Write,Error};
use std::mem::transmute;
impl Markdown{
    pub fn to_html(&self, out: &mut Write) -> Result<(),Error>{
        let text:&str =
        unsafe{transmute::<_, &str>(self.obj)};
        out.write_all(&parse(text));
        Ok(())
    }
}
pub fn render(obj:&str)->Markdown{
    Markdown{obj:obj}
}<commit_msg>html tag strip<commit_after>extern crate ructe;
extern crate core;
fn parse_linkmark(text:&[u8])->(&[u8], Vec<u8>){

    let mut contents:Option<(usize, usize)> = None;
    let mut link :(Option<usize>, Option<usize>) = (None, None);
    for (i,ch) in text.iter().enumerate(){
        if let None = contents{
            if *ch == b']'{
                contents = Some((1,i));
            }
        }
        else{
            if *ch == b'('{
                link.0 = Some(i + 1);
            }
            if let Some( _ ) = link.0{
                if *ch == b')'{
                    link.1 = Some(i);
                    break;
                }
            }
        }
    }
    let r = match (link.0, link.1, contents){
        (Some(l1), Some(l2), Some(c))=>(l1,l2,c.0, c.1),
        _=>{
            return (&text[1..],vec![b'[']);
        }
    };
    let mut link = Vec::<u8>::new();
    let mut contents = Vec::<u8>::new();
    for ch in &text[r.0..r.1]{
        if *ch == b'"'{
            link.extend(b"%22");
        }
        else{
            link.push(*ch);
        }
    }
    for ch in &text[r.2 .. r.3]{
        match *ch{
            b'<'=>{
                contents.extend(b"<");
            },
            b'>'=>{
                contents.extend(b">");
            }
            _=>{
                contents.push(*ch);
            }
        }
    }
    let mut msg = Vec::<u8>::new();
    msg.extend(b"");
    msg.append(&mut contents);
    msg.extend(b"");
    return (&text[(r.1)+1..], msg);
}
fn parse_nomark(text:&[u8])->(&[u8], Vec<u8>){
    let mut res = Vec::<u8>::new();
    let mut t = &text[2..];

    while t.len() >= 2{
        if &t[..2] == b"}}"{
            return (&t[2..], res); //(&t[2..], format!("{}",link, res));
        }
        let s = match &t[0..1]{
            b"<"=>b"<",
            b">"=>b">",
            c@_=>c
        } ;
        res.extend(s);
        t = &t[1..];
    }

    return (&text[1..],vec![b'{']);
}
fn parse_strikethrough<'a>(text:&'a [u8], tag:&'a [u8])->(&'a[u8], Vec<u8>){
    let mut res = Vec::<u8>::new();
    let mut t = &text[2..];

    while t.len() > 0{
        if t.len() >= 2{
            if &t[..2] == tag{
                let mut msg = Vec::<u8>::new();
                msg.extend(b"");
                msg.append(&mut res);
                msg.extend(b"");
                return (&t[2..], msg); //(&t[2..], format!("{}",link, res));
            }
            if &t[..2] == b"__"{
                let mut r = parse_bold(t, &t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
            if &t[..2] == b"**"{
                let mut r = parse_bold(t, &t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
        }
        if &t[..1] == b"["{
            let mut r = parse_linkmark(t);
            res.append(&mut r.1);
            t = r.0;
            continue;
        }

        let s = match &t[0..1]{
            b"<"=>b"<",
            b">"=>b">",
            c@_=>c
        } ;
        res.extend(s);
        t = &t[1..];
    }

    return (&text[1..],Vec::from(&tag[..1]));
}
fn parse_bold<'a>(text:&'a [u8], tag:&'a [u8])->(&'a[u8], Vec<u8>){
    let mut res = Vec::<u8>::new();
    let mut t = &text[2..];

    while t.len() > 0{
        if t.len() >= 2{
            if &t[..2] == tag{
                let mut msg = Vec::<u8>::new();
                msg.extend(b"");
                msg.append(&mut res);
                msg.extend(b"");
                return (&t[2..], msg); //(&t[2..], format!("{}",link, res));
            }
            if &t[..2] == b"~~"{
                let mut r = parse_strikethrough(t,&t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
            if &t[..2] == b"--"{
                let mut r = parse_strikethrough(t, &t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
        }
        if &t[..1] == b"["{
            let mut r = parse_linkmark(t);
            res.append(&mut r.1);
            t = r.0;
            continue;
        }

        let s = match &t[0..1]{
            b"<"=>b"<",
            b">"=>b">",
            c@_=>c
        } ;
        res.extend(s);
        t = &t[1..];
    }

    return (&text[1..],Vec::from(&tag[..1]));
}
fn parse_blackqoute(text:&[u8])->(&[u8], Vec<u8>){
    let mut t = text;
    let mut html = Vec::<u8>::new();
    return (t, html);
}
fn parse(text:&str)->Vec<u8>{
    let mut res = Vec::<u8>::new();
    let mut t:&[u8] = text.as_bytes();
    res.extend(b"
");
    while t.len() != 0{
        if t.len() > 2{
            if (t.len() == text.as_bytes().len() && &t[..1] == b">") ||
            &t[..2] == b"\n>"{

            }
            if &t[..2] == b"\n\n"{
                res.extend(b"

");
                t = &t[2..];
                continue;
            }
            if &t[..2] == b"  "{
                res.extend(b"
");
                t = &t[2..];
                continue;
            }
            if &t[..2] == b"~~"{
                let mut r = parse_strikethrough(t, &t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
            if &t[..2] == b"--"{
                let mut r = parse_strikethrough(t, &t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
            if &t[..2] == b"{{"{
                let mut r = parse_nomark(t);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
            if &t[..2] == b"__"{
                let mut r = parse_bold(t,&t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
            if &t[..2] == b"**"{
                let mut r = parse_bold(t,&t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
        }
        if &t[..1] == b"["{
            let mut r = parse_linkmark(t);
            res.append(&mut r.1);
            t = r.0;
            continue;
        }

        let s = match &t[0..1]{
            b"<"=>b"<",
            b">"=>b">",
            c@_=>c
        } ;
        res.extend(s);
        t = &t[1..];
    }
    res.extend(b"
");
    return res;
    //return String::from_utf8(res).unwrap();
}

pub struct Markdown{
    obj:*const str
}
use std::io::{Write,Error};
use std::mem::transmute;
impl Markdown{
    pub fn to_html(&self, out: &mut Write) -> Result<(),Error>{
        let text:&str =
        unsafe{transmute::<_, &str>(self.obj)};
        out.write_all(&parse(text));
        Ok(())
    }
}
pub fn render(obj:&str)->Markdown{
    Markdown{obj:obj}
}<|endoftext|>