<commit_before>#[feature(globs)];
use basics::*;
use std::fmt;
use sim::Environment;

pub struct Message {
    from: ServerID,
    to: ServerID,
    body: MessageBody,
}

impl fmt::Default for Message {
    fn fmt(msg: &Message, f: &mut fmt::Formatter) {
        write!(f.buf, "{} to {}: {}",
               *msg.from,
               *msg.to,
               msg.body);
    }
}

pub enum MessageBody {
    RequestVoteRequest {
        term: Term,
        lastLogIndex: Index,
    },
    RequestVoteResponse {
        term: Term,
        granted: bool,
    },
    AppendEntriesRequest {
        term: Term,
        seqno: uint,
    },
    AppendEntriesResponse {
        term: Term,
        seqno: uint,
    },
}

impl fmt::Default for MessageBody {
    fn fmt(b: &MessageBody, f: &mut fmt::Formatter) {
        match *b {
            RequestVoteRequest{term, lastLogIndex} =>
                write!(f.buf, "RequestVoteRequest(term: {}, lastLogIndex: {})",
                       term, lastLogIndex),
            RequestVoteResponse{term, granted} =>
                write!(f.buf, "RequestVoteResponse(term: {}, granted: {})",
                       term, granted),
            AppendEntriesRequest{term, seqno} =>
                write!(f.buf, "AppendEntriesRequest(term: {}, seqno: {})",
                       term, seqno),
            AppendEntriesResponse{term, seqno} =>
                write!(f.buf, "AppendEntriesResponse(term: {}, seqno: {})",
                       term, seqno),
        }
    }
}

enum ServerState {
    Follower  { timer: Time },
    Candidate { timer: Time, votes: uint },
    Leader    { timer: Time, heartbeat_seqno: uint, acks: uint, start_time: Time},
}

impl fmt::Default for ServerState {
    fn fmt(state: &ServerState, f: &mut fmt::Formatter) {
        match *state {
            Follower{timer} => {
                write!(f.buf, "Follower(timer: {})",
                       timer)
            },
            Candidate{timer, votes} => {
                write!(f.buf, "Candidate(timer: {}, votes: {})",
                       timer, votes)
            },
            Leader{timer, heartbeat_seqno, acks, start_time} => {
                write!(f.buf, "Leader(timer: {}, seqno: {}, acks: {}, start_time: {})",
                       timer, heartbeat_seqno, acks, start_time)
            },
        }
    }
}

struct LogEntry {
    term: Term,
    // command: ~str,
}

struct Server {
    id: ServerID,
    peers: ~[ServerID],
    algorithm: ~str,
    term: Term,
    state: ServerState,
    vote: Option<ServerID>,
    lastLogIndex: Index,
}

impl Server {
    pub fn new(id : ServerID, peers: ~[ServerID], env: &Environment,
               algorithm: &str) -> Server {
        Server {
            id: id,
            peers: peers,
            algorithm: algorithm.into_owned(),
            term: Term(0),
            state: Follower { timer: env.make_time(150, 299) },
            lastLogIndex: Index(0),
            vote: None,
        }
    }
    pub fn tick(&mut self, env: &mut Environment) {
        let mut need_step_down = false;
        match self.state {
            Follower  {timer, _} |
            Candidate {timer, _} => {
                if timer <= env.clock {
                    self.term = Term(*self.term + 1);
                    self.vote = None;
                    self.state = Candidate {
                        timer: env.make_time(150, 299),
                        votes: 1,
                    };
                    env.multicast(self.id, self.peers, &RequestVoteRequest {
                        term: self.term,
                        lastLogIndex: self.lastLogIndex,
                    });
                }
                self.try_become_leader(env);
            },
            Leader {timer: ref mut timer, heartbeat_seqno: ref mut heartbeat_seqno, acks: ref mut acks, _} => {
                if *timer <= env.clock {
                    if (*acks > (self.peers.len() + 1) / 2) {
                        *heartbeat_seqno += 1;
                        *acks = 1;
                        *timer = env.make_time(75, 75);
                        env.multicast(self.id, self.peers, &AppendEntriesRequest {
                            term: self.term,
                            seqno: *heartbeat_seqno,
                        });
                    } else {
                        // TODO: this is probably too agressive, should only
                        // step down after election timeout
                        need_step_down = true;
                    }
                }
            },
        }
        if need_step_down {
            println!("Failed to maintain leadership: {}", *self);
            self.step_down(env, self.term)
        }
    }
    fn step_down(&mut self, env: &Environment, term: Term) {
        self.term = term;
        self.vote = None;
        self.state = Follower {
            timer: env.make_time(150, 299),
        };
    }

    fn try_become_leader(&mut self, env: &mut Environment) {
        match self.state {
            Follower {_} => {},
            Candidate {votes, _} => {
                if votes > (self.peers.len() + 1) / 2 {
                    self.state = Leader {
                        timer: env.make_time(75, 75),
                        heartbeat_seqno: 0,
                        acks: 1,
                        start_time: env.clock,
                    };
                    env.multicast(self.id, self.peers, &AppendEntriesRequest {
                        term: self.term,
                        seqno: 0,
                    });
                }
            },
            Leader {_} => {},
        }
    }

    pub fn stable_leader_start_time(&self) -> Option<Time> {
        match self.state {
            Leader {heartbeat_seqno, start_time, _} if heartbeat_seqno > 4 => Some(start_time),
            _ => None,
        }
    }

    pub fn handle(&mut self, env: &mut Environment, msg: &Message) {
        match msg.body {
            RequestVoteRequest {term, lastLogIndex} => {
                let reply = |granted| {
                    env.reply(msg, &RequestVoteResponse {
                        term: self.term,
                        granted: granted,
                    });
                };
                if term < self.term {
                    reply(false);
                } else {
                    if term > self.term {
                        self.step_down(env, term);
                    }
                    match self.vote {
                        None => {
                            if lastLogIndex >= self.lastLogIndex {
                                self.vote = Some(msg.from);
                                reply(true);
                            } else {
                                reply(false);
                            }
                        },
                        Some(c) => {
                            reply(c == msg.from);
                        },
                    }
                }
            },
            RequestVoteResponse {term, granted, _} => {
                if term == self.term && granted {
                    match self.state {
                        Follower {_} => {},
                        Candidate {votes: ref mut votes, _} => {
                            *votes += 1;
                        },
                        Leader {_} => {},
                    }
                    self.try_become_leader(env)
                } else if term > self.term {
                    self.step_down(env, term);
                }
            },
            AppendEntriesRequest {term, seqno} => {
                if term == self.term {
                    self.state = Follower {
                        timer: env.make_time(150, 299),
                    };
                } else if term > self.term {
                    self.step_down(env, term);
                }
                env.reply(msg, &AppendEntriesResponse {
                    term: self.term,
                    seqno: seqno,
                });
            },
            AppendEntriesResponse {term, seqno} => {
                if term == self.term {
                    match self.state {
                        Follower {_} | Candidate {_} => {},
                        Leader { heartbeat_seqno, acks: ref mut acks, _ } => {
                            if seqno == heartbeat_seqno {
                                *acks += 1;
                            }
                        },
                    }
                }
            },
        }
    }

} // Server impl

impl fmt::Default for Server {
    fn fmt(server: &Server, f: &mut fmt::Formatter) {
        write!(f.buf, "Server {}: {}", *server.id, server.state)
    }
}

<commit_msg>Differentiate vote granted responses<commit_after>#[feature(globs)];
use basics::*;
use std::fmt;
use sim::Environment;

pub struct Message {
    from: ServerID,
    to: ServerID,
    body: MessageBody,
}

impl fmt::Default for Message {
    fn fmt(msg: &Message, f: &mut fmt::Formatter) {
        write!(f.buf, "{} to {}: {}",
               *msg.from,
               *msg.to,
               msg.body);
    }
}

#[deriving(Eq)]
enum VoteGranted {
    GRANTED,
    TERM_STALE,
    LOG_STALE,
    VOTED,
}

impl fmt::Default for VoteGranted {
    fn fmt(g: &VoteGranted, f: &mut fmt::Formatter) {
        write!(f.buf, "{}",
               *g);
    }
}

pub enum MessageBody {
    RequestVoteRequest {
        term: Term,
        lastLogIndex: Index,
    },
    RequestVoteResponse {
        term: Term,
        granted: VoteGranted,
    },
    AppendEntriesRequest {
        term: Term,
        seqno: uint,
    },
    AppendEntriesResponse {
        term: Term,
        seqno: uint,
    },
}

impl fmt::Default for MessageBody {
    fn fmt(b: &MessageBody, f: &mut fmt::Formatter) {
        match *b {
            RequestVoteRequest{term, lastLogIndex} =>
                write!(f.buf, "RequestVoteRequest(term: {}, lastLogIndex: {})",
                       term, lastLogIndex),
            RequestVoteResponse{term, granted} =>
                write!(f.buf, "RequestVoteResponse(term: {}, granted: {})",
                       term, granted),
            AppendEntriesRequest{term, seqno} =>
                write!(f.buf, "AppendEntriesRequest(term: {}, seqno: {})",
                       term, seqno),
            AppendEntriesResponse{term, seqno} =>
                write!(f.buf, "AppendEntriesResponse(term: {}, seqno: {})",
                       term, seqno),
        }
    }
}

enum ServerState {
    Follower  { timer: Time },
    Candidate { timer: Time, votes: uint },
    Leader    { timer: Time, heartbeat_seqno: uint, acks: uint, start_time: Time},
}

impl fmt::Default for ServerState {
    fn fmt(state: &ServerState, f: &mut fmt::Formatter) {
        match *state {
            Follower{timer} => {
                write!(f.buf, "Follower(timer: {})",
                       timer)
            },
            Candidate{timer, votes} => {
                write!(f.buf, "Candidate(timer: {}, votes: {})",
                       timer, votes)
            },
            Leader{timer, heartbeat_seqno, acks, start_time} => {
                write!(f.buf, "Leader(timer: {}, seqno: {}, acks: {}, start_time: {})",
                       timer, heartbeat_seqno, acks, start_time)
            },
        }
    }
}

struct LogEntry {
    term: Term,
    // command: ~str,
}

struct Server {
    id: ServerID,
    peers: ~[ServerID],
    algorithm: ~str,
    term: Term,
    state: ServerState,
    vote: Option<ServerID>,
    lastLogIndex: Index,
}

