<commit_before>// Copyright (c) 2018, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

#![allow(dead_code)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]

include!(concat!(env!("OUT_DIR"), "/aom.rs"));

use super::*;
use rand::{ChaChaRng, Rng, SeedableRng};
use std::{mem, ptr, slice};
use std::collections::VecDeque;
use std::ffi::CStr;
use std::sync::Arc;

fn fill_frame(ra: &mut ChaChaRng, frame: &mut Frame) {
  for plane in frame.planes.iter_mut() {
    let stride = plane.cfg.stride;
    for row in plane.data.chunks_mut(stride) {
      for mut pixel in row {
        let v: u8 = ra.gen();
        *pixel = v as u16;
      }
    }
  }
}

struct AomDecoder {
  dec: aom_codec_ctx
}

fn setup_decoder(w: usize, h: usize) -> AomDecoder {
  unsafe {
    let interface = aom_codec_av1_dx();
    let mut dec: AomDecoder = mem::uninitialized();
    let cfg = aom_codec_dec_cfg_t {
      threads: 1,
      w: w as u32,
      h: h as u32,
      allow_lowbitdepth: 1,
      cfg: cfg_options { ext_partition: 1 }
    };

    let ret = aom_codec_dec_init_ver(
      &mut dec.dec,
      interface,
      &cfg,
      0,
      AOM_DECODER_ABI_VERSION as i32
    );
    if ret != 0 {
      panic!("Cannot instantiate the decoder {}", ret);
    }

    dec
  }
}

impl Drop for AomDecoder {
  fn drop(&mut self) {
    unsafe { aom_codec_destroy(&mut self.dec) };
  }
}

fn setup_encoder(
  w: usize, h: usize, speed: usize, quantizer: usize, bit_depth: usize,
  chroma_sampling: ChromaSampling, min_keyint: u64, max_keyint: u64,
  low_latency: bool
) -> Context {
  unsafe {
    av1_rtcd();
    aom_dsp_rtcd();
  }

  let mut enc = EncoderConfig::with_speed_preset(speed);
  enc.quantizer = quantizer;
  enc.min_key_frame_interval = min_keyint;
  enc.max_key_frame_interval = max_keyint;
  enc.low_latency = low_latency;

  let cfg = Config {
    frame_info: FrameInfo {
      width: w,
      height: h,
      bit_depth,
      chroma_sampling,
      ..Default::default()
    },
    timebase: Rational::new(1, 1000),
    enc
  };

  cfg.new_context()
}

// TODO: support non-multiple-of-16 dimensions
static DIMENSION_OFFSETS: &[(usize, usize)] =
  &[(0, 0), (4, 4), (8, 8), (16, 16)];

fn speed(s: usize) {
  let quantizer = 100;
  let limit = 5;
  let w = 64;
  let h = 80;

  for b in DIMENSION_OFFSETS.iter() {
      encode_decode(w + b.0, h + b.1, s, quantizer, limit, 8, 15, 15, true);
  }
}

macro_rules! test_speeds {
  ($($S:expr),+) => {
    $(
        paste::item!{
            #[test]
            #[ignore]
            fn [<speed_ $S>]() {
                speed($S)
            }
        }
    )*
  }
}

test_speeds!{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }

macro_rules! test_dimensions {
  ($(($W:expr, $H:expr)),+) => {
    $(
        paste::item!{
            #[test]
            #[ignore]
            fn [<dimension_ $W x $H>]() {
                dimension($W, $H)
            }
        }
    )*
  }
}

test_dimensions!{
  (8, 8),
  (16, 16),
  (32, 32),
  (64, 64),
  (128, 128),
  (256, 256),
  (512, 512),
  (1024, 1024),
  (2048, 2048),
  (258, 258),
  (260, 260),
  (262, 262),
  (264, 264),
  (265, 265)
}

fn dimension(w: usize, h: usize) {
  let quantizer = 100;
  let limit = 1;
  let speed = 4;

  encode_decode(w, h, speed, quantizer, limit, 8, 15, 15, true);
}

#[test]
fn quantizer() {
  let limit = 5;
  let w = 64;
  let h = 80;
  let speed = 4;

  for b in DIMENSION_OFFSETS.iter() {
    for &q in [80, 100, 120].iter() {
      encode_decode(w + b.0, h + b.1, speed, q, limit, 8, 15, 15, true);
    }
  }
}

#[test]
fn keyframes() {
  let limit = 12;
  let w = 64;
  let h = 80;
  let speed = 10;
  let q = 100;

  encode_decode(w, h, speed, q, limit, 8, 6, 6, true);
}

#[test]
fn reordering() {
  let limit = 12;
  let w = 64;
  let h = 80;
  let speed = 10;
  let q = 100;

  for keyint in &[4, 5, 6] {
    encode_decode(w, h, speed, q, limit, 8, *keyint, *keyint, false);
  }
}

#[test]
#[ignore]
fn odd_size_frame_with_full_rdo() {
  let limit = 3;
  let w = 512 + 32 + 16 + 5;
  let h = 512 + 16 + 5;
  let speed = 0;
  let qindex = 100;

  encode_decode(w, h, speed, qindex, limit, 8, 15, 15, true);
}

#[test]
fn high_bd() {
  let quantizer = 100;
  let limit = 3; // Include inter frames
  let speed = 0; // Test as many tools as possible
  let w = 64;
  let h = 80;

  // 10-bit
  encode_decode(w, h, speed, quantizer, limit, 10, 15, 15, true);

  // 12-bit
  encode_decode(w, h, speed, quantizer, limit, 12, 15, 15, true);
}

fn compare_plane<T: Ord + std::fmt::Debug>(
  rec: &[T], rec_stride: usize, dec: &[T], dec_stride: usize, width: usize,
  height: usize
) {
  for line in rec.chunks(rec_stride).zip(dec.chunks(dec_stride)).take(height) {
    assert_eq!(&line.0[..width], &line.1[..width]);
  }
}

fn compare_img(img: *const aom_image_t, frame: &Frame, bit_depth: usize, width: usize, height: usize) {
  let img = unsafe { *img };
  let img_iter = img.planes.iter().zip(img.stride.iter());

  for (img_plane, frame_plane) in img_iter.zip(frame.planes.iter()) {
    let w = width >> frame_plane.cfg.xdec;
    let h = height >> frame_plane.cfg.ydec;
    let rec_stride = frame_plane.cfg.stride;

    if bit_depth > 8 {
      let dec_stride = *img_plane.1 as usize / 2;

      let dec = unsafe {
        let data = *img_plane.0 as *const u16;
        let size = dec_stride * h;

        slice::from_raw_parts(data, size)
      };

      let rec: Vec<u16> =
        frame_plane.data_origin().iter().map(|&v| v).collect();

      compare_plane::<u16>(&rec[..], rec_stride, dec, dec_stride, w, h);
    } else {
      let dec_stride = *img_plane.1 as usize;

      let dec = unsafe {
        let data = *img_plane.0 as *const u8;
        let size = dec_stride * h;

        slice::from_raw_parts(data, size)
      };

      let rec: Vec<u8> =
        frame_plane.data_origin().iter().map(|&v| v as u8).collect();

      compare_plane::<u8>(&rec[..], rec_stride, dec, dec_stride, w, h);
    }
  }
}

fn encode_decode(
  w: usize, h: usize, speed: usize, quantizer: usize, limit: usize,
  bit_depth: usize, min_keyint: u64, max_keyint: u64, low_latency: bool
) {
  let mut ra = ChaChaRng::from_seed([0; 32]);

  let mut dec = setup_decoder(w, h);
  let mut ctx =
    setup_encoder(w, h, speed, quantizer, bit_depth, ChromaSampling::Cs420,
                  min_keyint, max_keyint, low_latency);

  println!("Encoding {}x{} speed {} quantizer {}", w, h, speed, quantizer);

  let mut iter: aom_codec_iter_t = ptr::null_mut();

  let mut rec_fifo = VecDeque::new();

  for _ in 0..limit {
    let mut input = ctx.new_frame();
    fill_frame(&mut ra, Arc::get_mut(&mut input).unwrap());

    let _ = ctx.send_frame(input);

    let mut done = false;
    let mut corrupted_count = 0;
    while !done {
      let res = ctx.receive_packet();
      if let Ok(pkt) = res {
        println!("Encoded packet {}", pkt.number);

        if let Some(pkt_rec) = pkt.rec {
          rec_fifo.push_back(pkt_rec.clone());
        }

        let packet = pkt.data;

        unsafe {
          println!("Decoding frame {}", pkt.number);
          let ret = aom_codec_decode(
            &mut dec.dec,
            packet.as_ptr(),
            packet.len(),
            ptr::null_mut()
          );
          println!("Decoded. -> {}", ret);
          if ret != 0 {
            let error_msg = aom_codec_error(&mut dec.dec);
            println!(
              "  Decode codec_decode failed: {}",
              CStr::from_ptr(error_msg).to_string_lossy()
            );
            let detail = aom_codec_error_detail(&mut dec.dec);
            if !detail.is_null() {
              println!(
                "  Decode codec_decode failed {}",
                CStr::from_ptr(detail).to_string_lossy()
              );
            }

            corrupted_count += 1;
          }

          if ret == 0 {
            loop {
              println!("Retrieving frame");
              let img = aom_codec_get_frame(&mut dec.dec, &mut iter);
              println!("Retrieved.");
              if img.is_null() {
                break;
              }
              let mut corrupted = 0;
              let ret = aom_codec_control_(
                &mut dec.dec,
                aom_dec_control_id_AOMD_GET_FRAME_CORRUPTED as i32,
                &mut corrupted
              );
              if ret != 0 {
                let detail = aom_codec_error_detail(&mut dec.dec);
                panic!(
                  "Decode codec_control failed {}",
                  CStr::from_ptr(detail).to_string_lossy()
                );
              }
              corrupted_count += corrupted;

              let rec = rec_fifo.pop_front().unwrap();
              compare_img(img, &rec, bit_depth, w, h);
            }
          }
        }
      } else {
        done = true;
      }
    }
    assert_eq!(corrupted_count, 0);
  }
}
<commit_msg>Unignore the speed tests<commit_after>// Copyright (c) 2018, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

#![allow(dead_code)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]

include!(concat!(env!("OUT_DIR"), "/aom.rs"));

use super::*;
use rand::{ChaChaRng, Rng, SeedableRng};
use std::{mem, ptr, slice};
use std::collections::VecDeque;
use std::ffi::CStr;
use std::sync::Arc;

fn fill_frame(ra: &mut ChaChaRng, frame: &mut Frame) {
  for plane in frame.planes.iter_mut() {
    let stride = plane.cfg.stride;
    for row in plane.data.chunks_mut(stride) {
      for mut pixel in row {
        let v: u8 = ra.gen();
        *pixel = v as u16;
      }
    }
  }
}

struct AomDecoder {
  dec: aom_codec_ctx
}

fn setup_decoder(w: usize, h: usize) -> AomDecoder {
  unsafe {
    let interface = aom_codec_av1_dx();
    let mut dec: AomDecoder = mem::uninitialized();
    let cfg = aom_codec_dec_cfg_t {
      threads: 1,
      w: w as u32,
      h: h as u32,
      allow_lowbitdepth: 1,
      cfg: cfg_options { ext_partition: 1 }
    };

    let ret = aom_codec_dec_init_ver(
      &mut dec.dec,
      interface,
      &cfg,
      0,
      AOM_DECODER_ABI_VERSION as i32
    );
    if ret != 0 {
      panic!("Cannot instantiate the decoder {}", ret);
    }

    dec
  }
}

impl Drop for AomDecoder {
  fn drop(&mut self) {
    unsafe { aom_codec_destroy(&mut self.dec) };
  }
}

fn setup_encoder(
  w: usize, h: usize, speed: usize, quantizer: usize, bit_depth: usize,
  chroma_sampling: ChromaSampling, min_keyint: u64, max_keyint: u64,
  low_latency: bool
) -> Context {
  unsafe {
    av1_rtcd();
    aom_dsp_rtcd();
  }

  let mut enc = EncoderConfig::with_speed_preset(speed);
  enc.quantizer = quantizer;
  enc.min_key_frame_interval = min_keyint;
  enc.max_key_frame_interval = max_keyint;
  enc.low_latency = low_latency;

  let cfg = Config {
    frame_info: FrameInfo {
      width: w,
      height: h,
      bit_depth,
      chroma_sampling,
      ..Default::default()
    },
    timebase: Rational::new(1, 1000),
    enc
  };

  cfg.new_context()
}

// TODO: support non-multiple-of-16 dimensions
static DIMENSION_OFFSETS: &[(usize, usize)] =
  &[(0, 0), (4, 4), (8, 8), (16, 16)];

