<commit_before>// FIXME: add the support of transient objects

use notify::{self, RecommendedWatcher, Watcher};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{Receiver, Sender, channel};
use std::thread;
use time::precise_time_s;

use id::Id;
use model::Model;
use object::Object;
use shader::Program;
use spline::Spline;

/// Class of types that can be loaded.
pub trait Load<'a>: Sized {
  /// Arguments passed at loading.
  type Args;

  // TODO: see whether we can use something with From/Into instead, so that we can use lambdas.
  fn load<P>(path: P, cache: &mut Cache<'a>, args: Self::Args) -> Result<Self, LoadError> where P: AsRef<Path>;
}

/// Class of types that can be reloaded.
///
/// The idea is to simply recover the arguments used in `Load::load`.
pub trait Reload<'a>: Load<'a> {
  fn reload_args(&self) -> Self::Args;
}

/// Default implementation for types which are loaded without any arguments.
impl<'a, T> Reload<'a> for T where T: Load<'a, Args=()> {
  fn reload_args(&self) -> Self::Args {
    ()
  }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum LoadError {
  FileNotFound(PathBuf, String),
  ParseFailed(String),
  ConversionFailed(String)
}

type Timestamp = f64;

/// Time to await after a resource update to establish that it should be reloaded.
const UPDATE_AWAIT_TIME: Timestamp = 0.1; // 100ms

struct CacheBlock<'a, T> where T: 'a {
  data: Vec<(T, PathBuf, (Receiver<Timestamp>, f64))>,
  ids: HashMap<String, Id<'a, T>>,
}

impl<'a, T> CacheBlock<'a, T> {
  pub fn new() -> Self {
    CacheBlock {
      data: Vec::new(),
      ids: HashMap::new(),
    }
  }
}

macro_rules! cache_struct {
  ($l:tt, $($n:ident : $t:ty),*) => {
    pub struct Cache<$l> {
      senders: Arc<Mutex<HashMap<PathBuf, Sender<Timestamp>>>>,
      $(
        $n: CacheBlock<$l, $t>
      ),*
    }

    impl<$l> Cache<$l> {
      pub fn new<P>(root:P) -> Self where P: AsRef<Path> {
        let senders: Arc<Mutex<HashMap<PathBuf, Sender<Timestamp>>>> = Arc::new(Mutex::new(HashMap::new()));

        // start watcher thread
        {
          let senders = senders.clone();
          let root = root.as_ref().to_path_buf();
          let (wsx, wrx) = channel();
          let mut watcher: RecommendedWatcher = Watcher::new(wsx).unwrap();

          let _ = thread::spawn(move || {
            let _ = watcher.watch(root);

            for event in wrx.iter() {
              match event {
                notify::Event { path: Some(path), op: Ok(notify::op::WRITE) } => {
                  if let Some(sx) = senders.lock().unwrap().get(&path) {
                    sx.send(precise_time_s()).unwrap();
                  }
                },
                _ => {}
              }
            }
          });
        }

        Cache {
          senders: senders,
          $(
            $n: CacheBlock::new()
          ),*
        }
      }
    }
  }
}

pub trait Get<'a, T> where T: 'a + Reload<'a> {
  fn get_id(&mut self, name: &str, args: T::Args) -> Option<Id<'a, T>>;
  fn get_by_id(&mut self, id: &Id<'a, T>) -> Option<&T>;
  fn get(&mut self, name: &str, args: T::Args) -> Option<&T> {
    self.get_id(name, args).and_then(move |i| self.get_by_id(&i))
  }
}

macro_rules! impl_get_id {
  ($n:ident : $t:ty, $this:ident, $name:ident, $args: ident) => {{
    let path_str = format!("data/{}/{}", stringify!($n), $name);
    let path = Path::new(&path_str);

    match $this.$n.ids.get($name).cloned() {
      id@Some(..) => {
        deb!("cache hit for {}", path_str);
        id
      },
      None => {
        deb!("cache miss for {}", path_str);

        // specific loading
        if path.exists() {
          match <$t as Load>::load(&path, $this, $args) {
            Ok(resource) => {
              let path_buf = path.to_owned();

              // create the id if we have loaded the resource
              let id: Id<$t> = ($this.$n.data.len() as u32).into();

              // create a channel to notify any update later and register the sender for the
              // given path
              let (sx, rx) = channel();
              {
                let mut senders = $this.senders.lock().unwrap();
                senders.insert(path_buf.clone(), sx);
              }

              // add the resource to the list of loaded ones
              $this.$n.data.push((resource, path_buf.clone(), (rx, precise_time_s())));
              // cache the resource
              $this.$n.ids.insert($name.to_owned(), id.clone());

              Some(id)
            },
            Err(e) => {
              err!("unable to load resource from {}: {:?}", path_str, e);
              None
            }
          }
        } else { // path doesn’t exist
          err!("ressource at {} cannot be found", path_str);
          None
        }
      }
    }
  }}
}

macro_rules! impl_get_by_id {
  ($n:ident : $t:ty, $this:ident, $id:ident) => {{
    // synchronization
    let mut reload_args = None;

    if let Some(data) = $this.$n.data.get($id.id as usize) {
      match (data.2).0.try_recv() {
        Ok(timestamp) if timestamp - (data.2).1 >= UPDATE_AWAIT_TIME => {
          reload_args = Some((data.1.to_owned(), data.0.reload_args()));
        },
        _ => {}
      }
    } else {
      return None;
    }

    if let Some((path, args)) = reload_args {
      match <$t as Load>::load(&path, $this, args) {
        Ok(new_resource) => {
          // replace the current resource with the freshly loaded one
          deb!("reloaded resource from {:?}", path);
          $this.$n.data[$id.id as usize].0 = new_resource;
        },
        Err(e) => {
          warn!("reloading resource from {:?} has failed: {:?}", path, e);
        }
      }
    }

    $this.$n.data.get($id.id as usize).map(|r| &r.0)
  }}
}

macro_rules! impl_get_no_lifetime {
  ($n:ident : $t:ty) => {
    impl<'a> Get<'a, $t> for Cache<'a> {
      fn get_id(&mut self, name: &str, args: <$t as Load<'a>>::Args) -> Option<Id<'a, $t>> {
        impl_get_id!($n: $t, self, name, args)
      }

      fn get_by_id(&mut self, id: &Id<'a, $t>) -> Option<&$t> {
        impl_get_by_id!($n: $t, self, id)
      }
    }
  }
}

cache_struct!('a,
              models: Model,
              objects: Object<'a>,
              shader_programs: Program,
              splines: Spline<f32>);

impl_get_no_lifetime!(models: Model);
impl_get_no_lifetime!(shader_programs: Program);
impl_get_no_lifetime!(splines: Spline<f32>);

impl<'a> Get<'a, Object<'a>> for Cache<'a> {
  fn get_id(&mut self, name: &str, args: <Object<'a> as Load<'a>>::Args) -> Option<Id<'a, Object<'a>>> {
    impl_get_id!(objects: Object<'a>, self, name, args)
  }

  fn get_by_id(&mut self, id: &Id<'a, Object<'a>>) -> Option<&Object<'a>> {
    impl_get_by_id!(objects: Object<'a>, self, id)
  }
}
<commit_msg>Fixed path to shaders.<commit_after>// FIXME: add the support of transient objects

use notify::{self, RecommendedWatcher, Watcher};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{Receiver, Sender, channel};
use std::thread;
use time::precise_time_s;

use id::Id;
use model::Model;
use object::Object;
use shader::Program;
use spline::Spline;

/// Class of types that can be loaded.
pub trait Load<'a>: Sized {
  /// Arguments passed at loading.
  type Args;