impl Server {
    pub fn new(id : ServerID, peers: ~[ServerID], env: &Environment,
               algorithm: &str) -> Server {
        Server {
            id: id,
            peers: peers,
            algorithm: algorithm.into_owned(),
            term: Term(0),
            state: Follower { timer: env.make_time(150, 299) },
            lastLogIndex: Index(0),
            vote: None,
        }
    }
    pub fn tick(&mut self, env: &mut Environment) {
        let mut need_step_down = false;
        match self.state {
            Follower  {timer, _} |
            Candidate {timer, _} => {
                if timer <= env.clock {
                    self.term = Term(*self.term + 1);
                    self.vote = None;
                    self.state = Candidate {
                        timer: env.make_time(150, 299),
                        votes: 1,
                    };
                    env.multicast(self.id, self.peers, &RequestVoteRequest {
                        term: self.term,
                        lastLogIndex: self.lastLogIndex,
                    });
                }
                self.try_become_leader(env);
            },
            Leader {timer: ref mut timer, heartbeat_seqno: ref mut heartbeat_seqno, acks: ref mut acks, _} => {
                if *timer <= env.clock {
                    if (*acks > (self.peers.len() + 1) / 2) {
                        *heartbeat_seqno += 1;
                        *acks = 1;
                        *timer = env.make_time(75, 75);
                        env.multicast(self.id, self.peers, &AppendEntriesRequest {
                            term: self.term,
                            seqno: *heartbeat_seqno,
                        });
                    } else {
                        // TODO: this is probably too agressive, should only
                        // step down after election timeout
                        need_step_down = true;
                    }
                }
            },
        }
        if need_step_down {
            println!("Failed to maintain leadership: {}", *self);
            self.step_down(env, self.term)
        }
    }
    fn step_down(&mut self, env: &Environment, term: Term) {
        self.term = term;
        self.vote = None;
        self.state = Follower {
            timer: env.make_time(150, 299),
        };
    }

    fn try_become_leader(&mut self, env: &mut Environment) {
        match self.state {
            Follower {_} => {},
            Candidate {votes, _} => {
                if votes > (self.peers.len() + 1) / 2 {
                    self.state = Leader {
                        timer: env.make_time(75, 75),
                        heartbeat_seqno: 0,
                        acks: 1,
                        start_time: env.clock,
                    };
                    env.multicast(self.id, self.peers, &AppendEntriesRequest {
                        term: self.term,
                        seqno: 0,
                    });
                }
            },
            Leader {_} => {},
        }
    }

    pub fn stable_leader_start_time(&self) -> Option<Time> {
        match self.state {
            Leader {heartbeat_seqno, start_time, _} if heartbeat_seqno > 4 => Some(start_time),
            _ => None,
        }
    }

    pub fn handle(&mut self, env: &mut Environment, msg: &Message) {
        match msg.body {
            RequestVoteRequest {term, lastLogIndex} => {
                let reply = |granted| {
                    env.reply(msg, &RequestVoteResponse {
                        term: self.term,
                        granted: granted,
                    });
                };
                if term < self.term {
                    reply(TERM_STALE);
                } else {
                    if term > self.term {
                        self.step_down(env, term);
                    }
                    match self.vote {
                        None => {
                            if lastLogIndex >= self.lastLogIndex {
                                self.vote = Some(msg.from);
                                reply(GRANTED);
                            } else {
                                reply(LOG_STALE);
                            }
                        },
                        Some(c) => {
                            reply(if c == msg.from { GRANTED } else { VOTED });
                        },
                    }
                }
            },
            RequestVoteResponse {term, granted, _} => {
                if term == self.term && granted == GRANTED {
                    match self.state {
                        Follower {_} => {},
                        Candidate {votes: ref mut votes, _} => {
                            *votes += 1;
                        },
                        Leader {_} => {},
                    }
                    self.try_become_leader(env)
                } else if term > self.term {
                    self.step_down(env, term);
                }
            },
            AppendEntriesRequest {term, seqno} => {
                if term == self.term {
                    self.state = Follower {
                        timer: env.make_time(150, 299),
                    };
                } else if term > self.term {
                    self.step_down(env, term);
                }
                env.reply(msg, &AppendEntriesResponse {
                    term: self.term,
                    seqno: seqno,
                });
            },
            AppendEntriesResponse {term, seqno} => {
                if term == self.term {
                    match self.state {
                        Follower {_} | Candidate {_} => {},
                        Leader { heartbeat_seqno, acks: ref mut acks, _ } => {
                            if seqno == heartbeat_seqno {
                                *acks += 1;
                            }
                        },
                    }
                }
            },
        }
    }

} // Server impl

impl fmt::Default for Server {
    fn fmt(server: &Server, f: &mut fmt::Formatter) {
        write!(f.buf, "Server {}: {}", *server.id, server.state)
    }
}

<|endoftext|>


<commit_before>use alloc::arc::{Arc, Weak};
use collections::VecDeque;
use spin::Mutex;

use arch::externs::memset;
use arch::memory::Frame;
use arch::paging::{ActivePageTable, InactivePageTable, Page, PageIter, PhysicalAddress, VirtualAddress};
use arch::paging::entry::{self, EntryFlags};
use arch::paging::temporary_page::TemporaryPage;

#[derive(Debug)]
pub struct Grant {
    start: VirtualAddress,
    size: usize,
    flags: EntryFlags
}

impl Grant {
    pub fn physmap(from: PhysicalAddress, to: VirtualAddress, size: usize, flags: EntryFlags) -> Grant {
        let mut active_table = unsafe { ActivePageTable::new() };

        let mut flush_all = false;

        let start_page = Page::containing_address(to);
        let end_page = Page::containing_address(VirtualAddress::new(to.get() + size - 1));
        for page in Page::range_inclusive(start_page, end_page) {
            let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().get() - to.get() + from.get()));
            active_table.map_to(page, frame, flags);
            flush_all = true;
        }

        if flush_all {
            active_table.flush_all();
        }

        Grant {
            start: to,
            size: size,
            flags: flags
        }
    }

    pub fn map_inactive(from: VirtualAddress, to: VirtualAddress, size: usize, flags: EntryFlags, new_table: &mut InactivePageTable, temporary_page: &mut TemporaryPage) -> Grant {
        let mut active_table = unsafe { ActivePageTable::new() };

        let mut frames = VecDeque::new();

        let start_page = Page::containing_address(from);
        let end_page = Page::containing_address(VirtualAddress::new(from.get() + size - 1));
        for page in Page::range_inclusive(start_page, end_page) {
            let frame = active_table.translate_page(page).expect("grant references unmapped memory");
            frames.push_back(frame);
        }

        active_table.with(new_table, temporary_page, |mapper| {
            let start_page = Page::containing_address(to);
            let end_page = Page::containing_address(VirtualAddress::new(to.get() + size - 1));
            for page in Page::range_inclusive(start_page, end_page) {
                let frame = frames.pop_front().expect("grant did not find enough frames");
                mapper.map_to(page, frame, flags);
            }
        });

        Grant {
            start: to,
            size: size,
            flags: flags
        }
    }

    pub fn start_address(&self) -> VirtualAddress {
        self.start
    }

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

    pub fn flags(&self) -> EntryFlags {
        self.flags
    }

    pub fn unmap(self) {
        let mut active_table = unsafe { ActivePageTable::new() };

        let mut flush_all = false;

        let start_page = Page::containing_address(self.start);
        let end_page = Page::containing_address(VirtualAddress::new(self.start.get() + self.size - 1));
        for page in Page::range_inclusive(start_page, end_page) {
            active_table.unmap_return(page);
            flush_all = true;
        }

        if flush_all {
            active_table.flush_all();
        }
    }

    pub fn unmap_inactive(self, new_table: &mut InactivePageTable, temporary_page: &mut TemporaryPage) {
        let mut active_table = unsafe { ActivePageTable::new() };

        active_table.with(new_table, temporary_page, |mapper| {
            let start_page = Page::containing_address(self.start);
            let end_page = Page::containing_address(VirtualAddress::new(self.start.get() + self.size - 1));
            for page in Page::range_inclusive(start_page, end_page) {
                mapper.unmap_return(page);
            }
        });
    }
}

#[derive(Clone, Debug)]
pub enum SharedMemory {
    Owned(Arc<Mutex<Memory>>),
    Borrowed(Weak<Mutex<Memory>>)
}

impl SharedMemory {
    pub fn with<F, T>(&self, f: F) -> T where F: FnOnce(&mut Memory) -> T {
        match *self {
            SharedMemory::Owned(ref memory_lock) => {
                let mut memory = memory_lock.lock();
                f(&mut *memory)
            },
            SharedMemory::Borrowed(ref memory_weak) => {
                let memory_lock = memory_weak.upgrade().expect("SharedMemory::Borrowed no longer valid");
                let mut memory = memory_lock.lock();
                f(&mut *memory)
            }
        }
    }

    pub fn borrow(&self) -> SharedMemory {
        match *self {
            SharedMemory::Owned(ref memory_lock) => SharedMemory::Borrowed(Arc::downgrade(memory_lock)),
            SharedMemory::Borrowed(ref memory_lock) => SharedMemory::Borrowed(memory_lock.clone())
        }
    }
}

#[derive(Debug)]
pub struct Memory {
    start: VirtualAddress,
    size: usize,
    flags: EntryFlags
}

impl Memory {
    pub fn new(start: VirtualAddress, size: usize, flags: EntryFlags, flush: bool, clear: bool) -> Self {
        let mut memory = Memory {
            start: start,
            size: size,
            flags: flags
        };

        memory.map(flush, clear);

        memory
    }

    pub fn to_shared(self) -> SharedMemory {
        SharedMemory::Owned(Arc::new(Mutex::new(self)))
    }

    pub fn start_address(&self) -> VirtualAddress {
        self.start
    }

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

    pub fn flags(&self) -> EntryFlags {
        self.flags
    }

    pub fn pages(&self) -> PageIter {
        let start_page = Page::containing_address(self.start);
        let end_page = Page::containing_address(VirtualAddress::new(self.start.get() + self.size - 1));
        Page::range_inclusive(start_page, end_page)
    }

    fn map(&mut self, flush: bool, clear: bool) {
        let mut active_table = unsafe { ActivePageTable::new() };

        let mut flush_all = false;

        //TODO: Clear pages?
        for page in self.pages() {
            active_table.map(page, self.flags);

            if flush {
                //active_table.flush(page);
                flush_all = true;
            }
        }

        if flush_all {
            active_table.flush_all();
        }

        if clear {
            assert!(flush && self.flags.contains(entry::WRITABLE));
            unsafe { memset(self.start_address().get() as *mut u8, 0, self.size); }
        }
    }