fn speed(s: usize) {
  let quantizer = 100;
  let limit = 5;
  let w = 64;
  let h = 80;

  for b in DIMENSION_OFFSETS.iter() {
      encode_decode(w + b.0, h + b.1, s, quantizer, limit, 8, 15, 15, true);
  }
}

macro_rules! test_speeds {
  ($($S:expr),+) => {
    $(
        paste::item!{
            #[test]
            #[ignore]
            fn [<speed_ $S>]() {
                speed($S)
            }
        }
    )*
  }
}

test_speeds!{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }

macro_rules! test_dimensions {
  ($(($W:expr, $H:expr)),+) => {
    $(
        paste::item!{
            #[test]
            fn [<dimension_ $W x $H>]() {
                dimension($W, $H)
            }
        }
    )*
  }
}

test_dimensions!{
  (8, 8),
  (16, 16),
  (32, 32),
  (64, 64),
  (128, 128),
  (256, 256),
  (512, 512),
  (1024, 1024),
  (2048, 2048),
  (258, 258),
  (260, 260),
  (262, 262),
  (264, 264),
  (265, 265)
}

fn dimension(w: usize, h: usize) {
  let quantizer = 100;
  let limit = 1;
  let speed = 4;

  encode_decode(w, h, speed, quantizer, limit, 8, 15, 15, true);
}

#[test]
fn quantizer() {
  let limit = 5;
  let w = 64;
  let h = 80;
  let speed = 4;

  for b in DIMENSION_OFFSETS.iter() {
    for &q in [80, 100, 120].iter() {
      encode_decode(w + b.0, h + b.1, speed, q, limit, 8, 15, 15, true);
    }
  }
}

#[test]
fn keyframes() {
  let limit = 12;
  let w = 64;
  let h = 80;
  let speed = 10;
  let q = 100;

  encode_decode(w, h, speed, q, limit, 8, 6, 6, true);
}

#[test]
fn reordering() {
  let limit = 12;
  let w = 64;
  let h = 80;
  let speed = 10;
  let q = 100;

  for keyint in &[4, 5, 6] {
    encode_decode(w, h, speed, q, limit, 8, *keyint, *keyint, false);
  }
}

#[test]
#[ignore]
fn odd_size_frame_with_full_rdo() {
  let limit = 3;
  let w = 512 + 32 + 16 + 5;
  let h = 512 + 16 + 5;
  let speed = 0;
  let qindex = 100;

  encode_decode(w, h, speed, qindex, limit, 8, 15, 15, true);
}

#[test]
fn high_bd() {
  let quantizer = 100;
  let limit = 3; // Include inter frames
  let speed = 0; // Test as many tools as possible
  let w = 64;
  let h = 80;

  // 10-bit
  encode_decode(w, h, speed, quantizer, limit, 10, 15, 15, true);

  // 12-bit
  encode_decode(w, h, speed, quantizer, limit, 12, 15, 15, true);
}

fn compare_plane<T: Ord + std::fmt::Debug>(
  rec: &[T], rec_stride: usize, dec: &[T], dec_stride: usize, width: usize,
  height: usize
) {
  for line in rec.chunks(rec_stride).zip(dec.chunks(dec_stride)).take(height) {
    assert_eq!(&line.0[..width], &line.1[..width]);
  }
}

fn compare_img(img: *const aom_image_t, frame: &Frame, bit_depth: usize, width: usize, height: usize) {
  let img = unsafe { *img };
  let img_iter = img.planes.iter().zip(img.stride.iter());

  for (img_plane, frame_plane) in img_iter.zip(frame.planes.iter()) {
    let w = width >> frame_plane.cfg.xdec;
    let h = height >> frame_plane.cfg.ydec;
    let rec_stride = frame_plane.cfg.stride;

    if bit_depth > 8 {
      let dec_stride = *img_plane.1 as usize / 2;

      let dec = unsafe {
        let data = *img_plane.0 as *const u16;
        let size = dec_stride * h;

        slice::from_raw_parts(data, size)
      };

      let rec: Vec<u16> =
        frame_plane.data_origin().iter().map(|&v| v).collect();

      compare_plane::<u16>(&rec[..], rec_stride, dec, dec_stride, w, h);
    } else {
      let dec_stride = *img_plane.1 as usize;

      let dec = unsafe {
        let data = *img_plane.0 as *const u8;
        let size = dec_stride * h;

        slice::from_raw_parts(data, size)
      };

      let rec: Vec<u8> =
        frame_plane.data_origin().iter().map(|&v| v as u8).collect();

      compare_plane::<u8>(&rec[..], rec_stride, dec, dec_stride, w, h);
    }
  }
}

fn encode_decode(
  w: usize, h: usize, speed: usize, quantizer: usize, limit: usize,
  bit_depth: usize, min_keyint: u64, max_keyint: u64, low_latency: bool
) {
  let mut ra = ChaChaRng::from_seed([0; 32]);

  let mut dec = setup_decoder(w, h);
  let mut ctx =
    setup_encoder(w, h, speed, quantizer, bit_depth, ChromaSampling::Cs420,
                  min_keyint, max_keyint, low_latency);

  println!("Encoding {}x{} speed {} quantizer {}", w, h, speed, quantizer);

  let mut iter: aom_codec_iter_t = ptr::null_mut();

  let mut rec_fifo = VecDeque::new();

  for _ in 0..limit {
    let mut input = ctx.new_frame();
    fill_frame(&mut ra, Arc::get_mut(&mut input).unwrap());

    let _ = ctx.send_frame(input);

    let mut done = false;
    let mut corrupted_count = 0;
    while !done {
      let res = ctx.receive_packet();
      if let Ok(pkt) = res {
        println!("Encoded packet {}", pkt.number);

        if let Some(pkt_rec) = pkt.rec {
          rec_fifo.push_back(pkt_rec.clone());
        }

        let packet = pkt.data;

        unsafe {
          println!("Decoding frame {}", pkt.number);
          let ret = aom_codec_decode(
            &mut dec.dec,
            packet.as_ptr(),
            packet.len(),
            ptr::null_mut()
          );
          println!("Decoded. -> {}", ret);
          if ret != 0 {
            let error_msg = aom_codec_error(&mut dec.dec);
            println!(
              "  Decode codec_decode failed: {}",
              CStr::from_ptr(error_msg).to_string_lossy()
            );
            let detail = aom_codec_error_detail(&mut dec.dec);
            if !detail.is_null() {
              println!(
                "  Decode codec_decode failed {}",
                CStr::from_ptr(detail).to_string_lossy()
              );
            }

            corrupted_count += 1;
          }

          if ret == 0 {
            loop {
              println!("Retrieving frame");
              let img = aom_codec_get_frame(&mut dec.dec, &mut iter);
              println!("Retrieved.");
              if img.is_null() {
                break;
              }
              let mut corrupted = 0;
              let ret = aom_codec_control_(
                &mut dec.dec,
                aom_dec_control_id_AOMD_GET_FRAME_CORRUPTED as i32,
                &mut corrupted
              );
              if ret != 0 {
                let detail = aom_codec_error_detail(&mut dec.dec);
                panic!(
                  "Decode codec_control failed {}",
                  CStr::from_ptr(detail).to_string_lossy()
                );
              }
              corrupted_count += corrupted;

              let rec = rec_fifo.pop_front().unwrap();
              compare_img(img, &rec, bit_depth, w, h);
            }
          }
        }
      } else {
        done = true;
      }
    }
    assert_eq!(corrupted_count, 0);
  }
}
<|endoftext|>


<commit_before>//! Interfaces for accessing instance data from hostcalls.
//!
//! This module contains both a Rust-friendly API ([`Vmctx`](struct.Vmctx.html)) as well as C-style
//! exports for compatibility with hostcalls written against `lucet-runtime-c`.

pub use crate::c_api::lucet_vmctx;

use crate::alloc::instance_heap_offset;
use crate::context::Context;
use crate::error::Error;
use crate::instance::{
    Instance, InstanceHandle, InstanceInternal, State, TerminationDetails, CURRENT_INSTANCE,
    HOST_CTX,
};
use std::any::Any;
use std::borrow::{Borrow, BorrowMut};
use std::cell::{Ref, RefCell, RefMut};

/// An opaque handle to a running instance's context.
#[derive(Debug)]
pub struct Vmctx {
    vmctx: *mut lucet_vmctx,
    heap: RefCell<Box<[u8]>>,
    globals: RefCell<Box<[i64]>>,
}

impl Drop for Vmctx {
    fn drop(&mut self) {
        let heap = self.heap.replace(Box::new([]));
        let globals = self.globals.replace(Box::new([]));
        Box::leak(heap);
        Box::leak(globals);
    }
}

pub trait VmctxInternal {
    /// Get a reference to the `Instance` for this guest.
    fn instance(&self) -> &Instance;

    /// Get a mutable reference to the `Instance` for this guest.
    ///
    /// ### Safety
    ///
    /// Using this method, you could hold on to multiple mutable references to the same
    /// `Instance`. Only use one at a time! This method does not take `&mut self` because otherwise
    /// you could not use orthogonal `&mut` refs that come from `Vmctx`, like the heap or
    /// terminating the instance.
    unsafe fn instance_mut(&self) -> &mut Instance;
}

impl VmctxInternal for Vmctx {
    fn instance(&self) -> &Instance {
        unsafe { instance_from_vmctx(self.vmctx) }
    }

    unsafe fn instance_mut(&self) -> &mut Instance {
        instance_from_vmctx(self.vmctx)
    }
}

impl Vmctx {
    /// Create a `Vmctx` from the compiler-inserted `vmctx` argument in a guest
    /// function.
    ///
    /// This is almost certainly not what you want to use to get a `Vmctx`; instead use the `&mut
    /// Vmctx` argument to a `lucet_hostcalls!`-wrapped function.
    pub unsafe fn from_raw(vmctx: *mut lucet_vmctx) -> Vmctx {
        let inst = instance_from_vmctx(vmctx);
        assert!(inst.valid_magic());

        let res = Vmctx {
            vmctx,
            heap: RefCell::new(Box::<[u8]>::from_raw(inst.heap_mut())),
            globals: RefCell::new(Box::<[i64]>::from_raw(inst.globals_mut())),
        };
        res
    }

    /// Return the underlying `vmctx` pointer.
    pub fn as_raw(&self) -> *mut lucet_vmctx {
        self.vmctx
    }

    /// Return the WebAssembly heap as a slice of bytes.
    ///
    /// If the heap is already mutably borrowed by `heap_mut()`, the instance will
    /// terminate with `TerminationDetails::BorrowError`.
    pub fn heap(&self) -> Ref<[u8]> {
        let r = self
            .heap
            .try_borrow()
            .unwrap_or_else(|_| panic!(TerminationDetails::BorrowError("heap")));
        Ref::map(r, |b| b.borrow())
    }

    /// Return the WebAssembly heap as a mutable slice of bytes.
    ///
    /// If the heap is already borrowed by `heap()` or `heap_mut()`, the instance will terminate
    /// with `TerminationDetails::BorrowError`.
    pub fn heap_mut(&self) -> RefMut<[u8]> {
        let r = self
            .heap
            .try_borrow_mut()
            .unwrap_or_else(|_| panic!(TerminationDetails::BorrowError("heap_mut")));
        RefMut::map(r, |b| b.borrow_mut())
    }

    /// Check whether a given range in the host address space overlaps with the memory that backs
    /// the instance heap.
    pub fn check_heap<T>(&self, ptr: *const T, len: usize) -> bool {
        self.instance().check_heap(ptr, len)
    }

    /// Check whether a context value of a particular type exists.
    pub fn contains_embed_ctx<T: Any>(&self) -> bool {
        self.instance().contains_embed_ctx::<T>()
    }

    /// Get a reference to a context value of a particular type.
    ///
    /// If a context of that type does not exist, the instance will terminate with
    /// `TerminationDetails::CtxNotFound`.
    ///
    /// If the context is already mutably borrowed by `get_embed_ctx_mut`, the instance will
    /// terminate with `TerminationDetails::BorrowError`.
    pub fn get_embed_ctx<T: Any>(&self) -> Ref<T> {
        match self.instance().embed_ctx.try_get::<T>() {
            Some(Ok(t)) => t,
            Some(Err(_)) => panic!(TerminationDetails::BorrowError("get_embed_ctx")),
            None => panic!(TerminationDetails::CtxNotFound),
        }
    }

