<commit_before>pub mod rope;
pub mod seq;
pub mod small;

use {Buf, IntoBuf};
use self::seq::Seq;
use self::small::Small;
use self::rope::{Rope, RopeBuf};
use std::{cmp, fmt, ops};
use std::io::Cursor;
use std::sync::Arc;

/// An immutable sequence of bytes
#[derive(Clone)]
pub struct Bytes {
    kind: Kind,
}

#[derive(Clone)]
enum Kind {
    Seq(Seq),
    Small(Small),
    Rope(Arc<Rope>),
}

pub struct BytesBuf<'a> {
    kind: BufKind<'a>,
}

enum BufKind<'a> {
    Cursor(Cursor<&'a [u8]>),
    Rope(RopeBuf<'a>),
}

impl Bytes {
    /// Return an empty `Bytes`
    pub fn empty() -> Bytes {
        Bytes { kind: Kind::Small(Small::empty()) }
    }

    pub fn from_slice<T: AsRef<[u8]>>(slice: T) -> Bytes {
        Small::from_slice(slice.as_ref())
            .map(|b| Bytes { kind: Kind::Small(b)})
            .unwrap_or_else(|| Seq::from_slice(slice.as_ref()))
    }

    /// Creates a new `Bytes` from an `Arc<Box<[u8]>>`, an offset, and a length.
    #[inline]
    pub fn from_boxed(mem: Arc<Box<[u8]>>, pos: usize, len: usize) -> Bytes {
        // Check ranges
        assert!(pos + len <= mem.len(), "invalid arguments");

        Small::from_slice(&mem[pos..pos + len])
            .map(|b| Bytes { kind: Kind::Small(b) })
            .unwrap_or_else(|| {
                let seq = Seq::new(mem, pos, len);
                Bytes { kind: Kind::Seq(seq) }
            })
    }

    pub fn buf(&self) -> BytesBuf {
        let kind = match self.kind {
            Kind::Seq(ref v) => BufKind::Cursor(v.buf()),
            Kind::Small(ref v) => BufKind::Cursor(v.buf()),
            Kind::Rope(ref v) => BufKind::Rope(v.buf()),
        };

        BytesBuf { kind: kind }
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    pub fn len(&self) -> usize {
        match self.kind {
            Kind::Seq(ref v) => v.len(),
            Kind::Small(ref v) => v.len(),
            Kind::Rope(ref v) => v.len(),
        }
    }

    pub fn concat(&self, other: &Bytes) -> Bytes {
        Rope::concat(self.clone(), other.clone())
    }

    /// Returns a new ByteStr value containing the byte range between `begin`
    /// (inclusive) and `end` (exclusive)
    pub fn slice(&self, begin: usize, end: usize) -> Bytes {
        match self.kind {
            Kind::Seq(ref v) => v.slice(begin, end),
            Kind::Small(ref v) => v.slice(begin, end),
            Kind::Rope(ref v) => v.slice(begin, end),
        }
    }

    /// Returns a new ByteStr value containing the byte range starting from
    /// `begin` (inclusive) to the end of the byte str.
    ///
    /// Equivalent to `bytes.slice(begin, bytes.len())`
    pub fn slice_from(&self, begin: usize) -> Bytes {
        self.slice(begin, self.len())
    }

    /// Returns a new ByteStr value containing the byte range from the start up
    /// to `end` (exclusive).
    ///
    /// Equivalent to `bytes.slice(0, end)`
    pub fn slice_to(&self, end: usize) -> Bytes {
        self.slice(0, end)
    }

    /// Returns the Rope depth
    fn depth(&self) -> u16 {
        match self.kind {
            Kind::Rope(ref r) => r.depth(),
            _ => 0,
        }
    }

    fn into_rope(self) -> Result<Arc<Rope>, Bytes> {
        match self.kind {
            Kind::Rope(r) => Ok(r),
            _ => Err(self),
        }
    }
}

impl<'a> From<&'a [u8]> for Bytes {
    fn from(src: &'a [u8]) -> Bytes {
        Bytes::from_slice(src)
    }
}

impl From<Vec<u8>> for Bytes {
    fn from(src: Vec<u8>) -> Bytes {
        let mem = Arc::new(src.into_boxed_slice());
        let len = mem.len();

        Bytes::from_boxed(mem, 0, len)
    }
}

impl ops::Index<usize> for Bytes {
    type Output = u8;

    fn index(&self, index: usize) -> &u8 {
        match self.kind {
            Kind::Seq(ref v) => v.index(index),
            Kind::Small(ref v) => v.index(index),
            Kind::Rope(ref v) => v.index(index),
        }
    }
}

impl cmp::PartialEq<Bytes> for Bytes {
    fn eq(&self, other: &Bytes) -> bool {
        if self.len() != other.len() {
            return false;
        }

        let mut buf1 = self.buf();
        let mut buf2 = self.buf();

        while buf1.has_remaining() {
            let len;

            {
                let b1 = buf1.bytes();
                let b2 = buf2.bytes();

                len = cmp::min(b1.len(), b2.len());

                if b1[..len] != b2[..len] {
                    return false;
                }
            }

            buf1.advance(len);
            buf2.advance(len);
        }

        true
    }

    fn ne(&self, other: &Bytes) -> bool {
        return !self.eq(other)
    }
}

impl<'a> IntoBuf for &'a Bytes {
    type Buf = BytesBuf<'a>;

    fn into_buf(self) -> Self::Buf {
        self.buf()
    }
}

/*
 *
 * ===== BytesBuf =====
 *
 */

impl<'a> Buf for BytesBuf<'a> {
    fn remaining(&self) -> usize {
        match self.kind {
            BufKind::Cursor(ref v) => v.remaining(),
            BufKind::Rope(ref v) => v.remaining(),
        }
    }

    fn bytes(&self) -> &[u8] {
        match self.kind {
            BufKind::Cursor(ref v) => v.bytes(),
            BufKind::Rope(ref v) => v.bytes(),
        }
    }

    fn advance(&mut self, cnt: usize) {
        match self.kind {
            BufKind::Cursor(ref mut v) => v.advance(cnt),
            BufKind::Rope(ref mut v) => v.advance(cnt),
        }
    }
}

/*
 *
 * ===== Internal utilities =====
 *
 */

impl fmt::Debug for Bytes {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let mut buf = self.buf();

        try!(write!(fmt, "Bytes[len={}; ", self.len()));

        let mut rem = 128;

        while buf.has_remaining() {
            let byte = buf.read_u8();

            if rem > 0 {
                if is_ascii(byte) {
                    try!(write!(fmt, "{}", byte as char));
                } else {
                    try!(write!(fmt, "\\x{:02X}", byte));
                }

                rem -= 1;
            } else {
                try!(write!(fmt, " ... "));
                break;
            }
        }

        try!(write!(fmt, "]"));

        Ok(())
    }
}

fn is_ascii(byte: u8) -> bool {
    match byte {
        10 | 13 | 32...126 => true,
        _ => false,
    }
}
<commit_msg>Add Bytes::from_vec<commit_after>pub mod rope;
pub mod seq;
pub mod small;

use {Buf, IntoBuf};
use self::seq::Seq;
use self::small::Small;
use self::rope::{Rope, RopeBuf};
use std::{cmp, fmt, ops};
use std::io::Cursor;
use std::sync::Arc;

/// An immutable sequence of bytes
#[derive(Clone)]
pub struct Bytes {
    kind: Kind,
}

#[derive(Clone)]
enum Kind {
    Seq(Seq),
    Small(Small),
    Rope(Arc<Rope>),
}

pub struct BytesBuf<'a> {
    kind: BufKind<'a>,
}