    fn unmap(&mut self, flush: bool) {
        let mut active_table = unsafe { ActivePageTable::new() };

        let mut flush_all = false;

        for page in self.pages() {
            active_table.unmap(page);

            if flush {
                //active_table.flush(page);
                flush_all = true;
            }
        }

        if flush_all {
            active_table.flush_all();
        }
    }

    /// A complicated operation to move a piece of memory to a new page table
    /// It also allows for changing the address at the same time
    pub fn move_to(&mut self, new_start: VirtualAddress, new_table: &mut InactivePageTable, temporary_page: &mut TemporaryPage, flush: bool) {
        let mut active_table = unsafe { ActivePageTable::new() };

        let mut flush_all = false;

        for page in self.pages() {
            let frame = active_table.unmap_return(page);

            active_table.with(new_table, temporary_page, |mapper| {
                let new_page = Page::containing_address(VirtualAddress::new(page.start_address().get() - self.start.get() + new_start.get()));
                mapper.map_to(new_page, frame, self.flags);
            });

            if flush {
                //active_table.flush(page);
                flush_all = true;
            }
        }

        if flush_all {
            active_table.flush_all();
        }

        self.start = new_start;
    }

    pub fn remap(&mut self, new_flags: EntryFlags, flush: bool) {
        let mut active_table = unsafe { ActivePageTable::new() };

        let mut flush_all = false;

        for page in self.pages() {
            active_table.remap(page, new_flags);

            if flush {
                //active_table.flush(page);
                flush_all = true;
            }
        }

        if flush_all {
            active_table.flush_all();
        }

        self.flags = new_flags;
    }

    pub fn resize(&mut self, new_size: usize, flush: bool, clear: bool) {
        let mut active_table = unsafe { ActivePageTable::new() };

        //TODO: Calculate page changes to minimize operations
        if new_size > self.size {
            let mut flush_all = false;

            let start_page = Page::containing_address(VirtualAddress::new(self.start.get() + self.size));
            let end_page = Page::containing_address(VirtualAddress::new(self.start.get() + new_size - 1));
            for page in Page::range_inclusive(start_page, end_page) {
                if active_table.translate_page(page).is_none() {
                    active_table.map(page, self.flags);

                    if flush {
                        //active_table.flush(page);
                        flush_all = true;
                    }
                }
            }

            if flush_all {
                active_table.flush_all();
            }

            if clear {
                assert!(flush);
                unsafe { memset((self.start.get() + self.size) as *mut u8, 0, new_size - self.size); }
            }
        } else if new_size < self.size {
            let mut flush_all = false;

            let start_page = Page::containing_address(VirtualAddress::new(self.start.get() + new_size));
            let end_page = Page::containing_address(VirtualAddress::new(self.start.get() + self.size - 1));
            for page in Page::range_inclusive(start_page, end_page) {
                if active_table.translate_page(page).is_some() {
                    active_table.unmap(page);

                    if flush {
                        //active_table.flush(page);
                        flush_all = true;
                    }
                }
            }

            if flush_all {
                active_table.flush_all();
            }
        }

        self.size = new_size;
    }
}

impl Drop for Memory {
    fn drop(&mut self) {
        self.unmap(true);
    }
}

#[derive(Debug)]
pub struct Tls {
    pub master: VirtualAddress,
    pub file_size: usize,
    pub mem: Memory
}
<commit_msg>Use core intrinsics instead of memset<commit_after>use alloc::arc::{Arc, Weak};
use collections::VecDeque;
use core::intrinsics;
use spin::Mutex;

use arch::memory::Frame;
use arch::paging::{ActivePageTable, InactivePageTable, Page, PageIter, PhysicalAddress, VirtualAddress};
use arch::paging::entry::{self, EntryFlags};
use arch::paging::temporary_page::TemporaryPage;

#[derive(Debug)]
pub struct Grant {
    start: VirtualAddress,
    size: usize,
    flags: EntryFlags
}

impl Grant {
    pub fn physmap(from: PhysicalAddress, to: VirtualAddress, size: usize, flags: EntryFlags) -> Grant {
        let mut active_table = unsafe { ActivePageTable::new() };

        let mut flush_all = false;

        let start_page = Page::containing_address(to);
        let end_page = Page::containing_address(VirtualAddress::new(to.get() + size - 1));
        for page in Page::range_inclusive(start_page, end_page) {
            let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().get() - to.get() + from.get()));
            active_table.map_to(page, frame, flags);
            flush_all = true;
        }

        if flush_all {
            active_table.flush_all();
        }

        Grant {
            start: to,
            size: size,
            flags: flags
        }
    }

    pub fn map_inactive(from: VirtualAddress, to: VirtualAddress, size: usize, flags: EntryFlags, new_table: &mut InactivePageTable, temporary_page: &mut TemporaryPage) -> Grant {
        let mut active_table = unsafe { ActivePageTable::new() };

        let mut frames = VecDeque::new();

        let start_page = Page::containing_address(from);
        let end_page = Page::containing_address(VirtualAddress::new(from.get() + size - 1));
        for page in Page::range_inclusive(start_page, end_page) {
            let frame = active_table.translate_page(page).expect("grant references unmapped memory");
            frames.push_back(frame);
        }

        active_table.with(new_table, temporary_page, |mapper| {
            let start_page = Page::containing_address(to);
            let end_page = Page::containing_address(VirtualAddress::new(to.get() + size - 1));
            for page in Page::range_inclusive(start_page, end_page) {
                let frame = frames.pop_front().expect("grant did not find enough frames");
                mapper.map_to(page, frame, flags);
            }
        });

        Grant {
            start: to,
            size: size,
            flags: flags
        }
    }

    pub fn start_address(&self) -> VirtualAddress {
        self.start
    }

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

    pub fn flags(&self) -> EntryFlags {
        self.flags
    }

    pub fn unmap(self) {
        let mut active_table = unsafe { ActivePageTable::new() };

        let mut flush_all = false;

        let start_page = Page::containing_address(self.start);
        let end_page = Page::containing_address(VirtualAddress::new(self.start.get() + self.size - 1));
        for page in Page::range_inclusive(start_page, end_page) {
            active_table.unmap_return(page);
            flush_all = true;
        }

        if flush_all {
            active_table.flush_all();
        }
    }

    pub fn unmap_inactive(self, new_table: &mut InactivePageTable, temporary_page: &mut TemporaryPage) {
        let mut active_table = unsafe { ActivePageTable::new() };

        active_table.with(new_table, temporary_page, |mapper| {
            let start_page = Page::containing_address(self.start);
            let end_page = Page::containing_address(VirtualAddress::new(self.start.get() + self.size - 1));
            for page in Page::range_inclusive(start_page, end_page) {
                mapper.unmap_return(page);
            }
        });
    }
}

#[derive(Clone, Debug)]
pub enum SharedMemory {
    Owned(Arc<Mutex<Memory>>),
    Borrowed(Weak<Mutex<Memory>>)
}

impl SharedMemory {
    pub fn with<F, T>(&self, f: F) -> T where F: FnOnce(&mut Memory) -> T {
        match *self {
            SharedMemory::Owned(ref memory_lock) => {
                let mut memory = memory_lock.lock();
                f(&mut *memory)
            },
            SharedMemory::Borrowed(ref memory_weak) => {
                let memory_lock = memory_weak.upgrade().expect("SharedMemory::Borrowed no longer valid");
                let mut memory = memory_lock.lock();
                f(&mut *memory)
            }
        }
    }

    pub fn borrow(&self) -> SharedMemory {
        match *self {
            SharedMemory::Owned(ref memory_lock) => SharedMemory::Borrowed(Arc::downgrade(memory_lock)),
            SharedMemory::Borrowed(ref memory_lock) => SharedMemory::Borrowed(memory_lock.clone())
        }
    }
}

#[derive(Debug)]
pub struct Memory {
    start: VirtualAddress,
    size: usize,
    flags: EntryFlags
}

impl Memory {
    pub fn new(start: VirtualAddress, size: usize, flags: EntryFlags, flush: bool, clear: bool) -> Self {
        let mut memory = Memory {
            start: start,
            size: size,
            flags: flags
        };

        memory.map(flush, clear);

        memory
    }

    pub fn to_shared(self) -> SharedMemory {
        SharedMemory::Owned(Arc::new(Mutex::new(self)))
    }

    pub fn start_address(&self) -> VirtualAddress {
        self.start
    }

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

    pub fn flags(&self) -> EntryFlags {
        self.flags
    }

    pub fn pages(&self) -> PageIter {
        let start_page = Page::containing_address(self.start);
        let end_page = Page::containing_address(VirtualAddress::new(self.start.get() + self.size - 1));
        Page::range_inclusive(start_page, end_page)
    }

    fn map(&mut self, flush: bool, clear: bool) {
        let mut active_table = unsafe { ActivePageTable::new() };

        let mut flush_all = false;

        //TODO: Clear pages?
        for page in self.pages() {
            active_table.map(page, self.flags);

            if flush {
                //active_table.flush(page);
                flush_all = true;
            }
        }

        if flush_all {
            active_table.flush_all();
        }

        if clear {
            assert!(flush && self.flags.contains(entry::WRITABLE));
            unsafe {
                intrinsics::write_bytes(self.start_address().get() as *mut u8, 0, self.size);
            }
        }
    }

    fn unmap(&mut self, flush: bool) {
        let mut active_table = unsafe { ActivePageTable::new() };

        let mut flush_all = false;

        for page in self.pages() {
            active_table.unmap(page);

            if flush {
                //active_table.flush(page);
                flush_all = true;
            }
        }

        if flush_all {
            active_table.flush_all();
        }
    }

    /// A complicated operation to move a piece of memory to a new page table
    /// It also allows for changing the address at the same time
    pub fn move_to(&mut self, new_start: VirtualAddress, new_table: &mut InactivePageTable, temporary_page: &mut TemporaryPage, flush: bool) {
        let mut active_table = unsafe { ActivePageTable::new() };

        let mut flush_all = false;

        for page in self.pages() {
            let frame = active_table.unmap_return(page);

            active_table.with(new_table, temporary_page, |mapper| {
                let new_page = Page::containing_address(VirtualAddress::new(page.start_address().get() - self.start.get() + new_start.get()));
                mapper.map_to(new_page, frame, self.flags);
            });

            if flush {
                //active_table.flush(page);
                flush_all = true;
            }
        }

        if flush_all {
            active_table.flush_all();
        }

        self.start = new_start;
    }