    /// Get a mutable reference to a context value of a particular type.
    ///
    /// If a context of that type does not exist, the instance will terminate with
    /// `TerminationDetails::CtxNotFound`.
    ///
    /// If the context is already borrowed by some other use of `get_embed_ctx` or
    /// `get_embed_ctx_mut`, the instance will terminate with `TerminationDetails::BorrowError`.
    pub fn get_embed_ctx_mut<T: Any>(&self) -> RefMut<T> {
        match unsafe { self.instance_mut().embed_ctx.try_get_mut::<T>() } {
            Some(Ok(t)) => t,
            Some(Err(_)) => panic!(TerminationDetails::BorrowError("get_embed_ctx_mut")),
            None => panic!(TerminationDetails::CtxNotFound),
        }
    }

    /// Terminate this guest and return to the host context without unwinding.
    ///
    /// This is almost certainly not what you want to use to terminate an instance from a hostcall,
    /// as any resources currently in scope will not be dropped. Instead, use
    /// `lucet_hostcall_terminate!` which unwinds to the enclosing hostcall body.
    pub unsafe fn terminate_no_unwind(&mut self, details: TerminationDetails) -> ! {
        self.instance_mut().terminate(details)
    }

    /// Grow the guest memory by the given number of WebAssembly pages.
    ///
    /// On success, returns the number of pages that existed before the call.
    pub fn grow_memory(&mut self, additional_pages: u32) -> Result<u32, Error> {
        unsafe { self.instance_mut().grow_memory(additional_pages) }
    }

    /// Return the WebAssembly globals as a slice of `i64`s.
    ///
    /// If the globals are already mutably borrowed by `globals_mut()`, the instance will terminate
    /// with `TerminationDetails::BorrowError`.
    pub fn globals(&self) -> Ref<[i64]> {
        let r = self
            .globals
            .try_borrow()
            .unwrap_or_else(|_| panic!(TerminationDetails::BorrowError("globals")));
        Ref::map(r, |b| b.borrow())
    }

    /// Return the WebAssembly globals as a mutable slice of `i64`s.
    ///
    /// If the globals are already borrowed by `globals()` or `globals_mut()`, the instance will
    /// terminate with `TerminationDetails::BorrowError`.
    pub fn globals_mut(&self) -> RefMut<[i64]> {
        let r = self
            .globals
            .try_borrow_mut()
            .unwrap_or_else(|_| panic!(TerminationDetails::BorrowError("globals_mut")));
        RefMut::map(r, |b| b.borrow_mut())
    }

    /// Get a function pointer by WebAssembly table and function index.
    ///
    /// This is useful when a hostcall takes a function pointer as its argument, as WebAssembly uses
    /// table indices as its runtime representation of function pointers.
    ///
    /// We do not currently reflect function type information into the Rust type system, so callers
    /// of the returned function must take care to cast it to the correct type before calling. The
    /// correct type will include the `vmctx` argument, which the caller is responsible for passing
    /// from its own context.
    ///
    /// ```no_run
    /// use lucet_runtime_internals::{lucet_hostcalls, lucet_hostcall_terminate};
    /// use lucet_runtime_internals::vmctx::{lucet_vmctx, Vmctx};
    ///
    /// lucet_hostcalls! {
    ///     #[no_mangle]
    ///     pub unsafe extern "C" fn hostcall_call_binop(
    ///         &mut vmctx,
    ///         binop_table_idx: u32,
    ///         binop_func_idx: u32,
    ///         operand1: u32,
    ///         operand2: u32,
    ///     ) -> u32 {
    ///         if let Ok(binop) = vmctx.get_func_from_idx(binop_table_idx, binop_func_idx) {
    ///             let typed_binop = binop as *const extern "C" fn(*mut lucet_vmctx, u32, u32) -> u32;
    ///             unsafe { (*typed_binop)(vmctx.as_raw(), operand1, operand2) }
    ///         } else {
    ///             lucet_hostcall_terminate!("invalid function index")
    ///         }
    ///     }
    /// }
    pub fn get_func_from_idx(
        &self,
        table_idx: u32,
        func_idx: u32,
    ) -> Result<*const extern "C" fn(), Error> {
        self.instance()
            .module()
            .get_func_from_idx(table_idx, func_idx)
    }
}

/// Get an `Instance` from the `vmctx` pointer.
///
/// Only safe to call from within the guest context.
pub unsafe fn instance_from_vmctx<'a>(vmctx: *mut lucet_vmctx) -> &'a mut Instance {
    assert!(!vmctx.is_null(), "vmctx is not null");

    let inst_ptr = (vmctx as usize - instance_heap_offset()) as *mut Instance;

    // We shouldn't actually need to access the thread local, only the exception handler should
    // need to. But, as long as the thread local exists, we should make sure that the guest
    // hasn't pulled any shenanigans and passed a bad vmctx. (Codegen should ensure the guest
    // cant pull any shenanigans but there have been bugs before.)
    CURRENT_INSTANCE.with(|current_instance| {
        if let Some(current_inst_ptr) = current_instance.borrow().map(|nn| nn.as_ptr()) {
            assert_eq!(
                inst_ptr, current_inst_ptr,
                "vmctx corresponds to current instance"
            );
        } else {
            panic!(
                "current instance is not set; thread local storage failure can indicate \
                 dynamic linking issues"
            );
        }
    });

    let inst = inst_ptr.as_mut().unwrap();
    assert!(inst.valid_magic());
    inst
}

impl Instance {
    /// Terminate the guest and swap back to the host context without unwinding.
    ///
    /// This is almost certainly not what you want to use to terminate from a hostcall; use panics
    /// with `TerminationDetails` instead.
    unsafe fn terminate(&mut self, details: TerminationDetails) -> ! {
        self.state = State::Terminated { details };
        #[allow(unused_unsafe)] // The following unsafe will be incorrectly warned as unused
        HOST_CTX.with(|host_ctx| unsafe { Context::set(&*host_ctx.get()) })
    }
}

/// Unsafely get a `Vmctx` from an `InstanceHandle`, and fake a current instance TLS variable.
///
/// This is provided for compatibility with the Terrarium memory management test suite, but should
/// absolutely not be used in newer code.
#[deprecated]
pub unsafe fn vmctx_from_mock_instance(inst: &InstanceHandle) -> Vmctx {
    CURRENT_INSTANCE.with(|current_instance| {
        let mut current_instance = current_instance.borrow_mut();
        *current_instance = Some(std::ptr::NonNull::new_unchecked(
            inst.alloc().slot().start as *mut Instance,
        ));
    });
    Vmctx::from_raw(inst.alloc().slot().heap as *mut lucet_vmctx)
}
<commit_msg>[lucet-runtime] tweak the terrarium-only vmctx testing interface<commit_after>//! Interfaces for accessing instance data from hostcalls.
//!
//! This module contains both a Rust-friendly API ([`Vmctx`](struct.Vmctx.html)) as well as C-style
//! exports for compatibility with hostcalls written against `lucet-runtime-c`.

pub use crate::c_api::lucet_vmctx;

use crate::alloc::instance_heap_offset;
use crate::context::Context;
use crate::error::Error;
use crate::instance::{
    Instance, InstanceHandle, InstanceInternal, State, TerminationDetails, CURRENT_INSTANCE,
    HOST_CTX,
};
use std::any::Any;
use std::borrow::{Borrow, BorrowMut};
use std::cell::{Ref, RefCell, RefMut};

/// An opaque handle to a running instance's context.
#[derive(Debug)]
pub struct Vmctx {
    vmctx: *mut lucet_vmctx,
    heap: RefCell<Box<[u8]>>,
    globals: RefCell<Box<[i64]>>,

    // only used for the `vmctx_from_mock_instance` fake vmctxs
    check_instance: bool,
}

impl Drop for Vmctx {
    fn drop(&mut self) {
        let heap = self.heap.replace(Box::new([]));
        let globals = self.globals.replace(Box::new([]));
        Box::leak(heap);
        Box::leak(globals);
    }
}

pub trait VmctxInternal {
    /// Get a reference to the `Instance` for this guest.
    fn instance(&self) -> &Instance;

    /// Get a mutable reference to the `Instance` for this guest.
    ///
    /// ### Safety
    ///
    /// Using this method, you could hold on to multiple mutable references to the same
    /// `Instance`. Only use one at a time! This method does not take `&mut self` because otherwise
    /// you could not use orthogonal `&mut` refs that come from `Vmctx`, like the heap or
    /// terminating the instance.
    unsafe fn instance_mut(&self) -> &mut Instance;
}

impl VmctxInternal for Vmctx {
    fn instance(&self) -> &Instance {
        unsafe { instance_from_vmctx(self.vmctx, self.check_instance) }
    }

    unsafe fn instance_mut(&self) -> &mut Instance {
        instance_from_vmctx(self.vmctx, self.check_instance)
    }
}

impl Vmctx {
    /// Create a `Vmctx` from the compiler-inserted `vmctx` argument in a guest function.
    ///
    /// This is almost certainly not what you want to use to get a `Vmctx`; instead use the `&mut
    /// Vmctx` argument to a `lucet_hostcalls!`-wrapped function.
    pub unsafe fn from_raw(vmctx: *mut lucet_vmctx) -> Vmctx {
        let inst = instance_from_vmctx(vmctx, true);
        assert!(inst.valid_magic());

        let res = Vmctx {
            vmctx,
            heap: RefCell::new(Box::<[u8]>::from_raw(inst.heap_mut())),
            globals: RefCell::new(Box::<[i64]>::from_raw(inst.globals_mut())),
            check_instance: true,
        };
        res
    }

    /// Return the underlying `vmctx` pointer.
    pub fn as_raw(&self) -> *mut lucet_vmctx {
        self.vmctx
    }

    /// Return the WebAssembly heap as a slice of bytes.
    ///
    /// If the heap is already mutably borrowed by `heap_mut()`, the instance will
    /// terminate with `TerminationDetails::BorrowError`.
    pub fn heap(&self) -> Ref<[u8]> {
        let r = self
            .heap
            .try_borrow()
            .unwrap_or_else(|_| panic!(TerminationDetails::BorrowError("heap")));
        Ref::map(r, |b| b.borrow())
    }

    /// Return the WebAssembly heap as a mutable slice of bytes.
    ///
    /// If the heap is already borrowed by `heap()` or `heap_mut()`, the instance will terminate
    /// with `TerminationDetails::BorrowError`.
    pub fn heap_mut(&self) -> RefMut<[u8]> {
        let r = self
            .heap
            .try_borrow_mut()
            .unwrap_or_else(|_| panic!(TerminationDetails::BorrowError("heap_mut")));
        RefMut::map(r, |b| b.borrow_mut())
    }

    /// Check whether a given range in the host address space overlaps with the memory that backs
    /// the instance heap.
    pub fn check_heap<T>(&self, ptr: *const T, len: usize) -> bool {
        self.instance().check_heap(ptr, len)
    }

    /// Check whether a context value of a particular type exists.
    pub fn contains_embed_ctx<T: Any>(&self) -> bool {
        self.instance().contains_embed_ctx::<T>()
    }

    /// Get a reference to a context value of a particular type.
    ///
    /// If a context of that type does not exist, the instance will terminate with
    /// `TerminationDetails::CtxNotFound`.
    ///
    /// If the context is already mutably borrowed by `get_embed_ctx_mut`, the instance will
    /// terminate with `TerminationDetails::BorrowError`.
    pub fn get_embed_ctx<T: Any>(&self) -> Ref<T> {
        match self.instance().embed_ctx.try_get::<T>() {
            Some(Ok(t)) => t,
            Some(Err(_)) => panic!(TerminationDetails::BorrowError("get_embed_ctx")),
            None => panic!(TerminationDetails::CtxNotFound),
        }
    }

    /// Get a mutable reference to a context value of a particular type.
    ///
    /// If a context of that type does not exist, the instance will terminate with
    /// `TerminationDetails::CtxNotFound`.
    ///
    /// If the context is already borrowed by some other use of `get_embed_ctx` or
    /// `get_embed_ctx_mut`, the instance will terminate with `TerminationDetails::BorrowError`.
    pub fn get_embed_ctx_mut<T: Any>(&self) -> RefMut<T> {
        match unsafe { self.instance_mut().embed_ctx.try_get_mut::<T>() } {
            Some(Ok(t)) => t,
            Some(Err(_)) => panic!(TerminationDetails::BorrowError("get_embed_ctx_mut")),
            None => panic!(TerminationDetails::CtxNotFound),
        }
    }

    /// Terminate this guest and return to the host context without unwinding.
    ///
    /// This is almost certainly not what you want to use to terminate an instance from a hostcall,
    /// as any resources currently in scope will not be dropped. Instead, use
    /// `lucet_hostcall_terminate!` which unwinds to the enclosing hostcall body.
    pub unsafe fn terminate_no_unwind(&mut self, details: TerminationDetails) -> ! {
        self.instance_mut().terminate(details)
    }