enum BufKind<'a> {
    Cursor(Cursor<&'a [u8]>),
    Rope(RopeBuf<'a>),
}

impl Bytes {
    /// Return an empty `Bytes`
    pub fn empty() -> Bytes {
        Bytes { kind: Kind::Small(Small::empty()) }
    }

    pub fn from_slice<T: AsRef<[u8]>>(slice: T) -> Bytes {
        Small::from_slice(slice.as_ref())
            .map(|b| Bytes { kind: Kind::Small(b)})
            .unwrap_or_else(|| Seq::from_slice(slice.as_ref()))
    }

    pub fn from_vec(mem: Vec<u8>) -> Bytes {
        let pos = 0;
        let len = mem.len();

        Small::from_slice(&mem[..])
            .map(|b| Bytes { kind: Kind::Small(b) })
            .unwrap_or_else(|| {
                let seq = Seq::new(Arc::new(mem.into_boxed_slice()), pos, len);
                Bytes { kind: Kind::Seq(seq) }
            })
    }

    /// Creates a new `Bytes` from an `Arc<Box<[u8]>>`, an offset, and a length.
    #[inline]
    pub fn from_boxed(mem: Arc<Box<[u8]>>, pos: usize, len: usize) -> Bytes {
        // Check ranges
        assert!(pos + len <= mem.len(), "invalid arguments");

        Small::from_slice(&mem[pos..pos + len])
            .map(|b| Bytes { kind: Kind::Small(b) })
            .unwrap_or_else(|| {
                let seq = Seq::new(mem, pos, len);
                Bytes { kind: Kind::Seq(seq) }
            })
    }

    pub fn buf(&self) -> BytesBuf {
        let kind = match self.kind {
            Kind::Seq(ref v) => BufKind::Cursor(v.buf()),
            Kind::Small(ref v) => BufKind::Cursor(v.buf()),
            Kind::Rope(ref v) => BufKind::Rope(v.buf()),
        };

        BytesBuf { kind: kind }
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    pub fn len(&self) -> usize {
        match self.kind {
            Kind::Seq(ref v) => v.len(),
            Kind::Small(ref v) => v.len(),
            Kind::Rope(ref v) => v.len(),
        }
    }

    pub fn concat(&self, other: &Bytes) -> Bytes {
        Rope::concat(self.clone(), other.clone())
    }

    /// Returns a new ByteStr value containing the byte range between `begin`
    /// (inclusive) and `end` (exclusive)
    pub fn slice(&self, begin: usize, end: usize) -> Bytes {
        match self.kind {
            Kind::Seq(ref v) => v.slice(begin, end),
            Kind::Small(ref v) => v.slice(begin, end),
            Kind::Rope(ref v) => v.slice(begin, end),
        }
    }

    /// Returns a new ByteStr value containing the byte range starting from
    /// `begin` (inclusive) to the end of the byte str.
    ///
    /// Equivalent to `bytes.slice(begin, bytes.len())`
    pub fn slice_from(&self, begin: usize) -> Bytes {
        self.slice(begin, self.len())
    }

    /// Returns a new ByteStr value containing the byte range from the start up
    /// to `end` (exclusive).
    ///
    /// Equivalent to `bytes.slice(0, end)`
    pub fn slice_to(&self, end: usize) -> Bytes {
        self.slice(0, end)
    }

    /// Returns the Rope depth
    fn depth(&self) -> u16 {
        match self.kind {
            Kind::Rope(ref r) => r.depth(),
            _ => 0,
        }
    }

    fn into_rope(self) -> Result<Arc<Rope>, Bytes> {
        match self.kind {
            Kind::Rope(r) => Ok(r),
            _ => Err(self),
        }
    }
}

impl<'a> From<&'a [u8]> for Bytes {
    fn from(src: &'a [u8]) -> Bytes {
        Bytes::from_slice(src)
    }
}

impl From<Vec<u8>> for Bytes {
    fn from(src: Vec<u8>) -> Bytes {
        let mem = Arc::new(src.into_boxed_slice());
        let len = mem.len();

        Bytes::from_boxed(mem, 0, len)
    }
}

impl ops::Index<usize> for Bytes {
    type Output = u8;

    fn index(&self, index: usize) -> &u8 {
        match self.kind {
            Kind::Seq(ref v) => v.index(index),
            Kind::Small(ref v) => v.index(index),
            Kind::Rope(ref v) => v.index(index),
        }
    }
}

impl cmp::PartialEq<Bytes> for Bytes {
    fn eq(&self, other: &Bytes) -> bool {
        if self.len() != other.len() {
            return false;
        }

        let mut buf1 = self.buf();
        let mut buf2 = self.buf();

        while buf1.has_remaining() {
            let len;

            {
                let b1 = buf1.bytes();
                let b2 = buf2.bytes();

                len = cmp::min(b1.len(), b2.len());

                if b1[..len] != b2[..len] {
                    return false;
                }
            }

            buf1.advance(len);
            buf2.advance(len);
        }

        true
    }

    fn ne(&self, other: &Bytes) -> bool {
        return !self.eq(other)
    }
}

impl<'a> IntoBuf for &'a Bytes {
    type Buf = BytesBuf<'a>;

    fn into_buf(self) -> Self::Buf {
        self.buf()
    }
}

/*
 *
 * ===== BytesBuf =====
 *
 */

impl<'a> Buf for BytesBuf<'a> {
    fn remaining(&self) -> usize {
        match self.kind {
            BufKind::Cursor(ref v) => v.remaining(),
            BufKind::Rope(ref v) => v.remaining(),
        }
    }

    fn bytes(&self) -> &[u8] {
        match self.kind {
            BufKind::Cursor(ref v) => v.bytes(),
            BufKind::Rope(ref v) => v.bytes(),
        }
    }

    fn advance(&mut self, cnt: usize) {
        match self.kind {
            BufKind::Cursor(ref mut v) => v.advance(cnt),
            BufKind::Rope(ref mut v) => v.advance(cnt),
        }
    }
}

/*
 *
 * ===== Internal utilities =====
 *
 */

impl fmt::Debug for Bytes {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let mut buf = self.buf();

        try!(write!(fmt, "Bytes[len={}; ", self.len()));

        let mut rem = 128;

        while buf.has_remaining() {
            let byte = buf.read_u8();

            if rem > 0 {
                if is_ascii(byte) {
                    try!(write!(fmt, "{}", byte as char));
                } else {
                    try!(write!(fmt, "\\x{:02X}", byte));
                }

                rem -= 1;
            } else {
                try!(write!(fmt, " ... "));
                break;
            }
        }

        try!(write!(fmt, "]"));

        Ok(())
    }
}

fn is_ascii(byte: u8) -> bool {
    match byte {
        10 | 13 | 32...126 => true,
        _ => false,
    }
}
<|endoftext|>


<commit_before>use std;
use binascii;

use server::Events;

#[derive(Deserialize, Clone)]
pub enum TrackerMode {

    /// In static mode torrents are tracked only if they were added ahead of time.
    #[serde(rename="static")]
    StaticMode,

    /// In dynamic mode, torrents are tracked being added ahead of time.
    #[serde(rename="dynamic")]
    DynamicMode,

    /// Tracker will only serve authenticated peers.
    #[serde(rename="private")]
    PrivateMode,
}

struct TorrentPeer {
    ip: std::net::SocketAddr,
    uploaded: u64,
    downloaded: u64,
    left: u64,
    event: Events,
    updated: std::time::SystemTime,
}

#[derive(Ord, PartialEq, Eq, Clone)]
pub struct InfoHash {
    info_hash: [u8; 20],
}

impl std::cmp::PartialOrd<InfoHash> for InfoHash {
    fn partial_cmp(&self, other: &InfoHash) -> Option<std::cmp::Ordering> {
        self.info_hash.partial_cmp(&other.info_hash)
    }
}

impl std::convert::Into<InfoHash> for [u8; 20] {
    fn into(self) -> InfoHash {
        InfoHash{
            info_hash: self,
        }
    }
}

impl serde::ser::Serialize for InfoHash {
    fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut buffer = [0u8; 40];
        let bytes_out = binascii::bin2hex(&self.info_hash, &mut buffer).ok().unwrap();
        let str_out = std::str::from_utf8(bytes_out).unwrap();

        serializer.serialize_str(str_out)
    }
}

pub type PeerId = [u8; 20];

#[derive(Serialize, Deserialize)]
pub struct TorrentEntry {
    is_flagged: bool,

    #[serde(skip)]
    peers: std::collections::BTreeMap<PeerId, TorrentPeer>,

    completed: u32,

    #[serde(skip)]
    seeders: u32,
}

impl TorrentEntry {
    pub fn new() -> TorrentEntry{
        TorrentEntry{
            is_flagged: false,
            peers: std::collections::BTreeMap::new(),
            completed: 0,
            seeders: 0,
        }
    }

    pub fn is_flagged(&self) -> bool {
        self.is_flagged
    }

    pub fn update_peer(&mut self, peer_id: &PeerId, remote_address: &std::net::SocketAddr, uploaded: u64, downloaded: u64, left: u64, event: Events) {
        let is_seeder = left == 0 && uploaded > 0;
        let mut was_seeder = false;
        let mut is_completed = left == 0 && (event as u32) == (Events::Complete as u32);
        if let Some(prev) = self.peers.insert(*peer_id, TorrentPeer{
            updated: std::time::SystemTime::now(),
            left,
            downloaded,
            uploaded,
            ip: *remote_address,
            event,
        }) {
            was_seeder = prev.left == 0 && prev.uploaded > 0;

            if is_completed && (prev.event as u32) == (Events::Complete as u32) {
                // don't update count again. a torrent should only be updated once per peer.
                is_completed = false;
            }
        }

        if is_seeder && !was_seeder {
            self.seeders += 1;
        } else if was_seeder && !is_seeder {
            self.seeders -= 1;
        }

        if is_completed {
            self.completed += 1;
        }
    }

    pub fn get_peers(&self, remote_addr: &std::net::SocketAddr) -> Vec<std::net::SocketAddr> {
        let mut list = Vec::new();
        for (_, peer) in self.peers.iter().filter(|e| e.1.ip.is_ipv4() == remote_addr.is_ipv4()).take(74) {
            if peer.ip == *remote_addr {
                continue;
            }

            list.push(peer.ip);
        }
        list
    }

    pub fn get_stats(&self) -> (u32, u32, u32) {
        let leechers = (self.peers.len() as u32) - self.seeders;
        (self.seeders, self.completed, leechers)
    }
}

struct TorrentDatabase {
    torrent_peers: std::sync::RwLock<std::collections::BTreeMap<InfoHash, TorrentEntry>>,
}

impl Default for TorrentDatabase {
    fn default() -> Self {
        TorrentDatabase{
            torrent_peers: std::sync::RwLock::new(std::collections::BTreeMap::new()),
        }
    }
}

pub struct TorrentTracker {
    mode: TrackerMode,
    database: TorrentDatabase,
}

pub enum TorrentStats {
    TorrentFlagged,
    TorrentNotRegistered,
    Stats{
        seeders: u32,
        leechers: u32,
        complete: u32,
    }
}

impl TorrentTracker {
    pub fn new(mode: TrackerMode) -> TorrentTracker {
        TorrentTracker{
            mode,
            database: TorrentDatabase{
                torrent_peers: std::sync::RwLock::new(std::collections::BTreeMap::new()),
            }
        }
    }

    /// Adding torrents is not relevant to dynamic trackers.
    pub fn add_torrent(&self, info_hash: &InfoHash) -> Result<(), ()> {
        let mut write_lock = self.database.torrent_peers.write().unwrap();
        match write_lock.entry(info_hash.clone()) {
            std::collections::btree_map::Entry::Vacant(ve) => {
                ve.insert(TorrentEntry::new());
                return Ok(());
            },
            std::collections::btree_map::Entry::Occupied(_entry) => {
                return Err(());
            }
        }
    }

    /// If the torrent is flagged, it will not be removed unless force is set to true.
    pub fn remove_torrent(&self, info_hash: &InfoHash, force: bool) -> Result<(), ()> {
        use std::collections::btree_map::Entry;
        let mut entry_lock = self.database.torrent_peers.write().unwrap();
        let torrent_entry = entry_lock.entry(info_hash.clone());
        match torrent_entry {
            Entry::Vacant(_) => {
                // no entry, nothing to do...
                return Err(());
            },
            Entry::Occupied(entry) => {
                if force || !entry.get().is_flagged() {
                    entry.remove();
                    return Ok(());
                }
                return Err(());
            },
        }
    }

    /// flagged torrents will result in a tracking error. This is to allow enforcement against piracy.
    pub fn set_torrent_flag(&self, info_hash: &InfoHash, is_flagged: bool) {
        if let Some(entry) = self.database.torrent_peers.write().unwrap().get_mut(info_hash) {
            if is_flagged && !entry.is_flagged {
                // empty peer list.
                entry.peers.clear();
            }
            entry.is_flagged = is_flagged;
        }
    }

    pub fn get_torrent_peers(&self, info_hash: &InfoHash, remote_addr: &std::net::SocketAddr) -> Option<Vec<std::net::SocketAddr>> {
        let read_lock = self.database.torrent_peers.read().unwrap();
        match read_lock.get(info_hash) {
            None => {
                return None;
            }
            Some(entry) => {
                return Some(entry.get_peers(remote_addr));
            }
        };
    }