  // TODO: see whether we can use something with From/Into instead, so that we can use lambdas.
  fn load<P>(path: P, cache: &mut Cache<'a>, args: Self::Args) -> Result<Self, LoadError> where P: AsRef<Path>;
}

/// Class of types that can be reloaded.
///
/// The idea is to simply recover the arguments used in `Load::load`.
pub trait Reload<'a>: Load<'a> {
  fn reload_args(&self) -> Self::Args;
}

/// Default implementation for types which are loaded without any arguments.
impl<'a, T> Reload<'a> for T where T: Load<'a, Args=()> {
  fn reload_args(&self) -> Self::Args {
    ()
  }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum LoadError {
  FileNotFound(PathBuf, String),
  ParseFailed(String),
  ConversionFailed(String)
}

type Timestamp = f64;

/// Time to await after a resource update to establish that it should be reloaded.
const UPDATE_AWAIT_TIME: Timestamp = 0.1; // 100ms

struct CacheBlock<'a, T> where T: 'a {
  data: Vec<(T, PathBuf, (Receiver<Timestamp>, f64))>,
  ids: HashMap<String, Id<'a, T>>,
}

impl<'a, T> CacheBlock<'a, T> {
  pub fn new() -> Self {
    CacheBlock {
      data: Vec::new(),
      ids: HashMap::new(),
    }
  }
}

macro_rules! cache_struct {
  ($l:tt, $($n:ident : $t:ty),*) => {
    pub struct Cache<$l> {
      senders: Arc<Mutex<HashMap<PathBuf, Sender<Timestamp>>>>,
      $(
        $n: CacheBlock<$l, $t>
      ),*
    }

    impl<$l> Cache<$l> {
      pub fn new<P>(root:P) -> Self where P: AsRef<Path> {
        let senders: Arc<Mutex<HashMap<PathBuf, Sender<Timestamp>>>> = Arc::new(Mutex::new(HashMap::new()));

        // start watcher thread
        {
          let senders = senders.clone();
          let root = root.as_ref().to_path_buf();
          let (wsx, wrx) = channel();
          let mut watcher: RecommendedWatcher = Watcher::new(wsx).unwrap();

          let _ = thread::spawn(move || {
            let _ = watcher.watch(root);

            for event in wrx.iter() {
              match event {
                notify::Event { path: Some(path), op: Ok(notify::op::WRITE) } => {
                  if let Some(sx) = senders.lock().unwrap().get(&path) {
                    sx.send(precise_time_s()).unwrap();
                  }
                },
                _ => {}
              }
            }
          });
        }

        Cache {
          senders: senders,
          $(
            $n: CacheBlock::new()
          ),*
        }
      }
    }
  }
}

pub trait Get<'a, T> where T: 'a + Reload<'a> {
  fn get_id(&mut self, name: &str, args: T::Args) -> Option<Id<'a, T>>;
  fn get_by_id(&mut self, id: &Id<'a, T>) -> Option<&T>;
  fn get(&mut self, name: &str, args: T::Args) -> Option<&T> {
    self.get_id(name, args).and_then(move |i| self.get_by_id(&i))
  }
}

macro_rules! impl_get_id {
  ($n:ident : $t:ty, $this:ident, $name:ident, $args: ident) => {{
    let path_str = format!("data/{}/{}", stringify!($n), $name);
    let path = Path::new(&path_str);

    match $this.$n.ids.get($name).cloned() {
      id@Some(..) => {
        deb!("cache hit for {}", path_str);
        id
      },
      None => {
        deb!("cache miss for {}", path_str);

        // specific loading
        if path.exists() {
          match <$t as Load>::load(&path, $this, $args) {
            Ok(resource) => {
              let path_buf = path.to_owned();

              // create the id if we have loaded the resource
              let id: Id<$t> = ($this.$n.data.len() as u32).into();

              // create a channel to notify any update later and register the sender for the
              // given path
              let (sx, rx) = channel();
              {
                let mut senders = $this.senders.lock().unwrap();
                senders.insert(path_buf.clone(), sx);
              }

              // add the resource to the list of loaded ones
              $this.$n.data.push((resource, path_buf.clone(), (rx, precise_time_s())));
              // cache the resource
              $this.$n.ids.insert($name.to_owned(), id.clone());

              Some(id)
            },
            Err(e) => {
              err!("unable to load resource from {}: {:?}", path_str, e);
              None
            }
          }
        } else { // path doesn’t exist
          err!("ressource at {} cannot be found", path_str);
          None
        }
      }
    }
  }}
}

macro_rules! impl_get_by_id {
  ($n:ident : $t:ty, $this:ident, $id:ident) => {{
    // synchronization
    let mut reload_args = None;

    if let Some(data) = $this.$n.data.get($id.id as usize) {
      match (data.2).0.try_recv() {
        Ok(timestamp) if timestamp - (data.2).1 >= UPDATE_AWAIT_TIME => {
          reload_args = Some((data.1.to_owned(), data.0.reload_args()));
        },
        _ => {}
      }
    } else {
      return None;
    }

    if let Some((path, args)) = reload_args {
      match <$t as Load>::load(&path, $this, args) {
        Ok(new_resource) => {
          // replace the current resource with the freshly loaded one
          deb!("reloaded resource from {:?}", path);
          $this.$n.data[$id.id as usize].0 = new_resource;
        },
        Err(e) => {
          warn!("reloading resource from {:?} has failed: {:?}", path, e);
        }
      }
    }

    $this.$n.data.get($id.id as usize).map(|r| &r.0)
  }}
}

macro_rules! impl_get_no_lifetime {
  ($n:ident : $t:ty) => {
    impl<'a> Get<'a, $t> for Cache<'a> {
      fn get_id(&mut self, name: &str, args: <$t as Load<'a>>::Args) -> Option<Id<'a, $t>> {
        impl_get_id!($n: $t, self, name, args)
      }

      fn get_by_id(&mut self, id: &Id<'a, $t>) -> Option<&$t> {
        impl_get_by_id!($n: $t, self, id)
      }
    }
  }
}

cache_struct!('a,
              models: Model,
              objects: Object<'a>,
              shaders: Program,
              splines: Spline<f32>);

impl_get_no_lifetime!(models: Model);
impl_get_no_lifetime!(shader_programs: Program);
impl_get_no_lifetime!(splines: Spline<f32>);

impl<'a> Get<'a, Object<'a>> for Cache<'a> {
  fn get_id(&mut self, name: &str, args: <Object<'a> as Load<'a>>::Args) -> Option<Id<'a, Object<'a>>> {
    impl_get_id!(objects: Object<'a>, self, name, args)
  }

  fn get_by_id(&mut self, id: &Id<'a, Object<'a>>) -> Option<&Object<'a>> {
    impl_get_by_id!(objects: Object<'a>, self, id)
  }
}
<|endoftext|>


<commit_before>use core::prelude::*;
use core::mem::transmute;

use con;
use con::write;
use cpu;
use dict::*;
use dlist::*;
use mem::heap_copy;
use start32;
use util::abort;

static log_add_pte : bool = false;

pub mod mapflag {
    pub static X : uint = 1;
    pub static W : uint = 2;
    pub static R : uint = 4;
    pub static RWX : uint = 7;
    // Anonymous memory allocated to zeroes on first use.
    pub static Anon : uint = 8;
    // handle is 0; offset is (paddr - vaddr)
    pub static Phys : uint = 16;
    // Physical memory allocated and locked at map time; and deallocated when
    // unmapped.
    pub static DMA : uint = Anon | Phys;
    pub static UserAllowed : uint = DMA | RWX;
}

// mapcard: the handle, offset and flags for the range of virtual addresses until
// the next card.
// 5 words:
// - 3 for dict_node w/ vaddr
// - 1 handle
// - 1 offset+flags
//   (since offsets must be page aligned we have 12 left-over bits)
// This structure is completely unrelated to the physical pages backing virtual
// memory - it represents each process' wishful thinking about how their memory
// should look. backings and sharings control physical memory.
pub struct MapCard {
    as_node : DictNode<uint, MapCard>,
    pub handle : uint,
    // .vaddr + .offset = handle-offset to be sent to backer on fault
    // For a direct physical mapping, paddr = .vaddr + .offset
    // .offset = handle-offset - vaddr
    // .offset = paddr - .vaddr
    // The low 12 bits contain flags.
    pub offset : uint,
}

impl DictItem<uint> for MapCard {
    fn node<'a>(&'a mut self) -> &'a mut DictNode<uint, MapCard> {
        return &mut self.as_node;
    }
}

impl MapCard {
    fn new(vaddr : uint, handle : uint, offset : uint) -> MapCard {
        MapCard { as_node: DictNode::new(vaddr), handle: handle, offset: offset }
    }

    pub fn vaddr(&self) -> uint {
        return self.as_node.key;
    }

    pub fn paddr(&self, vaddr : uint) -> uint {
        return vaddr + (self.offset & !0xfff);
    }

    pub fn flags(&self) -> uint {
        return self.offset & 0xfff;
    }

    fn same_addr(&self, other : &MapCard) -> bool {
        self.vaddr() == other.vaddr()
    }

    fn same(&self, other: &MapCard) -> bool {
        return self.handle == other.handle && self.offset == other.offset;
    }

    fn set(&mut self, other : &MapCard) {
        self.handle = other.handle;
        self.offset = other.offset;
    }
}

// backing: mapping *one page* to the place that page came from.
// Indexed by vaddr for the process that maps it. The vaddr includes flags, so
// look up by vaddr|0xfff.
// This is likely to exist once per physical page per process. Should be
// minimized.
// 6 words:
// - 3 words for dict_node w/ vaddr
// - 1 word for parent
// - 2 words for child-list links
//
// Could be reduced to 4 words: flags, parent, child-list links, if moving to
// an external dictionary. 32 bytes per page gives 128 entries in the last-level
// table. Flags could indicate how many levels that are required, and e.g. a
// very small process could have only one level, and map 128 pages at 0..512kB.
pub struct Backing {
    // Flags stored in low bits of vaddr/key!
    as_node : DictNode<uint, Backing>,
    // Pointer to parent sharing. Needed to unlink self when unmapping.
    // Could have room for flags (e.g. to let it be a paddr when we don't
    // need the parent - we might have a direct physical address mapping)
    parent_paddr : uint,
    // Space to participate in parent's list of remappings.
    child_node : DListNode<Backing>
}

impl DictItem<uint> for Backing {
    fn node<'a>(&'a mut self) -> &'a mut DictNode<uint, Backing> {
        return &mut self.as_node;
    }
}

impl DListItem for Backing {
    fn node<'a>(&'a mut self) -> &'a mut DListNode<Backing> {
        return &mut self.child_node;
    }
}

fn alloc_frame_paddr() -> uint {
    let p : *mut u8 = cpu().memory.alloc_frame_panic();
    p as uint - start32::kernel_base
}

impl Backing {
    fn new_phys(vaddrFlags : uint, phys_addr : uint) -> Backing {
        if phys_addr & 0xfff != 0 {
            abort("Bad physical address in new_phys");
        }
        Backing {
            as_node: DictNode::new(vaddrFlags),
            parent_paddr : phys_addr,
            child_node: DListNode::new(),
        }
    }

    fn new_anon(vaddrFlags : uint) -> Backing {
        Backing {
            as_node: DictNode::new(vaddrFlags | mapflag::Phys),
            parent_paddr : alloc_frame_paddr(),
            child_node: DListNode::new(),
        }
    }

    fn new_share(vaddrFlags: uint, share: &mut Sharing) -> Backing {
        Backing {
            as_node: DictNode::new(vaddrFlags),
            parent_paddr : (share as *mut Sharing) as uint,
            child_node: DListNode::new(),
        }
    }

    pub fn has_vaddr(&self, vaddr : uint) -> bool {
        return self.vaddr() == vaddr & !0xfff;
    }

    pub fn vaddr(&self) -> uint {
        return self.as_node.key & !0xfff;
    }

    pub fn flags(&self) -> uint {
        return self.as_node.key & 0xfff;
    }

    fn pte_flags(&self) -> uint {
        let flags = self.flags();
        let mut pte = 5; // Present, User-accessible
        if (flags & mapflag::X) == 0 {
            // Set bit 63 to *disable* execute permission
            pte |= 1 << 63;
        }
        if (flags & mapflag::W) != 0 {
            pte |= 2;
        }
        return pte;
    }

    pub fn pte(&self) -> uint {
        self.paddr() | self.pte_flags()
    }

    pub fn paddr(&self) -> uint {
        if (self.flags() & mapflag::Phys) != 0 {
            self.parent_paddr
        } else {
            self.parent().paddr
        }
    }

    pub fn parent<'a>(&self) -> &Sharing {
        if (self.flags() & mapflag::Phys) == 0 {
            unsafe { &*(self.parent_paddr as *Sharing) }
        } else {
            abort("no parent: direct backing");
        }
    }
}

// sharing: mapping one page to every place it's been shared to
// 7 words!
pub struct Sharing {
    as_node : DictNode<uint, Sharing>,
    paddr : uint,
    aspace : *mut AddressSpace,
    children : DList<Backing>,
}

impl DictItem<uint> for Sharing {
    fn node<'a>(&'a mut self) -> &'a mut DictNode<uint, Sharing> {
        return &mut self.as_node;
    }
}

impl Sharing {
    fn new(aspace: *mut AddressSpace, back: &Backing) -> Sharing {
        Sharing {
            as_node: DictNode::new(back.vaddr()),
            paddr : back.paddr(),
            aspace: aspace,
            children : DList::empty(),
        }
    }
}