    /// Grow the guest memory by the given number of WebAssembly pages.
    ///
    /// On success, returns the number of pages that existed before the call.
    pub fn grow_memory(&mut self, additional_pages: u32) -> Result<u32, Error> {
        unsafe { self.instance_mut().grow_memory(additional_pages) }
    }

    /// Return the WebAssembly globals as a slice of `i64`s.
    ///
    /// If the globals are already mutably borrowed by `globals_mut()`, the instance will terminate
    /// with `TerminationDetails::BorrowError`.
    pub fn globals(&self) -> Ref<[i64]> {
        let r = self
            .globals
            .try_borrow()
            .unwrap_or_else(|_| panic!(TerminationDetails::BorrowError("globals")));
        Ref::map(r, |b| b.borrow())
    }

    /// Return the WebAssembly globals as a mutable slice of `i64`s.
    ///
    /// If the globals are already borrowed by `globals()` or `globals_mut()`, the instance will
    /// terminate with `TerminationDetails::BorrowError`.
    pub fn globals_mut(&self) -> RefMut<[i64]> {
        let r = self
            .globals
            .try_borrow_mut()
            .unwrap_or_else(|_| panic!(TerminationDetails::BorrowError("globals_mut")));
        RefMut::map(r, |b| b.borrow_mut())
    }

    /// Get a function pointer by WebAssembly table and function index.
    ///
    /// This is useful when a hostcall takes a function pointer as its argument, as WebAssembly uses
    /// table indices as its runtime representation of function pointers.
    ///
    /// We do not currently reflect function type information into the Rust type system, so callers
    /// of the returned function must take care to cast it to the correct type before calling. The
    /// correct type will include the `vmctx` argument, which the caller is responsible for passing
    /// from its own context.
    ///
    /// ```no_run
    /// use lucet_runtime_internals::{lucet_hostcalls, lucet_hostcall_terminate};
    /// use lucet_runtime_internals::vmctx::{lucet_vmctx, Vmctx};
    ///
    /// lucet_hostcalls! {
    ///     #[no_mangle]
    ///     pub unsafe extern "C" fn hostcall_call_binop(
    ///         &mut vmctx,
    ///         binop_table_idx: u32,
    ///         binop_func_idx: u32,
    ///         operand1: u32,
    ///         operand2: u32,
    ///     ) -> u32 {
    ///         if let Ok(binop) = vmctx.get_func_from_idx(binop_table_idx, binop_func_idx) {
    ///             let typed_binop = binop as *const extern "C" fn(*mut lucet_vmctx, u32, u32) -> u32;
    ///             unsafe { (*typed_binop)(vmctx.as_raw(), operand1, operand2) }
    ///         } else {
    ///             lucet_hostcall_terminate!("invalid function index")
    ///         }
    ///     }
    /// }
    pub fn get_func_from_idx(
        &self,
        table_idx: u32,
        func_idx: u32,
    ) -> Result<*const extern "C" fn(), Error> {
        self.instance()
            .module()
            .get_func_from_idx(table_idx, func_idx)
    }
}

/// Get an `Instance` from the `vmctx` pointer.
///
/// Only safe to call from within the guest context.
pub unsafe fn instance_from_vmctx<'a>(
    vmctx: *mut lucet_vmctx,
    check_instance: bool,
) -> &'a mut Instance {
    assert!(!vmctx.is_null(), "vmctx is not null");

    let inst_ptr = (vmctx as usize - instance_heap_offset()) as *mut Instance;

    if check_instance {
        // We shouldn't actually need to access the thread local, only the exception handler should
        // need to. But, as long as the thread local exists, we should make sure that the guest
        // hasn't pulled any shenanigans and passed a bad vmctx. (Codegen should ensure the guest
        // cant pull any shenanigans but there have been bugs before.)
        CURRENT_INSTANCE.with(|current_instance| {
            if let Some(current_inst_ptr) = current_instance.borrow().map(|nn| nn.as_ptr()) {
                assert_eq!(
                    inst_ptr, current_inst_ptr,
                    "vmctx corresponds to current instance"
                );
            } else {
                panic!(
                    "current instance is not set; thread local storage failure can indicate \
                     dynamic linking issues"
                );
            }
        });
    }

    let inst = inst_ptr.as_mut().unwrap();
    assert!(inst.valid_magic());
    inst
}

impl Instance {
    /// Terminate the guest and swap back to the host context without unwinding.
    ///
    /// This is almost certainly not what you want to use to terminate from a hostcall; use panics
    /// with `TerminationDetails` instead.
    unsafe fn terminate(&mut self, details: TerminationDetails) -> ! {
        self.state = State::Terminated { details };
        #[allow(unused_unsafe)] // The following unsafe will be incorrectly warned as unused
        HOST_CTX.with(|host_ctx| unsafe { Context::set(&*host_ctx.get()) })
    }
}

/// Unsafely get a `Vmctx` from an `InstanceHandle` without checking the current instance TLS
/// variable.
///
/// This is provided for compatibility with the Terrarium memory management test suite, but should
/// absolutely not be used in newer code.
#[deprecated]
#[allow(deprecated)]
pub unsafe fn vmctx_from_mock_instance(inst: &InstanceHandle) -> Vmctx {
    Vmctx::from_raw_unchecked(inst.alloc().slot().heap as *mut lucet_vmctx)
}

impl Vmctx {
    #[deprecated]
    unsafe fn from_raw_unchecked(vmctx: *mut lucet_vmctx) -> Vmctx {
        let inst = instance_from_vmctx(vmctx, false);
        assert!(inst.valid_magic());

        let res = Vmctx {
            vmctx,
            heap: RefCell::new(Box::<[u8]>::from_raw(inst.heap_mut())),
            globals: RefCell::new(Box::<[i64]>::from_raw(inst.globals_mut())),
            check_instance: false,
        };
        res
    }
}
<|endoftext|>


<commit_before>/* Reimplement Rust's AtomicUsize and AtomicBool for non-Arm and non-x86 targets,
 * Right now, for example, RISC-V atomic exchange instructions are missing from
 * Rust nightly's LLVM, so we'll implement them here. As soon as Rust picks up
 * working full atomic support from LLVM, we'll ditch this code and instead use
 * the official sources.
 *
 * This code calls down to platform code, which must be implemented for the given target.
 * This code also only implements what's needed for lazy static's once and mutex code.
 * ie: new, load, store, and compare_and_swap. load and store are handled by intrinsics,
 * leaving us to implement atomic compare-and-swap.
 *
 * (c) Chris Williams, 2018.
 * See diosix LICENSE for usage and copying
 *
 * Based on Rust's libcore core::sync::atomic API define here:
 * https://github.com/rust-lang/rust/blob/master/src/libcore/sync/atomic.rs
 * libcore is MIT-licensed: https://github.com/rust-lang/rust/blob/master/LICENSE-MIT
 *
 */

use core::sync::atomic::Ordering::{self, *};
use core::intrinsics;
use core::cell::UnsafeCell;

extern "C"
{
    fn platform_cpu_wait();
    fn platform_compare_and_swap(ptr: *mut usize, expected: usize, new: usize) -> usize;
    fn platform_aq_compare_and_swap(ptr: *mut usize, expected: usize, new: usize) -> usize;
}

/* do something to avoid optimizing away a loop */
pub fn cpu_relax()
{
    unsafe { platform_cpu_wait() /* NOP */ };
}

/* implement atomic load: fetch from *ptr using given ordering */
unsafe fn atomic_load<T>(ptr: *const T, order: Ordering) -> T
{
    match order
    {
        Acquire => intrinsics::atomic_load_acq(ptr),
        Relaxed => intrinsics::atomic_load_relaxed(ptr),
        SeqCst => intrinsics::atomic_load(ptr),
        _ => panic!("Not possible to atomically load with ordering {:?}", order)
    }
}

/* implement atomic store: write valye to *ptr using given ordering */
unsafe fn atomic_store<T>(ptr: *mut T, value: T, order: Ordering) {
    match order
    {
        Release => intrinsics::atomic_store_rel(ptr, value),
        Relaxed => intrinsics::atomic_store_relaxed(ptr, value),
        SeqCst => intrinsics::atomic_store(ptr, value),
        _ => panic!("Not possible to atomically store with ordering {:?}", order)
    }
}

/* implement atomic compare-and-swap: update *ptr if new value if it contained expected value,
and return the pre-update value. if the pre-update value equals the expected value then the update occurred */
unsafe fn atomic_compare_exchange(ptr: *mut usize, expected: usize, new: usize, order: Ordering) -> usize
{
    match order
    {
        SeqCst => platform_compare_and_swap(ptr, expected, new),
        Acquire => platform_aq_compare_and_swap(ptr, expected, new),
        _ => panic!("Not possible to atomically exchange with ordering {:?}", order)
    }
}

/* ensure we're aligned to a double word boundary to keep all
architectures happy: not every CPU can do non-aligned atomics */
#[derive(Debug)]
#[repr(C, align(8))]
pub struct AtomicBool
{
    /* keeping it usize keeps everything simple and ensures
    we don't try to atomically operate a single byte which
    isn't possible on certain architectures, such as RV32 */
    contents: UnsafeCell<usize>
}

unsafe impl Sync for AtomicBool {}
pub const ATOMIC_BOOL_INIT: AtomicBool = AtomicBool::new(false);

impl AtomicBool
{
    pub const fn new(flag: bool) -> AtomicBool
    {
        AtomicBool { contents: UnsafeCell::new(flag as usize) }
    }

    pub fn load(&self, order: Ordering) -> bool
    {
        unsafe { atomic_load(self.contents.get(), order) != 0 }
    }

    pub fn store(&self, flag: bool, order: Ordering)
    {
        unsafe { atomic_store(self.contents.get(), flag as usize, order) };
    }

    pub fn compare_and_swap(&self, expected: bool, new: bool, order: Ordering) -> bool
    {
        unsafe { atomic_compare_exchange(self.contents.get(), expected as usize, new as usize, order) != 0 }
    }
}

/* AtomicUsize is basically the same as AtomicBool but using usize rather than bool */
#[derive(Debug)]
#[repr(C, align(8))]
pub struct AtomicUsize
{
    contents: UnsafeCell<usize>
}

unsafe impl Sync for AtomicUsize {}

impl AtomicUsize
{
    pub const fn new(value: usize) -> AtomicUsize
    {
        AtomicUsize { contents: UnsafeCell::new(value) }
    }

    pub fn load(&self, order: Ordering) -> usize
    {
        unsafe { atomic_load(self.contents.get(), order) }
    }

    pub fn store(&self, value: usize, order: Ordering)
    {
        unsafe { atomic_store(self.contents.get(), value, order) };
    }

    pub fn compare_and_swap(&self, expected: usize, new: usize, order: Ordering) -> usize
    {
        unsafe { atomic_compare_exchange(self.contents.get(), expected, new, order) }
    }
}
<commit_msg>Fix confusing typo in code comment<commit_after>/* Reimplement Rust's AtomicUsize and AtomicBool for non-Arm and non-x86 targets,
 * Right now, for example, RISC-V atomic exchange instructions are missing from
 * Rust nightly's LLVM, so we'll implement them here. As soon as Rust picks up
 * working full atomic support from LLVM, we'll ditch this code and instead use
 * the official sources.
 *
 * This code calls down to platform code, which must be implemented for the given target.
 * This code also only implements what's needed for lazy static's once and mutex code.
 * ie: new, load, store, and compare_and_swap. load and store are handled by intrinsics,
 * leaving us to implement atomic compare-and-swap.
 *
 * (c) Chris Williams, 2018.
 * See diosix LICENSE for usage and copying
 *
 * Based on Rust's libcore core::sync::atomic API define here:
 * https://github.com/rust-lang/rust/blob/master/src/libcore/sync/atomic.rs
 * libcore is MIT-licensed: https://github.com/rust-lang/rust/blob/master/LICENSE-MIT
 *
 */

use core::sync::atomic::Ordering::{self, *};
use core::intrinsics;
use core::cell::UnsafeCell;

extern "C"
{
    fn platform_cpu_wait();
    fn platform_compare_and_swap(ptr: *mut usize, expected: usize, new: usize) -> usize;
    fn platform_aq_compare_and_swap(ptr: *mut usize, expected: usize, new: usize) -> usize;
}

/* do something to avoid optimizing away a loop */
pub fn cpu_relax()
{
    unsafe { platform_cpu_wait() /* NOP */ };
}