    pub fn update_torrent_and_get_stats(&self, info_hash: &InfoHash, peer_id: &PeerId, remote_address: &std::net::SocketAddr, uploaded: u64, downloaded: u64, left: u64, event: Events) -> TorrentStats {
        use std::collections::btree_map::Entry;
        let mut torrent_peers = self.database.torrent_peers.write().unwrap();
        let torrent_entry = match torrent_peers.entry(info_hash.clone()) {
            Entry::Vacant(vacant) => {
                match self.mode {
                    TrackerMode::DynamicMode => {
                        vacant.insert(TorrentEntry::new())
                    },
                    _ => {
                        return TorrentStats::TorrentNotRegistered;
                    }
                }
            },
            Entry::Occupied(entry) => {
                if entry.get().is_flagged() {
                    return TorrentStats::TorrentFlagged;
                }
                entry.into_mut()
            },
        };

        torrent_entry.update_peer(peer_id, remote_address, uploaded, downloaded, left, event);

        let (seeders, complete, leechers) = torrent_entry.get_stats();

        return TorrentStats::Stats {
            seeders,
            leechers,
            complete,
        };
    }

    pub (crate) fn get_database(&self) -> std::sync::RwLockReadGuard<std::collections::BTreeMap<InfoHash, TorrentEntry>>{
        self.database.torrent_peers.read().unwrap()
    }
}

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

    fn is_sync<T: Sync>() {}
    fn is_send<T: Send>() {}

    #[test]
    fn tracker_send() {
        is_send::<TorrentTracker>();
    }

    #[test]
    fn tracker_sync() {
        is_sync::<TorrentTracker>();
    }
}<commit_msg>Implemented deserializer for InfoHash<commit_after>use std;
use serde;
use binascii;

use server::Events;

#[derive(Deserialize, Clone)]
pub enum TrackerMode {

    /// In static mode torrents are tracked only if they were added ahead of time.
    #[serde(rename="static")]
    StaticMode,

    /// In dynamic mode, torrents are tracked being added ahead of time.
    #[serde(rename="dynamic")]
    DynamicMode,

    /// Tracker will only serve authenticated peers.
    #[serde(rename="private")]
    PrivateMode,
}

struct TorrentPeer {
    ip: std::net::SocketAddr,
    uploaded: u64,
    downloaded: u64,
    left: u64,
    event: Events,
    updated: std::time::SystemTime,
}

#[derive(Ord, PartialEq, Eq, Clone)]
pub struct InfoHash {
    info_hash: [u8; 20],
}

impl std::cmp::PartialOrd<InfoHash> for InfoHash {
    fn partial_cmp(&self, other: &InfoHash) -> Option<std::cmp::Ordering> {
        self.info_hash.partial_cmp(&other.info_hash)
    }
}

impl std::convert::Into<InfoHash> for [u8; 20] {
    fn into(self) -> InfoHash {
        InfoHash{
            info_hash: self,
        }
    }
}

impl serde::ser::Serialize for InfoHash {
    fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut buffer = [0u8; 40];
        let bytes_out = binascii::bin2hex(&self.info_hash, &mut buffer).ok().unwrap();
        let str_out = std::str::from_utf8(bytes_out).unwrap();

        serializer.serialize_str(str_out)
    }
}

struct InfoHashVisitor;

impl<'v> serde::de::Visitor<'v> for InfoHashVisitor {
    type Value = InfoHash;

    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(formatter, "a 40 character long hash")
    }

    fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
        if v.len() != 40 {
            return Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(v), &"expected a 40 character long string"));
        }

        let mut res = InfoHash{
            info_hash: [0u8; 20],
        };

        if let Err(_) = binascii::hex2bin(v.as_bytes(), &mut res.info_hash) {
            return Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(v), &"expected a hexadecimal string"));
        } else {
            return Ok(res);
        }
    }
}

impl<'de> serde::de::Deserialize<'de> for InfoHash {
    fn deserialize<D: serde::de::Deserializer<'de>>(des: D) -> Result<Self, D::Error> {
        des.deserialize_str(InfoHashVisitor)
    }
}

pub type PeerId = [u8; 20];

#[derive(Serialize, Deserialize)]
pub struct TorrentEntry {
    is_flagged: bool,

    #[serde(skip)]
    peers: std::collections::BTreeMap<PeerId, TorrentPeer>,

    completed: u32,

    #[serde(skip)]
    seeders: u32,
}

impl TorrentEntry {
    pub fn new() -> TorrentEntry{
        TorrentEntry{
            is_flagged: false,
            peers: std::collections::BTreeMap::new(),
            completed: 0,
            seeders: 0,
        }
    }

    pub fn is_flagged(&self) -> bool {
        self.is_flagged
    }

    pub fn update_peer(&mut self, peer_id: &PeerId, remote_address: &std::net::SocketAddr, uploaded: u64, downloaded: u64, left: u64, event: Events) {
        let is_seeder = left == 0 && uploaded > 0;
        let mut was_seeder = false;
        let mut is_completed = left == 0 && (event as u32) == (Events::Complete as u32);
        if let Some(prev) = self.peers.insert(*peer_id, TorrentPeer{
            updated: std::time::SystemTime::now(),
            left,
            downloaded,
            uploaded,
            ip: *remote_address,
            event,
        }) {
            was_seeder = prev.left == 0 && prev.uploaded > 0;

            if is_completed && (prev.event as u32) == (Events::Complete as u32) {
                // don't update count again. a torrent should only be updated once per peer.
                is_completed = false;
            }
        }

        if is_seeder && !was_seeder {
            self.seeders += 1;
        } else if was_seeder && !is_seeder {
            self.seeders -= 1;
        }

        if is_completed {
            self.completed += 1;
        }
    }

    pub fn get_peers(&self, remote_addr: &std::net::SocketAddr) -> Vec<std::net::SocketAddr> {
        let mut list = Vec::new();
        for (_, peer) in self.peers.iter().filter(|e| e.1.ip.is_ipv4() == remote_addr.is_ipv4()).take(74) {
            if peer.ip == *remote_addr {
                continue;
            }

            list.push(peer.ip);
        }
        list
    }

    pub fn get_stats(&self) -> (u32, u32, u32) {
        let leechers = (self.peers.len() as u32) - self.seeders;
        (self.seeders, self.completed, leechers)
    }
}

struct TorrentDatabase {
    torrent_peers: std::sync::RwLock<std::collections::BTreeMap<InfoHash, TorrentEntry>>,
}

impl Default for TorrentDatabase {
    fn default() -> Self {
        TorrentDatabase{
            torrent_peers: std::sync::RwLock::new(std::collections::BTreeMap::new()),
        }
    }
}

pub struct TorrentTracker {
    mode: TrackerMode,
    database: TorrentDatabase,
}

pub enum TorrentStats {
    TorrentFlagged,
    TorrentNotRegistered,
    Stats{
        seeders: u32,
        leechers: u32,
        complete: u32,
    }
}

impl TorrentTracker {
    pub fn new(mode: TrackerMode) -> TorrentTracker {
        TorrentTracker{
            mode,
            database: TorrentDatabase{
                torrent_peers: std::sync::RwLock::new(std::collections::BTreeMap::new()),
            }
        }
    }

    /// Adding torrents is not relevant to dynamic trackers.
    pub fn add_torrent(&self, info_hash: &InfoHash) -> Result<(), ()> {
        let mut write_lock = self.database.torrent_peers.write().unwrap();
        match write_lock.entry(info_hash.clone()) {
            std::collections::btree_map::Entry::Vacant(ve) => {
                ve.insert(TorrentEntry::new());
                return Ok(());
            },
            std::collections::btree_map::Entry::Occupied(_entry) => {
                return Err(());
            }
        }
    }

    /// If the torrent is flagged, it will not be removed unless force is set to true.
    pub fn remove_torrent(&self, info_hash: &InfoHash, force: bool) -> Result<(), ()> {
        use std::collections::btree_map::Entry;
        let mut entry_lock = self.database.torrent_peers.write().unwrap();
        let torrent_entry = entry_lock.entry(info_hash.clone());
        match torrent_entry {
            Entry::Vacant(_) => {
                // no entry, nothing to do...
                return Err(());
            },
            Entry::Occupied(entry) => {
                if force || !entry.get().is_flagged() {
                    entry.remove();
                    return Ok(());
                }
                return Err(());
            },
        }
    }

    /// flagged torrents will result in a tracking error. This is to allow enforcement against piracy.
    pub fn set_torrent_flag(&self, info_hash: &InfoHash, is_flagged: bool) {
        if let Some(entry) = self.database.torrent_peers.write().unwrap().get_mut(info_hash) {
            if is_flagged && !entry.is_flagged {
                // empty peer list.
                entry.peers.clear();
            }
            entry.is_flagged = is_flagged;
        }
    }

    pub fn get_torrent_peers(&self, info_hash: &InfoHash, remote_addr: &std::net::SocketAddr) -> Option<Vec<std::net::SocketAddr>> {
        let read_lock = self.database.torrent_peers.read().unwrap();
        match read_lock.get(info_hash) {
            None => {
                return None;
            }
            Some(entry) => {
                return Some(entry.get_peers(remote_addr));
            }
        };
    }

    pub fn update_torrent_and_get_stats(&self, info_hash: &InfoHash, peer_id: &PeerId, remote_address: &std::net::SocketAddr, uploaded: u64, downloaded: u64, left: u64, event: Events) -> TorrentStats {
        use std::collections::btree_map::Entry;
        let mut torrent_peers = self.database.torrent_peers.write().unwrap();
        let torrent_entry = match torrent_peers.entry(info_hash.clone()) {
            Entry::Vacant(vacant) => {
                match self.mode {
                    TrackerMode::DynamicMode => {
                        vacant.insert(TorrentEntry::new())
                    },
                    _ => {
                        return TorrentStats::TorrentNotRegistered;
                    }
                }
            },
            Entry::Occupied(entry) => {
                if entry.get().is_flagged() {
                    return TorrentStats::TorrentFlagged;
                }
                entry.into_mut()
            },
        };

        torrent_entry.update_peer(peer_id, remote_address, uploaded, downloaded, left, event);

        let (seeders, complete, leechers) = torrent_entry.get_stats();

        return TorrentStats::Stats {
            seeders,
            leechers,
            complete,
        };
    }