type PageTable = [u64, ..512];
type PML4 = PageTable;

pub struct AddressSpace {
    // Upon setup, pml4 is set to a freshly allocated frame that is empty
    // except for the mapping to the kernel memory area (which, as long as
    // it's less than 4TB is only a single entry in the PML4).
    // (This is the virtual address in the higher half. proc.cr3 is a
    // physical address.)
    pml4 : *mut PML4,
    count : uint,
    // Do we need a list of processes that share an address space?
    // (That would remove the need for .count, I think.)
    //.procs    resq 1
//  .handles    restruc dict
//  .pending    restruc dict

    mapcards : Dict<uint, MapCard>,
    backings : Dict<uint, Backing>,
    sharings : Dict<uint, Sharing>
}

fn alloc_pml4() -> *mut PML4 {
    let res : *mut PML4 = cpu().memory.alloc_frame_panic();
    // Copy a reference to the kernel memory range into the new PML4.
    // Since this currently is at most one 4TB range, this is easy: only a
    // single PML4 entry maps everything by sharing the kernel's lower
    // page tables between all processes.
    unsafe { (*res)[511] = start32::kernel_pdp_addr() | 3; }
    return res;
}

fn get_alloc_pt(table : *mut PML4, mut index : uint, flags : uint) -> *mut PageTable {
    index &= 0x1ff;
    unsafe {
        let existing = (*table)[index];
        if (existing & 1) == 0 {
            let new : *mut PML4 = cpu().memory.alloc_frame_panic();
            (*table)[index] = ((new as uint - start32::kernel_base) | flags) as u64;
            return new;
        } else {
            return ((existing & !0xfff) + start32::kernel_base as u64) as *mut PageTable;
        }
    }
}

impl AddressSpace {
    pub fn new() -> AddressSpace {
        AddressSpace {
            pml4 : alloc_pml4(),
            count : 1,
            mapcards : Dict::empty(),
            backings : Dict::empty(),
            sharings : Dict::empty()
        }
    }

    pub fn cr3(&self) -> uint {
        let vaddr : *PML4 = unsafe { transmute(self.pml4) };
        return vaddr as uint - start32::kernel_base;
    }

    // FIXME This should return by-value and have a default value instead.
    pub fn mapcard_find<'a>(&mut self, vaddr : uint) -> Option<&'a mut MapCard> {
        return self.mapcards.find(vaddr);
    }

    pub fn mapcard_find_def(&self, vaddr: uint) -> MapCard {
        match self.mapcards.find_const(vaddr) {
            Some(card) => *card,
            None => MapCard::new(vaddr, 0, 0),
        }
    }

    fn mapcard_add(&mut self, card : &MapCard) {
        self.mapcards.insert(heap_copy(*card));
    }

    pub fn mapcard_set(&mut self, vaddr : uint, handle : uint, offset : uint, access : uint) {
        // TODO Assert that vaddr & 0xfff == offset & 0xfff == 0
        self.mapcard_set_(&MapCard::new(vaddr, handle, offset | access));
    }

    fn mapcard_set_(&mut self, new : &MapCard) {
        match self.mapcard_find(new.vaddr()) {
            Some(card) => {
                if card.same_addr(new) {
                    card.set(new);
                    return;
                }
            },
            None => {}
        }
        self.mapcard_add(new);
    }

    pub fn map_range(&mut self, start: uint, end: uint, handle: uint, offset: uint) {
        let end_card = self.mapcard_find_def(end);
        let new_end_card = MapCard::new(end, end_card.handle, end_card.offset);
        let start_card = MapCard::new(start, handle, offset);
        if start_card.same(&end_card) {
            self.mapcards.remove(end_card.vaddr());
        } else {
            // Insert a new card
            self.mapcard_set_(&new_end_card);
        }
        self.mapcards.remove_range_exclusive(start, end);
        self.mapcard_set_(&start_card);
    }

    pub fn add_shared_backing<'a>(&mut self, vaddr: uint, prot: uint,
            share: &mut Sharing) -> &'a mut Backing {
        let b = heap_copy(Backing::new_share(vaddr | prot, share));
        share.children.append(b);
        self.backings.insert(b)
    }

    fn add_phys_backing<'a>(&mut self, card : MapCard, vaddr : uint)
    -> &'a Backing {
        let b = heap_copy(Backing::new_phys(vaddr | card.flags(), card.paddr(vaddr)));
        self.backings.insert(b);
        return unsafe { &*b };
    }

    fn add_anon_backing<'a>(&mut self, card : MapCard, vaddr : uint)
    -> &'a Backing {
        let b = heap_copy(Backing::new_anon(vaddr | card.flags()));
        self.backings.insert(b);
        return unsafe { &*b };
    }

    pub fn find_add_backing<'a>(&mut self, vaddr : uint) -> &'a Backing {
        use aspace::mapflag::*;
        use util::abort;

        match self.backings.find_const(vaddr | 0xfff) {
            Some(ref back) if back.has_vaddr(vaddr) => { return &**back; },
            _ => ()
        }

        match self.mapcard_find(vaddr) {
            Some(card) => {
                if (card.flags() & RWX) == 0 {
                    abort("No access!");
                }
                if card.handle == 0 {
                    if (card.flags() & Anon) != 0 {
                        return self.add_anon_backing(*card, vaddr);
                    } else if (card.flags() & Phys) != 0 {
                        return self.add_phys_backing(*card, vaddr);
                    } else {
                        abort("not anon or phys for handle==0");
                    }
                } else {
                    abort("user mappings unimplemented");
                }
            },
            None => {
                abort("No mapping found!");
            }
        }
    }

    pub fn share_backing<'a>(&mut self, vaddr: uint) -> &'a mut Sharing {
        let back = self.find_add_backing(vaddr);
        self.sharings.insert(heap_copy(Sharing::new(self, back)))
    }

    pub fn add_pte(&mut self, vaddr : uint, pte : uint) {
        if log_add_pte {
            write("Mapping ");
            con::writePHex(vaddr);
            write(" to ");
            con::writePHex(pte);
            con::newline();
        }
        let pdp = get_alloc_pt(self.pml4, vaddr >> 39, 7);
        let pd = get_alloc_pt(pdp, vaddr >> 30, 7);
        let pt = get_alloc_pt(pd, vaddr >> 21, 7);
        unsafe { (*pt)[(vaddr >> 12) & 0x1ff] = pte as u64; }
    }
}
<commit_msg>Don't reallocate DMA memory on faults<commit_after>use core::prelude::*;
use core::mem::transmute;

use con;
use con::write;
use cpu;
use dict::*;
use dlist::*;
use mem::heap_copy;
use start32;
use util::abort;

static log_add_pte : bool = false;

pub mod mapflag {
    pub static X : uint = 1;
    pub static W : uint = 2;
    pub static R : uint = 4;
    pub static RWX : uint = 7;
    // Anonymous memory allocated to zeroes on first use.
    pub static Anon : uint = 8;
    // handle is 0; offset is (paddr - vaddr)
    pub static Phys : uint = 16;
    // Physical memory allocated and locked at map time; and deallocated when
    // unmapped.
    pub static DMA : uint = Anon | Phys;
    pub static UserAllowed : uint = DMA | RWX;
}

// mapcard: the handle, offset and flags for the range of virtual addresses until
// the next card.
// 5 words:
// - 3 for dict_node w/ vaddr
// - 1 handle
// - 1 offset+flags
//   (since offsets must be page aligned we have 12 left-over bits)
// This structure is completely unrelated to the physical pages backing virtual
// memory - it represents each process' wishful thinking about how their memory
// should look. backings and sharings control physical memory.
pub struct MapCard {
    as_node : DictNode<uint, MapCard>,
    pub handle : uint,
    // .vaddr + .offset = handle-offset to be sent to backer on fault
    // For a direct physical mapping, paddr = .vaddr + .offset
    // .offset = handle-offset - vaddr
    // .offset = paddr - .vaddr
    // The low 12 bits contain flags.
    pub offset : uint,
}

impl DictItem<uint> for MapCard {
    fn node<'a>(&'a mut self) -> &'a mut DictNode<uint, MapCard> {
        return &mut self.as_node;
    }
}

impl MapCard {
    fn new(vaddr : uint, handle : uint, offset : uint) -> MapCard {
        MapCard { as_node: DictNode::new(vaddr), handle: handle, offset: offset }
    }

    pub fn vaddr(&self) -> uint {
        return self.as_node.key;
    }

    pub fn paddr(&self, vaddr : uint) -> uint {
        return vaddr + (self.offset & !0xfff);
    }

    pub fn flags(&self) -> uint {
        return self.offset & 0xfff;
    }

    fn same_addr(&self, other : &MapCard) -> bool {
        self.vaddr() == other.vaddr()
    }

    fn same(&self, other: &MapCard) -> bool {
        return self.handle == other.handle && self.offset == other.offset;
    }

    fn set(&mut self, other : &MapCard) {
        self.handle = other.handle;
        self.offset = other.offset;
    }
}

// backing: mapping *one page* to the place that page came from.
// Indexed by vaddr for the process that maps it. The vaddr includes flags, so
// look up by vaddr|0xfff.
// This is likely to exist once per physical page per process. Should be
// minimized.
// 6 words:
// - 3 words for dict_node w/ vaddr
// - 1 word for parent
// - 2 words for child-list links
//
// Could be reduced to 4 words: flags, parent, child-list links, if moving to
// an external dictionary. 32 bytes per page gives 128 entries in the last-level
// table. Flags could indicate how many levels that are required, and e.g. a
// very small process could have only one level, and map 128 pages at 0..512kB.
pub struct Backing {
    // Flags stored in low bits of vaddr/key!
    as_node : DictNode<uint, Backing>,
    // Pointer to parent sharing. Needed to unlink self when unmapping.
    // Could have room for flags (e.g. to let it be a paddr when we don't
    // need the parent - we might have a direct physical address mapping)
    parent_paddr : uint,
    // Space to participate in parent's list of remappings.
    child_node : DListNode<Backing>
}

impl DictItem<uint> for Backing {
    fn node<'a>(&'a mut self) -> &'a mut DictNode<uint, Backing> {
        return &mut self.as_node;
    }
}

impl DListItem for Backing {
    fn node<'a>(&'a mut self) -> &'a mut DListNode<Backing> {
        return &mut self.child_node;
    }
}

fn alloc_frame_paddr() -> uint {
    let p : *mut u8 = cpu().memory.alloc_frame_panic();
    p as uint - start32::kernel_base
}