/* implement atomic load: fetch from *ptr using given ordering */
unsafe fn atomic_load<T>(ptr: *const T, order: Ordering) -> T
{
    match order
    {
        Acquire => intrinsics::atomic_load_acq(ptr),
        Relaxed => intrinsics::atomic_load_relaxed(ptr),
        SeqCst => intrinsics::atomic_load(ptr),
        _ => panic!("Not possible to atomically load with ordering {:?}", order)
    }
}

/* implement atomic store: write valye to *ptr using given ordering */
unsafe fn atomic_store<T>(ptr: *mut T, value: T, order: Ordering) {
    match order
    {
        Release => intrinsics::atomic_store_rel(ptr, value),
        Relaxed => intrinsics::atomic_store_relaxed(ptr, value),
        SeqCst => intrinsics::atomic_store(ptr, value),
        _ => panic!("Not possible to atomically store with ordering {:?}", order)
    }
}

/* implement atomic compare-and-swap: update *ptr to new value if it contained expected value,
and return the pre-update value. if the pre-update value equals the expected value then the update occurred */
unsafe fn atomic_compare_exchange(ptr: *mut usize, expected: usize, new: usize, order: Ordering) -> usize
{
    match order
    {
        SeqCst => platform_compare_and_swap(ptr, expected, new),
        Acquire => platform_aq_compare_and_swap(ptr, expected, new),
        _ => panic!("Not possible to atomically exchange with ordering {:?}", order)
    }
}

/* ensure we're aligned to a double word boundary to keep all
architectures happy: not every CPU can do non-aligned atomics */
#[derive(Debug)]
#[repr(C, align(8))]
pub struct AtomicBool
{
    /* keeping it usize keeps everything simple and ensures
    we don't try to atomically operate a single byte which
    isn't possible on certain architectures, such as RV32 */
    contents: UnsafeCell<usize>
}

unsafe impl Sync for AtomicBool {}
pub const ATOMIC_BOOL_INIT: AtomicBool = AtomicBool::new(false);

impl AtomicBool
{
    pub const fn new(flag: bool) -> AtomicBool
    {
        AtomicBool { contents: UnsafeCell::new(flag as usize) }
    }

    pub fn load(&self, order: Ordering) -> bool
    {
        unsafe { atomic_load(self.contents.get(), order) != 0 }
    }

    pub fn store(&self, flag: bool, order: Ordering)
    {
        unsafe { atomic_store(self.contents.get(), flag as usize, order) };
    }

    pub fn compare_and_swap(&self, expected: bool, new: bool, order: Ordering) -> bool
    {
        unsafe { atomic_compare_exchange(self.contents.get(), expected as usize, new as usize, order) != 0 }
    }
}

/* AtomicUsize is basically the same as AtomicBool but using usize rather than bool */
#[derive(Debug)]
#[repr(C, align(8))]
pub struct AtomicUsize
{
    contents: UnsafeCell<usize>
}

unsafe impl Sync for AtomicUsize {}

impl AtomicUsize
{
    pub const fn new(value: usize) -> AtomicUsize
    {
        AtomicUsize { contents: UnsafeCell::new(value) }
    }

    pub fn load(&self, order: Ordering) -> usize
    {
        unsafe { atomic_load(self.contents.get(), order) }
    }

    pub fn store(&self, value: usize, order: Ordering)
    {
        unsafe { atomic_store(self.contents.get(), value, order) };
    }

    pub fn compare_and_swap(&self, expected: usize, new: usize, order: Ordering) -> usize
    {
        unsafe { atomic_compare_exchange(self.contents.get(), expected, new, order) }
    }
}
<|endoftext|>


<commit_before>/*
 * Cymbalum, Molecular Simulation in Rust
 * Copyright (C) 2015 Guillaume Fraux
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/
*/

use std::collections::HashMap;
use std::ops::{Index, IndexMut};

use ::potentials::PairPotential;
use ::types::Vector3D;

use super::Particle;
use super::UnitCell;
use super::interactions::Interactions;

/// The Universe type hold all the data about a system.
pub struct Universe {
    /// List of particles in the system
    particles: Vec<Particle>,
    /// Particles types, associating particles names and indexes
    types: HashMap<String, usize>,
    /// Interactions is a hash map associating particles types and potentials
    interactions: Interactions,
    /// Unit cell of the universe
    cell: UnitCell,
}

impl Universe {
    /// Create a new empty Universe
    pub fn new() -> Universe {
        Universe{
            particles: Vec::new(),
            types: HashMap::new(),
            interactions: Interactions::new(),
            cell: UnitCell::new(),
        }
    }

    /// Create an empty universe with a specific UnitCell
    pub fn from_cell(cell: UnitCell) -> Universe {
        let mut universe = Universe::new();
        universe.set_cell(cell);
        return universe;
    }

    /// Get the universe unit cell
    pub fn cell<'a>(&'a self) -> &'a UnitCell {&self.cell}
    /// Set the universe unit cell
    pub fn set_cell(&mut self, cell: UnitCell) {self.cell = cell;}

    /// Insert a particle at the end of the internal list
    pub fn add_particle(&mut self, p: Particle) {self.particles.push(p);}
    /// Get the number of particles in this universe
    pub fn size(&self) -> usize {self.particles.len()}

    /// Get the list of pair interaction betweent the atom i and the atom j
    pub fn pairs<'a>(&'a self, i: usize, j: usize) -> &'a Vec<Box<PairPotential>> {
        let itype = self.types[self.particles[i].name()];
        let jtype = self.types[self.particles[j].name()];
        &self.interactions.pairs[&(itype, jtype)]
    }

    /// Add an interaction between the particles with names `names`
    pub fn add_pair_interaction<T>(&mut self, i: &str, j: &str, pot: T) where T: PairPotential + 'static {
        let itype = self.get_type(i);
        let jtype = self.get_type(j);

        if !self.interactions.pairs.contains_key(&(itype, jtype)) {
            self.interactions.pairs.insert((itype, jtype), Vec::new());
        }
        let pairs = self.interactions.pairs.get_mut(&(itype, jtype)).unwrap();
        pairs.push(Box::new(pot));
    }

    /// Get the distance between the particles at indexes `i` and `j`
    pub fn distance(&self, i: usize, j:usize) -> f64 {
        self.cell.distance(self.particles[i].position(), self.particles[j].position())
    }

    /// Get the distance between the particles at indexes `i` and `j`
    pub fn wrap_vector(&self, i: usize, j:usize) -> Vector3D {
        let mut res = *self.particles[i].position() - *self.particles[j].position();
        self.cell.wrap_vector(&mut res);
        return res;
    }

    /// Get or create the usize type for the particle `name`
    fn get_type(&mut self, name: &str) -> usize {
        if self.types.contains_key(name) {
            self.types[name]
        } else {
            let index = self.types.len();
            self.types.insert(name.to_string(), index);
            return index;
        }
    }
}

impl Index<usize> for Universe {
    type Output = Particle;
    fn index<'a>(&'a self, index: usize) -> &'a Particle {
        &self.particles[index]
    }
}

impl IndexMut<usize> for Universe {
    #[inline]
    fn index_mut<'a>(&'a mut self, index: usize) -> &'a mut Particle {
        &mut self.particles[index]
    }
}

#[cfg(test)]
mod tests {
    use ::universe::*;
    use ::types::*;
    use ::potentials::*;

    #[test]
    fn particles() {
        let mut universe = Universe::new();
        universe.add_particle(Particle::new("O"));
        universe.add_particle(Particle::new("H"));
        universe.add_particle(Particle::new("H"));

        assert_eq!(universe.size(), 3);
        assert_eq!(universe[0].name(), "O");
        assert_eq!(universe[1].name(), "H");
        assert_eq!(universe[2].name(), "H");
    }

    #[test]
    fn distances() {
        let mut universe = Universe::from_cell(UnitCell::cubic(5.0));
        universe.add_particle(Particle::new("O"));
        universe.add_particle(Particle::new("H"));

        universe[0].set_position(Vector3D::new(9.0, 0.0, 0.0));
        universe[1].set_position(Vector3D::new(0.0, 0.0, 0.0));
        assert_eq!(universe.distance(0, 1), 1.0);

        universe.set_cell(UnitCell::new());
        assert_eq!(universe.distance(0, 1), 9.0);
    }

    #[test]
    fn pairs() {
        let mut universe = Universe::new();
        universe.add_particle(Particle::new("He"));

        universe.add_pair_interaction("He", "He", LennardJones{sigma: 0.3, epsilon: 2.0});
        universe.add_pair_interaction("He", "He", Harmonic{k: 100.0, r0: 1.1});

        assert_eq!(universe.pairs(0, 0).len(), 2);
    }
}
<commit_msg>Better documentation for the Universe struct<commit_after>/*
 * Cymbalum, Molecular Simulation in Rust
 * Copyright (C) 2015 Guillaume Fraux
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/
*/

use std::collections::HashMap;
use std::ops::{Index, IndexMut};

use ::potentials::PairPotential;
use ::types::Vector3D;

use super::Particle;
use super::UnitCell;
use super::interactions::Interactions;

/// The Universe type hold all the data about a system. This data contains:
///   - an unit cell, containing the system;
///   - a list of particles in the system;
///   - a list of interactions, associating particles types and potentials
///   - a hash map associating particles names and particles types.
pub struct Universe {
    /// Unit cell of the universe
    cell: UnitCell,
    /// List of particles in the system
    particles: Vec<Particle>,
    /// Particles types, associating particles names and indexes
    types: HashMap<String, usize>,
    /// Interactions is a hash map associating particles types and potentials
    interactions: Interactions,
}

impl Universe {
    /// Create a new empty Universe
    pub fn new() -> Universe {
        Universe{
            particles: Vec::new(),
            types: HashMap::new(),
            interactions: Interactions::new(),
            cell: UnitCell::new(),
        }
    }

    /// Create an empty universe with a specific UnitCell
    pub fn from_cell(cell: UnitCell) -> Universe {
        let mut universe = Universe::new();
        universe.set_cell(cell);
        return universe;
    }

    /// Get the universe unit cell
    pub fn cell<'a>(&'a self) -> &'a UnitCell {&self.cell}
    /// Set the universe unit cell
    pub fn set_cell(&mut self, cell: UnitCell) {self.cell = cell;}

    /// Insert a particle at the end of the internal list
    pub fn add_particle(&mut self, p: Particle) {self.particles.push(p);}
    /// Get the number of particles in this universe
    pub fn size(&self) -> usize {self.particles.len()}

    /// Get the list of pair interaction betweent the atom i and the atom j
    pub fn pairs<'a>(&'a self, i: usize, j: usize) -> &'a Vec<Box<PairPotential>> {
        let itype = self.types[self.particles[i].name()];
        let jtype = self.types[self.particles[j].name()];
        &self.interactions.pairs[&(itype, jtype)]
    }

    /// Add an interaction between the particles with names `names`
    pub fn add_pair_interaction<T>(&mut self, i: &str, j: &str, pot: T) where T: PairPotential + 'static {
        let itype = self.get_type(i);
        let jtype = self.get_type(j);

        if !self.interactions.pairs.contains_key(&(itype, jtype)) {
            self.interactions.pairs.insert((itype, jtype), Vec::new());
        }
        let pairs = self.interactions.pairs.get_mut(&(itype, jtype)).unwrap();
        pairs.push(Box::new(pot));
    }

    /// Get the distance between the particles at indexes `i` and `j`
    pub fn distance(&self, i: usize, j:usize) -> f64 {
        self.cell.distance(self.particles[i].position(), self.particles[j].position())
    }

    /// Get the distance between the particles at indexes `i` and `j`
    pub fn wrap_vector(&self, i: usize, j:usize) -> Vector3D {
        let mut res = *self.particles[i].position() - *self.particles[j].position();
        self.cell.wrap_vector(&mut res);
        return res;
    }

    /// Get or create the usize type for the particle `name`
    fn get_type(&mut self, name: &str) -> usize {
        if self.types.contains_key(name) {
            self.types[name]
        } else {
            let index = self.types.len();
            self.types.insert(name.to_string(), index);
            return index;
        }
    }
}

impl Index<usize> for Universe {
    type Output = Particle;
    fn index<'a>(&'a self, index: usize) -> &'a Particle {
        &self.particles[index]
    }
}

impl IndexMut<usize> for Universe {
    #[inline]
    fn index_mut<'a>(&'a mut self, index: usize) -> &'a mut Particle {
        &mut self.particles[index]
    }
}

#[cfg(test)]
mod tests {
    use ::universe::*;
    use ::types::*;
    use ::potentials::*;