    pub (crate) fn get_database(&self) -> std::sync::RwLockReadGuard<std::collections::BTreeMap<InfoHash, TorrentEntry>>{
        self.database.torrent_peers.read().unwrap()
    }
}

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

    fn is_sync<T: Sync>() {}
    fn is_send<T: Send>() {}

    #[test]
    fn tracker_send() {
        is_send::<TorrentTracker>();
    }

    #[test]
    fn tracker_sync() {
        is_sync::<TorrentTracker>();
    }

    #[test]
    fn test_infohash_de() {
        use serde_json;

        let ih: InfoHash = [0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1].into();

        let serialized_ih = serde_json::to_string(&ih).unwrap();

        let de_ih: InfoHash = serde_json::from_str(serialized_ih.as_str()).unwrap();

        assert!(de_ih == ih);
    }
}<|endoftext|>


<commit_before>use std::path::PathBuf;

use xshell::pushd;

use crate::{cmd, Metadata, Result};

mod spec_links;

use spec_links::check_spec_links;

const MSRV: &str = "1.55";

/// Task to run CI tests.
pub struct CiTask {
    /// Which version of Rust to test against.
    version: Option<String>,

    /// The root of the workspace.
    project_root: PathBuf,
}

impl CiTask {
    pub(crate) fn new(version: Option<String>) -> Result<Self> {
        let project_root = Metadata::load()?.workspace_root;
        Ok(Self { version, project_root })
    }

    pub(crate) fn run(self) -> Result<()> {
        let _p = pushd(&self.project_root)?;

        match self.version.as_deref() {
            Some("msrv") => self.build_msrv()?,
            Some("stable") => self.build_stable()?,
            Some("nightly") => self.build_nightly()?,
            Some(_) => return Err("Wrong Rust version specified.".into()),
            None => {
                self.build_msrv().and(self.build_stable()).and(self.build_nightly())?;
            }
        }

        Ok(())
    }

    fn build_msrv(&self) -> Result<()> {
        // Check all crates with all features except
        // * ruma (would pull in ruma-signatures)
        // * ruma-client (tested with default features due to optional HTTP client deps)
        // * ruma-signatures (MSRV exception)
        // * xtask (no real reason to enforce an MSRV for it)
        cmd!(
            "rustup run {MSRV} cargo check --workspace --all-features
                --exclude ruma
                --exclude ruma-client
                --exclude ruma-signatures
                --exclude xtask"
        )
        .run()?;

        // Check ruma-client crate with default features
        cmd!("rustup run {MSRV} cargo check -p ruma-client").run()?;

        // Check ruma crate with default features
        cmd!("rustup run {MSRV} cargo check -p ruma").run().map_err(Into::into)
    }

    fn build_stable(&self) -> Result<()> {
        // 1. Make sure everything compiles
        cmd!("rustup run stable cargo check --workspace --all-features").run()?;
        cmd!("rustup run stable cargo check -p ruma-client --no-default-features").run()?;
        cmd!("rustup run stable cargo check -p ruma-identifiers --no-default-features").run()?;

        // 2. Run tests
        let workspace_res =
            cmd!("rustup run stable cargo test --workspace --features full,unstable-pre-spec")
                .run();
        let events_compat_res =
            cmd!("rustup run stable cargo test -p ruma-events --features compat compat").run();

        workspace_res.and(events_compat_res).map_err(Into::into)
    }

    fn build_nightly(&self) -> Result<()> {
        // Check formatting
        let fmt_res = cmd!("rustup run nightly cargo fmt -- --check").run();
        // Check `ruma` crate with `full` feature (sometimes things only compile with an unstable
        // flag)
        let check_full_res = cmd!("rustup run nightly cargo check -p ruma --features full").run();
        // Check everything with default features with clippy
        let clippy_default_res = cmd!(
            "
            rustup run nightly cargo clippy
                --workspace --all-targets --features=full -- -D warnings
            "
        )
        .run();
        // Check everything with almost all features with clippy
        let clippy_all_res = cmd!(
            "
            rustup run nightly cargo clippy
                --workspace --all-targets --features=full,compat,unstable-pre-spec -- -D warnings
            "
        )
        .run();
        // Check dependencies being sorted
        let sort_res = cmd!(
            "
            rustup run nightly cargo sort
                --workspace --grouped --check
                --order package,lib,features,dependencies,dev-dependencies,build-dependencies
            "
        )
        .run();
        // Check that all links point to the same version of the spec
        let spec_links = check_spec_links(&self.project_root.join("crates"));

        fmt_res
            .and(check_full_res)
            .and(clippy_default_res)
            .and(clippy_all_res)
            .and(sort_res)
            .map_err(Into::into)
            .and(spec_links)
    }
}
<commit_msg>Fix CI to cross-check ruma-client and ruma-client-api<commit_after>use std::path::PathBuf;

use xshell::pushd;

use crate::{cmd, Metadata, Result};

mod spec_links;

use spec_links::check_spec_links;

const MSRV: &str = "1.55";

/// Task to run CI tests.
pub struct CiTask {
    /// Which version of Rust to test against.
    version: Option<String>,

    /// The root of the workspace.
    project_root: PathBuf,
}

impl CiTask {
    pub(crate) fn new(version: Option<String>) -> Result<Self> {
        let project_root = Metadata::load()?.workspace_root;
        Ok(Self { version, project_root })
    }

    pub(crate) fn run(self) -> Result<()> {
        let _p = pushd(&self.project_root)?;

        match self.version.as_deref() {
            Some("msrv") => self.build_msrv()?,
            Some("stable") => self.build_stable()?,
            Some("nightly") => self.build_nightly()?,
            Some(_) => return Err("Wrong Rust version specified.".into()),
            None => {
                self.build_msrv().and(self.build_stable()).and(self.build_nightly())?;
            }
        }

        Ok(())
    }

    fn build_msrv(&self) -> Result<()> {
        // Check all crates with all features except
        // * ruma (would pull in ruma-signatures)
        // * ruma-client (tested only with client-api feature due to optional HTTP client deps)
        // * ruma-signatures (MSRV exception)
        // * xtask (no real reason to enforce an MSRV for it)
        cmd!(
            "rustup run {MSRV} cargo check --workspace --all-features
                --exclude ruma
                --exclude ruma-client
                --exclude ruma-signatures
                --exclude xtask"
        )
        .run()?;

        // Check ruma-client crate with default features
        cmd!("rustup run {MSRV} cargo check -p ruma-client").run()?;

        // Check ruma crate with default features
        cmd!("rustup run {MSRV} cargo check -p ruma").run().map_err(Into::into)
    }

    fn build_stable(&self) -> Result<()> {
        // 1. Make sure everything compiles
        cmd!("rustup run stable cargo check --workspace --all-features").run()?;
        cmd!("rustup run stable cargo check -p ruma-client --no-default-features --features client-api").run()?;
        cmd!("rustup run stable cargo check -p ruma-identifiers --no-default-features").run()?;

        // 2. Run tests
        let workspace_res =
            cmd!("rustup run stable cargo test --workspace --features full,unstable-pre-spec")
                .run();
        let events_compat_res =
            cmd!("rustup run stable cargo test -p ruma-events --features compat compat").run();

        workspace_res.and(events_compat_res).map_err(Into::into)
    }

    fn build_nightly(&self) -> Result<()> {
        // Check formatting
        let fmt_res = cmd!("rustup run nightly cargo fmt -- --check").run();
        // Check `ruma` crate with `full` feature (sometimes things only compile with an unstable
        // flag)
        let check_full_res = cmd!("rustup run nightly cargo check -p ruma --features full").run();
        // Check everything with default features with clippy
        let clippy_default_res = cmd!(
            "
            rustup run nightly cargo clippy
                --workspace --all-targets --features=full -- -D warnings
            "
        )
        .run();
        // Check everything with almost all features with clippy
        let clippy_all_res = cmd!(
            "
            rustup run nightly cargo clippy
                --workspace --all-targets --features=full,compat,unstable-pre-spec -- -D warnings
            "
        )
        .run();
        // Check dependencies being sorted
        let sort_res = cmd!(
            "
            rustup run nightly cargo sort
                --workspace --grouped --check
                --order package,lib,features,dependencies,dev-dependencies,build-dependencies
            "
        )
        .run();
        // Check that all links point to the same version of the spec
        let spec_links = check_spec_links(&self.project_root.join("crates"));

        fmt_res
            .and(check_full_res)
            .and(clippy_default_res)
            .and(clippy_all_res)
            .and(sort_res)
            .map_err(Into::into)
            .and(spec_links)
    }
}
<|endoftext|>


<commit_before>use std::io::{self, Read, Write, Seek, SeekFrom};
use std::fs::File;
use std::fmt;
use std::path::Path;
use std::env;
use std;

use super::imp;

/// An unnamed temporary file.
///
/// This variant is secure/reliable in the presence of a pathological temporary file cleaner.
///
/// Deletion:
///
/// Linux >= 3.11: The temporary file is never linked into the filesystem so it can't be leaked.
///
/// Other *nix: The temporary file is immediately unlinked on create. The OS will delete it when
/// the last open copy of it is closed (the last TempFile reference to it is dropped).
///
/// Windows: The temporary file is marked DeleteOnClose and, again, will be deleted when the last
/// open copy of it is closed. Unlike *nix operating systems, the file is not immediately unlinked
/// from the filesystem.
pub struct TempFile(File);

impl fmt::Debug for TempFile {
    #[cfg(unix)]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use std::os::unix::io::AsRawFd;
        write!(f, "TempFile({})", self.0.as_raw_fd())
    }

    #[cfg(windows)]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use std::os::windows::io::AsRawHandle;
        write!(f, "TempFile({})", self.0.as_raw_handle() as usize)
    }
}

#[allow(len_without_is_empty)]
impl TempFile {
    /// Create a new temporary file.
    pub fn new() -> io::Result<TempFile> {
        Self::new_in(&env::temp_dir())
    }

    /// Create a new temporary file in the specified directory.
    pub fn new_in<P: AsRef<Path>>(dir: P) -> io::Result<TempFile> {
        imp::create(dir.as_ref()).map(TempFile)
    }

    /// Create a new temporary file and open it `count` times returning `count` independent
    /// references to the same file.
    ///
    /// This can be useful if you want multiple seek positions in the same temporary file.
    /// Additionally, this function guarantees that all of the returned temporary file objects
    /// refer to the same underlying temporary file even in the presence of a pathological
    /// temporary file cleaner.
    pub fn shared(count: usize) -> io::Result<Vec<TempFile>> {
        Self::shared_in(&env::temp_dir(), count)
    }

    /// Same as `shared` but creates the file in the specified directory.
    pub fn shared_in<P: AsRef<Path>>(dir: P, count: usize) -> io::Result<Vec<TempFile>> {
        imp::create_shared(dir.as_ref(), count).map(|files| {
            files.into_iter().map(TempFile).collect()
        })
    }