    pub fn remap(&mut self, new_flags: EntryFlags, flush: bool) {
        let mut active_table = unsafe { ActivePageTable::new() };

        let mut flush_all = false;

        for page in self.pages() {
            active_table.remap(page, new_flags);

            if flush {
                //active_table.flush(page);
                flush_all = true;
            }
        }

        if flush_all {
            active_table.flush_all();
        }

        self.flags = new_flags;
    }

    pub fn resize(&mut self, new_size: usize, flush: bool, clear: bool) {
        let mut active_table = unsafe { ActivePageTable::new() };

        //TODO: Calculate page changes to minimize operations
        if new_size > self.size {
            let mut flush_all = false;

            let start_page = Page::containing_address(VirtualAddress::new(self.start.get() + self.size));
            let end_page = Page::containing_address(VirtualAddress::new(self.start.get() + new_size - 1));
            for page in Page::range_inclusive(start_page, end_page) {
                if active_table.translate_page(page).is_none() {
                    active_table.map(page, self.flags);

                    if flush {
                        //active_table.flush(page);
                        flush_all = true;
                    }
                }
            }

            if flush_all {
                active_table.flush_all();
            }

            if clear {
                assert!(flush);
                unsafe {
                    intrinsics::write_bytes((self.start.get() + self.size) as *mut u8, 0, new_size - self.size);
                }
            }
        } else if new_size < self.size {
            let mut flush_all = false;

            let start_page = Page::containing_address(VirtualAddress::new(self.start.get() + new_size));
            let end_page = Page::containing_address(VirtualAddress::new(self.start.get() + self.size - 1));
            for page in Page::range_inclusive(start_page, end_page) {
                if active_table.translate_page(page).is_some() {
                    active_table.unmap(page);

                    if flush {
                        //active_table.flush(page);
                        flush_all = true;
                    }
                }
            }

            if flush_all {
                active_table.flush_all();
            }
        }

        self.size = new_size;
    }
}

impl Drop for Memory {
    fn drop(&mut self) {
        self.unmap(true);
    }
}

#[derive(Debug)]
pub struct Tls {
    pub master: VirtualAddress,
    pub file_size: usize,
    pub mem: Memory
}
<|endoftext|>


<commit_before>// Re-exports
pub use kernels::{KernelSelector, KernelSpace, TaskResult};

mod executable;
pub use executable::*;

mod compile;
pub use compile::*;

mod execute;
pub use execute::*;

mod messages;
pub use messages::*;

mod utils;

#[cfg(test)]
mod tests {
    use std::sync::Arc;

    use super::*;
    use codec::CodecTrait;
    use codec_md::MdCodec;
    use eyre::Result;
    use graph::{PlanOptions, PlanOrdering};

    use kernels::{Kernel, KernelType};
    use node_address::Slot;
    use node_patch::{Operation};
    use test_snaps::{
        fixtures,
        insta::{self, assert_json_snapshot},
        snapshot_set_suffix,
    };
    use tokio::sync::{mpsc, RwLock};