    #[test]
    fn particles() {
        let mut universe = Universe::new();
        universe.add_particle(Particle::new("O"));
        universe.add_particle(Particle::new("H"));
        universe.add_particle(Particle::new("H"));

        assert_eq!(universe.size(), 3);
        assert_eq!(universe[0].name(), "O");
        assert_eq!(universe[1].name(), "H");
        assert_eq!(universe[2].name(), "H");
    }

    #[test]
    fn distances() {
        let mut universe = Universe::from_cell(UnitCell::cubic(5.0));
        universe.add_particle(Particle::new("O"));
        universe.add_particle(Particle::new("H"));

        universe[0].set_position(Vector3D::new(9.0, 0.0, 0.0));
        universe[1].set_position(Vector3D::new(0.0, 0.0, 0.0));
        assert_eq!(universe.distance(0, 1), 1.0);

        universe.set_cell(UnitCell::new());
        assert_eq!(universe.distance(0, 1), 9.0);
    }

    #[test]
    fn pairs() {
        let mut universe = Universe::new();
        universe.add_particle(Particle::new("He"));

        universe.add_pair_interaction("He", "He", LennardJones{sigma: 0.3, epsilon: 2.0});
        universe.add_pair_interaction("He", "He", Harmonic{k: 100.0, r0: 1.1});

        assert_eq!(universe.pairs(0, 0).len(), 2);
    }
}
<|endoftext|>


<commit_before>//
// zhtta.rs
//
// Starting code for PS3
// Running on Rust 0.9
//
// Note that this code has serious security risks!	You should not run it
// on any system with access to sensitive files.
//
// University of Virginia - cs4414 Spring 2014
// Weilin Xu and David Evans
// Version 0.5

// To see debug! outputs set the RUST_LOG environment variable, e.g.: export RUST_LOG="zhtta=debug"

#[feature(globs)];
extern mod extra;

use std::io::*;
use std::io::net::ip::{SocketAddr};
use std::{os, str, libc, from_str};
use std::path::Path;
use std::hashmap::HashMap;

use extra::getopts;
use extra::arc::MutexArc;

static SERVER_NAME : &'static str = "Zhtta Version 0.5";

static IP : &'static str = "127.0.0.1";
static PORT : uint = 4414;
static WWW_DIR : &'static str = "./www";

static HTTP_OK : &'static str = "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n";
static HTTP_BAD : &'static str = "HTTP/1.1 404 Not Found\r\n\r\n";

static COUNTER_STYLE : &'static str = "<doctype !html><title>Hello, Rust!</title>

			 ";

struct HTTP_Request {
	// Use peer_name as the key to access TcpStream in hashmap.

	// (Due to a bug in extra::arc in Rust 0.9, it is very inconvenient to use TcpStream without the "Freeze" bound.
	//	See issue: https://github.com/mozilla/rust/issues/12139)
	peer_name: ~str,
	path: ~Path,
}

struct WebServer {
	ip: ~str,
	port: uint,
	www_dir_path: ~Path,

	request_queue_arc: MutexArc<~[HTTP_Request]>,
	visitor_count_arc: MutexArc<uint>,
	stream_map_arc: MutexArc<HashMap<~str, Option<std::io::net::tcp::TcpStream>>>,

	notify_port: Port<()>,
	shared_notify_chan: SharedChan<()>,
}

impl WebServer {
	fn new(ip: &str, port: uint, www_dir: &str) -> WebServer {
		let (notify_port, shared_notify_chan) = SharedChan::new();
		let www_dir_path = ~Path::new(www_dir);
		os::change_dir(www_dir_path.clone());

		WebServer {
			ip: ip.to_owned(),
			port: port,
			www_dir_path: www_dir_path,

			request_queue_arc: MutexArc::new(~[]),
			visitor_count_arc: MutexArc::new(0),
			stream_map_arc: MutexArc::new(HashMap::new()),

			notify_port: notify_port,
			shared_notify_chan: shared_notify_chan,
		}
	}

	fn run(&mut self) {
		self.listen();
		self.dequeue_static_file_request();
	}

	fn listen(&mut self) {
		let addr = from_str::<SocketAddr>(format!("{:s}:{:u}", self.ip, self.port)).expect("Address error.");
		let www_dir_path_str = self.www_dir_path.as_str().expect("invalid www path?").to_owned();

		let request_queue_arc = self.request_queue_arc.clone();
		let visitor_count_arc = self.visitor_count_arc.clone();
		let shared_notify_chan = self.shared_notify_chan.clone();
		let stream_map_arc = self.stream_map_arc.clone();

		spawn(proc() {
			let mut acceptor = net::tcp::TcpListener::bind(addr).listen();
			println!("{:s} listening on {:s} (serving from: {:s}).",
					 SERVER_NAME, addr.to_str(), www_dir_path_str);

			for stream in acceptor.incoming() {
				let (queue_port, queue_chan) = Chan::new();
				queue_chan.send(request_queue_arc.clone());

				let (visit_count_port, visit_count_chan) = Chan::new();
				visit_count_chan.send(visitor_count_arc.clone());

				let notify_chan = shared_notify_chan.clone();
				let stream_map_arc = stream_map_arc.clone();

				// Spawn a task to handle the connection.
				spawn(proc() {
					let request_queue_arc = queue_port.recv();

					let visitor_count_arc = visit_count_port.recv();
					visitor_count_arc.access( |count| { *count +=  1; });

					let mut stream = stream;

					let peer_name = WebServer::get_peer_name(&mut stream);

					let mut buf = [0, ..500];
					stream.read(buf);
					let request_str = str::from_utf8(buf);
					debug!("Request:\n{:s}", request_str);

					let req_group : ~[&str]= request_str.splitn(' ', 3).collect();
					if req_group.len() > 2 {
						let path_str = "." + req_group[1].to_owned();

						let mut path_obj = ~os::getcwd();
						path_obj.push(path_str.clone());

						let ext_str = match path_obj.extension_str() {
							Some(e) => e,
							None => "",
						};

						debug!("Requested path: [{:s}]", path_obj.as_str().expect("error"));
						debug!("Requested path: [{:s}]", path_str);

						if path_str == ~"./" {
							debug!("===== Counter Page request =====");
							WebServer::respond_with_counter_page(stream, visitor_count_arc.access( |count| { return *count; } ) );
							debug!("=====Terminated connection from [{:s}].=====", peer_name);
						} else if !path_obj.exists() || path_obj.is_dir() {
							debug!("===== Error page request =====");
							WebServer::respond_with_error_page(stream, path_obj);
							debug!("=====Terminated connection from [{:s}].=====", peer_name);
						} else if ext_str == "shtml" { // Dynamic web pages.
							debug!("===== Dynamic Page request =====");
							WebServer::respond_with_dynamic_page(stream, path_obj);
							debug!("=====Terminated connection from [{:s}].=====", peer_name);
						} else {
							debug!("===== Static Page request =====");
							WebServer::enqueue_static_file_request(stream, path_obj, stream_map_arc, request_queue_arc, notify_chan);
						}
					}
				});
			}
		});
	}

	fn respond_with_error_page(stream: Option<std::io::net::tcp::TcpStream>, path: &Path) {
		let mut stream = stream;
		let msg: ~str = format!("Cannot open: {:s}", path.as_str().expect("invalid path").to_owned());

		stream.write(HTTP_BAD.as_bytes());
		stream.write(msg.as_bytes());
	}

	// DONE: Safe visitor counter.
	fn respond_with_counter_page(stream: Option<std::io::net::tcp::TcpStream>, visit_count : uint) {
		let mut stream = stream;
		let response: ~str =
			format!("{:s}{:s}
Greetings, Krusty!

Visitor count: {:u}
\r\n",
					HTTP_OK, COUNTER_STYLE,
					visit_count );
		debug!("Responding to counter request");
		stream.write(response.as_bytes());
	}

	// TODO: Streaming file.
	// TODO: Application-layer file caching.
	fn respond_with_static_file(stream: Option<std::io::net::tcp::TcpStream>, path: &Path) {
		let mut stream = stream;
		let mut file_reader = File::open(path).expect("Invalid file!");
		stream.write(HTTP_OK.as_bytes());
		stream.write(file_reader.read_to_end());
	}

	// TODO: Server-side gashing.
	fn respond_with_dynamic_page(stream: Option<std::io::net::tcp::TcpStream>, path: &Path) {
		// for now, just serve as static file
		WebServer::respond_with_static_file(stream, path);
	}

	// TODO: Smarter Scheduling.
	fn enqueue_static_file_request(stream: Option<std::io::net::tcp::TcpStream>, path_obj: &Path, stream_map_arc: MutexArc<HashMap<~str, Option<std::io::net::tcp::TcpStream>>>, req_queue_arc: MutexArc<~[HTTP_Request]>, notify_chan: SharedChan<()>) {
		// Save stream in hashmap for later response.
		let mut stream = stream;
		let peer_name = WebServer::get_peer_name(&mut stream);
		let (stream_port, stream_chan) = Chan::new();
		stream_chan.send(stream);
		unsafe {
			// Use an unsafe method, because TcpStream in Rust 0.9 doesn't have "Freeze" bound.
			stream_map_arc.unsafe_access(|local_stream_map| {
				let stream = stream_port.recv();
				local_stream_map.swap(peer_name.clone(), stream);
			});
		}

		// Enqueue the HTTP request.
		let req = HTTP_Request { peer_name: peer_name.clone(), path: ~path_obj.clone() };
		let (req_port, req_chan) = Chan::new();
		req_chan.send(req);

		debug!("Waiting for queue mutex lock.");
		req_queue_arc.access(|local_req_queue| {
			debug!("Got queue mutex lock.");
			let req: HTTP_Request = req_port.recv();
			local_req_queue.push(req);
			debug!("A new request enqueued, now the length of queue is {:u}.", local_req_queue.len());
		});

		notify_chan.send(()); // Send incoming notification to responder task.

	}

	// TODO: Smarter Scheduling.
	fn dequeue_static_file_request(&mut self) {
		let req_queue_get = self.request_queue_arc.clone();
		let stream_map_get = self.stream_map_arc.clone();

		// Port<> cannot be sent to another task. So we have to make this task as the main task that can access self.notify_port.

		let (request_port, request_chan) = Chan::new();
		loop {
			self.notify_port.recv();	// waiting for new request enqueued.

			req_queue_get.access( |req_queue| {
				match req_queue.shift_opt() { // FIFO queue.
					None => { /* do nothing */ }
					Some(req) => {
						request_chan.send(req);
						debug!("A new request dequeued, now the length of queue is {:u}.", req_queue.len());
					}
				}
			});

			let request = request_port.recv();

			// Get stream from hashmap.
			// Use unsafe method, because TcpStream in Rust 0.9 doesn't have "Freeze" bound.
			let (stream_port, stream_chan) = Chan::new();
			unsafe {
				stream_map_get.unsafe_access(|local_stream_map| {
					let stream = local_stream_map.pop(&request.peer_name).expect("no option tcpstream");
					stream_chan.send(stream);
				});
			}

			// TODO: Spawning more tasks to respond the dequeued requests concurrently. You may need a semophore to control the concurrency.
			let stream = stream_port.recv();
			WebServer::respond_with_static_file(stream, request.path);
			// Close stream automatically.
			debug!("=====Terminated connection from [{:s}].=====", request.peer_name);
		}
	}

	fn get_peer_name(stream: &mut Option<std::io::net::tcp::TcpStream>) -> ~str {
		match *stream {
			Some(ref mut s) => {
						 match s.peer_name() {
							Some(pn) => {pn.to_str()},
							None => (~"")
						 }
					   },
			None => (~"")
		}
	}
}

fn get_args() -> (~str, uint, ~str) {
	fn print_usage(program: &str) {
		println!("Usage: {:s} [options]", program);
		println!("--ip	   \tIP address, \"{:s}\" by default.", IP);
		println!("--port   \tport number, \"{:u}\" by default.", PORT);
		println!("--www    \tworking directory, \"{:s}\" by default", WWW_DIR);
		println("-h --help \tUsage");
	}

	/* Begin processing program arguments and initiate the parameters. */
	let args = os::args();
	let program = args[0].clone();

	let opts = ~[
		getopts::optopt("ip"),
		getopts::optopt("port"),
		getopts::optopt("www"),
		getopts::optflag("h"),
		getopts::optflag("help")
	];

	let matches = match getopts::getopts(args.tail(), opts) {
		Ok(m) => { m }
		Err(f) => { fail!(f.to_err_msg()) }
	};

	if matches.opt_present("h") || matches.opt_present("help") {
		print_usage(program);
		unsafe { libc::exit(1); }
	}

	let ip_str = if matches.opt_present("ip") {
					matches.opt_str("ip").expect("invalid ip address?").to_owned()
				 } else {
					IP.to_owned()
				 };

	let port:uint = if matches.opt_present("port") {
						from_str::from_str(matches.opt_str("port").expect("invalid port number?")).expect("not uint?")
					} else {
						PORT
					};

	let www_dir_str = if matches.opt_present("www") {
						matches.opt_str("www").expect("invalid www argument?")
					  } else { WWW_DIR.to_owned() };

	(ip_str, port, www_dir_str)
}