    /// Number of bytes in the file.
    pub fn len(&self) -> io::Result<u64> {
        self.0.metadata().map(|m| m.len())
    }

    /// Truncate the file to `size` bytes.
    pub fn set_len(&self, size: u64) -> io::Result<()> {
        self.0.set_len(size)
    }
}

impl Read for TempFile {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl Write for TempFile {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }
    #[inline]
    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}

impl Seek for TempFile {
    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
        self.0.seek(pos)
    }
}

#[cfg(unix)]
impl std::os::unix::io::AsRawFd for TempFile {
    #[inline]
    fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
        self.0.as_raw_fd()
    }
}

#[cfg(windows)]
impl std::os::windows::io::AsRawHandle for TempFile {
    #[inline]
    fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
        self.0.as_raw_handle()
    }
}
<commit_msg>Don't assume clippy<commit_after>use std::io::{self, Read, Write, Seek, SeekFrom};
use std::fs::File;
use std::fmt;
use std::path::Path;
use std::env;
use std;

use super::imp;

/// An unnamed temporary file.
///
/// This variant is secure/reliable in the presence of a pathological temporary file cleaner.
///
/// Deletion:
///
/// Linux >= 3.11: The temporary file is never linked into the filesystem so it can't be leaked.
///
/// Other *nix: The temporary file is immediately unlinked on create. The OS will delete it when
/// the last open copy of it is closed (the last TempFile reference to it is dropped).
///
/// Windows: The temporary file is marked DeleteOnClose and, again, will be deleted when the last
/// open copy of it is closed. Unlike *nix operating systems, the file is not immediately unlinked
/// from the filesystem.
pub struct TempFile(File);

impl fmt::Debug for TempFile {
    #[cfg(unix)]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use std::os::unix::io::AsRawFd;
        write!(f, "TempFile({})", self.0.as_raw_fd())
    }

    #[cfg(windows)]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use std::os::windows::io::AsRawHandle;
        write!(f, "TempFile({})", self.0.as_raw_handle() as usize)
    }
}

impl TempFile {
    /// Create a new temporary file.
    pub fn new() -> io::Result<TempFile> {
        Self::new_in(&env::temp_dir())
    }

    /// Create a new temporary file in the specified directory.
    pub fn new_in<P: AsRef<Path>>(dir: P) -> io::Result<TempFile> {
        imp::create(dir.as_ref()).map(TempFile)
    }

    /// Create a new temporary file and open it `count` times returning `count` independent
    /// references to the same file.
    ///
    /// This can be useful if you want multiple seek positions in the same temporary file.
    /// Additionally, this function guarantees that all of the returned temporary file objects
    /// refer to the same underlying temporary file even in the presence of a pathological
    /// temporary file cleaner.
    pub fn shared(count: usize) -> io::Result<Vec<TempFile>> {
        Self::shared_in(&env::temp_dir(), count)
    }

    /// Same as `shared` but creates the file in the specified directory.
    pub fn shared_in<P: AsRef<Path>>(dir: P, count: usize) -> io::Result<Vec<TempFile>> {
        imp::create_shared(dir.as_ref(), count).map(|files| {
            files.into_iter().map(TempFile).collect()
        })
    }

    /// Number of bytes in the file.
    pub fn len(&self) -> io::Result<u64> {
        self.0.metadata().map(|m| m.len())
    }

    /// Truncate the file to `size` bytes.
    pub fn set_len(&self, size: u64) -> io::Result<()> {
        self.0.set_len(size)
    }
}

impl Read for TempFile {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl Write for TempFile {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }
    #[inline]
    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}

impl Seek for TempFile {
    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
        self.0.seek(pos)
    }
}

#[cfg(unix)]
impl std::os::unix::io::AsRawFd for TempFile {
    #[inline]
    fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
        self.0.as_raw_fd()
    }
}

#[cfg(windows)]
impl std::os::windows::io::AsRawHandle for TempFile {
    #[inline]
    fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
        self.0.as_raw_handle()
    }
}
<|endoftext|>


<commit_before>#![crate_type = "rlib"]

//! Macro to create a "view box", a box containing immutable data plus
//! a "view" struct that can have interior references into the data.
//! The view box can be moved around as an atomic unit.  I haven't
//! convinced myself this is memory safe yet.  Known issues:
//!
//! * If the view type implements Drop (via #[unsafe_destructor]), it
//! could potentially observe dangling references depending on the
//! order in which struct fields are destroyed.  Currently, struct
//! fields seem to be destroyed in reverse order, which means things
//! should work fine as-is, but this depends on implementation details
//! of the compiler.  This could be fixed trivially by changing the
//! struct internals and adding a Drop impl that forces the view
//! to be destroyed first (e.g. by putting it in an Option and calling
//! .take()) at the cost of memory usage.
#![feature(macro_rules, globs)]

#[macro_export]
macro_rules! viewbox(
    (struct $name:ident<$d:ty, $v:ident>;) => (
        pub struct $name {
            data: Box<$d>,
            view: $v<'static>
        }

        #[allow(dead_code)]
        impl $name {
            pub fn new(data: $d, f: <'a>|&'a mut $d| -> $v<'a>) -> $name {
                let mut d = box data;
                let v = unsafe { ::std::mem::transmute(f(&mut *d)) };

                $name { data: d, view: v }
            }

            pub fn new_result<E>(data: $d,
                                 f: <'a>|&'a mut $d|
                                    -> ::std::result::Result<$v<'a>,E>)
                                 -> ::std::result::Result<$name,($d,E)> {
                let mut d = box data;
                match f(&mut *d).map(|v| unsafe { ::std::mem::transmute(v) }) {
                    Ok(v) => Ok($name { data: d, view: v }),
                    Err(e) => Err((*d,e))
                }
            }

            pub fn unwrap(self)-> $d {
                let $name { data: box data, .. } = self;
                data
            }

            pub fn view<'a>(&'a self) -> &'a $v<'a> {
                unsafe { ::std::mem::transmute(&self.view) }
            }

            pub fn mut_view<'a>(&'a mut self) -> &'a mut $v<'a> {
                unsafe { ::std::mem::transmute(&mut self.view) }
            }
        }
    );
    (#[deriving(PartialEq $(,$derive:ident)*)] struct $name:ident<$d:ty, $v:ident>;) => (
        impl ::std::cmp::PartialEq for $name {
            fn eq(&self, other: &$name) -> bool { self.view() == other.view(); }
        }
        viewbox!(struct $name<$d,$v>;)
    );
    (#[deriving(Show $(,$derive:ident)*)] struct $name:ident<$d:ty, $v:ident>;) => (
        impl ::std::fmt::Show for $name {
            fn fmt(&self, fmt: &mut ::std::fmt::Formatter)
                   -> ::std::result::Result<(), ::std::fmt::FormatError> {
                self.view().fmt(fmt)
            }
        }
        viewbox!(struct $name<$d,$v>;)
    );
    (#[deriving()] struct $name:ident<$d:ty, $v:ident>;) => (
        viewbox!(struct $name<$d,$v>;)
    );
)

#[cfg(test)]
mod test {
    // Test data structure
    #[deriving(PartialEq,Show)]
    struct TestData {
        foo: i32,
        bar: String
    }

    // View structure that has references into the boxed data
    struct TestView<'a> {
        x: &'a i32,
        y: &'a str
    }

    // Create TestBox which combines TestData and TestView
    viewbox! {
        struct TestBox<TestData,TestView>;
    }

    // Mutable view into TestData
    struct MutView<'a> {
        x: &'a mut i32,
        y: &'a mut String
    }

    // MutBox combines TestData and MutView
    viewbox! {
        struct MutBox<TestData, MutView>;
    }

    #[test]
    fn basic() {
        // Create some data
        let t = TestData { foo: 42, bar: "Hello".to_string() };
        // Move it into box, creating a view with interior references
        let v = TestBox::new(t, |d| TestView { x: &d.foo, y: d.bar.as_slice() });

        // We can access the boxed data via the view
        assert_eq!(v.view().x, &42i32);
        assert_eq!(v.view().y, "Hello");
        // We can unwrap the view to get the data back
        let t = v.unwrap();
        assert_eq!(t.foo, 42i32);
        assert_eq!(t.bar, "Hello".to_string());
    }

    #[test]
    fn mutation() {
        let t = TestData { foo: 42, bar: "Hello".to_string() };
        let mut v = MutBox::new(t, |d| MutView { x: &mut d.foo, y: &mut d.bar });

        *v.mut_view().x = 5;
        *v.mut_view().y = "Goodbye".to_string();
        let t = v.unwrap();
        assert_eq!(t, TestData { foo: 5, bar: "Goodbye".to_string() });
    }

    #[test]
    fn error() {
        let t = TestData { foo: 42, bar: "Hello".to_string() };
        let v = MutBox::new_result(t, |_| Err("Nope")).err().unwrap();
        assert_eq!(v, (TestData { foo: 42, bar: "Hello".to_string() }, "Nope"))
    }
}
<commit_msg>Fix incorrect comment<commit_after>#![crate_type = "rlib"]

//! Macro to create a "view box", a box containing data plus
//! a "view" struct that can have interior references into the data.
//! The view box can be moved around as an atomic unit.  I haven't
//! convinced myself this is memory safe yet.  Known issues:
//!
//! * If the view type implements Drop (via #[unsafe_destructor]), it
//! could potentially observe dangling references depending on the
//! order in which struct fields are destroyed.  Currently, struct
//! fields seem to be destroyed in reverse order, which means things
//! should work fine as-is, but this depends on implementation details
//! of the compiler.  This could be fixed trivially by changing the
//! struct internals and adding a Drop impl that forces the view
//! to be destroyed first (e.g. by putting it in an Option and calling
//! .take()) at the cost of memory usage.
#![feature(macro_rules, globs)]