impl Backing {
    fn new_phys(vaddrFlags : uint, phys_addr : uint) -> Backing {
        if phys_addr & 0xfff != 0 {
            abort("Bad physical address in new_phys");
        }
        Backing {
            as_node: DictNode::new(vaddrFlags),
            parent_paddr : phys_addr,
            child_node: DListNode::new(),
        }
    }

    fn new_anon(vaddrFlags : uint) -> Backing {
        Backing {
            as_node: DictNode::new(vaddrFlags | mapflag::Phys),
            parent_paddr : alloc_frame_paddr(),
            child_node: DListNode::new(),
        }
    }

    fn new_share(vaddrFlags: uint, share: &mut Sharing) -> Backing {
        Backing {
            as_node: DictNode::new(vaddrFlags),
            parent_paddr : (share as *mut Sharing) as uint,
            child_node: DListNode::new(),
        }
    }

    pub fn has_vaddr(&self, vaddr : uint) -> bool {
        return self.vaddr() == vaddr & !0xfff;
    }

    pub fn vaddr(&self) -> uint {
        return self.as_node.key & !0xfff;
    }

    pub fn flags(&self) -> uint {
        return self.as_node.key & 0xfff;
    }

    fn pte_flags(&self) -> uint {
        let flags = self.flags();
        let mut pte = 5; // Present, User-accessible
        if (flags & mapflag::X) == 0 {
            // Set bit 63 to *disable* execute permission
            pte |= 1 << 63;
        }
        if (flags & mapflag::W) != 0 {
            pte |= 2;
        }
        return pte;
    }

    pub fn pte(&self) -> uint {
        self.paddr() | self.pte_flags()
    }

    pub fn paddr(&self) -> uint {
        if (self.flags() & mapflag::Phys) != 0 {
            self.parent_paddr
        } else {
            self.parent().paddr
        }
    }

    pub fn parent<'a>(&self) -> &Sharing {
        if (self.flags() & mapflag::Phys) == 0 {
            unsafe { &*(self.parent_paddr as *Sharing) }
        } else {
            abort("no parent: direct backing");
        }
    }
}

// sharing: mapping one page to every place it's been shared to
// 7 words!
pub struct Sharing {
    as_node : DictNode<uint, Sharing>,
    paddr : uint,
    aspace : *mut AddressSpace,
    children : DList<Backing>,
}

impl DictItem<uint> for Sharing {
    fn node<'a>(&'a mut self) -> &'a mut DictNode<uint, Sharing> {
        return &mut self.as_node;
    }
}

impl Sharing {
    fn new(aspace: *mut AddressSpace, back: &Backing) -> Sharing {
        Sharing {
            as_node: DictNode::new(back.vaddr()),
            paddr : back.paddr(),
            aspace: aspace,
            children : DList::empty(),
        }
    }
}

type PageTable = [u64, ..512];
type PML4 = PageTable;

pub struct AddressSpace {
    // Upon setup, pml4 is set to a freshly allocated frame that is empty
    // except for the mapping to the kernel memory area (which, as long as
    // it's less than 4TB is only a single entry in the PML4).
    // (This is the virtual address in the higher half. proc.cr3 is a
    // physical address.)
    pml4 : *mut PML4,
    count : uint,
    // Do we need a list of processes that share an address space?
    // (That would remove the need for .count, I think.)
    //.procs    resq 1
//  .handles    restruc dict
//  .pending    restruc dict

    mapcards : Dict<uint, MapCard>,
    backings : Dict<uint, Backing>,
    sharings : Dict<uint, Sharing>
}

fn alloc_pml4() -> *mut PML4 {
    let res : *mut PML4 = cpu().memory.alloc_frame_panic();
    // Copy a reference to the kernel memory range into the new PML4.
    // Since this currently is at most one 4TB range, this is easy: only a
    // single PML4 entry maps everything by sharing the kernel's lower
    // page tables between all processes.
    unsafe { (*res)[511] = start32::kernel_pdp_addr() | 3; }
    return res;
}

fn get_alloc_pt(table : *mut PML4, mut index : uint, flags : uint) -> *mut PageTable {
    index &= 0x1ff;
    unsafe {
        let existing = (*table)[index];
        if (existing & 1) == 0 {
            let new : *mut PML4 = cpu().memory.alloc_frame_panic();
            (*table)[index] = ((new as uint - start32::kernel_base) | flags) as u64;
            return new;
        } else {
            return ((existing & !0xfff) + start32::kernel_base as u64) as *mut PageTable;
        }
    }
}

impl AddressSpace {
    pub fn new() -> AddressSpace {
        AddressSpace {
            pml4 : alloc_pml4(),
            count : 1,
            mapcards : Dict::empty(),
            backings : Dict::empty(),
            sharings : Dict::empty()
        }
    }

    pub fn cr3(&self) -> uint {
        let vaddr : *PML4 = unsafe { transmute(self.pml4) };
        return vaddr as uint - start32::kernel_base;
    }

    // FIXME This should return by-value and have a default value instead.
    pub fn mapcard_find<'a>(&mut self, vaddr : uint) -> Option<&'a mut MapCard> {
        return self.mapcards.find(vaddr);
    }

    pub fn mapcard_find_def(&self, vaddr: uint) -> MapCard {
        match self.mapcards.find_const(vaddr) {
            Some(card) => *card,
            None => MapCard::new(vaddr, 0, 0),
        }
    }

    fn mapcard_add(&mut self, card : &MapCard) {
        self.mapcards.insert(heap_copy(*card));
    }

    pub fn mapcard_set(&mut self, vaddr : uint, handle : uint, offset : uint, access : uint) {
        // TODO Assert that vaddr & 0xfff == offset & 0xfff == 0
        self.mapcard_set_(&MapCard::new(vaddr, handle, offset | access));
    }

    fn mapcard_set_(&mut self, new : &MapCard) {
        match self.mapcard_find(new.vaddr()) {
            Some(card) => {
                if card.same_addr(new) {
                    card.set(new);
                    return;
                }
            },
            None => {}
        }
        self.mapcard_add(new);
    }

    pub fn map_range(&mut self, start: uint, end: uint, handle: uint, offset: uint) {
        let end_card = self.mapcard_find_def(end);
        let new_end_card = MapCard::new(end, end_card.handle, end_card.offset);
        let start_card = MapCard::new(start, handle, offset);
        if start_card.same(&end_card) {
            self.mapcards.remove(end_card.vaddr());
        } else {
            // Insert a new card
            self.mapcard_set_(&new_end_card);
        }
        self.mapcards.remove_range_exclusive(start, end);
        self.mapcard_set_(&start_card);
    }

    pub fn add_shared_backing<'a>(&mut self, vaddr: uint, prot: uint,
            share: &mut Sharing) -> &'a mut Backing {
        let b = heap_copy(Backing::new_share(vaddr | prot, share));
        share.children.append(b);
        self.backings.insert(b)
    }

    fn add_phys_backing<'a>(&mut self, card : MapCard, vaddr : uint)
    -> &'a Backing {
        let b = heap_copy(Backing::new_phys(vaddr | card.flags(), card.paddr(vaddr)));
        self.backings.insert(b);
        return unsafe { &*b };
    }

    fn add_anon_backing<'a>(&mut self, card : MapCard, vaddr : uint)
    -> &'a Backing {
        let b = heap_copy(Backing::new_anon(vaddr | card.flags()));
        self.backings.insert(b);
        return unsafe { &*b };
    }

    pub fn find_add_backing<'a>(&mut self, vaddr : uint) -> &'a Backing {
        use aspace::mapflag::*;
        use util::abort;

        match self.backings.find_const(vaddr | 0xfff) {
            Some(ref back) if back.has_vaddr(vaddr) => { return &**back; },
            _ => ()
        }

        match self.mapcard_find(vaddr) {
            Some(card) => {
                if (card.flags() & RWX) == 0 {
                    abort("No access!");
                }
                if card.handle == 0 {
                    if (card.flags() & DMA) == Anon {
                        return self.add_anon_backing(*card, vaddr);
                    } else if (card.flags() & Phys) != 0 {
                        return self.add_phys_backing(*card, vaddr);
                    } else {
                        abort("not anon or phys for handle==0");
                    }
                } else {
                    abort("user mappings unimplemented");
                }
            },
            None => {
                abort("No mapping found!");
            }
        }
    }

    pub fn share_backing<'a>(&mut self, vaddr: uint) -> &'a mut Sharing {
        let back = self.find_add_backing(vaddr);
        self.sharings.insert(heap_copy(Sharing::new(self, back)))
    }

    pub fn add_pte(&mut self, vaddr : uint, pte : uint) {
        if log_add_pte {
            write("Mapping ");
            con::writePHex(vaddr);
            write(" to ");
            con::writePHex(pte);
            con::newline();
        }
        let pdp = get_alloc_pt(self.pml4, vaddr >> 39, 7);
        let pd = get_alloc_pt(pdp, vaddr >> 30, 7);
        let pt = get_alloc_pt(pd, vaddr >> 21, 7);
        unsafe { (*pt)[(vaddr >> 12) & 0x1ff] = pte as u64; }
    }
}
<|endoftext|>


<commit_before>/*
 * UpdateHub
 * Copyright (C) 2017
 * O.S. Systems Sofware LTDA: contact@ossystems.com.br
 *
 * SPDX-License-Identifier:     GPL-2.0
 */

use serde::{Deserialize, Deserializer, de};
use serde_ini;

use std::io;
use std::time::Duration;

const SYSTEM_SETTINGS_PATH: &'static str = "/etc/updatehub.conf";

#[derive(Default, PartialEq, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Settings {
    pub polling: Polling,
    pub storage: Storage,
    pub update: Update,
    pub network: Network,
    pub firmware: Firmware,
}

impl Settings {
    pub fn new() -> Self {
        Settings::default()
    }

    pub fn load(self) -> Result<Self, SettingsError> {
        use std::path::Path;
        use std::fs::File;
        use std::io::Read;

        let path = Path::new(SYSTEM_SETTINGS_PATH);

        if path.exists() {
            info!(
                "Loading system settings from '{}'...",
                path.to_string_lossy()
            );

            let mut content = String::new();
            File::open(path)?.read_to_string(&mut content)?;

            Ok(self.parse(&content)?)
        } else {
            debug!(
                "System settings file {} does not exists.",
                path.to_string_lossy()
            );
            info!("Using default system settings...");
            Ok(self)
        }
    }