fn main() {
	let (ip_str, port, www_dir_str) = get_args();
	let mut zhtta = WebServer::new(ip_str, port, www_dir_str);
	zhtta.run();
}
<commit_msg>Got #exec working. Matt will change variables names...<commit_after>//
// zhtta.rs
//
// Starting code for PS3
// Running on Rust 0.9
//
// Note that this code has serious security risks!	You should not run it
// on any system with access to sensitive files.
//
// University of Virginia - cs4414 Spring 2014
// Weilin Xu and David Evans
// Version 0.5

// To see debug! outputs set the RUST_LOG environment variable, e.g.: export RUST_LOG="zhtta=debug"

#[feature(globs)];
extern mod extra;

use std::io::*;
use std::io::net::ip::{SocketAddr};
use std::{os, str, libc, from_str};
use std::path::Path;
use std::hashmap::HashMap;

use extra::getopts;
use extra::arc::MutexArc;

use gash::*;
mod gash;

static SERVER_NAME : &'static str = "Zhtta Version 0.5";

static IP : &'static str = "127.0.0.1";
static PORT : uint = 4414;
static WWW_DIR : &'static str = "./www";

static HTTP_OK : &'static str = "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n";
static HTTP_BAD : &'static str = "HTTP/1.1 404 Not Found\r\n\r\n";

static COUNTER_STYLE : &'static str = "<doctype !html><title>Hello, Rust!</title>

			 ";

struct HTTP_Request {
	// Use peer_name as the key to access TcpStream in hashmap.

	// (Due to a bug in extra::arc in Rust 0.9, it is very inconvenient to use TcpStream without the "Freeze" bound.
	//	See issue: https://github.com/mozilla/rust/issues/12139)
	peer_name: ~str,
	path: ~Path,
}

struct WebServer {
	ip: ~str,
	port: uint,
	www_dir_path: ~Path,

	request_queue_arc: MutexArc<~[HTTP_Request]>,
	visitor_count_arc: MutexArc<uint>,
	stream_map_arc: MutexArc<HashMap<~str, Option<std::io::net::tcp::TcpStream>>>,

	notify_port: Port<()>,
	shared_notify_chan: SharedChan<()>,
}

impl WebServer {
	fn new(ip: &str, port: uint, www_dir: &str) -> WebServer {
		let (notify_port, shared_notify_chan) = SharedChan::new();
		let www_dir_path = ~Path::new(www_dir);
		os::change_dir(www_dir_path.clone());

		WebServer {
			ip: ip.to_owned(),
			port: port,
			www_dir_path: www_dir_path,

			request_queue_arc: MutexArc::new(~[]),
			visitor_count_arc: MutexArc::new(0),
			stream_map_arc: MutexArc::new(HashMap::new()),

			notify_port: notify_port,
			shared_notify_chan: shared_notify_chan,
		}
	}

	fn run(&mut self) {
		self.listen();
		self.dequeue_static_file_request();
	}

	fn listen(&mut self) {
		let addr = from_str::<SocketAddr>(format!("{:s}:{:u}", self.ip, self.port)).expect("Address error.");
		let www_dir_path_str = self.www_dir_path.as_str().expect("invalid www path?").to_owned();

		let request_queue_arc = self.request_queue_arc.clone();
		let visitor_count_arc = self.visitor_count_arc.clone();
		let shared_notify_chan = self.shared_notify_chan.clone();
		let stream_map_arc = self.stream_map_arc.clone();

		spawn(proc() {
			let mut acceptor = net::tcp::TcpListener::bind(addr).listen();
			println!("{:s} listening on {:s} (serving from: {:s}).",
					 SERVER_NAME, addr.to_str(), www_dir_path_str);

			for stream in acceptor.incoming() {
				let (queue_port, queue_chan) = Chan::new();
				queue_chan.send(request_queue_arc.clone());

				let (visit_count_port, visit_count_chan) = Chan::new();
				visit_count_chan.send(visitor_count_arc.clone());

				let notify_chan = shared_notify_chan.clone();
				let stream_map_arc = stream_map_arc.clone();

				// Spawn a task to handle the connection.
				spawn(proc() {
					let request_queue_arc = queue_port.recv();

					let visitor_count_arc = visit_count_port.recv();
					visitor_count_arc.access( |count| { *count +=  1; });

					let mut stream = stream;

					let peer_name = WebServer::get_peer_name(&mut stream);

					let mut buf = [0, ..500];
					stream.read(buf);
					let request_str = str::from_utf8(buf);
					debug!("Request:\n{:s}", request_str);

					let req_group : ~[&str]= request_str.splitn(' ', 3).collect();
					if req_group.len() > 2 {
						let path_str = "." + req_group[1].to_owned();

						let mut path_obj = ~os::getcwd();
						path_obj.push(path_str.clone());

						let ext_str = match path_obj.extension_str() {
							Some(e) => e,
							None => "",
						};

						debug!("Requested path: [{:s}]", path_obj.as_str().expect("error"));
						debug!("Requested path: [{:s}]", path_str);

						if path_str == ~"./" {
							debug!("===== Counter Page request =====");
							WebServer::respond_with_counter_page(stream, visitor_count_arc.access( |count| { return *count; } ) );
							debug!("=====Terminated connection from [{:s}].=====", peer_name);
						} else if !path_obj.exists() || path_obj.is_dir() {
							debug!("===== Error page request =====");
							WebServer::respond_with_error_page(stream, path_obj);
							debug!("=====Terminated connection from [{:s}].=====", peer_name);
						} else if ext_str == "shtml" { // Dynamic web pages.
							debug!("===== Dynamic Page request =====");
							WebServer::respond_with_dynamic_page(stream, path_obj);
							debug!("=====Terminated connection from [{:s}].=====", peer_name);
						} else {
							debug!("===== Static Page request =====");
							WebServer::enqueue_static_file_request(stream, path_obj, stream_map_arc, request_queue_arc, notify_chan);
						}
					}
				});
			}
		});
	}

	fn respond_with_error_page(stream: Option<std::io::net::tcp::TcpStream>, path: &Path) {
		let mut stream = stream;
		let msg: ~str = format!("Cannot open: {:s}", path.as_str().expect("invalid path").to_owned());

		stream.write(HTTP_BAD.as_bytes());
		stream.write(msg.as_bytes());
	}

	// DONE: Safe visitor counter.
	fn respond_with_counter_page(stream: Option<std::io::net::tcp::TcpStream>, visit_count : uint) {
		let mut stream = stream;
		let response: ~str =
			format!("{:s}{:s}
Greetings, Krusty!

Visitor count: {:u}
\r\n",
					HTTP_OK, COUNTER_STYLE,
					visit_count );
		debug!("Responding to counter request");
		stream.write(response.as_bytes());
	}

	// TODO: Streaming file.
	// TODO: Application-layer file caching.
	fn respond_with_static_file(stream: Option<std::io::net::tcp::TcpStream>, path: &Path) {
		let mut stream = stream;
		let mut file_reader = File::open(path).expect("Invalid file!");
		stream.write(HTTP_OK.as_bytes());
		stream.write(file_reader.read_to_end());
	}