#[macro_export]
macro_rules! viewbox(
    (struct $name:ident<$d:ty, $v:ident>;) => (
        pub struct $name {
            data: Box<$d>,
            view: $v<'static>
        }

        #[allow(dead_code)]
        impl $name {
            pub fn new(data: $d, f: <'a>|&'a mut $d| -> $v<'a>) -> $name {
                let mut d = box data;
                let v = unsafe { ::std::mem::transmute(f(&mut *d)) };

                $name { data: d, view: v }
            }

            pub fn new_result<E>(data: $d,
                                 f: <'a>|&'a mut $d|
                                    -> ::std::result::Result<$v<'a>,E>)
                                 -> ::std::result::Result<$name,($d,E)> {
                let mut d = box data;
                match f(&mut *d).map(|v| unsafe { ::std::mem::transmute(v) }) {
                    Ok(v) => Ok($name { data: d, view: v }),
                    Err(e) => Err((*d,e))
                }
            }

            pub fn unwrap(self)-> $d {
                let $name { data: box data, .. } = self;
                data
            }

            pub fn view<'a>(&'a self) -> &'a $v<'a> {
                unsafe { ::std::mem::transmute(&self.view) }
            }

            pub fn mut_view<'a>(&'a mut self) -> &'a mut $v<'a> {
                unsafe { ::std::mem::transmute(&mut self.view) }
            }
        }
    );
    (#[deriving(PartialEq $(,$derive:ident)*)] struct $name:ident<$d:ty, $v:ident>;) => (
        impl ::std::cmp::PartialEq for $name {
            fn eq(&self, other: &$name) -> bool { self.view() == other.view(); }
        }
        viewbox!(struct $name<$d,$v>;)
    );
    (#[deriving(Show $(,$derive:ident)*)] struct $name:ident<$d:ty, $v:ident>;) => (
        impl ::std::fmt::Show for $name {
            fn fmt(&self, fmt: &mut ::std::fmt::Formatter)
                   -> ::std::result::Result<(), ::std::fmt::FormatError> {
                self.view().fmt(fmt)
            }
        }
        viewbox!(struct $name<$d,$v>;)
    );
    (#[deriving()] struct $name:ident<$d:ty, $v:ident>;) => (
        viewbox!(struct $name<$d,$v>;)
    );
)

#[cfg(test)]
mod test {
    // Test data structure
    #[deriving(PartialEq,Show)]
    struct TestData {
        foo: i32,
        bar: String
    }

    // View structure that has references into the boxed data
    struct TestView<'a> {
        x: &'a i32,
        y: &'a str
    }

    // Create TestBox which combines TestData and TestView
    viewbox! {
        struct TestBox<TestData,TestView>;
    }

    // Mutable view into TestData
    struct MutView<'a> {
        x: &'a mut i32,
        y: &'a mut String
    }

    // MutBox combines TestData and MutView
    viewbox! {
        struct MutBox<TestData, MutView>;
    }

    #[test]
    fn basic() {
        // Create some data
        let t = TestData { foo: 42, bar: "Hello".to_string() };
        // Move it into box, creating a view with interior references
        let v = TestBox::new(t, |d| TestView { x: &d.foo, y: d.bar.as_slice() });

        // We can access the boxed data via the view
        assert_eq!(v.view().x, &42i32);
        assert_eq!(v.view().y, "Hello");
        // We can unwrap the view to get the data back
        let t = v.unwrap();
        assert_eq!(t.foo, 42i32);
        assert_eq!(t.bar, "Hello".to_string());
    }

    #[test]
    fn mutation() {
        let t = TestData { foo: 42, bar: "Hello".to_string() };
        let mut v = MutBox::new(t, |d| MutView { x: &mut d.foo, y: &mut d.bar });

        *v.mut_view().x = 5;
        *v.mut_view().y = "Goodbye".to_string();
        let t = v.unwrap();
        assert_eq!(t, TestData { foo: 5, bar: "Goodbye".to_string() });
    }

    #[test]
    fn error() {
        let t = TestData { foo: 42, bar: "Hello".to_string() };
        let v = MutBox::new_result(t, |_| Err("Nope")).err().unwrap();
        assert_eq!(v, (TestData { foo: 42, bar: "Hello".to_string() }, "Nope"))
    }
}
<|endoftext|>


<commit_before>use utils::bigint::{M256, U256, U512};
use utils::gas::Gas;
use utils::address::Address;
use utils::opcode::Opcode;
use vm::{VM, Machine, Memory, Stack, PC, ExecutionError, ExecutionResult, Storage};
use std::cmp::{min, max};

const G_ZERO: usize = 0;
const G_BASE: usize = 2;
const G_VERYLOW: usize = 3;
const G_LOW: usize = 5;
const G_MID: usize = 8;
const G_HIGH: usize = 10;
const G_EXTCODE_DEFAULT: usize = 20;
const G_EXTCODE_EIP150: usize = 700;
const G_BALANCE_DEFAULT: usize = 20;
const G_BALANCE_EIP150: usize = 400;
const G_SLOAD_DEFAULT: usize = 50;
const G_SLOAD_EIP150: usize = 200;
const G_JUMPDEST: usize = 1;
const G_SSET: usize = 20000;
const G_SRESET: usize = 5000;
const R_SCLEAR: usize = 15000;
const R_SUICIDE: usize = 24000;
const G_SUICIDE_DEFAULT: usize = 0;
const G_SUICIDE_EIP150: usize = 5000;
const G_CREATE: usize = 32000;
const G_CODEDEPOSITE: usize = 200;
const G_CALL_DEFAULT: usize = 40;
const G_CALL_EIP150: usize = 700;
const G_CALLVALUE: usize = 9000;
const G_CALLSTIPEND: usize = 2300;
const G_NEWACCOUNT: usize = 25000;
const G_EXP: usize = 10;
const G_EXPBYTE_DEFAULT: usize = 10;
const G_EXPBYTE_EIP160: usize = 50;
const G_MEMORY: usize = 3;
const G_TXCREATE: usize = 32000;
const G_TXDATAZERO: usize = 4;
const G_TXDATANONZERO: usize = 68;
const G_TRANSACTION: usize = 21000;
const G_LOG: usize = 375;
const G_LOGDATA: usize = 8;
const G_LOGTOPIC: usize = 375;
const G_SHA3: usize = 30;
const G_SHA3WORD: usize = 6;
const G_COPY: usize = 3;
const G_BLOCKHASH: usize = 20;

fn memory_cost(a: Gas) -> Gas {
    (Gas::from(G_MEMORY) * a + a * a / Gas::from(512u64)).into()
}

fn sstore_cost<M: Memory + Default,
               S: Storage + Default>(machine: &Machine<M, S>) -> ExecutionResult<Gas> {
    let index = machine.stack().peek(0)?;
    let value = machine.stack().peek(1)?;
    let address = machine.context.address;

    if value != M256::zero() && machine.account_state.storage(address)?.read(index)? == M256::zero() {
        Ok(G_SSET.into())
    } else {
        Ok(G_SRESET.into())
    }
}

fn call_cost<M: Memory + Default,
             S: Storage + Default>(machine: &Machine<M, S>, available_gas: Gas) -> ExecutionResult<Gas> {
    Ok(gascap_cost(machine, available_gas)? + extra_cost(machine)?)
}

fn callgas_cost<M: Memory + Default,
                S: Storage + Default>(machine: &Machine<M, S>, available_gas: Gas) -> ExecutionResult<Gas> {
    let val = machine.stack().peek(2)?;
    if val != M256::zero() {
        Ok(gascap_cost(machine, available_gas)? + G_CALLSTIPEND.into())
    } else {
        Ok(gascap_cost(machine, available_gas)?)
    }
}

fn gascap_cost<M: Memory + Default,
               S: Storage + Default>(machine: &Machine<M, S>, available_gas: Gas) -> ExecutionResult<Gas> {
    // let base2 = machine.stack().peek(0)?.into();

    // if available_gas >= extra_cost(machine)? {
    //     let base1 = available_gas - extra_cost(machine)?;
    //     Ok(min(base1 - base1 / Gas::from(64u64), base2))
    // } else {
    //     Ok(base2)
    // }
    Ok(Gas::zero())
}

fn extra_cost<M: Memory + Default,
              S: Storage + Default>(machine: &Machine<M, S>) -> ExecutionResult<Gas> {
    Ok(Gas::from(if machine.patch.eip150() { G_CALL_EIP150 } else { G_CALL_DEFAULT }) + xfer_cost(machine)? + new_cost(machine)?)
}

fn xfer_cost<M: Memory + Default,
             S: Storage + Default>(machine: &Machine<M, S>) -> ExecutionResult<Gas> {
    let val = machine.stack().peek(2)?;
    if val != M256::zero() {
        Ok(G_CALLVALUE.into())
    } else {
        Ok(Gas::zero())
    }
}

fn new_cost<M: Memory + Default,
            S: Storage + Default>(machine: &Machine<M, S>) -> ExecutionResult<Gas> {
    let address: Address = machine.stack().peek(1)?.into();
    if machine.account_state.balance(address)? == U256::zero() && machine.account_state.nonce(address)? == M256::zero() && machine.account_state.code(address)?.len() == 0 {
        Ok(G_NEWACCOUNT.into())
    } else {
        Ok(Gas::zero())
    }
}

fn suicide_cost<M: Memory + Default,
                S: Storage + Default>(machine: &Machine<M, S>) -> ExecutionResult<Gas> {
    let address: Address = machine.stack().peek(0)?.into();
    Ok(Gas::from(if machine.patch.eip150() { G_SUICIDE_EIP150 } else { G_SUICIDE_DEFAULT }) + if address == Address::default() {
        Gas::from(G_NEWACCOUNT)
    } else {
        Gas::zero()
    })
}

#[derive(Clone, Copy, Debug)]
pub struct CostAggregrator(Gas);

impl CostAggregrator {
    pub fn active_memory_len(&self) -> M256 {
        self.0.into()
    }
}

impl Default for CostAggregrator {
    fn default() -> CostAggregrator {
        CostAggregrator(Gas::zero())
    }
}

fn memory_expand(current: Gas, from: Gas, len: Gas) -> Gas {
    if len == Gas::zero() {
        return current;
    }

    let rem = (from + len) % Gas::from(32u64);
    let new = if rem == Gas::zero() {
        (from + len) / Gas::from(32u64)
    } else {
        (from + len) / Gas::from(32u64) + Gas::from(1u64)
    };
    max(current, new)
}