    fn parse(self, content: &str) -> Result<Self, SettingsError> {
        fn has_protocol_prefix(server: &str) -> bool {
            server.starts_with("http://") || server.starts_with("https://")
        }

        fn has_valid_polling_interval(interval: &Duration) -> bool {
            if *interval < Duration::new(60, 0) {
                false
            } else {
                true
            }
        }

        let settings = serde_ini::from_str::<Settings>(&content)?;

        if !has_valid_polling_interval(&settings.polling.interval) {
            error!("Invalid setting for polling interval. The interval cannot be less than 60 seconds");
            return Err(SettingsError::InvalidInterval);
        }

        if !has_protocol_prefix(&settings.network.server_address) {
            error!("Invalid setting for server address. The server address must use the protocol prefix");
            return Err(SettingsError::InvalidServerAddress);
        }

        Ok(settings)
    }
}

#[derive(Debug)]
pub enum SettingsError {
    Io(io::Error),
    Ini(serde_ini::de::Error),
    InvalidInterval,
    InvalidServerAddress,
}

impl From<io::Error> for SettingsError {
    fn from(err: io::Error) -> SettingsError {
        SettingsError::Io(err)
    }
}

impl From<serde_ini::de::Error> for SettingsError {
    fn from(err: serde_ini::de::Error) -> SettingsError {
        SettingsError::Ini(err)
    }
}

#[derive(Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Polling {
    #[serde(deserialize_with = "ini_de_duration_from_str")]
    pub interval: Duration,
    #[serde(deserialize_with = "ini_de_bool_from_str")]
    pub enabled: bool,
}

impl Default for Polling {
    fn default() -> Self {
        Polling {
            interval: Duration::new(86_400, 0), // 1 day
            enabled: true,
        }
    }
}

#[derive(Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Storage {
    #[serde(deserialize_with = "ini_de_bool_from_str")]
    pub read_only: bool,
    pub runtime_settings: String,
}

impl Default for Storage {
    fn default() -> Self {
        Storage {
            read_only: false,
            runtime_settings: "/var/lib/updatehub.conf".to_string(),
        }
    }
}

#[derive(Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Update {
    pub download_dir: String,
    #[serde(rename = "SupportedInstallModes")]
    #[serde(deserialize_with = "ini_de_vec_from_str")]
    pub install_modes: Vec<String>,
}

impl Default for Update {
    fn default() -> Self {
        Update {
            download_dir: "/tmp".to_string(),
            install_modes: [
                "dry-run",
                "copy",
                "flash",
                "imxkobs",
                "raw",
                "tarball",
                "ubifs",
            ].iter()
                .map(|i| i.to_string())
                .collect(),
        }
    }
}

#[derive(Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Network {
    pub server_address: String,
}

impl Default for Network {
    fn default() -> Self {
        Network { server_address: "https://api.updatehub.io".to_string() }
    }
}

#[derive(Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Firmware {
    pub metadata_path: String,
}

impl Default for Firmware {
    fn default() -> Self {
        Firmware { metadata_path: "/usr/share/updatehub".to_string() }
    }
}

fn ini_de_duration_from_str<'de, D>(deserializer: D) -> Result<Duration, D::Error>
where
    D: Deserializer<'de>,
{
    use parse_duration::parse;

    let s = String::deserialize(deserializer)?;
    parse(&s).map_err(de::Error::custom)
}

fn ini_de_bool_from_str<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
    D: Deserializer<'de>,
{
    use std::str::FromStr;

    let s = String::deserialize(deserializer)?;
    bool::from_str(&s).map_err(de::Error::custom)
}

fn ini_de_vec_from_str<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
where
    D: Deserializer<'de>,
{
    Ok(
        String::deserialize(deserializer)?
            .split(",")
            .map(|s| s.to_string())
            .collect(),
    )
}

/*
extern crate chrono;
use self::chrono::{DateTime, Utc};

#[derive(Deserialize)]
struct PersistentSettings {
    polling: PersistentPolling,
}

struct PersistentPolling {
    last: DateTime<Utc>,
    first: DateTime<Utc>,
    extra_interval: usize,
    retries: usize,
    now: bool,
}

struct PersistentUpdate {
    upgrading_to: i8,
}
impl Default for PersistentPolling {
    fn default() -> Self {
        PersistentPolling {
            last: Utc::now(),
            first: Utc::now(),
            extra_interval: 0,
            retries: 0,
            now: false,
        }
    }
}
*/

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

    #[test]
    fn ok() {
        let ini = r"
[Polling]
Interval=60s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=http://localhost

[Firmware]
MetadataPath=/tmp/metadata
";

        let expected = Settings {
            polling: Polling {
                interval: Duration::new(60, 0),
                enabled: false,
            },
            storage: Storage {
                read_only: true,
                runtime_settings: "/run/updatehub/state".to_string(),
            },
            update: Update {
                download_dir: "/tmp/download".to_string(),
                install_modes: ["mode1", "mode2"].iter().map(|i| i.to_string()).collect(),
            },
            network: Network { server_address: "http://localhost".to_string() },
            firmware: Firmware { metadata_path: "/tmp/metadata".to_string() },
        };

        assert!(
            serde_ini::from_str::<Settings>(&ini)
                .map_err(|e| println!("{}", e))
                .ok() == Some(expected)
        );
    }

    #[test]
    fn invalid_polling_interval() {
        let ini = r"
[Polling]
Interval=59s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=http://localhost

[Firmware]
MetadataPath=/tmp/metadata
";
        assert!(Settings::new().parse(&ini).is_err());
    }

    #[test]
    fn invalid_network_server_address() {
        let ini = r"
[Polling]
Interval=60s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=localhost

[Firmware]
MetadataPath=/tmp/metadata
";
        assert!(Settings::new().parse(&ini).is_err());
    }
}
<commit_msg>settings: test default values<commit_after>/*
 * UpdateHub
 * Copyright (C) 2017
 * O.S. Systems Sofware LTDA: contact@ossystems.com.br
 *
 * SPDX-License-Identifier:     GPL-2.0
 */

use serde::{Deserialize, Deserializer, de};
use serde_ini;

use std::io;
use std::time::Duration;

const SYSTEM_SETTINGS_PATH: &'static str = "/etc/updatehub.conf";

#[derive(Default, PartialEq, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Settings {
    pub polling: Polling,
    pub storage: Storage,
    pub update: Update,
    pub network: Network,
    pub firmware: Firmware,
}

impl Settings {
    pub fn new() -> Self {
        Settings::default()
    }

    pub fn load(self) -> Result<Self, SettingsError> {
        use std::path::Path;
        use std::fs::File;
        use std::io::Read;

        let path = Path::new(SYSTEM_SETTINGS_PATH);

        if path.exists() {
            info!(
                "Loading system settings from '{}'...",
                path.to_string_lossy()
            );

            let mut content = String::new();
            File::open(path)?.read_to_string(&mut content)?;

            Ok(self.parse(&content)?)
        } else {
            debug!(
                "System settings file {} does not exists.",
                path.to_string_lossy()
            );
            info!("Using default system settings...");
            Ok(self)
        }
    }

    fn parse(self, content: &str) -> Result<Self, SettingsError> {
        fn has_protocol_prefix(server: &str) -> bool {
            server.starts_with("http://") || server.starts_with("https://")
        }

        fn has_valid_polling_interval(interval: &Duration) -> bool {
            if *interval < Duration::new(60, 0) {
                false
            } else {
                true
            }
        }

        let settings = serde_ini::from_str::<Settings>(&content)?;

        if !has_valid_polling_interval(&settings.polling.interval) {
            error!("Invalid setting for polling interval. The interval cannot be less than 60 seconds");
            return Err(SettingsError::InvalidInterval);
        }

        if !has_protocol_prefix(&settings.network.server_address) {
            error!("Invalid setting for server address. The server address must use the protocol prefix");
            return Err(SettingsError::InvalidServerAddress);
        }

        Ok(settings)
    }
}

#[derive(Debug)]
pub enum SettingsError {
    Io(io::Error),
    Ini(serde_ini::de::Error),
    InvalidInterval,
    InvalidServerAddress,
}

impl From<io::Error> for SettingsError {
    fn from(err: io::Error) -> SettingsError {
        SettingsError::Io(err)
    }
}

impl From<serde_ini::de::Error> for SettingsError {
    fn from(err: serde_ini::de::Error) -> SettingsError {
        SettingsError::Ini(err)
    }
}

#[derive(Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Polling {
    #[serde(deserialize_with = "ini_de_duration_from_str")]
    pub interval: Duration,
    #[serde(deserialize_with = "ini_de_bool_from_str")]
    pub enabled: bool,
}

impl Default for Polling {
    fn default() -> Self {
        Polling {
            interval: Duration::new(86_400, 0), // 1 day
            enabled: true,
        }
    }
}

#[derive(Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Storage {
    #[serde(deserialize_with = "ini_de_bool_from_str")]
    pub read_only: bool,
    pub runtime_settings: String,
}

impl Default for Storage {
    fn default() -> Self {
        Storage {
            read_only: false,
            runtime_settings: "/var/lib/updatehub.conf".to_string(),
        }
    }
}

#[derive(Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Update {
    pub download_dir: String,
    #[serde(rename = "SupportedInstallModes")]
    #[serde(deserialize_with = "ini_de_vec_from_str")]
    pub install_modes: Vec<String>,
}

impl Default for Update {
    fn default() -> Self {
        Update {
            download_dir: "/tmp/updatehub".to_string(),
            install_modes: [
                "dry-run",
                "copy",
                "flash",
                "imxkobs",
                "raw",
                "tarball",
                "ubifs",
            ].iter()
                .map(|i| i.to_string())
                .collect(),
        }
    }
}

#[derive(Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Network {
    pub server_address: String,
}

impl Default for Network {
    fn default() -> Self {
        Network { server_address: "https://api.updatehub.io".to_string() }
    }
}

#[derive(Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Firmware {
    pub metadata_path: String,
}

impl Default for Firmware {
    fn default() -> Self {
        Firmware { metadata_path: "/usr/share/updatehub".to_string() }
    }
}

fn ini_de_duration_from_str<'de, D>(deserializer: D) -> Result<Duration, D::Error>
where
    D: Deserializer<'de>,
{
    use parse_duration::parse;

    let s = String::deserialize(deserializer)?;
    parse(&s).map_err(de::Error::custom)
}

fn ini_de_bool_from_str<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
    D: Deserializer<'de>,
{
    use std::str::FromStr;

    let s = String::deserialize(deserializer)?;
    bool::from_str(&s).map_err(de::Error::custom)
}

fn ini_de_vec_from_str<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
where
    D: Deserializer<'de>,
{
    Ok(
        String::deserialize(deserializer)?
            .split(",")
            .map(|s| s.to_string())
            .collect(),
    )
}