    /// Higher level tests of the top level functions in this crate
    #[tokio::test]
    async fn md_articles() -> Result<()> {
        let fixtures = fixtures();

        // So that test results are not dependant upon the the machine the test is run on or how
        // the test is compiled only use built-in kernels
        let kernels: Vec<Kernel> = kernels::available()
            .await?
            .into_iter()
            .filter(|kernel| matches!(kernel.r#type, KernelType::Builtin))
            .collect();

        let mut settings = insta::Settings::clone_current();
        settings.set_prepend_module_to_snapshot(false);
        settings.bind_to_thread();

        for name in ["code.md", "code-relations.md"] {
            let path = fixtures.join("articles").join(name);

            // Load the article
            let root = Arc::new(RwLock::new(MdCodec::from_path(&path, None).await?));

            // Strip the fixtures path so it does not differ between machines
            let path = path.strip_prefix(&fixtures)?;
            let project = path.parent().unwrap();

            // Compile the article and snapshot the result
            let (patch_sender, mut patch_receiver) = mpsc::unbounded_channel::<PatchMessage>();
            tokio::spawn(async move {
                while let Some(_patch) = patch_receiver.recv().await {
                    // Ignore for this test
                }
            });
            let (addresses, graph) = compile(path, project, &root, &patch_sender).await?;
            snapshot_set_suffix(&[name, "-compile"].concat(), || {
                assert_json_snapshot!((&addresses, &graph))
            });

            // Generate various execution plans for the article using alternative options
            // and snapshot them all. Always specify `max_concurrency` to avoid differences
            // due to machine (number of CPUs)
            for (suffix, options) in [
                (
                    "appearance",
                    PlanOptions {
                        ordering: PlanOrdering::Appearance,
                        max_concurrency: 10,
                    },
                ),
                (
                    "appearance-concurrency-0",
                    PlanOptions {
                        ordering: PlanOrdering::Appearance,
                        max_concurrency: 0,
                    },
                ),
                (
                    "topological",
                    PlanOptions {
                        ordering: PlanOrdering::Topological,
                        max_concurrency: 10,
                    },
                ),
                (
                    "topological-concurrency-0",
                    PlanOptions {
                        ordering: PlanOrdering::Topological,
                        max_concurrency: 0,
                    },
                ),
            ] {
                let plan = graph
                    .plan(None, Some(kernels.clone()), Some(options))
                    .await?;
                snapshot_set_suffix(&[name, "-", suffix].concat(), || {
                    assert_json_snapshot!(&plan)
                });
            }

            // Execute the article (with topological execution plan) and snapshot the resultant patches
            let plan = graph
                .plan(
                    None,
                    Some(kernels.clone()),
                    Some(PlanOptions {
                        ordering: PlanOrdering::Topological,
                        max_concurrency: 10,
                    }),
                )
                .await?;

            let (patch_sender, mut patch_receiver) = mpsc::unbounded_channel::<PatchMessage>();
            let patches = tokio::spawn(async move {
                let mut patches = Vec::new();
                while let Some(PatchMessage { mut patch, .. }) = patch_receiver.recv().await {
                    // Redact execute time and duration from patch because they will
                    // change across test runs
                    for op in patch.ops.iter_mut() {
                        if let Operation::Add { address, value, .. } = op {
                            if let Some(Slot::Name(name)) = address.back() {
                                if name == "execute_ended" || name == "execute_duration" {
                                    *value = Box::new("<redacted>".to_string());
                                }
                            }
                        }
                    }
                    patches.push(patch);
                }
                patches
            });

            execute(
                &plan,
                &root,
                &Arc::new(RwLock::new(addresses)),
                &Arc::new(RwLock::new(graph)),
                &patch_sender,
                None,
            )
            .await?;

            drop(patch_sender);

            let _patches = patches.await?;
            /*
            Snapshotting of patches turned off for now because order is not-deterministic

            snapshot_set_suffix(&[name, "-patches"].concat(), || {
                assert_json_snapshot!(&patches);
            });
            */
        }

        Ok(())
    }
}
<commit_msg>chore(Node execution): Formatting<commit_after>// Re-exports
pub use kernels::{KernelSelector, KernelSpace, TaskResult};

mod executable;
pub use executable::*;

mod compile;
pub use compile::*;

mod execute;
pub use execute::*;

mod messages;
pub use messages::*;

mod utils;

#[cfg(test)]
mod tests {
    use std::sync::Arc;

    use super::*;
    use codec::CodecTrait;
    use codec_md::MdCodec;
    use eyre::Result;
    use graph::{PlanOptions, PlanOrdering};

    use kernels::{Kernel, KernelType};
    use node_address::Slot;
    use node_patch::Operation;
    use test_snaps::{
        fixtures,
        insta::{self, assert_json_snapshot},
        snapshot_set_suffix,
    };
    use tokio::sync::{mpsc, RwLock};

    /// Higher level tests of the top level functions in this crate
    #[tokio::test]
    async fn md_articles() -> Result<()> {
        let fixtures = fixtures();

        // So that test results are not dependant upon the the machine the test is run on or how
        // the test is compiled only use built-in kernels
        let kernels: Vec<Kernel> = kernels::available()
            .await?
            .into_iter()
            .filter(|kernel| matches!(kernel.r#type, KernelType::Builtin))
            .collect();

        let mut settings = insta::Settings::clone_current();
        settings.set_prepend_module_to_snapshot(false);
        settings.bind_to_thread();

        for name in ["code.md", "code-relations.md"] {
            let path = fixtures.join("articles").join(name);

            // Load the article
            let root = Arc::new(RwLock::new(MdCodec::from_path(&path, None).await?));

            // Strip the fixtures path so it does not differ between machines
            let path = path.strip_prefix(&fixtures)?;
            let project = path.parent().unwrap();

            // Compile the article and snapshot the result
            let (patch_sender, mut patch_receiver) = mpsc::unbounded_channel::<PatchMessage>();
            tokio::spawn(async move {
                while let Some(_patch) = patch_receiver.recv().await {
                    // Ignore for this test
                }
            });
            let (addresses, graph) = compile(path, project, &root, &patch_sender).await?;
            snapshot_set_suffix(&[name, "-compile"].concat(), || {
                assert_json_snapshot!((&addresses, &graph))
            });

            // Generate various execution plans for the article using alternative options
            // and snapshot them all. Always specify `max_concurrency` to avoid differences
            // due to machine (number of CPUs)
            for (suffix, options) in [
                (
                    "appearance",
                    PlanOptions {
                        ordering: PlanOrdering::Appearance,
                        max_concurrency: 10,
                    },
                ),
                (
                    "appearance-concurrency-0",
                    PlanOptions {
                        ordering: PlanOrdering::Appearance,
                        max_concurrency: 0,
                    },
                ),
                (
                    "topological",
                    PlanOptions {
                        ordering: PlanOrdering::Topological,
                        max_concurrency: 10,
                    },
                ),
                (
                    "topological-concurrency-0",
                    PlanOptions {
                        ordering: PlanOrdering::Topological,
                        max_concurrency: 0,
                    },
                ),
            ] {
                let plan = graph
                    .plan(None, Some(kernels.clone()), Some(options))
                    .await?;
                snapshot_set_suffix(&[name, "-", suffix].concat(), || {
                    assert_json_snapshot!(&plan)
                });
            }

            // Execute the article (with topological execution plan) and snapshot the resultant patches
            let plan = graph
                .plan(
                    None,
                    Some(kernels.clone()),
                    Some(PlanOptions {
                        ordering: PlanOrdering::Topological,
                        max_concurrency: 10,
                    }),
                )
                .await?;

            let (patch_sender, mut patch_receiver) = mpsc::unbounded_channel::<PatchMessage>();
            let patches = tokio::spawn(async move {
                let mut patches = Vec::new();
                while let Some(PatchMessage { mut patch, .. }) = patch_receiver.recv().await {
                    // Redact execute time and duration from patch because they will
                    // change across test runs
                    for op in patch.ops.iter_mut() {
                        if let Operation::Add { address, value, .. } = op {
                            if let Some(Slot::Name(name)) = address.back() {
                                if name == "execute_ended" || name == "execute_duration" {
                                    *value = Box::new("<redacted>".to_string());
                                }
                            }
                        }
                    }
                    patches.push(patch);
                }
                patches
            });

            execute(
                &plan,
                &root,
                &Arc::new(RwLock::new(addresses)),
                &Arc::new(RwLock::new(graph)),
                &patch_sender,
                None,
            )
            .await?;

            drop(patch_sender);

            let _patches = patches.await?;
            /*
            Snapshotting of patches turned off for now because order is not-deterministic

            snapshot_set_suffix(&[name, "-patches"].concat(), || {
                assert_json_snapshot!(&patches);
            });
            */
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate lalrpop_util;
extern crate toml;

pub mod json;
use std::io::prelude::*;
use std::fs::File;
use std::env;

fn json_file_to_toml(fname: &str) -> String {
    let mut file = File::open(fname).unwrap();
    let mut json = String::new();
    let _ = file.read_to_string(&mut json);
    format!("{}", json::parse_json(&json).unwrap())
}

fn main() {
    let args: Vec<String> = env::args().collect();

    if args.len() != 2 {
println!("usage:
glstyleconv <jsonfilename>");
    } else {
        println!("{}", json_file_to_toml(&args[1]));
    }
}

#[test]
fn json() {
    use lalrpop_util::ParseError;

    assert!(json::parse_json(r#"{"circle-radius": {"stops": [1, 0]}}"#).is_ok());
    assert!(json::parse_json(r#"{"circle-radius": {"property": "temperature","stops": [[{"zoom": 0, "value": 0}, 0],[{"zoom": 20, "value": 5}, 20]]}}"#).is_ok());
    assert!(json::parse_json(r#"{"string": "\u12345\\\"\r"}"#).is_ok());
    assert_eq!(json::parse_json(r#"{zoom: 0}"#), Err(ParseError::InvalidToken { location: 1 }));
}

#[test]
fn value_to_toml() {
    assert_eq!(json::parse_value("-1"), Ok(toml::Value::Integer(-1)));
    assert_eq!(json::parse_value("3.5"), Ok(toml::Value::Float(3.5)));
    assert_eq!(json::parse_value("2.5e10"), Ok(toml::Value::Float(2.5e10)));
    assert_eq!(json::parse_value("2e-10"), Ok(toml::Value::Float(2e-10)));
    assert_eq!(json::parse_value("true"), Ok(toml::Value::Boolean(true)));
    assert_eq!(json::parse_value("false"), Ok(toml::Value::Boolean(false)));
}

#[test]
fn json_to_toml() {
    let json = r#"[1,2,3]"#;
    let toml = "json = [1, 2, 3]\n";
    println!("{}", json::parse_json(json).unwrap());
    assert_eq!(format!("{}", json::parse_json(json).unwrap()), toml);
    assert!(toml::Parser::new(toml).parse().is_some());

    let json = r##"
        {
          "id": "water",
          "source": "mapbox-streets",
          "source-layer": "water",
          "type": "fill",
          "paint": {
            "fill-color": "#00ffff"
          }
        }"##;
    let toml = r##"id = "water"
source = "mapbox-streets"
source-layer = "water"
type = "fill"

[paint]
fill-color = "#00ffff"
"##;
    println!("{}", json::parse_json(json).unwrap());
    assert_eq!(format!("{}", json::parse_json(json).unwrap()), toml);
    assert!(toml::Parser::new(toml).parse().is_some());

    //top-level array shall be embedded in a table
    let json = r##"[
        {
          "id": "water",
          "source": "mapbox-streets",
          "source-layer": "water",
          "type": "fill",
          "paint": {
            "fill-color": "#00ffff"
          }
        }
    ]"##;
    let toml = r##"[[json]]
id = "water"
source = "mapbox-streets"
source-layer = "water"
type = "fill"

[json.paint]
fill-color = "#00ffff"
"##;
    println!("{}", json::parse_json(json).unwrap());
    assert_eq!(format!("{}", json::parse_json(json).unwrap()), toml);
    assert!(toml::Parser::new(toml).parse().is_some());

    let json = r#"{"circle-radius": {"stops": [[5, 1],[10, 2]]}}"#;
    let toml = r#"[circle-radius]
[[circle-radius.stops]]
in = 5
out = 1

[[circle-radius.stops]]
in = 10
out = 2
"#;
    let tomlshort = r#"[circle-radius]
    stops = [{in = 5, out = 1}, {in = 10, out = 2}]
"#;

    let tomlparsed = json::parse_json(json).unwrap();
    println!("{}", tomlparsed);
    assert_eq!(format!("{}", tomlparsed), toml);
    assert_eq!(tomlparsed, toml::Value::Table(toml::Parser::new(tomlshort).parse().unwrap()));

    let json = r#"{"circle-radius": {"property": "temperature","stops": [[{"zoom": 0, "value": 0}, 0],[{"zoom": 20, "value": 5}, 20]]}}"#;
    let toml = r#"[circle-radius]
property = "temperature"

[[circle-radius.stops]]
out = 0

[circle-radius.stops.in]
value = 0
zoom = 0

[[circle-radius.stops]]
out = 20

[circle-radius.stops.in]
value = 5
zoom = 20
"#;
    let tomlshort = r#"[circle-radius]
    property = "temperature"
    stops = [{in = {value = 0, zoom = 0}, out = 0}, {in = {value = 5, zoom = 20}, out = 20}]
"#;
    let tomlparsed = json::parse_json(json).unwrap();
    println!("{}", tomlparsed);
    assert_eq!(format!("{}", tomlparsed), toml);
    assert!(toml::Parser::new(toml).parse().is_some());
    assert_eq!(tomlparsed, toml::Value::Table(toml::Parser::new(tomlshort).parse().unwrap()));
}

#[test]
fn mstudio_to_toml() {
    let toml = json_file_to_toml("testdata/bright-v9-cdn.json");
    println!("{}", toml);
    assert!(toml.contains(r##"[[layers]]
id = "background"
interactive = true
type = "background"

[layers.paint]
background-color = "#f8f4f0"

[[layers]]
filter = ["==", "class", "national_park"]
id = "landuse_overlay_national_park"
interactive = true
source = "mapbox"
source-layer = "landuse_overlay"
type = "fill"

[layers.metadata]
"mapbox:group" = "1444849388993.3071"

[layers.paint]
fill-color = "#d8e8c8"
fill-opacity = 0.75

[[layers]]
filter = ["==", "class", "park"]
id = "landuse_park"
interactive = true
source = "mapbox"
source-layer = "landuse"
type = "fill"
"##));

    assert!(toml.contains(r##"[[layers]]
filter = ["==", "scalerank", 1]
id = "country_label_1"
interactive = true
source = "mapbox"
source-layer = "country_label"
type = "symbol"

[layers.layout]
text-field = "{name_en}"
text-font = ["Open Sans Bold"]
text-max-width = 6.25
text-transform = "uppercase"
"##));

assert!(toml.contains(r##"
[layers.paint]
text-color = "#334"
text-halo-blur = 1
text-halo-color = "rgba(255,255,255,0.8)"
text-halo-width = 2
"##));

    assert!(toml.contains(r#"filter = [["all"], ["!=", "class", "river"], ["!=", "class", "stream"], ["!=", "class", "canal"]]"#));
}
<commit_msg>Accept JSON from stdin<commit_after>extern crate lalrpop_util;
extern crate toml;

pub mod json;

use std::io;
use std::fs::File;
use std::env;

fn read_to_toml(input: &mut io::Read) -> String {
    let mut json = String::new();
    let _ = input.read_to_string(&mut json);
    format!("{}", json::parse_json(&json).unwrap())
}

fn json_file_to_toml(fname: &str) -> String {
    let mut file = File::open(fname).unwrap();
    read_to_toml(&mut file)
}

fn main() {
    let args: Vec<String> = env::args().collect();

    match args.len() {
        1 => println!("{}", read_to_toml(&mut io::stdin())),
        2 => println!("{}", json_file_to_toml(&args[1])),
        _ => {
            println!("usage:
glstyleconv [jsonfilename]");
        }
    }
}

#[test]
fn json() {
    use lalrpop_util::ParseError;

    assert!(json::parse_json(r#"{"circle-radius": {"stops": [1, 0]}}"#).is_ok());
    assert!(json::parse_json(r#"{"circle-radius": {"property": "temperature","stops": [[{"zoom": 0, "value": 0}, 0],[{"zoom": 20, "value": 5}, 20]]}}"#).is_ok());
    assert!(json::parse_json(r#"{"string": "\u12345\\\"\r"}"#).is_ok());
    assert_eq!(json::parse_json(r#"{zoom: 0}"#), Err(ParseError::InvalidToken { location: 1 }));
}

#[test]
fn value_to_toml() {
    assert_eq!(json::parse_value("-1"), Ok(toml::Value::Integer(-1)));
    assert_eq!(json::parse_value("3.5"), Ok(toml::Value::Float(3.5)));
    assert_eq!(json::parse_value("2.5e10"), Ok(toml::Value::Float(2.5e10)));
    assert_eq!(json::parse_value("2e-10"), Ok(toml::Value::Float(2e-10)));
    assert_eq!(json::parse_value("true"), Ok(toml::Value::Boolean(true)));
    assert_eq!(json::parse_value("false"), Ok(toml::Value::Boolean(false)));
}

#[test]
fn json_to_toml() {
    let json = r#"[1,2,3]"#;
    let toml = "json = [1, 2, 3]\n";
    println!("{}", json::parse_json(json).unwrap());
    assert_eq!(format!("{}", json::parse_json(json).unwrap()), toml);
    assert!(toml::Parser::new(toml).parse().is_some());

    let json = r##"
        {
          "id": "water",
          "source": "mapbox-streets",
          "source-layer": "water",
          "type": "fill",
          "paint": {
            "fill-color": "#00ffff"
          }
        }"##;
    let toml = r##"id = "water"
source = "mapbox-streets"
source-layer = "water"
type = "fill"

[paint]
fill-color = "#00ffff"
"##;
    println!("{}", json::parse_json(json).unwrap());
    assert_eq!(format!("{}", json::parse_json(json).unwrap()), toml);
    assert!(toml::Parser::new(toml).parse().is_some());

    //top-level array shall be embedded in a table
    let json = r##"[
        {
          "id": "water",
          "source": "mapbox-streets",
          "source-layer": "water",
          "type": "fill",
          "paint": {
            "fill-color": "#00ffff"
          }
        }
    ]"##;
    let toml = r##"[[json]]
id = "water"
source = "mapbox-streets"
source-layer = "water"
type = "fill"

[json.paint]
fill-color = "#00ffff"
"##;
    println!("{}", json::parse_json(json).unwrap());
    assert_eq!(format!("{}", json::parse_json(json).unwrap()), toml);
    assert!(toml::Parser::new(toml).parse().is_some());

    let json = r#"{"circle-radius": {"stops": [[5, 1],[10, 2]]}}"#;
    let toml = r#"[circle-radius]
[[circle-radius.stops]]
in = 5
out = 1

[[circle-radius.stops]]
in = 10
out = 2
"#;
    let tomlshort = r#"[circle-radius]
    stops = [{in = 5, out = 1}, {in = 10, out = 2}]
"#;

    let tomlparsed = json::parse_json(json).unwrap();
    println!("{}", tomlparsed);
    assert_eq!(format!("{}", tomlparsed), toml);
    assert_eq!(tomlparsed, toml::Value::Table(toml::Parser::new(tomlshort).parse().unwrap()));

    let json = r#"{"circle-radius": {"property": "temperature","stops": [[{"zoom": 0, "value": 0}, 0],[{"zoom": 20, "value": 5}, 20]]}}"#;
    let toml = r#"[circle-radius]
property = "temperature"

[[circle-radius.stops]]
out = 0

[circle-radius.stops.in]
value = 0
zoom = 0

[[circle-radius.stops]]
out = 20

[circle-radius.stops.in]
value = 5
zoom = 20
"#;
    let tomlshort = r#"[circle-radius]
    property = "temperature"
    stops = [{in = {value = 0, zoom = 0}, out = 0}, {in = {value = 5, zoom = 20}, out = 20}]
"#;
    let tomlparsed = json::parse_json(json).unwrap();
    println!("{}", tomlparsed);
    assert_eq!(format!("{}", tomlparsed), toml);
    assert!(toml::Parser::new(toml).parse().is_some());
    assert_eq!(tomlparsed, toml::Value::Table(toml::Parser::new(tomlshort).parse().unwrap()));
}

#[test]
fn mstudio_to_toml() {
    let toml = json_file_to_toml("testdata/bright-v9-cdn.json");
    println!("{}", toml);
    assert!(toml.contains(r##"[[layers]]
id = "background"
interactive = true
type = "background"

[layers.paint]
background-color = "#f8f4f0"

[[layers]]
filter = ["==", "class", "national_park"]
id = "landuse_overlay_national_park"
interactive = true
source = "mapbox"
source-layer = "landuse_overlay"
type = "fill"

[layers.metadata]
"mapbox:group" = "1444849388993.3071"

[layers.paint]
fill-color = "#d8e8c8"
fill-opacity = 0.75

[[layers]]
filter = ["==", "class", "park"]
id = "landuse_park"
interactive = true
source = "mapbox"
source-layer = "landuse"
type = "fill"
"##));

    assert!(toml.contains(r##"[[layers]]
filter = ["==", "scalerank", 1]
id = "country_label_1"
interactive = true
source = "mapbox"
source-layer = "country_label"
type = "symbol"

[layers.layout]
text-field = "{name_en}"
text-font = ["Open Sans Bold"]
text-max-width = 6.25
text-transform = "uppercase"
"##));

assert!(toml.contains(r##"
[layers.paint]
text-color = "#334"
text-halo-blur = 1
text-halo-color = "rgba(255,255,255,0.8)"
text-halo-width = 2
"##));

    assert!(toml.contains(r#"filter = [["all"], ["!=", "class", "river"], ["!=", "class", "stream"], ["!=", "class", "canal"]]"#));
}
<|endoftext|>


<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/. */

#![doc = "Rust wrappers around the raw JS apis"]

use libc::types::os::arch::c95::{size_t, c_uint};
use libc::c_char;
use std::cmp;
use std::rc;
use std::rt::Runtime;
use jsapi::*;
use jsval::{JSVal, NullValue};
use default_stacksize;
use default_heapsize;
use JSOPTION_VAROBJFIX;
use JSOPTION_METHODJIT;
use JSOPTION_TYPE_INFERENCE;
use ERR;
use std::ptr::null;
use result;
use result_obj;
use std::str::raw::from_c_str;
use std::cast;
use green::task::GreenTask;

// ___________________________________________________________________________
// friendly Rustic API to runtimes

pub type rt = rc::Rc<rt_rsrc>;

pub struct rt_rsrc {
    pub ptr : *JSRuntime,
}

impl Drop for rt_rsrc {
    fn drop(&mut self) {
        unsafe {
            JS_Finish(self.ptr);
        }
    }
}

pub fn new_runtime(p: *JSRuntime) -> rt {
    return rc::Rc::new(rt_rsrc {
        ptr: p
    })
}

pub trait RtUtils {
    fn cx(&self) -> rc::Rc<Cx>;
}

impl RtUtils for rc::Rc<rt_rsrc> {
    fn cx(&self) -> rc::Rc<Cx> {
        unsafe {
            new_context(JS_NewContext(self.deref().ptr,
                                      default_stacksize as size_t), self.clone())
        }
    }
}

// FIXME: Is this safe once we have more than one stack segment?
extern fn gc_callback(rt: *JSRuntime, _status: JSGCStatus) {
    use std::rt::local::Local;
    use std::rt::task::Task;
    unsafe {
        let mut task = Local::borrow(None::<Task>);
        let green_task: ~GreenTask = task.get().maybe_take_runtime().unwrap();
        let (start, end) = green_task.stack_bounds();
        JS_SetNativeStackBounds(rt, cmp::min(start, end), cmp::max(start, end));
        task.get().put_runtime(green_task);
    }
}

pub fn rt() -> rt {
    unsafe {
        let runtime = JS_Init(default_heapsize);
        JS_SetGCCallback(runtime, gc_callback);
        return new_runtime(runtime);
    }
}

// ___________________________________________________________________________
// contexts

pub struct Cx {
    pub ptr: *JSContext,
    pub rt: rt,
}

#[unsafe_destructor]
impl Drop for Cx {
    fn drop(&mut self) {
        unsafe {
            JS_DestroyContext(self.ptr);
        }
    }
}

pub fn new_context(ptr: *JSContext, rt: rt) -> rc::Rc<Cx> {
    return rc::Rc::new(Cx {
        ptr: ptr,
        rt: rt,
    })
}

pub trait CxUtils {
    fn rooted_obj(&self, obj: *JSObject) -> jsobj;
    fn new_compartment(&self, globcls: *JSClass) -> Result<rc::Rc<Compartment>,()>;
    fn new_compartment_with_global(&self, global: *JSObject) -> Result<rc::Rc<Compartment>,()>;
}

impl CxUtils for rc::Rc<Cx> {
    fn rooted_obj(&self, obj: *JSObject) -> jsobj {
        let cxptr = self.deref().ptr;
        let jsobj = rc::Rc::new(jsobj_rsrc {cx: self.clone(), cxptr: cxptr, ptr: obj});
        unsafe {
            JS_AddObjectRoot(cxptr, &jsobj.deref().ptr);
        }
        jsobj
    }

    fn new_compartment(&self, globcls: *JSClass) -> Result<rc::Rc<Compartment>,()> {
        unsafe {
            let ptr = self.deref().ptr;
            let globobj = JS_NewGlobalObject(ptr, globcls, null());
            result(JS_InitStandardClasses(ptr, globobj)).and_then(|_ok| {
                Ok(rc::Rc::new(Compartment {
                    cx: self.clone(),
                    global_obj: globobj,
                }))
            })
        }
    }

    fn new_compartment_with_global(&self, global: *JSObject) -> Result<rc::Rc<Compartment>,()> {
        Ok(rc::Rc::new(Compartment {
            cx: self.clone(),
            global_obj: global,
        }))
    }
}

impl Cx {
    pub fn set_default_options_and_version(&self) {
        self.set_options(JSOPTION_VAROBJFIX | JSOPTION_METHODJIT |
                         JSOPTION_TYPE_INFERENCE);
        self.set_version(JSVERSION_LATEST);
    }

    pub fn set_options(&self, v: c_uint) {
        unsafe {
            JS_SetOptions(self.ptr, v);
        }
    }

    pub fn set_version(&self, v: i32) {
        unsafe {
            JS_SetVersion(self.ptr, v);
        }
    }

    pub fn set_logging_error_reporter(&self) {
        unsafe {
            JS_SetErrorReporter(self.ptr, reportError);
        }
    }

    pub fn set_error_reporter(&self, reportfn: extern "C" fn(*JSContext, *c_char, *JSErrorReport)) {
        unsafe {
            JS_SetErrorReporter(self.ptr, reportfn);
        }
    }

    pub fn evaluate_script(&self, glob: *JSObject, script: ~str, filename: ~str, line_num: uint)
                    -> Result<(),()> {
        let script_utf16 = script.to_utf16();
        filename.to_c_str().with_ref(|filename_cstr| {
            let rval: JSVal = NullValue();
            debug!("Evaluating script from {:s} with content {}", filename, script);
            unsafe {
                if ERR == JS_EvaluateUCScript(self.ptr, glob,
                                              script_utf16.as_ptr(), script_utf16.len() as c_uint,
                                              filename_cstr, line_num as c_uint,
                                              &rval) {
                    debug!("...err!");
                    Err(())
                } else {
                    // we could return the script result but then we'd have
                    // to root it and so forth and, really, who cares?
                    debug!("...ok!");
                    Ok(())
                }
            }
        })
    }

    pub unsafe fn get_cx_private(&self) -> *() {
        cast::transmute(JS_GetContextPrivate(self.ptr))
    }

    pub unsafe fn set_cx_private(&self, data: *()) {
        JS_SetContextPrivate(self.ptr, cast::transmute(data));
    }

    pub unsafe fn get_obj_private(&self, obj: *JSObject) -> *() {
        cast::transmute(JS_GetPrivate(obj))
    }

    pub unsafe fn set_obj_private(&self, obj: *JSObject, data: *()) {
        JS_SetPrivate(obj, cast::transmute(data));
    }
}

pub extern fn reportError(_cx: *JSContext, msg: *c_char, report: *JSErrorReport) {
    unsafe {
        let fnptr = (*report).filename;
        let fname = if fnptr.is_not_null() {from_c_str(fnptr)} else {~"none"};
        let lineno = (*report).lineno;
        let msg = from_c_str(msg);
        error!("Error at {:s}:{}: {:s}\n", fname, lineno, msg);
    }
}

// ___________________________________________________________________________
// compartment

pub struct Compartment {
    pub cx: rc::Rc<Cx>,
    pub global_obj: *JSObject,
}

impl Compartment {
    pub fn define_functions(&self, specvec: &'static [JSFunctionSpec]) -> Result<(),()> {
        unsafe {
            result(JS_DefineFunctions(self.cx.deref().ptr,
                                      self.global_obj,
                                      specvec.as_ptr()))
        }
    }
    pub fn define_properties(&self, specvec: &'static [JSPropertySpec]) -> Result<(),()> {
        unsafe {
            result(JS_DefineProperties(self.cx.deref().ptr,
                                       self.global_obj,
                                       specvec.as_ptr()))
        }
    }
    pub fn define_property(&self,
                           name: &'static str,
                           value: JSVal,
                           getter: JSPropertyOp, setter: JSStrictPropertyOp,
                           attrs: c_uint)
        -> Result<(),()> {
        unsafe {
            name.to_c_str().with_ref(|name| {
                result(JS_DefineProperty(self.cx.deref().ptr,
                                         self.global_obj,
                                         name,
                                         value,
                                         Some(getter),
                                         Some(setter),
                                         attrs))
            })
        }
    }
    pub fn new_object(&self, classptr: *JSClass, proto: *JSObject, parent: *JSObject)
               -> Result<jsobj, ()> {
        unsafe {
            let obj = self.cx.rooted_obj(JS_NewObject(self.cx.deref().ptr, classptr, proto, parent));
            result_obj(obj)
        }
    }
}

// ___________________________________________________________________________
// objects

pub type jsobj = rc::Rc<jsobj_rsrc>;

pub struct jsobj_rsrc {
    cx: rc::Rc<Cx>,
    cxptr: *JSContext,
    pub ptr: *JSObject,
}

#[unsafe_destructor]
impl Drop for jsobj_rsrc {
    fn drop(&mut self) {
        unsafe {
            JS_RemoveObjectRoot(self.cxptr, &self.ptr);
        }
    }
}

impl jsobj_rsrc {
    pub fn new_object(&self, cx: rc::Rc<Cx>, cxptr: *JSContext, ptr: *JSObject) -> jsobj {
        return rc::Rc::new(jsobj_rsrc {
            cx: cx,
            cxptr: cxptr,
            ptr: ptr
        })
    }
}

// ___________________________________________________________________________
// random utilities

pub trait to_jsstr {
    fn to_jsstr(self, cx: rc::Rc<Cx>) -> *JSString;
}

impl to_jsstr for ~str {
    fn to_jsstr(self, cx: rc::Rc<Cx>) -> *JSString {
        unsafe {
            let cbuf = cast::transmute(self.as_ptr());
            JS_NewStringCopyN(cx.deref().ptr, cbuf, self.len() as size_t)
        }
    }
}

#[cfg(test)]
pub mod test {
    use super::rt;
    use super::{CxUtils, RtUtils};
    use super::super::global;
    use super::super::jsapi::{JS_GC, JS_GetRuntime};

    #[test]
    pub fn dummy() {
        let rt = rt();
        let cx = rt.cx();
        cx.deref().set_default_options_and_version();
        cx.deref().set_logging_error_reporter();
        cx.new_compartment(&global::BASIC_GLOBAL).and_then(|comp| {
            unsafe { JS_GC(JS_GetRuntime(cx.deref().ptr)); }

            comp.deref().define_functions(global::DEBUG_FNS);

            let s = ~"debug(22);";
            cx.deref().evaluate_script(comp.deref().global_obj, s, ~"test", 1u)
        });
    }

}
<commit_msg>Implement with_compartment to enter compartments from Rust code.<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/. */

#![doc = "Rust wrappers around the raw JS apis"]

use libc::types::os::arch::c95::{size_t, c_uint};
use libc::c_char;
use std::cmp;
use std::rc;
use std::rt::Runtime;
use jsapi::*;
use jsval::{JSVal, NullValue};
use default_stacksize;
use default_heapsize;
use JSOPTION_VAROBJFIX;
use JSOPTION_METHODJIT;
use JSOPTION_TYPE_INFERENCE;
use ERR;
use std::ptr::null;
use result;
use result_obj;
use std::str::raw::from_c_str;
use std::cast;
use green::task::GreenTask;

// ___________________________________________________________________________
// friendly Rustic API to runtimes

pub type rt = rc::Rc<rt_rsrc>;

pub struct rt_rsrc {
    pub ptr : *JSRuntime,
}

impl Drop for rt_rsrc {
    fn drop(&mut self) {
        unsafe {
            JS_Finish(self.ptr);
        }
    }
}

pub fn new_runtime(p: *JSRuntime) -> rt {
    return rc::Rc::new(rt_rsrc {
        ptr: p
    })
}

pub trait RtUtils {
    fn cx(&self) -> rc::Rc<Cx>;
}

impl RtUtils for rc::Rc<rt_rsrc> {
    fn cx(&self) -> rc::Rc<Cx> {
        unsafe {
            new_context(JS_NewContext(self.deref().ptr,
                                      default_stacksize as size_t), self.clone())
        }
    }
}

// FIXME: Is this safe once we have more than one stack segment?
extern fn gc_callback(rt: *JSRuntime, _status: JSGCStatus) {
    use std::rt::local::Local;
    use std::rt::task::Task;
    unsafe {
        let mut task = Local::borrow(None::<Task>);
        let green_task: ~GreenTask = task.get().maybe_take_runtime().unwrap();
        let (start, end) = green_task.stack_bounds();
        JS_SetNativeStackBounds(rt, cmp::min(start, end), cmp::max(start, end));
        task.get().put_runtime(green_task);
    }
}

pub fn rt() -> rt {
    unsafe {
        let runtime = JS_Init(default_heapsize);
        JS_SetGCCallback(runtime, gc_callback);
        return new_runtime(runtime);
    }
}

// ___________________________________________________________________________
// contexts

pub struct Cx {
    pub ptr: *JSContext,
    pub rt: rt,
}

#[unsafe_destructor]
impl Drop for Cx {
    fn drop(&mut self) {
        unsafe {
            JS_DestroyContext(self.ptr);
        }
    }
}

pub fn new_context(ptr: *JSContext, rt: rt) -> rc::Rc<Cx> {
    return rc::Rc::new(Cx {
        ptr: ptr,
        rt: rt,
    })
}

pub trait CxUtils {
    fn rooted_obj(&self, obj: *JSObject) -> jsobj;
    fn new_compartment(&self, globcls: *JSClass) -> Result<rc::Rc<Compartment>,()>;
    fn new_compartment_with_global(&self, global: *JSObject) -> Result<rc::Rc<Compartment>,()>;
}

impl CxUtils for rc::Rc<Cx> {
    fn rooted_obj(&self, obj: *JSObject) -> jsobj {
        let cxptr = self.deref().ptr;
        let jsobj = rc::Rc::new(jsobj_rsrc {cx: self.clone(), cxptr: cxptr, ptr: obj});
        unsafe {
            JS_AddObjectRoot(cxptr, &jsobj.deref().ptr);
        }
        jsobj
    }

    fn new_compartment(&self, globcls: *JSClass) -> Result<rc::Rc<Compartment>,()> {
        unsafe {
            let ptr = self.deref().ptr;
            let globobj = JS_NewGlobalObject(ptr, globcls, null());
            result(JS_InitStandardClasses(ptr, globobj)).and_then(|_ok| {
                Ok(rc::Rc::new(Compartment {
                    cx: self.clone(),
                    global_obj: globobj,
                }))
            })
        }
    }

    fn new_compartment_with_global(&self, global: *JSObject) -> Result<rc::Rc<Compartment>,()> {
        Ok(rc::Rc::new(Compartment {
            cx: self.clone(),
            global_obj: global,
        }))
    }
}

impl Cx {
    pub fn set_default_options_and_version(&self) {
        self.set_options(JSOPTION_VAROBJFIX | JSOPTION_METHODJIT |
                         JSOPTION_TYPE_INFERENCE);
        self.set_version(JSVERSION_LATEST);
    }

    pub fn set_options(&self, v: c_uint) {
        unsafe {
            JS_SetOptions(self.ptr, v);
        }
    }

    pub fn set_version(&self, v: i32) {
        unsafe {
            JS_SetVersion(self.ptr, v);
        }
    }

    pub fn set_logging_error_reporter(&self) {
        unsafe {
            JS_SetErrorReporter(self.ptr, reportError);
        }
    }

    pub fn set_error_reporter(&self, reportfn: extern "C" fn(*JSContext, *c_char, *JSErrorReport)) {
        unsafe {
            JS_SetErrorReporter(self.ptr, reportfn);
        }
    }

    pub fn evaluate_script(&self, glob: *JSObject, script: ~str, filename: ~str, line_num: uint)
                    -> Result<(),()> {
        let script_utf16 = script.to_utf16();
        filename.to_c_str().with_ref(|filename_cstr| {
            let rval: JSVal = NullValue();
            debug!("Evaluating script from {:s} with content {}", filename, script);
            unsafe {
                if ERR == JS_EvaluateUCScript(self.ptr, glob,
                                              script_utf16.as_ptr(), script_utf16.len() as c_uint,
                                              filename_cstr, line_num as c_uint,
                                              &rval) {
                    debug!("...err!");
                    Err(())
                } else {
                    // we could return the script result but then we'd have
                    // to root it and so forth and, really, who cares?
                    debug!("...ok!");
                    Ok(())
                }
            }
        })
    }

    pub unsafe fn get_cx_private(&self) -> *() {
        cast::transmute(JS_GetContextPrivate(self.ptr))
    }

    pub unsafe fn set_cx_private(&self, data: *()) {
        JS_SetContextPrivate(self.ptr, cast::transmute(data));
    }

    pub unsafe fn get_obj_private(&self, obj: *JSObject) -> *() {
        cast::transmute(JS_GetPrivate(obj))
    }

    pub unsafe fn set_obj_private(&self, obj: *JSObject, data: *()) {
        JS_SetPrivate(obj, cast::transmute(data));
    }
}

pub extern fn reportError(_cx: *JSContext, msg: *c_char, report: *JSErrorReport) {
    unsafe {
        let fnptr = (*report).filename;
        let fname = if fnptr.is_not_null() {from_c_str(fnptr)} else {~"none"};
        let lineno = (*report).lineno;
        let msg = from_c_str(msg);
        error!("Error at {:s}:{}: {:s}\n", fname, lineno, msg);
    }
}

pub fn with_compartment<R>(cx: *JSContext, object: *JSObject, cb: || -> R) -> R {
    unsafe {
        let call = JS_EnterCrossCompartmentCall(cx, object);
        let result = cb();
        JS_LeaveCrossCompartmentCall(call);
        result
    }
}

// ___________________________________________________________________________
// compartment

pub struct Compartment {
    pub cx: rc::Rc<Cx>,
    pub global_obj: *JSObject,
}

impl Compartment {
    pub fn define_functions(&self, specvec: &'static [JSFunctionSpec]) -> Result<(),()> {
        unsafe {
            result(JS_DefineFunctions(self.cx.deref().ptr,
                                      self.global_obj,
                                      specvec.as_ptr()))
        }
    }
    pub fn define_properties(&self, specvec: &'static [JSPropertySpec]) -> Result<(),()> {
        unsafe {
            result(JS_DefineProperties(self.cx.deref().ptr,
                                       self.global_obj,
                                       specvec.as_ptr()))
        }
    }
    pub fn define_property(&self,
                           name: &'static str,
                           value: JSVal,
                           getter: JSPropertyOp, setter: JSStrictPropertyOp,
                           attrs: c_uint)
        -> Result<(),()> {
        unsafe {
            name.to_c_str().with_ref(|name| {
                result(JS_DefineProperty(self.cx.deref().ptr,
                                         self.global_obj,
                                         name,
                                         value,
                                         Some(getter),
                                         Some(setter),
                                         attrs))
            })
        }
    }
    pub fn new_object(&self, classptr: *JSClass, proto: *JSObject, parent: *JSObject)
               -> Result<jsobj, ()> {
        unsafe {
            let obj = self.cx.rooted_obj(JS_NewObject(self.cx.deref().ptr, classptr, proto, parent));
            result_obj(obj)
        }
    }
}

// ___________________________________________________________________________
// objects

pub type jsobj = rc::Rc<jsobj_rsrc>;

pub struct jsobj_rsrc {
    cx: rc::Rc<Cx>,
    cxptr: *JSContext,
    pub ptr: *JSObject,
}

#[unsafe_destructor]
impl Drop for jsobj_rsrc {
    fn drop(&mut self) {
        unsafe {
            JS_RemoveObjectRoot(self.cxptr, &self.ptr);
        }
    }
}

impl jsobj_rsrc {
    pub fn new_object(&self, cx: rc::Rc<Cx>, cxptr: *JSContext, ptr: *JSObject) -> jsobj {
        return rc::Rc::new(jsobj_rsrc {
            cx: cx,
            cxptr: cxptr,
            ptr: ptr
        })
    }
}

// ___________________________________________________________________________
// random utilities

pub trait to_jsstr {
    fn to_jsstr(self, cx: rc::Rc<Cx>) -> *JSString;
}

impl to_jsstr for ~str {
    fn to_jsstr(self, cx: rc::Rc<Cx>) -> *JSString {
        unsafe {
            let cbuf = cast::transmute(self.as_ptr());
            JS_NewStringCopyN(cx.deref().ptr, cbuf, self.len() as size_t)
        }
    }
}

#[cfg(test)]
pub mod test {
    use super::rt;
    use super::{CxUtils, RtUtils};
    use super::super::global;
    use super::super::jsapi::{JS_GC, JS_GetRuntime};

    #[test]
    pub fn dummy() {
        let rt = rt();
        let cx = rt.cx();
        cx.deref().set_default_options_and_version();
        cx.deref().set_logging_error_reporter();
        cx.new_compartment(&global::BASIC_GLOBAL).and_then(|comp| {
            unsafe { JS_GC(JS_GetRuntime(cx.deref().ptr)); }

            comp.deref().define_functions(global::DEBUG_FNS);

            let s = ~"debug(22);";
            cx.deref().evaluate_script(comp.deref().global_obj, s, ~"test", 1u)
        });
    }

}
<|endoftext|>


<commit_before>#[macro_use]
extern crate abomonation;
use abomonation::*;

#[test] fn test_option_box_u64() { _test_pass(vec![Some(Box::new(0u64))]); }
#[test] fn test_option_vec() { _test_pass(vec![Some(vec![0, 1, 2])]); }
#[test] fn test_u32x4_pass() { _test_pass(vec![((1,2,3),vec![(0u32, 0u32, 0u32, 0u32); 1024])]); }
#[test] fn test_u64_pass() { _test_pass(vec![0u64; 1024]); }
#[test] fn test_string_pass() { _test_pass(vec![format!("grawwwwrr!"); 1024]); }
#[test] fn test_vec_u_s_pass() { _test_pass(vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }

#[test] fn test_u64_fail() { _test_fail(vec![0u64; 1024]); }
#[test] fn test_string_fail() { _test_fail(vec![format!("grawwwwrr!"); 1024]); }
#[test] fn test_vec_u_s_fail() { _test_fail(vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }

fn _test_pass<T: Abomonation+Eq>(record: T) {
    let mut bytes = Vec::new();
    encode(&record, &mut bytes);
    let (result, rest) = decode::<T>(&mut bytes[..]).unwrap();
    assert!(&record == result);
    assert!(rest.len() == 0);
}

fn _test_fail<T: Abomonation>(record: T) {
    let mut bytes = Vec::new();
    encode(&record, &mut bytes);
    bytes.pop();
    if let Ok(_) = decode::<T>(&mut bytes[..]) { panic!() }
    else { }
}

#[derive(Eq, PartialEq)]
struct MyStruct {
    a: String,
    b: u64,
    c: Vec<u8>,
}

abomonate!(MyStruct : a, b, c);

#[test]
fn test_macro() {
    // create some test data out of abomonation-approved types
    let record = MyStruct{ a: "test".to_owned(), b: 0, c: vec![0, 1, 2] };

    // encode vector into a Vec<u8>
    let mut bytes = Vec::new();
    encode(&record, &mut bytes);

    // decode a &Vec<(u64, String)> from binary data
    if let Ok((result, rest)) = decode::<MyStruct>(&mut bytes) {
        assert!(result == &record);
        assert!(rest.len() == 0);
    }
}

#[test]
fn test_multiple_encode_decode() {
    let mut bytes = Vec::new();
    encode(&0u32, &mut bytes);
    encode(&7u64, &mut bytes);
    encode(&vec![1,2,3], &mut bytes);
    encode(&"grawwwwrr".to_owned(), &mut bytes);

    let (t, r) = decode::<u32>(&mut bytes).unwrap(); assert!(*t == 0);
    let (t, r) = decode::<u64>(r).unwrap(); assert!(*t == 7);
    let (t, r) = decode::<Vec<i32>>(r).unwrap(); assert!(*t == vec![1,2,3]);
    let (t, r) = decode::<String>(r).unwrap(); assert!(*t == "grawwwwrr".to_owned());
}
<commit_msg>suppressed unused variable warning<commit_after>#[macro_use]
extern crate abomonation;
use abomonation::*;

#[test] fn test_option_box_u64() { _test_pass(vec![Some(Box::new(0u64))]); }
#[test] fn test_option_vec() { _test_pass(vec![Some(vec![0, 1, 2])]); }
#[test] fn test_u32x4_pass() { _test_pass(vec![((1,2,3),vec![(0u32, 0u32, 0u32, 0u32); 1024])]); }
#[test] fn test_u64_pass() { _test_pass(vec![0u64; 1024]); }
#[test] fn test_string_pass() { _test_pass(vec![format!("grawwwwrr!"); 1024]); }
#[test] fn test_vec_u_s_pass() { _test_pass(vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }

#[test] fn test_u64_fail() { _test_fail(vec![0u64; 1024]); }
#[test] fn test_string_fail() { _test_fail(vec![format!("grawwwwrr!"); 1024]); }
#[test] fn test_vec_u_s_fail() { _test_fail(vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }

fn _test_pass<T: Abomonation+Eq>(record: T) {
    let mut bytes = Vec::new();
    encode(&record, &mut bytes);
    let (result, rest) = decode::<T>(&mut bytes[..]).unwrap();
    assert!(&record == result);
    assert!(rest.len() == 0);
}

fn _test_fail<T: Abomonation>(record: T) {
    let mut bytes = Vec::new();
    encode(&record, &mut bytes);
    bytes.pop();
    if let Ok(_) = decode::<T>(&mut bytes[..]) { panic!() }
    else { }
}

#[derive(Eq, PartialEq)]
struct MyStruct {
    a: String,
    b: u64,
    c: Vec<u8>,
}

abomonate!(MyStruct : a, b, c);

#[test]
fn test_macro() {
    // create some test data out of abomonation-approved types
    let record = MyStruct{ a: "test".to_owned(), b: 0, c: vec![0, 1, 2] };

    // encode vector into a Vec<u8>
    let mut bytes = Vec::new();
    encode(&record, &mut bytes);

    // decode a &Vec<(u64, String)> from binary data
    if let Ok((result, rest)) = decode::<MyStruct>(&mut bytes) {
        assert!(result == &record);
        assert!(rest.len() == 0);
    }
}

#[test]
fn test_multiple_encode_decode() {
    let mut bytes = Vec::new();
    encode(&0u32, &mut bytes);
    encode(&7u64, &mut bytes);
    encode(&vec![1,2,3], &mut bytes);
    encode(&"grawwwwrr".to_owned(), &mut bytes);

    let (t, r) = decode::<u32>(&mut bytes).unwrap(); assert!(*t == 0);
    let (t, r) = decode::<u64>(r).unwrap(); assert!(*t == 7);
    let (t, r) = decode::<Vec<i32>>(r).unwrap(); assert!(*t == vec![1,2,3]);
    let (t, _r) = decode::<String>(r).unwrap(); assert!(*t == "grawwwwrr".to_owned());
}
<|endoftext|>