fn memory_gas_cost<M: Memory + Default,
                   S: Storage + Default>(opcode: Opcode, machine: &Machine<M, S>,
                                          aggregrator: CostAggregrator)
                               -> ExecutionResult<(Gas, CostAggregrator)> {
    let ref stack = machine.stack();
    let ref memory = machine.memory();

    let current = aggregrator.0;
    let next = match opcode {
        Opcode::SHA3 | Opcode::RETURN | Opcode::LOG(_) => {
            let from: U256 = stack.peek(0)?.into();
            let len: U256 = stack.peek(1)?.into();
            memory_expand(current, Gas::from(from), Gas::from(len))
        },
        Opcode::CODECOPY | Opcode::CALLDATACOPY => {
            let from: U256 = stack.peek(0)?.into();
            let len: U256 = stack.peek(2)?.into();
            memory_expand(current, Gas::from(from), Gas::from(len))
        },
        Opcode::EXTCODECOPY => {
            let from: U256 = stack.peek(1)?.into();
            let len: U256 = stack.peek(3)?.into();
            memory_expand(current, Gas::from(from), Gas::from(len))
        },
        Opcode::MLOAD | Opcode::MSTORE => {
            let from: U256 = stack.peek(0)?.into();
            memory_expand(current, Gas::from(from), Gas::from(32u64))
        },
        Opcode::MSTORE8 => {
            let from: U256 = stack.peek(0)?.into();
            memory_expand(current, Gas::from(from), Gas::from(1u64))
        },
        Opcode::CREATE => {
            let from: U256 = stack.peek(1)?.into();
            let len: U256 = stack.peek(2)?.into();
            memory_expand(current, Gas::from(from), Gas::from(len))
        },
        _ => {
            current
        }
    };
    Ok((memory_cost(next) - memory_cost(current), CostAggregrator(next)))
}

pub fn gas_cost<M: Memory + Default,
                S: Storage + Default>(opcode: Opcode, machine: &Machine<M, S>,
                                       aggregrator: CostAggregrator)
                            -> ExecutionResult<(Gas, CostAggregrator)> {
    let self_cost: Gas = match opcode {
        Opcode::CALL | Opcode::CALLCODE |
        Opcode::DELEGATECALL => call_cost(machine, machine.available_gas())?,
        Opcode::SUICIDE => suicide_cost(machine)?,
        Opcode::SSTORE => sstore_cost(machine)?,

        Opcode::SHA3 => {
            let len = machine.stack.peek(1)?;
            let wordd = Gas::from(len) / Gas::from(32u64);
            let wordr = Gas::from(len) % Gas::from(32u64);
            (Gas::from(G_SHA3) + Gas::from(G_SHA3WORD) * if wordr == Gas::zero() { wordd } else { wordd + Gas::from(1u64) }).into()
        },

        Opcode::LOG(v) => {
            let len = machine.stack.peek(1)?;
            (Gas::from(G_LOG) + Gas::from(G_LOGDATA) * Gas::from(len) + Gas::from(G_LOGTOPIC) * Gas::from(v)).into()
        },

        Opcode::EXTCODECOPY => {
            let len = machine.stack.peek(3)?;
            let wordd = Gas::from(len) / Gas::from(32u64);
            let wordr = Gas::from(len) % Gas::from(32u64);
            (Gas::from(if machine.patch.eip150() { G_EXTCODE_EIP150 } else { G_EXTCODE_DEFAULT }) + Gas::from(G_COPY) * if wordr == Gas::zero() { wordd } else { wordd + Gas::from(1u64) }).into()
        },

        Opcode::CALLDATACOPY | Opcode::CODECOPY => {
            let len = machine.stack.peek(2)?;
            let wordd = Gas::from(len) / Gas::from(32u64);
            let wordr = Gas::from(len) % Gas::from(32u64);
            (Gas::from(G_VERYLOW) + Gas::from(G_COPY) * if wordr == Gas::zero() { wordd } else { wordd + Gas::from(1u64) }).into()
        },

        Opcode::EXP => {
            if machine.stack.peek(1)? == M256::zero() {
                Gas::from(G_EXP)
            } else {
                Gas::from(G_EXP) + Gas::from(if machine.patch.eip160() { G_EXPBYTE_EIP160 } else { G_EXPBYTE_DEFAULT }) * (Gas::from(1u64) + Gas::from(machine.stack.peek(1)?.log2floor()) / Gas::from(8u64))
            }
        }

        Opcode::CREATE => G_CREATE.into(),
        Opcode::JUMPDEST => G_JUMPDEST.into(),
        Opcode::SLOAD => (if machine.patch.eip150() { G_SLOAD_EIP150 } else { G_SLOAD_DEFAULT }).into(),

        // W_zero
        Opcode::STOP | Opcode::RETURN
            => G_ZERO.into(),

        // W_base
        Opcode::ADDRESS | Opcode::ORIGIN | Opcode::CALLER |
        Opcode::CALLVALUE | Opcode::CALLDATASIZE |
        Opcode::CODESIZE | Opcode::GASPRICE | Opcode::COINBASE |
        Opcode::TIMESTAMP | Opcode::NUMBER | Opcode::DIFFICULTY |
        Opcode::GASLIMIT | Opcode::POP | Opcode::PC |
        Opcode::MSIZE | Opcode::GAS
            => G_BASE.into(),

        // W_verylow
        Opcode::ADD | Opcode::SUB | Opcode::NOT | Opcode::LT |
        Opcode::GT | Opcode::SLT | Opcode::SGT | Opcode::EQ |
        Opcode::ISZERO | Opcode::AND | Opcode::OR | Opcode::XOR |
        Opcode::BYTE | Opcode::CALLDATALOAD | Opcode::MLOAD |
        Opcode::MSTORE | Opcode::MSTORE8 | Opcode::PUSH(_) |
        Opcode::DUP(_) | Opcode::SWAP(_)
            => G_VERYLOW.into(),

        // W_low
        Opcode::MUL | Opcode::DIV | Opcode::SDIV | Opcode::MOD |
        Opcode::SMOD | Opcode::SIGNEXTEND
            => G_LOW.into(),

        // W_mid
        Opcode::ADDMOD | Opcode::MULMOD | Opcode::JUMP
            => G_MID.into(),

        // W_high
        Opcode::JUMPI => G_HIGH.into(),

        // W_extcode
        Opcode::EXTCODESIZE => (if machine.patch.eip150() { G_EXTCODE_EIP150 } else { G_EXTCODE_DEFAULT }).into(),

        Opcode::BALANCE => (if machine.patch.eip150() { G_BALANCE_EIP150 } else { G_BALANCE_DEFAULT }).into(),
        Opcode::BLOCKHASH => G_BLOCKHASH.into(),
        Opcode::INVALID => Gas::zero(),
    };
    let (memory_gas, agg) = memory_gas_cost(opcode, machine, aggregrator)?;
    Ok((self_cost + memory_gas, agg))
}

pub fn gas_refund<M: Memory + Default,
                  S: Storage + Default>(opcode: Opcode, machine: &Machine<M, S>) -> ExecutionResult<Gas> {
    match opcode {
        Opcode::SSTORE => {
            let index = machine.stack().peek(0)?;
            let value = machine.stack().peek(1)?;
            let address = machine.context.address;

            if value == M256::zero() && machine.account_state.storage(address)?.read(index)? != M256::zero() {
                Ok(Gas::from(R_SCLEAR))
            } else {
                Ok(Gas::zero())
            }
        },
        Opcode::SUICIDE => {
            // TODO: check whether I_a belongs to A_s
            Ok(Gas::zero())
        },
        _ => Ok(Gas::zero())
    }
}
<commit_msg>Problem: CALL memory cost is not handled Solution: add CALL case<commit_after>use utils::bigint::{M256, U256, U512};
use utils::gas::Gas;
use utils::address::Address;
use utils::opcode::Opcode;
use vm::{VM, Machine, Memory, Stack, PC, ExecutionError, ExecutionResult, Storage};
use std::cmp::{min, max};

const G_ZERO: usize = 0;
const G_BASE: usize = 2;
const G_VERYLOW: usize = 3;
const G_LOW: usize = 5;
const G_MID: usize = 8;
const G_HIGH: usize = 10;
const G_EXTCODE_DEFAULT: usize = 20;
const G_EXTCODE_EIP150: usize = 700;
const G_BALANCE_DEFAULT: usize = 20;
const G_BALANCE_EIP150: usize = 400;
const G_SLOAD_DEFAULT: usize = 50;
const G_SLOAD_EIP150: usize = 200;
const G_JUMPDEST: usize = 1;
const G_SSET: usize = 20000;
const G_SRESET: usize = 5000;
const R_SCLEAR: usize = 15000;
const R_SUICIDE: usize = 24000;
const G_SUICIDE_DEFAULT: usize = 0;
const G_SUICIDE_EIP150: usize = 5000;
const G_CREATE: usize = 32000;
const G_CODEDEPOSITE: usize = 200;
const G_CALL_DEFAULT: usize = 40;
const G_CALL_EIP150: usize = 700;
const G_CALLVALUE: usize = 9000;
const G_CALLSTIPEND: usize = 2300;
const G_NEWACCOUNT: usize = 25000;
const G_EXP: usize = 10;
const G_EXPBYTE_DEFAULT: usize = 10;
const G_EXPBYTE_EIP160: usize = 50;
const G_MEMORY: usize = 3;
const G_TXCREATE: usize = 32000;
const G_TXDATAZERO: usize = 4;
const G_TXDATANONZERO: usize = 68;
const G_TRANSACTION: usize = 21000;
const G_LOG: usize = 375;
const G_LOGDATA: usize = 8;
const G_LOGTOPIC: usize = 375;
const G_SHA3: usize = 30;
const G_SHA3WORD: usize = 6;
const G_COPY: usize = 3;
const G_BLOCKHASH: usize = 20;

fn memory_cost(a: Gas) -> Gas {
    (Gas::from(G_MEMORY) * a + a * a / Gas::from(512u64)).into()
}

fn sstore_cost<M: Memory + Default,
               S: Storage + Default>(machine: &Machine<M, S>) -> ExecutionResult<Gas> {
    let index = machine.stack().peek(0)?;
    let value = machine.stack().peek(1)?;
    let address = machine.context.address;

    if value != M256::zero() && machine.account_state.storage(address)?.read(index)? == M256::zero() {
        Ok(G_SSET.into())
    } else {
        Ok(G_SRESET.into())
    }
}

fn call_cost<M: Memory + Default,
             S: Storage + Default>(machine: &Machine<M, S>, available_gas: Gas) -> ExecutionResult<Gas> {
    Ok(gascap_cost(machine, available_gas)? + extra_cost(machine)?)
}

fn callgas_cost<M: Memory + Default,
                S: Storage + Default>(machine: &Machine<M, S>, available_gas: Gas) -> ExecutionResult<Gas> {
    let val = machine.stack().peek(2)?;
    if val != M256::zero() {
        Ok(gascap_cost(machine, available_gas)? + G_CALLSTIPEND.into())
    } else {
        Ok(gascap_cost(machine, available_gas)?)
    }
}

fn gascap_cost<M: Memory + Default,
               S: Storage + Default>(machine: &Machine<M, S>, available_gas: Gas) -> ExecutionResult<Gas> {
    // let base2 = machine.stack().peek(0)?.into();

    // if available_gas >= extra_cost(machine)? {
    //     let base1 = available_gas - extra_cost(machine)?;
    //     Ok(min(base1 - base1 / Gas::from(64u64), base2))
    // } else {
    //     Ok(base2)
    // }
    Ok(Gas::zero())
}