/*
extern crate chrono;
use self::chrono::{DateTime, Utc};

#[derive(Deserialize)]
struct PersistentSettings {
    polling: PersistentPolling,
}

struct PersistentPolling {
    last: DateTime<Utc>,
    first: DateTime<Utc>,
    extra_interval: usize,
    retries: usize,
    now: bool,
}

struct PersistentUpdate {
    upgrading_to: i8,
}
impl Default for PersistentPolling {
    fn default() -> Self {
        PersistentPolling {
            last: Utc::now(),
            first: Utc::now(),
            extra_interval: 0,
            retries: 0,
            now: false,
        }
    }
}
*/

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

    #[test]
    fn ok() {
        let ini = r"
[Polling]
Interval=60s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=http://localhost

[Firmware]
MetadataPath=/tmp/metadata
";

        let expected = Settings {
            polling: Polling {
                interval: Duration::new(60, 0),
                enabled: false,
            },
            storage: Storage {
                read_only: true,
                runtime_settings: "/run/updatehub/state".to_string(),
            },
            update: Update {
                download_dir: "/tmp/download".to_string(),
                install_modes: ["mode1", "mode2"].iter().map(|i| i.to_string()).collect(),
            },
            network: Network { server_address: "http://localhost".to_string() },
            firmware: Firmware { metadata_path: "/tmp/metadata".to_string() },
        };

        assert!(
            serde_ini::from_str::<Settings>(&ini)
                .map_err(|e| println!("{}", e))
                .ok() == Some(expected)
        );
    }

    #[test]
    fn invalid_polling_interval() {
        let ini = r"
[Polling]
Interval=59s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=http://localhost

[Firmware]
MetadataPath=/tmp/metadata
";
        assert!(Settings::new().parse(&ini).is_err());
    }

    #[test]
    fn invalid_network_server_address() {
        let ini = r"
[Polling]
Interval=60s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=localhost

[Firmware]
MetadataPath=/tmp/metadata
";
        assert!(Settings::new().parse(&ini).is_err());
    }

    #[test]
    fn default() {
        let settings = Settings::default();
        let expected = Settings {
            polling: Polling {
                interval: Duration::new(86_400, 0),
                enabled: true,
            },
            storage: Storage {
                read_only: false,
                runtime_settings: "/var/lib/updatehub.conf".to_string(),
            },
            update: Update {
                download_dir: "/tmp/updatehub".to_string(),
                install_modes: [
                    "dry-run",
                    "copy",
                    "flash",
                    "imxkobs",
                    "raw",
                    "tarball",
                    "ubifs",
                ].iter()
                    .map(|i| i.to_string())
                    .collect(),
            },
            network: Network { server_address: "https://api.updatehub.io".to_string() },
            firmware: Firmware { metadata_path: "/usr/share/updatehub".to_string() },
        };

        assert!(Some(settings) == Some(expected));
    }
}
<|endoftext|>


<commit_before>use std::{fmt, iter, ptr};

pub enum NextNotFound {
    NoMoreElements,
    NoSuchElement,
}

// ////////////////////////////////////////////////////////////////////////////
// SkipNode
// ////////////////////////////////////////////////////////////////////////////

/// SkipNodes are make up the SkipList.  The SkipList owns the first head-node
/// (which has no value) and each node has ownership of the next node through
/// `next`.
///
/// The node has a `level` which corresponds to how 'high' the node reaches, and
/// this should be equal to the length of the vector of links.  There is a
/// corresponding vector of link lengths which is used to reach a particular
/// index.
///
/// Lastly, each node contains a link to the immediately previous node in case
/// one needs to parse the list backwards.
///
/// In cases where the value is not applicable, `None` should be used.  In
/// particular, as there is no tail node, the value of `next` in the last node
/// should be `None`.
#[derive(Clone, Debug)]
pub struct SkipNode<V> {
    // key and value should never be None, with the sole exception being the
    // head node.
    pub value: Option<V>,
    // how high the node reaches.  This should be equal to the vector length.
    pub level: usize,
    // The immediately next element (and owns that next node).
    pub next: Option<Box<SkipNode<V>>>,
    // The immediately previous element.
    pub prev: Option<*mut SkipNode<V>>,
    // Vector of links to the next node at the respective level.  This vector
    // *must* be of length `self.level + 1`.  links[0] stores a pointer to the
    // same node as next.
    pub links: Vec<Option<*mut SkipNode<V>>>,
    // The corresponding length of each link
    pub links_len: Vec<usize>,
}

// ///////////////////////////////////////////////
// Inherent methods
// ///////////////////////////////////////////////

impl<V> SkipNode<V> {
    /// Create a new head node.
    pub fn head(total_levels: usize) -> Self {
        SkipNode {
            value: None,
            level: total_levels - 1,
            next: None,
            prev: None,
            links: iter::repeat(None).take(total_levels).collect(),
            links_len: iter::repeat(0).take(total_levels).collect(),
        }
    }

    /// Create a new SkipNode with the given value.  The values of `prev` and
    /// `next` will all be `None` and have to be adjusted.
    pub fn new(value: V, level: usize) -> Self {
        SkipNode {
            value: Some(value),
            level,
            next: None,
            prev: None,
            links: iter::repeat(None).take(level + 1).collect(),
            links_len: iter::repeat(0).take(level + 1).collect(),
        }
    }

    /// Consumes the node returning the value it contains.
    pub fn into_inner(mut self) -> Option<V> {
        self.value.take()
    }

    /// Returns `true` is the node is a head-node.
    pub fn is_head(&self) -> bool {
        self.prev.is_none()
    }

    /// Try to move to next nth node at specified level.
    /// If it's impossible, then move as far as possible.
    /// Returns a reference to the new node and the distance travelled.
    pub fn advance_len(&self, level: usize, target: usize) -> (&Self, usize) {
        let mut current = self;
        let mut travelled = 0;
        loop {
            match current.advance_if(level, |current, _| {
                travelled + current.links_len[level] <= target
            }) {
                Ok((node, steps)) => {
                    current = node;
                    travelled += steps;
                }
                Err(node) => return (node, travelled),
            }
        }
    }

    /// Try to move to next nth node at specified level.
    /// If it's impossible, then move as far as possible.
    /// Returns a mutable reference to the new node and the distance travelled.
    pub fn advance_len_mut(&mut self, level: usize, target: usize) -> (&mut Self, usize) {
        let mut current = self;
        let mut travelled = 0;
        loop {
            match current.advance_if_mut(level, |current, _| {
                travelled + current.links_len[level] <= target
            }) {
                Ok((node, steps)) => {
                    current = node;
                    travelled += steps;
                }
                Err(node) => return (node, travelled),
            }
        }
    }

    pub fn advance_if<'a>(
        &'a self,
        level: usize,
        predicate: impl FnOnce(&Self, &Self) -> bool,
    ) -> Result<(&'a Self, usize), &'a Self> {
        let next = unsafe { self.links[level].and_then(|ptr| ptr.as_ref()) };
        match next {
            Some(next) if predicate(self, next) => Ok((next, self.links_len[level])),
            _ => Err(self),
        }
    }

    // Due to Rust lifetime semantics, the lifetime of result is the same as self.
    // Sometimes Rust cannot determine the result is unused, e.g. in a loop.
    // As a result, self might be  borrowed forever and caller cannot return that value
    // if they call this function in a loop.
    // Therefore this function always return self when it fails to advance.
    pub fn advance_if_mut<'a>(
        &'a mut self,
        level: usize,
        predicate: impl FnOnce(&Self, &Self) -> bool,
    ) -> Result<(&'a mut Self, usize), &'a mut Self> {
        let next = unsafe { self.links[level].and_then(|ptr| ptr.as_mut()) };
        match next {
            Some(next) if predicate(self, next) => Ok((next, self.links_len[level])),
            _ => Err(self),
        }
    }
}

impl<V> Drop for SkipNode<V> {
    fn drop(&mut self) {
        while let Some(mut node) = self.next.take() {
            self.next = node.next.take();
        }
    }
}

// ///////////////////////////////////////////////
// Trait implementation
// ///////////////////////////////////////////////

impl<V> fmt::Display for SkipNode<V>
where
    V: fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(ref v) = self.value {
            write!(f, "{}", v)
        } else {
            Ok(())
        }
    }
}

// /////////////////////////////////
// Iterators
// /////////////////////////////////
// Since Iterators (currently) only pop from front and back,
// they can be shared by some data structures.
// There's no need for a dummy head (that contains no value) in the iterator.
// so the members are named first and last instaed of head/end to avoid confusion.

/// Consuming iterator.
pub struct IntoIter<T> {
    pub(crate) first: Option<Box<SkipNode<T>>>,
    pub(crate) last: *mut SkipNode<T>,
    pub(crate) size: usize,
}

impl<T> Iterator for IntoIter<T> {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        let mut popped_node = self.first.take()?;
        self.size -= 1;
        self.first = popped_node.next.take().map(|mut node| {
            node.prev = None;
            node
        });
        if self.first.is_none() {
            self.last = ptr::null_mut();
        }
        popped_node.into_inner()
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.size, Some(self.size))
    }
}

impl<T> DoubleEndedIterator for IntoIter<T> {
    fn next_back(&mut self) -> Option<T> {
        if self.first.is_none() {
            return None;
        }
        assert!(!self.last.is_null());
        unsafe {
            let new_last = (*self.last).prev;
            let popped_node = match new_last {
                Some(new_last) => {
                    self.last = new_last;
                    (*new_last).next.take().unwrap()
                }
                None => self.first.take().unwrap(),
            };
            self.size -= 1;
            popped_node.into_inner()
        }
    }
}
<commit_msg>Add advance_while()<commit_after>use std::{fmt, iter, ptr};

pub enum NextNotFound {
    NoMoreElements,
    NoSuchElement,
}

// ////////////////////////////////////////////////////////////////////////////
// SkipNode
// ////////////////////////////////////////////////////////////////////////////

/// SkipNodes are make up the SkipList.  The SkipList owns the first head-node
/// (which has no value) and each node has ownership of the next node through
/// `next`.
///
/// The node has a `level` which corresponds to how 'high' the node reaches, and
/// this should be equal to the length of the vector of links.  There is a
/// corresponding vector of link lengths which is used to reach a particular
/// index.
///
/// Lastly, each node contains a link to the immediately previous node in case
/// one needs to parse the list backwards.
///
/// In cases where the value is not applicable, `None` should be used.  In
/// particular, as there is no tail node, the value of `next` in the last node
/// should be `None`.
#[derive(Clone, Debug)]
pub struct SkipNode<V> {
    // key and value should never be None, with the sole exception being the
    // head node.
    pub value: Option<V>,
    // how high the node reaches.  This should be equal to the vector length.
    pub level: usize,
    // The immediately next element (and owns that next node).
    pub next: Option<Box<SkipNode<V>>>,
    // The immediately previous element.
    pub prev: Option<*mut SkipNode<V>>,
    // Vector of links to the next node at the respective level.  This vector
    // *must* be of length `self.level + 1`.  links[0] stores a pointer to the
    // same node as next.
    pub links: Vec<Option<*mut SkipNode<V>>>,
    // The corresponding length of each link
    pub links_len: Vec<usize>,
}