	// TODO: Server-side gashing.
	fn respond_with_dynamic_page(stream: Option<std::io::net::tcp::TcpStream>, path: &Path) {
		// for now, just serve as static file
		let mut stream = stream;
		let mut staticFile = File::open(path).expect("Invalid file!");
		let read = staticFile.read_to_end();
		let mut fileA = str::from_utf8(read).to_owned();
		let mut commIndex = match fileA.find_str("") { Some(x) => x, None => -1 };
			println!("{:u}", endComm);
			let commSplit = [splitOnComm[1].slice_to(endComm), splitOnComm[1].slice_from(endComm+5)];
			let cmdResult = gash::run_cmdline(commSplit[0]);
			fileA = (splitOnComm[0] + cmdResult + commSplit[1]);
			commIndex = match fileA.find_str("


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Module that implements lights for `PhilipsHueAdapter`
//!
//! This module implements AdapterManager-facing functionality.
//! It registers a service for every light and adds setters and
//! getters according to the light type.

use foxbox_taxonomy::api::Error;
use foxbox_taxonomy::manager::*;
use foxbox_taxonomy::services::*;
use foxbox_taxonomy::values::{ Type };
use super::*;
use super::hub_api::HubApi;
use std::collections::HashSet;
use std::sync::Arc;
use traits::Controller;

const CUSTOM_PROPERTY_MANUFACTURER: &'static str = "manufacturer";
const CUSTOM_PROPERTY_MODEL: &'static str = "model";
const CUSTOM_PROPERTY_NAME: &'static str = "name";
const CUSTOM_PROPERTY_TYPE: &'static str = "type";

#[derive(Clone)]
pub struct Light {
    api: Arc<HubApi>,
    hub_id: String,
    light_id: String,
    service_id: Id<ServiceId>,
    pub get_available_id: Id<Getter>,
    pub get_power_id: Id<Getter>,
    pub set_power_id: Id<Setter>,
}

impl Light {
    pub fn new(api: Arc<HubApi>, hub_id: &str, light_id: &str)
        -> Self
    {
        Light {
            api: api,
            hub_id: hub_id.to_owned(),
            light_id: light_id.to_owned(),
            service_id: create_light_id(&hub_id, &light_id),
            get_available_id: create_getter_id("available", &hub_id, &light_id),
            get_power_id: create_getter_id("power", &hub_id, &light_id),
            set_power_id: create_setter_id("power", &hub_id, &light_id),
        }
    }
    pub fn start(&self) {
        // Nothing to do, yet
    }
    pub fn stop(&self) {
        // Nothing to do, yet
    }
    pub fn init_service(&mut self, manager: Arc<AdapterManager>,
        services: LightServiceMap) -> Result<(), Error>
    {
        let adapter_id = create_adapter_id();
        let status = self.api.get_light_status(&self.light_id);
        if status.lighttype == "Extended color light" {
            info!("New Philips Hue Extended Color Light service for light {} on bridge {}",
                self.light_id, self.hub_id);
            let mut service = Service::empty(self.service_id.clone(), adapter_id.clone());
            service.properties.insert(CUSTOM_PROPERTY_MANUFACTURER.to_owned(),
                status.manufacturername.to_owned());
            service.properties.insert(CUSTOM_PROPERTY_MODEL.to_owned(),
                status.modelid.to_owned());
            service.properties.insert(CUSTOM_PROPERTY_NAME.to_owned(),
                status.name.to_owned());
            service.properties.insert(CUSTOM_PROPERTY_TYPE.to_owned(),
                "Light/ColorLight".to_owned());
            service.tags.insert(tag_id!("type:Light/ColorLight"));

            try!(manager.add_service(service));

            // The `available` getter yields `On` when the light
            // is plugged in and `Off` when it is not. Availability
            // Has no effect on the API other than that you won't
            // see the light change because it lacks external power.
            try!(manager.add_getter(Channel {
                tags: HashSet::new(),
                adapter: adapter_id.clone(),
                id: self.get_available_id.clone(),
                last_seen: None,
                service: self.service_id.clone(),
                mechanism: Getter {
                    kind: ChannelKind::Extension {
                        vendor: Id::new("foxlink@mozilla.com"),
                        adapter: Id::new("Philips Hue Adapter"),
                        kind: Id::new("available"),
                        typ: Type::OnOff,
                    },
                    updated: None,
                },
            }));

            try!(manager.add_getter(Channel {
                tags: HashSet::new(),
                adapter: adapter_id.clone(),
                id: self.get_power_id.clone(),
                last_seen: None,
                service: self.service_id.clone(),
                mechanism: Getter {
                    kind: ChannelKind::Extension {
                        vendor: Id::new("foxlink@mozilla.com"),
                        adapter: Id::new("Philips Hue Adapter"),
                        kind: Id::new("power"),
                        typ: Type::OnOff,
                    },
                    updated: None,
                },
            }));

            try!(manager.add_setter(Channel {
                tags: HashSet::new(),
                adapter: adapter_id.clone(),
                id: self.set_power_id.clone(),
                last_seen: None,
                service: self.service_id.clone(),
                mechanism: Setter {
                    kind: ChannelKind::Extension {
                        vendor: Id::new("foxlink@mozilla.com"),
                        adapter: Id::new("Philips Hue Adapter"),
                        kind: Id::new("power"),
                        typ: Type::OnOff,
                    },
                    updated: None,
                },
            }));

            let mut services_lock = services.lock().unwrap();
            services_lock.getters.insert(self.get_available_id.clone(), self.clone());
            services_lock.getters.insert(self.get_power_id.clone(), self.clone());
            services_lock.setters.insert(self.set_power_id.clone(), self.clone());

        } else {
            warn!("Ignoring unsupported Hue light type {}, ID {} on bridge {}",
                status.lighttype, self.light_id, self.hub_id);
        }
        Ok(())
    }

    pub fn get_available(&self) -> bool {
        let status = self.api.get_light_status(&self.light_id);
        status.state.reachable
    }

    pub fn get_power(&self) -> bool {
        let status = self.api.get_light_status(&self.light_id);
        status.state.on
    }

    pub fn set_power(&self, on: bool) {
        self.api.set_light_power(&self.light_id, on);
    }

}
<commit_msg>Philips Hue now uses LightOn for lights being on/off (#349)<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Module that implements lights for `PhilipsHueAdapter`
//!
//! This module implements AdapterManager-facing functionality.
//! It registers a service for every light and adds setters and
//! getters according to the light type.

use foxbox_taxonomy::api::Error;
use foxbox_taxonomy::manager::*;
use foxbox_taxonomy::services::*;
use foxbox_taxonomy::values::{ Type };
use super::*;
use super::hub_api::HubApi;
use std::collections::HashSet;
use std::sync::Arc;
use traits::Controller;

const CUSTOM_PROPERTY_MANUFACTURER: &'static str = "manufacturer";
const CUSTOM_PROPERTY_MODEL: &'static str = "model";
const CUSTOM_PROPERTY_NAME: &'static str = "name";
const CUSTOM_PROPERTY_TYPE: &'static str = "type";

#[derive(Clone)]
pub struct Light {
    api: Arc<HubApi>,
    hub_id: String,
    light_id: String,
    service_id: Id<ServiceId>,
    pub get_available_id: Id<Getter>,
    pub get_power_id: Id<Getter>,
    pub set_power_id: Id<Setter>,
}

impl Light {
    pub fn new(api: Arc<HubApi>, hub_id: &str, light_id: &str)
        -> Self
    {
        Light {
            api: api,
            hub_id: hub_id.to_owned(),
            light_id: light_id.to_owned(),
            service_id: create_light_id(&hub_id, &light_id),
            get_available_id: create_getter_id("available", &hub_id, &light_id),
            get_power_id: create_getter_id("power", &hub_id, &light_id),
            set_power_id: create_setter_id("power", &hub_id, &light_id),
        }
    }
    pub fn start(&self) {
        // Nothing to do, yet
    }
    pub fn stop(&self) {
        // Nothing to do, yet
    }
    pub fn init_service(&mut self, manager: Arc<AdapterManager>,
        services: LightServiceMap) -> Result<(), Error>
    {
        let adapter_id = create_adapter_id();
        let status = self.api.get_light_status(&self.light_id);
        if status.lighttype == "Extended color light" {
            info!("New Philips Hue Extended Color Light service for light {} on bridge {}",
                self.light_id, self.hub_id);
            let mut service = Service::empty(self.service_id.clone(), adapter_id.clone());
            service.properties.insert(CUSTOM_PROPERTY_MANUFACTURER.to_owned(),
                status.manufacturername.to_owned());
            service.properties.insert(CUSTOM_PROPERTY_MODEL.to_owned(),
                status.modelid.to_owned());
            service.properties.insert(CUSTOM_PROPERTY_NAME.to_owned(),
                status.name.to_owned());
            service.properties.insert(CUSTOM_PROPERTY_TYPE.to_owned(),
                "Light/ColorLight".to_owned());
            service.tags.insert(tag_id!("type:Light/ColorLight"));

            try!(manager.add_service(service));

            // The `available` getter yields `On` when the light
            // is plugged in and `Off` when it is not. Availability
            // Has no effect on the API other than that you won't
            // see the light change because it lacks external power.
            try!(manager.add_getter(Channel {
                tags: HashSet::new(),
                adapter: adapter_id.clone(),
                id: self.get_available_id.clone(),
                last_seen: None,
                service: self.service_id.clone(),
                mechanism: Getter {
                    kind: ChannelKind::Extension {
                        vendor: Id::new("foxlink@mozilla.com"),
                        adapter: Id::new("Philips Hue Adapter"),
                        kind: Id::new("available"),
                        typ: Type::OnOff,
                    },
                    updated: None,
                },
            }));

            try!(manager.add_getter(Channel {
                tags: HashSet::new(),
                adapter: adapter_id.clone(),
                id: self.get_power_id.clone(),
                last_seen: None,
                service: self.service_id.clone(),
                mechanism: Getter {
                    kind: ChannelKind::LightOn,
                    updated: None,
                },
            }));

            try!(manager.add_setter(Channel {
                tags: HashSet::new(),
                adapter: adapter_id.clone(),
                id: self.set_power_id.clone(),
                last_seen: None,
                service: self.service_id.clone(),
                mechanism: Setter {
                    kind: ChannelKind::LightOn,
                    updated: None,
                },
            }));

            let mut services_lock = services.lock().unwrap();
            services_lock.getters.insert(self.get_available_id.clone(), self.clone());
            services_lock.getters.insert(self.get_power_id.clone(), self.clone());
            services_lock.setters.insert(self.set_power_id.clone(), self.clone());

        } else {
            warn!("Ignoring unsupported Hue light type {}, ID {} on bridge {}",
                status.lighttype, self.light_id, self.hub_id);
        }
        Ok(())
    }

    pub fn get_available(&self) -> bool {
        let status = self.api.get_light_status(&self.light_id);
        status.state.reachable
    }

    pub fn get_power(&self) -> bool {
        let status = self.api.get_light_status(&self.light_id);
        status.state.on
    }

    pub fn set_power(&self, on: bool) {
        self.api.set_light_power(&self.light_id, on);
    }

}
<|endoftext|>


<commit_before>use business::usecase::{NewEntry, UpdateEntry};
use entities::Entry;

pub fn email_confirmation_email(u_id: &str) -> String {
    format!("Na du Weltverbesserer*,\nwir freuen uns dass du bei der Karte von Morgen mit dabei bist!\n\nBitte bestätige deine Email-Adresse hier:\nhttps://prototyp.kartevonmorgen.org/#/?confirm_email={}.\n\neuphorische Grüße\ndas Karte von Morgen-Team",
    u_id)
}

pub fn new_entry_email(e: &NewEntry, id: &str, categories: Vec<String>) -> String {
    let intro_sentence = "ein neuer Eintrag auf der Karte von Morgen wurde erstellt";
    let entry = Entry {
        id : id.into(),
        title:e.title.clone(),
        description:e.description.clone(),
        street:e.street.clone(),
        zip:e.zip.clone(),
        city:e.city.clone(),
        country :e.country.clone(),
        email:e.email.clone(),
        telephone:e.telephone.clone(),
        homepage:e.homepage.clone(),
        categories:e.categories.clone(),
        lat:0.0,
        lng:0.0,
        created:0,
        version:0,
        license:None
    };
    entry_email(&entry, categories, &e.tags, intro_sentence)
}

pub fn changed_entry_email(e: &UpdateEntry, categories: Vec<String>) -> String {
    let intro_sentence = "folgender Eintrag der Karte von Morgen wurde verändert";
    let entry = Entry {
        id : e.id.clone(),
        title:e.title.clone(),
        description:e.description.clone(),
        street:e.street.clone(),
        zip:e.zip.clone(),
        city:e.city.clone(),
        country :e.country.clone(),
        email:e.email.clone(),
        telephone:e.telephone.clone(),
        homepage:e.homepage.clone(),
        categories:e.categories.clone(),
        lat:0.0,
        lng:0.0,
        created:0,
        version:0,
        license:None
    };
    entry_email(&entry, categories, &e.tags, intro_sentence)
}

pub fn entry_email(e: &Entry, categories: Vec<String>, tags: &Vec<String>, intro_sentence: &str) -> String{
    let category = if categories.len() > 0 { categories[0].clone() } else { "".to_string() };
    let address = vec![
        e.street.clone().unwrap_or("".into()),
        vec![e.zip.clone().unwrap_or("".into()),
            e.city.clone().unwrap_or("".into())].join(" "),
        e.country.clone().unwrap_or("".into())]
        .join(", ");

    format!(
"Hallo,
{introSentence}:\n
{title} ({category})
{description}\n
    Tags: {tags}
    Adresse: {address}
    Webseite: {homepage}
    Email-Adresse: {email}
    Telefon: {telephone}\n
Eintrag anschauen oder bearbeiten:
https://kartevonmorgen.org/#/?entry={id}\n
Du kannst dein Abonnement des Kartenbereichs abbestellen indem du dich auf https://kartevonmorgen.org einloggst.\n
euphorische Grüße
das Karte von Morgen-Team",
        introSentence = intro_sentence,
        title = &e.title,
        id = &e.id,
        description= &e.description,
        address = address,
        email = e.email.clone().unwrap_or("".into()),
        telephone = e.telephone.clone().unwrap_or("".into()),
        homepage = e.homepage.clone().unwrap_or("".into()),
        category = category,
        tags = tags.join(", "))
}<commit_msg>fix(emails): remove 'prototype' from a link in confirmation email<commit_after>use business::usecase::{NewEntry, UpdateEntry};
use entities::Entry;

pub fn email_confirmation_email(u_id: &str) -> String {
    format!("Na du Weltverbesserer*,\nwir freuen uns dass du bei der Karte von Morgen mit dabei bist!\n\nBitte bestätige deine Email-Adresse hier:\nhttps://kartevonmorgen.org/#/?confirm_email={}.\n\neuphorische Grüße\ndas Karte von Morgen-Team",
    u_id)
}

pub fn new_entry_email(e: &NewEntry, id: &str, categories: Vec<String>) -> String {
    let intro_sentence = "ein neuer Eintrag auf der Karte von Morgen wurde erstellt";
    let entry = Entry {
        id : id.into(),
        title:e.title.clone(),
        description:e.description.clone(),
        street:e.street.clone(),
        zip:e.zip.clone(),
        city:e.city.clone(),
        country :e.country.clone(),
        email:e.email.clone(),
        telephone:e.telephone.clone(),
        homepage:e.homepage.clone(),
        categories:e.categories.clone(),
        lat:0.0,
        lng:0.0,
        created:0,
        version:0,
        license:None
    };
    entry_email(&entry, categories, &e.tags, intro_sentence)
}

pub fn changed_entry_email(e: &UpdateEntry, categories: Vec<String>) -> String {
    let intro_sentence = "folgender Eintrag der Karte von Morgen wurde verändert";
    let entry = Entry {
        id : e.id.clone(),
        title:e.title.clone(),
        description:e.description.clone(),
        street:e.street.clone(),
        zip:e.zip.clone(),
        city:e.city.clone(),
        country :e.country.clone(),
        email:e.email.clone(),
        telephone:e.telephone.clone(),
        homepage:e.homepage.clone(),
        categories:e.categories.clone(),
        lat:0.0,
        lng:0.0,
        created:0,
        version:0,
        license:None
    };
    entry_email(&entry, categories, &e.tags, intro_sentence)
}

pub fn entry_email(e: &Entry, categories: Vec<String>, tags: &Vec<String>, intro_sentence: &str) -> String{
    let category = if categories.len() > 0 { categories[0].clone() } else { "".to_string() };
    let address = vec![
        e.street.clone().unwrap_or("".into()),
        vec![e.zip.clone().unwrap_or("".into()),
            e.city.clone().unwrap_or("".into())].join(" "),
        e.country.clone().unwrap_or("".into())]
        .join(", ");

    format!(
"Hallo,
{introSentence}:\n
{title} ({category})
{description}\n
    Tags: {tags}
    Adresse: {address}
    Webseite: {homepage}
    Email-Adresse: {email}
    Telefon: {telephone}\n
Eintrag anschauen oder bearbeiten:
https://kartevonmorgen.org/#/?entry={id}\n
Du kannst dein Abonnement des Kartenbereichs abbestellen indem du dich auf https://kartevonmorgen.org einloggst.\n
euphorische Grüße
das Karte von Morgen-Team",
        introSentence = intro_sentence,
        title = &e.title,
        id = &e.id,
        description= &e.description,
        address = address,
        email = e.email.clone().unwrap_or("".into()),
        telephone = e.telephone.clone().unwrap_or("".into()),
        homepage = e.homepage.clone().unwrap_or("".into()),
        category = category,
        tags = tags.join(", "))
}<|endoftext|>