fn extra_cost<M: Memory + Default,
              S: Storage + Default>(machine: &Machine<M, S>) -> ExecutionResult<Gas> {
    Ok(Gas::from(if machine.patch.eip150() { G_CALL_EIP150 } else { G_CALL_DEFAULT }) + xfer_cost(machine)? + new_cost(machine)?)
}

fn xfer_cost<M: Memory + Default,
             S: Storage + Default>(machine: &Machine<M, S>) -> ExecutionResult<Gas> {
    let val = machine.stack().peek(2)?;
    if val != M256::zero() {
        Ok(G_CALLVALUE.into())
    } else {
        Ok(Gas::zero())
    }
}

fn new_cost<M: Memory + Default,
            S: Storage + Default>(machine: &Machine<M, S>) -> ExecutionResult<Gas> {
    let address: Address = machine.stack().peek(1)?.into();
    if machine.account_state.balance(address)? == U256::zero() && machine.account_state.nonce(address)? == M256::zero() && machine.account_state.code(address)?.len() == 0 {
        Ok(G_NEWACCOUNT.into())
    } else {
        Ok(Gas::zero())
    }
}

fn suicide_cost<M: Memory + Default,
                S: Storage + Default>(machine: &Machine<M, S>) -> ExecutionResult<Gas> {
    let address: Address = machine.stack().peek(0)?.into();
    Ok(Gas::from(if machine.patch.eip150() { G_SUICIDE_EIP150 } else { G_SUICIDE_DEFAULT }) + if address == Address::default() {
        Gas::from(G_NEWACCOUNT)
    } else {
        Gas::zero()
    })
}

#[derive(Clone, Copy, Debug)]
pub struct CostAggregrator(Gas);

impl CostAggregrator {
    pub fn active_memory_len(&self) -> M256 {
        self.0.into()
    }
}

impl Default for CostAggregrator {
    fn default() -> CostAggregrator {
        CostAggregrator(Gas::zero())
    }
}

fn memory_expand(current: Gas, from: Gas, len: Gas) -> Gas {
    if len == Gas::zero() {
        return current;
    }

    let rem = (from + len) % Gas::from(32u64);
    let new = if rem == Gas::zero() {
        (from + len) / Gas::from(32u64)
    } else {
        (from + len) / Gas::from(32u64) + Gas::from(1u64)
    };
    max(current, new)
}

fn memory_gas_cost<M: Memory + Default,
                   S: Storage + Default>(opcode: Opcode, machine: &Machine<M, S>,
                                          aggregrator: CostAggregrator)
                               -> ExecutionResult<(Gas, CostAggregrator)> {
    let ref stack = machine.stack();
    let ref memory = machine.memory();

    let current = aggregrator.0;
    let next = match opcode {
        Opcode::SHA3 | Opcode::RETURN | Opcode::LOG(_) => {
            let from: U256 = stack.peek(0)?.into();
            let len: U256 = stack.peek(1)?.into();
            memory_expand(current, Gas::from(from), Gas::from(len))
        },
        Opcode::CODECOPY | Opcode::CALLDATACOPY => {
            let from: U256 = stack.peek(0)?.into();
            let len: U256 = stack.peek(2)?.into();
            memory_expand(current, Gas::from(from), Gas::from(len))
        },
        Opcode::EXTCODECOPY => {
            let from: U256 = stack.peek(1)?.into();
            let len: U256 = stack.peek(3)?.into();
            memory_expand(current, Gas::from(from), Gas::from(len))
        },
        Opcode::MLOAD | Opcode::MSTORE => {
            let from: U256 = stack.peek(0)?.into();
            memory_expand(current, Gas::from(from), Gas::from(32u64))
        },
        Opcode::MSTORE8 => {
            let from: U256 = stack.peek(0)?.into();
            memory_expand(current, Gas::from(from), Gas::from(1u64))
        },
        Opcode::CREATE => {
            let from: U256 = stack.peek(1)?.into();
            let len: U256 = stack.peek(2)?.into();
            memory_expand(current, Gas::from(from), Gas::from(len))
        },
        Opcode::CALL => {
            let in_from: U256 = stack.peek(3)?.into();
            let in_len: U256 = stack.peek(4)?.into();
            let out_from: U256 = stack.peek(5)?.into();
            let out_len: U256 = stack.peek(6)?.into();
            memory_expand(memory_expand(current, Gas::from(in_from), Gas::from(in_len)),
                          Gas::from(out_from), Gas::from(out_len))
        },
        _ => {
            current
        }
    };
    Ok((memory_cost(next) - memory_cost(current), CostAggregrator(next)))
}

pub fn gas_cost<M: Memory + Default,
                S: Storage + Default>(opcode: Opcode, machine: &Machine<M, S>,
                                       aggregrator: CostAggregrator)
                            -> ExecutionResult<(Gas, CostAggregrator)> {
    let self_cost: Gas = match opcode {
        Opcode::CALL | Opcode::CALLCODE |
        Opcode::DELEGATECALL => call_cost(machine, machine.available_gas())?,
        Opcode::SUICIDE => suicide_cost(machine)?,
        Opcode::SSTORE => sstore_cost(machine)?,

        Opcode::SHA3 => {
            let len = machine.stack.peek(1)?;
            let wordd = Gas::from(len) / Gas::from(32u64);
            let wordr = Gas::from(len) % Gas::from(32u64);
            (Gas::from(G_SHA3) + Gas::from(G_SHA3WORD) * if wordr == Gas::zero() { wordd } else { wordd + Gas::from(1u64) }).into()
        },

        Opcode::LOG(v) => {
            let len = machine.stack.peek(1)?;
            (Gas::from(G_LOG) + Gas::from(G_LOGDATA) * Gas::from(len) + Gas::from(G_LOGTOPIC) * Gas::from(v)).into()
        },

        Opcode::EXTCODECOPY => {
            let len = machine.stack.peek(3)?;
            let wordd = Gas::from(len) / Gas::from(32u64);
            let wordr = Gas::from(len) % Gas::from(32u64);
            (Gas::from(if machine.patch.eip150() { G_EXTCODE_EIP150 } else { G_EXTCODE_DEFAULT }) + Gas::from(G_COPY) * if wordr == Gas::zero() { wordd } else { wordd + Gas::from(1u64) }).into()
        },

        Opcode::CALLDATACOPY | Opcode::CODECOPY => {
            let len = machine.stack.peek(2)?;
            let wordd = Gas::from(len) / Gas::from(32u64);
            let wordr = Gas::from(len) % Gas::from(32u64);
            (Gas::from(G_VERYLOW) + Gas::from(G_COPY) * if wordr == Gas::zero() { wordd } else { wordd + Gas::from(1u64) }).into()
        },

        Opcode::EXP => {
            if machine.stack.peek(1)? == M256::zero() {
                Gas::from(G_EXP)
            } else {
                Gas::from(G_EXP) + Gas::from(if machine.patch.eip160() { G_EXPBYTE_EIP160 } else { G_EXPBYTE_DEFAULT }) * (Gas::from(1u64) + Gas::from(machine.stack.peek(1)?.log2floor()) / Gas::from(8u64))
            }
        }

        Opcode::CREATE => G_CREATE.into(),
        Opcode::JUMPDEST => G_JUMPDEST.into(),
        Opcode::SLOAD => (if machine.patch.eip150() { G_SLOAD_EIP150 } else { G_SLOAD_DEFAULT }).into(),

        // W_zero
        Opcode::STOP | Opcode::RETURN
            => G_ZERO.into(),

        // W_base
        Opcode::ADDRESS | Opcode::ORIGIN | Opcode::CALLER |
        Opcode::CALLVALUE | Opcode::CALLDATASIZE |
        Opcode::CODESIZE | Opcode::GASPRICE | Opcode::COINBASE |
        Opcode::TIMESTAMP | Opcode::NUMBER | Opcode::DIFFICULTY |
        Opcode::GASLIMIT | Opcode::POP | Opcode::PC |
        Opcode::MSIZE | Opcode::GAS
            => G_BASE.into(),

        // W_verylow
        Opcode::ADD | Opcode::SUB | Opcode::NOT | Opcode::LT |
        Opcode::GT | Opcode::SLT | Opcode::SGT | Opcode::EQ |
        Opcode::ISZERO | Opcode::AND | Opcode::OR | Opcode::XOR |
        Opcode::BYTE | Opcode::CALLDATALOAD | Opcode::MLOAD |
        Opcode::MSTORE | Opcode::MSTORE8 | Opcode::PUSH(_) |
        Opcode::DUP(_) | Opcode::SWAP(_)
            => G_VERYLOW.into(),

        // W_low
        Opcode::MUL | Opcode::DIV | Opcode::SDIV | Opcode::MOD |
        Opcode::SMOD | Opcode::SIGNEXTEND
            => G_LOW.into(),

        // W_mid
        Opcode::ADDMOD | Opcode::MULMOD | Opcode::JUMP
            => G_MID.into(),

        // W_high
        Opcode::JUMPI => G_HIGH.into(),

        // W_extcode
        Opcode::EXTCODESIZE => (if machine.patch.eip150() { G_EXTCODE_EIP150 } else { G_EXTCODE_DEFAULT }).into(),

        Opcode::BALANCE => (if machine.patch.eip150() { G_BALANCE_EIP150 } else { G_BALANCE_DEFAULT }).into(),
        Opcode::BLOCKHASH => G_BLOCKHASH.into(),
        Opcode::INVALID => Gas::zero(),
    };
    let (memory_gas, agg) = memory_gas_cost(opcode, machine, aggregrator)?;
    Ok((self_cost + memory_gas, agg))
}

pub fn gas_refund<M: Memory + Default,
                  S: Storage + Default>(opcode: Opcode, machine: &Machine<M, S>) -> ExecutionResult<Gas> {
    match opcode {
        Opcode::SSTORE => {
            let index = machine.stack().peek(0)?;
            let value = machine.stack().peek(1)?;
            let address = machine.context.address;

            if value == M256::zero() && machine.account_state.storage(address)?.read(index)? != M256::zero() {
                Ok(Gas::from(R_SCLEAR))
            } else {
                Ok(Gas::zero())
            }
        },
        Opcode::SUICIDE => {
            // TODO: check whether I_a belongs to A_s
            Ok(Gas::zero())
        },
        _ => Ok(Gas::zero())
    }
}
<|endoftext|>