// ///////////////////////////////////////////////
// Inherent methods
// ///////////////////////////////////////////////

impl<V> SkipNode<V> {
    /// Create a new head node.
    pub fn head(total_levels: usize) -> Self {
        SkipNode {
            value: None,
            level: total_levels - 1,
            next: None,
            prev: None,
            links: iter::repeat(None).take(total_levels).collect(),
            links_len: iter::repeat(0).take(total_levels).collect(),
        }
    }

    /// Create a new SkipNode with the given value.  The values of `prev` and
    /// `next` will all be `None` and have to be adjusted.
    pub fn new(value: V, level: usize) -> Self {
        SkipNode {
            value: Some(value),
            level,
            next: None,
            prev: None,
            links: iter::repeat(None).take(level + 1).collect(),
            links_len: iter::repeat(0).take(level + 1).collect(),
        }
    }

    /// Consumes the node returning the value it contains.
    pub fn into_inner(mut self) -> Option<V> {
        self.value.take()
    }

    /// Returns `true` is the node is a head-node.
    pub fn is_head(&self) -> bool {
        self.prev.is_none()
    }

    /// Try to move to next nth node at specified level.
    /// If it's impossible, then move as far as possible.
    /// Returns a reference to the new node and the distance travelled.
    pub fn advance_len(&self, level: usize, mut max_distance: usize) -> (&Self, usize) {
        self.advance_while(level, move |current_node, _| {
            let travelled = current_node.links_len[level];
            if travelled <= max_distance {
                max_distance -= travelled;
                return true;
            } else {
                return false;
            }
        })
    }

    /// Try to move to next nth node at specified level.
    /// If it's impossible, then move as far as possible.
    /// Returns a mutable reference to the new node and the distance travelled.
    pub fn advance_len_mut(&mut self, level: usize, mut max_distance: usize) -> (&mut Self, usize) {
        self.advance_while_mut(level, move |current_node, _| {
            let travelled = current_node.links_len[level];
            if travelled <= max_distance {
                max_distance -= travelled;
                return true;
            } else {
                return false;
            }
        })
    }

    pub fn advance_while(
        &self,
        level: usize,
        mut pred: impl FnMut(&Self, &Self) -> bool,
    ) -> (&Self, usize) {
        let mut current = self;
        let mut travelled = 0;
        loop {
            match current.advance_if(level, &mut pred) {
                Ok((node, steps)) => {
                    current = node;
                    travelled += steps;
                }
                Err(node) => return (node, travelled),
            }
        }
    }

    pub fn advance_while_mut(
        &mut self,
        level: usize,
        mut pred: impl FnMut(&Self, &Self) -> bool,
    ) -> (&mut Self, usize) {
        let mut current = self;
        let mut travelled = 0;
        loop {
            match current.advance_if_mut(level, &mut pred) {
                Ok((node, steps)) => {
                    current = node;
                    travelled += steps;
                }
                Err(node) => return (node, travelled),
            }
        }
    }

    // Due to Rust lifetime semantics, the lifetime of result is the same as self.
    // Sometimes Rust cannot determine the result is unused, e.g. in a loop.
    // As a result, self might be  borrowed forever and caller cannot return that value
    // if they call this function in a loop.
    // Therefore this function always return self when it fails to advance.
    pub fn advance_if_mut<'a>(
        &'a mut self,
        level: usize,
        predicate: impl FnOnce(&Self, &Self) -> bool,
    ) -> Result<(&'a mut Self, usize), &'a mut Self> {
        let next = unsafe { self.links[level].and_then(|ptr| ptr.as_mut()) };
        match next {
            Some(next) if predicate(self, next) => Ok((next, self.links_len[level])),
            _ => Err(self),
        }
    }

    pub fn advance_if<'a>(
        &'a self,
        level: usize,
        predicate: impl FnOnce(&Self, &Self) -> bool,
    ) -> Result<(&'a Self, usize), &'a Self> {
        let next = unsafe { self.links[level].and_then(|ptr| ptr.as_ref()) };
        match next {
            Some(next) if predicate(self, next) => Ok((next, self.links_len[level])),
            _ => Err(self),
        }
    }
}

impl<V> Drop for SkipNode<V> {
    fn drop(&mut self) {
        while let Some(mut node) = self.next.take() {
            self.next = node.next.take();
        }
    }
}

// ///////////////////////////////////////////////
// Trait implementation
// ///////////////////////////////////////////////

impl<V> fmt::Display for SkipNode<V>
where
    V: fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(ref v) = self.value {
            write!(f, "{}", v)
        } else {
            Ok(())
        }
    }
}

// /////////////////////////////////
// Iterators
// /////////////////////////////////
// Since Iterators (currently) only pop from front and back,
// they can be shared by some data structures.
// There's no need for a dummy head (that contains no value) in the iterator.
// so the members are named first and last instaed of head/end to avoid confusion.

/// Consuming iterator.
pub struct IntoIter<T> {
    pub(crate) first: Option<Box<SkipNode<T>>>,
    pub(crate) last: *mut SkipNode<T>,
    pub(crate) size: usize,
}

impl<T> Iterator for IntoIter<T> {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        let mut popped_node = self.first.take()?;
        self.size -= 1;
        self.first = popped_node.next.take().map(|mut node| {
            node.prev = None;
            node
        });
        if self.first.is_none() {
            self.last = ptr::null_mut();
        }
        popped_node.into_inner()
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.size, Some(self.size))
    }
}

impl<T> DoubleEndedIterator for IntoIter<T> {
    fn next_back(&mut self) -> Option<T> {
        if self.first.is_none() {
            return None;
        }
        assert!(!self.last.is_null());
        unsafe {
            let new_last = (*self.last).prev;
            let popped_node = match new_last {
                Some(new_last) => {
                    self.last = new_last;
                    (*new_last).next.take().unwrap()
                }
                None => self.first.take().unwrap(),
            };
            self.size -= 1;
            popped_node.into_inner()
        }
    }
}
<|endoftext|>


<commit_before>use core::fmt;
use spin::Mutex;
use volatile::Volatile;
use lazy_static::lazy_static;

lazy_static! {
    /// A global `Writer` instance that can be used for printing to the VGA text buffer.
    ///
    /// Used by the `print!` and `println!` macros.
    pub static ref WRITER: Mutex<Writer> = Mutex::new(Writer {
        column_position: 0,
        color_code: ColorCode::new(Color::Yellow, Color::Black),
        buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
    });
}

/// The standard color palette in VGA text mode.
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Color {
    Black = 0,
    Blue = 1,
    Green = 2,
    Cyan = 3,
    Red = 4,
    Magenta = 5,
    Brown = 6,
    LightGray = 7,
    DarkGray = 8,
    LightBlue = 9,
    LightGreen = 10,
    LightCyan = 11,
    LightRed = 12,
    Pink = 13,
    Yellow = 14,
    White = 15,
}

/// A combination of a foreground and a background color.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct ColorCode(u8);

impl ColorCode {
    /// Create a new `ColorCode` with the given foreground and background colors.
    fn new(foreground: Color, background: Color) -> ColorCode {
        ColorCode((background as u8) << 4 | (foreground as u8))
    }
}

/// A screen character in the VGA text buffer, consisting of an ASCII character and a `ColorCode`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
struct ScreenChar {
    ascii_character: u8,
    color_code: ColorCode,
}

/// The height of the text buffer (normally 25 lines).
const BUFFER_HEIGHT: usize = 25;
/// The width of the text buffer (normally 80 columns).
const BUFFER_WIDTH: usize = 80;

/// A structure representing the VGA text buffer.
struct Buffer {
    chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
}

/// A writer type that allows writing ASCII bytes and strings to an underlying `Buffer`.
///
/// Wraps lines at `BUFFER_WIDTH`. Supports newline characters and implements the
/// `core::fmt::Write` trait.
pub struct Writer {
    column_position: usize,
    color_code: ColorCode,
    buffer: &'static mut Buffer,
}

impl Writer {
    /// Writes an ASCII byte to the buffer.
    ///
    /// Wraps lines at `BUFFER_WIDTH`. Supports the `\n` newline character.
    pub fn write_byte(&mut self, byte: u8) {
        match byte {
            b'\n' => self.new_line(),
            byte => {
                if self.column_position >= BUFFER_WIDTH {
                    self.new_line();
                }

                let row = BUFFER_HEIGHT - 1;
                let col = self.column_position;

                let color_code = self.color_code;
                self.buffer.chars[row][col].write(ScreenChar {
                    ascii_character: byte,
                    color_code,
                });
                self.column_position += 1;
            }
        }
    }

    /// Writes the given ASCII string to the buffer.
    ///
    /// Wraps lines at `BUFFER_WIDTH`. Supports the `\n` newline character. Does **not**
    /// support strings with non-ASCII characters, since they can't be printed in the VGA text
    /// mode.
    fn write_string(&mut self, s: &str) {
        for byte in s.bytes() {
            match byte {
                // printable ASCII byte or newline
                0x20...0x7e | b'\n' => self.write_byte(byte),
                // not part of printable ASCII range
                _ => self.write_byte(0xfe),
            }
        }
    }

    /// Shifts all lines one line up and clears the last row.
    fn new_line(&mut self) {
        for row in 1..BUFFER_HEIGHT {
            for col in 0..BUFFER_WIDTH {
                let character = self.buffer.chars[row][col].read();
                self.buffer.chars[row - 1][col].write(character);
            }
        }
        self.clear_row(BUFFER_HEIGHT - 1);
        self.column_position = 0;
    }

    /// Clears a row by overwriting it with blank characters.
    fn clear_row(&mut self, row: usize) {
        let blank = ScreenChar {
            ascii_character: b' ',
            color_code: self.color_code,
        };
        for col in 0..BUFFER_WIDTH {
            self.buffer.chars[row][col].write(blank);
        }
    }
}

impl fmt::Write for Writer {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        self.write_string(s);
        Ok(())
    }
}

/// Like the `print!` macro in the standard library, but prints to the VGA text buffer.
#[macro_export]
macro_rules! print {
    ($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*)));
}

/// Like the `print!` macro in the standard library, but prints to the VGA text buffer.
#[macro_export]
macro_rules! println {
    () => ($crate::print!("\n"));
    ($fmt:expr) => ($crate::print!(concat!($fmt, "\n")));
    ($fmt:expr, $($arg:tt)*) => ($crate::print!(concat!($fmt, "\n"), $($arg)*));
}

/// Prints the given formatted string to the VGA text buffer through the global `WRITER` instance.
pub fn _print(args: fmt::Arguments) {
    use core::fmt::Write;
    use x86_64::instructions::interrupts;

    interrupts::without_interrupts(|| {
        WRITER.lock().write_fmt(args).unwrap();
    });
}

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

    fn construct_writer() -> Writer {
        use std::boxed::Box;

        let buffer = construct_buffer();
        Writer {
            column_position: 0,
            color_code: ColorCode::new(Color::Blue, Color::Magenta),
            buffer: Box::leak(Box::new(buffer)),
        }
    }

    fn construct_buffer() -> Buffer {
        use array_init::array_init;

        Buffer {
            chars: array_init(|_| array_init(|_| Volatile::new(empty_char()))),
        }
    }

    fn empty_char() -> ScreenChar {
        ScreenChar {
            ascii_character: b' ',
            color_code: ColorCode::new(Color::Green, Color::Brown),
        }
    }

    #[test]
    fn write_byte() {
        let mut writer = construct_writer();
        writer.write_byte(b'X');
        writer.write_byte(b'Y');

        for (i, row) in writer.buffer.chars.iter().enumerate() {
            for (j, screen_char) in row.iter().enumerate() {
                let screen_char = screen_char.read();
                if i == BUFFER_HEIGHT - 1 && j == 0 {
                    assert_eq!(screen_char.ascii_character, b'X');
                    assert_eq!(screen_char.color_code, writer.color_code);
                } else if i == BUFFER_HEIGHT - 1 && j == 1 {
                    assert_eq!(screen_char.ascii_character, b'Y');
                    assert_eq!(screen_char.color_code, writer.color_code);
                } else {
                    assert_eq!(screen_char, empty_char());
                }
            }
        }
    }

    #[test]
    fn write_formatted() {
        use core::fmt::Write;

        let mut writer = construct_writer();
        writeln!(&mut writer, "a").unwrap();
        writeln!(&mut writer, "b{}", "c").unwrap();

        for (i, row) in writer.buffer.chars.iter().enumerate() {
            for (j, screen_char) in row.iter().enumerate() {
                let screen_char = screen_char.read();
                if i == BUFFER_HEIGHT - 3 && j == 0 {
                    assert_eq!(screen_char.ascii_character, b'a');
                    assert_eq!(screen_char.color_code, writer.color_code);
                } else if i == BUFFER_HEIGHT - 2 && j == 0 {
                    assert_eq!(screen_char.ascii_character, b'b');
                    assert_eq!(screen_char.color_code, writer.color_code);
                } else if i == BUFFER_HEIGHT - 2 && j == 1 {
                    assert_eq!(screen_char.ascii_character, b'c');
                    assert_eq!(screen_char.color_code, writer.color_code);
                } else if i >= BUFFER_HEIGHT - 2 {
                    assert_eq!(screen_char.ascii_character, b' ');
                    assert_eq!(screen_char.color_code, writer.color_code);
                } else {
                    assert_eq!(screen_char, empty_char());
                }
            }
        }
    }
}
<commit_msg>Update to a newer println definition<commit_after>use core::fmt;
use spin::Mutex;
use volatile::Volatile;
use lazy_static::lazy_static;

lazy_static! {
    /// A global `Writer` instance that can be used for printing to the VGA text buffer.
    ///
    /// Used by the `print!` and `println!` macros.
    pub static ref WRITER: Mutex<Writer> = Mutex::new(Writer {
        column_position: 0,
        color_code: ColorCode::new(Color::Yellow, Color::Black),
        buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
    });
}

/// The standard color palette in VGA text mode.
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Color {
    Black = 0,
    Blue = 1,
    Green = 2,
    Cyan = 3,
    Red = 4,
    Magenta = 5,
    Brown = 6,
    LightGray = 7,
    DarkGray = 8,
    LightBlue = 9,
    LightGreen = 10,
    LightCyan = 11,
    LightRed = 12,
    Pink = 13,
    Yellow = 14,
    White = 15,
}

/// A combination of a foreground and a background color.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct ColorCode(u8);

impl ColorCode {
    /// Create a new `ColorCode` with the given foreground and background colors.
    fn new(foreground: Color, background: Color) -> ColorCode {
        ColorCode((background as u8) << 4 | (foreground as u8))
    }
}

/// A screen character in the VGA text buffer, consisting of an ASCII character and a `ColorCode`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
struct ScreenChar {
    ascii_character: u8,
    color_code: ColorCode,
}

/// The height of the text buffer (normally 25 lines).
const BUFFER_HEIGHT: usize = 25;
/// The width of the text buffer (normally 80 columns).
const BUFFER_WIDTH: usize = 80;

/// A structure representing the VGA text buffer.
struct Buffer {
    chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
}

/// A writer type that allows writing ASCII bytes and strings to an underlying `Buffer`.
///
/// Wraps lines at `BUFFER_WIDTH`. Supports newline characters and implements the
/// `core::fmt::Write` trait.
pub struct Writer {
    column_position: usize,
    color_code: ColorCode,
    buffer: &'static mut Buffer,
}

impl Writer {
    /// Writes an ASCII byte to the buffer.
    ///
    /// Wraps lines at `BUFFER_WIDTH`. Supports the `\n` newline character.
    pub fn write_byte(&mut self, byte: u8) {
        match byte {
            b'\n' => self.new_line(),
            byte => {
                if self.column_position >= BUFFER_WIDTH {
                    self.new_line();
                }

                let row = BUFFER_HEIGHT - 1;
                let col = self.column_position;

                let color_code = self.color_code;
                self.buffer.chars[row][col].write(ScreenChar {
                    ascii_character: byte,
                    color_code,
                });
                self.column_position += 1;
            }
        }
    }

    /// Writes the given ASCII string to the buffer.
    ///
    /// Wraps lines at `BUFFER_WIDTH`. Supports the `\n` newline character. Does **not**
    /// support strings with non-ASCII characters, since they can't be printed in the VGA text
    /// mode.
    fn write_string(&mut self, s: &str) {
        for byte in s.bytes() {
            match byte {
                // printable ASCII byte or newline
                0x20...0x7e | b'\n' => self.write_byte(byte),
                // not part of printable ASCII range
                _ => self.write_byte(0xfe),
            }
        }
    }

    /// Shifts all lines one line up and clears the last row.
    fn new_line(&mut self) {
        for row in 1..BUFFER_HEIGHT {
            for col in 0..BUFFER_WIDTH {
                let character = self.buffer.chars[row][col].read();
                self.buffer.chars[row - 1][col].write(character);
            }
        }
        self.clear_row(BUFFER_HEIGHT - 1);
        self.column_position = 0;
    }

    /// Clears a row by overwriting it with blank characters.
    fn clear_row(&mut self, row: usize) {
        let blank = ScreenChar {
            ascii_character: b' ',
            color_code: self.color_code,
        };
        for col in 0..BUFFER_WIDTH {
            self.buffer.chars[row][col].write(blank);
        }
    }
}

impl fmt::Write for Writer {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        self.write_string(s);
        Ok(())
    }
}

/// Like the `print!` macro in the standard library, but prints to the VGA text buffer.
#[macro_export]
macro_rules! print {
    ($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*)));
}

/// Like the `println!` macro in the standard library, but prints to the VGA text buffer.
#[macro_export]
macro_rules! println {
    () => ($crate::print!("\n"));
    ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
}

/// Prints the given formatted string to the VGA text buffer through the global `WRITER` instance.
pub fn _print(args: fmt::Arguments) {
    use core::fmt::Write;
    use x86_64::instructions::interrupts;

    interrupts::without_interrupts(|| {
        WRITER.lock().write_fmt(args).unwrap();
    });
}

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

    fn construct_writer() -> Writer {
        use std::boxed::Box;

        let buffer = construct_buffer();
        Writer {
            column_position: 0,
            color_code: ColorCode::new(Color::Blue, Color::Magenta),
            buffer: Box::leak(Box::new(buffer)),
        }
    }

    fn construct_buffer() -> Buffer {
        use array_init::array_init;

        Buffer {
            chars: array_init(|_| array_init(|_| Volatile::new(empty_char()))),
        }
    }

    fn empty_char() -> ScreenChar {
        ScreenChar {
            ascii_character: b' ',
            color_code: ColorCode::new(Color::Green, Color::Brown),
        }
    }

    #[test]
    fn write_byte() {
        let mut writer = construct_writer();
        writer.write_byte(b'X');
        writer.write_byte(b'Y');

        for (i, row) in writer.buffer.chars.iter().enumerate() {
            for (j, screen_char) in row.iter().enumerate() {
                let screen_char = screen_char.read();
                if i == BUFFER_HEIGHT - 1 && j == 0 {
                    assert_eq!(screen_char.ascii_character, b'X');
                    assert_eq!(screen_char.color_code, writer.color_code);
                } else if i == BUFFER_HEIGHT - 1 && j == 1 {
                    assert_eq!(screen_char.ascii_character, b'Y');
                    assert_eq!(screen_char.color_code, writer.color_code);
                } else {
                    assert_eq!(screen_char, empty_char());
                }
            }
        }
    }

    #[test]
    fn write_formatted() {
        use core::fmt::Write;

        let mut writer = construct_writer();
        writeln!(&mut writer, "a").unwrap();
        writeln!(&mut writer, "b{}", "c").unwrap();

        for (i, row) in writer.buffer.chars.iter().enumerate() {
            for (j, screen_char) in row.iter().enumerate() {
                let screen_char = screen_char.read();
                if i == BUFFER_HEIGHT - 3 && j == 0 {
                    assert_eq!(screen_char.ascii_character, b'a');
                    assert_eq!(screen_char.color_code, writer.color_code);
                } else if i == BUFFER_HEIGHT - 2 && j == 0 {
                    assert_eq!(screen_char.ascii_character, b'b');
                    assert_eq!(screen_char.color_code, writer.color_code);
                } else if i == BUFFER_HEIGHT - 2 && j == 1 {
                    assert_eq!(screen_char.ascii_character, b'c');
                    assert_eq!(screen_char.color_code, writer.color_code);
                } else if i >= BUFFER_HEIGHT - 2 {
                    assert_eq!(screen_char.ascii_character, b' ');
                    assert_eq!(screen_char.color_code, writer.color_code);
                } else {
                    assert_eq!(screen_char, empty_char());
                }
            }
        }
    }
}
<|endoftext|>